From fe8826d5b8e8c161c7f6de28ed048c4b7e004157 Mon Sep 17 00:00:00 2001 From: XProger Date: Thu, 24 Feb 2022 02:10:05 +0300 Subject: [PATCH] #407 32X support, initial commit, cinematic cutscenes --- src/fixed/camera.h | 63 ++ src/fixed/common.cpp | 17 +- src/fixed/common.h | 127 ++- src/fixed/draw.h | 42 + src/fixed/game.h | 38 +- src/fixed/item.h | 12 +- src/fixed/lara.h | 8 + src/fixed/level.h | 8 + src/fixed/object.h | 29 + src/platform/32x/32x.h | 128 +++ src/platform/32x/Makefile | 91 ++ src/platform/32x/asm/data.s | 17 + src/platform/32x/asm/matrixPush.s | 42 + src/platform/32x/crt/m68k_crt0.s | 85 ++ src/platform/32x/crt/m68k_crt1.s | 548 ++++++++++++ src/platform/32x/crt/sh2_crt0.s | 719 ++++++++++++++++ src/platform/32x/deploy.sh | 3 + src/platform/32x/main.cpp | 149 ++++ src/platform/32x/rasterizer.h | 809 ++++++++++++++++++ src/platform/32x/render.cpp | 1320 +++++++++++++++++++++++++++++ src/platform/32x/sound.cpp | 51 ++ 21 files changed, 4251 insertions(+), 55 deletions(-) create mode 100644 src/platform/32x/32x.h create mode 100644 src/platform/32x/Makefile create mode 100644 src/platform/32x/asm/data.s create mode 100644 src/platform/32x/asm/matrixPush.s create mode 100644 src/platform/32x/crt/m68k_crt0.s create mode 100644 src/platform/32x/crt/m68k_crt1.s create mode 100644 src/platform/32x/crt/sh2_crt0.s create mode 100644 src/platform/32x/deploy.sh create mode 100644 src/platform/32x/main.cpp create mode 100644 src/platform/32x/rasterizer.h create mode 100644 src/platform/32x/render.cpp create mode 100644 src/platform/32x/sound.cpp diff --git a/src/fixed/camera.h b/src/fixed/camera.h index 8f822b79..b0bf2e7e 100644 --- a/src/fixed/camera.h +++ b/src/fixed/camera.h @@ -13,6 +13,69 @@ #define CAM_ANGLE_COMBAT ANGLE(-10) #define CAM_ANGLE_MAX ANGLE(85) +void Camera::initCinematic() +{ + switch (gLevelID) + { + case LVL_TR1_CUT_1: + gCinematicCamera.view.pos.x = 36668; + gCinematicCamera.view.pos.z = 63180; + gCinematicCamera.targetAngle.y = -ANGLE(128); + break; + case LVL_TR1_CUT_2: + gCinematicCamera.view.pos.x = 51962; + gCinematicCamera.view.pos.z = 53760; + gCinematicCamera.targetAngle.y = ANGLE_90 - 4; + break; + case LVL_TR1_CUT_3: + gCinematicCamera.targetAngle.y = ANGLE_90; + //level.flip(); + break; + case LVL_TR1_CUT_4: + gCinematicCamera.targetAngle.y = ANGLE_90; + break; + default: + ASSERT(false); + break; + } +} + +void Camera::updateCinematic() +{ + const CameraFrame &frame = level.cameraFrames[timer++]; + + int32 px = frame.pos.x; + int32 py = frame.pos.y; + int32 pz = frame.pos.z; + + int32 dx = frame.target.x - px; + int32 dy = frame.target.y - py; + int32 dz = frame.target.z - pz; + + anglesFromVector(dx, dy, dz, angle.x, angle.y); + + int32 s, c; + sincos(targetAngle.y, s, c); + + X_ROTXY(pz, px, s, c); + + px += view.pos.x; + py += view.pos.y; + pz += view.pos.z; + + matrixSetView(_vec3i(px, py, pz), angle.x, angle.y + targetAngle.y); + + Room* nextRoom = view.room->getRoom(px, py, pz); + if (nextRoom) { + view.room = nextRoom; + } + + if (timer >= level.cameraFramesCount) { + timer = level.cameraFramesCount - 1; + nextLevel(LevelID(gLevelID + 1)); + } +} + void Camera::init(ItemObj* lara) { ASSERT(lara->extraL); diff --git a/src/fixed/common.cpp b/src/fixed/common.cpp index ff6f1576..0740bbac 100644 --- a/src/fixed/common.cpp +++ b/src/fixed/common.cpp @@ -7,7 +7,7 @@ vec3i gCameraViewPos; Matrix gMatrixStack[MAX_MATRICES]; Matrix* gMatrixPtr = gMatrixStack; -EWRAM_DATA Sphere gSpheres[2][MAX_SPHERES]; +EWRAM_DATA Sphere gSpheres[2][MAX_SPHERES]; // EWRAM 1k const FloorData* gLastFloorData; FloorData gLastFloorSlant; @@ -45,7 +45,7 @@ EWRAM_DATA ExtraInfoLara playersExtra[MAX_PLAYERS]; #ifdef __3DO__ // TODO fix the title scren on 3DO EWRAM_DATA LevelID gLevelID = LVL_TR1_1; #else -EWRAM_DATA LevelID gLevelID = LVL_TR1_TITLE; +EWRAM_DATA LevelID gLevelID = LVL_TR1_1; #endif const LevelInfo gLevelInfo[LVL_MAX] = { @@ -1144,7 +1144,7 @@ X_INLINE int16 lerpAngleSlow(int16 a, int16 b, int32 mul, int32 div) void matrixPush_c() { ASSERT(gMatrixPtr - gMatrixStack < MAX_MATRICES); - memcpy(gMatrixPtr + 1, gMatrixPtr, sizeof(Matrix)); + gMatrixPtr[1] = gMatrixPtr[0]; gMatrixPtr++; } @@ -1556,7 +1556,8 @@ void palSet(const uint16* palette, int32 gamma, int32 bright) if (gamma || bright) { - uint16 tmp[256]; + uint16* tmp = (uint16*)&gSpheres; + if (gamma) { palGamma(pal, tmp, gamma); pal = tmp; @@ -1574,9 +1575,11 @@ void palSet(const uint16* palette, int32 gamma, int32 bright) void dmaFill(void* dst, uint8 value, uint32 count) { ASSERT((count & 3) == 0); -#ifdef __GBA__ +#if defined(__GBA__) vu32 v = value; dma3_fill(dst, v, count); +#elif defined(__32X__) + fast_memset(dst, value, count >> 2); #else memset(dst, value, count); #endif @@ -1586,8 +1589,10 @@ void dmaFill(void* dst, uint8 value, uint32 count) void dmaCopy(const void* src, void* dst, uint32 size) { ASSERT((size & 3) == 0); -#ifdef __GBA__ +#if defined(__GBA__) dma3_cpy(dst, src, size); +#elif defined(__32X__) + fast_memcpy(dst, src, size >> 2); #else memcpy(dst, src, size); #endif diff --git a/src/fixed/common.h b/src/fixed/common.h index d2c86e35..7a3501fb 100644 --- a/src/fixed/common.h +++ b/src/fixed/common.h @@ -10,46 +10,64 @@ #if defined(_WIN32) #define MODE4 - //#define MODE13 #define USE_DIV_TABLE + #define MODE4 + #define FRAME_WIDTH 240 + #define FRAME_HEIGHT 160 + #define _CRT_SECURE_NO_WARNINGS #include #elif defined(__GBA__) - #define MODE4 #define USE_DIV_TABLE #define ROM_READ #define USE_ASM #define ALIGNED_LIGHTMAP + #define MODE4 + #define FRAME_WIDTH 240 + #define FRAME_HEIGHT 160 + #include #elif defined(__NDS__) - #define MODEHW #define USE_DIV_TABLE + #define MODEHW + #define FRAME_WIDTH 256 + #define FRAME_HEIGHT 192 + #include #include #include #elif defined(__TNS__) - #define MODE13 #define USE_DIV_TABLE + #define MODE13 + #define FRAME_WIDTH 320 + #define FRAME_HEIGHT 240 + #include #elif defined(__DOS__) - #define MODE13 #define USE_DIV_TABLE + #define MODE13 + #define FRAME_WIDTH 320 + #define FRAME_HEIGHT 200 + #include #include #include #include #include #elif defined(__3DO__) - #define MODEHW #define USE_DIV_TABLE // 4k of DRAM #define CPU_BIG_ENDIAN #define USE_ASM + #define MODEHW + #define FRAME_WIDTH 320 + #define FRAME_HEIGHT 240 + #include #include #include @@ -69,6 +87,16 @@ #include #include #include +#elif defined(__32X__) + #define USE_DIV_TABLE + #define CPU_BIG_ENDIAN + #define ROM_READ + + #define MODE13 + #define FRAME_WIDTH 320 + #define FRAME_HEIGHT 224 + + #include "32x.h" #else #error unsupported platform #endif @@ -81,30 +109,8 @@ #include #include -#include - -#if defined(MODEHW) - #if defined(__3DO__) - #define FRAME_WIDTH 320 - #define FRAME_HEIGHT 240 - #elif defined(__NDS__) - #define FRAME_WIDTH 256 - #define FRAME_HEIGHT 192 - #endif -#elif defined(MODE4) - #define VRAM_WIDTH 120 // in shorts (240 bytes) - #define FRAME_WIDTH 240 - #define FRAME_HEIGHT 160 -#elif defined(MODE13) - #define VRAM_WIDTH 160 // in shorts (320 bytes) - #define FRAME_WIDTH 320 - #if defined(__TNS__) - #define FRAME_HEIGHT 240 // MODE X? - #else - #define FRAME_HEIGHT 200 - #endif -#endif +#define VRAM_WIDTH (FRAME_WIDTH/2) // in shorts // Optimization flags ========================================================= #ifdef __GBA__ @@ -143,6 +149,23 @@ #define FAST_HITMASK #endif +#ifdef __32X__ +// hide dead enemies after a while to reduce the number of polygons on the screen + #define HIDE_CORPSES (30*10) // 10 sec +// replace trap flor geometry by two flat quads in the static state + #define LOD_TRAP_FLOOR +// disable some plants environment to reduce overdraw of transparent geometry + #define NO_STATIC_MESH_PLANTS +// use IWRAM_CODE section that faster for matrix interpolation (GBA only) + #define IWRAM_MATRIX_LERP +// the maximum of active enemies + #define MAX_ENEMIES 3 +// visibility distance + #define VIEW_DIST (1024 * 10) +// skip collideSpheres for enemies + #define FAST_HITMASK +#endif + #ifndef NAV_STEPS #define NAV_STEPS 5 #endif @@ -190,16 +213,24 @@ typedef unsigned int uint32; typedef uint16 divTableInt; #endif -#if defined(__3DO__) +//#include +inline void* operator new(size_t, void *ptr) +{ + return ptr; +} + +#if defined(__3DO__) || defined(__32X__) X_INLINE int32 abs(int32 x) { return (x >= 0) ? x : -x; } #endif -#if defined(__GBA__) || defined(__NDS__) +#if defined(__GBA__) || defined(__NDS__) || defined(__32X__) #define int2str(x,str) itoa(x, str, 10) #elif defined(__3DO__) #define int2str(x,str) sprintf(str, "%d", x) +#elif defined(__TNS__) + #define int2str(x,str) __itoa(x, str, 10) #else #define int2str(x,str) _itoa(x, str, 10) #endif @@ -436,7 +467,7 @@ extern int32 fps; #define MAX_TEXTURES 1536 #define MAX_SPRITES 180 #define MAX_FACES 1920 -#define MAX_ROOM_LIST 16 +#define MAX_ROOM_LIST 32 #define MAX_PORTALS 16 #define MAX_CAUSTICS 32 #define MAX_RAND_TABLE 32 @@ -521,10 +552,11 @@ enum InputKey { IK_C = (1 << 6), IK_X = (1 << 7), IK_Y = (1 << 8), - IK_L = (1 << 9), - IK_R = (1 << 10), - IK_START = (1 << 11), - IK_SELECT = (1 << 12) + IK_Z = (1 << 9), + IK_L = (1 << 10), + IK_R = (1 << 11), + IK_START = (1 << 12), + IK_SELECT = (1 << 13) }; // action keys (ItemObj::input) @@ -1322,6 +1354,9 @@ struct Camera { bool lastFixed; bool center; + void initCinematic(); + void updateCinematic(); + void init(ItemObj* lara); Location getLocationForAngle(int32 angle, int32 distH, int32 distV); void clip(Location &loc); @@ -1517,6 +1552,8 @@ struct ExtraInfoLara extern ExtraInfoLara playersExtra[MAX_PLAYERS]; +#define gCinematicCamera playersExtra[0].camera + struct Enemy; enum EnemyMood @@ -1993,6 +2030,14 @@ struct Box uint16 overlap; }; +struct CameraFrame +{ + vec3s target; + vec3s pos; + int16 fov; + int16 roll; +}; + struct Level { uint32 magic; @@ -2037,7 +2082,7 @@ struct Level const uint16* zones[2][ZONE_MAX]; const int16* animTexData; const ItemObjInfo* itemsInfo; - uint32 cameraFrames; + const CameraFrame* cameraFrames; const uint16* soundMap; const SoundInfo* soundsInfo; const uint8* soundData; @@ -2645,6 +2690,13 @@ vec3i boxPushOut(const AABBi &a, const AABBi &b); void flush_c(); #endif +#ifdef __32X__ // TODO + #undef matrixPush + #define matrixPush matrixPush_asm + + extern "C" void matrixPush_asm(); +#endif + #define matrixPop() gMatrixPtr-- X_INLINE vec3i matrixGetDir(const Matrix &m) @@ -2678,6 +2730,7 @@ void drawText(int32 x, int32 y, const char* text, TextAlign align); void drawModel(const ItemObj* item); void drawItem(const ItemObj* item); void drawRooms(Camera* camera); +void drawCinematicRooms(); void drawHUD(Lara* lara); void drawNodesLerp(const ItemObj* item, const AnimFrame* frameA, const AnimFrame* frameB, int32 frameDelta, int32 frameRate); diff --git a/src/fixed/draw.h b/src/fixed/draw.h index 9a712a8b..36911b30 100644 --- a/src/fixed/draw.h +++ b/src/fixed/draw.h @@ -771,6 +771,48 @@ void drawRooms(Camera* camera) setViewport(vp); } +void drawCinematicRooms() +{ + RectMinMax vp = viewport; +#if 1 + gCinematicCamera.view.room->clip = viewport; + + Room** visRoom = gCinematicCamera.view.room->getVisibleRooms(); + + // draw rooms and objects + while (*visRoom) + { + Room* room = *visRoom++; + drawRoom(room); + room->reset(); + } +#else + for (int32 i = 0; i < level.roomsCount; i++) + { + rooms[i].clip = vp; + } + + for (int32 i = 0; i < level.roomsCount; i++) + { + if (rooms[i].info->alternateRoom != NO_ROOM) { + rooms[rooms[i].info->alternateRoom].clip.x0 = 0xFFFF; + } + } + + for (int32 i = 0; i < level.roomsCount; i++) + { + const Room* room = rooms + i; + if (room->clip.x0 == 0xFFFF) + continue; + + drawRoom(room); + } +#endif + + setPaletteIndex(0); + setViewport(vp); +} + void drawHUD(Lara* lara) { int32 x = (FRAME_WIDTH - (100 + 2 + 2)) - 4; diff --git a/src/fixed/game.h b/src/fixed/game.h index 095d93b5..3e4c8793 100644 --- a/src/fixed/game.h +++ b/src/fixed/game.h @@ -149,6 +149,11 @@ void gameLoadLevel(const void* data) } ItemObj::sFirstFree = items + level.itemsCount; + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + players[i] = NULL; + } + if (gLevelID == LVL_TR1_TITLE) { // init dummy Lara for updateInput() items->extraL = playersExtra; @@ -173,13 +178,19 @@ void gameLoadLevel(const void* data) item->angle.y = ((info->flags >> 14) - 2) * ANGLE_90; item->flags = info->flags; - if (item->type == ITEM_LARA || item->type == ITEM_CUT_1) { + if (item->type == ITEM_LARA) { players[0] = (Lara*)item; } item->init(rooms + info->roomIndex); } + if (isCutsceneLevel()) + { + gCinematicCamera.initCinematic(); + } + + // gym //resetLara(0, 7, _vec3i(39038, -1280, 51712), ANGLE_90); // start //resetLara(0, 8, _vec3i(55994, 0, 52603), ANGLE_90); // piano @@ -233,10 +244,17 @@ void updateItems() item = next; } - for (int32 i = 0; i < MAX_PLAYERS; i++) + if (isCutsceneLevel()) + { + gCinematicCamera.updateCinematic(); + } + else { - if (players[i]) { - players[i]->update(); + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (players[i]) { + players[i]->update(); + } } } } @@ -306,9 +324,17 @@ void gameRender() { // TODO set viewports for coop #ifndef PROFILE_SOUNDTIME - drawRooms(&players[i]->extraL->camera); + if (isCutsceneLevel()) { + drawCinematicRooms(); + } else { + drawRooms(&players[i]->extraL->camera); + } #endif - drawHUD(players[i]); + + if (players[i]) + { + drawHUD(players[i]); + } } } else { inventory.draw(); diff --git a/src/fixed/item.h b/src/fixed/item.h index 6cd4e775..218cbf8c 100644 --- a/src/fixed/item.h +++ b/src/fixed/item.h @@ -937,7 +937,7 @@ int32 ItemObj::getSpheres(Sphere* spheres, bool flag) const #include "enemy.h" #include "object.h" -#ifdef __GBA__ +#ifdef __GNUC__ // ItemObj ctor is called on existing and pre-filled memory #pragma GCC diagnostic ignored "-Wuninitialized" #endif @@ -992,7 +992,7 @@ ItemObj::ItemObj(Room* room) room->add(this); } -#ifdef __GBA__ +#ifdef __GNUC__ #pragma GCC diagnostic warning "-Wuninitialized" #endif @@ -1472,10 +1472,10 @@ ItemObj* ItemObj::init(Room* room) // INIT_ITEM( GEARS_1 , ??? ); // INIT_ITEM( GEARS_2 , ??? ); // INIT_ITEM( GEARS_3 , ??? ); - // INIT_ITEM( CUT_1 , ??? ); - // INIT_ITEM( CUT_2 , ??? ); - // INIT_ITEM( CUT_3 , ??? ); - // INIT_ITEM( CUT_4 , ??? ); + INIT_ITEM( CUT_1 , CinematicObject ); + INIT_ITEM( CUT_2 , CinematicObject ); + INIT_ITEM( CUT_3 , CinematicObject ); + INIT_ITEM( CUT_4 , CinematicObject ); // INIT_ITEM( INV_PASSPORT_CLOSED , ??? ); // INIT_ITEM( INV_MAP , ??? ); INIT_ITEM( CRYSTAL , Crystal ); diff --git a/src/fixed/lara.h b/src/fixed/lara.h index d33c8d5e..0683c7df 100644 --- a/src/fixed/lara.h +++ b/src/fixed/lara.h @@ -2737,6 +2737,14 @@ struct Lara : ItemObj if (keys & IK_L) input |= IN_LOOK; if (keys & IK_R) input |= IN_WALK; } + #elif defined(__32X__) + // 6 buttons + if (keys & IK_A) input |= IN_JUMP; + if (keys & IK_B) input |= IN_ACTION; + if (keys & IK_C) input |= IN_WEAPON; + if (keys & IK_X) input |= IN_WALK; + if (keys & IK_Y) input |= IN_UP | IN_DOWN; + if (keys & IK_Z) input |= IN_LOOK; #elif defined(__GBA__) || defined(_WIN32) int32 ikA, ikB; diff --git a/src/fixed/level.h b/src/fixed/level.h index 528af50f..2f87e3a6 100644 --- a/src/fixed/level.h +++ b/src/fixed/level.h @@ -224,4 +224,12 @@ int32 getAmbientTrack() return gLevelInfo[gLevelID].track; } +bool isCutsceneLevel() +{ + return (gLevelID == LVL_TR1_CUT_1) || + (gLevelID == LVL_TR1_CUT_2) || + (gLevelID == LVL_TR1_CUT_3) || + (gLevelID == LVL_TR1_CUT_4); +} + #endif diff --git a/src/fixed/object.h b/src/fixed/object.h index c178454a..75534dcb 100644 --- a/src/fixed/object.h +++ b/src/fixed/object.h @@ -335,6 +335,35 @@ struct TrapDoor : Object }; +struct CinematicObject : Object +{ + CinematicObject(Room* room) : Object(room) + { + angle.y = 0; + + if (type == ITEM_CUT_1) + { + gCinematicCamera.view.pos = pos; + gCinematicCamera.view.room = room; + } + + flags |= ITEM_FLAG_SHADOW; + + activate(); + } + + virtual void update() + { + if (type == ITEM_CUT_1 || type == ITEM_CUT_2 || type == ITEM_CUT_3) + { + pos = playersExtra[0].camera.view.pos; + angle.y = playersExtra[0].camera.targetAngle.y; + } + animProcess(); + } +}; + + struct Crystal : Object { Crystal(Room* room) : Object(room) diff --git a/src/platform/32x/32x.h b/src/platform/32x/32x.h new file mode 100644 index 00000000..1bb3bf6e --- /dev/null +++ b/src/platform/32x/32x.h @@ -0,0 +1,128 @@ +#ifndef __32X_H__ +#define __32X_H__ + +#define MARS_CRAM (*(volatile unsigned short *)0x20004200) +#define MARS_FRAMEBUFFER (*(volatile unsigned short *)0x24000000) +#define MARS_OVERWRITE_IMG (*(volatile unsigned short *)0x24020000) +#define MARS_SDRAM (*(volatile unsigned short *)0x26000000) + +#define MARS_SYS_INTMSK (*(volatile unsigned short *)0x20004000) +#define MARS_SYS_DMACTR (*(volatile unsigned short *)0x20004006) +#define MARS_SYS_DMASAR (*(volatile unsigned long *)0x20004008) +#define MARS_SYS_DMADAR (*(volatile unsigned long *)0x2000400C) +#define MARS_SYS_DMALEN (*(volatile unsigned short *)0x20004010) +#define MARS_SYS_DMAFIFO (*(volatile unsigned short *)0x20004012) +#define MARS_SYS_VRESI_CLR (*(volatile unsigned short *)0x20004014) +#define MARS_SYS_VINT_CLR (*(volatile unsigned short *)0x20004016) +#define MARS_SYS_HINT_CLR (*(volatile unsigned short *)0x20004018) +#define MARS_SYS_CMDI_CLR (*(volatile unsigned short *)0x2000401A) +#define MARS_SYS_PWMI_CLR (*(volatile unsigned short *)0x2000401C) +#define MARS_SYS_COMM0 (*(volatile unsigned short *)0x20004020) /* Master SH2 communication */ +#define MARS_SYS_COMM2 (*(volatile unsigned short *)0x20004022) +#define MARS_SYS_COMM4 (*(volatile unsigned short *)0x20004024) /* Slave SH2 communication */ +#define MARS_SYS_COMM6 (*(volatile unsigned short *)0x20004026) +#define MARS_SYS_COMM8 (*(volatile unsigned short *)0x20004028) /* controller 1 current value */ +#define MARS_SYS_COMM10 (*(volatile unsigned short *)0x2000402A) /* controller 2 current value */ +#define MARS_SYS_COMM12 (*(volatile unsigned long *)0x2000402C) /* vcount current value */ + +#define MARS_PWM_CTRL (*(volatile unsigned short *)0x20004030) +#define MARS_PWM_CYCLE (*(volatile unsigned short *)0x20004032) +#define MARS_PWM_LEFT (*(volatile unsigned short *)0x20004034) +#define MARS_PWM_RIGHT (*(volatile unsigned short *)0x20004036) +#define MARS_PWM_MONO (*(volatile unsigned short *)0x20004038) + +#define MARS_VDP_DISPMODE (*(volatile unsigned short *)0x20004100) +#define MARS_VDP_FILLEN (*(volatile unsigned short *)0x20004104) +#define MARS_VDP_FILADR (*(volatile unsigned short *)0x20004106) +#define MARS_VDP_FILDAT (*(volatile unsigned short *)0x20004108) +#define MARS_VDP_FBCTL (*(volatile unsigned short *)0x2000410A) + +#define MARS_SH2_ACCESS_VDP 0x8000 +#define MARS_68K_ACCESS_VDP 0x0000 + +#define MARS_PAL_FORMAT 0x0000 +#define MARS_NTSC_FORMAT 0x8000 + +#define MARS_VDP_PRIO_68K 0x0000 +#define MARS_VDP_PRIO_32X 0x0080 + +#define MARS_224_LINES 0x0000 +#define MARS_240_LINES 0x0040 + +#define MARS_VDP_MODE_OFF 0x0000 +#define MARS_VDP_MODE_256 0x0001 +#define MARS_VDP_MODE_32K 0x0002 +#define MARS_VDP_MODE_RLE 0x0003 + +#define MARS_VDP_VBLK 0x8000 +#define MARS_VDP_HBLK 0x4000 +#define MARS_VDP_PEN 0x2000 +#define MARS_VDP_FEN 0x0002 +#define MARS_VDP_FS 0x0001 + +#define SH2_CCTL_CP 0x10 +#define SH2_CCTL_TW 0x08 +#define SH2_CCTL_CE 0x01 + +#define SH2_FRT_TIER (*(volatile unsigned char *)0xFFFFFE10) +#define SH2_FRT_FTCSR (*(volatile unsigned char *)0xFFFFFE11) +#define SH2_FRT_FRCH (*(volatile unsigned char *)0xFFFFFE12) +#define SH2_FRT_FRCL (*(volatile unsigned char *)0xFFFFFE13) +#define SH2_FRT_OCRH (*(volatile unsigned char *)0xFFFFFE14) +#define SH2_FRT_OCRL (*(volatile unsigned char *)0xFFFFFE15) +#define SH2_FRT_TCR (*(volatile unsigned char *)0xFFFFFE16) +#define SH2_FRT_TOCR (*(volatile unsigned char *)0xFFFFFE17) +#define SH2_FRT_ICRH (*(volatile unsigned char *)0xFFFFFE18) +#define SH2_FRT_ICRL (*(volatile unsigned char *)0xFFFFFE19) + +#define SH2_DMA_SAR0 (*(volatile unsigned long *)0xFFFFFF80) +#define SH2_DMA_DAR0 (*(volatile unsigned long *)0xFFFFFF84) +#define SH2_DMA_TCR0 (*(volatile unsigned long *)0xFFFFFF88) +#define SH2_DMA_CHCR0 (*(volatile unsigned long *)0xFFFFFF8C) +#define SH2_DMA_VCR0 (*(volatile unsigned long *)0xFFFFFFA0) +#define SH2_DMA_DRCR0 (*(volatile unsigned char *)0xFFFFFE71) + +#define SH2_DMA_SAR1 (*(volatile unsigned long *)0xFFFFFF90) +#define SH2_DMA_DAR1 (*(volatile unsigned long *)0xFFFFFF94) +#define SH2_DMA_TCR1 (*(volatile unsigned long *)0xFFFFFF98) +#define SH2_DMA_CHCR1 (*(volatile unsigned long *)0xFFFFFF9C) +#define SH2_DMA_VCR1 (*(volatile unsigned long *)0xFFFFFFA8) +#define SH2_DMA_DRCR1 (*(volatile unsigned char *)0xFFFFFE72) + +#define SH2_DMA_DMAOR (*(volatile unsigned long *)0xFFFFFFB0) + +#define SH2_INT_IPRA (*(volatile unsigned short *)0xFFFFFEE2) + +#define SEGA_CTRL_UP 0x0001 +#define SEGA_CTRL_DOWN 0x0002 +#define SEGA_CTRL_LEFT 0x0004 +#define SEGA_CTRL_RIGHT 0x0008 +#define SEGA_CTRL_B 0x0010 +#define SEGA_CTRL_C 0x0020 +#define SEGA_CTRL_A 0x0040 +#define SEGA_CTRL_START 0x0080 +#define SEGA_CTRL_Z 0x0100 +#define SEGA_CTRL_Y 0x0200 +#define SEGA_CTRL_X 0x0400 +#define SEGA_CTRL_MODE 0x0800 + +#define SEGA_CTRL_TYPE 0xF000 +#define SEGA_CTRL_THREE 0x0000 +#define SEGA_CTRL_SIX 0x1000 +#define SEGA_CTRL_NONE 0xF000 + +#define MASTER_STATUS_OK 1 +#define SLAVE_STATUS_OK 2 +#define MASTER_LOCK 4 +#define SLAVE_LOCK 8 + +/* global functions in sh2_crt0.s */ +extern "C" +{ + void fast_memcpy(void *dst, const void *src, int len); + void fast_memset(void *dst, int value, int len); + void CacheControl(int mode); + void CacheClearLine(void* ptr); +} + +#endif diff --git a/src/platform/32x/Makefile b/src/platform/32x/Makefile new file mode 100644 index 00000000..ce6c80fb --- /dev/null +++ b/src/platform/32x/Makefile @@ -0,0 +1,91 @@ +ifdef $(GENDEV) +ROOTDIR = $(GENDEV) +else +ROOTDIR = /opt/toolchains/sega +endif + +TARGET = OpenLara +BUILD = build +SOURCES = . asm ../../fixed + +LDSCRIPTSDIR = $(ROOTDIR)/ldscripts + +SHPREFIX = $(ROOTDIR)/sh-elf/bin/sh-elf- +SHCC = $(SHPREFIX)gcc +SHXX = $(SHPREFIX)g++ +SHAS = $(SHPREFIX)as +SHLD = $(SHPREFIX)ld +SHOBJC = $(SHPREFIX)objcopy + +CC_VER := $(shell $(SHCC) -dumpversion) + +MDPREFIX = $(ROOTDIR)/m68k-elf/bin/m68k-elf- +MDAS = $(MDPREFIX)as +MDLD = $(MDPREFIX)ld + +LIBPATH = -L$(ROOTDIR)/sh-elf/lib -L$(ROOTDIR)/sh-elf/lib/gcc/sh-elf/$(CC_VER) -L$(ROOTDIR)/sh-elf/sh-elf/lib +INCPATH = -I. -I../../fixed -I$(ROOTDIR)/sh-elf/include -I$(ROOTDIR)/sh-elf/sh-elf/include + +SHCCFLAGS = -D__32X__ -m2 -mb -O2 -Wall -g -c -fno-rtti -fno-exceptions -fno-asynchronous-unwind-tables -fomit-frame-pointer -ffast-math -Wno-class-memaccess +SHLDFLAGS = -T $(LDSCRIPTSDIR)/mars.ld -Wl,-Map=$(BUILD)/output.map -nostdlib +SHASFLAGS = --big + +MDLDFLAGS = -T $(LDSCRIPTSDIR)/md.ld --oformat binary +MDASFLAGS = -m68000 --register-prefix-optional + +DD = dd +RM = rm -f + +FILES_AS = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.s)) +FILES_CC = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.c)) +FILES_XX = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.cpp)) + +LIBS = $(LIBPATH) -lm -lc -lgcc -lgcc-Os-4-200 -lnosys +OBJS = $(BUILD)/sh2_crt0.o +OBJS += $(addprefix $(BUILD)/, $(notdir $(FILES_AS:%.s=%.o))) +OBJS += $(addprefix $(BUILD)/, $(notdir $(FILES_CC:%.c=%.o))) +OBJS += $(addprefix $(BUILD)/, $(notdir $(FILES_XX:%.cpp=%.o))) + +.PHONY: dump clean + +all: $(BUILD) m68k_crt0.bin m68k_crt1.bin $(TARGET).32x + +dump: + @[ -d dump ] || mkdir -p dump + $(SHXX) $(SHCCFLAGS) $(INCPATH) -S -o dump/main.s main.cpp + $(SHXX) $(SHCCFLAGS) $(INCPATH) -S -o dump/render.s render.cpp + $(SHXX) $(SHCCFLAGS) $(INCPATH) -S -o dump/sound.s sound.cpp + $(SHXX) $(SHCCFLAGS) $(INCPATH) -S -o dump/common.s ../../fixed/common.cpp + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + +$(TARGET).32x: $(TARGET).elf + $(SHOBJC) -O binary $< temp.bin + $(DD) if=temp.bin of=$@ bs=1024K conv=sync + $(RM) -f temp.bin + +$(TARGET).elf: $(OBJS) + $(SHCC) $(SHLDFLAGS) $(OBJS) $(LIBS) -o $(TARGET).elf + +%.bin: crt/%.s + $(MDAS) $(MDASFLAGS) -o $(BUILD)/$@.o $< + $(MDLD) $(MDLDFLAGS) -o $(BUILD)/$@ $(BUILD)/$@.o + +$(BUILD)/%.o: crt/%.s + $(SHAS) $(SHASFLAGS) $(INCPATH) -o $@ $< + +$(BUILD)/%.o: asm/%.s + $(SHAS) $(SHASFLAGS) $(INCPATH) -o $@ $< + +$(BUILD)/%.o: %.c + $(SHCC) $(SHCCFLAGS) $(INCPATH) -o $@ $< + +$(BUILD)/%.o: %.cpp + $(SHXX) $(SHCCFLAGS) $(INCPATH) -o $@ $< + +$(BUILD)/%.o: ../../fixed/%.cpp + $(SHXX) $(SHCCFLAGS) $(INCPATH) -o $@ $< + +clean: + $(RM) $(BUILD)/* *.32x *.elf diff --git a/src/platform/32x/asm/data.s b/src/platform/32x/asm/data.s new file mode 100644 index 00000000..dcd12eef --- /dev/null +++ b/src/platform/32x/asm/data.s @@ -0,0 +1,17 @@ + .text +/* + .global _TITLE_PKD + .align 4 +_TITLE_PKD: + .long TITLE +TITLE: + .incbin "data/TITLE.PKD" +*/ + .global _LEVEL1_PKD + .align 4 +_LEVEL1_PKD: + .long LEVEL1 +LEVEL1: + .incbin "data/LEVEL1.PKD" + + .align 4 diff --git a/src/platform/32x/asm/matrixPush.s b/src/platform/32x/asm/matrixPush.s new file mode 100644 index 00000000..2d692c39 --- /dev/null +++ b/src/platform/32x/asm/matrixPush.s @@ -0,0 +1,42 @@ + .text + + .global _matrixPush_asm +_matrixPush_asm: + mov.l .var_matrixPtr, r5 + mov.l @r5, r0 + + /* row[0] */ + mov.l @r0+, r1 + mov.l @r0+, r2 + mov.l @r0+, r3 + mov.l @r0+, r4 + mov.l r1, @(32, r0) + mov.l r2, @(36, r0) + mov.l r3, @(40, r0) + mov.l r4, @(44, r0) + + /* row[1] */ + mov.l @r0+, r1 + mov.l @r0+, r2 + mov.l @r0+, r3 + mov.l @r0+, r4 + mov.l r1, @(32, r0) + mov.l r2, @(36, r0) + mov.l r3, @(40, r0) + mov.l r4, @(44, r0) + + /* row[2] */ + mov.l @r0+, r1 + mov.l @r0+, r2 + mov.l @r0+, r3 + mov.l @r0+, r4 + mov.l r1, @(32, r0) + mov.l r2, @(36, r0) + mov.l r3, @(40, r0) + mov.l r4, @(44, r0) + + rts + mov.l r0, @r5 + +.var_matrixPtr: + .long _gMatrixPtr diff --git a/src/platform/32x/crt/m68k_crt0.s b/src/platform/32x/crt/m68k_crt0.s new file mode 100644 index 00000000..c77c46a5 --- /dev/null +++ b/src/platform/32x/crt/m68k_crt0.s @@ -0,0 +1,85 @@ +| SEGA 32X support code for the 68000 +| by Chilly Willy +| First part of rom header + + .text + +| Initial exception vectors. When the console is first turned on, it is +| in MegaDrive mode. All vectors just point to the code to start up the +| Mars adapter. After the adapter is enabled, none of these vectors will +| appear as the adpater uses its own vector table to route exceptions to +| the jump table. 0x3F0 is where the 68000 starts at for the 32X. + + .long 0x01000000,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + .long 0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + .long 0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + .long 0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + .long 0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + .long 0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + .long 0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + .long 0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0,0x000003F0 + +| Standard MegaDrive ROM header at 0x100 + + .ascii "SEGA 32X " /* First 4 bytes must be "SEGA" */ + .ascii "XProger (c)2022 " /* Copyright and date */ + .ascii "OpenLara Alpha " /* JP Name */ + .ascii "OpenLara Alpha " /* EN Name */ + .ascii "GM 14021968-01" /* Serial No. */ + .word 0x0000 + .ascii "J6 " + .long 0x00000000,0x003FFFFF /* ROM start, end */ + .long 0x00FF0000,0x00FFFFFF /* RAM start, end */ + +| .ascii "RA" /* External RAM */ +| .byte 0xF8 /* don't clear + odd bytes */ +| .byte 0x20 /* SRAM */ +| .long 0x00200001,0x0020FFFF /* SRAM start, end */ + + .ascii " " /* no SRAM */ + + .ascii " " + .ascii " " + .ascii " " + .ascii " " + .ascii "F " /* enable any hardware configuration */ + +| Mars exception vector jump table at 0x200 + + jmp 0x880800.l /* reset = hot start */ + jsr 0x880806.l /* EX_BusError */ + jsr 0x880806.l /* EX_AddrError */ + jsr 0x880806.l /* EX_IllInstr */ + jsr 0x880806.l /* EX_DivByZero */ + jsr 0x880806.l /* EX_CHK */ + jsr 0x880806.l /* EX_TrapV */ + jsr 0x880806.l /* EX_Priviledge */ + jsr 0x880806.l /* EX_Trace */ + jsr 0x880806.l /* EX_LineA */ + jsr 0x880806.l /* EX_LineF */ + .space 72 /* reserved */ + jsr 0x880806.l /* EX_Spurious */ + jsr 0x880806.l /* EX_Level1 */ + jsr 0x880806.l /* EX_Level2 */ + jsr 0x880806.l /* EX_Level3 */ + jmp 0x88080C.l /* EX_Level4 HBlank */ + jsr 0x880806.l /* EX_Level5 */ + jmp 0x880812.l /* EX_Level6 VBlank */ + jsr 0x880806.l /* EX_Level7 */ + jsr 0x880806.l /* EX_Trap0 */ + jsr 0x880806.l /* EX_Trap1 */ + jsr 0x880806.l /* EX_Trap2 */ + jsr 0x880806.l /* EX_Trap3 */ + jsr 0x880806.l /* EX_Trap4 */ + jsr 0x880806.l /* EX_Trap5 */ + jsr 0x880806.l /* EX_Trap6 */ + jsr 0x880806.l /* EX_Trap7 */ + jsr 0x880806.l /* EX_Trap8 */ + jsr 0x880806.l /* EX_Trap9 */ + jsr 0x880806.l /* EX_TrapA */ + jsr 0x880806.l /* EX_TrapB */ + jsr 0x880806.l /* EX_TrapC */ + jsr 0x880806.l /* EX_TrapD */ + jsr 0x880806.l /* EX_TrapE */ + jsr 0x880806.l /* EX_TrapF */ + .space 166 /* reserved */ diff --git a/src/platform/32x/crt/m68k_crt1.s b/src/platform/32x/crt/m68k_crt1.s new file mode 100644 index 00000000..bfffb9ef --- /dev/null +++ b/src/platform/32x/crt/m68k_crt1.s @@ -0,0 +1,548 @@ +| SEGA 32X support code for the 68000 +| by Chilly Willy +| Third part of rom header + + .text + +| Standard Mars startup code at 0x3F0 - this is included as binary as SEGA +| uses this as a security key. US law allows us to include this as-is because +| it's used for security. The interoperability clauses in the law state this +| trumps copyright... and the Supreme Court agrees. :P + + .word 0x287C,0xFFFF,0xFFC0,0x23FC,0x0000,0x0000,0x00A1,0x5128 + .word 0x46FC,0x2700,0x4BF9,0x00A1,0x0000,0x7001,0x0CAD,0x4D41 + .word 0x5253,0x30EC,0x6600,0x03E6,0x082D,0x0007,0x5101,0x67F8 + .word 0x4AAD,0x0008,0x6710,0x4A6D,0x000C,0x670A,0x082D,0x0000 + .word 0x5101,0x6600,0x03B8,0x102D,0x0001,0x0200,0x000F,0x6706 + .word 0x2B78,0x055A,0x4000,0x7200,0x2C41,0x4E66,0x41F9,0x0000 + .word 0x04D4,0x6100,0x0152,0x6100,0x0176,0x47F9,0x0000,0x04E8 + .word 0x43F9,0x00A0,0x0000,0x45F9,0x00C0,0x0011,0x3E3C,0x0100 + .word 0x7000,0x3B47,0x1100,0x3B47,0x1200,0x012D,0x1100,0x66FA + .word 0x7425,0x12DB,0x51CA,0xFFFC,0x3B40,0x1200,0x3B40,0x1100 + .word 0x3B47,0x1200,0x149B,0x149B,0x149B,0x149B,0x41F9,0x0000 + .word 0x04C0,0x43F9,0x00FF,0x0000,0x22D8,0x22D8,0x22D8,0x22D8 + .word 0x22D8,0x22D8,0x22D8,0x22D8,0x41F9,0x00FF,0x0000,0x4ED0 + .word 0x1B7C,0x0001,0x5101,0x41F9,0x0000,0x06BC,0xD1FC,0x0088 + .word 0x0000,0x4ED0,0x0404,0x303C,0x076C,0x0000,0x0000,0xFF00 + .word 0x8137,0x0002,0x0100,0x0000,0xAF01,0xD91F,0x1127,0x0021 + .word 0x2600,0xF977,0xEDB0,0xDDE1,0xFDE1,0xED47,0xED4F,0xD1E1 + .word 0xF108,0xD9C1,0xD1E1,0xF1F9,0xF3ED,0x5636,0xE9E9,0x9FBF + .word 0xDFFF,0x4D41,0x5253,0x2049,0x6E69,0x7469,0x616C,0x2026 + .word 0x2053,0x6563,0x7572,0x6974,0x7920,0x5072,0x6F67,0x7261 + .word 0x6D20,0x2020,0x2020,0x2020,0x2020,0x2043,0x6172,0x7472 + .word 0x6964,0x6765,0x2056,0x6572,0x7369,0x6F6E,0x2020,0x2020 + .word 0x436F,0x7079,0x7269,0x6768,0x7420,0x5345,0x4741,0x2045 + .word 0x4E54,0x4552,0x5052,0x4953,0x4553,0x2C4C,0x5444,0x2E20 + .word 0x3139,0x3934,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020 + .word 0x2020,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020 + .word 0x2020,0x2020,0x2020,0x524F,0x4D20,0x5665,0x7273,0x696F + .word 0x6E20,0x312E,0x3000,0x48E7,0xC040,0x43F9,0x00C0,0x0004 + .word 0x3011,0x303C,0x8000,0x323C,0x0100,0x3E3C,0x0012,0x1018 + .word 0x3280,0xD041,0x51CF,0xFFF8,0x4CDF,0x0203,0x4E75,0x48E7 + .word 0x81C0,0x41F9,0x0000,0x063E,0x43F9,0x00C0,0x0004,0x3298 + .word 0x3298,0x3298,0x3298,0x3298,0x3298,0x3298,0x2298,0x3341 + .word 0xFFFC,0x3011,0x0800,0x0001,0x66F8,0x3298,0x3298,0x7000 + .word 0x22BC,0xC000,0x0000,0x7E0F,0x3340,0xFFFC,0x3340,0xFFFC + .word 0x3340,0xFFFC,0x3340,0xFFFC,0x51CF,0xFFEE,0x22BC,0x4000 + .word 0x0010,0x7E09,0x3340,0xFFFC,0x3340,0xFFFC,0x3340,0xFFFC + .word 0x3340,0xFFFC,0x51CF,0xFFEE,0x4CDF,0x0381,0x4E75,0x8114 + .word 0x8F01,0x93FF,0x94FF,0x9500,0x9600,0x9780,0x4000,0x0080 + .word 0x8104,0x8F02,0x48E7,0xC140,0x43F9,0x00A1,0x5180,0x08A9 + .word 0x0007,0xFF80,0x66F8,0x3E3C,0x00FF,0x7000,0x7200,0x337C + .word 0x00FF,0x0004,0x3341,0x0006,0x3340,0x0008,0x4E71,0x0829 + .word 0x0001,0x000B,0x66F8,0x0641,0x0100,0x51CF,0xFFE8,0x4CDF + .word 0x0283,0x4E75,0x48E7,0x8180,0x41F9,0x00A1,0x5200,0x08A8 + .word 0x0007,0xFF00,0x66F8,0x3E3C,0x001F,0x20C0,0x20C0,0x20C0 + .word 0x20C0,0x51CF,0xFFF6,0x4CDF,0x0181,0x4E75,0x41F9,0x00FF + .word 0x0000,0x3E3C,0x07FF,0x7000,0x20C0,0x20C0,0x20C0,0x20C0 + .word 0x20C0,0x20C0,0x20C0,0x20C0,0x51CF,0xFFEE,0x3B7C,0x0000 + .word 0x1200,0x7E0A,0x51CF,0xFFFE,0x43F9,0x00A1,0x5100,0x7000 + .word 0x2340,0x0020,0x2340,0x0024,0x1B7C,0x0003,0x5101,0x2E79 + .word 0x0088,0x0000,0x0891,0x0007,0x66FA,0x7000,0x3340,0x0002 + .word 0x3340,0x0004,0x3340,0x0006,0x2340,0x0008,0x2340,0x000C + .word 0x3340,0x0010,0x3340,0x0030,0x3340,0x0032,0x3340,0x0038 + .word 0x3340,0x0080,0x3340,0x0082,0x08A9,0x0000,0x008B,0x66F8 + .word 0x6100,0xFF12,0x08E9,0x0000,0x008B,0x67F8,0x6100,0xFF06 + .word 0x08A9,0x0000,0x008B,0x6100,0xFF3C,0x303C,0x0040,0x2229 + .word 0x0020,0x0C81,0x5351,0x4552,0x6700,0x0092,0x303C,0x0080 + .word 0x2229,0x0020,0x0C81,0x5344,0x4552,0x6700,0x0080,0x21FC + .word 0x0088,0x02A2,0x0070,0x303C,0x0002,0x7200,0x122D,0x0001 + .word 0x1429,0x0080,0xE14A,0x8242,0x0801,0x000F,0x660A,0x0801 + .word 0x0006,0x6700,0x0058,0x6008,0x0801,0x0006,0x6600,0x004E + .word 0x7020,0x41F9,0x0088,0x0000,0x3C28,0x018E,0x4A46,0x6700 + .word 0x0010,0x3429,0x0028,0x0C42,0x0000,0x67F6,0xB446,0x662C + .word 0x7000,0x2340,0x0028,0x2340,0x002C,0x3E14,0x2C7C,0xFFFF + .word 0xFFC0,0x4CD6,0x7FF9,0x44FC,0x0000,0x6014,0x43F9,0x00A1 + .word 0x5100,0x3340,0x0006,0x303C,0x8000,0x6004,0x44FC,0x0001 + +| At this point (0x800), the Work RAM is clear, the VDP initialized, the +| VRAM/VSRAM/CRAM cleared, the Z80 initialized, the 32X initialized, +| both 32X framebuffers cleared, the 32X palette cleared, the SH2s +| checked for a startup error, the adapter TV mode matches the MD TV +| mode, and the ROM checksum checked. If any error is detected, the +| carry is set, otherwise it is cleared. The 68000 main code is now +| entered. + + jmp __start+0x00880000+0x3F0 + +| 68000 General exception handler at 0x806 + + jmp __except+0x00880000+0x3F0 + +| 68000 Level 4 interrupt handler at 0x80C - HBlank IRQ + + jmp __hblank+0x00880000+0x3F0 + +| 68000 Level 6 interrupt handler at 0x812 - VBlank IRQ + + jmp __vblank+0x00880000+0x3F0 + +__except: + move.l d0,-(sp) + move.l 4(sp),d0 /* jump table return address */ + sub.w #0x206,d0 /* 0 = BusError, 6 = AddrError, etc */ + +| handle exception + + move.l (sp)+,d0 + addq.l #4,sp /* pop jump table return address */ + rte + +__hblank: + rte + +__vblank: + move.l d0,-(sp) + move.l 0xFF0FFC,d0 + beq.b 1f + move.l a0,-(sp) + movea.l d0,a0 + jmp (a0) +1: + move.l (sp)+,d0 + rte + + +__start: + move.b #0,0xA15107 /* clear RV - allow SH2 to access ROM */ +0: + cmp.l #0x4D5F4F4B,0xA15120 /* M_OK */ + bne.b 0b /* wait for master ok */ +1: + cmp.l #0x535F4F4B,0xA15124 /* S_OK */ + bne.b 1b /* wait for slave ok */ + +| init joyports + move.b #0x40,0xA10009 + move.b #0x40,0xA1000B + move.b #0x40,0xA10003 + move.b #0x40,0xA10005 + +| init MD VDP + lea 0xC00004,a0 + move.w #0x8004,(a0) /* reg. 0 - Disable HBL INT */ + move.w #0x8174,(a0) /* reg. 1 - Enable display, VBL INT, DMA + 28 VCell size */ + move.w #0x8230,(a0) /* reg. 2 - Plane A =$30*$400=$C000 */ + move.w #0x832C,(a0) /* reg. 3 - Window =$2C*$400=$B000 */ + move.w #0x8407,(a0) /* reg. 4 - Plane B =$7*$2000=$E000 */ + move.w #0x855E,(a0) /* reg. 5 - sprite table begins at $BC00=$5E*$200 */ + move.w #0x8600,(a0) /* reg. 6 - not used */ + move.w #0x8700,(a0) /* reg. 7 - Background Color number*/ + move.w #0x8800,(a0) /* reg. 8 - not used */ + move.w #0x8900,(a0) /* reg. 9 - not used */ + move.w #0x8A01,(a0) /* reg 10 - HInterrupt timing */ + move.w #0x8B00,(a0) /* reg 11 - $0000abcd a=extr.int b=vscr cd=hscr */ + move.w #0x8C81,(a0) /* reg 12 - hcell mode + shadow/highight + interlaced mode (40 cell, no shadow, no interlace)*/ + move.w #0x8D2E,(a0) /* reg 13 - HScroll Table = $B800 */ + move.w #0x8E00,(a0) /* reg 14 - not used */ + move.w #0x8F02,(a0) /* reg 15 - auto increment data */ + move.w #0x9011,(a0) /* reg 16 - scrl screen v&h size (64x64) */ + move.w #0x9100,(a0) /* reg 17 - window hpos */ + move.w #0x92FF,(a0) /* reg 18 - window vpos */ + +| Copy 68000 main loop to Work RAM to keep contention for the ROM with +| SH2s to a minimum. + lea __m68k_start(pc),a0 + lea 0x00FF1000,a1 + move.w #__m68k_end-__m68k_start-1,d0 +cpyloop: + move.b (a0)+,(a1)+ + dbra d0,cpyloop + + move.w #0,0xA15128 /* controller 1 */ + move.w #0,0xA1512A /* controller 2 */ +| look for mouse + lea 0xA10003,a0 +0: + bsr get_mky + cmpi.l #-2,d0 + beq.b 0b /* timeout */ + cmpi.l #-1,d0 + beq.b 1f /* no mouse */ + move.w #0xF001,0xA15128 /* mouse in port 1 */ +1: + lea 2(a0),a0 +2: + bsr get_mky + cmpi.l #-2,d0 + beq.b 2b /* timeout */ + cmpi.l #-1,d0 + beq.b 3f /* no mouse */ + move.w #0xF001,0xA1512A /* mouse in port 2 */ +3: + move.l #0,0xA1512C /* clear the vblank count */ + +| jump to main loop in Work RAM + jmp 0xFF1000.l + +| this block of code must be pc relative as it's copied into Work RAM + +__m68k_start: + move.b #1,0xA15107 /* set RV */ + move.b #2,0xA130F1 /* SRAM disabled, write protected */ + move.b #0,0xA15107 /* clear RV */ + + move.w 0xA15100,d0 + or.w #0x8000,d0 + move.w d0,0xA15100 /* set FM - allow SH2 access to MARS hw */ + move.l #0,0xA15120 /* let Master SH2 run */ + + lea vert_blank(pc),a0 + move.l a0,0xFF0FFC /* set vertical blank interrupt handler */ + move.w #0x2000,sr /* enable interrupts */ + +main_loop: + move.w 0xA15120,d0 /* get COMM0 */ + bne.b handle_req + +| any other 68000 tasks here + + bra.b main_loop + +| process request from Master SH2 +handle_req: + cmpi.w #0x01FF,d0 + bls read_sram + cmpi.w #0x02FF,d0 + bls write_sram + cmpi.w #0x03FF,d0 + bls start_music + cmpi.w #0x04FF,d0 + bls stop_music + cmpi.w #0x05FF,d0 + bls read_mouse +| unknown command + move.w #0,0xA15120 /* done */ + bra.b main_loop + +read_sram: + move.w #0x2700,sr /* disable ints */ + moveq #0,d1 + moveq #0,d0 + move.w 0xA15122,d0 /* COMM2 holds offset */ + lea 0x200000,a0 + move.b #1,0xA15107 /* set RV */ + move.b #3,0xA130F1 /* SRAM enabled, write protected */ + move.b 1(a0,d0.l),d1 /* read SRAM */ + move.b #2,0xA130F1 /* SRAM disabled, write protected */ + move.b #0,0xA15107 /* clear RV */ + move.w d1,0xA15122 /* COMM2 holds return byte */ + move.w #0,0xA15120 /* done */ + move.w #0x2000,sr /* enable ints */ + bra main_loop + +write_sram: + move.w #0x2700,sr /* disable ints */ + moveq #0,d1 + move.w 0xA15122,d1 /* COMM2 holds offset */ + lea 0x200000,a0 + move.b #1,0xA15107 /* set RV */ + move.b #1,0xA130F1 /* SRAM enabled, write enabled */ + move.b d0,1(a0,d1.l) /* write SRAM */ + move.b #2,0xA130F1 /* SRAM disabled, write protected */ + move.b #0,0xA15107 /* clear RV */ + move.w #0,0xA15120 /* done */ + move.w #0x2000,sr /* enable ints */ + bra main_loop + +set_rom_bank: + move.l a0,d3 + swap d3 + lsr.w #4,d3 + andi.w #3,d3 + move.w d3,0xA15104 /* set ROM bank select */ + move.l a0,d3 + andi.l #0x0FFFFF,d3 + ori.l #0x900000,d3 + movea.l d3,a1 + rts + +start_music: +stop_music: + move.w #0,0xA15120 /* done */ + bra main_loop + +read_mouse: + tst.b d0 + bne.b 1f /* skip port 1 */ + + move.w 0xA15128,d0 + andi.w #0xF001,d0 + cmpi.w #0xF001,d0 + bne.b 1f /* no mouse in port 1 */ + lea 0xA10003,a0 + bsr get_mky + bset #31,d0 + move.w d0,0xA15122 + swap d0 + move.w d0,0xA15120 +0: + move.w 0xA15120,d0 + bne.b 0b /* wait for SH2 to read mouse value */ + bra main_loop +1: + move.w 0xA1512A,d0 + andi.w #0xF001,d0 + cmpi.w #0xF001,d0 + bne.b 3f /* no mouse in port 2 */ + lea 0xA10005,a0 + bsr get_mky + bset #31,d0 + move.w d0,0xA15122 + swap d0 + move.w d0,0xA15120 +2: + move.w 0xA15120,d0 + bne.b 2b /* wait for SH2 to read mouse value */ + bra main_loop +3: + move.l #-1,d0 /* no mouse */ + move.w d0,0xA15122 + swap d0 + move.w d0,0xA15120 +4: + move.w 0xA15120,d0 + bne.b 4b /* wait for SH2 to read mouse value */ + bra main_loop + + +vert_blank: + move.l d1,-(sp) + move.l d2,-(sp) + + /* read controllers */ + move.w 0xA15128,d0 + andi.w #0xF000,d0 + cmpi.w #0xF000,d0 + beq.b 0f /* no pad in port 1 (or mouse) */ + lea 0xA10003,a0 + bsr.b get_pad + move.w d2,0xA15128 /* controller 1 current value */ +0: + move.w 0xA1512A,d0 + andi.w #0xF000,d0 + cmpi.w #0xF000,d0 + beq.b 1f /* no pad in port 2 (or mouse) */ + lea 0xA10005,a0 + bsr.b get_pad + move.w d2,0xA1512A /* controller 2 current value */ +1: + move.l 0xA1512C,d0 + addq.l #1,d0 + move.l d0,0xA1512C /* increment the vblank count */ + + move.l (sp)+,d2 + move.l (sp)+,d1 + movea.l (sp)+,a0 + move.l (sp)+,d0 + rte + +| get current pad value +| entry: a0 = pad control port +| exit: d2 = pad value (0 0 0 1 M X Y Z S A C B R L D U) or (0 0 0 0 0 0 0 0 S A C B R L D U) +get_pad: + bsr.b get_input /* - 0 s a 0 0 d u - 1 c b r l d u */ + move.w d0,d1 + andi.w #0x0C00,d0 + bne.b no_pad + bsr.b get_input /* - 0 s a 0 0 d u - 1 c b r l d u */ + bsr.b get_input /* - 0 s a 0 0 0 0 - 1 c b m x y z */ + move.w d0,d2 + bsr.b get_input /* - 0 s a 1 1 1 1 - 1 c b r l d u */ + andi.w #0x0F00,d0 /* 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 */ + cmpi.w #0x0F00,d0 + beq.b common /* six button pad */ + move.w #0x010F,d2 /* three button pad */ +common: + lsl.b #4,d2 /* - 0 s a 0 0 0 0 m x y z 0 0 0 0 */ + lsl.w #4,d2 /* 0 0 0 0 m x y z 0 0 0 0 0 0 0 0 */ + andi.w #0x303F,d1 /* 0 0 s a 0 0 0 0 0 0 c b r l d u */ + move.b d1,d2 /* 0 0 0 0 m x y z 0 0 c b r l d u */ + lsr.w #6,d1 /* 0 0 0 0 0 0 0 0 s a 0 0 0 0 0 0 */ + or.w d1,d2 /* 0 0 0 0 m x y z s a c b r l d u */ + eori.w #0x1FFF,d2 /* 0 0 0 1 M X Y Z S A C B R L D U */ + rts + +no_pad: + move.w #0xF000,d2 + rts + +| read single phase from controller +get_input: + move.b #0x00,(a0) + nop + nop + move.b (a0),d0 + move.b #0x40,(a0) + lsl.w #8,d0 + move.b (a0),d0 + rts + +| get current mouse value +| entry: a0 = mouse control port +| exit: d0 = mouse value (0 0 0 0 0 0 0 0 YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0) or -2 (timeout) or -1 (no mouse) +get_mky: + move.w sr,d2 + move.w #0x2700,sr /* disable ints */ + + move.b #0x60,6(a0) /* set direction bits */ + nop + nop + move.b #0x60,(a0) /* first phase of mouse packet */ + nop + nop +0: + btst #4,(a0) + beq.b 0b /* wait on handshake */ + move.b (a0),d0 + andi.b #15,d0 + bne mky_err /* not 0 means not mouse */ + + move.b #0x20,(a0) /* next phase */ + move.w #254,d1 /* number retries before timeout */ +1: + btst #4,(a0) + bne.b 2f /* handshake */ + dbra d1,1b + bra timeout_err +2: + move.b (a0),d0 + andi.b #15,d0 + move.b #0,(a0) /* next phase */ + cmpi.b #11,d0 + bne mky_err /* not 11 means not mouse */ +3: + btst #4,(a0) + beq.b 4f /* handshake */ + dbra d1,3b + bra timeout_err +4: + move.b (a0),d0 /* specs say should be 15 */ + nop + nop + move.b #0x20,(a0) /* next phase */ + nop + nop +5: + btst #4,(a0) + bne.b 6f + dbra d1,5b + bra timeout_err +6: + move.b (a0),d0 /* specs say should be 15 */ + nop + nop + move.b #0,(a0) /* next phase */ + moveq #0,d0 /* clear reg to hold packet */ + nop +7: + btst #4,(a0) + beq.b 8f /* handshake */ + dbra d1,7b + bra timeout_err +8: + move.b (a0),d0 /* YO XO YS XS */ + move.b #0x20,(a0) /* next phase */ + lsl.w #8,d0 /* save nibble */ +9: + btst #4,(a0) + bne.b 10f /* handshake */ + dbra d1,9b + bra timeout_err +10: + move.b (a0),d0 /* S M R L */ + move.b #0,(a0) /* next phase */ + lsl.b #4,d0 /* YO XO YS XS S M R L 0 0 0 0 */ + lsl.l #4,d0 /* YO XO YS XS S M R L 0 0 0 0 0 0 0 0 */ +11: + btst #4,(a0) + beq.b 12f /* handshake */ + dbra d1,11b + bra timeout_err +12: + move.b (a0),d0 /* X7 X6 X5 X4 */ + move.b #0x20,(a0) /* next phase */ + lsl.b #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 0 0 0 0 */ + lsl.l #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 0 0 0 0 0 0 0 0 */ +13: + btst #4,(a0) + bne.b 14f /* handshake */ + dbra d1,13b + bra timeout_err +14: + move.b (a0),d0 /* X3 X2 X1 X0 */ + move.b #0,(a0) /* next phase */ + lsl.b #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 0 0 0 0 */ + lsl.l #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 0 0 0 0 0 0 0 0 */ +15: + btst #4,(a0) + beq.b 16f /* handshake */ + dbra d1,15b + bra timeout_err +16: + move.b (a0),d0 /* Y7 Y6 Y5 Y4 */ + move.b #0x20,(a0) /* next phase */ + lsl.b #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 Y7 Y6 Y5 Y4 0 0 0 0 */ + lsl.l #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 Y7 Y6 Y5 Y4 0 0 0 0 0 0 0 0*/ +17: + btst #4,(a0) + beq.b 18f /* handshake */ + dbra d1,17b + bra timeout_err +18: + move.b (a0),d0 /* Y3 Y2 Y1 Y0 */ + move.b #0x60,(a0) /* first phase */ + lsl.b #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 0 0 0 0 */ + lsr.l #4,d0 /* YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ +19: + btst #4,(a0) + beq.b 19b /* wait on handshake */ + + move.w d2,sr /* restore int status */ + rts + +timeout_err: + move.b #0x60,(a0) /* first phase */ + nop + nop +0: + btst #4,(a0) + beq.b 0b /* wait on handshake */ + + move.w d2,sr /* restore int status */ + moveq #-2,d0 + rts + +mky_err: + move.b #0x40,6(a0) /* set direction bits */ + nop + nop + move.b #0x40,(a0) + + move.w d2,sr /* restore int status */ + moveq #-1,d0 + rts + +__m68k_end: + + .align 4 diff --git a/src/platform/32x/crt/sh2_crt0.s b/src/platform/32x/crt/sh2_crt0.s new file mode 100644 index 00000000..d4bdee4b --- /dev/null +++ b/src/platform/32x/crt/sh2_crt0.s @@ -0,0 +1,719 @@ +! SEGA 32X support code for SH2 +! by Chilly Willy +! Rom header and SH2 init/exception code - must be first in object list + + .text + +! Standard MD Header at 0x000 + + .incbin "build/m68k_crt0.bin", 0, 0x3C0 + +! Standard Mars Header at 0x3C0 + + .ascii "OpenLara " /* module name */ + .long 0x00000000 /* version */ + .long __text_size /* Source (in ROM) */ + .long 0x00000000 /* Destination (in SDRAM) */ + .long __data_size /* Size */ + .long 0x06000240 /* Master SH2 Jump */ + .long 0x06000244 /* Slave SH2 Jump */ + .long 0x06000000 /* Master SH2 VBR */ + .long 0x06000120 /* Slave SH2 VBR */ + +! Standard MD startup code at 0x3F0 + + .incbin "build/m68k_crt1.bin" + + + .data + +! Master Vector Base Table at 0x06000000 + + .long mstart /* Cold Start PC */ + .long 0x0603FC00 /* Cold Start SP */ + .long mstart /* Manual Reset PC */ + .long 0x0603FC00 /* Manual Reset SP */ + .long main_err /* Illegal instruction */ + .long 0x00000000 /* reserved */ + .long main_err /* Invalid slot instruction */ + .long 0x20100400 /* reserved */ + .long 0x20100420 /* reserved */ + .long main_err /* CPU address error */ + .long main_err /* DMA address error */ + .long main_err /* NMI vector */ + .long main_err /* User break vector */ + .space 76 /* reserved */ + .long main_err /* TRAPA #32 */ + .long main_err /* TRAPA #33 */ + .long main_err /* TRAPA #34 */ + .long main_err /* TRAPA #35 */ + .long main_err /* TRAPA #36 */ + .long main_err /* TRAPA #37 */ + .long main_err /* TRAPA #38 */ + .long main_err /* TRAPA #39 */ + .long main_err /* TRAPA #40 */ + .long main_err /* TRAPA #41 */ + .long main_err /* TRAPA #42 */ + .long main_err /* TRAPA #43 */ + .long main_err /* TRAPA #44 */ + .long main_err /* TRAPA #45 */ + .long main_err /* TRAPA #46 */ + .long main_err /* TRAPA #47 */ + .long main_err /* TRAPA #48 */ + .long main_err /* TRAPA #49 */ + .long main_err /* TRAPA #50 */ + .long main_err /* TRAPA #51 */ + .long main_err /* TRAPA #52 */ + .long main_err /* TRAPA #53 */ + .long main_err /* TRAPA #54 */ + .long main_err /* TRAPA #55 */ + .long main_err /* TRAPA #56 */ + .long main_err /* TRAPA #57 */ + .long main_err /* TRAPA #58 */ + .long main_err /* TRAPA #59 */ + .long main_err /* TRAPA #60 */ + .long main_err /* TRAPA #61 */ + .long main_err /* TRAPA #62 */ + .long main_err /* TRAPA #63 */ + .long main_irq /* Level 1 IRQ */ + .long main_irq /* Level 2 & 3 IRQ's */ + .long main_irq /* Level 4 & 5 IRQ's */ + .long main_irq /* PWM interupt */ + .long main_irq /* Command interupt */ + .long main_irq /* H Blank interupt */ + .long main_irq /* V Blank interupt */ + .long main_irq /* Reset Button */ + +! Slave Vector Base Table at 0x06000120 + + .long sstart /* Cold Start PC */ + .long 0x06040000 /* Cold Start SP */ + .long sstart /* Manual Reset PC */ + .long 0x06040000 /* Manual Reset SP */ + .long slav_err /* Illegal instruction */ + .long 0x00000000 /* reserved */ + .long slav_err /* Invalid slot instruction */ + .long 0x20100400 /* reserved */ + .long 0x20100420 /* reserved */ + .long slav_err /* CPU address error */ + .long slav_err /* DMA address error */ + .long slav_err /* NMI vector */ + .long slav_err /* User break vector */ + .space 76 /* reserved */ + .long slav_err /* TRAPA #32 */ + .long slav_err /* TRAPA #33 */ + .long slav_err /* TRAPA #34 */ + .long slav_err /* TRAPA #35 */ + .long slav_err /* TRAPA #36 */ + .long slav_err /* TRAPA #37 */ + .long slav_err /* TRAPA #38 */ + .long slav_err /* TRAPA #39 */ + .long slav_err /* TRAPA #40 */ + .long slav_err /* TRAPA #41 */ + .long slav_err /* TRAPA #42 */ + .long slav_err /* TRAPA #43 */ + .long slav_err /* TRAPA #44 */ + .long slav_err /* TRAPA #45 */ + .long slav_err /* TRAPA #46 */ + .long slav_err /* TRAPA #47 */ + .long slav_err /* TRAPA #48 */ + .long slav_err /* TRAPA #49 */ + .long slav_err /* TRAPA #50 */ + .long slav_err /* TRAPA #51 */ + .long slav_err /* TRAPA #52 */ + .long slav_err /* TRAPA #53 */ + .long slav_err /* TRAPA #54 */ + .long slav_err /* TRAPA #55 */ + .long slav_err /* TRAPA #56 */ + .long slav_err /* TRAPA #57 */ + .long slav_err /* TRAPA #58 */ + .long slav_err /* TRAPA #59 */ + .long slav_err /* TRAPA #60 */ + .long slav_err /* TRAPA #61 */ + .long slav_err /* TRAPA #62 */ + .long slav_err /* TRAPA #63 */ + .long slav_irq /* Level 1 IRQ */ + .long slav_irq /* Level 2 & 3 IRQ's */ + .long slav_irq /* Level 4 & 5 IRQ's */ + .long slav_irq /* PWM interupt */ + .long slav_irq /* Command interupt */ + .long slav_irq /* H Blank interupt */ + .long slav_irq /* V Blank interupt */ + .long slav_irq /* Reset Button */ + +! The main SH2 starts here at 0x06000240 + +mstart: + bra mcont + nop + +! The slave SH2 starts here at 0x06000244 + +sstart: + bra scont + nop + +! Each section of code below has its own data table so that the code +! can be extended without worrying about the offsets becoming too big. +! This results in duplicate entries, but not so many that we care. :) + +mcont: +! clear interrupt flags + mov.l _master_int_clr,r1 + mov.w r0,@-r1 /* PWM INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* CMD INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* H INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* V INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* VRES INT clear */ + mov.w r0,@r1 + + mov.l _master_stk,r15 +! purge cache and turn it off + mov.l _master_cctl,r0 + mov #0x10,r1 + mov.b r1,@r0 + +! clear bss + mov #0,r0 + mov.l _master_bss_start,r1 + mov.l _master_bss_end,r2 +0: + mov.l r0,@r1 + cmp/eq r1,r2 + bf/s 0b + add #4,r1 + +! wait for 68000 to finish init + mov.l _master_sts,r0 + mov.l _master_ok,r1 +1: + mov.l @r0,r2 + nop + nop + cmp/eq r1,r2 + bt 1b + +! let Slave SH2 run + mov #0,r1 + mov.l r1,@(4,r0) /* clear slave status */ + + mov #0x80,r0 + mov.l _master_adapter,r1 + mov.b r0,@r1 /* set FM */ + mov #0x00,r0 + mov.b r0,@(1,r1) /* set int enables */ + mov #0x20,r0 + ldc r0,sr /* allow ints */ + +! purge cache, turn it on, and run main() + mov.l _master_cctl,r0 + mov #0x11,r1 + mov.b r1,@r0 + mov.l _master_go,r0 + jmp @r0 + nop + + .align 2 +_master_int_clr: + .long 0x2000401E /* one word passed last int clr reg */ +_master_stk: + .long 0x0603FC00 /* Cold Start SP */ +_master_sts: + .long 0x20004020 +_master_ok: + .ascii "M_OK" +_master_adapter: + .long 0x20004000 +_master_cctl: + .long 0xFFFFFE92 +_master_go: + .long _main + +_master_bss_start: + .long __bss_start +_master_bss_end: + .long __bss_end + +scont: +! clear interrupt flags + mov.l _slave_int_clr,r1 + mov.w r0,@-r1 /* PWM INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* CMD INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* H INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* V INT clear */ + mov.w r0,@r1 + mov.w r0,@-r1 /* VRES INT clear */ + mov.w r0,@r1 + + mov.l _slave_stk,r15 +! wait for Master SH2 and 68000 to finish init + mov.l _slave_sts,r0 + mov.l _slave_ok,r1 +1: + mov.l @r0,r2 + nop + nop + cmp/eq r1,r2 + bt 1b + + mov.l _slave_adapter,r1 + mov #0x00,r0 + mov.b r0,@(1,r1) /* set int enables (different from master despite same address!) */ + mov #0x20,r0 + ldc r0,sr /* allow ints */ + +! purge cache, turn it on, and run slave() + mov.l _slave_cctl,r0 + mov #0x11,r1 + mov.b r1,@r0 + mov.l _slave_go,r0 + jmp @r0 + nop + + .align 2 +_slave_int_clr: + .long 0x2000401E /* one word passed last int clr reg */ +_slave_stk: + .long 0x06040000 /* Cold Start SP */ +_slave_sts: + .long 0x20004024 +_slave_ok: + .ascii "S_OK" +_slave_adapter: + .long 0x20004000 +_slave_cctl: + .long 0xFFFFFE92 +_slave_go: + .long _slave + +! Master exception handler + +main_err: + rte + nop + +! Master IRQ handler + +main_irq: + mov.l r0,@-r15 + + stc sr,r0 /* SR holds IRQ level in I3-I0 */ + shlr2 r0 + and #0x38,r0 + cmp/eq #0x28,r0 + bt main_h_irq + cmp/eq #0x18,r0 + bt main_pwm_irq + cmp/eq #0x30,r0 + bt main_v_irq + cmp/eq #0x20,r0 + bt main_cmd_irq + cmp/eq #0x38,r0 + bt main_vres_irq + + mov.l @r15+,r0 + rte + nop + +main_v_irq: + mov.l r1,@-r15 + + mov.l mvi_mars_adapter,r1 + mov.w r0,@(0x16,r1) /* clear V IRQ */ + nop + nop + nop + nop + + ! handle V IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +mvi_mars_adapter: + .long 0x20004000 + +main_h_irq: + mov.l r1,@-r15 + + mov.l mhi_mars_adapter,r1 + mov.w r0,@(0x18,r1) /* clear H IRQ */ + nop + nop + nop + nop + + ! handle H IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +mhi_mars_adapter: + .long 0x20004000 + +main_cmd_irq: + mov.l r1,@-r15 + + mov.l mci_mars_adapter,r1 + mov.w r0,@(0x1A,r1) /* clear CMD IRQ */ + nop + nop + nop + nop + + ! handle CMD IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +mci_mars_adapter: + .long 0x20004000 + +main_pwm_irq: + mov.l r1,@-r15 + + mov.l mpi_mars_adapter,r1 + mov.w r0,@(0x1C,r1) /* clear PWM IRQ */ + nop + nop + nop + nop + + ! handle PWM IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +mpi_mars_adapter: + .long 0x20004000 + +main_vres_irq: + mov.l mvri_mars_adapter,r1 + mov.w r0,@(0x14,r1) /* clear VRES IRQ */ + nop + nop + nop + nop + + mov #0x0F,r0 + shll2 r0 + shll2 r0 + ldc r0,sr /* disallow ints */ + + mov.l mvri_master_stk,r15 + mov.l mvri_master_vres,r0 + jmp @r0 + nop + + .align 2 +mvri_mars_adapter: + .long 0x20004000 +mvri_master_stk: + .long 0x0603FC00 /* Cold Start SP */ +mvri_master_vres: + .long main_reset + +! Slave exception handler + +slav_err: + rte + nop + +! Slave IRQ handler + +slav_irq: + mov.l r0,@-r15 + + stc sr,r0 /* SR holds IRQ level I3-I0 */ + shlr2 r0 + and #0x38,r0 + cmp/eq #0x28,r0 + bt slav_h_irq + cmp/eq #0x18,r0 + bt slav_pwm_irq + cmp/eq #0x30,r0 + bt slav_v_irq + cmp/eq #0x20,r0 + bt slav_cmd_irq + cmp/eq #0x38,r0 + bt slav_vres_irq + + mov.l @r15+,r0 + rte + nop + +slav_v_irq: + mov.l r1,@-r15 + + mov.l svi_mars_adapter,r1 + mov.w r0,@(0x16,r1) /* clear V IRQ */ + nop + nop + nop + nop + + ! handle V IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +svi_mars_adapter: + .long 0x20004000 + +slav_h_irq: + mov.l r1,@-r15 + + mov.l shi_mars_adapter,r1 + mov.w r0,@(0x18,r1) /* clear H IRQ */ + nop + nop + nop + nop + + ! handle H IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +shi_mars_adapter: + .long 0x20004000 + +slav_cmd_irq: + mov.l r1,@-r15 + + mov.l sci_mars_adapter,r1 + mov.w r0,@(0x1A,r1) /* clear CMD IRQ */ + nop + nop + nop + nop + + ! handle CMD IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +sci_mars_adapter: + .long 0x20004000 + +slav_pwm_irq: + mov.l r1,@-r15 + + mov.l spi_mars_adapter,r1 + mov.w r0,@(0x1C,r1) /* clear PWM IRQ */ + nop + nop + nop + nop + + ! handle PWM IRQ + + mov.l @r15+,r1 + mov.l @r15+,r0 + rte + nop + + .align 2 +spi_mars_adapter: + .long 0x20004000 + +slav_vres_irq: + mov.l svri_mars_adapter,r1 + mov.w r0,@(0x14,r1) /* clear VRES IRQ */ + nop + nop + nop + nop + + mov #0x0F,r0 + shll2 r0 + shll2 r0 + ldc r0,sr /* disallow ints */ + + mov.l svri_slave_stk,r15 + mov.l svri_slave_vres,r0 + jmp @r0 + nop + + .align 2 +svri_mars_adapter: + .long 0x20004000 +svri_slave_stk: + .long 0x06040000 /* Cold Start SP */ +svri_slave_vres: + .long slav_reset + + +! Fast memcpy function - copies longs, runs from sdram for speed +! On entry: r4 = dst, r5 = src, r6 = len (in longs) + + .align 4 + .global _fast_memcpy +_fast_memcpy: + mov.l @r5+,r3 + mov.l r3,@r4 + dt r6 + bf/s _fast_memcpy + add #4,r4 + rts + nop + +! Fast memset function - sets long values, runs from sdram for speed +! On entry: r4 = dst, r5 = value, r6 = len (in longs) + + .align 4 + .global _fast_memset +_fast_memset: + mov.l r5,@r4 + dt r6 + bf/s _fast_memset + add #4,r4 + rts + nop + +! Cache clear line function +! On entry: r4 = ptr - should be 16 byte aligned + + .align 4 + .global _CacheClearLine +_CacheClearLine: + mov.l _cache_flush,r0 + or r0,r4 + mov #0,r0 + mov.l r0,@r4 + rts + nop + + .align 2 + +_cache_flush: + .long 0x40000000 + +! Cache control function +! On entry: r4 = cache mode => 0x10 = CP, 0x08 = TW, 0x01 = CE + + .align 4 + .global _CacheControl +_CacheControl: + mov.l _sh2_cctl,r0 + mov.b r4,@r0 + rts + nop + + .align 2 + +_sh2_cctl: + .long 0xFFFFFE92 + +main_reset: +! do any master SH2 specific reset code here + + mov.l slav_st,r0 + mov.l slav_ok,r1 +0: + mov.l @r0,r2 + nop + nop + cmp/eq r1,r2 + bf 0b /* wait for slave */ + + ! recopy rom data to sdram + + mov.l rom_header,r1 + mov.l @r1,r2 /* src relative to start of rom */ + mov.l @(4,r1),r3 /* dst relative to start of sdram */ + mov.l @(8,r1),r4 /* size (longword aligned) */ + mov.l rom_start,r1 + add r1,r2 + mov.l sdram_start,r1 + add r1,r3 + shlr2 r4 /* number of longs */ + add #-1,r4 +1: + mov.l @r2+,r0 + mov.l r0,@r3 + add #4,r3 + dt r4 + bf 1b + + mov.l main_st,r0 + mov.l main_ok,r1 + mov.l r1,@r0 /* tell everyone reset complete */ + + mov.l main_go,r0 + jmp @r0 + nop + +slav_reset: +! do any slave SH2 specific reset code here + + mov.l slav_st,r0 + mov.l slav_ok,r1 + mov.l r1,@r0 /* tell master to start reset */ + + mov.l main_st,r0 + mov.l main_ok,r1 +0: + mov.l @r0,r2 + nop + nop + cmp/eq r1,r2 + bf 0b /* wait for master to do the work */ + + mov.l slav_go,r0 + jmp @r0 + nop + + .align 2 +main_st: + .long 0x20004020 +main_ok: + .ascii "M_OK" +main_go: + .long mstart +rom_header: + .long 0x220003D4 +rom_start: + .long 0x22000000 +sdram_start: + .long 0x26000000 + +slav_st: + .long 0x20004024 +slav_ok: + .ascii "S_OK" +slav_go: + .long sstart + + + .global _start +_start: + diff --git a/src/platform/32x/deploy.sh b/src/platform/32x/deploy.sh new file mode 100644 index 00000000..285a2344 --- /dev/null +++ b/src/platform/32x/deploy.sh @@ -0,0 +1,3 @@ +make clean +make +/c/RetroArch/retroarch.exe -L C:\\RetroArch\\cores\\picodrive_libretro.dll OpenLara.32x diff --git a/src/platform/32x/main.cpp b/src/platform/32x/main.cpp new file mode 100644 index 00000000..0bf4ce7c --- /dev/null +++ b/src/platform/32x/main.cpp @@ -0,0 +1,149 @@ +const void* TRACKS_IMA; +const void* TITLE_SCR; + +extern void* LEVEL1_PKD; + +#include "game.h" + +int32 fps; + +void osSetPalette(const uint16* palette) +{ + void *dst = (void*)&MARS_CRAM; + memcpy(dst, palette, 256 * 2); +} + +int32 osGetSystemTimeMS() +{ + return 0; +} + +bool osSaveSettings() +{ + return false; +} + +bool osLoadSettings() +{ + return false; +} + +bool osCheckSave() +{ + return false; +} + +bool osSaveGame() +{ + return false; +} + +bool osLoadGame() +{ + return false; +} + +void osJoyVibrate(int32 index, int32 L, int32 R) +{ + // nope +} + +void updateInput() +{ + keys = 0; + + uint16 mask = MARS_SYS_COMM8; + if (mask & SEGA_CTRL_UP) keys |= IK_UP; + if (mask & SEGA_CTRL_RIGHT) keys |= IK_RIGHT; + if (mask & SEGA_CTRL_DOWN) keys |= IK_DOWN; + if (mask & SEGA_CTRL_LEFT) keys |= IK_LEFT; + if (mask & SEGA_CTRL_A) keys |= IK_A; + if (mask & SEGA_CTRL_B) keys |= IK_B; + if (mask & SEGA_CTRL_C) keys |= IK_C; + if (mask & SEGA_CTRL_X) keys |= IK_X; + if (mask & SEGA_CTRL_Y) keys |= IK_Y; + if (mask & SEGA_CTRL_Z) keys |= IK_Z; + if (mask & SEGA_CTRL_START) keys |= IK_START; + if (mask & SEGA_CTRL_MODE) keys |= IK_SELECT; +} + +void* osLoadLevel(const char* name) +{ + return (void*)LEVEL1_PKD; +} + +uint16 pageIndex = 0; + +void pageWait() +{ + while ((MARS_VDP_FBCTL & MARS_VDP_FS) != pageIndex); +} + +void pageFlip() +{ + pageIndex ^= 1; + MARS_VDP_FBCTL = pageIndex; +} + +extern "C" void slave(void) +{ + // TODO +} + +int main() +{ + while ((MARS_SYS_INTMSK & MARS_SH2_ACCESS_VDP) == 0); + MARS_VDP_DISPMODE = MARS_224_LINES | MARS_VDP_MODE_256; + + for (int32 page = 0; page < 2; page++) + { + pageFlip(); + pageWait(); + + volatile uint16* lineTable = &MARS_FRAMEBUFFER; + uint16 wordOffset = 0x100; + for (int32 i = 0; i < 224; i++, wordOffset += FRAME_WIDTH / 2) + { + lineTable[i] = wordOffset; + } + + uint8* fb = (uint8*)(lineTable + 0x100); + memset(fb, 0, 0x10000 - 0x200); + } + + gameInit(gLevelInfo[gLevelID].name); + + int32 lastFrame = (MARS_SYS_COMM12 >> 1) - 1; + + while (1) + { + int32 frame = MARS_SYS_COMM12; + + if (!(MARS_VDP_DISPMODE & MARS_NTSC_FORMAT)) + { + frame = frame * 6 / 5; // PAL fix + } + + frame >>= 1; + + int32 delta = frame - lastFrame; + + if (!delta) + continue; + + lastFrame = frame; + + updateInput(); + + gameUpdate(delta); + + pageWait(); + + gameRender(); + + pageFlip(); + } + + return 0; +} + diff --git a/src/platform/32x/rasterizer.h b/src/platform/32x/rasterizer.h new file mode 100644 index 00000000..f8a49888 --- /dev/null +++ b/src/platform/32x/rasterizer.h @@ -0,0 +1,809 @@ +#ifndef H_RASTERIZER_MODE13 +#define H_RASTERIZER_MODE13 + +#include "common.h" + +extern uint8 gLightmap[256 * 32]; +extern const uint8* gTile; + +#define rasterizeS rasterizeS_c +#define rasterizeF rasterizeF_c +#define rasterizeG rasterizeG_c +#define rasterizeFT rasterizeFT_c +#define rasterizeGT rasterizeGT_c +#define rasterizeFTA rasterizeFTA_c +#define rasterizeGTA rasterizeGTA_c +#define rasterizeSprite rasterizeSprite_c +#define rasterizeLineH rasterizeLineH_c +#define rasterizeLineV rasterizeLineV_c +#define rasterizeFillS rasterizeFillS_c + +void rasterizeS_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[0x1A00]; + + int32 Lh = 0; + int32 Rh = 0; + int32 Ldx = 0; + int32 Rdx = 0; + int32 Rx; + int32 Lx; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + + if (Lh > 1) + { + uint32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + + if (Rh > 1) { + uint32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + volatile uint8* ptr = (uint8*)pixel + x1; + + if (x1 & 1) + { + ptr[0] = ft_lightmap[ptr[0]]; + ptr++; + width--; + } + + if (width & 1) + { + width--; + ptr[width] = ft_lightmap[ptr[width]]; + } + + while (width) + { + uint16 p = *(uint16*)ptr; + + uint16 index = ft_lightmap[p & 0xFF]; + index |= ft_lightmap[p >> 8] << 8; + + *(uint16*)ptr = index; + ptr += 2; + width -= 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + } + } +} + +void rasterizeF_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + uint32 color = (uint32)R; + color = gLightmap[(L->v.g << 8) | color]; + color |= (color << 8); + + int32 Lh = 0; + int32 Rh = 0; + int32 Ldx = 0; + int32 Rdx = 0; + int32 Rx; + int32 Lx; + + R = L; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + ASSERT(L->v.y >= 0); + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + + if (Lh > 1) + { + uint32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + ASSERT(R->v.y >= 0); + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + + if (Rh > 1) { + uint32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = uint8(color); + width--; + } + + if (width & 1) + { + ptr[width - 1] = uint8(color); + } + + if (width & 2) + { + *(uint16*)ptr = color; + ptr += 2; + } + + width >>= 2; + while (width--) + { + *(uint16*)ptr = color; + ptr += 2; + *(uint16*)ptr = color; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + } + } +} + +void rasterizeG_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + int32 Lg, Rg, Ldg = 0, Rdg = 0; + + const uint8* ft_lightmap = gLightmap + L->t.t; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lg = L->v.g; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + Ldg = tmp * (N->v.g - Lg); + } + + Lx <<= 16; + Lg <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rg = R->v.g; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + Rdg = tmp * (N->v.g - Rg); + } + + Rx <<= 16; + Rg <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + int32 tmp = FixedInvU(width); + + int32 dgdx = tmp * ((Rg - Lg) >> 5) >> 10; + + int32 g = Lg; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = ft_lightmap[g >> 16 << 8]; + g += dgdx >> 1; + width--; + } + + if (width & 1) + { + ptr[width - 1] = ft_lightmap[Rg >> 16 << 8]; + } + + if (width & 2) + { + uint8 p = ft_lightmap[g >> 16 << 8]; + g += dgdx; + *(uint16*)ptr = p | (p << 8); + ptr += 2; + } + + width >>= 2; + while (width--) + { + uint8 p; + + p = ft_lightmap[g >> 16 << 8]; + *(uint16*)ptr = p | (p << 8); + g += dgdx; + ptr += 2; + + p = ft_lightmap[g >> 16 << 8]; + *(uint16*)ptr = p | (p << 8); + g += dgdx; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lg += Ldg; + Rg += Rdg; + } + } +} + +void rasterizeFT_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[L->v.g << 8]; + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + uint32 tmp = FixedInvU(width); + + uint32 duv = Rt - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16); + + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + ptr[width - 1] = ft_lightmap[gTile[(tmp & 0xFF00) | (tmp >> 24)]]; + } + + width >>= 1; + while (width--) + { + uint8 indexA = ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + uint8 indexB = ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + + #ifdef CPU_BIG_ENDIAN + *(uint16*)ptr = indexB | (indexA << 8); + #else + *(uint16*)ptr = indexA | (indexB << 8); + #endif + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeGT_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ +#ifdef ALIGNED_LIGHTMAP + ASSERT((intptr_t(gLightmap) & 0xFFFF) == 0); // lightmap should be 64k aligned +#endif + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + // 8-bit fractional part precision for Gouraud component + // has some artifacts but allow to save one reg for inner loop + // for aligned by 64k address of lightmap array + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lg = L->v.g; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + Ldg = tmp * (N->v.g - Lg) >> 8; + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + Lg <<= 8; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rg = R->v.g; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + Rdg = tmp * (N->v.g - Rg) >> 8; + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + Rg <<= 8; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + int32 tmp = FixedInvU(width); + + int32 dgdx = tmp * (Rg - Lg) >> 15; + + uint32 duv = Rt - Lt; + uint32 u = tmp * int16(duv >> 16); + uint32 v = tmp * int16(duv); + uint32 dtdx = (u & 0xFFFF0000) | (v >> 16); + + int32 g = Lg; + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + g += dgdx >> 1; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + ptr[width - 1] = gLightmap[(Rg >> 8 << 8) | gTile[(tmp & 0xFF00) | (tmp >> 24)]]; + } + + #ifdef ALIGNED_LIGHTMAP + g += intptr_t(gLightmap); + #endif + + width >>= 1; + + while (width--) + { + #ifdef ALIGNED_LIGHTMAP + const uint8* LMAP = (uint8*)(g >> 8 << 8); + + uint8 indexA = LMAP[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + uint8 indexB = LMAP[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + g += dgdx; + #else + uint8 indexA = gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + uint8 indexB = gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + g += dgdx; + #endif + + #ifdef CPU_BIG_ENDIAN + *(uint16*)ptr = indexB | (indexA << 8); + #else + *(uint16*)ptr = indexA | (indexB << 8); + #endif + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lg += Ldg; + Rg += Rdg; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeFTA_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[L->v.g << 8]; + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + uint32 tmp = FixedInvU(width); + + uint32 duv = Rt - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16); + + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + uint8 p = gTile[(t & 0xFF00) | (t >> 24)]; + if (p) { + *ptr = ft_lightmap[p]; + } + ptr++; + t += dtdx; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + uint8 p = gTile[(tmp & 0xFF00) | (tmp >> 24)]; + if (p) { + ptr[width - 1] = ft_lightmap[p]; + } + } + + width >>= 1; + while (width--) + { + uint8 indexA = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + uint8 indexB = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + + + if (indexA && indexB) + { + indexA = ft_lightmap[indexA]; + indexB = ft_lightmap[indexB]; + + #ifdef CPU_BIG_ENDIAN + *(uint16*)ptr = indexB | (indexA << 8); + #else + *(uint16*)ptr = indexA | (indexB << 8); + #endif + + }/* else if (indexA) { + *(uint16*)ptr = (*(uint16*)ptr & 0xFF00) | ft_lightmap[indexA]; + } else if (indexB) { + *(uint16*)ptr = (*(uint16*)ptr & 0x00FF) | (ft_lightmap[indexB] << 8); + }*/ + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeGTA_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + rasterizeFTA(pixel, L, R); +} + +void rasterizeSprite_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +void rasterizeLineH_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +void rasterizeLineV_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +void rasterizeFillS_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +#endif diff --git a/src/platform/32x/render.cpp b/src/platform/32x/render.cpp new file mode 100644 index 00000000..0e719712 --- /dev/null +++ b/src/platform/32x/render.cpp @@ -0,0 +1,1320 @@ +#include "common.h" + +struct Vertex +{ + int16 x; + int16 y; + int16 z; + uint8 g; + uint8 clip; +}; + +struct VertexLink +{ + Vertex v; + TexCoord t; + int8 prev; + int8 next; + uint16 padding; +}; + +struct ViewportRel { + int32 minXY; + int32 maxXY; +}; + +ViewportRel viewportRel; + +#if defined(_WIN32) + uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#elif defined(__GBA__) + uint32 fb = MEM_VRAM; +#elif defined(__TNS__) + uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#elif defined(__DOS__) + uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#elif defined(__32X__) + #define fb ((uint8*)&MARS_FRAMEBUFFER + 0x200) +#endif + +enum FaceType { + FACE_TYPE_SHADOW, + FACE_TYPE_F, + FACE_TYPE_FT, + FACE_TYPE_FTA, + FACE_TYPE_GT, + FACE_TYPE_GTA, + FACE_TYPE_SPRITE, + FACE_TYPE_FILL_S, + FACE_TYPE_LINE_H, + FACE_TYPE_LINE_V, + FACE_TYPE_MAX +}; + +#define FACE_TRIANGLE (1 << 19) +#define FACE_CLIPPED (1 << 18) +#define FACE_TYPE_SHIFT 14 +#define FACE_TYPE_MASK 15 +#define FACE_GOURAUD (2 << FACE_TYPE_SHIFT) +#define FACE_TEXTURE 0x3FFF + +#include "rasterizer.h" + +extern Level level; + +const uint8* gTile; + +Vertex* gVerticesBase; +Face* gFacesBase; + +//EWRAM_DATA uint8 gBackgroundCopy[FRAME_WIDTH * FRAME_HEIGHT]; // EWRAM 37.5k +EWRAM_DATA ALIGN8 Vertex gVertices[MAX_VERTICES]; // EWRAM 16k +EWRAM_DATA Face gFaces[MAX_FACES]; // EWRAM 30k +Face* gOT[OT_SIZE]; // IWRAM 2.5k + +enum ClipFlags { + CLIP_LEFT = 1 << 0, + CLIP_RIGHT = 1 << 1, + CLIP_TOP = 1 << 2, + CLIP_BOTTOM = 1 << 3, + CLIP_FAR = 1 << 4, + CLIP_NEAR = 1 << 5, + CLIP_MASK_VP = (CLIP_LEFT | CLIP_RIGHT | CLIP_TOP | CLIP_BOTTOM), +}; + +const MeshQuad gShadowQuads[] = { + { (FACE_TYPE_SHADOW << FACE_TYPE_SHIFT), {0, 1, 2, 7} }, + { (FACE_TYPE_SHADOW << FACE_TYPE_SHIFT), {7, 2, 3, 6} }, + { (FACE_TYPE_SHADOW << FACE_TYPE_SHIFT), {6, 3, 4, 5} } +}; + +void setViewport(const RectMinMax &vp) +{ + viewport = vp; + + int32 minX = vp.x0 - (FRAME_WIDTH >> 1); + int32 minY = vp.y0 - (FRAME_HEIGHT >> 1); + int32 maxX = vp.x1 - (FRAME_WIDTH >> 1); + int32 maxY = vp.y1 - (FRAME_HEIGHT >> 1); + + viewportRel.minXY = (minX << 16) | (minY & 0xFFFF); + viewportRel.maxXY = (maxX << 16) | (maxY & 0xFFFF); +} + +void setPaletteIndex(int32 index) +{ + // TODO +} + +X_INLINE Face* faceAdd(int32 depth) +{ + ASSERT(depth >= 0 && depth < OT_SIZE); + + Face* face = gFacesBase++; + face->next = gOT[depth]; + gOT[depth] = face; + + return face; +} + +extern "C" { + X_NOINLINE void drawPoly(uint32 flags, VertexLink* v); + X_NOINLINE void drawTriangle(uint32 flags, VertexLink* v); + X_NOINLINE void drawQuad(uint32 flags, VertexLink* v); +} + +#ifdef USE_ASM + #define transformRoom transformRoom_asm + #define transformRoomUW transformRoomUW_asm + #define transformMesh transformMesh_asm + #define faceAddRoomQuads faceAddRoomQuads_asm + #define faceAddRoomTriangles faceAddRoomTriangles_asm + #define faceAddMeshQuads faceAddMeshQuads_asm + #define faceAddMeshTriangles faceAddMeshTriangles_asm + #define rasterize rasterize_asm + + extern "C" { + void transformRoom_asm(const RoomVertex* vertices, int32 count); + void transformRoomUW_asm(const RoomVertex* vertices, int32 count); + void transformMesh_asm(const MeshVertex* vertices, int32 count, int32 intensity); + void faceAddRoomQuads_asm(const RoomQuad* polys, int32 count); + void faceAddRoomTriangles_asm(const RoomTriangle* polys, int32 count); + void faceAddMeshQuads_asm(const MeshQuad* polys, int32 count); + void faceAddMeshTriangles_asm(const MeshTriangle* polys, int32 count); + void rasterize_asm(uint32 flags, VertexLink* top); + } +#else + #define transformRoom transformRoom_c + #define transformRoomUW transformRoomUW_c + #define transformMesh transformMesh_c + #define faceAddRoomQuads faceAddRoomQuads_c + #define faceAddRoomTriangles faceAddRoomTriangles_c + #define faceAddMeshQuads faceAddMeshQuads_c + #define faceAddMeshTriangles faceAddMeshTriangles_c + #define rasterize rasterize_c + +X_INLINE bool checkBackface(const Vertex *a, const Vertex *b, const Vertex *c) +{ + return (b->x - a->x) * (c->y - a->y) <= (c->x - a->x) * (b->y - a->y); +} + +void transformRoom_c(const RoomVertex* vertices, int32 count) +{ + Vertex* res = gVerticesBase; + + for (int32 i = 0; i < count; i++, res++) + { + uint32 value = *(uint32*)(vertices++); + + int32 vx = (value & (0xFF)) << 10; + int32 vy = (value & (0xFF << 8)); + int32 vz = (value & (0xFF << 16)) >> 6; + int32 vg = (value & (0xFF << 24)) >> (24 - 5); + + const Matrix &m = matrixGet(); + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, vx, vy, vz); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, vx, vy, vz); + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, vx, vy, vz); + + uint32 clip = 0; + + if (z <= VIEW_MIN_F) { + clip = CLIP_NEAR; + z = VIEW_MIN_F; + } + + if (z >= VIEW_MAX_F) { + clip = CLIP_FAR; + z = VIEW_MAX_F; + } + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + if (z > FOG_MIN) + { + vg += (z - FOG_MIN) << FOG_SHIFT; + if (vg > 8191) { + vg = 8191; + } + } + + PERSPECTIVE(x, y, z); + + // use this in case of overflow + //x = X_CLAMP(x, -512, 512); + //y = X_CLAMP(y, -512, 512); + + x += (FRAME_WIDTH >> 1); + y += (FRAME_HEIGHT >> 1); + + if (x < viewport.x0) clip |= CLIP_LEFT; + if (x > viewport.x1) clip |= CLIP_RIGHT; + if (y < viewport.y0) clip |= CLIP_TOP; + if (y > viewport.y1) clip |= CLIP_BOTTOM; + + res->x = x; + res->y = y; + res->z = z; + res->g = vg >> 8; + res->clip = clip; + } +} + +void transformRoomUW_c(const RoomVertex* vertices, int32 count) +{ + Vertex* res = gVerticesBase; + + for (int32 i = 0; i < count; i++, res++) + { + uint32 value = *(uint32*)(vertices++); + + int32 vx = (value & (0xFF)) << 10; + int32 vy = (value & (0xFF << 8)); + int32 vz = (value & (0xFF << 16)) >> 6; + int32 vg = (value & (0xFF << 24)) >> (24 - 5); + + const Matrix &m = matrixGet(); + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, vx, vy, vz); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, vx, vy, vz); + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, vx, vy, vz); + + uint32 clip = 0; + + if (z <= VIEW_MIN_F) { + clip = CLIP_NEAR; + z = VIEW_MIN_F; + } + + if (z >= VIEW_MAX_F) { + clip = CLIP_FAR; + z = VIEW_MAX_F; + } + + int32 causticsValue = gCaustics[(gRandTable[i & (MAX_RAND_TABLE - 1)] + gCausticsFrame) & (MAX_CAUSTICS - 1)]; + vg = X_CLAMP(vg + causticsValue, 0, 8191); + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + if (z > FOG_MIN) + { + vg += (z - FOG_MIN) << FOG_SHIFT; + if (vg > 8191) { + vg = 8191; + } + } + + PERSPECTIVE(x, y, z); + + x += (FRAME_WIDTH >> 1); + y += (FRAME_HEIGHT >> 1); + + if (x < viewport.x0) clip |= CLIP_LEFT; + if (x > viewport.x1) clip |= CLIP_RIGHT; + if (y < viewport.y0) clip |= CLIP_TOP; + if (y > viewport.y1) clip |= CLIP_BOTTOM; + + res->x = x; + res->y = y; + res->z = z; + res->g = vg >> 8; + res->clip = clip; + } +} + +void transformMesh_c(const MeshVertex* vertices, int32 count, int32 intensity) +{ + Vertex* res = gVerticesBase; + + int32 vg = X_CLAMP((intensity + gLightAmbient) >> 8, 0, 31); + + for (int32 i = 0; i < count; i++, res++) + { + int32 vx = vertices->x; + int32 vy = vertices->y; + int32 vz = vertices->z; + vertices++; + + const Matrix &m = matrixGet(); + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, vx, vy, vz); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, vx, vy, vz); + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, vx, vy, vz); + + uint32 clip = 0; + + if (z <= VIEW_MIN_F) { + clip = CLIP_NEAR; + z = VIEW_MIN_F; + } + + if (z >= VIEW_MAX_F) { + clip = CLIP_FAR; + z = VIEW_MAX_F; + } + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + PERSPECTIVE(x, y, z); + + x += (FRAME_WIDTH >> 1); + y += (FRAME_HEIGHT >> 1); + + if (x < viewport.x0) clip |= CLIP_LEFT; + if (x > viewport.x1) clip |= CLIP_RIGHT; + if (y < viewport.y0) clip |= CLIP_TOP; + if (y > viewport.y1) clip |= CLIP_BOTTOM; + + res->x = x; + res->y = y; + res->z = z; + res->g = vg; + res->clip = clip; + } +} + +void faceAddRoomQuads_c(const RoomQuad* polys, int32 count) +{ + const Vertex* v = gVerticesBase; + + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; + const Vertex* v3 = v + polys->indices[3]; + + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; + uint32 c3 = v3->clip; + + if (c0 & c1 & c2 & c3) + continue; + + if ((c0 | c1 | c2 | c3) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; + } + + uint32 g0 = v0->g; + uint32 g1 = v1->g; + uint32 g2 = v2->g; + uint32 g3 = v3->g; + + if (g0 != g1 || g0 != g2 || g0 != g3) { + flags += FACE_GOURAUD; + } + + if (checkBackface(v0, v1, v2)) + continue; + + int32 depth = X_MAX(v0->z, X_MAX(v1->z, X_MAX(v2->z, v3->z))) >> OT_SHIFT; + + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + f->indices[3] = v3 - gVertices; + } +} + +void faceAddRoomTriangles_c(const RoomTriangle* polys, int32 count) +{ + const Vertex* v = gVerticesBase; + + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; + + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; + + if (c0 & c1 & c2) + continue; + + if ((c0 | c1 | c2) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; + } + + uint32 g0 = v0->g; + uint32 g1 = v1->g; + uint32 g2 = v2->g; + + if (g0 != g1 || g0 != g2) { + flags += FACE_GOURAUD; + } + flags |= FACE_TRIANGLE; + + if (checkBackface(v0, v1, v2)) + continue; + + int32 depth = X_MAX(v0->z, X_MAX(v1->z, v2->z)) >> OT_SHIFT; + + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + } +} + +void faceAddMeshQuads_c(const MeshQuad* polys, int32 count) +{ + const Vertex* v = gVerticesBase; + + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; + const Vertex* v3 = v + polys->indices[3]; + + if (checkBackface(v0, v1, v2)) + continue; + + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; + uint32 c3 = v3->clip; + + if (c0 & c1 & c2 & c3) + continue; + + if ((c0 | c1 | c2 | c3) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; + } + + int32 depth = (v0->z + v1->z + v2->z + v3->z) >> (2 + OT_SHIFT); + + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + f->indices[3] = v3 - gVertices; + } +} + +void faceAddMeshTriangles_c(const MeshTriangle* polys, int32 count) +{ + const Vertex* v = gVerticesBase; + + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; + + if (checkBackface(v0, v1, v2)) + continue; + + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; + + if (c0 & c1 & c2) + continue; + + if ((c0 | c1 | c2) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; + } + flags |= FACE_TRIANGLE; + + int32 depth = (v0->z + v1->z + v2->z + v2->z) >> (2 + OT_SHIFT); + + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + } +} + +bool transformBoxRect(const AABBs* box, RectMinMax* rect) +{ + const Matrix &m = matrixGet(); + + if ((m.e23 < VIEW_MIN_F) || (m.e23 >= VIEW_MAX_F)) + return false; + + vec3i v[8]; + v[0] = _vec3i( box->minX, box->minY, box->minZ ), + v[1] = _vec3i( box->maxX, box->minY, box->minZ ), + v[2] = _vec3i( box->minX, box->maxY, box->minZ ), + v[3] = _vec3i( box->maxX, box->maxY, box->minZ ), + v[4] = _vec3i( box->minX, box->minY, box->maxZ ), + v[5] = _vec3i( box->maxX, box->minY, box->maxZ ), + v[6] = _vec3i( box->minX, box->maxY, box->maxZ ), + v[7] = _vec3i( box->maxX, box->maxY, box->maxZ ); + + *rect = RectMinMax( INT_MAX, INT_MAX, INT_MIN, INT_MIN ); + + for (int32 i = 0; i < 8; i++) + { + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, v[i].x, v[i].y, v[i].z); + + if (z < VIEW_MIN_F || z >= VIEW_MAX_F) + continue; + + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, v[i].x, v[i].y, v[i].z); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, v[i].x, v[i].y, v[i].z); + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + PERSPECTIVE(x, y, z); + + if (x < rect->x0) rect->x0 = x; + if (x > rect->x1) rect->x1 = x; + if (y < rect->y0) rect->y0 = y; + if (y > rect->y1) rect->y1 = y; + } + + rect->x0 += (FRAME_WIDTH / 2); + rect->y0 += (FRAME_HEIGHT / 2); + rect->x1 += (FRAME_WIDTH / 2); + rect->y1 += (FRAME_HEIGHT / 2); + + return true; +} + +int32 rectIsVisible(const RectMinMax* rect) +{ + if (rect->x0 > rect->x1 || + rect->x0 > viewport.x1 || + rect->x1 < viewport.x0 || + rect->y0 > viewport.y1 || + rect->y1 < viewport.y0) return 0; // not visible + + if (rect->x0 < viewport.x0 || + rect->x1 > viewport.x1 || + rect->y0 < viewport.y0 || + rect->y1 > viewport.y1) return -1; // clipped + + return 1; // fully visible +} + +int32 boxIsVisible_c(const AABBs* box) +{ + RectMinMax rect; + if (!transformBoxRect(box, &rect)) + return 0; // not visible + return rectIsVisible(&rect); +} + +int32 sphereIsVisible_c(int32 sx, int32 sy, int32 sz, int32 r) +{ + Matrix &m = matrixGet(); + + if (abs(sx) < r && abs(sy) < r && abs(sz) < r) + return 1; + + int32 z = DP33(m.e20, m.e21, m.e22, sx, sy, sz); + + if (z < 0) + return 0; + + int32 x = DP33(m.e00, m.e01, m.e02, sx, sy, sz); + int32 y = DP33(m.e10, m.e11, m.e12, sx, sy, sz); + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + z = PERSPECTIVE_DZ(z); + if (z >= DIV_TABLE_SIZE) z = DIV_TABLE_SIZE - 1; + int32 d = FixedInvU(z); + x = (x * d) >> 12; + y = (y * d) >> 12; + r = (r * d) >> 12; + + x += (FRAME_WIDTH / 2); + y += (FRAME_HEIGHT / 2); + + int32 rMinX = x - r; + int32 rMinY = y - r; + int32 rMaxX = x + r; + int32 rMaxY = y + r; + + if (rMinX > viewport.x1 || + rMaxX < viewport.x0 || + rMinY > viewport.y1 || + rMaxY < viewport.y0) return 0; // not visible + + return 1; +} + +typedef void (*RasterProc)(uint16* pixel, const VertexLink* L, const VertexLink* R); + +RasterProc gRasterProc[FACE_TYPE_MAX] = { // IWRAM + rasterizeS, + rasterizeF, + rasterizeFT, + rasterizeFTA, + rasterizeGT, + rasterizeGTA, + rasterizeSprite, + rasterizeFillS, + rasterizeLineH, + rasterizeLineV +}; + +X_NOINLINE void rasterize_c(uint32 flags, VertexLink* top) +{ + uint8* pixel = (uint8*)fb + top->v.y * FRAME_WIDTH; + + uint32 type = (flags >> FACE_TYPE_SHIFT) & FACE_TYPE_MASK; + + VertexLink* R = (type == FACE_TYPE_F) ? (VertexLink*)(flags & 0xFF) : top; + + gRasterProc[type]((uint16*)pixel, top, R); +} + +void flush_c() +{ +#ifdef PROFILING + #if !defined(PROFILE_FRAMETIME) && !defined(PROFILE_SOUNDTIME) + gCounters[CNT_VERT] += gVerticesBase - gVertices; + gCounters[CNT_POLY] += gFacesBase - gFaces; + #endif +#endif + + gVerticesBase = gVertices; + + if (gFacesBase == gFaces) + return; + + gFacesBase = gFaces; + + PROFILE(CNT_FLUSH); + + for (int32 i = OT_SIZE - 1; i >= 0; i--) + { + if (!gOT[i]) continue; + + Face *face = gOT[i]; + gOT[i] = NULL; + + do { + uint32 flags = face->flags; + + VertexLink v[16]; + + uint32 type = (flags >> FACE_TYPE_SHIFT) & FACE_TYPE_MASK; + + if (type <= FACE_TYPE_GTA) + { + if (type > FACE_TYPE_F) + { + const Texture &tex = level.textures[flags & FACE_TEXTURE]; + gTile = (uint8*)tex.tile; + + v[0].t.t = 0xFF00FF00 & (tex.uv01); + v[1].t.t = 0xFF00FF00 & (tex.uv01 << 8); + v[2].t.t = 0xFF00FF00 & (tex.uv23); + v[3].t.t = 0xFF00FF00 & (tex.uv23 << 8); + } + + v[0].v = gVertices[face->indices[0]]; + v[1].v = gVertices[face->indices[1]]; + v[2].v = gVertices[face->indices[2]]; + if (!(flags & FACE_TRIANGLE)) { + v[3].v = gVertices[face->indices[3]]; + } + + if (flags & FACE_CLIPPED) { + drawPoly(flags, v); + } else { + if (flags & FACE_TRIANGLE) { + drawTriangle(flags, v); + } else { + drawQuad(flags, v); + } + } + } + else + { + const Vertex *vert = gVertices + face->indices[0]; + v[0].v = vert[0]; + v[1].v = vert[1]; + + if (type == FACE_TYPE_SPRITE) + { + const Sprite &sprite = level.sprites[flags & FACE_TEXTURE]; + gTile = (uint8*)sprite.tile; + v[0].t.t = (sprite.uwvh) & (0xFF00FF00); + v[1].t.t = (sprite.uwvh) & (0xFF00FF00 >> 8); + } + + rasterize(flags, v); + } + + face = face->next; + + } while (face); + } +} +#endif + +VertexLink* clipPoly(VertexLink* poly, VertexLink* tmp, int32 &pCount) +{ + #define LERP_SHIFT 6 + #define LERP(a,b,t) (b + ((a - b) * t >> LERP_SHIFT)) + //#define LERP2(a,b,ta,tb) LERP(a,b,t) + #define LERP2(a,b,ta,tb) (b + (((a - b) * ta / tb) >> LERP_SHIFT) ) // less gaps between clipped polys, but slow + + #define CLIP_AXIS(X, Y, edge, output) {\ + int32 ta = (edge - b->v.X) << LERP_SHIFT;\ + int32 tb = (a->v.X - b->v.X);\ + ASSERT(tb != 0);\ + int32 t = ta / tb;\ + VertexLink* v = output + count++;\ + v->v.X = edge;\ + v->v.Y = LERP2(a->v.Y, b->v.Y, ta, tb);\ + v->v.g = LERP(a->v.g, b->v.g, t);\ + v->t.uv.u = LERP(a->t.uv.u, b->t.uv.u, t);\ + v->t.uv.v = LERP(a->t.uv.v, b->t.uv.v, t);\ + } + + #define CLIP_XY(X, Y, X0, X1, input, output) {\ + const VertexLink *a, *b = input + pCount - 1;\ + for (int32 i = 0; i < pCount; i++) {\ + a = b;\ + b = input + i;\ + if (a->v.X < X0) {\ + if (b->v.X < X0) continue;\ + CLIP_AXIS(X, Y, X0, output);\ + } else if (a->v.X > X1) {\ + if (b->v.X > X1) continue;\ + CLIP_AXIS(X, Y, X1, output);\ + }\ + if (b->v.X < X0) {\ + CLIP_AXIS(X, Y, X0, output);\ + } else if (b->v.X > X1) {\ + CLIP_AXIS(X, Y, X1, output);\ + } else {\ + output[count++] = *b;\ + }\ + }\ + if (count < 3) return NULL;\ + } + + int32 count = 0; + + VertexLink *in = poly; + VertexLink *out = tmp; + + // clip x + CLIP_XY(x, y, viewport.x0, viewport.x1, in, out); + + pCount = count; + count = 0; + + // clip y + CLIP_XY(y, x, viewport.y0, viewport.y1, out, in); + pCount = count; + + return in; +} + +void renderInit() +{ + gVerticesBase = gVertices; + gFacesBase = gFaces; +} + +extern "C" X_NOINLINE void drawTriangle(uint32 flags, VertexLink* v) +{ + VertexLink* v0 = v + 0; + VertexLink* v1 = v + 1; + VertexLink* v2 = v + 2; + + v0->next = v1 - v0; + v1->next = v2 - v1; + v2->next = v0 - v2; + v0->prev = v2 - v0; + v1->prev = v0 - v1; + v2->prev = v1 - v2; + + VertexLink* top; + + if (v0->v.y < v1->v.y) { + if (v0->v.y < v2->v.y) { + top = v0; + } else { + top = v2; + } + } else { + if (v1->v.y < v2->v.y) { + top = v1; + } else { + top = v2; + } + } + + rasterize(flags, top); +} + +extern "C" X_NOINLINE void drawQuad(uint32 flags, VertexLink* v) +{ + VertexLink* v0 = v + 0; + VertexLink* v1 = v + 1; + VertexLink* v2 = v + 2; + VertexLink* v3 = v + 3; + + v0->next = v1 - v0; + v1->next = v2 - v1; + v2->next = v3 - v2; + v3->next = v0 - v3; + v0->prev = v3 - v0; + v1->prev = v0 - v1; + v2->prev = v1 - v2; + v3->prev = v2 - v3; + + VertexLink* top; + + if (v0->v.y < v1->v.y) { + if (v0->v.y < v2->v.y) { + top = (v0->v.y < v3->v.y) ? v0 : v3; + } else { + top = (v2->v.y < v3->v.y) ? v2 : v3; + } + } else { + if (v1->v.y < v2->v.y) { + top = (v1->v.y < v3->v.y) ? v1 : v3; + } else { + top = (v2->v.y < v3->v.y) ? v2 : v3; + } + } + + rasterize(flags, top); +} + +extern "C" X_NOINLINE void drawPoly(uint32 flags, VertexLink* v) +{ + VertexLink tmp[16]; + + int32 count = (flags & FACE_TRIANGLE) ? 3 : 4; + + v = clipPoly(v, tmp, count); + + if (!v) return; + + if (count <= 4) + { + if (count == 3) { + + if (v[0].v.y == v[1].v.y && + v[0].v.y == v[2].v.y) + return; + + drawTriangle(flags, v); + } else { + + if (v[0].v.y == v[1].v.y && + v[0].v.y == v[2].v.y && + v[0].v.y == v[3].v.y) + return; + + drawQuad(flags, v); + } + return; + } + + VertexLink* top = v; + top->next = (v + 1) - top; + top->prev = (v + count - 1) - top; + + bool skip = true; + + for (int32 i = 1; i < count; i++) + { + int8 next = i + 1; + int8 prev = i - 1; + + if (next >= count) { + next -= count; + } + + if (prev < 0) { + prev += count; + } + + next -= i; + prev -= i; + + VertexLink *p = v + i; + p->next = next; + p->prev = prev; + + if (p->v.y != top->v.y) + { + if (p->v.y < top->v.y) { + top = p; + } + skip = false; + } + } + + if (skip) { + return; // zero height poly + } + + rasterize(flags, top); +} + +void faceAddRoom(const Room* room) +{ + if (room->info->quadsCount > 0) { + faceAddRoomQuads(room->data.quads, room->info->quadsCount); + } + + if (room->info->trianglesCount > 0) { + faceAddRoomTriangles(room->data.triangles, room->info->trianglesCount); + } +} + +void faceAddMesh(const MeshQuad* quads, const MeshTriangle* triangles, int32 qCount, int32 tCount) +{ + if (qCount > 0) { + faceAddMeshQuads(quads, qCount); + } + + if (tCount > 0) { + faceAddMeshTriangles(triangles, tCount); + } +} + +void clear() +{ + dmaFill((void*)fb, 0, FRAME_WIDTH * FRAME_HEIGHT); +} + +void renderRoom(const Room* room) +{ + int32 vCount = room->info->verticesCount; + if (vCount <= 0) + return; + + if ((gVerticesBase - gVertices) + vCount > MAX_VERTICES) + { + ASSERT(false); + return; + } + + { + PROFILE(CNT_TRANSFORM); + if (ROOM_FLAG_WATER(room->info->flags)) { + transformRoomUW(room->data.vertices, vCount); + } else { + transformRoom(room->data.vertices, vCount); + } + } + + { + PROFILE(CNT_ADD); + faceAddRoom(room); + } + + gVerticesBase += vCount; +} + +void renderMesh(const Mesh* mesh) +{ + int32 vCount = mesh->vCount; + if (vCount <= 0) + return; + + if ((gVerticesBase - gVertices) + vCount > MAX_VERTICES) + { + ASSERT(false); + return; + } + + int32 fCount = mesh->rCount + mesh->tCount; + if ((gFacesBase - gFaces) + fCount > MAX_FACES) + { + ASSERT(false); + return; + } + + const uint8* ptr = (uint8*)mesh + sizeof(Mesh); + + const MeshVertex* vertices = (MeshVertex*)ptr; + ptr += vCount * sizeof(vertices[0]); + + MeshQuad* quads = (MeshQuad*)ptr; + ptr += mesh->rCount * sizeof(MeshQuad); + + MeshTriangle* triangles = (MeshTriangle*)ptr; + + { + PROFILE(CNT_TRANSFORM); + transformMesh(vertices, vCount, mesh->intensity); + } + + { + PROFILE(CNT_ADD); + faceAddMesh(quads, triangles, mesh->rCount, mesh->tCount); + } + + gVerticesBase += vCount; +} + +void renderShadow(int32 x, int32 z, int32 sx, int32 sz) +{ + if (gVerticesBase - gVertices + 8 > MAX_VERTICES) { + ASSERT(false); + return; + } + + if (gFacesBase - gFaces + 3 > MAX_FACES) { + ASSERT(false); + return; + } + + int16 xns1 = x - sx; + int16 xps1 = x + sx; + int16 xns2 = xns1 - sx; + int16 xps2 = xps1 + sx; + + int16 zns1 = z - sz; + int16 zps1 = z + sz; + int16 zns2 = zns1 - sz; + int16 zps2 = zps1 + sz; + + MeshVertex v[8]; + v[0].x = xns1; v[0].y = 0; v[0].z = zps2; + v[1].x = xps1; v[1].y = 0; v[1].z = zps2; + v[2].x = xps2; v[2].y = 0; v[2].z = zps1; + v[3].x = xps2; v[3].y = 0; v[3].z = zns1; + v[4].x = xps1; v[4].y = 0; v[4].z = zns2; + v[5].x = xns1; v[5].y = 0; v[5].z = zns2; + v[6].x = xns2; v[6].y = 0; v[6].z = zns1; + v[7].x = xns2; v[7].y = 0; v[7].z = zps1; + + transformMesh(v, 8, 0); + faceAddMeshQuads(gShadowQuads, 3); + + gVerticesBase += 8; +} + +X_NOINLINE void renderFill(int32 x, int32 y, int32 width, int32 height, int32 shade, int32 z) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); + return; + } + + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); + return; + } + + gVerticesBase[0].x = x; + gVerticesBase[0].y = y; + gVerticesBase[0].g = shade; + + gVerticesBase[1].x = width; + gVerticesBase[1].y = height; + + Face* f = faceAdd(z); + f->flags = (FACE_TYPE_FILL_S << FACE_TYPE_SHIFT); + f->indices[0] = gVerticesBase - gVertices; + + gVerticesBase += 2; +} + +X_NOINLINE void renderLine(int32 x, int32 y, int32 width, int32 height, int32 index, int32 z) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); + return; + } + + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); + return; + } + + ASSERT(width == 1 || height == 1); + ASSERT(width > 0); + ASSERT(height > 0); + + gVerticesBase[0].x = x; + gVerticesBase[0].y = y; + gVerticesBase[0].g = index; + + gVerticesBase[1].x = width; + gVerticesBase[1].y = height; + + int32 idx = gVerticesBase - gVertices; + + Face* f = faceAdd(z); + f->flags = (height == 1) ? (FACE_TYPE_LINE_H << FACE_TYPE_SHIFT) : (FACE_TYPE_LINE_V << FACE_TYPE_SHIFT); + f->indices[0] = idx; + + gVerticesBase += 2; +} + +void renderSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); + return; + } + + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); + return; + } + + const Matrix &m = matrixGet(); + + vx -= gCameraViewPos.x; + vy -= gCameraViewPos.y; + vz -= gCameraViewPos.z; + + int32 z = DP33(m.e20, m.e21, m.e22, vx, vy, vz); + + if (z < VIEW_MIN_F || z >= VIEW_MAX_F) + { + return; + } + + int32 x = DP33(m.e00, m.e01, m.e02, vx, vy, vz); + int32 y = DP33(m.e10, m.e11, m.e12, vx, vy, vz); + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + const Sprite* sprite = level.sprites + index; + + int32 l = x + sprite->l; + int32 r = x + sprite->r; + int32 t = y + sprite->t; + int32 b = y + sprite->b; + + // TODO one projection + PERSPECTIVE(l, t, z); + + l += (FRAME_WIDTH >> 1); + if (l >= viewport.x1) return; + + t += (FRAME_HEIGHT >> 1); + if (t >= viewport.y1) return; + + PERSPECTIVE(r, b, z); + + r += (FRAME_WIDTH >> 1); + if (r < viewport.x0) return; + + b += (FRAME_HEIGHT >> 1); + if (b < viewport.y0) return; + + if (l == r) return; + if (t == b) return; + + if (z > FOG_MIN) + { + vg += (z - FOG_MIN) << FOG_SHIFT; + if (vg > 8191) { + vg = 8191; + } + } + vg >>= 8; + + Vertex* v1 = gVerticesBase++; + v1->x = l; + v1->y = t; + //v1->z = z; + v1->g = vg; + + Vertex* v2 = gVerticesBase++; + v2->x = r; + v2->y = b; + //v2->z = z; + //v2->g = vg; + + int32 depth = X_MAX(0, z - 128); // depth hack + + Face* f = faceAdd(depth >> OT_SHIFT); + f->flags = (FACE_TYPE_SPRITE << FACE_TYPE_SHIFT) | index; + f->indices[0] = v1 - gVertices; + + gVerticesBase += 2; +} + +void renderGlyph(int32 vx, int32 vy, int32 index) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); + return; + } + + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); + return; + } + + const Sprite* sprite = level.sprites + index; + + int32 l = vx + sprite->l; + int32 r = vx + sprite->r; + int32 t = vy + sprite->t; + int32 b = vy + sprite->b; + + if (l == r) return; + if (t == b) return; + if (r < 0) return; + if (b < 0) return; + if (l >= FRAME_WIDTH) return; + if (t >= FRAME_HEIGHT) return; + + Vertex* v1 = gVerticesBase++; + v1->x = l; + v1->y = t; + //v1->z = z; + v1->g = 16; + + Vertex* v2 = gVerticesBase++; + v2->x = r; + v2->y = b; + //v2->z = z; + //v2->g = vg; + + Face* f = faceAdd(0); + f->flags = (FACE_TYPE_SPRITE << FACE_TYPE_SHIFT) | index; + f->indices[0] = v1 - gVertices; + + gVerticesBase += 2; +} + +#define BAR_HEIGHT 5 + +const int32 BAR_COLORS[BAR_MAX][5] = { + { 8, 11, 8, 6, 24 }, + { 32, 41, 32, 19, 21 }, + { 28, 29, 28, 26, 27 }, + { 43, 44, 43, 42, 41 }, +}; + +X_NOINLINE void renderBorder(int32 x, int32 y, int32 width, int32 height, int32 shade, int32 color1, int32 color2, int32 z) +{ + // background + if (shade >= 0) { + renderFill(x + 1, y + 1, width - 2, height - 2, shade, z); + } + + // frame + renderLine(x + 1, y, width - 2, 1, color1, z); + renderLine(x + 1, y + height - 1, width - 2, 1, color2, z); + renderLine(x, y, 1, height, color1, z); + renderLine(x + width - 1, y, 1, height, color2, z); +} + +void renderBar(int32 x, int32 y, int32 width, int32 value, BarType type) +{ + // colored bar + int32 ix = x + 2; + int32 iy = y + 2; + int32 w = value * width >> 8; + + if (w > 0) + { + for (int32 i = 0; i < 5; i++) + { + renderLine(ix, iy++, w, 1, BAR_COLORS[type][i], 0); + } + } + + renderBorder(x, y, width + 4, BAR_HEIGHT + 4, 27, 19, 17, 0); +} + +void renderBackground(const void* background) +{ + clear(); +// dmaCopy(background, (void*)fb, FRAME_WIDTH * FRAME_HEIGHT); +} + +void* copyBackground() +{ +// dmaCopy((void*)fb, gBackgroundCopy, FRAME_WIDTH * FRAME_HEIGHT); +// palGrayRemap(gBackgroundCopy, FRAME_WIDTH * FRAME_HEIGHT); +// return gBackgroundCopy; + return NULL; +} diff --git a/src/platform/32x/sound.cpp b/src/platform/32x/sound.cpp new file mode 100644 index 00000000..2512df5c --- /dev/null +++ b/src/platform/32x/sound.cpp @@ -0,0 +1,51 @@ +#include "common.h" + +void sndInit() +{ + // TODO +} + +void sndInitSamples() +{ + // TODO +} + +void sndFreeSamples() +{ + // TODO +} + +void* sndPlaySample(int32 index, int32 volume, int32 pitch, int32 mode) +{ + return NULL; // TODO +} + +void sndPlayTrack(int32 track) +{ + // TODO +} + +void sndStopTrack() +{ + // TODO +} + +bool sndTrackIsPlaying() +{ + return false; // TODO +} + +void sndStopSample(int32 index) +{ + // TODO +} + +void sndStop() +{ + // TODO +} + +void sndFill(uint8* buffer, int32 count) +{ + // TODO +}