From 3f49864d1a1340e5739f6ae5e9e7510254d89549 Mon Sep 17 00:00:00 2001 From: BlazeEntertainment Date: Tue, 15 Jun 2021 14:41:19 +0000 Subject: [PATCH 01/15] Fix controls for some games, bump up version, add to-do --- README.md | 3 +++ src/controller.c | 40 ++++++++++++++++++++++++++++++++-- src/libretro.c | 56 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 44d5c95..07d4be4 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,6 @@ Mattel Intellivision games were often meant to be played with game-specific card | Start | Pause Game | | Select | Controller Swap | +## Todo + +- Some games won't work well with modern game controllers without a keypad. These games have been remapped to facebuttons to be functional like Night Stalker, however these fixes are currently achieved with #IFDEF's. Need to replace these IFDEFS with some form of decent game detection so it automatically sets these new mappings. \ No newline at end of file diff --git a/src/controller.c b/src/controller.c index 4284a2d..f2806c0 100644 --- a/src/controller.c +++ b/src/controller.c @@ -70,11 +70,16 @@ int getQuickKeypadState(int player); void controllerInit() { - controllerSwap = 0; // by default input 0 maps to Right Controller (0x1FE) // and input 1 maps to Left Controller (0x1FF) // pressing select (freeintv_libretro.c) will // swap the left and right controllers +#ifdef SHARKSHARK + //These titles require controller swap from boot + controllerSwap = 1; +#else + controllerSwap = 0; +#endif } void setControllerInput(int player, int state) @@ -104,11 +109,42 @@ int getControllerState(int joypad[], int player) if(joypad[1]!=0 && joypad[2]!=0) { state |= D_SW; } // 0x36 - Down+Left if(joypad[1]!=0 && joypad[3]!=0) { state |= D_SE; } // 0x3C - Down+Right +#ifdef NIGHTSTALKER + //Nightstalker requires keypad to play so map keypad to face buttons + if(joypad[7]!=0) { state |= K_4; } + if(joypad[4]!=0) { state |= K_6; } + if(joypad[5]!=0) { state |= K_8; } + if(joypad[6]!=0) { state |= K_2; } +#elif SHARKSHARK + //Add Dart moves to shark shark + if(joypad[7]!=0) { state |= K_1; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= K_3; } // 0x9F - Button Left + if(joypad[5]!=0) { state |= B_RIGHT; } // 0x3F - Button Right + if(joypad[6]!=0) { state |= K_6; } +#elif defined(ASTROSMASH) + //Astrosmash requires keypad to play so map Button Top to K3 (Hyper space) + if(joypad[7]!=0) { state |= K_3; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= K_2; } // 0x3F - Button Right + if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left + if(joypad[6]!=0) { state |= K_1; } +#elif defined(PINBALL) + //Pinball needs the LEFT/RIGHT button flipping + if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= B_RIGHT; } // 0x3F - Button Right + if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left + if(joypad[6]!=0) { state |= getQuickKeypadState(player); } +#elif defined(SLAPSHOT) + //Slapshot needs choose player + if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= B_RIGHT; } // 0x3F - Button Right + if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left + if(joypad[6]!=0) { state |= K_E; } +#else if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= B_LEFT; } // 0x9F - Button Left if(joypad[5]!=0) { state |= B_RIGHT; } // 0x3F - Button Right - if(joypad[6]!=0) { state |= getQuickKeypadState(player); } +#endif /* Analog Controls for 16-way disc control */ diff --git a/src/libretro.c b/src/libretro.c index 6a410f1..b3edf5a 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -221,12 +221,59 @@ void retro_run(void) if(paused) { OSD_drawPaused(); +#ifdef XBOXPADSTYLEHELP + OSD_drawTextCenterBG(21, "HELP - PRESS B"); +#else OSD_drawTextCenterBG(21, "HELP - PRESS A"); +#endif } } if(paused) { +#ifdef XBOXPADSTYLE + // If core is being built for a device which uses XBOX style + // facebuttons instead of retropad. Reverse the mapping help + // help menu // + if(joypad0[4]==1 || joypad1[4]==1) + { + OSD_drawTextBG(3, 4, " "); + OSD_drawTextBG(3, 5, " - HELP - "); + OSD_drawTextBG(3, 6, " "); + OSD_drawTextBG(3, 7, " B - RIGHT ACTION BUTTON "); + OSD_drawTextBG(3, 8, " A - LEFT ACTION BUTTON "); + OSD_drawTextBG(3, 9, " X - TOP ACTION BUTTON "); + OSD_drawTextBG(3, 10, " Y - LAST SELECTED KEYPAD BUTTON "); + OSD_drawTextBG(3, 11, " L/R - SHOW KEYPAD "); + OSD_drawTextBG(3, 12, " "); + OSD_drawTextBG(3, 13, " START - PAUSE GAME "); + OSD_drawTextBG(3, 14, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); + OSD_drawTextBG(3, 15, " "); + OSD_drawTextBG(3, 16, " FREEINTV 1.2 LICENSE GPL V3 "); + OSD_drawTextBG(3, 17, " "); + } +#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) + // These games have special mappings so the mapping details + // are offputting so just tell them to refer to manual or overlay. + // help menu // + if(joypad0[4]==1 || joypad1[4]==1) + { + OSD_drawTextBG(3, 4, " "); + OSD_drawTextBG(3, 5, " - HELP - "); + OSD_drawTextBG(3, 6, " "); + OSD_drawTextBG(3, 7, " "); + OSD_drawTextBG(3, 8, " PLEASE CHECK MANUAL "); + OSD_drawTextBG(3, 9, " OR OVERLAY "); + OSD_drawTextBG(3, 10, " "); + OSD_drawTextBG(3, 11, " L/R - SHOW KEYPAD "); + OSD_drawTextBG(3, 12, " "); + OSD_drawTextBG(3, 13, " START - PAUSE GAME "); + OSD_drawTextBG(3, 14, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); + OSD_drawTextBG(3, 15, " "); + OSD_drawTextBG(3, 16, " FREEINTV 1.2 LICENSE GPL V3 "); + OSD_drawTextBG(3, 17, " "); + } +#else // help menu // if(joypad0[4]==1 || joypad1[4]==1) { @@ -242,9 +289,10 @@ void retro_run(void) OSD_drawTextBG(3, 13, " START - PAUSE GAME "); OSD_drawTextBG(3, 14, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); OSD_drawTextBG(3, 15, " "); - OSD_drawTextBG(3, 16, " FREEINTV 1.1 LICENSE GPL V3 "); + OSD_drawTextBG(3, 16, " FREEINTV 1.2 LICENSE GPL V3 "); OSD_drawTextBG(3, 17, " "); } +#endif } else { @@ -297,7 +345,7 @@ void retro_run(void) audioBufferPos = 0.0; PSGFrame(); } - +#ifndef SHARKSHARK // Swap Left/Right Controller if(joypad0[9]==1 || joypad1[9]==1) { @@ -314,7 +362,7 @@ void retro_run(void) OSD_drawRightLeft(); } } - +#endif if (intv_halt) OSD_drawTextBG(3, 5, "INTELLIVISION HALTED"); // send frame to libretro @@ -331,7 +379,7 @@ void retro_get_system_info(struct retro_system_info *info) { memset(info, 0, sizeof(*info)); info->library_name = "FreeIntv"; - info->library_version = "1.1"; + info->library_version = "1.2"; info->valid_extensions = "int|bin|rom"; info->need_fullpath = true; } From ef8369c2f95ed4eb9ab21185b67c1d3861721600 Mon Sep 17 00:00:00 2001 From: BlazeEntertainment Date: Tue, 15 Jun 2021 14:57:23 +0000 Subject: [PATCH 02/15] Fix slapshot instructions --- src/libretro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libretro.c b/src/libretro.c index b3edf5a..f08e57c 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -252,7 +252,7 @@ void retro_run(void) OSD_drawTextBG(3, 16, " FREEINTV 1.2 LICENSE GPL V3 "); OSD_drawTextBG(3, 17, " "); } -#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) +#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) // These games have special mappings so the mapping details // are offputting so just tell them to refer to manual or overlay. // help menu // From 2edd45239b97720621b60b2bdae83ffd7aed03fe Mon Sep 17 00:00:00 2001 From: BlazeEntertainment Date: Tue, 15 Jun 2021 16:34:22 +0000 Subject: [PATCH 03/15] Update and fix slapshot mapping --- Makefile | 1 + src/controller.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0c023ef..f775be4 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +CFLAGS += -DXBOXPADSTYLEHELP -DSLAPSHOT STATIC_LINKING=0 AR := ar diff --git a/src/controller.c b/src/controller.c index f2806c0..6d65a8e 100644 --- a/src/controller.c +++ b/src/controller.c @@ -138,7 +138,7 @@ int getControllerState(int joypad[], int player) if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= B_RIGHT; } // 0x3F - Button Right if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left - if(joypad[6]!=0) { state |= K_E; } + if(joypad[6]!=0) { state |= K_0; } #else if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= B_LEFT; } // 0x9F - Button Left From c39af64e6417646f94be8c9c0b9cad11b55def43 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 16 Jun 2021 17:44:50 +0200 Subject: [PATCH 04/15] [WIP] Minimal working Savestates TODO: - Check audio (partly unserialized) - Check cart interaction after state load (missing serialization of pos etc) - Use fixed size Integer types for safe cross platform use - Maybe serialize the frame contents & scanline states --- src/cart.c | 3 +- src/controller.c | 4 +- src/cp1610.c | 379 ++++++++++++++++++++++++----------------------- src/cp1610.h | 28 +++- src/intv.c | 14 +- src/libretro.c | 41 ++++- src/memory.c | 58 ++++---- src/psg.c | 39 ++--- src/stic.c | 41 ++--- 9 files changed, 337 insertions(+), 270 deletions(-) diff --git a/src/cart.c b/src/cart.c index fa8b4e3..7baf07c 100644 --- a/src/cart.c +++ b/src/cart.c @@ -17,6 +17,7 @@ #include #include "memory.h" +#include "cp1610.h" #include "cart.h" #include "osd.h" @@ -129,7 +130,7 @@ void loadRange(int start, int stop) { while(start<=stop && pos #include "controller.h" + +#include "cp1610.h" #include "memory.h" const double PI = 3.14159265358979323846; @@ -79,7 +81,7 @@ void controllerInit() void setControllerInput(int player, int state) { - Memory[(player^controllerSwap) + 0x1FE] = (state^0xFF) & 0xFF; + CTX(Memory)[(player^controllerSwap) + 0x1FE] = (state^0xFF) & 0xFF; } int getControllerState(int joypad[], int player) diff --git a/src/cp1610.c b/src/cp1610.c index b70feb5..66b098b 100644 --- a/src/cp1610.c +++ b/src/cp1610.c @@ -24,35 +24,38 @@ // http://spatula-city.org/~im14u2c/chips/GICP1600.pdf // ftp://bitsavers.informatik.uni-stuttgart.de/components/gi/CP1600/CP-1600_Microprocessor_Users_Manual_May75.pdf -int (*OpCodes[0x400])(int); -int Interuptable[0x400]; -const char *Nmemonic[0x400]; +#define regSP 6 // Stack Pointer (R6) +#define regPC 7 // Program Counter (R7) -unsigned int R[8] = {0, 0, 0, 0, 0, 0, 0x02F1, 0x1000}; // Registers R0-R7 +CP1610_Context gCP1610_Context = { + .Version = CP1610_SERIALIZE_VERSION, + + .R = {0, 0, 0, 0, 0, 0, 0x02F1, 0x1000}, // Registers R0-R7 -const int PC = 7; // const Program Counter (R7) -const int SP = 6; // const Stack Pointer (R6) + .InstructionRegister = 0, // four external lines? -int InstructionRegister = 0; // four external lines? - -int Flag_DoubleByteData = 0; -int Flag_InteruptEnable = 0; -int Flag_Carry = 0; -int Flag_Sign = 0; -int Flag_Zero = 0; -int Flag_Overflow = 0; + .Flag_DoubleByteData = 0, + .Flag_InteruptEnable = 0, + .Flag_Carry = 0, + .Flag_Sign = 0, + .Flag_Zero = 0, + .Flag_Overflow = 0 +}; void CP1610Reset() { - Flag_DoubleByteData = 0; - Flag_InteruptEnable = 0; - Flag_Carry = 0; - Flag_Sign = 0; - Flag_Zero = 0; - Flag_Overflow = 0; - R[0] = R[1] = R[2] = R[3] = R[4] = R[5] = 0; - R[SP] = 0x02F1; // Stack is at System Ram 0x02F1-0x0318 - R[PC] = 0x1000; // EXEC entry point + CTX(Version) = CP1610_SERIALIZE_VERSION; + CTX(Flag_DoubleByteData) = 0; + CTX(Flag_InteruptEnable) = 0; + CTX(Flag_Carry) = 0; + CTX(Flag_Sign) = 0; + CTX(Flag_Zero) = 0; + CTX(Flag_Overflow) = 0; + + memset(&CTX(R), 0, sizeof(CTX(R))); + + CTX(R[regSP]) = 0x02F1; // Stack is at System Ram 0x02F1-0x0318 + CTX(R[regPC]) = 0x1000; // EXEC entry point } int readIndirect(int reg) // Read Indirect, handle SDBD, update autoincriment registers @@ -60,20 +63,20 @@ int readIndirect(int reg) // Read Indirect, handle SDBD, update autoincriment re int val = 0; int adr = 0; - if(reg==6) { R[reg] = R[reg] - 1; } // decriment R6 (SP) before read - adr = R[reg]; + if(reg==6) { CTX(R)[reg] = CTX(R)[reg] - 1; } // decriment R6 (regSP) before read + adr = CTX(R)[reg]; val = readMem(adr); - if(reg==4 || reg==5 || reg==7) // autoincrement registers R4-R7 excluding SP (R6) + if(reg==4 || reg==5 || reg==7) // autoincrement registers R4-R7 excluding regSP (R6) { - R[reg] = (R[reg]+1) & 0xFFFF; + CTX(R)[reg] = (CTX(R)[reg]+1) & 0xFFFF; } - if(Flag_DoubleByteData == 1) { + if(CTX(Flag_DoubleByteData) == 1) { val &= 0xff; if(reg==4 || reg==5 || reg==7) // autoincrement registers (incremented twice for double byte data) { val |= ((readMem(adr+1) & 0xFF)<<8); - R[reg] = (R[reg]+1) & 0xFFFF; + CTX(R)[reg] = (CTX(R)[reg]+1) & 0xFFFF; } else { val |= val << 8; } @@ -83,34 +86,34 @@ int readIndirect(int reg) // Read Indirect, handle SDBD, update autoincriment re void writeIndirect(int reg, int val) { - int adr = R[reg]; + int adr = CTX(R)[reg]; writeMem(adr, val); if(reg>=4) // autoincrement registers R4-R7 { - R[reg] = (R[reg]+1) & 0xFFFF; + CTX(R)[reg] = (CTX(R)[reg]+1) & 0xFFFF; } } int readOperand(void) { - int val = readMem(R[PC]); - R[PC]++; + int val = readMem(CTX(R)[regPC]); + CTX(R)[regPC]++; return val; } int readOperandIndirect(void) { - int adr = readMem(R[PC]); + int adr = readMem(CTX(R)[regPC]); int val = readMem(adr); - R[PC]++; + CTX(R)[regPC]++; return val; } void SetFlagsSZ(int reg) { - R[reg] = R[reg] & 0xFFFF; - Flag_Sign = (R[reg] & 0x8000)!=0; - Flag_Zero = R[reg]==0; + CTX(R)[reg] = CTX(R)[reg] & 0xFFFF; + CTX(Flag_Sign) = (CTX(R)[reg] & 0x8000)!=0; + CTX(Flag_Zero) = CTX(R)[reg]==0; } int AddSetSZOC(int A, int B) @@ -120,13 +123,13 @@ int AddSetSZOC(int A, int B) int result = (A+B); int signr = result & 0x8000; - Flag_Overflow = (signa==signb && signa!=signr) ? 1 : 0; - Flag_Carry = (result & 0x10000) != 0; + CTX(Flag_Overflow) = (signa==signb && signa!=signr) ? 1 : 0; + CTX(Flag_Carry) = (result & 0x10000) != 0; result = result & 0xFFFF; - Flag_Sign = (result & 0x8000)!=0; - Flag_Zero = result==0; + CTX(Flag_Sign) = (result & 0x8000)!=0; + CTX(Flag_Zero) = result==0; return result; } int SubSetOC(int A, int B) @@ -135,17 +138,17 @@ int SubSetOC(int A, int B) int signb = B & 0x8000; int result = (A + (B ^ 0xFFFF) + 1); // A - B using 1's compliment; int signr = result & 0x8000; - Flag_Carry = (result & 0x10000)!=0; - Flag_Overflow = (signa!=signb && signa!=signr) ? 1 : 0; + CTX(Flag_Carry) = (result & 0x10000)!=0; + CTX(Flag_Overflow) = (signa!=signb && signa!=signr) ? 1 : 0; return result & 0xFFFF; } int CP1610Tick(int debug) { // execute one instruction // - int sdbd = Flag_DoubleByteData; + int sdbd = CTX(Flag_DoubleByteData); - unsigned int instruction = readMem(R[PC]); + unsigned int instruction = readMem(CTX(R)[regPC]); int ticks = 0; @@ -154,7 +157,7 @@ int CP1610Tick(int debug) { FILE *debug_file; - fprintf(stdout, "%04x:[%03x%c %04x %04x %04x %04x %04x %04x %04x %s %c%c%c%c%c%c\n", R[7], instruction, instruction > 0x03ff ? 'X' : ']', R[0], R[1], R[2], R[3], R[4], R[5], R[6], Nmemonic[instruction], Flag_Sign ? 'S' : '-', Flag_Carry ? 'C' : '-', Flag_Overflow ? 'O' : '-', Flag_Zero ? 'Z' : '-', Flag_InteruptEnable ? 'I' : '-', Flag_DoubleByteData ? 'D' : '-'); + fprintf(stdout, "%04x:[%03x%c %04x %04x %04x %04x %04x %04x %04x %s %c%c%c%c%c%c\n", CTX(R)[7], instruction, instruction > 0x03ff ? 'X' : ']', CTX(R)[0], CTX(R)[1], CTX(R)[2], CTX(R)[3], CTX(R)[4], CTX(R)[5], CTX(R)[6], Nmemonic[instruction], CTX(Flag_Sign) ? 'S' : '-', CTX(Flag_Carry) ? 'C' : '-', CTX(Flag_Overflow) ? 'O' : '-', CTX(Flag_Zero) ? 'Z' : '-', CTX(Flag_InteruptEnable) ? 'I' : '-', CTX(Flag_DoubleByteData) ? 'D' : '-'); } #endif @@ -165,21 +168,21 @@ int CP1610Tick(int debug) return 0; } - R[PC]++; // point PC/R7 at operand/next address + CTX(R)[regPC]++; // point regPC/R7 at operand/next address - ticks = OpCodes[instruction](instruction); // execute instruction + ticks = CTX(OpCodes)[instruction](instruction); // execute instruction - if(sdbd==1) { Flag_DoubleByteData = 0; } // reset SDBD + if(sdbd==1) { CTX(Flag_DoubleByteData) = 0; } // reset SDBD // check interupt request - if(Flag_InteruptEnable == 1 && SR1>0) + if(CTX(Flag_InteruptEnable) == 1 && SR1>0) { - if(Interuptable[instruction]) + if(CTX(Interuptable)[instruction]) { // Take VBlank Interupt // SR1 = 0; - writeIndirect(SP, R[PC]); // push PC... - R[PC] = 0x1004; // Jump + writeIndirect(regSP, CTX(R)[regPC]); // push regPC... + CTX(R)[regPC] = 0x1004; // Jump ticks += 12; } } @@ -192,13 +195,13 @@ int HLT(int v) // Halt Instruction found! // printf("\n\n[ERROR] [FREEINTV] HALT!\n"); - R[PC]--; // Repeat instruction forever instead of exiting without warning + CTX(R)[regPC]--; // Repeat instruction forever instead of exiting without warning return 0; } -int SDBD(int v) { Flag_DoubleByteData = 1; return 4; } // Set Double Byte Data -int EIS(int v) { Flag_InteruptEnable = 1; return 4; } // Enable Interrupt System -int DIS(int v) { Flag_InteruptEnable = 0; return 4; } // Disable Interrupt System +int SDBD(int v) { CTX(Flag_DoubleByteData) = 1; return 4; } // Set Double Byte Data +int EIS(int v) { CTX(Flag_InteruptEnable) = 1; return 4; } // Enable Interrupt System +int DIS(int v) { CTX(Flag_InteruptEnable) = 0; return 4; } // Disable Interrupt System int Jump(int v) { // J, JE, JD, JSR, JSRE, JSRD, CALL @@ -211,58 +214,58 @@ int Jump(int v) if(reg!=3) { reg = reg + 4; - R[reg] = R[PC]; // store return address (PC already advanced to PC+3) + CTX(R)[reg] = CTX(R)[regPC]; // store return address (regPC already advanced to regPC+3) } - if(ff==1) { Flag_InteruptEnable = 1; } // set Interupt flag - if(ff==2) { Flag_InteruptEnable = 0; } // clear Interrupt flag - R[PC] = adr; // Jump + if(ff==1) { CTX(Flag_InteruptEnable) = 1; } // set Interupt flag + if(ff==2) { CTX(Flag_InteruptEnable) = 0; } // clear Interrupt flag + CTX(R)[regPC] = adr; // Jump return 13; } int TCI(int v) { return 4; } // Terminate Current Interrupt (not used) -int CLRC(int v) { Flag_Carry = 0; return 4; } // Clear Carry -int SETC(int v) { Flag_Carry = 1; return 4; } // Set Carry +int CLRC(int v) { CTX(Flag_Carry) = 0; return 4; } // Clear Carry +int SETC(int v) { CTX(Flag_Carry) = 1; return 4; } // Set Carry #define EXTRA_IF_R6R7(reg) (reg >= 6 ? 1 : 0) int INCR(int v) // Increment Register { int reg = v & 0x07; - R[reg] = R[reg]+1; + CTX(R)[reg] = CTX(R)[reg]+1; SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int DECR(int v) // Decrement Register { int reg = v & 0x07; - R[reg] = R[reg]-1; + CTX(R)[reg] = CTX(R)[reg]-1; SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int COMR(int v) // Complement Register (One's Compliment) { int reg = v & 0x07; - R[reg] = R[reg] ^ 0xFFFF; + CTX(R)[reg] = CTX(R)[reg] ^ 0xFFFF; SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int NEGR(int v) // Negate Register (Two's Compliment) { int reg = v & 0x07; - R[reg] = SubSetOC(0, R[reg]); + CTX(R)[reg] = SubSetOC(0, CTX(R)[reg]); SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int ADCR(int v) // Add Carry to Register { int reg = v & 0x07; - R[reg] = AddSetSZOC(R[reg], Flag_Carry); + CTX(R)[reg] = AddSetSZOC(CTX(R)[reg], CTX(Flag_Carry)); return 6 + EXTRA_IF_R6R7(reg); } int GSWD(int v) // Get the Status Word szoc:0000:szoc:0000 { int reg = v & 0x03; - unsigned int szoc = (Flag_Sign<<3) | (Flag_Zero<<2) | (Flag_Overflow<<1) | Flag_Carry; - R[reg] = (szoc<<12) | (szoc<<4); + unsigned int szoc = (CTX(Flag_Sign)<<3) | (CTX(Flag_Zero)<<2) | (CTX(Flag_Overflow)<<1) | CTX(Flag_Carry); + CTX(R)[reg] = (szoc<<12) | (szoc<<4); return 6; } int NOP(int v) { return 6; } // No Operation @@ -271,31 +274,31 @@ int SIN(int v) { return 6; } // Software Interrupt (not used) int RSWD(int v) // Return Status Word szoc:0000 { int reg = v & 0x07; - unsigned int szoc = R[reg]>>4; - Flag_Sign = (szoc>>3) & 1; - Flag_Zero = (szoc>>2) & 1; - Flag_Overflow = (szoc>>1) & 1; - Flag_Carry = szoc & 1; + unsigned int szoc = CTX(R)[reg]>>4; + CTX(Flag_Sign) = (szoc>>3) & 1; + CTX(Flag_Zero) = (szoc>>2) & 1; + CTX(Flag_Overflow) = (szoc>>1) & 1; + CTX(Flag_Carry) = szoc & 1; return 6; } int SWAP(int v) // Swap 0000:0trr { int reg = v & 0x03; int times = (v>>2) & 1; - int upper = (R[reg]>>8) & 0xFF; - int lower = R[reg] & 0xFF; + int upper = (CTX(R)[reg]>>8) & 0xFF; + int lower = CTX(R)[reg] & 0xFF; if(times==0) // single swap { - R[reg] = (lower<<8) | upper; - Flag_Sign = (R[reg]>>7) & 1; - Flag_Zero = R[reg]==0; + CTX(R)[reg] = (lower<<8) | upper; + CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; + CTX(Flag_Zero) = CTX(R)[reg]==0; return 6; } else // double swap { - R[reg] = (lower<<8) | lower; - Flag_Sign = (R[reg]>>7) & 1; - Flag_Zero = R[reg]==0; + CTX(R)[reg] = (lower<<8) | lower; + CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; + CTX(Flag_Zero) = CTX(R)[reg]==0; return 8; } } @@ -303,7 +306,7 @@ int SLL(int v) // Shift Logical Left 0000:1drr { int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - R[reg] = R[reg]<>2) & 1); - int bit15 = (R[reg]>>15) & 1; - int bit14 = (R[reg]>>14) & 1; + int bit15 = (CTX(R)[reg]>>15) & 1; + int bit14 = (CTX(R)[reg]>>14) & 1; if(times==0) // Single rotate { - R[reg] = R[reg] << 1; - R[reg] = R[reg] | Flag_Carry; - Flag_Carry = bit15; + CTX(R)[reg] = CTX(R)[reg] << 1; + CTX(R)[reg] = CTX(R)[reg] | CTX(Flag_Carry); + CTX(Flag_Carry) = bit15; } else // Double rotate { - R[reg] = R[reg] << 2; - R[reg] = R[reg] | ((Flag_Carry << 1) | Flag_Overflow); - Flag_Carry = bit15; - Flag_Overflow = bit14; + CTX(R)[reg] = CTX(R)[reg] << 2; + CTX(R)[reg] = CTX(R)[reg] | ((CTX(Flag_Carry) << 1) | CTX(Flag_Overflow)); + CTX(Flag_Carry) = bit15; + CTX(Flag_Overflow) = bit14; } SetFlagsSZ(reg); return 6+(2*times); // 6 single or 8 double @@ -338,15 +341,15 @@ int SLLC(int v) // Shift Logical Left through Carry // The wiki method seems to be correct int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - int bit15 = (R[reg]>>15) & 1; - int bit14 = (R[reg]>>14) & 1; - R[reg] = (R[reg]<>15) & 1; + int bit14 = (CTX(R)[reg]>>14) & 1; + CTX(R)[reg] = (CTX(R)[reg]<>2) & 1)+1; - R[reg] = R[reg]>>dist; - Flag_Sign = (R[reg]>>7) & 1; - Flag_Zero = R[reg]==0; + CTX(R)[reg] = CTX(R)[reg]>>dist; + CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; + CTX(Flag_Zero) = CTX(R)[reg]==0; return 6+(2*(dist-1)); // 6 <<1 or 8 <<2 } int SAR(int v) // Shift Arithmetic Right { int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - int bit15 = (R[reg]>>15) & 1; + int bit15 = (CTX(R)[reg]>>15) & 1; - R[reg] = R[reg]>>dist; + CTX(R)[reg] = CTX(R)[reg]>>dist; if(dist==1) { - R[reg] = R[reg] | (bit15<<15); + CTX(R)[reg] = CTX(R)[reg] | (bit15<<15); } else { - R[reg] = R[reg] | (bit15<<15); - R[reg] = R[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high bits" + CTX(R)[reg] = CTX(R)[reg] | (bit15<<15); + CTX(R)[reg] = CTX(R)[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high bits" } - Flag_Sign = (R[reg]>>7) & 1; - Flag_Zero = R[reg]==0; + CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; + CTX(Flag_Zero) = CTX(R)[reg]==0; return 6+(2*(dist-1)); // 6 <<1 or 8 <<2 } int RRC(int v) // Rotate Right Through Carry { int reg = v & 0x03; int dist = ((v>>2) & 1); - int bit1 = (R[reg]>>1) & 1; - int bit0 = R[reg] & 1; + int bit1 = (CTX(R)[reg]>>1) & 1; + int bit0 = CTX(R)[reg] & 1; if(dist==0) { - R[reg] = R[reg]>>1; - R[reg] = R[reg] | (Flag_Carry<<15); + CTX(R)[reg] = CTX(R)[reg]>>1; + CTX(R)[reg] = CTX(R)[reg] | (CTX(Flag_Carry)<<15); } else { - R[reg] = R[reg]>>2; - R[reg] = R[reg] | (Flag_Overflow<<15); - R[reg] = R[reg] | (Flag_Carry<<14); - Flag_Overflow = bit1; + CTX(R)[reg] = CTX(R)[reg]>>2; + CTX(R)[reg] = CTX(R)[reg] | (CTX(Flag_Overflow)<<15); + CTX(R)[reg] = CTX(R)[reg] | (CTX(Flag_Carry)<<14); + CTX(Flag_Overflow) = bit1; } - Flag_Carry = bit0; - Flag_Sign = (R[reg]>>7) & 1; - Flag_Zero = R[reg]==0; + CTX(Flag_Carry) = bit0; + CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; + CTX(Flag_Zero) = CTX(R)[reg]==0; return 6+(2*(dist)); // 6 <<1 or 8 <<2 } int SARC(int v) // Shift Arithmetic Right Through Carry { int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - int bit15 = (R[reg]>>15) & 1; - int bit1 = (R[reg]>>1) & 1; - int bit0 = R[reg] & 1; + int bit15 = (CTX(R)[reg]>>15) & 1; + int bit1 = (CTX(R)[reg]>>1) & 1; + int bit0 = CTX(R)[reg] & 1; - R[reg] = R[reg]>>dist; - R[reg] = R[reg] | (bit15<<15); + CTX(R)[reg] = CTX(R)[reg]>>dist; + CTX(R)[reg] = CTX(R)[reg] | (bit15<<15); if(dist==2) { - R[reg] = R[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high 2 bits" - Flag_Overflow = bit1; + CTX(R)[reg] = CTX(R)[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high 2 bits" + CTX(Flag_Overflow) = bit1; } - Flag_Carry = bit0; - Flag_Sign = (R[reg]>>7) & 1; - Flag_Zero = R[reg]==0; + CTX(Flag_Carry) = bit0; + CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; + CTX(Flag_Zero) = CTX(R)[reg]==0; return 6+(2*(dist-1)); // 6 <<1 or 8 <<2 } int MOVR(int v) // Move Register { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - R[dreg] = R[sreg]; + CTX(R)[dreg] = CTX(R)[sreg]; SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -436,14 +439,14 @@ int ADDR(int v) // Add Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - R[dreg] = AddSetSZOC(R[dreg], R[sreg]); + CTX(R)[dreg] = AddSetSZOC(CTX(R)[dreg], CTX(R)[sreg]); return 6 + EXTRA_IF_R6R7(dreg); } int SUBR(int v) // Subtract Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - R[dreg] = SubSetOC(R[dreg], R[sreg]); + CTX(R)[dreg] = SubSetOC(CTX(R)[dreg], CTX(R)[sreg]); SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -451,16 +454,16 @@ int CMPR(int v) // Compare Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - int res = SubSetOC(R[dreg], R[sreg]); - Flag_Sign = (res & 0x8000)!=0; - Flag_Zero = res==0; + int res = SubSetOC(CTX(R)[dreg], CTX(R)[sreg]); + CTX(Flag_Sign) = (res & 0x8000)!=0; + CTX(Flag_Zero) = res==0; return 6 + EXTRA_IF_R6R7(dreg); } int ANDR(int v) // And Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - R[dreg] = R[dreg] & R[sreg]; + CTX(R)[dreg] = CTX(R)[dreg] & CTX(R)[sreg]; SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -468,7 +471,7 @@ int XORR(int v) // Xor Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - R[dreg] = R[dreg] ^ R[sreg]; + CTX(R)[dreg] = CTX(R)[dreg] ^ CTX(R)[sreg]; SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -490,29 +493,29 @@ int Branch(int v) // Branch - B, BC, BOV, BPL, BEQ, BLT, BLE, BUSC, NOPP, BNC, B // digital states to be sampled by the CPU during the execution of the BEXT // (Branch on EXTernal) instruction // --- I don't know what is meant by 'instruction register' - if((InstructionRegister & 0x0F)==(v & 0x0F)) + if((CTX(InstructionRegister) & 0x0F)==(v & 0x0F)) { - if(direction==0) { R[PC] = R[PC]+offset; } - if(direction==1) { R[PC] = R[PC]-offset-1; } + if(direction==0) { CTX(R)[regPC] = CTX(R)[regPC]+offset; } + if(direction==1) { CTX(R)[regPC] = CTX(R)[regPC]-offset-1; } } return 7; } switch(condition) { case 0: branch = 1; break; // B, NOPP - case 1: branch = (Flag_Carry==1); break; // BC, BNC - case 2: branch = (Flag_Overflow==1); break; // BOV, BNOV - case 3: branch = (Flag_Sign==0); break; // BPL, BMI - case 4: branch = (Flag_Zero==1); break; // BEQ, BNEQ - case 5: branch = (Flag_Sign!=Flag_Overflow); break; // BLT, BGE - case 6: branch = (Flag_Zero==1)||(Flag_Sign!=Flag_Overflow); break; // BLE, BGT - case 7: branch = (Flag_Sign!=Flag_Carry); break; // BUSC, BESC + case 1: branch = (CTX(Flag_Carry)==1); break; // BC, BNC + case 2: branch = (CTX(Flag_Overflow)==1); break; // BOV, BNOV + case 3: branch = (CTX(Flag_Sign)==0); break; // BPL, BMI + case 4: branch = (CTX(Flag_Zero)==1); break; // BEQ, BNEQ + case 5: branch = (CTX(Flag_Sign)!=CTX(Flag_Overflow)); break; // BLT, BGE + case 6: branch = (CTX(Flag_Zero)==1)||(CTX(Flag_Sign)!=CTX(Flag_Overflow)); break; // BLE, BGT + case 7: branch = (CTX(Flag_Sign)!=CTX(Flag_Carry)); break; // BUSC, BESC } if(notbit==1) { branch = !branch; } if(branch) { - if(direction==0) { R[PC] = R[PC]+offset; } - if(direction==1) { R[PC] = R[PC]-(offset+1); } + if(direction==0) { CTX(R)[regPC] = CTX(R)[regPC]+offset; } + if(direction==1) { CTX(R)[regPC] = CTX(R)[regPC]-(offset+1); } return 9; } return 7; @@ -521,7 +524,7 @@ int MVO(int v) // Move Out { int reg = v & 0x07; int adr = readOperand(); - writeMem(adr, R[reg]); + writeMem(adr, CTX(R)[reg]); return 11; } int MVOa(int v) // MVO@ - Move Out Indirect 0000:0010:01aa:asss @@ -529,40 +532,40 @@ int MVOa(int v) // MVO@ - Move Out Indirect 0000:0010:01aa:asss // The PSHR Rx instruction is an alias for MVOa Rx, R6 int areg = (v >> 3) & 0x7; int sreg = v & 0x7; - writeIndirect(areg, R[sreg]); + writeIndirect(areg, CTX(R)[sreg]); return 9; } int MVOI(int v) // Move Out Immediate 0000:0010:0111:1sss { - return(MVOa(v)); // call indirect copies R[sss] to address in R[PC] + return(MVOa(v)); // call indirect copies CTX(R)[sss] to address in CTX(R)[regPC] } int MVI(int v) // Move In 0000:0010:1000:0rrr aaaa:aaaa:aaaa:aaaa { int reg = v & 0x07; - R[reg] = readOperandIndirect(); + CTX(R)[reg] = readOperandIndirect(); return 10; } int MVIa(int v) // Move In Indirect 0000:0010:10aa:addd { int areg = (v >> 3) & 0x7; int dreg = v & 0x7; - R[dreg] = readIndirect(areg); - return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + CTX(R)[dreg] = readIndirect(areg); + return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); } int MVII(int v) // Move In Immediate (copies operand to register) { - // These instructions are only one word, so don't advance PC past operand. + // These instructions are only one word, so don't advance regPC past operand. // Auto incrementing registers will move past the operands automatically. - // This works exactly like MVI@ with PC as the address register. + // This works exactly like MVI@ with regPC as the address register. // All nnnI instructions work this way. - v = v | 0x0038; // set address register to PC + v = v | 0x0038; // set address register to regPC return(MVIa(v)); // call indirect } int ADD(int v) // Add { int reg = v & 0x07; int val = readOperandIndirect(); - R[reg] = AddSetSZOC(R[reg], val); + CTX(R)[reg] = AddSetSZOC(CTX(R)[reg], val); return 10; } int ADDa(int v) // Add Indirect @@ -570,19 +573,19 @@ int ADDa(int v) // Add Indirect int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - R[dreg] = AddSetSZOC(R[dreg], val); - return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + CTX(R)[dreg] = AddSetSZOC(CTX(R)[dreg], val); + return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); } int ADDI(int v) // Add Immediate { - v = v | 0x0038; // set address register to PC + v = v | 0x0038; // set address register to regPC return(ADDa(v)); // call indirect } int SUB(int v) // Subtract { int reg = v & 0x07; int val = readOperandIndirect(); - R[reg] = SubSetOC(R[reg], val); + CTX(R)[reg] = SubSetOC(CTX(R)[reg], val); SetFlagsSZ(reg); return 10; } @@ -591,22 +594,22 @@ int SUBa(int v) // Subtract Indirect int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - R[dreg] = SubSetOC(R[dreg], val); + CTX(R)[dreg] = SubSetOC(CTX(R)[dreg], val); SetFlagsSZ(dreg); - return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); } int SUBI(int v) // Subtract Immediate { - v = v | 0x0038; // set address register to PC + v = v | 0x0038; // set address register to regPC return(SUBa(v)); // call indirect } int CMP(int v) { int reg = v & 0x07; int val = readOperandIndirect(); - int res = SubSetOC(R[reg], val); - Flag_Sign = (res & 0x8000)!=0; - Flag_Zero = res==0; + int res = SubSetOC(CTX(R)[reg], val); + CTX(Flag_Sign) = (res & 0x8000)!=0; + CTX(Flag_Zero) = res==0; return 10; } int CMPa(int v) @@ -614,21 +617,21 @@ int CMPa(int v) int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - int res = SubSetOC(R[dreg], val); - Flag_Sign = (res & 0x8000)!=0; - Flag_Zero = res==0; - return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + int res = SubSetOC(CTX(R)[dreg], val); + CTX(Flag_Sign) = (res & 0x8000)!=0; + CTX(Flag_Zero) = res==0; + return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); } int CMPI(int v) // CMP Immediate { - v = v | 0x0038; // set address register to PC + v = v | 0x0038; // set address register to regPC return(CMPa(v)); // call indirect } int AND(int v) // And { int reg = v & 0x07; int val = readOperandIndirect(); - R[reg] = R[reg] & val; + CTX(R)[reg] = CTX(R)[reg] & val; SetFlagsSZ(reg); return 10; } @@ -637,20 +640,20 @@ int ANDa(int v) // And Indirect int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - R[dreg] = R[dreg] & val; + CTX(R)[dreg] = CTX(R)[dreg] & val; SetFlagsSZ(dreg); - return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); } int ANDI(int v) // And Immediate { - v = v | 0x0038; // set address register to PC + v = v | 0x0038; // set address register to regPC return(ANDa(v)); // call indirect } int XOR(int v) // Xor { int reg = v & 0x07; int val = readOperandIndirect(); - R[reg] = R[reg] ^ val; + CTX(R)[reg] = CTX(R)[reg] ^ val; SetFlagsSZ(reg); return 10; } @@ -659,13 +662,13 @@ int XORa(int v) // Xor Indirect int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - R[dreg] = R[dreg] ^ val; + CTX(R)[dreg] = CTX(R)[dreg] ^ val; SetFlagsSZ(dreg); - return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); } int XORI(int v) // Xor Immediate { - v = v | 0x0038; // set address register to PC + v = v | 0x0038; // set address register to regPC return(XORa(v)); // call indirect } @@ -677,9 +680,9 @@ void addInstruction(int start, int end, int caninterupt, const char *name, int ( int i; for(i=start; i<=end; i++) { - Interuptable[i] = caninterupt; - Nmemonic[i] = name; - OpCodes[i] = callback; + CTX(Interuptable)[i] = caninterupt; + CTX(Nmemonic)[i] = name; + CTX(OpCodes)[i] = callback; } } diff --git a/src/cp1610.h b/src/cp1610.h index aede93e..a97934a 100644 --- a/src/cp1610.h +++ b/src/cp1610.h @@ -17,10 +17,36 @@ along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ +#include +typedef struct CP1610_Context_t +{ + uint32_t Version; + int (*OpCodes[0x400])(int); + int Interuptable[0x400]; + const char *Nmemonic[0x400]; + + unsigned int R[8]; // Registers R0-R7 + + int InstructionRegister; // four external lines? + + int Flag_DoubleByteData; + int Flag_InteruptEnable; + int Flag_Carry; + int Flag_Sign; + int Flag_Zero; + int Flag_Overflow; + + unsigned int Memory[0x10000]; +} CP1610_Context, *PCP1610_Context; + +extern CP1610_Context gCP1610_Context; +#define CTX(x) gCP1610_Context.x +#define CP1610_SERIALIZE_VERSION 1 + void CP1610Init(void); // Adds opcodes to lookup tables void CP1610Reset(void); // reset cpu int CP1610Tick(int debug); // execute a single instruction, return cycles used -#endif \ No newline at end of file +#endif diff --git a/src/intv.c b/src/intv.c index b00dd0a..fdd7f83 100644 --- a/src/intv.c +++ b/src/intv.c @@ -53,7 +53,7 @@ void loadExec(const char* path) for(i=0x1000; i<=0x1FFF; i++) { fread(word,sizeof(word),1,fp); - Memory[i] = (word[0]<<8) | word[1]; + CTX(Memory)[i] = (word[0]<<8) | word[1]; } fclose(fp); @@ -79,7 +79,7 @@ void loadGrom(const char* path) for(i=0x3000; i<=0x37FF; i++) { fread(word,sizeof(word),1,fp); - Memory[i] = word[0]; + CTX(Memory)[i] = word[0]; } fclose(fp); @@ -170,8 +170,8 @@ int exec(void) // Run one instruction stic_gram = 1; // GRAM accessible break; case 2: - delayV = ((Memory[0x31])&0x7); - delayH = ((Memory[0x30])&0x7); + delayV = ((CTX(Memory)[0x31])&0x7); + delayH = ((CTX(Memory)[0x30])&0x7); phase_len += 120 + 114 * delayV + delayH; if (stic_vid_enable) { stic_gram = 0; // GRAM now inaccessible @@ -187,8 +187,8 @@ int exec(void) // Run one instruction } break; case 14: - delayV = ((Memory[0x31])&0x7); - delayH = ((Memory[0x30])&0x7); + delayV = ((CTX(Memory)[0x31])&0x7); + delayH = ((CTX(Memory)[0x30])&0x7); phase_len += 912 - 114 * delayV - delayH; if (stic_vid_enable) { phase_len -= 108; // BUSRQ period (STIC reads RAM) @@ -196,7 +196,7 @@ int exec(void) // Run one instruction } break; case 15: - delayV = ((Memory[0x31])&0x7); + delayV = ((CTX(Memory)[0x31])&0x7); phase_len += 57 + 17; if (stic_vid_enable && delayV == 0) { phase_len -= 38; // BUSRQ period (STIC reads RAM) diff --git a/src/libretro.c b/src/libretro.c index 6a410f1..ceaeaf3 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -24,6 +24,7 @@ #include "intv.h" #include "memory.h" +#include "cp1610.h" #include "stic.h" #include "psg.h" #include "controller.h" @@ -373,7 +374,7 @@ RETRO_API void *retro_get_memory_data(unsigned id) { if(id==RETRO_MEMORY_SYSTEM_RAM) { - return Memory; + return CTX(Memory); } return 0; } @@ -387,11 +388,43 @@ RETRO_API size_t retro_get_memory_size(unsigned id) return 0; } +size_t retro_serialize_size(void) { + return sizeof(gCP1610_Context); +} + +bool retro_serialize(void *data, size_t size) { + if(size != retro_serialize_size()) + { + // Uhoh + return false; + } + + memcpy(data, &gCP1610_Context, sizeof(gCP1610_Context)); + + return true; +} + +bool retro_unserialize(const void *data, size_t size) { + if(size != retro_serialize_size()) + { + // Uhoh + return false; + } + + PCP1610_Context restoreContext = (PCP1610_Context)data; + if(restoreContext->Version != 1) + { + // Uhoh, TODO + return false; + } + + memcpy(&gCP1610_Context, restoreContext, sizeof(gCP1610_Context)); + + return true; +} + /* Stubs */ unsigned int retro_api_version(void) { return RETRO_API_VERSION; } -size_t retro_serialize_size(void) { return 0; } -bool retro_serialize(void *data, size_t size) { return false; } -bool retro_unserialize(const void *data, size_t size) { return false; } void retro_cheat_reset(void) { } void retro_cheat_set(unsigned index, bool enabled, const char *code) { } bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) { return false; } diff --git a/src/memory.c b/src/memory.c index df48700..d4f6a94 100644 --- a/src/memory.c +++ b/src/memory.c @@ -14,14 +14,14 @@ You should have received a copy of the GNU General Public License along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ + +#include "cp1610.h" #include "memory.h" #include "stic.h" #include "psg.h" #include -unsigned int Memory[0x10000]; - int stic_and[64] = { 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, @@ -65,13 +65,13 @@ void writeMem(int adr, int val) // Write (should handle hooks/alias) case 0x17: case 0x1f: if (stic_gram != 0) - Memory[adr & 0x39FF] = val; + CTX(Memory)[adr & 0x39FF] = val; return; } if(adr>=0x100 && adr<=0x1FF) { val = val & 0xFF; - Memory[adr] = val; + CTX(Memory)[adr] = val; //PSG Registers if(adr>=0x01F0 && adr<=0x1FD) { @@ -96,11 +96,11 @@ void writeMem(int adr, int val) // Write (should handle hooks/alias) if((adr>=0x0000 && adr<=0x003F) || (adr>=0x4000 && adr<=0x403F) || (adr>=0x8000 && adr<=0x803F) || (adr>=0xC000 && adr<=0xC03F)) { if (stic_reg != 0) - Memory[adr & 0x3F] = (val & stic_and[adr & 0x3f]) | stic_or[adr & 0x3f];; + CTX(Memory)[adr & 0x3F] = (val & stic_and[adr & 0x3f]) | stic_or[adr & 0x3f];; return; } - Memory[adr] = val; + CTX(Memory)[adr] = val; } @@ -111,7 +111,7 @@ int readMem(int adr) // Read (should handle hooks/alias) int val; adr &= 0xffff; - val = Memory[adr]; + val = CTX(Memory)[adr]; if(adr>=0x100 && adr<=0x1FF) { @@ -122,7 +122,7 @@ int readMem(int adr) // Read (should handle hooks/alias) { if(adr<=0x3F) { - val = (Memory[adr] & stic_and[adr]) | stic_or[adr]; + val = (CTX(Memory)[adr] & stic_and[adr]) | stic_or[adr]; } // read sensitive addresses @@ -137,25 +137,25 @@ int readMem(int adr) // Read (should handle hooks/alias) void MemoryInit() { int i; - for(i=0x0000; i<=0x0007; i++) { Memory[i] = 0x3800; } // STIC Registers - for(i=0x0008; i<=0x000F; i++) { Memory[i] = 0x3000; } - for(i=0x0010; i<=0x0017; i++) { Memory[i] = 0x0000; } - for(i=0x0018; i<=0x001F; i++) { Memory[i] = 0x3C00; } - for(i=0x0020; i<=0x003F; i++) { Memory[i] = 0x3FFF; } - for(i=0x0028; i<=0x002C; i++) { Memory[i] = 0x3FF0; } - Memory[0x30] = 0x3FF8; - Memory[0x31] = 0x3FF8; - Memory[0x32] = 0x3FFC; - for(i=0x0040; i<=0x007F; i++) { Memory[i] = 0x0000; } - for(i=0x0080; i<=0x00FF; i++) { Memory[i] = 0xFFFF; } - for(i=0x0100; i<=0x035F; i++) { Memory[i] = 0x0000; } // Scratch, PSG (1F0-1FF), System Ram - for(i=0x0360; i<=0x0FFF; i++) { Memory[i] = 0xFFFF; } - for(i=0x1000; i<=0x1FFF; i++) { Memory[i] = 0x0000; } // EXEC ROM - for(i=0x2000; i<=0x2FFF; i++) { Memory[i] = 0xFFFF; } - for(i=0x3000; i<=0x3FFF; i++) { Memory[i] = 0x0000; } // GROM, GRAM - for(i=0x4000; i<=0x4FFF; i++) { Memory[i] = 0xFFFF; } - for(i=0x5000; i<=0x5FFF; i++) { Memory[i] = 0x0000; } - for(i=0x6000; i<=0xFFFF; i++) { Memory[i] = 0xFFFF; } - Memory[0x1FE] = 0xFF; // Controller R - Memory[0x1FF] = 0xFF; // Controller L + for(i=0x0000; i<=0x0007; i++) { CTX(Memory)[i] = 0x3800; } // STIC Registers + for(i=0x0008; i<=0x000F; i++) { CTX(Memory)[i] = 0x3000; } + for(i=0x0010; i<=0x0017; i++) { CTX(Memory)[i] = 0x0000; } + for(i=0x0018; i<=0x001F; i++) { CTX(Memory)[i] = 0x3C00; } + for(i=0x0020; i<=0x003F; i++) { CTX(Memory)[i] = 0x3FFF; } + for(i=0x0028; i<=0x002C; i++) { CTX(Memory)[i] = 0x3FF0; } + CTX(Memory)[0x30] = 0x3FF8; + CTX(Memory)[0x31] = 0x3FF8; + CTX(Memory)[0x32] = 0x3FFC; + for(i=0x0040; i<=0x007F; i++) { CTX(Memory)[i] = 0x0000; } + for(i=0x0080; i<=0x00FF; i++) { CTX(Memory)[i] = 0xFFFF; } + for(i=0x0100; i<=0x035F; i++) { CTX(Memory)[i] = 0x0000; } // Scratch, PSG (1F0-1FF), System Ram + for(i=0x0360; i<=0x0FFF; i++) { CTX(Memory)[i] = 0xFFFF; } + for(i=0x1000; i<=0x1FFF; i++) { CTX(Memory)[i] = 0x0000; } // EXEC ROM + for(i=0x2000; i<=0x2FFF; i++) { CTX(Memory)[i] = 0xFFFF; } + for(i=0x3000; i<=0x3FFF; i++) { CTX(Memory)[i] = 0x0000; } // GROM, GRAM + for(i=0x4000; i<=0x4FFF; i++) { CTX(Memory)[i] = 0xFFFF; } + for(i=0x5000; i<=0x5FFF; i++) { CTX(Memory)[i] = 0x0000; } + for(i=0x6000; i<=0xFFFF; i++) { CTX(Memory)[i] = 0xFFFF; } + CTX(Memory)[0x1FE] = 0xFF; // Controller R + CTX(Memory)[0x1FF] = 0xFF; // Controller L } diff --git a/src/psg.c b/src/psg.c index f61c2cf..8abc33b 100644 --- a/src/psg.c +++ b/src/psg.c @@ -18,6 +18,7 @@ #include #include #include "psg.h" +#include "cp1610.h" #include "memory.h" int PSGBufferSize; @@ -72,28 +73,28 @@ int EnvHold; void readRegisters(void) { - ChA = (Memory[0x01F0] & 0xFF) | ((Memory[0x1F4] & 0x0F)<<8); - ChB = (Memory[0x01F1] & 0xFF) | ((Memory[0x1F5] & 0x0F)<<8); - ChC = (Memory[0x01F2] & 0xFF) | ((Memory[0x1F6] & 0x0F)<<8); + ChA = (CTX(Memory)[0x01F0] & 0xFF) | ((CTX(Memory)[0x1F4] & 0x0F)<<8); + ChB = (CTX(Memory)[0x01F1] & 0xFF) | ((CTX(Memory)[0x1F5] & 0x0F)<<8); + ChC = (CTX(Memory)[0x01F2] & 0xFF) | ((CTX(Memory)[0x1F6] & 0x0F)<<8); - VolA = Memory[0x01FB] & 0x0F; - VolB = Memory[0x01FC] & 0x0F; - VolC = Memory[0x01FD] & 0x0F; + VolA = CTX(Memory)[0x01FB] & 0x0F; + VolB = CTX(Memory)[0x01FC] & 0x0F; + VolC = CTX(Memory)[0x01FD] & 0x0F; - NoiseP = (Memory[0x01F9] & 0x1F)<<1; - NoiseA = (Memory[0x01F8]>>3) & 0x01; - NoiseB = (Memory[0x01F8]>>4) & 0x01; - NoiseC = (Memory[0x01F8]>>5) & 0x01; + NoiseP = (CTX(Memory)[0x01F9] & 0x1F)<<1; + NoiseA = (CTX(Memory)[0x01F8]>>3) & 0x01; + NoiseB = (CTX(Memory)[0x01F8]>>4) & 0x01; + NoiseC = (CTX(Memory)[0x01F8]>>5) & 0x01; - ToneA = Memory[0x01F8] & 0x01; - ToneB = (Memory[0x01F8]>>1) & 0x01; - ToneC = (Memory[0x01F8]>>2) & 0x01; + ToneA = CTX(Memory)[0x01F8] & 0x01; + ToneB = (CTX(Memory)[0x01F8]>>1) & 0x01; + ToneC = (CTX(Memory)[0x01F8]>>2) & 0x01; - EnvP = ((Memory[0x01F3] & 0xFF) | ((Memory[0x1F7] & 0xFF)<<8))<<1; - EnvFlags = Memory[0x01FA] & 0x0F; - EnvA = (Memory[0x01FB]>>4) & 0x03; - EnvB = (Memory[0x01FC]>>4) & 0x03; - EnvC = (Memory[0x01FD]>>4) & 0x03; + EnvP = ((CTX(Memory)[0x01F3] & 0xFF) | ((CTX(Memory)[0x1F7] & 0xFF)<<8))<<1; + EnvFlags = CTX(Memory)[0x01FA] & 0x0F; + EnvA = (CTX(Memory)[0x01FB]>>4) & 0x03; + EnvB = (CTX(Memory)[0x01FC]>>4) & 0x03; + EnvC = (CTX(Memory)[0x01FD]>>4) & 0x03; ChA = ChA + (0x1000 * (ChA==0)); // a Channel Period value of 0 ChB = ChB + (0x1000 * (ChB==0)); // indicates a value of 0x1000 @@ -141,7 +142,7 @@ int psg_masks[16] = { void PSGNotify(int adr, int val) // PSG Registers Modified 0x01F0-0x1FD (called from writeMem) { - Memory[adr] &= psg_masks[adr - 0x1f0]; + CTX(Memory)[adr] &= psg_masks[adr - 0x1f0]; readRegisters(); // Note: updating frequencies doesn't reset counters in real chip // (otherwise sound glitch happens in games) diff --git a/src/stic.c b/src/stic.c index fdde89c..e211f70 100644 --- a/src/stic.c +++ b/src/stic.c @@ -17,6 +17,7 @@ #include "intv.h" #include "memory.h" +#include "cp1610.h" #include "stic.h" #include @@ -135,7 +136,7 @@ void drawBorder(int scanline) { int i; int cbit = 1<<9; // bit 9 - border collision - int color = colors[Memory[0x2C] & 0x0f]; // border color + int color = colors[CTX(Memory)[0x2C] & 0x0f]; // border color if(scanline>=112) { return; } if (scanline == delayV - 1 || scanline == 104) { @@ -198,14 +199,14 @@ void drawBackgroundFGBG(int scanline) // Draw cards for (col=0; col<20; col++) // for each card on the current row... { - card = Memory[0x200+row+col]; // card info from BACKTAB + card = CTX(Memory)[0x200+row+col]; // card info from BACKTAB fgcolor = colors[card & 0x07]; bgcolor = colors[((card>>9)&0x03) | ((card>>11)&0x04) | ((card>>9)&0x08)]; // bits 12,13,10,9 gaddress = 0x3000 + (card & 0x09f8); - gdata = Memory[gaddress + cardrow]; // fetch current line of current card graphic + gdata = CTX(Memory)[gaddress + cardrow]; // fetch current line of current card graphic for(i=7; i>=0; i--) // draw one line of card graphic { @@ -260,12 +261,12 @@ void drawBackgroundColorStack(int scanline) // Draw cards for (col=0; col<20; col++) // for each card on the current row... { - card = Memory[0x200+row+col]; // card info from BACKTAB + card = CTX(Memory)[0x200+row+col]; // card info from BACKTAB if(((card>>11)&0x03)==2) // Color Squares Mode { if (cardrow == 0) - bgcard[col] = colors[Memory[CSP] & 0x0F]; + bgcard[col] = colors[CTX(Memory)[CSP] & 0x0F]; // set colors colors[7] = bgcard[col]; // color 7 is top of color stack color1 = card & 0x07; @@ -309,7 +310,7 @@ void drawBackgroundColorStack(int scanline) advcolor = (card>>13) & 0x01; // do we need to advance the CSP? CSP = (CSP+advcolor) & 0x2B; // cycles through 0x28-0x2B fgcard[col] = colors[(card&0x07)|((card>>9)&0x08)]; // bits 12, 2, 1, 0 - bgcard[col] = colors[Memory[CSP] & 0x0F]; + bgcard[col] = colors[CTX(Memory)[CSP] & 0x0F]; } fgcolor = fgcard[col]; @@ -320,7 +321,7 @@ void drawBackgroundColorStack(int scanline) else gaddress = 0x3000 + (card & 0x0ff8); - gdata = Memory[gaddress + cardrow]; // fetch current line of current card graphic + gdata = CTX(Memory)[gaddress + cardrow]; // fetch current line of current card graphic for(i=7; i>=0; i--) // draw one line of card graphic { if(((gdata>>i)&1)==1) @@ -374,9 +375,9 @@ void drawSprites(int scanline) // MOBs for(i=7; i>=0; i--) // draw sprites 0-7 in reverse order { - Rx = Memory[0x00+i]; // 14 bits ; -- -SVI xxxx xxxx ; Size, Visible, Interactive, X Position - Ry = Memory[0x08+i]; // 14 bits ; -- YX42 Ryyy yyyy ; Flip Y, Flip X, Size 4, Size 2, Y Resolution, Y Position - Ra = Memory[0x10+i]; // 14 bits ; PF Gnnn nnnn nFFF ; Priority, FG Color Bit 3, GRAM, n Card #, FG Color Bits 2-0 + Rx = CTX(Memory)[0x00+i]; // 14 bits ; -- -SVI xxxx xxxx ; Size, Visible, Interactive, X Position + Ry = CTX(Memory)[0x08+i]; // 14 bits ; -- YX42 Ryyy yyyy ; Flip Y, Flip X, Size 4, Size 2, Y Resolution, Y Position + Ra = CTX(Memory)[0x10+i]; // 14 bits ; PF Gnnn nnnn nFFF ; Priority, FG Color Bit 3, GRAM, n Card #, FG Color Bits 2-0 posX = Rx & 0xFF; posY = Ry & 0x7F; @@ -428,14 +429,14 @@ void drawSprites(int scanline) // MOBs { spriterow = (7+(8*yRes)) - spriterow; gaddress = gaddress + spriterow; - gdata = Memory[gaddress] & 0xFF; - gdata2 = Memory[gaddress - (sizeY==0)] & 0xFF; + gdata = CTX(Memory)[gaddress] & 0xFF; + gdata2 = CTX(Memory)[gaddress - (sizeY==0)] & 0xFF; } else { gaddress = gaddress + spriterow; - gdata = Memory[gaddress] & 0xFF; - gdata2 = Memory[gaddress + (sizeY==0)] & 0xFF; + gdata = CTX(Memory)[gaddress] & 0xFF; + gdata2 = CTX(Memory)[gaddress + (sizeY==0)] & 0xFF; } if(flipX) @@ -493,7 +494,7 @@ void STICDrawFrame(int enabled) if (enabled == 0) { for (row = 0; row < 112; row++) { - int color = colors[Memory[0x2C] & 0x0f]; // border color + int color = colors[CTX(Memory)[0x2C] & 0x0f]; // border color for(i=0; i<352; i++) { @@ -505,12 +506,12 @@ void STICDrawFrame(int enabled) offset += 352 * 2; } } else { - extendTop = (Memory[0x32]>>1)&0x01; + extendTop = (CTX(Memory)[0x32]>>1)&0x01; - extendLeft = (Memory[0x32])&0x01; + extendLeft = (CTX(Memory)[0x32])&0x01; - delayV = 8 + ((Memory[0x31])&0x7); - delayH = 8 + ((Memory[0x30])&0x7); + delayV = 8 + ((CTX(Memory)[0x31])&0x7); + delayH = 8 + ((CTX(Memory)[0x30])&0x7); delayH = delayH * 2; @@ -547,7 +548,7 @@ void STICDrawFrame(int enabled) continue; for (j = 0; j < 8; j++) { if (((collBuffer[i] >> j) & 1) != 0) { - Memory[0x18 + j] |= collBuffer[i] & ~(1 << j); + CTX(Memory)[0x18 + j] |= collBuffer[i] & ~(1 << j); } } } From 61dcaa0111be92940536ef3a634c346f0e5036a0 Mon Sep 17 00:00:00 2001 From: BlazeEntertainment <85935589+BlazeEntertainment@users.noreply.github.com> Date: Thu, 1 Jul 2021 18:10:10 +0100 Subject: [PATCH 05/15] Remove leftover Makefile modification --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index f775be4..0c023ef 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ -CFLAGS += -DXBOXPADSTYLEHELP -DSLAPSHOT STATIC_LINKING=0 AR := ar From 9730fc8642b6ae36f5559ba6d59e56fb45c6f9f0 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 21 Jul 2021 15:23:34 +0200 Subject: [PATCH 06/15] Remove OpCodes, Interuptable, Nmemonic from serialization Oversight when doing the initial impl --- src/cp1610.c | 14 +++++++++----- src/cp1610.h | 3 --- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/cp1610.c b/src/cp1610.c index 66b098b..57df2ba 100644 --- a/src/cp1610.c +++ b/src/cp1610.c @@ -27,6 +27,10 @@ #define regSP 6 // Stack Pointer (R6) #define regPC 7 // Program Counter (R7) +static int (*OpCodes[0x400])(int); +static int Interuptable[0x400]; +static const char *Nmemonic[0x400]; + CP1610_Context gCP1610_Context = { .Version = CP1610_SERIALIZE_VERSION, @@ -170,14 +174,14 @@ int CP1610Tick(int debug) CTX(R)[regPC]++; // point regPC/R7 at operand/next address - ticks = CTX(OpCodes)[instruction](instruction); // execute instruction + ticks = OpCodes[instruction](instruction); // execute instruction if(sdbd==1) { CTX(Flag_DoubleByteData) = 0; } // reset SDBD // check interupt request if(CTX(Flag_InteruptEnable) == 1 && SR1>0) { - if(CTX(Interuptable)[instruction]) + if(Interuptable[instruction]) { // Take VBlank Interupt // SR1 = 0; @@ -680,9 +684,9 @@ void addInstruction(int start, int end, int caninterupt, const char *name, int ( int i; for(i=start; i<=end; i++) { - CTX(Interuptable)[i] = caninterupt; - CTX(Nmemonic)[i] = name; - CTX(OpCodes)[i] = callback; + Interuptable[i] = caninterupt; + Nmemonic[i] = name; + OpCodes[i] = callback; } } diff --git a/src/cp1610.h b/src/cp1610.h index a97934a..9b29b94 100644 --- a/src/cp1610.h +++ b/src/cp1610.h @@ -21,9 +21,6 @@ typedef struct CP1610_Context_t { uint32_t Version; - int (*OpCodes[0x400])(int); - int Interuptable[0x400]; - const char *Nmemonic[0x400]; unsigned int R[8]; // Registers R0-R7 From c1f4982a620758abdabfeb753d92ad57fb09ca07 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 21 Jul 2021 15:24:15 +0200 Subject: [PATCH 07/15] Serialization for stic --- src/cp1610.c | 8 +- src/cp1610.h | 27 ++++++ src/intv.c | 66 +++++++-------- src/libretro.c | 10 +-- src/memory.c | 16 ++-- src/stic.c | 222 ++++++++++++++++++++++--------------------------- src/stic.h | 14 ---- 7 files changed, 179 insertions(+), 184 deletions(-) diff --git a/src/cp1610.c b/src/cp1610.c index 57df2ba..56b29e2 100644 --- a/src/cp1610.c +++ b/src/cp1610.c @@ -43,7 +43,13 @@ CP1610_Context gCP1610_Context = { .Flag_Carry = 0, .Flag_Sign = 0, .Flag_Zero = 0, - .Flag_Overflow = 0 + .Flag_Overflow = 0, + + // Stic + .delayH = 0, + .delayV = 0, + .extendTop = 0, + .extendLeft = 0 }; void CP1610Reset() diff --git a/src/cp1610.h b/src/cp1610.h index 9b29b94..92d64f9 100644 --- a/src/cp1610.h +++ b/src/cp1610.h @@ -34,6 +34,33 @@ typedef struct CP1610_Context_t int Flag_Overflow; unsigned int Memory[0x10000]; + + // Stick + unsigned int STICMode; + + int stic_phase; + int stic_vid_enable; + int stic_reg; + int stic_gram; + int phase_len; + + int DisplayEnabled; + + unsigned int frame[352*224]; + + unsigned int scanBuffer[768]; // buffer for current scanline (352+32)*2 + unsigned int collBuffer[768]; // buffer for collision -- made larger than needed to save checks + + int delayH; // Horizontal Delay + int delayV; // Vertical Delay + + int extendTop; + int extendLeft; + + unsigned int CSP; // Color Stack Pointer + unsigned int cscolors[4]; // color squares colors + unsigned int fgcard[20]; // cached colors for cards on current row + unsigned int bgcard[20]; // (used for normal color stack mode) } CP1610_Context, *PCP1610_Context; extern CP1610_Context gCP1610_Context; diff --git a/src/intv.c b/src/intv.c index fdd7f83..14fb949 100644 --- a/src/intv.c +++ b/src/intv.c @@ -149,57 +149,57 @@ int exec(void) // Run one instruction if(SR1<0) { SR1 = 0; } } - phase_len -= ticks; - if (phase_len < 0) { - stic_phase = (stic_phase + 1) & 15; - switch (stic_phase) { + CTX(phase_len) -= ticks; + if (CTX(phase_len) < 0) { + CTX(stic_phase) = (CTX(stic_phase) + 1) & 15; + switch (CTX(stic_phase)) { case 0: // Start of VBLANK - stic_reg = 1; // STIC registers accessible - stic_gram = 1; // GRAM accessible - phase_len += 2900; - SR1 = phase_len; + CTX(stic_reg) = 1; // STIC registers accessible + CTX(stic_gram) = 1; // GRAM accessible + CTX(phase_len) += 2900; + SR1 = CTX(phase_len); // Render Frame // - STICDrawFrame(stic_vid_enable); + STICDrawFrame(CTX(stic_vid_enable)); return 0; case 1: - phase_len += 3796 - 2900; - stic_vid_enable = DisplayEnabled; - DisplayEnabled = 0; - if (stic_vid_enable) - stic_reg = 0; // STIC registers now inaccessible - stic_gram = 1; // GRAM accessible + CTX(phase_len) += 3796 - 2900; + CTX(stic_vid_enable) = CTX(DisplayEnabled); + CTX(DisplayEnabled) = 0; + if (CTX(stic_vid_enable)) + CTX(stic_reg) = 0; // STIC registers now inaccessible + CTX(stic_gram) = 1; // GRAM accessible break; case 2: - delayV = ((CTX(Memory)[0x31])&0x7); - delayH = ((CTX(Memory)[0x30])&0x7); - phase_len += 120 + 114 * delayV + delayH; - if (stic_vid_enable) { - stic_gram = 0; // GRAM now inaccessible - phase_len -= 68; // BUSRQ period (STIC reads RAM) + CTX(delayV) = ((CTX(Memory)[0x31])&0x7); + CTX(delayH) = ((CTX(Memory)[0x30])&0x7); + CTX(phase_len) += 120 + 114 * CTX(delayV) + CTX(delayH); + if (CTX(stic_vid_enable)) { + CTX(stic_gram) = 0; // GRAM now inaccessible + CTX(phase_len) -= 68; // BUSRQ period (STIC reads RAM) PSGTick(68); } break; default: - phase_len += 912; - if (stic_vid_enable) { - phase_len -= 108; // BUSRQ period (STIC reads RAM) + CTX(phase_len) += 912; + if (CTX(stic_vid_enable)) { + CTX(phase_len) -= 108; // BUSRQ period (STIC reads RAM) PSGTick(108); } break; case 14: - delayV = ((CTX(Memory)[0x31])&0x7); - delayH = ((CTX(Memory)[0x30])&0x7); - phase_len += 912 - 114 * delayV - delayH; - if (stic_vid_enable) { - phase_len -= 108; // BUSRQ period (STIC reads RAM) + CTX(delayV) = ((CTX(Memory)[0x31])&0x7); + CTX(delayH) = ((CTX(Memory)[0x30])&0x7); + CTX(phase_len) += 912 - 114 * CTX(delayV) - CTX(delayH); + if (CTX(stic_vid_enable)) { + CTX(phase_len) -= 108; // BUSRQ period (STIC reads RAM) PSGTick(108); } break; case 15: - delayV = ((CTX(Memory)[0x31])&0x7); - phase_len += 57 + 17; - if (stic_vid_enable && delayV == 0) { - phase_len -= 38; // BUSRQ period (STIC reads RAM) + CTX(delayV) = ((CTX(Memory)[0x31])&0x7); + CTX(phase_len) += 57 + 17; + if (CTX(stic_vid_enable) && CTX(delayV) == 0) { + CTX(phase_len) -= 38; // BUSRQ period (STIC reads RAM) PSGTick(38); } break; diff --git a/src/libretro.c b/src/libretro.c index ceaeaf3..9028c16 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -115,8 +115,8 @@ void retro_init(void) struct retro_keyboard_callback kb = { Keyboard }; // init buffers, structs - memset(frame, 0, frameSize); - OSD_setDisplay(frame, MaxWidth, MaxHeight); + memset(CTX(frame), 0, frameSize); + OSD_setDisplay(CTX(frame), MaxWidth, MaxHeight); // setup controller swap controllerInit(); @@ -281,8 +281,8 @@ void retro_run(void) Run(); // draw overlays - if(showKeypad0) { drawMiniKeypad(0, frame); } - if(showKeypad1) { drawMiniKeypad(1, frame); } + if(showKeypad0) { drawMiniKeypad(0, CTX(frame)); } + if(showKeypad1) { drawMiniKeypad(1, CTX(frame)); } // sample audio from buffer audioInc = 3733.5 / audioSamples; @@ -319,7 +319,7 @@ void retro_run(void) if (intv_halt) OSD_drawTextBG(3, 5, "INTELLIVISION HALTED"); // send frame to libretro - Video(frame, frameWidth, frameHeight, sizeof(unsigned int) * frameWidth); + Video(CTX(frame), frameWidth, frameHeight, sizeof(unsigned int) * frameWidth); } diff --git a/src/memory.c b/src/memory.c index d4f6a94..ffb490f 100644 --- a/src/memory.c +++ b/src/memory.c @@ -64,7 +64,7 @@ void writeMem(int adr, int val) // Write (should handle hooks/alias) case 0x0f: case 0x17: case 0x1f: - if (stic_gram != 0) + if (CTX(stic_gram) != 0) CTX(Memory)[adr & 0x39FF] = val; return; } @@ -83,19 +83,19 @@ void writeMem(int adr, int val) // Write (should handle hooks/alias) // STIC Display Enable if(adr==0x20 || adr==0x4020 || adr==0x8020 || adr==0xC020) { - if (stic_reg != 0) - DisplayEnabled = 1; + if (CTX(stic_reg) != 0) + CTX(DisplayEnabled) = 1; } // STIC Mode Select if(adr==0x21 || adr==0x4021 || adr==0x8021 || adr==0xC021) { - if (stic_reg != 0) - STICMode = 0; + if (CTX(stic_reg) != 0) + CTX(STICMode) = 0; } //STIC Alias if((adr>=0x0000 && adr<=0x003F) || (adr>=0x4000 && adr<=0x403F) || (adr>=0x8000 && adr<=0x803F) || (adr>=0xC000 && adr<=0xC03F)) { - if (stic_reg != 0) + if (CTX(stic_reg) != 0) CTX(Memory)[adr & 0x3F] = (val & stic_and[adr & 0x3f]) | stic_or[adr & 0x3f];; return; } @@ -118,7 +118,7 @@ int readMem(int adr) // Read (should handle hooks/alias) val = val & 0xFF; } - if(stic_reg != 0) + if(CTX(stic_reg) != 0) { if(adr<=0x3F) { @@ -128,7 +128,7 @@ int readMem(int adr) // Read (should handle hooks/alias) // read sensitive addresses if(adr==0x21 || adr==0x4021 || adr==0x8021 || adr==0xC021) { - STICMode = 1; + CTX(STICMode) = 1; } } return val; diff --git a/src/stic.c b/src/stic.c index e211f70..457c2af 100644 --- a/src/stic.c +++ b/src/stic.c @@ -33,31 +33,7 @@ void drawBackgroundColorStack(int scanline); // http://spatula-city.org/~im14u2c/intv/jzintv-1.0-beta3/doc/programming/stic.txt // http://spatula-city.org/~im14u2c/intv/tech/master.html -unsigned int STICMode; -int stic_phase; -int stic_vid_enable; -int stic_reg; -int stic_gram; -int phase_len; - -int DisplayEnabled; - -unsigned int frame[352*224]; - -unsigned int scanBuffer[768]; // buffer for current scanline (352+32)*2 -unsigned int collBuffer[768]; // buffer for collision -- made larger than needed to save checks - -int delayH = 0; // Horizontal Delay -int delayV = 0; // Vertical Delay - -int extendTop = 0; -int extendLeft = 0; - -unsigned int CSP; // Color Stack Pointer -unsigned int cscolors[4]; // color squares colors -unsigned int fgcard[20]; // cached colors for cards on current row -unsigned int bgcard[20]; // (used for normal color stack mode) #if defined(ABGR1555) unsigned int color7 = 0xFFFCFF; // Copy of color 7 (for color squares mode) unsigned int colors[16] = @@ -124,12 +100,12 @@ int reverse[256] = // lookup table to reverse the bits in a byte // void STICReset(void) { - STICMode = 1; + CTX(STICMode) = 1; SR1 = 0; - DisplayEnabled = 0; - CSP = 0x28; - stic_phase = 15; - phase_len = 2782; + CTX(DisplayEnabled) = 0; + CTX(CSP) = 0x28; + CTX(stic_phase) = 15; + CTX(phase_len) = 2782; } void drawBorder(int scanline) @@ -139,40 +115,40 @@ void drawBorder(int scanline) int color = colors[CTX(Memory)[0x2C] & 0x0f]; // border color if(scanline>=112) { return; } - if (scanline == delayV - 1 || scanline == 104) { + if (scanline == CTX(delayV) - 1 || scanline == 104) { for(i=7 * 2; i < (9 + 160) * 2; i += 2) { - collBuffer[i] |= cbit; - collBuffer[i+384] |= cbit; + CTX(collBuffer)[i] |= cbit; + CTX(collBuffer)[i+384] |= cbit; } } else { i = 7 * 2; - collBuffer[i] |= cbit; - collBuffer[i + 384] |= cbit; + CTX(collBuffer)[i] |= cbit; + CTX(collBuffer)[i + 384] |= cbit; i = (8 + 160) * 2; - collBuffer[i] |= cbit; - collBuffer[i + 384] |= cbit; + CTX(collBuffer)[i] |= cbit; + CTX(collBuffer)[i + 384] |= cbit; } - if (extendTop != 0) + if (CTX(extendTop) != 0) i = 16; else - i = delayV; + i = CTX(delayV); if(scanline=104) // top and bottom border { for(i=0; i<352; i++) { - scanBuffer[i] = color; - scanBuffer[i+384] = color; + CTX(scanBuffer)[i] = color; + CTX(scanBuffer)[i+384] = color; } } else // left and right border { - for(i=0; i<16+(16*extendLeft); i++) + for(i=0; i<16+(16*CTX(extendLeft)); i++) { - scanBuffer[i] = color; - scanBuffer[i+336] = color; - scanBuffer[i+384] = color; - scanBuffer[i+384+336] = color; + CTX(scanBuffer)[i] = color; + CTX(scanBuffer)[i+336] = color; + CTX(scanBuffer)[i+384] = color; + CTX(scanBuffer)[i+384+336] = color; } } } @@ -188,7 +164,7 @@ void drawBackgroundFGBG(int scanline) int gaddress; // card graphic address int gdata; // current card graphic byte int cbit = 1<<8; // bit 8 - collision bit for Background - int x = delayH; // current pixel offset + int x = CTX(delayH); // current pixel offset // Tiled background is 20x12, cards are 8x8 row = scanline / 8; // Which tile row? (Background is 96 lines high) @@ -213,21 +189,21 @@ void drawBackgroundFGBG(int scanline) if(((gdata>>i)&1)==1) { // draw pixel - scanBuffer[x] = fgcolor; - scanBuffer[x+1] = fgcolor; - scanBuffer[x+384] = fgcolor; - scanBuffer[x+384+1] = fgcolor; + CTX(scanBuffer)[x] = fgcolor; + CTX(scanBuffer)[x+1] = fgcolor; + CTX(scanBuffer)[x+384] = fgcolor; + CTX(scanBuffer)[x+384+1] = fgcolor; // write to collision buffer - collBuffer[x] |= cbit; - collBuffer[x+384] |= cbit; + CTX(collBuffer)[x] |= cbit; + CTX(collBuffer)[x+384] |= cbit; } else { // draw background - scanBuffer[x] = bgcolor; - scanBuffer[x+1] = bgcolor; - scanBuffer[x+384] = bgcolor; - scanBuffer[x+384+1] = bgcolor; + CTX(scanBuffer)[x] = bgcolor; + CTX(scanBuffer)[x+1] = bgcolor; + CTX(scanBuffer)[x+384] = bgcolor; + CTX(scanBuffer)[x+384+1] = bgcolor; } x+=2; } @@ -246,9 +222,9 @@ void drawBackgroundColorStack(int scanline) unsigned int fgcolor; int gaddress; // card graphic address int gdata; // current card graphic byte - int advcolor; // Flag - Advance CSP + int advcolor; // Flag - Advance CTX(CSP) int cbit = 1<<8; // bit 8 - collision bit for Background - int x = delayH; // current pixel offset + int x = CTX(delayH); // current pixel offset // Tiled background is 20x12, cards are 8x8 row = (scanline / 8); // Which tile row? (Background is 96 lines high) @@ -256,7 +232,7 @@ void drawBackgroundColorStack(int scanline) cardrow = scanline % 8; // which line of this row of cards to draw - if(row==0 && cardrow==0) { CSP = 0x28; } // reset CSP on display of first card on screen + if(row==0 && cardrow==0) { CTX(CSP) = 0x28; } // reset CTX(CSP) on display of first card on screen // Draw cards for (col=0; col<20; col++) // for each card on the current row... @@ -266,9 +242,9 @@ void drawBackgroundColorStack(int scanline) if(((card>>11)&0x03)==2) // Color Squares Mode { if (cardrow == 0) - bgcard[col] = colors[CTX(Memory)[CSP] & 0x0F]; + CTX(bgcard)[col] = colors[CTX(Memory)[CTX(CSP)] & 0x0F]; // set colors - colors[7] = bgcard[col]; // color 7 is top of color stack + colors[7] = CTX(bgcard)[col]; // color 7 is top of color stack color1 = card & 0x07; color2 = (card>>3) & 0x07; if(cardrow>=4) // switch to lower squares colors @@ -286,18 +262,18 @@ void drawBackgroundColorStack(int scanline) // draw squares for(i=0; i<8; i += 2) { - scanBuffer[x] = color1; - scanBuffer[x+1] = color1; - scanBuffer[x+8] = color2; - scanBuffer[x+9] = color2; - scanBuffer[x+384] = color1; - scanBuffer[x+384+1] = color1; - scanBuffer[x+384+8] = color2; - scanBuffer[x+384+9] = color2; - collBuffer[x] |= cbit1; - collBuffer[x+8] |= cbit2; - collBuffer[x+384] |= cbit1; - collBuffer[x+384+8] |= cbit2; + CTX(scanBuffer)[x] = color1; + CTX(scanBuffer)[x+1] = color1; + CTX(scanBuffer)[x+8] = color2; + CTX(scanBuffer)[x+9] = color2; + CTX(scanBuffer)[x+384] = color1; + CTX(scanBuffer)[x+384+1] = color1; + CTX(scanBuffer)[x+384+8] = color2; + CTX(scanBuffer)[x+384+9] = color2; + CTX(collBuffer)[x] |= cbit1; + CTX(collBuffer)[x+8] |= cbit2; + CTX(collBuffer)[x+384] |= cbit1; + CTX(collBuffer)[x+384+8] |= cbit2; x+=2; } x+=8; @@ -305,16 +281,16 @@ void drawBackgroundColorStack(int scanline) } else // Color Stack Mode { - if(cardrow == 0) // only advance CSP once per card, cache card colors for later scanlines + if(cardrow == 0) // only advance CTX(CSP) once per card, cache card colors for later scanlines { - advcolor = (card>>13) & 0x01; // do we need to advance the CSP? - CSP = (CSP+advcolor) & 0x2B; // cycles through 0x28-0x2B - fgcard[col] = colors[(card&0x07)|((card>>9)&0x08)]; // bits 12, 2, 1, 0 - bgcard[col] = colors[CTX(Memory)[CSP] & 0x0F]; + advcolor = (card>>13) & 0x01; // do we need to advance the CTX(CSP)? + CTX(CSP) = (CTX(CSP)+advcolor) & 0x2B; // cycles through 0x28-0x2B + CTX(fgcard)[col] = colors[(card&0x07)|((card>>9)&0x08)]; // bits 12, 2, 1, 0 + CTX(bgcard)[col] = colors[CTX(Memory)[CTX(CSP)] & 0x0F]; } - fgcolor = fgcard[col]; - bgcolor = bgcard[col]; + fgcolor = CTX(fgcard)[col]; + bgcolor = CTX(bgcard)[col]; if (((card >> 11) & 0x01) != 0) // Limit GRAM to 64 cards gaddress = 0x3000 + (card & 0x09f8); @@ -327,21 +303,21 @@ void drawBackgroundColorStack(int scanline) if(((gdata>>i)&1)==1) { // draw pixel - scanBuffer[x] = fgcolor; - scanBuffer[x+1] = fgcolor; - scanBuffer[x+384] = fgcolor; - scanBuffer[x+384+1] = fgcolor; + CTX(scanBuffer)[x] = fgcolor; + CTX(scanBuffer)[x+1] = fgcolor; + CTX(scanBuffer)[x+384] = fgcolor; + CTX(scanBuffer)[x+384+1] = fgcolor; // write to collision buffer - collBuffer[x] |= cbit; - collBuffer[x+384] |= cbit; + CTX(collBuffer)[x] |= cbit; + CTX(collBuffer)[x+384] |= cbit; } else { // draw background - scanBuffer[x] = bgcolor; - scanBuffer[x+1] = bgcolor; - scanBuffer[x+384] = bgcolor; - scanBuffer[x+384+1] = bgcolor; + CTX(scanBuffer)[x] = bgcolor; + CTX(scanBuffer)[x+1] = bgcolor; + CTX(scanBuffer)[x+384] = bgcolor; + CTX(scanBuffer)[x+384+1] = bgcolor; } x+=2; } @@ -397,7 +373,7 @@ void drawSprites(int scanline) // MOBs } // Limit card number to 64 if in GRAM or in Foreground/Background mode - if(STICMode==0 || ((Ra>>11) & 0x01) == 1) { card = card & 0x09f8; } + if(CTX(STICMode)==0 || ((Ra>>11) & 0x01) == 1) { card = card & 0x09f8; } gaddress = 0x3000 + card; fgcolor = colors[((Ra>>9)&0x08)|(Ra&0x07)]; @@ -446,7 +422,7 @@ void drawSprites(int scanline) // MOBs } // draw sprite row // - x = (delayH-16) + (posX * 2); // pixels are 2x2 to accomodate half-height pixels + x = (CTX(delayH)-16) + (posX * 2); // pixels are 2x2 to accomodate half-height pixels for(j=0; j<2; j++) { @@ -460,11 +436,11 @@ void drawSprites(int scanline) // MOBs // set collision and collision buffer bits // if((Rx>>8)&1) // if sprite is interactive { - collBuffer[x] |= cbit; - collBuffer[x+2*sizeX] |= cbit; // for double width + CTX(collBuffer)[x] |= cbit; + CTX(collBuffer)[x+2*sizeX] |= cbit; // for double width } - if(priority && ((collBuffer[x]>>8)&1)) // don't draw if sprite is behind background + if(priority && ((CTX(collBuffer)[x]>>8)&1)) // don't draw if sprite is behind background { continue; } @@ -472,14 +448,14 @@ void drawSprites(int scanline) // MOBs // draw sprite // if((Rx>>9)&1) // if sprite is visible { - scanBuffer[x] = fgcolor; - scanBuffer[x+1] = fgcolor; - scanBuffer[x+2*sizeX] = fgcolor; // for double width - scanBuffer[x+3*sizeX] = fgcolor; + CTX(scanBuffer)[x] = fgcolor; + CTX(scanBuffer)[x+1] = fgcolor; + CTX(scanBuffer)[x+2*sizeX] = fgcolor; // for double width + CTX(scanBuffer)[x+3*sizeX] = fgcolor; } } gdata = gdata2; // for second half-pixel row // - x = (delayH-16) + 384 + (posX * 2); // for second half-pixel row // + x = (CTX(delayH)-16) + 384 + (posX * 2); // for second half-pixel row // } } } @@ -498,62 +474,62 @@ void STICDrawFrame(int enabled) for(i=0; i<352; i++) { - scanBuffer[i] = color; - scanBuffer[i+384] = color; + CTX(scanBuffer)[i] = color; + CTX(scanBuffer)[i+384] = color; } - memcpy(&frame[offset], &scanBuffer[0], 352 * sizeof(unsigned int)); - memcpy(&frame[offset + 352], &scanBuffer[384], 352 * sizeof(unsigned int)); + memcpy(&CTX(frame)[offset], &CTX(scanBuffer)[0], 352 * sizeof(unsigned int)); + memcpy(&CTX(frame)[offset + 352], &CTX(scanBuffer)[384], 352 * sizeof(unsigned int)); offset += 352 * 2; } } else { - extendTop = (CTX(Memory)[0x32]>>1)&0x01; + CTX(extendTop) = (CTX(Memory)[0x32]>>1)&0x01; - extendLeft = (CTX(Memory)[0x32])&0x01; + CTX(extendLeft) = (CTX(Memory)[0x32])&0x01; - delayV = 8 + ((CTX(Memory)[0x31])&0x7); - delayH = 8 + ((CTX(Memory)[0x30])&0x7); + CTX(delayV) = 8 + ((CTX(Memory)[0x31])&0x7); + CTX(delayH) = 8 + ((CTX(Memory)[0x30])&0x7); - delayH = delayH * 2; + CTX(delayH) = CTX(delayH) * 2; for(row=0; row<112; row++) { - memset(&collBuffer[0], 0, sizeof(collBuffer)); + memset(&CTX(collBuffer)[0], 0, sizeof(CTX(collBuffer))); // draw backtab - if(row>=delayV && row<(96+delayV)) + if(row>=CTX(delayV) && row<(96+CTX(delayV))) { - if(STICMode==0) // Foreground/Background Mode + if(CTX(STICMode)==0) // Foreground/Background Mode { - drawBackgroundFGBG(row-delayV); + drawBackgroundFGBG(row-CTX(delayV)); } else // Color Stack Modes { - drawBackgroundColorStack(row-delayV); + drawBackgroundColorStack(row-CTX(delayV)); } } - if (row>=delayV - 1 && row<(97 + delayV)) { + if (row>=CTX(delayV) - 1 && row<(97 + CTX(delayV))) { // draw MOBs - drawSprites((row-delayV)+8); + drawSprites((row-CTX(delayV))+8); } // draw border and set final collision bits drawBorder(row); // clear collisions in column 167 // - collBuffer[167 * 2] = 0; - collBuffer[167 * 2 + 384] = 0; + CTX(collBuffer)[167 * 2] = 0; + CTX(collBuffer)[167 * 2 + 384] = 0; for (i = 14; i < 169 * 2; i += 2) { - if (collBuffer[i] == 0) + if (CTX(collBuffer)[i] == 0) continue; for (j = 0; j < 8; j++) { - if (((collBuffer[i] >> j) & 1) != 0) { - CTX(Memory)[0x18 + j] |= collBuffer[i] & ~(1 << j); + if (((CTX(collBuffer)[i] >> j) & 1) != 0) { + CTX(Memory)[0x18 + j] |= CTX(collBuffer)[i] & ~(1 << j); } } } - memcpy(&frame[offset], &scanBuffer[0], 352 * sizeof(unsigned int)); - memcpy(&frame[offset + 352], &scanBuffer[384], 352 * sizeof(unsigned int)); + memcpy(&CTX(frame)[offset], &CTX(scanBuffer)[0], 352 * sizeof(unsigned int)); + memcpy(&CTX(frame)[offset + 352], &CTX(scanBuffer)[384], 352 * sizeof(unsigned int)); offset += 352 * 2; } } diff --git a/src/stic.h b/src/stic.h index 903f59f..6924cfb 100644 --- a/src/stic.h +++ b/src/stic.h @@ -17,20 +17,6 @@ along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ -extern unsigned int STICMode; // 0-foreground/background, 1-color stack/color squares - -extern int stic_phase; -extern int stic_vid_enable; -extern int stic_reg; -extern int stic_gram; -extern int phase_len; -extern int delayV; -extern int delayH; - -extern int DisplayEnabled; // determines if frame should be updated or not - -extern unsigned int frame[352*224]; // frame buffer - void STICDrawFrame(int); void STICReset(void); From 06078bd9834ef18099e84fe0890200bcc00d947c Mon Sep 17 00:00:00 2001 From: Blaze Entertainment Date: Mon, 10 Jan 2022 14:30:25 +0000 Subject: [PATCH 08/15] sync --- .gitlab-ci.yml | 10 + Makefile | 16 + Makefile.common | 3 +- README.md | 8 +- src/.DS_Store | Bin 0 -> 6148 bytes src/controller.c | 8 + src/cp1610.c | 440 ++++++------ src/cp1610.h | 61 +- src/intv.c | 89 ++- src/intv.h | 2 + src/ivoice.c | 1735 ++++++++++++++++++++++++++++++++++++++++++++++ src/ivoice.h | 113 +++ src/libretro.c | 250 ++++--- src/memory.c | 138 ++-- src/psg.c | 181 +++-- src/psg.h | 37 + src/stic.c | 350 ++++++---- src/stic.h | 41 ++ 18 files changed, 2832 insertions(+), 650 deletions(-) create mode 100644 src/.DS_Store create mode 100644 src/ivoice.c create mode 100644 src/ivoice.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fc59d2c..5181b27 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -93,6 +93,10 @@ include: # PlayStation2 - project: 'libretro-infrastructure/ci-templates' file: '/ps2-static.yml' + + # OpenDingux + - project: 'libretro-infrastructure/ci-templates' + file: '/dingux-mips32.yml' # tvOS (AppleTV) - project: 'libretro-infrastructure/ci-templates' @@ -256,3 +260,9 @@ libretro-build-libnx-aarch64: extends: - .libretro-libnx-static-retroarch-master - .core-defs + +# RetroFW +libretro-build-retrofw-mips32: + extends: + - .libretro-retrofw-mips32-make-default + - .core-defs diff --git a/Makefile b/Makefile index 0c023ef..11dd7ef 100644 --- a/Makefile +++ b/Makefile @@ -310,6 +310,22 @@ else ifeq ($(platform), gcw0) DISABLE_ERROR_LOGGING := 1 CFLAGS += -march=mips32 -mtune=mips32r2 -mhard-float + +# RETROFW +else ifeq ($(platform), retrofw) + TARGET := $(TARGET_NAME)_libretro.so + CC = /opt/retrofw-toolchain/usr/bin/mipsel-linux-gcc + AR = /opt/retrofw-toolchain/usr/bin/mipsel-linux-ar + fpic := -fPIC + SHARED := -shared -Wl,--version-script=link.T -Wl,-no-undefined + + DISABLE_ERROR_LOGGING := 1 + CFLAGS += -Ofast + CFLAGS += -march=mips32 -mtune=mips32 -mhard-float + CFLAGS += -falign-functions=1 -falign-jumps=1 -falign-loops=1 + CFLAGS += -fomit-frame-pointer -ffast-math + CFLAGS += -funsafe-math-optimizations -fsingle-precision-constant -fexpensive-optimizations + CFLAGS += -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops # Windows MSVC 2010 x64 else ifeq ($(platform), windows_msvc2010_x64) diff --git a/Makefile.common b/Makefile.common index 82fe3ab..24a914d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -11,7 +11,8 @@ SOURCES_C := \ $(SOURCE_DIR)/cp1610.c \ $(SOURCE_DIR)/cart.c \ $(SOURCE_DIR)/controller.c \ - $(SOURCE_DIR)/osd.c \ + $(SOURCE_DIR)/osd.c \ + $(SOURCE_DIR)/ivoice.c \ $(SOURCE_DIR)/psg.c \ $(SOURCE_DIR)/stic.c diff --git a/README.md b/README.md index 07d4be4..b4142fc 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ FreeIntv is a libretro emulation core for the Mattel Intellivision designed to b ## Authors FreeIntv was created by David Richardson. -The PSG and STIC emulation was made closer to hardware and optimized by Oscar Toledo G. (nanochess) +The PSG and STIC emulation was made closer to hardware and optimized by Oscar Toledo G. (nanochess), who also added save states. + +The Intellivoice code has been contributed by Joe Zbiciak (author of jzintv), and adapted by Oscar Toledo G. (nanochess) ## License The FreeIntv core is licensed under GPLv3. More information at https://github.com/markwkidd/FreeIntv/blob/master/LICENSE. @@ -19,8 +21,8 @@ FreeIntv requires two Intellivision BIOS files to be placed in the libretro 'sys * BIOS filenames are case-sensitive -## Entertainment Computer System and Intellivoice -FreeIntv does not currently support Entertainment Computer System (ECS) and Intellivoice functionality. Contributions to the code are welcome! +## Entertainment Computer System +FreeIntv does not currently support Entertainment Computer System (ECS) functionality. Contributions to the code are welcome! ## Controller overlays Mattel Intellivision games were often meant to be played with game-specific cards overlaid on the numeric keypad. These overlays convey information which can be very useful in gameplay. Images of a limited selection of Intellivision titles are available at: http://www.intellivisionlives.com/bluesky/games/instructions.shtml diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0Flag_DoubleByteData = Flag_DoubleByteData; + all->Flag_InteruptEnable = Flag_InteruptEnable; + all->Flag_Carry = Flag_Carry; + all->Flag_Sign = Flag_Sign; + all->Flag_Zero = Flag_Zero; + all->Flag_Overflow = Flag_Overflow; + memcpy(&all->R[0], &R[0], sizeof(R)); +} - memset(&CTX(R), 0, sizeof(CTX(R))); +void CP1610Unserialize(const struct CP1610serialized *all) +{ + Flag_DoubleByteData = all->Flag_DoubleByteData; + Flag_InteruptEnable = all->Flag_InteruptEnable; + Flag_Carry = all->Flag_Carry; + Flag_Sign = all->Flag_Sign; + Flag_Zero = all->Flag_Zero; + Flag_Overflow = all->Flag_Overflow; + memcpy(&R[0], &all->R[0], sizeof(R)); +} - CTX(R[regSP]) = 0x02F1; // Stack is at System Ram 0x02F1-0x0318 - CTX(R[regPC]) = 0x1000; // EXEC entry point +void CP1610Reset() +{ + Flag_DoubleByteData = 0; + Flag_InteruptEnable = 0; + Flag_Carry = 0; + Flag_Sign = 0; + Flag_Zero = 0; + Flag_Overflow = 0; + R[0] = R[1] = R[2] = R[3] = R[4] = R[5] = 0; + R[SP] = 0x02F1; // Stack is at System Ram 0x02F1-0x0318 + R[PC] = 0x1000; // EXEC entry point } int readIndirect(int reg) // Read Indirect, handle SDBD, update autoincriment registers @@ -73,20 +82,20 @@ int readIndirect(int reg) // Read Indirect, handle SDBD, update autoincriment re int val = 0; int adr = 0; - if(reg==6) { CTX(R)[reg] = CTX(R)[reg] - 1; } // decriment R6 (regSP) before read - adr = CTX(R)[reg]; + if(reg==6) { R[reg] = R[reg] - 1; } // decriment R6 (SP) before read + adr = R[reg]; val = readMem(adr); - if(reg==4 || reg==5 || reg==7) // autoincrement registers R4-R7 excluding regSP (R6) + if(reg==4 || reg==5 || reg==7) // autoincrement registers R4-R7 excluding SP (R6) { - CTX(R)[reg] = (CTX(R)[reg]+1) & 0xFFFF; + R[reg] = (R[reg]+1) & 0xFFFF; } - if(CTX(Flag_DoubleByteData) == 1) { + if(Flag_DoubleByteData == 1) { val &= 0xff; if(reg==4 || reg==5 || reg==7) // autoincrement registers (incremented twice for double byte data) { val |= ((readMem(adr+1) & 0xFF)<<8); - CTX(R)[reg] = (CTX(R)[reg]+1) & 0xFFFF; + R[reg] = (R[reg]+1) & 0xFFFF; } else { val |= val << 8; } @@ -96,34 +105,34 @@ int readIndirect(int reg) // Read Indirect, handle SDBD, update autoincriment re void writeIndirect(int reg, int val) { - int adr = CTX(R)[reg]; + int adr = R[reg]; writeMem(adr, val); if(reg>=4) // autoincrement registers R4-R7 { - CTX(R)[reg] = (CTX(R)[reg]+1) & 0xFFFF; + R[reg] = (R[reg]+1) & 0xFFFF; } } int readOperand(void) { - int val = readMem(CTX(R)[regPC]); - CTX(R)[regPC]++; + int val = readMem(R[PC]); + R[PC]++; return val; } int readOperandIndirect(void) { - int adr = readMem(CTX(R)[regPC]); + int adr = readMem(R[PC]); int val = readMem(adr); - CTX(R)[regPC]++; + R[PC]++; return val; } void SetFlagsSZ(int reg) { - CTX(R)[reg] = CTX(R)[reg] & 0xFFFF; - CTX(Flag_Sign) = (CTX(R)[reg] & 0x8000)!=0; - CTX(Flag_Zero) = CTX(R)[reg]==0; + R[reg] = R[reg] & 0xFFFF; + Flag_Sign = (R[reg] & 0x8000)!=0; + Flag_Zero = R[reg]==0; } int AddSetSZOC(int A, int B) @@ -133,13 +142,13 @@ int AddSetSZOC(int A, int B) int result = (A+B); int signr = result & 0x8000; - CTX(Flag_Overflow) = (signa==signb && signa!=signr) ? 1 : 0; - CTX(Flag_Carry) = (result & 0x10000) != 0; + Flag_Overflow = (signa==signb && signa!=signr) ? 1 : 0; + Flag_Carry = (result & 0x10000) != 0; result = result & 0xFFFF; - CTX(Flag_Sign) = (result & 0x8000)!=0; - CTX(Flag_Zero) = result==0; + Flag_Sign = (result & 0x8000)!=0; + Flag_Zero = result==0; return result; } int SubSetOC(int A, int B) @@ -148,26 +157,44 @@ int SubSetOC(int A, int B) int signb = B & 0x8000; int result = (A + (B ^ 0xFFFF) + 1); // A - B using 1's compliment; int signr = result & 0x8000; - CTX(Flag_Carry) = (result & 0x10000)!=0; - CTX(Flag_Overflow) = (signa!=signb && signa!=signr) ? 1 : 0; + Flag_Carry = (result & 0x10000)!=0; + Flag_Overflow = (signa!=signb && signa!=signr) ? 1 : 0; return result & 0xFFFF; } int CP1610Tick(int debug) { // execute one instruction // - int sdbd = CTX(Flag_DoubleByteData); + int sdbd = Flag_DoubleByteData; - unsigned int instruction = readMem(CTX(R)[regPC]); + unsigned int instruction = readMem(R[PC]); int ticks = 0; +#if 0 + static int global_ticks = 0; +#endif // DEBUG #if 0 { FILE *debug_file; - fprintf(stdout, "%04x:[%03x%c %04x %04x %04x %04x %04x %04x %04x %s %c%c%c%c%c%c\n", CTX(R)[7], instruction, instruction > 0x03ff ? 'X' : ']', CTX(R)[0], CTX(R)[1], CTX(R)[2], CTX(R)[3], CTX(R)[4], CTX(R)[5], CTX(R)[6], Nmemonic[instruction], CTX(Flag_Sign) ? 'S' : '-', CTX(Flag_Carry) ? 'C' : '-', CTX(Flag_Overflow) ? 'O' : '-', CTX(Flag_Zero) ? 'Z' : '-', CTX(Flag_InteruptEnable) ? 'I' : '-', CTX(Flag_DoubleByteData) ? 'D' : '-'); + fprintf(stdout, "%04x:[%03x%c %04x %04x %04x %04x %04x %04x %04x %s %c%c%c%c%c%c\n", R[7], instruction, instruction > 0x03ff ? 'X' : ']', R[0], R[1], R[2], R[3], R[4], R[5], R[6], Nmemonic[instruction], Flag_Sign ? 'S' : '-', Flag_Carry ? 'C' : '-', Flag_Overflow ? 'O' : '-', Flag_Zero ? 'Z' : '-', Flag_InteruptEnable ? 'I' : '-', Flag_DoubleByteData ? 'D' : '-'); + } +#endif +#if 0 // Debug output compatible with JZINTV for comparison purposes + { + FILE *debug_file; + + fprintf(debug_file, " %04X %04X %04X %04X %04X %04X %04X %04X %c%c%c%c%c%c%c%c %20s %d\n", R[0], R[1], R[2], R[3], R[4], R[5], R[6], R[7], + Flag_Sign ? 'S' : '-', + Flag_Zero ? 'Z' : '-', + Flag_Overflow ? 'O' : '-', + Flag_Carry ? 'C' : '-', + Flag_InteruptEnable ? 'I' : '-', + Flag_DoubleByteData ? 'D' : '-', + Interuptable[instruction] ? 'i' : '-', + SR1 > 0 ? 'q' : '-' , Nmemonic[instruction], global_ticks); } #endif @@ -178,25 +205,28 @@ int CP1610Tick(int debug) return 0; } - CTX(R)[regPC]++; // point regPC/R7 at operand/next address + R[PC]++; // point PC/R7 at operand/next address ticks = OpCodes[instruction](instruction); // execute instruction - if(sdbd==1) { CTX(Flag_DoubleByteData) = 0; } // reset SDBD + if(sdbd==1) { Flag_DoubleByteData = 0; } // reset SDBD // check interupt request - if(CTX(Flag_InteruptEnable) == 1 && SR1>0) + if(Flag_InteruptEnable == 1 && SR1>0) { if(Interuptable[instruction]) { // Take VBlank Interupt // SR1 = 0; - writeIndirect(regSP, CTX(R)[regPC]); // push regPC... - CTX(R)[regPC] = 0x1004; // Jump + writeIndirect(SP, R[PC]); // push PC... + R[PC] = 0x1004; // Jump ticks += 12; } } +#if 0 + global_ticks += ticks; +#endif return ticks; } @@ -205,13 +235,13 @@ int HLT(int v) // Halt Instruction found! // printf("\n\n[ERROR] [FREEINTV] HALT!\n"); - CTX(R)[regPC]--; // Repeat instruction forever instead of exiting without warning + R[PC]--; // Repeat instruction forever instead of exiting without warning return 0; } -int SDBD(int v) { CTX(Flag_DoubleByteData) = 1; return 4; } // Set Double Byte Data -int EIS(int v) { CTX(Flag_InteruptEnable) = 1; return 4; } // Enable Interrupt System -int DIS(int v) { CTX(Flag_InteruptEnable) = 0; return 4; } // Disable Interrupt System +int SDBD(int v) { Flag_DoubleByteData = 1; return 4; } // Set Double Byte Data +int EIS(int v) { Flag_InteruptEnable = 1; return 4; } // Enable Interrupt System +int DIS(int v) { Flag_InteruptEnable = 0; return 4; } // Disable Interrupt System int Jump(int v) { // J, JE, JD, JSR, JSRE, JSRD, CALL @@ -224,58 +254,59 @@ int Jump(int v) if(reg!=3) { reg = reg + 4; - CTX(R)[reg] = CTX(R)[regPC]; // store return address (regPC already advanced to regPC+3) + R[reg] = R[PC]; // store return address (PC already advanced to PC+3) } - if(ff==1) { CTX(Flag_InteruptEnable) = 1; } // set Interupt flag - if(ff==2) { CTX(Flag_InteruptEnable) = 0; } // clear Interrupt flag - CTX(R)[regPC] = adr; // Jump + if(ff==1) { Flag_InteruptEnable = 1; } // set Interupt flag + if(ff==2) { Flag_InteruptEnable = 0; } // clear Interrupt flag + R[PC] = adr; // Jump return 13; } int TCI(int v) { return 4; } // Terminate Current Interrupt (not used) -int CLRC(int v) { CTX(Flag_Carry) = 0; return 4; } // Clear Carry -int SETC(int v) { CTX(Flag_Carry) = 1; return 4; } // Set Carry +int CLRC(int v) { Flag_Carry = 0; return 4; } // Clear Carry +int SETC(int v) { Flag_Carry = 1; return 4; } // Set Carry +#define EXTRA_IF_R6(reg) (reg == 6 ? 3 : 0) #define EXTRA_IF_R6R7(reg) (reg >= 6 ? 1 : 0) int INCR(int v) // Increment Register { int reg = v & 0x07; - CTX(R)[reg] = CTX(R)[reg]+1; + R[reg] = R[reg]+1; SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int DECR(int v) // Decrement Register { int reg = v & 0x07; - CTX(R)[reg] = CTX(R)[reg]-1; + R[reg] = R[reg]-1; SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int COMR(int v) // Complement Register (One's Compliment) { int reg = v & 0x07; - CTX(R)[reg] = CTX(R)[reg] ^ 0xFFFF; + R[reg] = R[reg] ^ 0xFFFF; SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int NEGR(int v) // Negate Register (Two's Compliment) { int reg = v & 0x07; - CTX(R)[reg] = SubSetOC(0, CTX(R)[reg]); + R[reg] = SubSetOC(0, R[reg]); SetFlagsSZ(reg); return 6 + EXTRA_IF_R6R7(reg); } int ADCR(int v) // Add Carry to Register { int reg = v & 0x07; - CTX(R)[reg] = AddSetSZOC(CTX(R)[reg], CTX(Flag_Carry)); + R[reg] = AddSetSZOC(R[reg], Flag_Carry); return 6 + EXTRA_IF_R6R7(reg); } int GSWD(int v) // Get the Status Word szoc:0000:szoc:0000 { int reg = v & 0x03; - unsigned int szoc = (CTX(Flag_Sign)<<3) | (CTX(Flag_Zero)<<2) | (CTX(Flag_Overflow)<<1) | CTX(Flag_Carry); - CTX(R)[reg] = (szoc<<12) | (szoc<<4); + unsigned int szoc = (Flag_Sign<<3) | (Flag_Zero<<2) | (Flag_Overflow<<1) | Flag_Carry; + R[reg] = (szoc<<12) | (szoc<<4); return 6; } int NOP(int v) { return 6; } // No Operation @@ -284,31 +315,31 @@ int SIN(int v) { return 6; } // Software Interrupt (not used) int RSWD(int v) // Return Status Word szoc:0000 { int reg = v & 0x07; - unsigned int szoc = CTX(R)[reg]>>4; - CTX(Flag_Sign) = (szoc>>3) & 1; - CTX(Flag_Zero) = (szoc>>2) & 1; - CTX(Flag_Overflow) = (szoc>>1) & 1; - CTX(Flag_Carry) = szoc & 1; + unsigned int szoc = R[reg]>>4; + Flag_Sign = (szoc>>3) & 1; + Flag_Zero = (szoc>>2) & 1; + Flag_Overflow = (szoc>>1) & 1; + Flag_Carry = szoc & 1; return 6; } int SWAP(int v) // Swap 0000:0trr { int reg = v & 0x03; int times = (v>>2) & 1; - int upper = (CTX(R)[reg]>>8) & 0xFF; - int lower = CTX(R)[reg] & 0xFF; + int upper = (R[reg]>>8) & 0xFF; + int lower = R[reg] & 0xFF; if(times==0) // single swap { - CTX(R)[reg] = (lower<<8) | upper; - CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; - CTX(Flag_Zero) = CTX(R)[reg]==0; + R[reg] = (lower<<8) | upper; + Flag_Sign = (R[reg]>>7) & 1; + Flag_Zero = R[reg]==0; return 6; } else // double swap { - CTX(R)[reg] = (lower<<8) | lower; - CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; - CTX(Flag_Zero) = CTX(R)[reg]==0; + R[reg] = (lower<<8) | lower; + Flag_Sign = (R[reg]>>7) & 1; + Flag_Zero = R[reg]==0; return 8; } } @@ -316,7 +347,7 @@ int SLL(int v) // Shift Logical Left 0000:1drr { int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - CTX(R)[reg] = CTX(R)[reg]<>2) & 1); - int bit15 = (CTX(R)[reg]>>15) & 1; - int bit14 = (CTX(R)[reg]>>14) & 1; + int bit15 = (R[reg]>>15) & 1; + int bit14 = (R[reg]>>14) & 1; if(times==0) // Single rotate { - CTX(R)[reg] = CTX(R)[reg] << 1; - CTX(R)[reg] = CTX(R)[reg] | CTX(Flag_Carry); - CTX(Flag_Carry) = bit15; + R[reg] = R[reg] << 1; + R[reg] = R[reg] | Flag_Carry; + Flag_Carry = bit15; } else // Double rotate { - CTX(R)[reg] = CTX(R)[reg] << 2; - CTX(R)[reg] = CTX(R)[reg] | ((CTX(Flag_Carry) << 1) | CTX(Flag_Overflow)); - CTX(Flag_Carry) = bit15; - CTX(Flag_Overflow) = bit14; + R[reg] = R[reg] << 2; + R[reg] = R[reg] | ((Flag_Carry << 1) | Flag_Overflow); + Flag_Carry = bit15; + Flag_Overflow = bit14; } SetFlagsSZ(reg); return 6+(2*times); // 6 single or 8 double @@ -351,15 +382,15 @@ int SLLC(int v) // Shift Logical Left through Carry // The wiki method seems to be correct int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - int bit15 = (CTX(R)[reg]>>15) & 1; - int bit14 = (CTX(R)[reg]>>14) & 1; - CTX(R)[reg] = (CTX(R)[reg]<>15) & 1; + int bit14 = (R[reg]>>14) & 1; + R[reg] = (R[reg]<>2) & 1)+1; - CTX(R)[reg] = CTX(R)[reg]>>dist; - CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; - CTX(Flag_Zero) = CTX(R)[reg]==0; + R[reg] = R[reg]>>dist; + Flag_Sign = (R[reg]>>7) & 1; + Flag_Zero = R[reg]==0; return 6+(2*(dist-1)); // 6 <<1 or 8 <<2 } int SAR(int v) // Shift Arithmetic Right { int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - int bit15 = (CTX(R)[reg]>>15) & 1; + int bit15 = (R[reg]>>15) & 1; - CTX(R)[reg] = CTX(R)[reg]>>dist; + R[reg] = R[reg]>>dist; if(dist==1) { - CTX(R)[reg] = CTX(R)[reg] | (bit15<<15); + R[reg] = R[reg] | (bit15<<15); } else { - CTX(R)[reg] = CTX(R)[reg] | (bit15<<15); - CTX(R)[reg] = CTX(R)[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high bits" + R[reg] = R[reg] | (bit15<<15); + R[reg] = R[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high bits" } - CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; - CTX(Flag_Zero) = CTX(R)[reg]==0; + Flag_Sign = (R[reg]>>7) & 1; + Flag_Zero = R[reg]==0; return 6+(2*(dist-1)); // 6 <<1 or 8 <<2 } int RRC(int v) // Rotate Right Through Carry { int reg = v & 0x03; int dist = ((v>>2) & 1); - int bit1 = (CTX(R)[reg]>>1) & 1; - int bit0 = CTX(R)[reg] & 1; + int bit1 = (R[reg]>>1) & 1; + int bit0 = R[reg] & 1; if(dist==0) { - CTX(R)[reg] = CTX(R)[reg]>>1; - CTX(R)[reg] = CTX(R)[reg] | (CTX(Flag_Carry)<<15); + R[reg] = R[reg]>>1; + R[reg] = R[reg] | (Flag_Carry<<15); } else { - CTX(R)[reg] = CTX(R)[reg]>>2; - CTX(R)[reg] = CTX(R)[reg] | (CTX(Flag_Overflow)<<15); - CTX(R)[reg] = CTX(R)[reg] | (CTX(Flag_Carry)<<14); - CTX(Flag_Overflow) = bit1; + R[reg] = R[reg]>>2; + R[reg] = R[reg] | (Flag_Overflow<<15); + R[reg] = R[reg] | (Flag_Carry<<14); + Flag_Overflow = bit1; } - CTX(Flag_Carry) = bit0; - CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; - CTX(Flag_Zero) = CTX(R)[reg]==0; + Flag_Carry = bit0; + Flag_Sign = (R[reg]>>7) & 1; + Flag_Zero = R[reg]==0; return 6+(2*(dist)); // 6 <<1 or 8 <<2 } int SARC(int v) // Shift Arithmetic Right Through Carry { int reg = v & 0x03; int dist = ((v>>2) & 1)+1; - int bit15 = (CTX(R)[reg]>>15) & 1; - int bit1 = (CTX(R)[reg]>>1) & 1; - int bit0 = CTX(R)[reg] & 1; + int bit15 = (R[reg]>>15) & 1; + int bit1 = (R[reg]>>1) & 1; + int bit0 = R[reg] & 1; - CTX(R)[reg] = CTX(R)[reg]>>dist; - CTX(R)[reg] = CTX(R)[reg] | (bit15<<15); + R[reg] = R[reg]>>dist; + R[reg] = R[reg] | (bit15<<15); if(dist==2) { - CTX(R)[reg] = CTX(R)[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high 2 bits" - CTX(Flag_Overflow) = bit1; + R[reg] = R[reg] | (bit15<<14); // CP-1600 manual says "sign bit copied to high 2 bits" + Flag_Overflow = bit1; } - CTX(Flag_Carry) = bit0; - CTX(Flag_Sign) = (CTX(R)[reg]>>7) & 1; - CTX(Flag_Zero) = CTX(R)[reg]==0; + Flag_Carry = bit0; + Flag_Sign = (R[reg]>>7) & 1; + Flag_Zero = R[reg]==0; return 6+(2*(dist-1)); // 6 <<1 or 8 <<2 } int MOVR(int v) // Move Register { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - CTX(R)[dreg] = CTX(R)[sreg]; + R[dreg] = R[sreg]; SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -449,14 +480,14 @@ int ADDR(int v) // Add Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - CTX(R)[dreg] = AddSetSZOC(CTX(R)[dreg], CTX(R)[sreg]); + R[dreg] = AddSetSZOC(R[dreg], R[sreg]); return 6 + EXTRA_IF_R6R7(dreg); } int SUBR(int v) // Subtract Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - CTX(R)[dreg] = SubSetOC(CTX(R)[dreg], CTX(R)[sreg]); + R[dreg] = SubSetOC(R[dreg], R[sreg]); SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -464,16 +495,16 @@ int CMPR(int v) // Compare Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - int res = SubSetOC(CTX(R)[dreg], CTX(R)[sreg]); - CTX(Flag_Sign) = (res & 0x8000)!=0; - CTX(Flag_Zero) = res==0; + int res = SubSetOC(R[dreg], R[sreg]); + Flag_Sign = (res & 0x8000)!=0; + Flag_Zero = res==0; return 6 + EXTRA_IF_R6R7(dreg); } int ANDR(int v) // And Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - CTX(R)[dreg] = CTX(R)[dreg] & CTX(R)[sreg]; + R[dreg] = R[dreg] & R[sreg]; SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -481,7 +512,7 @@ int XORR(int v) // Xor Registers { int sreg = (v >> 3) & 0x7; int dreg = v & 0x7; - CTX(R)[dreg] = CTX(R)[dreg] ^ CTX(R)[sreg]; + R[dreg] = R[dreg] ^ R[sreg]; SetFlagsSZ(dreg); return 6 + EXTRA_IF_R6R7(dreg); } @@ -503,29 +534,30 @@ int Branch(int v) // Branch - B, BC, BOV, BPL, BEQ, BLT, BLE, BUSC, NOPP, BNC, B // digital states to be sampled by the CPU during the execution of the BEXT // (Branch on EXTernal) instruction // --- I don't know what is meant by 'instruction register' - if((CTX(InstructionRegister) & 0x0F)==(v & 0x0F)) + if((InstructionRegister & 0x0F)==(v & 0x0F)) { - if(direction==0) { CTX(R)[regPC] = CTX(R)[regPC]+offset; } - if(direction==1) { CTX(R)[regPC] = CTX(R)[regPC]-offset-1; } + if(direction==0) { R[PC] = R[PC]+offset; } + if(direction==1) { R[PC] = R[PC]-offset-1; } + return 9; } return 7; } switch(condition) { case 0: branch = 1; break; // B, NOPP - case 1: branch = (CTX(Flag_Carry)==1); break; // BC, BNC - case 2: branch = (CTX(Flag_Overflow)==1); break; // BOV, BNOV - case 3: branch = (CTX(Flag_Sign)==0); break; // BPL, BMI - case 4: branch = (CTX(Flag_Zero)==1); break; // BEQ, BNEQ - case 5: branch = (CTX(Flag_Sign)!=CTX(Flag_Overflow)); break; // BLT, BGE - case 6: branch = (CTX(Flag_Zero)==1)||(CTX(Flag_Sign)!=CTX(Flag_Overflow)); break; // BLE, BGT - case 7: branch = (CTX(Flag_Sign)!=CTX(Flag_Carry)); break; // BUSC, BESC + case 1: branch = (Flag_Carry==1); break; // BC, BNC + case 2: branch = (Flag_Overflow==1); break; // BOV, BNOV + case 3: branch = (Flag_Sign==0); break; // BPL, BMI + case 4: branch = (Flag_Zero==1); break; // BEQ, BNEQ + case 5: branch = (Flag_Sign!=Flag_Overflow); break; // BLT, BGE + case 6: branch = (Flag_Zero==1)||(Flag_Sign!=Flag_Overflow); break; // BLE, BGT + case 7: branch = (Flag_Sign!=Flag_Carry); break; // BUSC, BESC } if(notbit==1) { branch = !branch; } if(branch) { - if(direction==0) { CTX(R)[regPC] = CTX(R)[regPC]+offset; } - if(direction==1) { CTX(R)[regPC] = CTX(R)[regPC]-(offset+1); } + if(direction==0) { R[PC] = R[PC]+offset; } + if(direction==1) { R[PC] = R[PC]-(offset+1); } return 9; } return 7; @@ -534,7 +566,7 @@ int MVO(int v) // Move Out { int reg = v & 0x07; int adr = readOperand(); - writeMem(adr, CTX(R)[reg]); + writeMem(adr, R[reg]); return 11; } int MVOa(int v) // MVO@ - Move Out Indirect 0000:0010:01aa:asss @@ -542,143 +574,143 @@ int MVOa(int v) // MVO@ - Move Out Indirect 0000:0010:01aa:asss // The PSHR Rx instruction is an alias for MVOa Rx, R6 int areg = (v >> 3) & 0x7; int sreg = v & 0x7; - writeIndirect(areg, CTX(R)[sreg]); + writeIndirect(areg, R[sreg]); return 9; } int MVOI(int v) // Move Out Immediate 0000:0010:0111:1sss { - return(MVOa(v)); // call indirect copies CTX(R)[sss] to address in CTX(R)[regPC] + return(MVOa(v)); // call indirect copies R[sss] to address in R[PC] } int MVI(int v) // Move In 0000:0010:1000:0rrr aaaa:aaaa:aaaa:aaaa { int reg = v & 0x07; - CTX(R)[reg] = readOperandIndirect(); - return 10; + R[reg] = readOperandIndirect(); + return 10 + EXTRA_IF_R6R7(reg); } int MVIa(int v) // Move In Indirect 0000:0010:10aa:addd { int areg = (v >> 3) & 0x7; int dreg = v & 0x7; - CTX(R)[dreg] = readIndirect(areg); - return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + R[dreg] = readIndirect(areg); + return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(dreg) + EXTRA_IF_R6(areg); } int MVII(int v) // Move In Immediate (copies operand to register) { - // These instructions are only one word, so don't advance regPC past operand. + // These instructions are only one word, so don't advance PC past operand. // Auto incrementing registers will move past the operands automatically. - // This works exactly like MVI@ with regPC as the address register. + // This works exactly like MVI@ with PC as the address register. // All nnnI instructions work this way. - v = v | 0x0038; // set address register to regPC + v = v | 0x0038; // set address register to PC return(MVIa(v)); // call indirect } int ADD(int v) // Add { int reg = v & 0x07; int val = readOperandIndirect(); - CTX(R)[reg] = AddSetSZOC(CTX(R)[reg], val); - return 10; + R[reg] = AddSetSZOC(R[reg], val); + return 10 + EXTRA_IF_R6R7(reg);; } int ADDa(int v) // Add Indirect { int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - CTX(R)[dreg] = AddSetSZOC(CTX(R)[dreg], val); - return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + R[dreg] = AddSetSZOC(R[dreg], val); + return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg) + EXTRA_IF_R6(areg); } int ADDI(int v) // Add Immediate { - v = v | 0x0038; // set address register to regPC + v = v | 0x0038; // set address register to PC return(ADDa(v)); // call indirect } int SUB(int v) // Subtract { int reg = v & 0x07; int val = readOperandIndirect(); - CTX(R)[reg] = SubSetOC(CTX(R)[reg], val); + R[reg] = SubSetOC(R[reg], val); SetFlagsSZ(reg); - return 10; + return 10 + EXTRA_IF_R6R7(reg); } int SUBa(int v) // Subtract Indirect { int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - CTX(R)[dreg] = SubSetOC(CTX(R)[dreg], val); + R[dreg] = SubSetOC(R[dreg], val); SetFlagsSZ(dreg); - return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg) + EXTRA_IF_R6(areg); } int SUBI(int v) // Subtract Immediate { - v = v | 0x0038; // set address register to regPC + v = v | 0x0038; // set address register to PC return(SUBa(v)); // call indirect } int CMP(int v) { int reg = v & 0x07; int val = readOperandIndirect(); - int res = SubSetOC(CTX(R)[reg], val); - CTX(Flag_Sign) = (res & 0x8000)!=0; - CTX(Flag_Zero) = res==0; - return 10; + int res = SubSetOC(R[reg], val); + Flag_Sign = (res & 0x8000)!=0; + Flag_Zero = res==0; + return 10 + EXTRA_IF_R6R7(reg); } int CMPa(int v) { int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - int res = SubSetOC(CTX(R)[dreg], val); - CTX(Flag_Sign) = (res & 0x8000)!=0; - CTX(Flag_Zero) = res==0; - return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + int res = SubSetOC(R[dreg], val); + Flag_Sign = (res & 0x8000)!=0; + Flag_Zero = res==0; + return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg) + EXTRA_IF_R6(areg); } int CMPI(int v) // CMP Immediate { - v = v | 0x0038; // set address register to regPC + v = v | 0x0038; // set address register to PC return(CMPa(v)); // call indirect } int AND(int v) // And { int reg = v & 0x07; int val = readOperandIndirect(); - CTX(R)[reg] = CTX(R)[reg] & val; + R[reg] = R[reg] & val; SetFlagsSZ(reg); - return 10; + return 10 + EXTRA_IF_R6R7(reg); } int ANDa(int v) // And Indirect { int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - CTX(R)[dreg] = CTX(R)[dreg] & val; + R[dreg] = R[dreg] & val; SetFlagsSZ(dreg); - return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg) + EXTRA_IF_R6(areg); } int ANDI(int v) // And Immediate { - v = v | 0x0038; // set address register to regPC + v = v | 0x0038; // set address register to PC return(ANDa(v)); // call indirect } int XOR(int v) // Xor { int reg = v & 0x07; int val = readOperandIndirect(); - CTX(R)[reg] = CTX(R)[reg] ^ val; + R[reg] = R[reg] ^ val; SetFlagsSZ(reg); - return 10; + return 10 + EXTRA_IF_R6R7(reg); } int XORa(int v) // Xor Indirect { int areg = (v >> 3) & 0x07; int dreg = v & 0x07; int val = readIndirect(areg); - CTX(R)[dreg] = CTX(R)[dreg] ^ val; + R[dreg] = R[dreg] ^ val; SetFlagsSZ(dreg); - return (CTX(Flag_DoubleByteData) == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg); + return (Flag_DoubleByteData == 1 ? 10 : 8) + EXTRA_IF_R6R7(areg) + EXTRA_IF_R6(areg); } int XORI(int v) // Xor Immediate { - v = v | 0x0038; // set address register to regPC + v = v | 0x0038; // set address register to PC return(XORa(v)); // call indirect } diff --git a/src/cp1610.h b/src/cp1610.h index 92d64f9..15c7239 100644 --- a/src/cp1610.h +++ b/src/cp1610.h @@ -17,55 +17,18 @@ along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ -#include -typedef struct CP1610_Context_t -{ - uint32_t Version; - - unsigned int R[8]; // Registers R0-R7 - - int InstructionRegister; // four external lines? - - int Flag_DoubleByteData; - int Flag_InteruptEnable; - int Flag_Carry; - int Flag_Sign; - int Flag_Zero; - int Flag_Overflow; - - unsigned int Memory[0x10000]; - - // Stick - unsigned int STICMode; - - int stic_phase; - int stic_vid_enable; - int stic_reg; - int stic_gram; - int phase_len; - - int DisplayEnabled; - - unsigned int frame[352*224]; - - unsigned int scanBuffer[768]; // buffer for current scanline (352+32)*2 - unsigned int collBuffer[768]; // buffer for collision -- made larger than needed to save checks - - int delayH; // Horizontal Delay - int delayV; // Vertical Delay - - int extendTop; - int extendLeft; - - unsigned int CSP; // Color Stack Pointer - unsigned int cscolors[4]; // color squares colors - unsigned int fgcard[20]; // cached colors for cards on current row - unsigned int bgcard[20]; // (used for normal color stack mode) -} CP1610_Context, *PCP1610_Context; - -extern CP1610_Context gCP1610_Context; -#define CTX(x) gCP1610_Context.x -#define CP1610_SERIALIZE_VERSION 1 +struct CP1610serialized { + int Flag_DoubleByteData; + int Flag_InteruptEnable; + int Flag_Carry; + int Flag_Sign; + int Flag_Zero; + int Flag_Overflow; + unsigned int R[8]; +}; + +void CP1610Serialize(struct CP1610serialized *); +void CP1610Unserialize(const struct CP1610serialized *); void CP1610Init(void); // Adds opcodes to lookup tables diff --git a/src/intv.c b/src/intv.c index 14fb949..d06bef9 100644 --- a/src/intv.c +++ b/src/intv.c @@ -24,6 +24,7 @@ #include "controller.h" #include "cart.h" #include "osd.h" +#include "ivoice.h" int SR1; int intv_halt; @@ -53,7 +54,7 @@ void loadExec(const char* path) for(i=0x1000; i<=0x1FFF; i++) { fread(word,sizeof(word),1,fp); - CTX(Memory)[i] = (word[0]<<8) | word[1]; + Memory[i] = (word[0]<<8) | word[1]; } fclose(fp); @@ -79,7 +80,7 @@ void loadGrom(const char* path) for(i=0x3000; i<=0x37FF; i++) { fread(word,sizeof(word),1,fp); - CTX(Memory)[i] = word[0]; + Memory[i] = word[0]; } fclose(fp); @@ -100,15 +101,16 @@ void Reset() SR1 = 0; intv_halt = 0; CP1610Reset(); - MemoryInit(); STICReset(); - PSGInit(); + ivoice_reset(); } void Init() { CP1610Init(); MemoryInit(); + PSGInit(); + ivoice_init(0, 1.0); } void Run() @@ -142,6 +144,9 @@ int exec(void) // Run one instruction // Tick PSG PSGTick(ticks); + + // Tick Intellivoice + ivoice_tk(ticks); if(SR1>0) { @@ -149,58 +154,68 @@ int exec(void) // Run one instruction if(SR1<0) { SR1 = 0; } } - CTX(phase_len) -= ticks; - if (CTX(phase_len) < 0) { - CTX(stic_phase) = (CTX(stic_phase) + 1) & 15; - switch (CTX(stic_phase)) { + phase_len -= ticks; + if (phase_len < 0) { + stic_phase = (stic_phase + 1) & 15; + switch (stic_phase) { case 0: // Start of VBLANK - CTX(stic_reg) = 1; // STIC registers accessible - CTX(stic_gram) = 1; // GRAM accessible - CTX(phase_len) += 2900; - SR1 = CTX(phase_len); + stic_reg = 1; // STIC registers accessible + stic_gram = 1; // GRAM accessible + phase_len += 2900; + SR1 = phase_len; // Render Frame // - STICDrawFrame(CTX(stic_vid_enable)); + STICDrawFrame(stic_vid_enable); + // The following line was below just after + // "stic_vid_enable = DisplayEnabled;" + // It caused D1K Homebrew to fail: + // o D1K misses a video interrupt. + // o However it updates DisplayEnabled in time (writing to 0x20) + // o So the DisplayEnabled variable should be reset here. + DisplayEnabled = 0; return 0; case 1: - CTX(phase_len) += 3796 - 2900; - CTX(stic_vid_enable) = CTX(DisplayEnabled); - CTX(DisplayEnabled) = 0; - if (CTX(stic_vid_enable)) - CTX(stic_reg) = 0; // STIC registers now inaccessible - CTX(stic_gram) = 1; // GRAM accessible + phase_len += 3796 - 2900; + stic_vid_enable = DisplayEnabled; + if (stic_vid_enable) + stic_reg = 0; // STIC registers now inaccessible + stic_gram = 1; // GRAM accessible break; case 2: - CTX(delayV) = ((CTX(Memory)[0x31])&0x7); - CTX(delayH) = ((CTX(Memory)[0x30])&0x7); - CTX(phase_len) += 120 + 114 * CTX(delayV) + CTX(delayH); - if (CTX(stic_vid_enable)) { - CTX(stic_gram) = 0; // GRAM now inaccessible - CTX(phase_len) -= 68; // BUSRQ period (STIC reads RAM) + delayV = ((Memory[0x31])&0x7); + delayH = ((Memory[0x30])&0x7); + phase_len += 120 + 114 * delayV + delayH; + if (stic_vid_enable) { + stic_gram = 0; // GRAM now inaccessible + phase_len -= 68; // BUSRQ period (STIC reads RAM) PSGTick(68); + ivoice_tk(68); } break; default: - CTX(phase_len) += 912; - if (CTX(stic_vid_enable)) { - CTX(phase_len) -= 108; // BUSRQ period (STIC reads RAM) + phase_len += 912; + if (stic_vid_enable) { + phase_len -= 108; // BUSRQ period (STIC reads RAM) PSGTick(108); + ivoice_tk(108); } break; case 14: - CTX(delayV) = ((CTX(Memory)[0x31])&0x7); - CTX(delayH) = ((CTX(Memory)[0x30])&0x7); - CTX(phase_len) += 912 - 114 * CTX(delayV) - CTX(delayH); - if (CTX(stic_vid_enable)) { - CTX(phase_len) -= 108; // BUSRQ period (STIC reads RAM) + delayV = ((Memory[0x31])&0x7); + delayH = ((Memory[0x30])&0x7); + phase_len += 912 - 114 * delayV - delayH; + if (stic_vid_enable) { + phase_len -= 108; // BUSRQ period (STIC reads RAM) PSGTick(108); + ivoice_tk(108); } break; case 15: - CTX(delayV) = ((CTX(Memory)[0x31])&0x7); - CTX(phase_len) += 57 + 17; - if (CTX(stic_vid_enable) && CTX(delayV) == 0) { - CTX(phase_len) -= 38; // BUSRQ period (STIC reads RAM) + delayV = ((Memory[0x31])&0x7); + phase_len += 57 + 17; + if (stic_vid_enable && delayV == 0) { + phase_len -= 38; // BUSRQ period (STIC reads RAM) PSGTick(38); + ivoice_tk(38); } break; diff --git a/src/intv.h b/src/intv.h index 011d647..c75acc2 100644 --- a/src/intv.h +++ b/src/intv.h @@ -17,6 +17,8 @@ along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ +#define AUDIO_FREQUENCY 44100 + extern int SR1; // SR1 line for interrupt extern int intv_halt; diff --git a/src/ivoice.c b/src/ivoice.c new file mode 100644 index 0000000..1caeec5 --- /dev/null +++ b/src/ivoice.c @@ -0,0 +1,1735 @@ +//#define SINGLE_STEP +//#define DEBUG +//#define DEBUG_FIFO +/* + * ============================================================================ + * Title: Intellivoice Emulation + * Author: J. Zbiciak + * ============================================================================ + * This module actually attempts to emulate the Intellivoice. Wild! + * ============================================================================ + * The Intellivoice is mapped into two locations in memory, $0080-$0081. + * (This ignores the separate 8-bit bus that the SPB-640 provides, since + * nothing uses it and I haven't emulated it.) + * + * Location $0080 provides an interface to the "Address LoaD" (ALD) + * mechanism on the SP0256. Reads from this address return the current + * "Load ReQuest" (LRQ) state in bit 15. When LRQ is 1 (ie. bit 15 of + * location $0080 reads as 1), the SP0256 is ready to receive a new command. + * A new command address may then be written to location $0080 to trigger + * the playback of a sound. Note that command address register is actually + * a 1-deep FIFO, and so LRQ will go to 1 before the SP0256 is finished + * speaking. + * + * Location $0081 provides an interface to the SPB-640's 64-decle speech + * FIFO. Reads from this address return the "FIFO full" state in bit 15. + * When bit 15 reads as 0, the FIFO has room for at least 1 more decle. + * Writes to this address can either clear the FIFO, or provide new data + * to the FIFO. To clear the FIFO, write a value with Bit 10 == 1. + * To put a decle into the FIFO, write a value with Bit 10 == 0. It's + * currently unknown what happens when a program attempts to write to the + * FIFO when the FIFO is full. This emulation drops the extra data. + * + * The exact format of the SP0256 speech data, as well as the overall + * system view from the SP0256's perspective is documented elsewhere. + * ============================================================================ + */ + +/*#define SINGLE_STEP*/ + +#undef DEBUG_FIFO +#ifdef DEBUG_FIFO +#define dfprintf(x) jzp_printf x ; jzp_flush() +#else +#define dfprintf(x) +#endif + +#undef DEBUG +#ifdef DEBUG +#define jzdprintf(x) jzp_printf x ; jzp_flush() +#else +#define jzdprintf(x) +#endif + +#undef HIGH_QUALITY +#define PER_PAUSE (64) /* Equiv timing period for pauses. */ +#define PER_NOISE (64) /* Equiv timing period for noise. */ + +#define FIFO_ADDR (0x1800 << 3) /* SP0256 address of speech FIFO. */ + +#include +#include +#include +#include +#include +#include "libretro.h" +#include "retro_inline.h" +#include "intv.h" +#include "ivoice.h" + +#define CONDFREE(p) if (p) free(p) + +ivoice_t intellivoice; +int ivoiceBufferSize; +int16_t ivoiceBuffer[AUDIO_FREQUENCY / 60 * 2]; + +void ivoiceSerialize(struct ivoiceSerialized *data) +{ + memcpy(&data->main, &intellivoice, sizeof(intellivoice)); + data->ivoiceBufferSize = ivoiceBufferSize; + memcpy(data->ivoiceBuffer, ivoiceBuffer, sizeof(ivoiceBuffer)); +} + +void ivoiceUnserialize(const struct ivoiceSerialized *data) +{ + // Copies everything except the pointers + memcpy(&intellivoice, &data->main, (unsigned char *) &intellivoice.cur_buf - (unsigned char *) &intellivoice); + ivoiceBufferSize = data->ivoiceBufferSize; + memcpy(ivoiceBuffer, data->ivoiceBuffer, sizeof(ivoiceBuffer)); +} + +/* ======================================================================== */ +/* Internal function prototypes. */ +/* ======================================================================== */ +static INLINE int16_t limit (int16_t s); +static INLINE uint32_t bitrev(uint32_t val); +static int lpc12_update(lpc12_t *f, int, int16_t *, uint32_t *); +static void lpc12_regdec(lpc12_t *f); +static uint32_t sp0256_getb(ivoice_t *ivoice, int len); +static void sp0256_micro(ivoice_t *iv); + +/* ======================================================================== */ +/* IVOICE_QTBL -- Coefficient Quantization Table. This comes from a */ +/* SP0250 data sheet, and should be correct for SP0256. */ +/* ======================================================================== */ +static const int16_t qtbl[128] = +{ + 0, 9, 17, 25, 33, 41, 49, 57, + 65, 73, 81, 89, 97, 105, 113, 121, + 129, 137, 145, 153, 161, 169, 177, 185, + 193, 201, 209, 217, 225, 233, 241, 249, + 257, 265, 273, 281, 289, 297, 301, 305, + 309, 313, 317, 321, 325, 329, 333, 337, + 341, 345, 349, 353, 357, 361, 365, 369, + 373, 377, 381, 385, 389, 393, 397, 401, + 405, 409, 413, 417, 421, 425, 427, 429, + 431, 433, 435, 437, 439, 441, 443, 445, + 447, 449, 451, 453, 455, 457, 459, 461, + 463, 465, 467, 469, 471, 473, 475, 477, + 479, 481, 482, 483, 484, 485, 486, 487, + 488, 489, 490, 491, 492, 493, 494, 495, + 496, 497, 498, 499, 500, 501, 502, 503, + 504, 505, 506, 507, 508, 509, 510, 511 +}; + +/* ======================================================================== */ +/* LIMIT -- Limiter function for digital sample output. */ +/* ======================================================================== */ +static INLINE int16_t limit(int16_t s) +{ +#ifdef HIGH_QUALITY /* Higher quality than the original, but who cares? */ + if (s > 8191) return 8191; + if (s < -8192) return -8192; +#else + if (s > 127) return 127; + if (s < -128) return -128; +#endif + return s; +} + +#if 0 +/* ======================================================================== */ +/* SAMP_MPY -- Multiply sample w/ coef */ +/* ======================================================================== */ +static int samp_mpy(int coef, int samp) +{ + return coef * samp; +} +#endif + +/* ======================================================================== */ +/* AMP_DECODE -- Decode amplitude register */ +/* ======================================================================== */ +static int amp_decode(uint8_t a) +{ + /* -------------------------------------------------------------------- */ + /* Amplitude has 3 bits of exponent and 5 bits of mantissa. This */ + /* contradicts USP 4,296,269 but matches the SP0250 Apps Manual. */ + /* -------------------------------------------------------------------- */ + int expn = (a & 0xE0) >> 5; + int mant = (a & 0x1F); + int ampl = mant << expn; + + /* -------------------------------------------------------------------- */ + /* Careful reading of USP 4,296,279, around line 60 in column 14 on */ + /* page 16 of the scan suggests the LSB might be held and injected */ + /* into the output while the exponent gets counted down, although */ + /* this seems dubious. */ + /* -------------------------------------------------------------------- */ +#if 0 + if (mant & 1) + ampl |= (1u << expn) - 1; +#endif + + return ampl; +} + +/* ======================================================================== */ +/* LPC12_UPDATE -- Update the 12-pole filter, outputting samples. */ +/* ======================================================================== */ +static int lpc12_update(lpc12_t *f, int num_samp, int16_t *out, uint32_t *optr) +{ + int i, j; + int16_t samp; + int do_int, bit; + int oidx = *optr; + + /* -------------------------------------------------------------------- */ + /* Iterate up to the desired number of samples. We actually may */ + /* break out early if our repeat count expires. */ + /* -------------------------------------------------------------------- */ + for (i = 0; i < num_samp; i++) + { + /* ---------------------------------------------------------------- */ + /* Generate a series of periodic impulses, or random noise. */ + /* ---------------------------------------------------------------- */ + do_int = 0; + samp = 0; + bit = f->rng & 1; + f->rng = (f->rng >> 1) ^ (bit ? 0x4001 : 0); + + if (--f->cnt <= 0) + { + if (f->rpt-- <= 0) /* Stop if we expire the repeat counter */ + { + f->cnt = f->rpt = 0; + break; + } + + f->cnt = f->per ? f->per : PER_NOISE; + samp = f->amp; + do_int = f->interp; + } + + if (!f->per) + samp = bit ? -f->amp : f->amp; + + /* ---------------------------------------------------------------- */ + /* If we need to, process the interpolation registers. */ + /* ---------------------------------------------------------------- */ + if (do_int) + { + f->r[0] += f->r[14]; + f->r[1] += f->r[15]; + + f->amp = amp_decode(f->r[0]); + f->per = f->r[1]; + + do_int = 0; + } + + /* ---------------------------------------------------------------- */ + /* Each 2nd order stage looks like one of these. The App. Manual */ + /* gives the first form, the patent gives the second form. */ + /* They're equivalent except for time delay. I implement the */ + /* first form. (Note: 1/Z == 1 unit of time delay.) */ + /* */ + /* ---->(+)-------->(+)----------+-------> */ + /* ^ ^ | */ + /* | | | */ + /* | | | */ + /* [B] [2*F] | */ + /* ^ ^ | */ + /* | | | */ + /* | | | */ + /* +---[1/Z]<--+---[1/Z]<--+ */ + /* */ + /* */ + /* +---[2*F]<---+ */ + /* | | */ + /* | | */ + /* v | */ + /* ---->(+)-->[1/Z]-->+-->[1/Z]---+------> */ + /* ^ | */ + /* | | */ + /* | | */ + /* +-----------[B]<---------+ */ + /* */ + /* ---------------------------------------------------------------- */ + for (j = 0; j < 6; j++) + { +#if 1 + samp += (((int)f->b_coef[j] * (int)f->z_data[j][1]) >> 9); + samp += (((int)f->f_coef[j] * (int)f->z_data[j][0]) >> 8); +#else + int temp; + /*temp = ((int)f->f_coef[j] * (int)f->z_data[j][0]) << 1;*/ + /*temp += ((int)f->b_coef[j] * (int)f->z_data[j][1]);*/ + static int fdly = 0; + + if (f->f_coef[j] >= 0) + temp = 2 * f->f_coef[j] * f->z_data[j][0] + fdly; + else + temp = -(-2 * f->f_coef[j] * f->z_data[j][0] + fdly); + + fdly = 1 & (temp >> 26); + temp += samp_mpy(f->b_coef[j], f->z_data[j][1]); + samp += temp >> 9; +#endif + + f->z_data[j][1] = f->z_data[j][0]; + f->z_data[j][0] = samp; + } + +#ifdef HIGH_QUALITY /* Higher quality than the original, but who cares? */ + out[oidx++ & SCBUF_MASK] = limit(samp) * 4; +#else + out[oidx++ & SCBUF_MASK] = limit(samp >> 4) * 256; +#endif + } + + *optr = oidx; + + return i; +} + +/*static int stage_map[6] = { 4, 2, 0, 5, 3, 1 };*/ +/*static int stage_map[6] = { 3, 0, 4, 1, 5, 2 };*/ +/*static int stage_map[6] = { 3, 0, 1, 4, 2, 5 };*/ +static const int stage_map[6] = { 0, 1, 2, 3, 4, 5 }; + +/* ======================================================================== */ +/* LPC12_REGDEC -- Decode the register set in the filter bank. */ +/* ======================================================================== */ +static void lpc12_regdec(lpc12_t *f) +{ + int i; + + /* -------------------------------------------------------------------- */ + /* Decode the Amplitude and Period registers. Force cnt to 0 to get */ + /* the initial impulse. (Redundant?) */ + /* -------------------------------------------------------------------- */ + f->amp = amp_decode(f->r[0]); + f->cnt = 0; + f->per = f->r[1]; + + /* -------------------------------------------------------------------- */ + /* Decode the filter coefficients from the quant table. */ + /* -------------------------------------------------------------------- */ + for (i = 0; i < 6; i++) + { + #define IQ(x) (((x) & 0x80) ? qtbl[0x7F & -(x)] : -qtbl[(x)]) + + f->b_coef[stage_map[i]] = IQ(f->r[2 + 2*i]); + f->f_coef[stage_map[i]] = IQ(f->r[3 + 2*i]); + } + + /* -------------------------------------------------------------------- */ + /* Set the Interp flag based on whether we have interpolation parms */ + /* -------------------------------------------------------------------- */ + f->interp = f->r[14] || f->r[15]; + + return; +} + +/* ======================================================================== */ +/* MASK table */ +/* ======================================================================== */ +static const uint8_t mask[4097] = +{ + 0xE8, 0xBB, 0xE8, 0x87, 0xE8, 0x17, 0xE8, 0x37, 0xE8, 0xF7, 0xE8, 0x8F, + 0xE8, 0xCF, 0xE2, 0xD8, 0xE2, 0x9A, 0xE2, 0x89, 0xE2, 0xDD, 0xE2, 0x37, + 0xE2, 0x2F, 0xEA, 0x04, 0xEA, 0x54, 0xEA, 0x4C, 0xEA, 0xD2, 0xEA, 0x8A, + 0xEA, 0x8E, 0xEA, 0xB1, 0xEA, 0xFD, 0xEA, 0x53, 0xEA, 0xAB, 0xEA, 0x47, + 0xEA, 0xCF, 0xEA, 0xFF, 0xE6, 0x10, 0xE6, 0x48, 0xE6, 0x3C, 0xE6, 0x62, + 0xE6, 0x8A, 0xE6, 0xBA, 0xE6, 0x76, 0xE6, 0x5E, 0xE6, 0xC1, 0xE6, 0xB1, + 0xE6, 0xCB, 0xEE, 0xC8, 0xEE, 0x98, 0xEE, 0xF8, 0xEE, 0xC2, 0xEE, 0x1E, + 0xEE, 0x7E, 0xEE, 0x2D, 0xEE, 0x6D, 0xEE, 0x1D, 0xEE, 0x5D, 0xEE, 0x3D, + 0x18, 0x2B, 0x15, 0xC0, 0x39, 0x24, 0x43, 0xE2, 0x1F, 0x00, 0x18, 0x23, + 0x24, 0xC0, 0x28, 0x23, 0x62, 0xC6, 0x1D, 0xA5, 0x03, 0x20, 0x66, 0x52, + 0x0C, 0x95, 0x03, 0x00, 0x19, 0x2C, 0x0C, 0x80, 0x31, 0x12, 0x62, 0xA7, + 0x1C, 0x00, 0x18, 0x2C, 0x0C, 0xC0, 0x29, 0x94, 0xE0, 0x64, 0x9C, 0x85, + 0x02, 0x38, 0x85, 0x12, 0x9C, 0x8C, 0x03, 0x00, 0x10, 0x35, 0xE7, 0x55, + 0xAD, 0x6D, 0x7F, 0x26, 0x91, 0x85, 0xD4, 0x3C, 0xAB, 0xD6, 0xCF, 0x99, + 0x7A, 0x00, 0x10, 0x34, 0x6F, 0xA1, 0x86, 0xCF, 0x3E, 0xAB, 0x0D, 0xBB, + 0x86, 0x7C, 0x6C, 0xB5, 0x6D, 0xCF, 0x24, 0xB2, 0x88, 0x9E, 0xA7, 0x16, + 0xF3, 0xA9, 0xD2, 0xE6, 0x3D, 0xD5, 0x55, 0xFD, 0x01, 0x00, 0x10, 0x32, + 0x74, 0x98, 0xA9, 0xB7, 0x81, 0x1E, 0xA9, 0x87, 0xF4, 0x66, 0xA3, 0xFC, + 0x8B, 0xD2, 0x96, 0x94, 0xFB, 0xFF, 0x10, 0x03, 0x80, 0x8E, 0x16, 0x0D, + 0x00, 0x10, 0x32, 0x7C, 0x90, 0xAB, 0xB7, 0x81, 0x1E, 0xA9, 0xA7, 0x6E, + 0xF7, 0x22, 0xDD, 0xC7, 0xAA, 0xFE, 0xA5, 0x9C, 0xDE, 0xCC, 0x7E, 0xF4, + 0x2E, 0xAC, 0xFA, 0xC7, 0xD9, 0x91, 0xA5, 0xA5, 0xE4, 0xDC, 0x5F, 0xF4, + 0x2B, 0x9D, 0xFC, 0x03, 0x00, 0x10, 0x31, 0x8F, 0xDC, 0xFF, 0x8C, 0x7C, + 0x97, 0xF6, 0x41, 0xE6, 0xE3, 0xF4, 0xF4, 0xF6, 0x47, 0x23, 0xC2, 0x84, + 0xB6, 0x85, 0x74, 0xFF, 0xD0, 0xDD, 0xCF, 0xEE, 0x3F, 0xB7, 0xEB, 0x01, + 0x00, 0x74, 0x7B, 0xA3, 0xDC, 0x2D, 0x3A, 0x5A, 0xB7, 0x56, 0xEE, 0x45, + 0xDF, 0x5B, 0xDA, 0xBF, 0x68, 0xE9, 0x3B, 0xFD, 0x1F, 0xF5, 0x78, 0x27, + 0xFF, 0xA2, 0x4E, 0xF2, 0xDC, 0x1F, 0x00, 0x10, 0x36, 0x76, 0x9B, 0xA9, + 0xB7, 0xBD, 0x1A, 0x1F, 0x66, 0xD4, 0x85, 0xA3, 0xBB, 0xCB, 0x95, 0x83, + 0x00, 0x10, 0x32, 0x6E, 0xDA, 0x27, 0xBB, 0x7D, 0x22, 0x1F, 0xC6, 0x94, + 0x16, 0x9C, 0xDE, 0x97, 0xD6, 0xA5, 0xD3, 0x7F, 0x52, 0x72, 0x58, 0xF2, + 0x4F, 0xD7, 0x85, 0x03, 0x00, 0x10, 0x32, 0x35, 0x96, 0xA9, 0xB9, 0xBD, + 0x1A, 0x1F, 0x86, 0xCE, 0x6E, 0x13, 0x3D, 0x09, 0xE9, 0xF6, 0x00, 0x10, + 0x32, 0x7B, 0x94, 0xAB, 0xB7, 0x81, 0x1E, 0xA9, 0x87, 0x6E, 0xAF, 0x1B, + 0xDD, 0xF9, 0xAA, 0xFE, 0xA4, 0x57, 0xE6, 0xCC, 0x5E, 0xF4, 0x36, 0xAD, + 0xFA, 0xC7, 0xD5, 0xB5, 0xA4, 0xA5, 0xED, 0xDC, 0x5F, 0xF4, 0x73, 0x9E, + 0xFC, 0x03, 0x00, 0x10, 0x32, 0xF7, 0x9F, 0xA9, 0xBD, 0x3F, 0x22, 0x11, + 0x86, 0x6E, 0xCF, 0xA3, 0xDB, 0xFB, 0x46, 0xEB, 0xC8, 0xE9, 0x3F, 0x00, + 0x10, 0x32, 0xAC, 0x98, 0x27, 0xBD, 0x81, 0x22, 0x1F, 0x87, 0xAE, 0x7E, + 0x1C, 0x6D, 0x81, 0xE7, 0xFF, 0x72, 0xE4, 0x20, 0x00, 0xF1, 0xE1, 0x00, + 0x00, 0x11, 0xFC, 0x13, 0xFF, 0x13, 0xFF, 0x00, 0xFE, 0x13, 0xFF, 0x00, + 0x11, 0xFF, 0x00, 0xFF, 0x00, 0xF7, 0x00, 0x18, 0x32, 0xDD, 0xA0, 0x7D, + 0x81, 0x0F, 0xC7, 0x03, 0xE3, 0xEA, 0x53, 0xC6, 0x75, 0xAB, 0xF0, 0x41, + 0xE8, 0x9E, 0x17, 0x73, 0xA1, 0xD2, 0xDC, 0x62, 0xF6, 0x14, 0x34, 0x4D, + 0x0F, 0x8C, 0xB7, 0x54, 0x99, 0x5A, 0xCB, 0x5F, 0x80, 0x84, 0x6D, 0x88, + 0xF3, 0x65, 0x2A, 0x73, 0xBD, 0xF5, 0x77, 0x50, 0xAD, 0x5D, 0xEF, 0xA1, + 0x5A, 0xF5, 0x45, 0x3C, 0x80, 0x53, 0x14, 0x83, 0xC8, 0xBC, 0xC9, 0x05, + 0x60, 0x09, 0x03, 0x68, 0xB0, 0xAF, 0xA9, 0x81, 0x00, 0x38, 0x78, 0xD8, + 0x8F, 0xD9, 0x61, 0xA2, 0x35, 0x77, 0x90, 0x7F, 0x07, 0xD3, 0xDA, 0x80, + 0xFF, 0xEC, 0xB4, 0x66, 0xDF, 0x31, 0xD8, 0xD8, 0x89, 0xBF, 0x65, 0x9B, + 0x9D, 0x5E, 0x82, 0x3E, 0x12, 0x24, 0x21, 0x6F, 0xFC, 0x24, 0x83, 0x03, + 0x00, 0xF2, 0xF3, 0x1F, 0x5C, 0x3E, 0x48, 0x90, 0x60, 0x0D, 0xEE, 0x03, + 0xA5, 0x8B, 0x00, 0x00, 0x1A, 0xFD, 0x38, 0x50, 0xA6, 0x00, 0xF0, 0x03, + 0x21, 0x6E, 0xC7, 0x8D, 0xD9, 0xF3, 0xA0, 0x30, 0xD2, 0x6F, 0x22, 0xF1, + 0x1A, 0x95, 0x71, 0x89, 0x0C, 0x44, 0x8A, 0xC6, 0xA7, 0xD1, 0x6B, 0xA2, + 0x33, 0xAF, 0x9A, 0x41, 0xD1, 0xCE, 0xFC, 0x2E, 0x3B, 0x4D, 0x74, 0xC6, + 0x24, 0x13, 0x18, 0x91, 0x61, 0x9E, 0x94, 0xD7, 0x75, 0xCE, 0xD4, 0x53, + 0x0A, 0x24, 0x2A, 0xDB, 0x8F, 0xF2, 0x34, 0xD0, 0x19, 0x5B, 0x6A, 0x80, + 0x64, 0x47, 0x79, 0xD7, 0x2D, 0xF7, 0x39, 0x53, 0x4B, 0x09, 0x90, 0xC8, + 0x68, 0x1F, 0xAB, 0xBD, 0x46, 0x69, 0xDA, 0x26, 0x85, 0x08, 0xA2, 0xFE, + 0x71, 0xF1, 0x55, 0xA9, 0xA4, 0x74, 0xE0, 0x87, 0x0F, 0x1E, 0x65, 0xCC, + 0xDC, 0x48, 0x06, 0x2C, 0x2A, 0xF3, 0xDB, 0xE6, 0xB8, 0x52, 0x9A, 0x7D, + 0xA8, 0xA0, 0x46, 0x85, 0x7E, 0x97, 0x0D, 0x47, 0x3A, 0x63, 0xFB, 0xD4, + 0x2B, 0xB0, 0x28, 0xBE, 0x50, 0xC2, 0x44, 0x67, 0xDE, 0xA1, 0x88, 0x16, + 0x19, 0xE6, 0x53, 0x39, 0x96, 0x28, 0x3F, 0x86, 0x49, 0x05, 0x80, 0xC7, + 0x06, 0x10, 0x49, 0x27, 0x71, 0x00, 0x10, 0xC9, 0xF8, 0x46, 0xDB, 0x33, + 0x5F, 0x51, 0xFB, 0x00, 0x0B, 0xCE, 0x76, 0x9F, 0x68, 0x36, 0xA6, 0x0D, + 0xB2, 0x67, 0xA8, 0x59, 0x19, 0xA6, 0x0A, 0xD8, 0x57, 0x2A, 0x30, 0x84, + 0x24, 0xE0, 0x22, 0x32, 0x8D, 0x6B, 0xB4, 0xCF, 0x60, 0xB3, 0xF4, 0xDF, + 0xDF, 0x82, 0xC5, 0xA0, 0x69, 0x91, 0x0C, 0x7A, 0x76, 0xAC, 0x1F, 0xC9, + 0x42, 0xAD, 0x32, 0xAF, 0x98, 0x41, 0x8B, 0x8A, 0xF5, 0x37, 0x59, 0x8A, + 0x75, 0xC6, 0xDE, 0x63, 0xC8, 0xD8, 0xC9, 0x1E, 0x57, 0xC3, 0x91, 0xCE, + 0xB8, 0x88, 0xEE, 0x15, 0x22, 0x8B, 0x13, 0x0E, 0xB3, 0xD0, 0x7D, 0x68, + 0x03, 0xF3, 0xFB, 0x18, 0x23, 0x1C, 0x00, 0x29, 0x18, 0x80, 0x2A, 0xB9, + 0xA6, 0x2E, 0x22, 0x20, 0xD9, 0xC1, 0x1D, 0x36, 0x63, 0x99, 0xCE, 0xD4, + 0x46, 0x04, 0x22, 0x33, 0xBA, 0xC7, 0x6A, 0xB6, 0xCE, 0xC9, 0xEF, 0xD7, + 0x0B, 0x24, 0x58, 0x44, 0xA7, 0xA1, 0x9D, 0xFA, 0x4D, 0x44, 0x12, 0x47, + 0x20, 0x5D, 0x9C, 0x32, 0x2F, 0x54, 0xC9, 0x0A, 0x13, 0xFA, 0x27, 0x3C, + 0xE9, 0x34, 0xE4, 0x02, 0xB0, 0x26, 0x52, 0x40, 0x98, 0x93, 0x58, 0x00, + 0xC5, 0x64, 0x8E, 0x86, 0x7B, 0x91, 0x07, 0x00, 0x93, 0x38, 0xD0, 0xF1, + 0x1F, 0xE2, 0x01, 0x58, 0xF3, 0x39, 0x70, 0x9E, 0x6B, 0xEC, 0x9E, 0x80, + 0x92, 0x1D, 0xFE, 0x6D, 0xF5, 0x9C, 0x67, 0x65, 0x09, 0xE0, 0x00, 0x00, + 0x00, 0xF1, 0xD0, 0xDC, 0x3C, 0x06, 0x1C, 0x4C, 0x6E, 0x07, 0xFC, 0xB1, + 0x54, 0x9A, 0xDA, 0xA7, 0x60, 0x41, 0xA4, 0xEB, 0x7D, 0xA1, 0x95, 0x2A, + 0xC3, 0x16, 0x11, 0x14, 0xD0, 0x6C, 0x0D, 0x1F, 0xA6, 0x50, 0x6B, 0x38, + 0x27, 0x82, 0x82, 0x99, 0x9D, 0xFF, 0xC7, 0x1C, 0xA3, 0x4C, 0x97, 0x34, + 0x50, 0x53, 0x95, 0x00, 0xAA, 0xE6, 0x91, 0x2D, 0x19, 0x00, 0x10, 0xF2, + 0x04, 0x2F, 0xDB, 0xD0, 0x06, 0xF1, 0x00, 0x10, 0x33, 0x66, 0xA6, 0x67, + 0x79, 0x85, 0x22, 0xA9, 0x87, 0xE6, 0x55, 0xB5, 0x6E, 0x00, 0x50, 0x24, + 0xF5, 0xCC, 0xBC, 0x67, 0x9E, 0xED, 0x0D, 0x8A, 0xA4, 0x9E, 0x51, 0x9B, + 0x6B, 0xF6, 0x5F, 0xBA, 0x97, 0xD1, 0xEE, 0x45, 0xCF, 0xBF, 0xB9, 0x3B, + 0x04, 0x8D, 0x39, 0xF9, 0xF9, 0x7C, 0xAE, 0x48, 0xEA, 0x11, 0x7D, 0x7B, + 0x69, 0xEE, 0xA5, 0xA6, 0x31, 0xBD, 0x3F, 0x1E, 0x00, 0x10, 0x33, 0x56, + 0x22, 0x47, 0x4D, 0x81, 0xAE, 0x92, 0x58, 0xC6, 0x85, 0x53, 0x68, 0xD1, + 0x6F, 0x95, 0xEE, 0xD7, 0xD8, 0x67, 0x1C, 0x35, 0xF4, 0xCE, 0x12, 0xF2, + 0x9A, 0xFB, 0x8D, 0xD8, 0x98, 0x20, 0x11, 0x86, 0x22, 0x7A, 0x3F, 0x5E, + 0xFD, 0x47, 0x5B, 0x57, 0xBB, 0xFF, 0x28, 0x4B, 0x6B, 0xF9, 0x1F, 0x2D, + 0x8F, 0xED, 0xFE, 0xF1, 0x00, 0xD0, 0x56, 0x10, 0x33, 0xEE, 0xD4, 0xE5, + 0xF9, 0xBF, 0x23, 0x2D, 0x67, 0xB4, 0xD5, 0x92, 0xDB, 0x97, 0xB6, 0x68, + 0x52, 0xFB, 0xD1, 0xF2, 0x4F, 0x62, 0x4F, 0xFA, 0x71, 0xCA, 0xEB, 0x47, + 0x39, 0x5F, 0x69, 0xFD, 0xE8, 0x83, 0x2D, 0xAB, 0x8F, 0x07, 0x00, 0xD0, + 0x3E, 0x18, 0x33, 0xED, 0x5E, 0xF9, 0x82, 0x8A, 0xD2, 0x03, 0x03, 0xEB, + 0x14, 0xC2, 0xA6, 0x5D, 0x33, 0xB5, 0x26, 0xD7, 0xE2, 0xC2, 0x90, 0xD6, + 0x86, 0xB4, 0xFB, 0xD1, 0x96, 0x76, 0xFA, 0x4F, 0x67, 0x3A, 0x63, 0xC8, + 0x90, 0xDA, 0xF6, 0x1E, 0x35, 0xB2, 0x07, 0x90, 0xAF, 0xCC, 0x78, 0x00, + 0xD0, 0x61, 0xD0, 0x19, 0xD0, 0x55, 0xF1, 0x00, 0xD0, 0x61, 0x10, 0x37, + 0x76, 0x99, 0xAD, 0xB3, 0x7F, 0x1E, 0xA2, 0xA7, 0x74, 0x8F, 0xB3, 0x1A, + 0xCC, 0xED, 0x8D, 0xA4, 0x37, 0xA8, 0xDD, 0x9F, 0xEE, 0x9E, 0x1D, 0x75, + 0x71, 0x29, 0xF7, 0xA2, 0x66, 0x30, 0xDD, 0x7E, 0xE5, 0x00, 0x98, 0x23, + 0xC2, 0xC7, 0x03, 0x00, 0xD0, 0x06, 0xD0, 0x06, 0xD0, 0x53, 0xD0, 0x06, + 0xF1, 0x00, 0xD0, 0x06, 0xD0, 0x06, 0xD0, 0xA7, 0xF1, 0x00, 0x10, 0x32, + 0xF6, 0x9F, 0xA9, 0xBD, 0x3F, 0x22, 0x11, 0x86, 0x6E, 0xCF, 0xA3, 0xBB, + 0xFB, 0x46, 0xEB, 0xC8, 0xE9, 0xFF, 0x3D, 0xB4, 0x15, 0xF1, 0x00, 0xD8, + 0xB0, 0xD8, 0xB4, 0xF1, 0x00, 0xD0, 0x56, 0x10, 0x34, 0x76, 0x9B, 0xAB, + 0xB9, 0xBD, 0x15, 0x1F, 0x87, 0xEE, 0xC6, 0x1B, 0xB5, 0x3B, 0xEB, 0xFE, + 0xA3, 0xA5, 0xED, 0xDC, 0x9F, 0x8E, 0xBC, 0x9D, 0xEB, 0x96, 0xE3, 0x01, + 0x00, 0x10, 0x32, 0x6D, 0xA0, 0xA7, 0xBF, 0x81, 0x15, 0x1F, 0xCA, 0xB4, + 0xB6, 0x9B, 0x1E, 0x88, 0x96, 0x7D, 0x53, 0xFF, 0xD3, 0x77, 0x8E, 0x6A, + 0x00, 0x7D, 0x0A, 0xF1, 0x00, 0xD0, 0x56, 0x10, 0x32, 0x9C, 0xA0, 0xA9, + 0x2D, 0xBF, 0x22, 0x1F, 0x68, 0xF4, 0xF4, 0xA3, 0xF8, 0x93, 0xDE, 0x80, + 0x55, 0x7F, 0xD3, 0xDA, 0xAF, 0xE6, 0x4F, 0x4A, 0x03, 0x56, 0x1C, 0x4A, + 0xCD, 0x3C, 0x7A, 0x43, 0x9C, 0x99, 0x77, 0x4A, 0xF9, 0xCD, 0x0B, 0x4A, + 0x06, 0x00, 0x53, 0x26, 0x78, 0x3C, 0x00, 0xD0, 0x3E, 0xD8, 0xD2, 0xFE, + 0xD0, 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD0, 0x61, 0xD0, 0x55, 0xF3, 0xD0, + 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD0, 0x61, 0xD8, 0x9E, 0xD0, 0x61, 0xF5, + 0xD0, 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD0, 0x06, 0xD0, 0x06, 0xD0, 0x53, + 0xD0, 0x06, 0xD0, 0x06, 0xF4, 0xD0, 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD0, + 0x06, 0xD0, 0x06, 0xD8, 0xD1, 0xD0, 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD8, + 0xCD, 0xFE, 0xD0, 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD8, 0xB0, 0xD8, 0xB4, + 0xD0, 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD0, 0x56, 0x10, 0x32, 0x6D, 0x93, + 0xAB, 0xB1, 0xBF, 0x1A, 0x1F, 0x46, 0xEE, 0xED, 0x1A, 0xAD, 0xC7, 0x6A, + 0xF6, 0xA2, 0x35, 0x5B, 0xDD, 0x9F, 0xF4, 0xA4, 0x9B, 0xFC, 0xDB, 0x8B, + 0x3C, 0x00, 0x87, 0x60, 0xF6, 0x7A, 0x68, 0x2B, 0xD8, 0x13, 0xF1, 0x00, + 0xD0, 0x3E, 0xD8, 0xD2, 0xD0, 0x56, 0xD8, 0x13, 0xF1, 0x00, 0xD0, 0x61, + 0xD0, 0x55, 0xF3, 0xD0, 0x56, 0xD8, 0x13, 0xF1, 0x00, 0xD0, 0x61, 0xD8, + 0x9E, 0xD0, 0x61, 0xD0, 0x56, 0xD8, 0x13, 0xF1, 0x00, 0xD0, 0x06, 0xD0, + 0x06, 0xD0, 0x53, 0xD0, 0x06, 0xD0, 0x06, 0xF4, 0xD0, 0x56, 0xD8, 0x13, + 0xF1, 0x00, 0xD0, 0x06, 0xD0, 0x06, 0xD8, 0xD1, 0xD0, 0x56, 0xD8, 0x13, + 0xF1, 0x00, 0xD8, 0xCD, 0xF7, 0xD0, 0x56, 0xD8, 0x13, 0xF1, 0x00, 0xD8, + 0xB0, 0xD8, 0xB4, 0xD0, 0x56, 0xD8, 0x13, 0xF1, 0x00, 0x10, 0x25, 0x02, + 0xC0, 0x10, 0x97, 0xBC, 0xA4, 0x01, 0xA8, 0x02, 0x93, 0xCF, 0xD8, 0x7D, + 0xB6, 0xD6, 0xFE, 0x6A, 0x7C, 0x1C, 0xD2, 0x1D, 0xD0, 0xEE, 0x3F, 0x5A, + 0xFE, 0x4D, 0xFD, 0x47, 0x4B, 0xC6, 0xB9, 0xFF, 0x88, 0x03, 0x20, 0x43, + 0x27, 0x97, 0xE9, 0x40, 0x3D, 0xBD, 0xED, 0xD5, 0xF8, 0x38, 0xA3, 0x2E, + 0x24, 0xDD, 0x5D, 0xF4, 0xCD, 0xA4, 0xDB, 0x8F, 0xBA, 0x95, 0x74, 0xFF, + 0xD1, 0x8E, 0x72, 0xEE, 0x1F, 0x0F, 0x00, 0xD0, 0x3E, 0x10, 0x35, 0x37, + 0x9A, 0xAB, 0xB5, 0xBF, 0x1A, 0x1F, 0xC7, 0x74, 0x4F, 0xB3, 0xFA, 0x97, + 0xBE, 0x7E, 0x15, 0x03, 0x52, 0x33, 0x93, 0x66, 0x60, 0x52, 0x00, 0xAC, + 0xF1, 0x06, 0x4E, 0x1A, 0x80, 0x3B, 0x06, 0xC5, 0x0C, 0xF7, 0xEA, 0x69, + 0xED, 0xAF, 0xC6, 0xC7, 0x21, 0xED, 0x90, 0xE7, 0x06, 0xA2, 0x15, 0xF6, + 0xD4, 0x7F, 0x3E, 0xA4, 0x00, 0x48, 0xE3, 0x91, 0xC7, 0x03, 0x00, 0xD0, + 0x56, 0xD8, 0xBA, 0xF1, 0x00, 0xD0, 0x56, 0xD8, 0x13, 0xF1, 0x00, 0x10, + 0x28, 0x1D, 0xC0, 0x18, 0x1D, 0x7C, 0x86, 0xDC, 0x33, 0xB5, 0x2E, 0x4F, + 0xE3, 0xD2, 0x8C, 0xD6, 0x7F, 0x75, 0xF7, 0x51, 0x1B, 0xB1, 0x6E, 0x3F, + 0x7A, 0xFB, 0xD5, 0xFD, 0xA1, 0x0D, 0x00, 0xD0, 0x06, 0xF1, 0x00, 0x10, + 0x34, 0x76, 0x9C, 0xA9, 0xBB, 0x7F, 0x1D, 0x22, 0x68, 0x74, 0x7F, 0xAB, + 0xFC, 0x8F, 0xB2, 0x77, 0x73, 0xFF, 0x99, 0xCB, 0x30, 0x62, 0xC7, 0x5F, + 0x53, 0x82, 0x9E, 0x4F, 0xE2, 0x01, 0x58, 0xF2, 0xF1, 0x67, 0x4C, 0x44, + 0x53, 0x6F, 0xFB, 0x3A, 0x44, 0x90, 0xA8, 0xE9, 0x4B, 0x77, 0x97, 0x2B, + 0xD1, 0xE3, 0x01, 0x00, 0xD0, 0x19, 0xD0, 0x55, 0xF1, 0x00, 0x10, 0x32, + 0xB4, 0xA9, 0xA9, 0xBB, 0x7F, 0x1D, 0x22, 0x48, 0xEE, 0x96, 0x0D, 0xDD, + 0x8F, 0x6B, 0xFF, 0x72, 0xBB, 0x73, 0xE8, 0x1E, 0x6D, 0xF9, 0x17, 0x7D, + 0x69, 0xEB, 0xFE, 0xA1, 0x2C, 0xE3, 0xDC, 0x60, 0xF4, 0xB4, 0x9B, 0x1A, + 0xC4, 0x9D, 0x69, 0x73, 0x56, 0x9B, 0xA8, 0x4B, 0x45, 0x37, 0x88, 0x63, + 0xAB, 0xE2, 0x01, 0x00, 0xF1, 0x00, 0xF1, 0x00, 0xF1, 0x00, 0xF1, 0x00, + 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* ======================================================================== */ +/* SP0256_DATAFMT -- Data format table for the SP0256's microsequencer */ +/* */ +/* len 4 bits Length of field to extract */ +/* lshift 4 bits Left-shift amount on field */ +/* param 4 bits Parameter number being updated */ +/* delta 1 bit This is a delta-update. (Implies sign-extend) */ +/* field 1 bit This is a field replace. */ +/* clr5 1 bit Clear F5, B5. */ +/* clrall 1 bit Clear all before doing this update */ +/* ======================================================================== */ + +#define CR(l,s,p,d,f,c5,ca) \ + ( \ + (((l) & 15) << 0) | \ + (((s) & 15) << 4) | \ + (((p) & 15) << 8) | \ + (((d) & 1) << 12) | \ + (((f) & 1) << 13) | \ + (((c5) & 1) << 14) | \ + (((ca) & 1) << 15) \ + ) + +#define CR_DELTA CR(0,0,0,1,0,0,0) +#define CR_FIELD CR(0,0,0,0,1,0,0) +#define CR_CLR5 CR(0,0,0,0,0,1,0) +#define CR_CLRL CR(0,0,0,0,0,0,1) +#define CR_LEN(x) ((x) & 15) +#define CR_SHF(x) (((x) >> 4) & 15) +#define CR_PRM(x) (((x) >> 8) & 15) + +enum { AM = 0, PR, B0, F0, B1, F1, B2, F2, B3, F3, B4, F4, B5, F5, IA, IP }; + +static const uint16_t sp0256_datafmt[] = +{ + /* -------------------------------------------------------------------- */ + /* OPCODE 1111: PAUSE */ + /* -------------------------------------------------------------------- */ + /* 0 */ CR( 0, 0, 0, 0, 0, 0, 0), /* Clear all */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0001: LOADALL */ + /* -------------------------------------------------------------------- */ + /* Mode modes x1 */ + /* 1 */ CR( 8, 0, AM, 0, 0, 0, 0), /* Amplitude */ + /* 2 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 3 */ CR( 8, 0, B0, 0, 0, 0, 0), /* B0 */ + /* 4 */ CR( 8, 0, F0, 0, 0, 0, 0), /* F0 */ + /* 5 */ CR( 8, 0, B1, 0, 0, 0, 0), /* B1 */ + /* 6 */ CR( 8, 0, F1, 0, 0, 0, 0), /* F1 */ + /* 7 */ CR( 8, 0, B2, 0, 0, 0, 0), /* B2 */ + /* 8 */ CR( 8, 0, F2, 0, 0, 0, 0), /* F2 */ + /* 9 */ CR( 8, 0, B3, 0, 0, 0, 0), /* B3 */ + /* 10 */ CR( 8, 0, F3, 0, 0, 0, 0), /* F3 */ + /* 11 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 12 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* 13 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 14 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + /* 15 */ CR( 8, 0, IA, 0, 0, 0, 0), /* Amp Interp */ + /* 16 */ CR( 8, 0, IP, 0, 0, 0, 0), /* Pit Interp */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0100: LOAD_4 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 and 01 */ + /* 17 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */ + /* 18 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 19 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 20 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */ + /* 21 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */ + /* 22 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */ + /* Mode 01 only */ + /* 23 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 24 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + + /* Mode 10 and 11 */ + /* 25 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */ + /* 26 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 27 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 28 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */ + /* 29 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 30 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* Mode 11 only */ + /* 31 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 32 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0110: SETMSB_6 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 only */ + /* 33 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 00 and 01 */ + /* 34 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 35 */ CR( 6, 2, F3, 0, 1, 0, 0), /* F3 (5 MSBs) */ + /* 36 */ CR( 6, 2, F4, 0, 1, 0, 0), /* F4 (5 MSBs) */ + /* Mode 01 only */ + /* 37 */ CR( 8, 0, F5, 0, 1, 0, 0), /* F5 (5 MSBs) */ + + /* Mode 10 only */ + /* 38 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 10 and 11 */ + /* 39 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 40 */ CR( 7, 1, F3, 0, 1, 0, 0), /* F3 (6 MSBs) */ + /* 41 */ CR( 8, 0, F4, 0, 1, 0, 0), /* F4 (6 MSBs) */ + /* Mode 11 only */ + /* 42 */ CR( 8, 0, F5, 0, 1, 0, 0), /* F5 (6 MSBs) */ + + /* 43 */ 0, + /* 44 */ 0, + + /* -------------------------------------------------------------------- */ + /* Opcode 1001: DELTA_9 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 and 01 */ + /* 45 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */ + /* 46 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 47 */ CR( 3, 4, B0, 1, 0, 0, 0), /* B0 4 MSBs */ + /* 48 */ CR( 3, 3, F0, 1, 0, 0, 0), /* F0 5 MSBs */ + /* 49 */ CR( 3, 4, B1, 1, 0, 0, 0), /* B1 4 MSBs */ + /* 50 */ CR( 3, 3, F1, 1, 0, 0, 0), /* F1 5 MSBs */ + /* 51 */ CR( 3, 4, B2, 1, 0, 0, 0), /* B2 4 MSBs */ + /* 52 */ CR( 3, 3, F2, 1, 0, 0, 0), /* F2 5 MSBs */ + /* 53 */ CR( 3, 3, B3, 1, 0, 0, 0), /* B3 5 MSBs */ + /* 54 */ CR( 4, 2, F3, 1, 0, 0, 0), /* F3 6 MSBs */ + /* 55 */ CR( 4, 1, B4, 1, 0, 0, 0), /* B4 7 MSBs */ + /* 56 */ CR( 4, 2, F4, 1, 0, 0, 0), /* F4 6 MSBs */ + /* Mode 01 only */ + /* 57 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 58 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* Mode 10 and 11 */ + /* 59 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */ + /* 60 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 61 */ CR( 4, 1, B0, 1, 0, 0, 0), /* B0 7 MSBs */ + /* 62 */ CR( 4, 2, F0, 1, 0, 0, 0), /* F0 6 MSBs */ + /* 63 */ CR( 4, 1, B1, 1, 0, 0, 0), /* B1 7 MSBs */ + /* 64 */ CR( 4, 2, F1, 1, 0, 0, 0), /* F1 6 MSBs */ + /* 65 */ CR( 4, 1, B2, 1, 0, 0, 0), /* B2 7 MSBs */ + /* 66 */ CR( 4, 2, F2, 1, 0, 0, 0), /* F2 6 MSBs */ + /* 67 */ CR( 4, 1, B3, 1, 0, 0, 0), /* B3 7 MSBs */ + /* 68 */ CR( 5, 1, F3, 1, 0, 0, 0), /* F3 7 MSBs */ + /* 69 */ CR( 5, 0, B4, 1, 0, 0, 0), /* B4 8 MSBs */ + /* 70 */ CR( 5, 0, F4, 1, 0, 0, 0), /* F4 8 MSBs */ + /* Mode 11 only */ + /* 71 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 72 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* -------------------------------------------------------------------- */ + /* Opcode 1010: SETMSB_A */ + /* -------------------------------------------------------------------- */ + /* Mode 00 only */ + /* 73 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 00 and 01 */ + /* 74 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 75 */ CR( 5, 3, F0, 0, 1, 0, 0), /* F0 (5 MSBs) */ + /* 76 */ CR( 5, 3, F1, 0, 1, 0, 0), /* F1 (5 MSBs) */ + /* 77 */ CR( 5, 3, F2, 0, 1, 0, 0), /* F2 (5 MSBs) */ + + /* Mode 10 only */ + /* 78 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 10 and 11 */ + /* 79 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 80 */ CR( 6, 2, F0, 0, 1, 0, 0), /* F0 (6 MSBs) */ + /* 81 */ CR( 6, 2, F1, 0, 1, 0, 0), /* F1 (6 MSBs) */ + /* 82 */ CR( 6, 2, F2, 0, 1, 0, 0), /* F2 (6 MSBs) */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0010: LOAD_2 Mode 00 and 10 */ + /* Opcode 1100: LOAD_C Mode 00 and 10 */ + /* -------------------------------------------------------------------- */ + /* LOAD_2, LOAD_C Mode 00 */ + /* 83 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 84 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 85 */ CR( 3, 4, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 86 */ CR( 5, 3, F0, 0, 0, 0, 0), /* F0 */ + /* 87 */ CR( 3, 4, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 88 */ CR( 5, 3, F1, 0, 0, 0, 0), /* F1 */ + /* 89 */ CR( 3, 4, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 90 */ CR( 5, 3, F2, 0, 0, 0, 0), /* F2 */ + /* 91 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 92 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */ + /* 93 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */ + /* 94 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */ + /* LOAD_2 only */ + /* 95 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 96 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* LOAD_2, LOAD_C Mode 10 */ + /* 97 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 98 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 99 */ CR( 6, 1, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 100 */ CR( 6, 2, F0, 0, 0, 0, 0), /* F0 */ + /* 101 */ CR( 6, 1, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 102 */ CR( 6, 2, F1, 0, 0, 0, 0), /* F1 */ + /* 103 */ CR( 6, 1, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 104 */ CR( 6, 2, F2, 0, 0, 0, 0), /* F2 */ + /* 105 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 106 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */ + /* 107 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 108 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* LOAD_2 only */ + /* 109 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 110 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* -------------------------------------------------------------------- */ + /* OPCODE 1101: DELTA_D */ + /* -------------------------------------------------------------------- */ + /* Mode 00 and 01 */ + /* 111 */ CR( 4, 2, AM, 1, 0, 0, 1), /* Amplitude */ + /* 112 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 113 */ CR( 3, 3, B3, 1, 0, 0, 0), /* B3 5 MSBs */ + /* 114 */ CR( 4, 2, F3, 1, 0, 0, 0), /* F3 6 MSBs */ + /* 115 */ CR( 4, 1, B4, 1, 0, 0, 0), /* B4 7 MSBs */ + /* 116 */ CR( 4, 2, F4, 1, 0, 0, 0), /* F4 6 MSBs */ + /* Mode 01 only */ + /* 117 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 118 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* Mode 10 and 11 */ + /* 119 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */ + /* 120 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 121 */ CR( 4, 1, B3, 1, 0, 0, 0), /* B3 7 MSBs */ + /* 122 */ CR( 5, 1, F3, 1, 0, 0, 0), /* F3 7 MSBs */ + /* 123 */ CR( 5, 0, B4, 1, 0, 0, 0), /* B4 8 MSBs */ + /* 124 */ CR( 5, 0, F4, 1, 0, 0, 0), /* F4 8 MSBs */ + /* Mode 11 only */ + /* 125 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 126 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* -------------------------------------------------------------------- */ + /* OPCODE 1110: LOAD_E */ + /* -------------------------------------------------------------------- */ + /* 127 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 128 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0010: LOAD_2 Mode 01 and 11 */ + /* Opcode 1100: LOAD_C Mode 01 and 11 */ + /* -------------------------------------------------------------------- */ + /* LOAD_2, LOAD_C Mode 01 */ + /* 129 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 130 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 131 */ CR( 3, 4, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 132 */ CR( 5, 3, F0, 0, 0, 0, 0), /* F0 */ + /* 133 */ CR( 3, 4, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 134 */ CR( 5, 3, F1, 0, 0, 0, 0), /* F1 */ + /* 135 */ CR( 3, 4, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 136 */ CR( 5, 3, F2, 0, 0, 0, 0), /* F2 */ + /* 137 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 138 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */ + /* 139 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */ + /* 140 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */ + /* 141 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 142 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + /* LOAD_2 only */ + /* 143 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 144 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* LOAD_2, LOAD_C Mode 11 */ + /* 145 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 146 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 147 */ CR( 6, 1, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 148 */ CR( 6, 2, F0, 0, 0, 0, 0), /* F0 */ + /* 149 */ CR( 6, 1, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 150 */ CR( 6, 2, F1, 0, 0, 0, 0), /* F1 */ + /* 151 */ CR( 6, 1, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 152 */ CR( 6, 2, F2, 0, 0, 0, 0), /* F2 */ + /* 153 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 154 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */ + /* 155 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 156 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* 157 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 158 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + /* LOAD_2 only */ + /* 159 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 160 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0011: SETMSB_3 */ + /* Opcode 0101: SETMSB_5 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 only */ + /* 161 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 00 and 01 */ + /* 162 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 163 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 164 */ CR( 5, 3, F0, 0, 1, 0, 0), /* F0 (5 MSBs) */ + /* 165 */ CR( 5, 3, F1, 0, 1, 0, 0), /* F1 (5 MSBs) */ + /* 166 */ CR( 5, 3, F2, 0, 1, 0, 0), /* F2 (5 MSBs) */ + /* SETMSB_3 only */ + /* 167 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 168 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* Mode 10 only */ + /* 169 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 10 and 11 */ + /* 170 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 171 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 172 */ CR( 6, 2, F0, 0, 1, 0, 0), /* F0 (6 MSBs) */ + /* 173 */ CR( 6, 2, F1, 0, 1, 0, 0), /* F1 (6 MSBs) */ + /* 174 */ CR( 6, 2, F2, 0, 1, 0, 0), /* F2 (6 MSBs) */ + /* SETMSB_3 only */ + /* 175 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 176 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0001: LOADALL */ + /* -------------------------------------------------------------------- */ + /* Mode x0 */ + /* 177 */ CR( 8, 0, AM, 0, 0, 0, 0), /* Amplitude */ + /* 178 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 179 */ CR( 8, 0, B0, 0, 0, 0, 0), /* B0 */ + /* 180 */ CR( 8, 0, F0, 0, 0, 0, 0), /* F0 */ + /* 181 */ CR( 8, 0, B1, 0, 0, 0, 0), /* B1 */ + /* 182 */ CR( 8, 0, F1, 0, 0, 0, 0), /* F1 */ + /* 183 */ CR( 8, 0, B2, 0, 0, 0, 0), /* B2 */ + /* 184 */ CR( 8, 0, F2, 0, 0, 0, 0), /* F2 */ + /* 185 */ CR( 8, 0, B3, 0, 0, 0, 0), /* B3 */ + /* 186 */ CR( 8, 0, F3, 0, 0, 0, 0), /* F3 */ + /* 187 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 188 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* 189 */ CR( 8, 0, IA, 0, 0, 0, 0), /* Amp Interp */ + /* 190 */ CR( 8, 0, IP, 0, 0, 0, 0), /* Pit Interp */ + +}; + + + +static const int16_t sp0256_df_idx[16 * 8] = +{ + /* OPCODE 0000 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 1000 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 0100 */ 17, 22, 17, 24, 25, 30, 25, 32, + /* OPCODE 1100 */ 83, 94, 129,142, 97, 108, 145,158, + /* OPCODE 0010 */ 83, 96, 129,144, 97, 110, 145,160, + /* OPCODE 1010 */ 73, 77, 74, 77, 78, 82, 79, 82, + /* OPCODE 0110 */ 33, 36, 34, 37, 38, 41, 39, 42, + /* OPCODE 1110 */ 127,128, 127,128, 127,128, 127,128, + /* OPCODE 0001 */ 177,190, 1, 16, 177,190, 1, 16, + /* OPCODE 1001 */ 45, 56, 45, 58, 59, 70, 59, 72, + /* OPCODE 0101 */ 161,166, 162,166, 169,174, 170,174, + /* OPCODE 1101 */ 111,116, 111,118, 119,124, 119,126, + /* OPCODE 0011 */ 161,168, 162,168, 169,176, 170,176, + /* OPCODE 1011 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 0111 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 1111 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* ======================================================================== */ +/* BITREV -- Bit-reverse a 32-bit number. */ +/* ======================================================================== */ +static INLINE uint32_t bitrev(uint32_t val) +{ + val = ((val & 0xFFFF0000) >> 16) | ((val & 0x0000FFFF) << 16); + val = ((val & 0xFF00FF00) >> 8) | ((val & 0x00FF00FF) << 8); + val = ((val & 0xF0F0F0F0) >> 4) | ((val & 0x0F0F0F0F) << 4); + val = ((val & 0xCCCCCCCC) >> 2) | ((val & 0x33333333) << 2); + val = ((val & 0xAAAAAAAA) >> 1) | ((val & 0x55555555) << 1); + + return val; +} + +/* ======================================================================== */ +/* SP0256_GETB -- Get up to 8 bits at the current PC. */ +/* ======================================================================== */ +static uint32_t sp0256_getb(ivoice_t *ivoice, int len) +{ + uint32_t data = 0; + uint32_t d0, d1; + + /* -------------------------------------------------------------------- */ + /* Fetch data from the FIFO or from the MASK */ + /* -------------------------------------------------------------------- */ + if (ivoice->fifo_sel) + { + d0 = ivoice->fifo[(ivoice->fifo_tail ) & 63]; + d1 = ivoice->fifo[(ivoice->fifo_tail + 1) & 63]; + + data = ((d1 << 10) | d0) >> ivoice->fifo_bitp; + +#ifdef DEBUG_FIFO + dfprintf(("IV: RD_FIFO %.3X %d.%d %d\n", data & ((1u << len) - 1), + ivoice->fifo_tail, ivoice->fifo_bitp, ivoice->fifo_head)); +#endif + + /* ---------------------------------------------------------------- */ + /* Note the PC doesn't advance when we execute from FIFO. */ + /* Just the FIFO's bit-pointer advances. (That's not REALLY */ + /* what happens, but that's roughly how it behaves.) */ + /* ---------------------------------------------------------------- */ + ivoice->fifo_bitp += len; + if (ivoice->fifo_bitp >= 10) + { + ivoice->fifo_tail++; + ivoice->fifo_bitp -= 10; + } + } else + { + /* ---------------------------------------------------------------- */ + /* Figure out which ROMs are being fetched into, and grab two */ + /* adjacent bytes. The byte we're interested in is extracted */ + /* from the appropriate bit-boundary between them. */ + /* ---------------------------------------------------------------- */ + int idx0 = (ivoice->pc ) >> 3, page0 = idx0 >> 12; + int idx1 = (ivoice->pc + 8) >> 3, page1 = idx1 >> 12; + + idx0 &= 0xFFF; + idx1 &= 0xFFF; + + d0 = d1 = 0; + + if (ivoice->rom[page0]) d0 = ivoice->rom[page0][idx0]; + if (ivoice->rom[page1]) d1 = ivoice->rom[page1][idx1]; + + data = ((d1 << 8) | d0) >> (ivoice->pc & 7); + + ivoice->pc += len; + } + + /* -------------------------------------------------------------------- */ + /* Mask data to the requested length. */ + /* -------------------------------------------------------------------- */ + data &= ((1u << len) - 1); + + return data; +} + +/* ======================================================================== */ +/* SP0256_MICRO -- Emulate the microsequencer in the SP0256. Executes */ +/* instructions either until the repeat count != 0 or */ +/* the sequencer gets halted by a RTS to 0. */ +/* ======================================================================== */ +static void sp0256_micro(ivoice_t *iv) +{ + uint8_t immed4; + uint8_t opcode; + uint16_t cr; + int ctrl_xfer = 0; + int repeat = 0; + int i, idx0, idx1; + + /* -------------------------------------------------------------------- */ + /* Only execute instructions while the filter is not busy. */ + /* -------------------------------------------------------------------- */ + while (iv->filt.rpt <= 0 && iv->filt.cnt <= 0) + { + /* ---------------------------------------------------------------- */ + /* If the CPU is halted, see if we have a new command pending */ + /* in the Address LoaD buffer. */ + /* ---------------------------------------------------------------- */ + if (iv->halted && !iv->lrq) + { + iv->pc = iv->ald | (0x1000 << 3); + iv->fifo_sel = 0; + iv->halted = 0; + iv->lrq = 0x8000; + iv->ald = 0; +/* for (i = 0; i < 16; i++)*/ +/* iv->filt.r[i] = 0;*/ + } + + /* ---------------------------------------------------------------- */ + /* If we're still halted, do nothing. */ + /* ---------------------------------------------------------------- */ + if (iv->halted) + { + iv->filt.rpt = 1; + iv->filt.cnt = 0; + iv->lrq = 0x8000; + iv->ald = 0; +/* for (i = 0; i < 16; i++)*/ +/* iv->filt.r[i] = 0;*/ + return; + } + + /* ---------------------------------------------------------------- */ + /* Fetch the first 8 bits of the opcode, which are always in the */ + /* same approximate format -- immed4 followed by opcode. */ + /* ---------------------------------------------------------------- */ + immed4 = sp0256_getb(iv, 4); + opcode = sp0256_getb(iv, 4); + repeat = 0; + ctrl_xfer = 0; + + jzdprintf(("$%.4X.%.1X: OPCODE %d%d%d%d.%d%d\n", + (iv->pc >> 3) - 1, iv->pc & 7, + !!(opcode & 1), !!(opcode & 2), + !!(opcode & 4), !!(opcode & 8), + !!(iv->mode&4), !!(iv->mode&2))); + + /* ---------------------------------------------------------------- */ + /* Handle the special cases for specific opcodes. */ + /* ---------------------------------------------------------------- */ + switch (opcode) + { + /* ------------------------------------------------------------ */ + /* OPCODE 0000: RTS / SETPAGE */ + /* ------------------------------------------------------------ */ + case 0x0: + { + /* -------------------------------------------------------- */ + /* If immed4 != 0, then this is a SETPAGE instruction. */ + /* -------------------------------------------------------- */ + if (immed4) /* SETPAGE */ + { + iv->page = bitrev(immed4) >> 13; + } else + /* -------------------------------------------------------- */ + /* Otherwise, this is an RTS / HLT. */ + /* -------------------------------------------------------- */ + { + uint32_t btrg; + + /* ---------------------------------------------------- */ + /* Figure out our branch target. */ + /* ---------------------------------------------------- */ + btrg = iv->stack; + + iv->stack = 0; + + /* ---------------------------------------------------- */ + /* If the branch target is zero, this is a HLT. */ + /* Otherwise, it's an RTS, so set the PC. */ + /* ---------------------------------------------------- */ + if (!btrg) + { + iv->halted = 1; + iv->pc = 0; + ctrl_xfer = 1; + } else + { + iv->pc = btrg; + ctrl_xfer = 1; + } + } + + break; + } + + /* ------------------------------------------------------------ */ + /* OPCODE 0111: JMP Jump to 12-bit/16-bit Abs Addr */ + /* OPCODE 1011: JSR Jump to Subroutine */ + /* ------------------------------------------------------------ */ + case 0xE: + case 0xD: + { + int btrg; + + /* -------------------------------------------------------- */ + /* Figure out our branch target. */ + /* -------------------------------------------------------- */ + btrg = iv->page | + (bitrev(immed4) >> 17) | + (bitrev(sp0256_getb(iv, 8)) >> 21); + ctrl_xfer = 1; + + /* -------------------------------------------------------- */ + /* If this is a JSR, push our return address on the */ + /* stack. Make sure it's byte aligned. */ + /* -------------------------------------------------------- */ + if (opcode == 0xD) + iv->stack = (iv->pc + 7) & ~7; + + /* -------------------------------------------------------- */ + /* Jump to the new location! */ + /* -------------------------------------------------------- */ + iv->pc = btrg; + break; + } + + /* ------------------------------------------------------------ */ + /* OPCODE 1000: SETMODE Set the Mode and Repeat MSBs */ + /* ------------------------------------------------------------ */ + case 0x1: + { + iv->mode = ((immed4 & 8) >> 2) | (immed4 & 4) | + ((immed4 & 3) << 4); + break; + } + + /* ------------------------------------------------------------ */ + /* OPCODE 0001: LOADALL Load All Parameters */ + /* OPCODE 0010: LOAD_2 Load Per, Ampl, Coefs, Interp. */ + /* OPCODE 0011: SETMSB_3 Load Pitch, Ampl, MSBs, & Intrp */ + /* OPCODE 0100: LOAD_4 Load Pitch, Ampl, Coeffs */ + /* OPCODE 0101: SETMSB_5 Load Pitch, Ampl, and Coeff MSBs */ + /* OPCODE 0110: SETMSB_6 Load Ampl, and Coeff MSBs. */ + /* OPCODE 1001: DELTA_9 Delta update Ampl, Pitch, Coeffs */ + /* OPCODE 1010: SETMSB_A Load Ampl and MSBs of 3 Coeffs */ + /* OPCODE 1100: LOAD_C Load Pitch, Ampl, Coeffs */ + /* OPCODE 1101: DELTA_D Delta update Ampl, Pitch, Coeffs */ + /* OPCODE 1110: LOAD_E Load Pitch, Amplitude */ + /* OPCODE 1111: PAUSE Silent pause */ + /* ------------------------------------------------------------ */ + default: + { + repeat = immed4 | (iv->mode & 0x30); + break; + } + } + if (opcode != 1) iv->mode &= 0xF; + + /* ---------------------------------------------------------------- */ + /* If this was a control transfer, handle setting "fifo_sel" */ + /* and all that ugliness. */ + /* ---------------------------------------------------------------- */ + if (ctrl_xfer) + { + jzdprintf(("jumping to $%.4X.%.1X: ", iv->pc >> 3, iv->pc & 7)); + + /* ------------------------------------------------------------ */ + /* Set our "FIFO Selected" flag based on whether we're going */ + /* to the FIFO's address. */ + /* ------------------------------------------------------------ */ + iv->fifo_sel = iv->pc == FIFO_ADDR; + + jzdprintf(("%s ", iv->fifo_sel ? "FIFO" : "ROM")); + + /* ------------------------------------------------------------ */ + /* Control transfers to the FIFO cause it to discard the */ + /* partial decle that's at the front of the FIFO. */ + /* ------------------------------------------------------------ */ + if (iv->fifo_sel && iv->fifo_bitp) + { + jzdprintf(("bitp = %d -> Flush", iv->fifo_bitp)); + + /* Discard partially-read decle. */ + if (iv->fifo_tail < iv->fifo_head) iv->fifo_tail++; + iv->fifo_bitp = 0; + } + + jzdprintf(("\n")); + + continue; + } + + /* ---------------------------------------------------------------- */ + /* Otherwise, if we have a repeat count, then go grab the data */ + /* block and feed it to the filter. */ + /* ---------------------------------------------------------------- */ + if (!repeat) continue; + + + #ifdef SINGLE_STEP + jzp_printf("NEXT:\n"); jzp_flush(); + { + char buf[1024]; + fgets(buf,sizeof(buf),stdin); if (opcode != 0xF) repeat <<= 3; + } + #endif + + iv->filt.rpt = repeat; + jzdprintf(("repeat = %d\n", repeat)); + + /* clear delay line on new opcode */ + for (i = 0; i < 6; i++) + iv->filt.z_data[i][0] = iv->filt.z_data[i][1] = 0; + + i = (opcode << 3) | (iv->mode & 6); + idx0 = sp0256_df_idx[i++]; + idx1 = sp0256_df_idx[i ]; + + assert(idx0 >= 0 && idx1 >= 0 && idx1 >= idx0); + + /* ---------------------------------------------------------------- */ + /* If we're in one of the 10-pole modes (x0), clear F5/B5. */ + /* ---------------------------------------------------------------- */ + if ((iv->mode & 2) == 0) + iv->filt.r[F5] = iv->filt.r[B5] = 0; + + + /* ---------------------------------------------------------------- */ + /* Step through control words in the description for data block. */ + /* ---------------------------------------------------------------- */ + for (i = idx0; i <= idx1; i++) + { + int len, shf, delta, field, prm, clrL; + int8_t value; + + /* ------------------------------------------------------------ */ + /* Get the control word and pull out some important fields. */ + /* ------------------------------------------------------------ */ + cr = sp0256_datafmt[i]; + + len = CR_LEN(cr); + shf = CR_SHF(cr); + prm = CR_PRM(cr); + clrL = cr & CR_CLRL; + delta = cr & CR_DELTA; + field = cr & CR_FIELD; + value = 0; + + jzdprintf(("$%.4X.%.1X: len=%2d shf=%2d prm=%2d d=%d f=%d ", + iv->pc >> 3, iv->pc & 7, len, shf, prm, !!delta, !!field)); + /* ------------------------------------------------------------ */ + /* Clear any registers that were requested to be cleared. */ + /* ------------------------------------------------------------ */ + if (clrL) + { + iv->filt.r[F0] = iv->filt.r[B0] = 0; + iv->filt.r[F1] = iv->filt.r[B1] = 0; + iv->filt.r[F2] = iv->filt.r[B2] = 0; + } + + /* ------------------------------------------------------------ */ + /* If this entry has a bitfield with it, grab the bitfield. */ + /* ------------------------------------------------------------ */ + if (len) + { + value = sp0256_getb(iv, len); + } + else + { + jzdprintf((" (no update)\n")); + continue; + } + + /* ------------------------------------------------------------ */ + /* Sign extend if this is a delta update. */ + /* ------------------------------------------------------------ */ + if (delta) /* Sign extend */ + { + if (value & (1u << (len - 1))) value |= -(1u << len); + } + + /* ------------------------------------------------------------ */ + /* Shift the value to the appropriate precision. */ + /* ------------------------------------------------------------ */ + if (shf) + value = value < 0 ? -(-value << shf) : (value << shf); + + jzdprintf(("v=%.2X (%c%.2X) ", value & 0xFF, + value & 0x80 ? '-' : '+', + 0xFF & (value & 0x80 ? -value : value))); + + iv->silent = 0; + + /* ------------------------------------------------------------ */ + /* If this is a field-replace, insert the field. */ + /* ------------------------------------------------------------ */ + if (field) + { + jzdprintf(("--field-> r[%2d] = %.2X -> ", prm, iv->filt.r[prm])); + + iv->filt.r[prm] &= ~(~0u << shf); /* Clear the old bits. */ + iv->filt.r[prm] |= value; /* Merge in the new bits. */ + + jzdprintf(("%.2X\n", iv->filt.r[prm])); + + continue; + } + + /* ------------------------------------------------------------ */ + /* If this is a delta update, add to the appropriate field. */ + /* ------------------------------------------------------------ */ + if (delta) + { + jzdprintf(("--delta-> r[%2d] = %.2X -> ", prm, iv->filt.r[prm])); + + iv->filt.r[prm] += value; + + jzdprintf(("%.2X\n", iv->filt.r[prm])); + + continue; + } + + /* ------------------------------------------------------------ */ + /* Otherwise, just write the new value. */ + /* ------------------------------------------------------------ */ + iv->filt.r[prm] = value; + jzdprintf(("--value-> r[%2d] = %.2X\n", prm, iv->filt.r[prm])); + } + + /* ---------------------------------------------------------------- */ + /* Most opcodes clear IA, IP. */ + /* ---------------------------------------------------------------- */ + if (opcode != 0x1 && opcode != 0x2 && opcode != 0x3) + { + iv->filt.r[IA] = 0; + iv->filt.r[IP] = 0; + } + + /* ---------------------------------------------------------------- */ + /* Special case: Set PAUSE's equivalent period. */ + /* ---------------------------------------------------------------- */ + if (opcode == 0xF) + { + iv->silent = 1; + iv->filt.r[AM] = 0; + iv->filt.r[PR] = PER_PAUSE; + } + + /* ---------------------------------------------------------------- */ + /* Now that we've updated the registers, go decode them. */ + /* ---------------------------------------------------------------- */ + lpc12_regdec(&iv->filt); + + /* ---------------------------------------------------------------- */ + /* Break out since we now have a repeat count. */ + /* ---------------------------------------------------------------- */ + break; + } +} + +/* ======================================================================== */ +/* IVOICE_TK -- Where the magic happens. Generate voice data for */ +/* our good friend, the Intellivoice. */ +/* ======================================================================== */ +uint32_t ivoice_tk(uint32_t len) +{ + ivoice_t *ivoice = &intellivoice; + uint64_t until = (ivoice->now + len) * 4; + int samples, did_samp, old_idx; + int sys_clock = ivoice->pal_mode ? 4000000 : 3579545; + int clock_per_samp = ivoice->pal_mode ? 400 : 358; + + /* -------------------------------------------------------------------- */ + /* If the rest of the machine hasn't caught up to us, just return. */ + /* -------------------------------------------------------------------- */ + if (until <= ivoice->sound_current) { + ivoice->now += len; + return 0; + } + + /* -------------------------------------------------------------------- */ + /* Iterate the sound engine. */ + /* -------------------------------------------------------------------- */ + while (ivoice->sound_current < until) + { + /* ---------------------------------------------------------------- */ + /* Renormalize our sc_head and sc_tail. */ + /* ---------------------------------------------------------------- */ + while (ivoice->sc_head > SCBUF_SIZE && ivoice->sc_tail > SCBUF_SIZE) + { + ivoice->sc_head -= SCBUF_SIZE; + ivoice->sc_tail -= SCBUF_SIZE; + } + + /* ---------------------------------------------------------------- */ + /* First, drain as much of our scratch buffer as we can into the */ + /* sound buffers. */ + /* ---------------------------------------------------------------- */ + while (ivoice->sc_tail < ivoice->sc_head) + { + int32_t s, ws; + + ws = s = ivoice->scratch[ivoice->sc_tail++ & SCBUF_MASK]; + + if (ivoice->skipping < 0.0) + { + ivoice->skipping += 1.0; + continue; + } + + if (ivoice->time_scale <= 1.0) + { + ivoice->sample_frc += ivoice->rate * clock_per_samp / + ivoice->time_scale; + } else + { + ivoice->sample_frc += ivoice->rate * clock_per_samp; + ivoice->skipping += ivoice->time_scale - 1.0; + } + + if (ivoice->skipping >= 512.0) + ivoice->skipping = -ivoice->skipping; + + /* ------------------------------------------------------------ */ + /* Update the sliding window in down-sample mode */ + /* ------------------------------------------------------------ */ + if (ivoice->rate < 10000) + { + ivoice->wind_sum -= ivoice->window[ivoice->wind_ptr ]; + ivoice->wind_sum += ivoice->window[ivoice->wind_ptr++] = s; + if (ivoice->wind_ptr >= ivoice->wind) ivoice->wind_ptr = 0; + + ws = ivoice->wind_sum / ivoice->wind; + } + + while (ivoice->sample_frc > sys_clock) + { + ivoice->sample_frc -= sys_clock; + + /* -------------------------------------------------------- */ + /* Update the sliding window in up-sample mode */ + /* -------------------------------------------------------- */ + if (ivoice->rate >= 10000) + { + ivoice->wind_sum -= ivoice->window[ivoice->wind_ptr ]; + ivoice->wind_sum += ivoice->window[ivoice->wind_ptr++] = s; + if (ivoice->wind_ptr >= ivoice->wind) ivoice->wind_ptr = 0; + + ws = ivoice->wind_sum / ivoice->wind; + } + + /* -------------------------------------------------------- */ + /* Store out the current sample. */ + /* -------------------------------------------------------- */ + ivoice->cur_buf[ivoice->cur_len++] = ws; + + /* -------------------------------------------------------- */ + /* Commit the buffer when it's full. */ + /* -------------------------------------------------------- */ + if (ivoice->cur_len >= ivoiceBufferSize) + { + ivoice->cur_len = 0; + } + } + } + + /* ---------------------------------------------------------------- */ + /* Calculate the number of samples required at ~10kHz. */ + /* (Actually, on NTSC this is 3579545 / 358, or 9998.73 Hz). */ + /* ---------------------------------------------------------------- */ + samples = ((int)(until - ivoice->sound_current + clock_per_samp - 1)) + / clock_per_samp; + + /* ---------------------------------------------------------------- */ + /* Process the current set of filter coefficients as long as the */ + /* repeat count holds up and we have room in our scratch buffer. */ + /* ---------------------------------------------------------------- */ + did_samp = 0; + old_idx = ivoice->sc_head; + if (samples > 0) do + { + int do_samp; + + /* ------------------------------------------------------------ */ + /* If our repeat count expired, emulate the microsequencer. */ + /* ------------------------------------------------------------ */ + if (ivoice->filt.rpt <= 0 && ivoice->filt.cnt <= 0) + sp0256_micro(ivoice); + + /* ------------------------------------------------------------ */ + /* Do as many samples as we can. */ + /* ------------------------------------------------------------ */ + do_samp = samples - did_samp; + if (ivoice->sc_head + do_samp - ivoice->sc_tail > SCBUF_SIZE) + do_samp = ivoice->sc_tail + SCBUF_SIZE - ivoice->sc_head; + + if (do_samp == 0) break; + + if (ivoice->silent && + ivoice->filt.rpt <= 0 && ivoice->filt.cnt <= 0) + { + int x, y = ivoice->sc_head; + + for (x = 0; x < do_samp; x++) + ivoice->scratch[y++ & SCBUF_MASK] = 0; + ivoice->sc_head += do_samp; + did_samp += do_samp; + } else + { + did_samp += lpc12_update(&ivoice->filt, do_samp, + ivoice->scratch, &ivoice->sc_head); + } + + } while ((ivoice->filt.rpt > 0 || ivoice->filt.cnt > 0) && + samples > did_samp); + + ivoice->sound_current += did_samp * clock_per_samp; + } + +// if (per->now*4 - ivoice->sound_current > THRESH) +// ivoice->snd_buf.drop++; + ivoice->now += len; + + return (ivoice->sound_current >> 2) - (ivoice->now - len); +} + + +/* ======================================================================== */ +/* IVOICE_RD -- Handle reads from the Intellivoice. */ +/* ======================================================================== */ +uint32_t ivoice_rd(uint32_t addr) +{ + ivoice_t *ivoice = &intellivoice; + + /* -------------------------------------------------------------------- */ + /* Address 0x80 returns the SP0256 LRQ status on bit 15. */ + /* -------------------------------------------------------------------- */ + if (addr == 0) + { + return ivoice->lrq; + } + + /* -------------------------------------------------------------------- */ + /* Address 0x81 returns the SPB640 FIFO full status on bit 15. */ + /* -------------------------------------------------------------------- */ + if (addr == 1) + { + return (ivoice->fifo_head - ivoice->fifo_tail) >= 64 ? 0x8000 : 0; + } + + /* -------------------------------------------------------------------- */ + /* Just return 255 for all other addresses in our range. */ + /* -------------------------------------------------------------------- */ + return 0x00FF; +} + +/* ======================================================================== */ +/* IVOICE_WR -- Handle writes to the Intellivoice. */ +/* ======================================================================== */ +void ivoice_wr(uint32_t addr, uint32_t data) +{ + ivoice_t *ivoice = &intellivoice; + + /* -------------------------------------------------------------------- */ + /* Ignore writes outside 0x80, 0x81. */ + /* -------------------------------------------------------------------- */ + if (addr > 1) return; + + /* -------------------------------------------------------------------- */ + /* Address 0x80 is for Address Loads (essentially speech commands). */ + /* -------------------------------------------------------------------- */ + if (addr == 0) + { + /* ---------------------------------------------------------------- */ + /* Drop writes to the ALD register if we're busy. */ + /* ---------------------------------------------------------------- */ + if (!ivoice->lrq) + return; + + /* ---------------------------------------------------------------- */ + /* Set LRQ to "busy" and load the 8 LSBs of the data into the ALD */ + /* reg. We take the command address, and multiply by 2 bytes to */ + /* get the new PC address. */ + /* ---------------------------------------------------------------- */ + ivoice->lrq = 0; + ivoice->ald = (0xFF & data) << 4; + + return; + } + + /* -------------------------------------------------------------------- */ + /* Address 0x81 is for FIFOing up decles. The FIFO is 64 decles */ + /* long. The Head pointer points to where we insert new decles and */ + /* the Tail pointer is where we pull them from. */ + /* -------------------------------------------------------------------- */ + if (addr == 1) + { + /* ---------------------------------------------------------------- */ + /* If Bit 10 is set, just reset the FIFO and SP0256. */ + /* ---------------------------------------------------------------- */ + if (data & 0x400) + { + ivoice->fifo_head = ivoice->fifo_tail = ivoice->fifo_bitp = 0; + + memset(&ivoice->filt, 0, sizeof(ivoice->filt)); + ivoice->halted = 1; + ivoice->filt.rpt = -1; + ivoice->filt.rng = 1; + ivoice->lrq = 0x8000; + ivoice->ald = 0x0000; + ivoice->pc = 0x0000; + ivoice->stack = 0x0000; + ivoice->fifo_sel = 0; + ivoice->mode = 0; + ivoice->page = 0x1000 << 3; + ivoice->silent = 1; + return; + } + + /* ---------------------------------------------------------------- */ + /* If the FIFO is full, drop the data. */ + /* ---------------------------------------------------------------- */ + if ((ivoice->fifo_head - ivoice->fifo_tail) >= 64) + { + jzdprintf(("IV: Dropped FIFO write\n")); + return; + } + + /* ---------------------------------------------------------------- */ + /* FIFO up the lower 10 bits of the data. */ + /* ---------------------------------------------------------------- */ +#ifdef DEBUG_FIFO + dfprintf(("IV: WR_FIFO %.3X %d.%d %d\n", data & 0x3FF, + ivoice->fifo_tail, ivoice->fifo_bitp, ivoice->fifo_head)); +#endif + ivoice->fifo[ivoice->fifo_head++ & 63] = data & 0x3FF; + + return; + } +} + +/* ======================================================================== */ +/* IVOICE_RESET -- Resets the Intellivoice */ +/* ======================================================================== */ +void ivoice_reset(void) +{ + /* -------------------------------------------------------------------- */ + /* Do a software-style reset of the Intellivoice. */ + /* -------------------------------------------------------------------- */ + ivoice_wr(1, 0x400); +} + +/* ======================================================================== */ +/* IVOICE_DTOR -- Destroy an Intellivoice */ +/* ======================================================================== */ +void ivoice_dtor(void) +{ + ivoice_t *ivoice = &intellivoice; + + CONDFREE(ivoice->window); + CONDFREE(ivoice->scratch); +} + +void ivoice_frame(void) +{ + ivoice_t *ivoice = &intellivoice; + int c; + + c = ivoice->cur_len - AUDIO_FREQUENCY / 60; + if (c > 0) + memmove(ivoiceBuffer, ivoiceBuffer + AUDIO_FREQUENCY / 60, c * sizeof(int16_t)); + else + c = 0; + ivoice->cur_len = c; +} + +/* ======================================================================== */ +/* IVOICE_INIT -- Makes a new Intellivoice */ +/* ======================================================================== */ +int ivoice_init +( + int pal_mode, /* PAL vs. NTSC */ + double time_scale /* For --macho */ +) +{ + ivoice_t *ivoice = &intellivoice; + int rate; + int wind; + + ivoiceBufferSize = AUDIO_FREQUENCY / 60 * 2; + rate = AUDIO_FREQUENCY; /* Sampling rate */ + wind = -1; /* Sliding window size */ + + /* -------------------------------------------------------------------- */ + /* First, lets zero out the structure to be safe. */ + /* -------------------------------------------------------------------- */ + memset(ivoice, 0, sizeof(ivoice_t)); + + /* -------------------------------------------------------------------- */ + /* Sanity checks. */ + /* -------------------------------------------------------------------- */ + if (rate < 10000 || rate > 96000) + { + fprintf(stderr, "ivoice: Sampling rate of %d is invalid. Must be " + "between 10000 and 96000.\n", rate); + return -1; + } + + /* -------------------------------------------------------------------- */ + /* If wind == -1, calculate a window size based on the ratio of our */ + /* sample rate to the device's native rate. */ + /* -------------------------------------------------------------------- */ + if (wind == -1) + { + if (rate > 10000) wind = rate / 5000 + 3; + else wind = 20000 / rate + 1; + + if (wind < 1) wind = 1; + +// jzp_printf("ivoice: Automatic sliding-window setting: %d\n", wind); + } + + /* -------------------------------------------------------------------- */ + /* Set up the peripheral. */ + /* -------------------------------------------------------------------- */ +// ivoice->periph.dtor = ivoice_dtor; + + /* -------------------------------------------------------------------- */ + /* Configure our internal variables. */ + /* -------------------------------------------------------------------- */ + ivoice->rom[1] = mask; + ivoice->rate = rate; + ivoice->filt.rng = 1; + ivoice->wind = wind; + ivoice->wind_sum = 0; + ivoice->pal_mode = pal_mode; + ivoice->time_scale = time_scale; + + /* -------------------------------------------------------------------- */ + /* Set up our initial working buffer. */ + /* -------------------------------------------------------------------- */ + ivoice->cur_buf = ivoiceBuffer; + ivoice->cur_len = 0; + + /* -------------------------------------------------------------------- */ + /* Allocate a scratch buffer for generating 10kHz samples. */ + /* -------------------------------------------------------------------- */ + ivoice->sc_head = ivoice->sc_tail = 0; + + /* -------------------------------------------------------------------- */ + /* Set up the microsequencer's initial state. */ + /* -------------------------------------------------------------------- */ + ivoice->halted = 1; + ivoice->filt.rpt = -1; + ivoice->lrq = 0x8000; + ivoice->page = 0x1000 << 3; + ivoice->silent = 1; + + return 0; +} + + +/* ======================================================================== */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +/* ======================================================================== */ +/* Copyright (c) 1998-2000, Joseph Zbiciak */ +/* ======================================================================== */ diff --git a/src/ivoice.h b/src/ivoice.h new file mode 100644 index 0000000..7a1364f --- /dev/null +++ b/src/ivoice.h @@ -0,0 +1,113 @@ +/* + * ============================================================================ + * Title: Intellivoice Emulation + * Author: J. Zbiciak + * ============================================================================ + * This is just a dummy object right now. + * ============================================================================ + */ +#ifndef IVOICE_H_ +#define IVOICE_H_ + +#define SCBUF_SIZE (4096) /* Must be power of 2 */ +#define SCBUF_MASK (SCBUF_SIZE - 1) + +typedef struct lpc12_t +{ + int rpt, cnt; /* Repeat counter, Period down-counter. */ + uint32_t per, rng; /* Period, Amplitude, Random Number Generator */ + int amp; + int16_t f_coef[6]; /* F0 through F5. */ + int16_t b_coef[6]; /* B0 through B5. */ + int16_t z_data[6][2]; /* Time-delay data for the filter stages. */ + uint8_t r[16]; /* The encoded register set. */ + int interp; +} lpc12_t; + + +typedef struct ivoice_t +{ + uint64_t now; + + int silent; /* Flag: Intellivoice is silent. */ + + int16_t scratch[SCBUF_SIZE]; /* Scratch buffer for audio. */ + uint32_t sc_head; /* Head/Tail pointer into scratch circular buf */ + uint32_t sc_tail; /* Head/Tail pointer into scratch circular buf */ + uint64_t sound_current; + int sample_frc, sample_int; + + int window[24]; /* Sliding Window. Enough for 96khz. */ + int wind_sum; /* Window sum. */ + int wind_ptr; /* Window pointer. */ + int rate, wind; /* Sample rate, Window size. */ + int pal_mode; /* PAL vs. NTSC */ + double time_scale; /* For --macho */ + double skipping; /* part of time-scale */ + + lpc12_t filt; /* 12-pole filter */ + int lrq; /* Load ReQuest. == 0 if we can accept a load */ + int ald; /* Address LoaD. < 0 if no command pending. */ + int pc; /* Microcontroller's PC value. */ + int stack; /* Microcontroller's PC stack. */ + int fifo_sel; /* True when executing from FIFO. */ + int halted; /* True when CPU is halted. */ + uint32_t mode; /* Mode register. */ + uint32_t page; /* Page set by SETPAGE */ + + uint32_t fifo_head; /* FIFO head pointer (where new data goes). */ + uint32_t fifo_tail; /* FIFO tail pointer (where data comes from). */ + uint32_t fifo_bitp; /* FIFO bit-pointer (for partial decles). */ + uint16_t fifo[64]; /* The 64-decle FIFO. */ + + int cur_len; /* Fullness of current sound buffer. */ + int16_t *cur_buf; /* Current sound buffer. */ + const uint8_t *rom[16]; /* 4K ROM pages. */ +} ivoice_t; + +struct ivoiceSerialized { + ivoice_t main; + int ivoiceBufferSize; + int16_t ivoiceBuffer[AUDIO_FREQUENCY / 60 * 2]; +}; + +void ivoiceSerialize(struct ivoiceSerialized *); +void ivoiceUnserialize(const struct ivoiceSerialized *); + +uint32_t ivoice_tk(uint32_t); +uint32_t ivoice_rd(uint32_t); +void ivoice_wr(uint32_t, uint32_t); +void ivoice_reset(void); +void ivoice_dtor(void); +void ivoice_frame(void); + +/* ======================================================================== */ +/* IVOICE_INIT -- Makes a new Intellivoice */ +/* ======================================================================== */ +int ivoice_init +( + int pal_mode, + double time_scale +); + +extern int ivoiceBufferSize; +extern int16_t ivoiceBuffer[]; + +#endif +/* ======================================================================== */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +/* ======================================================================== */ +/* Copyright (c) 1998-2000, Joseph Zbiciak */ +/* ======================================================================== */ diff --git a/src/libretro.c b/src/libretro.c index 9dad238..e33330a 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -23,10 +23,11 @@ #include #include "intv.h" -#include "memory.h" #include "cp1610.h" +#include "memory.h" #include "stic.h" #include "psg.h" +#include "ivoice.h" #include "controller.h" #include "osd.h" @@ -66,11 +67,13 @@ int keyboardState = 0; // at 44.1khz, read 735 samples (44100/60) // at 48khz, read 800 samples (48000/60) // e.g. audioInc = 3733.5 / 735 -int audioSamples = 735; -//int audioSamples = 800; +int audioSamples = AUDIO_FREQUENCY / 60; double audioBufferPos = 0.0; -double audioInc = 1; +double audioInc; + +double ivoiceBufferPos = 0.0; +double ivoiceInc; unsigned int frameWidth = MaxWidth; unsigned int frameHeight = MaxHeight; @@ -114,13 +117,58 @@ void retro_init(void) char gromPath[PATH_MAX_LENGTH]; struct retro_keyboard_callback kb = { Keyboard }; + // controller descriptors + struct retro_input_descriptor desc[] = { + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Disc Left" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Disc Up" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Disc Down" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Disc Right" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Left Action Button" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Right Action Button" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Top Action Button" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Last Selected Keypad Button" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Swap Left/Right Controllers" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Console Pause" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Show Keypad" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Show Keypad" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Keypad Clear" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Keypad Enter" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Keypad 0" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Keypad 5" }, + { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Keypad [1-9]" }, + { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Keypad [1-9]" }, + + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Disc Left" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Disc Up" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Disc Down" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Disc Right" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Left Action Button" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Right Action Button" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Top Action Button" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Last Selected Keypad Button" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Swap Left/Right Controllers" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Console Pause" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Show Keypad" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Show Keypad" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Keypad Clear" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Keypad Enter" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Keypad 0" }, + { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Keypad 5" }, + { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Keypad [1-9]" }, + { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Keypad [1-9]" }, + + { 0 }, + }; + // init buffers, structs - memset(CTX(frame), 0, frameSize); - OSD_setDisplay(CTX(frame), MaxWidth, MaxHeight); + memset(frame, 0, frameSize); + OSD_setDisplay(frame, MaxWidth, MaxHeight); // setup controller swap controllerInit(); + Environ(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc); + // reset console Init(); Reset(); @@ -153,7 +201,7 @@ void retro_unload_game(void) void retro_run(void) { - int i = 0; + int c, i, j, k, l; int showKeypad0 = false; int showKeypad1 = false; @@ -166,7 +214,6 @@ void retro_run(void) } /* JoyPad 0 */ - joypad0[0] = InputState(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP); joypad0[1] = InputState(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN); joypad0[2] = InputState(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT); @@ -189,9 +236,10 @@ void retro_run(void) joypad0[15] = InputState(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y); joypad0[16] = InputState(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); joypad0[17] = InputState(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); + joypad0[18] = InputState(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3); + joypad0[19] = InputState(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3); /* JoyPad 1 */ - joypad1[0] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP); joypad1[1] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN); joypad1[2] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT); @@ -214,6 +262,8 @@ void retro_run(void) joypad1[15] = InputState(1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y); joypad1[16] = InputState(1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); joypad1[17] = InputState(1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); + joypad1[18] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3); + joypad1[18] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3); // Pause if((joypad0[8]==1 && joypre0[8]==0) || (joypad1[8]==1 && joypre1[8]==0)) @@ -222,59 +272,12 @@ void retro_run(void) if(paused) { OSD_drawPaused(); -#ifdef XBOXPADSTYLEHELP - OSD_drawTextCenterBG(21, "HELP - PRESS B"); -#else OSD_drawTextCenterBG(21, "HELP - PRESS A"); -#endif } } if(paused) { -#ifdef XBOXPADSTYLE - // If core is being built for a device which uses XBOX style - // facebuttons instead of retropad. Reverse the mapping help - // help menu // - if(joypad0[4]==1 || joypad1[4]==1) - { - OSD_drawTextBG(3, 4, " "); - OSD_drawTextBG(3, 5, " - HELP - "); - OSD_drawTextBG(3, 6, " "); - OSD_drawTextBG(3, 7, " B - RIGHT ACTION BUTTON "); - OSD_drawTextBG(3, 8, " A - LEFT ACTION BUTTON "); - OSD_drawTextBG(3, 9, " X - TOP ACTION BUTTON "); - OSD_drawTextBG(3, 10, " Y - LAST SELECTED KEYPAD BUTTON "); - OSD_drawTextBG(3, 11, " L/R - SHOW KEYPAD "); - OSD_drawTextBG(3, 12, " "); - OSD_drawTextBG(3, 13, " START - PAUSE GAME "); - OSD_drawTextBG(3, 14, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); - OSD_drawTextBG(3, 15, " "); - OSD_drawTextBG(3, 16, " FREEINTV 1.2 LICENSE GPL V3 "); - OSD_drawTextBG(3, 17, " "); - } -#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) - // These games have special mappings so the mapping details - // are offputting so just tell them to refer to manual or overlay. - // help menu // - if(joypad0[4]==1 || joypad1[4]==1) - { - OSD_drawTextBG(3, 4, " "); - OSD_drawTextBG(3, 5, " - HELP - "); - OSD_drawTextBG(3, 6, " "); - OSD_drawTextBG(3, 7, " "); - OSD_drawTextBG(3, 8, " PLEASE CHECK MANUAL "); - OSD_drawTextBG(3, 9, " OR OVERLAY "); - OSD_drawTextBG(3, 10, " "); - OSD_drawTextBG(3, 11, " L/R - SHOW KEYPAD "); - OSD_drawTextBG(3, 12, " "); - OSD_drawTextBG(3, 13, " START - PAUSE GAME "); - OSD_drawTextBG(3, 14, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); - OSD_drawTextBG(3, 15, " "); - OSD_drawTextBG(3, 16, " FREEINTV 1.2 LICENSE GPL V3 "); - OSD_drawTextBG(3, 17, " "); - } -#else // help menu // if(joypad0[4]==1 || joypad1[4]==1) { @@ -286,14 +289,14 @@ void retro_run(void) OSD_drawTextBG(3, 9, " Y - TOP ACTION BUTTON "); OSD_drawTextBG(3, 10, " X - LAST SELECTED KEYPAD BUTTON "); OSD_drawTextBG(3, 11, " L/R - SHOW KEYPAD "); - OSD_drawTextBG(3, 12, " "); - OSD_drawTextBG(3, 13, " START - PAUSE GAME "); - OSD_drawTextBG(3, 14, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); - OSD_drawTextBG(3, 15, " "); - OSD_drawTextBG(3, 16, " FREEINTV 1.2 LICENSE GPL V3 "); - OSD_drawTextBG(3, 17, " "); + OSD_drawTextBG(3, 12, " LT/RT - KEYPAD CLEAR/ENTER "); + OSD_drawTextBG(3, 13, " "); + OSD_drawTextBG(3, 14, " START - PAUSE GAME "); + OSD_drawTextBG(3, 15, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); + OSD_drawTextBG(3, 16, " "); + OSD_drawTextBG(3, 17, " FREEINTV 1.2 LICENSE GPL V3 "); + OSD_drawTextBG(3, 18, " "); } -#endif } else { @@ -329,24 +332,48 @@ void retro_run(void) Run(); // draw overlays - if(showKeypad0) { drawMiniKeypad(0, CTX(frame)); } - if(showKeypad1) { drawMiniKeypad(1, CTX(frame)); } + if(showKeypad0) { drawMiniKeypad(0, frame); } + if(showKeypad1) { drawMiniKeypad(1, frame); } // sample audio from buffer audioInc = 3733.5 / audioSamples; + ivoiceInc = 1.0; + j = 0; for(i=0; i= ivoiceBufferSize) + ivoiceBufferPos = 0.0; audioBufferPos = audioBufferPos * (audioBufferPos<(PSGBufferSize-1)); } audioBufferPos = 0.0; PSGFrame(); + ivoiceBufferPos = 0.0; + ivoice_frame(); } -#ifndef SHARKSHARK + // Swap Left/Right Controller if(joypad0[9]==1 || joypad1[9]==1) { @@ -363,11 +390,11 @@ void retro_run(void) OSD_drawRightLeft(); } } -#endif - if (intv_halt) - OSD_drawTextBG(3, 5, "INTELLIVISION HALTED"); + + if (intv_halt) + OSD_drawTextBG(3, 5, "INTELLIVISION HALTED"); // send frame to libretro - Video(CTX(frame), frameWidth, frameHeight, sizeof(unsigned int) * frameWidth); + Video(frame, frameWidth, frameHeight, sizeof(unsigned int) * frameWidth); } @@ -387,7 +414,7 @@ void retro_get_system_info(struct retro_system_info *info) void retro_get_system_av_info(struct retro_system_av_info *info) { - int pixelformat = RETRO_PIXEL_FORMAT_XRGB8888; + int pixelformat = RETRO_PIXEL_FORMAT_XRGB8888; memset(info, 0, sizeof(*info)); info->geometry.base_width = MaxWidth; @@ -397,12 +424,8 @@ void retro_get_system_av_info(struct retro_system_av_info *info) info->geometry.aspect_ratio = ((float)MaxWidth) / ((float)MaxHeight); info->timing.fps = DefaultFPS; - info->timing.sample_rate = 44100.0; + info->timing.sample_rate = AUDIO_FREQUENCY; -#if 0 - info->timing.sample_rate = 48000.0; - info->timing.sample_rate = 224010; -#endif Environ(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pixelformat); } @@ -422,7 +445,7 @@ RETRO_API void *retro_get_memory_data(unsigned id) { if(id==RETRO_MEMORY_SYSTEM_RAM) { - return CTX(Memory); + return Memory; } return 0; } @@ -436,39 +459,56 @@ RETRO_API size_t retro_get_memory_size(unsigned id) return 0; } -size_t retro_serialize_size(void) { - return sizeof(gCP1610_Context); +#define SERIALIZED_VERSION 0x4f544702 + +struct serialized { + int version; + struct CP1610serialized CP1610; + struct STICserialized STIC; + struct PSGserialized PSG; + struct ivoiceSerialized ivoice; + unsigned int Memory[0x10000]; // Should be equal to Memory.c + // Extra variables from intv.c + int SR1; + int intv_halt; +}; + +size_t retro_serialize_size(void) +{ + return sizeof(struct serialized); } -bool retro_serialize(void *data, size_t size) { - if(size != retro_serialize_size()) - { - // Uhoh - return false; - } - - memcpy(data, &gCP1610_Context, sizeof(gCP1610_Context)); - +bool retro_serialize(void *data, size_t size) +{ + struct serialized *all; + + all = (struct serialized *) data; + all->version = SERIALIZED_VERSION; + CP1610Serialize(&all->CP1610); + STICSerialize(&all->STIC); + PSGSerialize(&all->PSG); + ivoiceSerialize(&all->ivoice); + memcpy(all->Memory, Memory, sizeof(Memory)); + all->SR1 = SR1; + all->intv_halt = intv_halt; return true; } -bool retro_unserialize(const void *data, size_t size) { - if(size != retro_serialize_size()) - { - // Uhoh - return false; - } +bool retro_unserialize(const void *data, size_t size) +{ + const struct serialized *all; - PCP1610_Context restoreContext = (PCP1610_Context)data; - if(restoreContext->Version != 1) - { - // Uhoh, TODO + all = (const struct serialized *) data; + if (all->version != SERIALIZED_VERSION) return false; - } - - memcpy(&gCP1610_Context, restoreContext, sizeof(gCP1610_Context)); - - return true; + CP1610Unserialize(&all->CP1610); + STICUnserialize(&all->STIC); + PSGUnserialize(&all->PSG); + ivoiceUnserialize(&all->ivoice); + memcpy(Memory, all->Memory, sizeof(Memory)); + SR1 = all->SR1; + intv_halt = all->intv_halt; + return true; } /* Stubs */ diff --git a/src/memory.c b/src/memory.c index ffb490f..ba04909 100644 --- a/src/memory.c +++ b/src/memory.c @@ -14,13 +14,15 @@ You should have received a copy of the GNU General Public License along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ +#include -#include "cp1610.h" +#include "intv.h" #include "memory.h" #include "stic.h" #include "psg.h" +#include "ivoice.h" -#include +unsigned int Memory[0x10000]; int stic_and[64] = { 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, @@ -59,19 +61,35 @@ void writeMem(int adr, int val) // Write (should handle hooks/alias) case 0x0b: /* 5800-5FFF */ case 0x0c: /* 6000-67FF */ case 0x0d: /* 6800-6FFF */ + case 0x14: /* A000-A7FF */ + case 0x15: /* A800-AFFF */ + case 0x16: /* B000-B7FF */ + case 0x1a: /* D000-D7FF */ + case 0x1b: /* D800-DFFF */ + case 0x1c: /* E000-E7FF */ + case 0x1d: /* E800-EFFF */ + case 0x1e: /* F000-F7FF */ return; /* Ignore */ - case 0x07: /* GRAM */ - case 0x0f: - case 0x17: - case 0x1f: - if (CTX(stic_gram) != 0) - CTX(Memory)[adr & 0x39FF] = val; + case 0x07: /* GRAM 3800-3fff */ + case 0x0f: /* GRAM 7800-7fff */ + case 0x17: /* GRAM B800-BFFF */ + case 0x1f: /* GRAM F800-FFFF */ + if (stic_gram != 0) { + // GRAM is 8-bit memory + // Note: Without the AND 0xff, Tower of Doom fails as it builds + // map from GRAM. + Memory[adr & 0x39FF] = val & 0xff; + } return; } + if (adr == 0x80 || adr == 0x81) { + ivoice_wr(adr & 1, val); + return; + } if(adr>=0x100 && adr<=0x1FF) { val = val & 0xFF; - CTX(Memory)[adr] = val; + Memory[adr] = val; //PSG Registers if(adr>=0x01F0 && adr<=0x1FD) { @@ -80,27 +98,22 @@ void writeMem(int adr, int val) // Write (should handle hooks/alias) return; } - // STIC Display Enable - if(adr==0x20 || adr==0x4020 || adr==0x8020 || adr==0xC020) - { - if (CTX(stic_reg) != 0) - CTX(DisplayEnabled) = 1; - } - // STIC Mode Select - if(adr==0x21 || adr==0x4021 || adr==0x8021 || adr==0xC021) - { - if (CTX(stic_reg) != 0) - CTX(STICMode) = 0; - } - //STIC Alias - if((adr>=0x0000 && adr<=0x003F) || (adr>=0x4000 && adr<=0x403F) || (adr>=0x8000 && adr<=0x803F) || (adr>=0xC000 && adr<=0xC03F)) - { - if (CTX(stic_reg) != 0) - CTX(Memory)[adr & 0x3F] = (val & stic_and[adr & 0x3f]) | stic_or[adr & 0x3f];; + // STIC access + if ((adr & 0x3fc0) == 0x0000) { + if (stic_reg != 0) { + adr &= 0x3f; + // STIC Display Enable + if (adr == 0x20) + DisplayEnabled = 1; + // STIC Mode Select + if (adr == 0x21) + STICMode = 0; // Foreground/Background mode + Memory[adr] = (val & stic_and[adr]) | stic_or[adr]; + } return; } - CTX(Memory)[adr] = val; + Memory[adr] = val; } @@ -111,51 +124,52 @@ int readMem(int adr) // Read (should handle hooks/alias) int val; adr &= 0xffff; - val = CTX(Memory)[adr]; + if (adr == 0x80 || adr == 0x81) + return ivoice_rd(adr & 1); + // STIC access + if ((adr & 0x3fc0) == 0x0000) { + if (stic_reg != 0 && (adr & 0x3f) == 0x21) + STICMode = 1; // Color Stack mode + if (adr >= 0x4000) + return 0xffff; + if (stic_reg == 0) // Return trash + return adr & 0x0e; + adr &= 0x3f; + val = (Memory[adr] & stic_and[adr]) | stic_or[adr]; + return val; + } + val = Memory[adr]; if(adr>=0x100 && adr<=0x1FF) { val = val & 0xFF; } - if(CTX(stic_reg) != 0) - { - if(adr<=0x3F) - { - val = (CTX(Memory)[adr] & stic_and[adr]) | stic_or[adr]; - } - - // read sensitive addresses - if(adr==0x21 || adr==0x4021 || adr==0x8021 || adr==0xC021) - { - CTX(STICMode) = 1; - } - } return val; } void MemoryInit() { int i; - for(i=0x0000; i<=0x0007; i++) { CTX(Memory)[i] = 0x3800; } // STIC Registers - for(i=0x0008; i<=0x000F; i++) { CTX(Memory)[i] = 0x3000; } - for(i=0x0010; i<=0x0017; i++) { CTX(Memory)[i] = 0x0000; } - for(i=0x0018; i<=0x001F; i++) { CTX(Memory)[i] = 0x3C00; } - for(i=0x0020; i<=0x003F; i++) { CTX(Memory)[i] = 0x3FFF; } - for(i=0x0028; i<=0x002C; i++) { CTX(Memory)[i] = 0x3FF0; } - CTX(Memory)[0x30] = 0x3FF8; - CTX(Memory)[0x31] = 0x3FF8; - CTX(Memory)[0x32] = 0x3FFC; - for(i=0x0040; i<=0x007F; i++) { CTX(Memory)[i] = 0x0000; } - for(i=0x0080; i<=0x00FF; i++) { CTX(Memory)[i] = 0xFFFF; } - for(i=0x0100; i<=0x035F; i++) { CTX(Memory)[i] = 0x0000; } // Scratch, PSG (1F0-1FF), System Ram - for(i=0x0360; i<=0x0FFF; i++) { CTX(Memory)[i] = 0xFFFF; } - for(i=0x1000; i<=0x1FFF; i++) { CTX(Memory)[i] = 0x0000; } // EXEC ROM - for(i=0x2000; i<=0x2FFF; i++) { CTX(Memory)[i] = 0xFFFF; } - for(i=0x3000; i<=0x3FFF; i++) { CTX(Memory)[i] = 0x0000; } // GROM, GRAM - for(i=0x4000; i<=0x4FFF; i++) { CTX(Memory)[i] = 0xFFFF; } - for(i=0x5000; i<=0x5FFF; i++) { CTX(Memory)[i] = 0x0000; } - for(i=0x6000; i<=0xFFFF; i++) { CTX(Memory)[i] = 0xFFFF; } - CTX(Memory)[0x1FE] = 0xFF; // Controller R - CTX(Memory)[0x1FF] = 0xFF; // Controller L + for(i=0x0000; i<=0x0007; i++) { Memory[i] = 0x3800; } // STIC Registers + for(i=0x0008; i<=0x000F; i++) { Memory[i] = 0x3000; } + for(i=0x0010; i<=0x0017; i++) { Memory[i] = 0x0000; } + for(i=0x0018; i<=0x001F; i++) { Memory[i] = 0x3C00; } + for(i=0x0020; i<=0x003F; i++) { Memory[i] = 0x3FFF; } + for(i=0x0028; i<=0x002C; i++) { Memory[i] = 0x3FF0; } + Memory[0x30] = 0x3FF8; + Memory[0x31] = 0x3FF8; + Memory[0x32] = 0x3FFC; + for(i=0x0040; i<=0x007F; i++) { Memory[i] = 0x0000; } + for(i=0x0080; i<=0x00FF; i++) { Memory[i] = 0xFFFF; } + for(i=0x0100; i<=0x035F; i++) { Memory[i] = 0x0000; } // Scratch, PSG (1F0-1FF), System Ram + for(i=0x0360; i<=0x0FFF; i++) { Memory[i] = 0xFFFF; } + for(i=0x1000; i<=0x1FFF; i++) { Memory[i] = 0x0000; } // EXEC ROM + for(i=0x2000; i<=0x2FFF; i++) { Memory[i] = 0xFFFF; } + for(i=0x3000; i<=0x3FFF; i++) { Memory[i] = 0x0000; } // GROM, GRAM + for(i=0x4000; i<=0x4FFF; i++) { Memory[i] = 0xFFFF; } + for(i=0x5000; i<=0x5FFF; i++) { Memory[i] = 0x0000; } + for(i=0x6000; i<=0xFFFF; i++) { Memory[i] = 0xFFFF; } + Memory[0x1FE] = 0xFF; // Controller R + Memory[0x1FF] = 0xFF; // Controller L } diff --git a/src/psg.c b/src/psg.c index 8abc33b..f766e57 100644 --- a/src/psg.c +++ b/src/psg.c @@ -15,22 +15,49 @@ along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ +#include #include #include #include "psg.h" -#include "cp1610.h" #include "memory.h" +int Volume[16] = { 0, 92, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 10922 }; + +int Envelope_Shift[4] = {8, 2, 1, 0}; + +// Volume levels assigned to each channel from PSG registers +#define VolA (Memory[0x01FB] & 0x0F) +#define VolB (Memory[0x01FC] & 0x0F) +#define VolC (Memory[0x01FD] & 0x0F) + +// Envelope shifts for channels (6-bit variations only) +#define EnvA ((Memory[0x01FB] >> 4) & 0x03) +#define EnvB ((Memory[0x01FC] >> 4) & 0x03) +#define EnvC ((Memory[0x01FD] >> 4) & 0x03) + +int NoiseP; // Noise Period + +// Detect Tone enabled for this channel (0- enabled, 1- disabled) +#define ToneA ((Memory[0x01F8] & 0x01) != 0) +#define ToneB ((Memory[0x01F8] & 0x02) != 0) +#define ToneC ((Memory[0x01F8] & 0x04) != 0) + +// Detect Noise enabled for this channel (0- enabled, 1- disabled) +#define NoiseA ((Memory[0x01F8] & 0x08) != 0) +#define NoiseB ((Memory[0x01F8] & 0x10) != 0) +#define NoiseC ((Memory[0x01F8] & 0x20) != 0) + +// Envelope type +#define EnvFlags (Memory[0x01FA] & 0x0F) + int PSGBufferSize; int16_t PSGBuffer[7467]; int PSGBufferPos; -int Volume[16] = { 0, 92, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 10922 }; - int Ticks; // CPU cycles not yet processed int CountA; // countdowns for tone generators -int CountB; // used to modulate square-wave +int CountB; // used to modulate square-wave int CountC; // according to Channel Period int CountN; // countdown for noise generator int CountE; // countdown for envelope generator @@ -42,28 +69,10 @@ int OutN; // Noise generator output int OutE; // Envelope generator output int ChA; // Channel Period from PSG Registers -int ChB; -int ChC; - -int VolA; // volume levels assigned to each Channel from PSG Registers -int VolB; -int VolC; - -int NoiseP; // Noise Period -int NoiseA; // is Noise enabled for this channel -int NoiseB; // 0 - enabled -int NoiseC; // 1 - disabled +int ChB; +int ChC; - -int ToneA; // is Tone enabled for this channel -int ToneB; // 0 - enabled -int ToneC; // 1 - disabled - -int EnvP; // Envelope Period -int EnvFlags; // Envelope Type -int EnvA; // Envelope Shifts for Channels (6-bit variations only) -int EnvB; -int EnvC; +int EnvP; // Envelope Period int StepE; // 1, 0, -1 -- Direction to Step Envelope at end of countdown int EnvContinue; // Flags from Envelope Type @@ -71,39 +80,82 @@ int EnvAttack; int EnvAlternate; int EnvHold; +void PSGSerialize(struct PSGserialized *all) +{ + all->PSGBufferSize = PSGBufferSize; + memcpy(all->PSGBuffer, PSGBuffer, sizeof(PSGBuffer)); + all->PSGBufferPos = PSGBufferPos; + all->Ticks = Ticks; + all->CountA = CountA; + all->CountB = CountB; + all->CountC = CountC; + all->CountN = CountN; + all->CountE = CountE; + all->OutA = OutA; + all->OutB = OutB; + all->OutC = OutC; + all->OutN = OutN; + all->OutE = OutE; + all->ChA = ChA; + all->ChB = ChB; + all->ChC = ChC; + all->NoiseP = NoiseP; + all->EnvP = EnvP; + all->StepE = StepE; + all->EnvContinue = EnvContinue; + all->EnvAttack = EnvAttack; + all->EnvAlternate = EnvAlternate; + all->EnvHold = EnvHold; +} + +void PSGUnserialize(const struct PSGserialized *all) +{ + PSGBufferSize = all->PSGBufferSize; + memcpy(PSGBuffer, all->PSGBuffer, sizeof(PSGBuffer)); + PSGBufferPos = all->PSGBufferPos; + Ticks = all->Ticks; + CountA = all->CountA; + CountB = all->CountB; + CountC = all->CountC; + CountN = all->CountN; + CountE = all->CountE; + OutA = all->OutA; + OutB = all->OutB; + OutC = all->OutC; + OutN = all->OutN; + OutE = all->OutE; + ChA = all->ChA; + ChB = all->ChB; + ChC = all->ChC; + NoiseP = all->NoiseP; + EnvP = all->EnvP; + StepE = all->StepE; + EnvContinue = all->EnvContinue; + EnvAttack = all->EnvAttack; + EnvAlternate = all->EnvAlternate; + EnvHold = all->EnvHold; +} + void readRegisters(void) { - ChA = (CTX(Memory)[0x01F0] & 0xFF) | ((CTX(Memory)[0x1F4] & 0x0F)<<8); - ChB = (CTX(Memory)[0x01F1] & 0xFF) | ((CTX(Memory)[0x1F5] & 0x0F)<<8); - ChC = (CTX(Memory)[0x01F2] & 0xFF) | ((CTX(Memory)[0x1F6] & 0x0F)<<8); + ChA = (Memory[0x01F0] & 0xFF) | ((Memory[0x1F4] & 0x0F)<<8); + ChB = (Memory[0x01F1] & 0xFF) | ((Memory[0x1F5] & 0x0F)<<8); + ChC = (Memory[0x01F2] & 0xFF) | ((Memory[0x1F6] & 0x0F)<<8); - VolA = CTX(Memory)[0x01FB] & 0x0F; - VolB = CTX(Memory)[0x01FC] & 0x0F; - VolC = CTX(Memory)[0x01FD] & 0x0F; - - NoiseP = (CTX(Memory)[0x01F9] & 0x1F)<<1; - NoiseA = (CTX(Memory)[0x01F8]>>3) & 0x01; - NoiseB = (CTX(Memory)[0x01F8]>>4) & 0x01; - NoiseC = (CTX(Memory)[0x01F8]>>5) & 0x01; - - ToneA = CTX(Memory)[0x01F8] & 0x01; - ToneB = (CTX(Memory)[0x01F8]>>1) & 0x01; - ToneC = (CTX(Memory)[0x01F8]>>2) & 0x01; - - EnvP = ((CTX(Memory)[0x01F3] & 0xFF) | ((CTX(Memory)[0x1F7] & 0xFF)<<8))<<1; - EnvFlags = CTX(Memory)[0x01FA] & 0x0F; - EnvA = (CTX(Memory)[0x01FB]>>4) & 0x03; - EnvB = (CTX(Memory)[0x01FC]>>4) & 0x03; - EnvC = (CTX(Memory)[0x01FD]>>4) & 0x03; - - ChA = ChA + (0x1000 * (ChA==0)); // a Channel Period value of 0 - ChB = ChB + (0x1000 * (ChB==0)); // indicates a value of 0x1000 - ChC = ChC + (0x1000 * (ChC==0)); - // a Noise Period of 0 indicates a period of 0x40 - NoiseP = NoiseP + (0x40 * (NoiseP==0)); - // an Envelope Period of 0 indicates a period of 0x20000 - EnvP = EnvP + (0x20000 * (EnvP==0)); - + ChA = ChA + (0x1000 * (ChA==0)); // a Channel Period value of 0 + ChB = ChB + (0x1000 * (ChB==0)); // indicates a value of 0x1000 + ChC = ChC + (0x1000 * (ChC==0)); + + NoiseP = (Memory[0x01F9] & 0x1F)<<1; + + // a Noise Period of 0 indicates a period of 0x40 + NoiseP = NoiseP + (0x40 * (NoiseP==0)); + + EnvP = ((Memory[0x01F3] & 0xFF) | ((Memory[0x1F7] & 0xFF)<<8))<<1; + + // an Envelope Period of 0 indicates a period of 0x20000 + EnvP = EnvP + (0x20000 * (EnvP==0)); + // Envelope Flags EnvContinue = (EnvFlags>>3) & 0x01; EnvAttack = (EnvFlags>>2) & 0x01; @@ -118,7 +170,7 @@ void PSGInit() OutA = 0; // tone generator outputs OutB = 0; OutC = 0; - OutN = 0x14000; // noise output + OutN = 0x10004; // noise output OutE = 0; // envelope output CountA = 0; // tone generator countdowns CountB = 0; @@ -131,6 +183,11 @@ void PSGInit() void PSGFrame() { PSGBufferPos = 0; + #if 0 // Debugging + { + fprintf(stderr, "%04x %04x %04x %02x %02x %02x\n", ChA, ChB, ChC, VolA, VolB, VolC); + } + #endif } int psg_masks[16] = { @@ -142,7 +199,7 @@ int psg_masks[16] = { void PSGNotify(int adr, int val) // PSG Registers Modified 0x01F0-0x1FD (called from writeMem) { - CTX(Memory)[adr] &= psg_masks[adr - 0x1f0]; + Memory[adr] &= psg_masks[adr - 0x1f0]; readRegisters(); // Note: updating frequencies doesn't reset counters in real chip // (otherwise sound glitch happens in games) @@ -173,9 +230,9 @@ void PSGTick(int ticks) // adds 1 sound sample per 4 cpu cycles to the buffer Ticks = Ticks + ticks; - while(Ticks > 3) + while(Ticks >= 4) { - Ticks = Ticks - 4; + Ticks -= 4; CountA--; CountB--; @@ -247,9 +304,9 @@ void PSGTick(int ticks) // adds 1 sound sample per 4 cpu cycles to the buffer c = (NoiseC | (OutN & 1)) & (ToneC | OutC); // Adjust amplitude (Volume / Envelope) - a = a * ( (Volume[VolA] * (EnvA==0)) | (Volume[OutE] * (EnvA!=0)) ); - b = b * ( (Volume[VolB] * (EnvB==0)) | (Volume[OutE] * (EnvB!=0)) ); - c = c * ( (Volume[VolC] * (EnvC==0)) | (Volume[OutE] * (EnvC!=0)) ); + a = a * ( (Volume[VolA] * (EnvA==0)) | (Volume[OutE >> Envelope_Shift[EnvA]]) ); + b = b * ( (Volume[VolB] * (EnvB==0)) | (Volume[OutE >> Envelope_Shift[EnvB]]) ); + c = c * ( (Volume[VolC] * (EnvC==0)) | (Volume[OutE >> Envelope_Shift[EnvC]]) ); sample = a + b + c; diff --git a/src/psg.h b/src/psg.h index 66a1564..ca3dfc9 100644 --- a/src/psg.h +++ b/src/psg.h @@ -23,6 +23,43 @@ extern int16_t PSGBuffer[7467]; // 14934 cpu cycles/frame ; 3733.5 psg cycles/fr extern int PSGBufferPos; // points to next location in output buffer extern int PSGBufferSize; +struct PSGserialized { + int PSGBufferSize; + int16_t PSGBuffer[7467]; + int PSGBufferPos; + + int Ticks; // CPU cycles not yet processed + + int CountA; // countdowns for tone generators + int CountB; // used to modulate square-wave + int CountC; // according to Channel Period + int CountN; // countdown for noise generator + int CountE; // countdown for envelope generator + + int OutA; // outputs for each tone generator + int OutB; + int OutC; + int OutN; // Noise generator output + int OutE; // Envelope generator output + + int ChA; // Channel Period from PSG Registers + int ChB; + int ChC; + + int NoiseP; // Noise Period + + int EnvP; // Envelope Period + int StepE; // 1, 0, -1 -- Direction to Step Envelope at end of countdown + + int EnvContinue; // Flags from Envelope Type + int EnvAttack; + int EnvAlternate; + int EnvHold; +}; + +void PSGSerialize(struct PSGserialized *); +void PSGUnserialize(const struct PSGserialized *); + void PSGInit(void); void PSGFrame(void); // Notify New Frame void PSGTick(int ticks); // ticks PSG some number of cpu cycles diff --git a/src/stic.c b/src/stic.c index 457c2af..b55db08 100644 --- a/src/stic.c +++ b/src/stic.c @@ -17,7 +17,6 @@ #include "intv.h" #include "memory.h" -#include "cp1610.h" #include "stic.h" #include @@ -33,7 +32,30 @@ void drawBackgroundColorStack(int scanline); // http://spatula-city.org/~im14u2c/intv/jzintv-1.0-beta3/doc/programming/stic.txt // http://spatula-city.org/~im14u2c/intv/tech/master.html +unsigned int STICMode; +int stic_phase; +int stic_vid_enable; +int stic_reg; +int stic_gram; +int phase_len; + +int DisplayEnabled; + +unsigned int frame[352*224]; + +unsigned int scanBuffer[768]; // buffer for current scanline (352+32)*2 +unsigned int collBuffer[768]; // buffer for collision -- made larger than needed to save checks + +int delayH = 0; // Horizontal Delay +int delayV = 0; // Vertical Delay + +int extendTop = 0; +int extendLeft = 0; + +unsigned int CSP; // Color Stack Pointer +unsigned int fgcard[20]; // cached colors for cards on current row +unsigned int bgcard[20]; // (used for normal color stack mode) #if defined(ABGR1555) unsigned int color7 = 0xFFFCFF; // Copy of color 7 (for color squares mode) unsigned int colors[16] = @@ -98,59 +120,105 @@ int reverse[256] = // lookup table to reverse the bits in a byte // 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF }; +void STICSerialize(struct STICserialized *all) +{ + all->STICMode = STICMode; + all->stic_phase = stic_phase; + all->stic_vid_enable = stic_vid_enable; + all->stic_reg = stic_reg; + all->stic_gram = stic_gram; + all->phase_len = phase_len; + all->DisplayEnabled = DisplayEnabled; + all->delayH = delayH; + all->delayV = delayV; + all->extendTop = extendTop; + all->extendLeft = extendLeft; + all->CSP = CSP; + memcpy(all->fgcard, fgcard, sizeof(fgcard)); + memcpy(all->bgcard, bgcard, sizeof(bgcard)); + memcpy(all->frame, frame, sizeof(frame)); +} + +void STICUnserialize(const struct STICserialized *all) +{ + STICMode = all->STICMode; + stic_phase = all->stic_phase; + stic_vid_enable = all->stic_vid_enable; + stic_reg = all->stic_reg; + stic_gram = all->stic_gram; + phase_len = all->phase_len; + DisplayEnabled = all->DisplayEnabled; + delayH = all->delayH; + delayV = all->delayV; + extendTop = all->extendTop; + extendLeft = all->extendLeft; + CSP = all->CSP; + memcpy(fgcard, all->fgcard, sizeof(fgcard)); + memcpy(bgcard, all->bgcard, sizeof(bgcard)); + memcpy(frame, all->frame, sizeof(frame)); +} + void STICReset(void) { - CTX(STICMode) = 1; - SR1 = 0; - CTX(DisplayEnabled) = 0; - CTX(CSP) = 0x28; - CTX(stic_phase) = 15; - CTX(phase_len) = 2782; + STICMode = 1; // Color Stack mode + SR1 = 0; // No interrupt pending + DisplayEnabled = 0; + CSP = 0x28; + stic_phase = 15; + stic_reg = 1; + stic_gram = 1; + phase_len = 2782; // Time to run before the first STIC interrupt } void drawBorder(int scanline) { int i; int cbit = 1<<9; // bit 9 - border collision - int color = colors[CTX(Memory)[0x2C] & 0x0f]; // border color + int color = colors[Memory[0x2C] & 0x0f]; // border color if(scanline>=112) { return; } - if (scanline == CTX(delayV) - 1 || scanline == 104) { - for(i=7 * 2; i < (9 + 160) * 2; i += 2) + if (scanline == delayV - 1 || scanline == 104 || extendTop != 0 && scanline >= 7 && scanline < 16) { // Collision border is 1 pixel thick, or 9 if extendTop is set + for(i=1 * 2; i < (8 + 160) * 2; i += 2) // It extends from column -7 to 159 { - CTX(collBuffer)[i] |= cbit; - CTX(collBuffer)[i+384] |= cbit; + collBuffer[i] |= cbit; + collBuffer[i+384] |= cbit; } - } else { - i = 7 * 2; - CTX(collBuffer)[i] |= cbit; - CTX(collBuffer)[i + 384] |= cbit; - i = (8 + 160) * 2; - CTX(collBuffer)[i] |= cbit; - CTX(collBuffer)[i + 384] |= cbit; + } else if (scanline > delayV - 1 && scanline < 104) { // Left and right side collision border + for(i=1 * 2; i < 16+(16*extendLeft); i += 2) // Left side from column -7 to -1 (or 7 if extendLeft is set) + { + collBuffer[i] |= cbit; + collBuffer[i+384] |= cbit; + } + i = (8 + 159) * 2; // Right side collision is 1 pixel thick + collBuffer[i] |= cbit; + collBuffer[i + 384] |= cbit; } - if (CTX(extendTop) != 0) + if (extendTop != 0) i = 16; else - i = CTX(delayV); + i = delayV; if(scanline=104) // top and bottom border { for(i=0; i<352; i++) { - CTX(scanBuffer)[i] = color; - CTX(scanBuffer)[i+384] = color; + scanBuffer[i] = color; + scanBuffer[i+384] = color; } } else // left and right border { - for(i=0; i<16+(16*CTX(extendLeft)); i++) + for(i=0; i<16+(16*extendLeft); i++) { - CTX(scanBuffer)[i] = color; - CTX(scanBuffer)[i+336] = color; - CTX(scanBuffer)[i+384] = color; - CTX(scanBuffer)[i+384+336] = color; + scanBuffer[i] = color; + scanBuffer[i+168*2] = color; + scanBuffer[i+384] = color; + scanBuffer[i+384+168*2] = color; } - } + scanBuffer[167*2] = color; // Invisible 160th column + scanBuffer[167*2 + 1] = color; + scanBuffer[167*2 + 384] = color; // Invisible 160th column + scanBuffer[167*2 + 384 + 1] = color; + } } void drawBackgroundFGBG(int scanline) @@ -164,7 +232,7 @@ void drawBackgroundFGBG(int scanline) int gaddress; // card graphic address int gdata; // current card graphic byte int cbit = 1<<8; // bit 8 - collision bit for Background - int x = CTX(delayH); // current pixel offset + int x = delayH; // current pixel offset // Tiled background is 20x12, cards are 8x8 row = scanline / 8; // Which tile row? (Background is 96 lines high) @@ -175,35 +243,35 @@ void drawBackgroundFGBG(int scanline) // Draw cards for (col=0; col<20; col++) // for each card on the current row... { - card = CTX(Memory)[0x200+row+col]; // card info from BACKTAB + card = Memory[0x200+row+col]; // card info from BACKTAB fgcolor = colors[card & 0x07]; bgcolor = colors[((card>>9)&0x03) | ((card>>11)&0x04) | ((card>>9)&0x08)]; // bits 12,13,10,9 gaddress = 0x3000 + (card & 0x09f8); - gdata = CTX(Memory)[gaddress + cardrow]; // fetch current line of current card graphic + gdata = Memory[gaddress + cardrow]; // fetch current line of current card graphic for(i=7; i>=0; i--) // draw one line of card graphic { if(((gdata>>i)&1)==1) { // draw pixel - CTX(scanBuffer)[x] = fgcolor; - CTX(scanBuffer)[x+1] = fgcolor; - CTX(scanBuffer)[x+384] = fgcolor; - CTX(scanBuffer)[x+384+1] = fgcolor; + scanBuffer[x] = fgcolor; + scanBuffer[x+1] = fgcolor; + scanBuffer[x+384] = fgcolor; + scanBuffer[x+384+1] = fgcolor; // write to collision buffer - CTX(collBuffer)[x] |= cbit; - CTX(collBuffer)[x+384] |= cbit; + collBuffer[x] |= cbit; + collBuffer[x+384] |= cbit; } else { // draw background - CTX(scanBuffer)[x] = bgcolor; - CTX(scanBuffer)[x+1] = bgcolor; - CTX(scanBuffer)[x+384] = bgcolor; - CTX(scanBuffer)[x+384+1] = bgcolor; + scanBuffer[x] = bgcolor; + scanBuffer[x+1] = bgcolor; + scanBuffer[x+384] = bgcolor; + scanBuffer[x+384+1] = bgcolor; } x+=2; } @@ -222,9 +290,9 @@ void drawBackgroundColorStack(int scanline) unsigned int fgcolor; int gaddress; // card graphic address int gdata; // current card graphic byte - int advcolor; // Flag - Advance CTX(CSP) + int advcolor; // Flag - Advance CSP int cbit = 1<<8; // bit 8 - collision bit for Background - int x = CTX(delayH); // current pixel offset + int x = delayH; // current pixel offset // Tiled background is 20x12, cards are 8x8 row = (scanline / 8); // Which tile row? (Background is 96 lines high) @@ -232,19 +300,19 @@ void drawBackgroundColorStack(int scanline) cardrow = scanline % 8; // which line of this row of cards to draw - if(row==0 && cardrow==0) { CTX(CSP) = 0x28; } // reset CTX(CSP) on display of first card on screen + if(row==0 && cardrow==0) { CSP = 0x28; } // reset CSP on display of first card on screen // Draw cards for (col=0; col<20; col++) // for each card on the current row... { - card = CTX(Memory)[0x200+row+col]; // card info from BACKTAB + card = Memory[0x200+row+col]; // card info from BACKTAB if(((card>>11)&0x03)==2) // Color Squares Mode { if (cardrow == 0) - CTX(bgcard)[col] = colors[CTX(Memory)[CTX(CSP)] & 0x0F]; + bgcard[col] = colors[Memory[CSP] & 0x0F]; // set colors - colors[7] = CTX(bgcard)[col]; // color 7 is top of color stack + colors[7] = bgcard[col]; // color 7 is top of color stack color1 = card & 0x07; color2 = (card>>3) & 0x07; if(cardrow>=4) // switch to lower squares colors @@ -262,18 +330,18 @@ void drawBackgroundColorStack(int scanline) // draw squares for(i=0; i<8; i += 2) { - CTX(scanBuffer)[x] = color1; - CTX(scanBuffer)[x+1] = color1; - CTX(scanBuffer)[x+8] = color2; - CTX(scanBuffer)[x+9] = color2; - CTX(scanBuffer)[x+384] = color1; - CTX(scanBuffer)[x+384+1] = color1; - CTX(scanBuffer)[x+384+8] = color2; - CTX(scanBuffer)[x+384+9] = color2; - CTX(collBuffer)[x] |= cbit1; - CTX(collBuffer)[x+8] |= cbit2; - CTX(collBuffer)[x+384] |= cbit1; - CTX(collBuffer)[x+384+8] |= cbit2; + scanBuffer[x] = color1; + scanBuffer[x+1] = color1; + scanBuffer[x+8] = color2; + scanBuffer[x+9] = color2; + scanBuffer[x+384] = color1; + scanBuffer[x+384+1] = color1; + scanBuffer[x+384+8] = color2; + scanBuffer[x+384+9] = color2; + collBuffer[x] |= cbit1; + collBuffer[x+8] |= cbit2; + collBuffer[x+384] |= cbit1; + collBuffer[x+384+8] |= cbit2; x+=2; } x+=8; @@ -281,43 +349,43 @@ void drawBackgroundColorStack(int scanline) } else // Color Stack Mode { - if(cardrow == 0) // only advance CTX(CSP) once per card, cache card colors for later scanlines + if(cardrow == 0) // only advance CSP once per card, cache card colors for later scanlines { - advcolor = (card>>13) & 0x01; // do we need to advance the CTX(CSP)? - CTX(CSP) = (CTX(CSP)+advcolor) & 0x2B; // cycles through 0x28-0x2B - CTX(fgcard)[col] = colors[(card&0x07)|((card>>9)&0x08)]; // bits 12, 2, 1, 0 - CTX(bgcard)[col] = colors[CTX(Memory)[CTX(CSP)] & 0x0F]; + advcolor = (card>>13) & 0x01; // do we need to advance the CSP? + CSP = (CSP+advcolor) & 0x2B; // cycles through 0x28-0x2B + fgcard[col] = colors[(card&0x07)|((card>>9)&0x08)]; // bits 12, 2, 1, 0 + bgcard[col] = colors[Memory[CSP] & 0x0F]; } - fgcolor = CTX(fgcard)[col]; - bgcolor = CTX(bgcard)[col]; + fgcolor = fgcard[col]; + bgcolor = bgcard[col]; if (((card >> 11) & 0x01) != 0) // Limit GRAM to 64 cards gaddress = 0x3000 + (card & 0x09f8); else gaddress = 0x3000 + (card & 0x0ff8); - gdata = CTX(Memory)[gaddress + cardrow]; // fetch current line of current card graphic + gdata = Memory[gaddress + cardrow]; // fetch current line of current card graphic for(i=7; i>=0; i--) // draw one line of card graphic { if(((gdata>>i)&1)==1) { // draw pixel - CTX(scanBuffer)[x] = fgcolor; - CTX(scanBuffer)[x+1] = fgcolor; - CTX(scanBuffer)[x+384] = fgcolor; - CTX(scanBuffer)[x+384+1] = fgcolor; + scanBuffer[x] = fgcolor; + scanBuffer[x+1] = fgcolor; + scanBuffer[x+384] = fgcolor; + scanBuffer[x+384+1] = fgcolor; // write to collision buffer - CTX(collBuffer)[x] |= cbit; - CTX(collBuffer)[x+384] |= cbit; + collBuffer[x] |= cbit; + collBuffer[x+384] |= cbit; } else { // draw background - CTX(scanBuffer)[x] = bgcolor; - CTX(scanBuffer)[x+1] = bgcolor; - CTX(scanBuffer)[x+384] = bgcolor; - CTX(scanBuffer)[x+384+1] = bgcolor; + scanBuffer[x] = bgcolor; + scanBuffer[x+1] = bgcolor; + scanBuffer[x+384] = bgcolor; + scanBuffer[x+384+1] = bgcolor; } x+=2; } @@ -351,16 +419,16 @@ void drawSprites(int scanline) // MOBs for(i=7; i>=0; i--) // draw sprites 0-7 in reverse order { - Rx = CTX(Memory)[0x00+i]; // 14 bits ; -- -SVI xxxx xxxx ; Size, Visible, Interactive, X Position - Ry = CTX(Memory)[0x08+i]; // 14 bits ; -- YX42 Ryyy yyyy ; Flip Y, Flip X, Size 4, Size 2, Y Resolution, Y Position - Ra = CTX(Memory)[0x10+i]; // 14 bits ; PF Gnnn nnnn nFFF ; Priority, FG Color Bit 3, GRAM, n Card #, FG Color Bits 2-0 + Rx = Memory[0x00+i]; // 14 bits ; -- -SVI xxxx xxxx ; Size, Visible, Interactive, X Position + Ry = Memory[0x08+i]; // 14 bits ; -- YX42 Ryyy yyyy ; Flip Y, Flip X, Size 4, Size 2, Y Resolution, Y Position + Ra = Memory[0x10+i]; // 14 bits ; PF Gnnn nnnn nFFF ; Priority, FG Color Bit 3, GRAM, n Card #, FG Color Bits 2-0 posX = Rx & 0xFF; posY = Ry & 0x7F; // if sprite x coordinate is 0 or >167, it's disabled // if it's not visible and not interactive, it's disabled - if(posX==0 || posX>=167 || ((Rx>>8)&0x03)==0 || posY>=104) { continue; } + if(posX==0 || posX>167 || ((Rx>>8)&0x03)==0 || posY>104) { continue; } cbit = 1<>11) & 0x01) == 1) { card = card & 0x09f8; } + if(STICMode==0 || ((Ra>>11) & 0x01) == 1) { card = card & 0x09f8; } gaddress = 0x3000 + card; fgcolor = colors[((Ra>>9)&0x08)|(Ra&0x07)]; @@ -405,14 +473,14 @@ void drawSprites(int scanline) // MOBs { spriterow = (7+(8*yRes)) - spriterow; gaddress = gaddress + spriterow; - gdata = CTX(Memory)[gaddress] & 0xFF; - gdata2 = CTX(Memory)[gaddress - (sizeY==0)] & 0xFF; + gdata = Memory[gaddress] & 0xFF; + gdata2 = Memory[gaddress - (sizeY==0)] & 0xFF; } else { gaddress = gaddress + spriterow; - gdata = CTX(Memory)[gaddress] & 0xFF; - gdata2 = CTX(Memory)[gaddress + (sizeY==0)] & 0xFF; + gdata = Memory[gaddress] & 0xFF; + gdata2 = Memory[gaddress + (sizeY==0)] & 0xFF; } if(flipX) @@ -422,7 +490,7 @@ void drawSprites(int scanline) // MOBs } // draw sprite row // - x = (CTX(delayH)-16) + (posX * 2); // pixels are 2x2 to accomodate half-height pixels + x = (delayH-16) + (posX * 2); // pixels are 2x2 to accomodate half-height pixels for(j=0; j<2; j++) { @@ -436,11 +504,11 @@ void drawSprites(int scanline) // MOBs // set collision and collision buffer bits // if((Rx>>8)&1) // if sprite is interactive { - CTX(collBuffer)[x] |= cbit; - CTX(collBuffer)[x+2*sizeX] |= cbit; // for double width + collBuffer[x] |= cbit; + collBuffer[x+2*sizeX] |= cbit; // for double width } - if(priority && ((CTX(collBuffer)[x]>>8)&1)) // don't draw if sprite is behind background + if(priority && ((collBuffer[x]>>8)&1)) // don't draw if sprite is behind background { continue; } @@ -448,14 +516,14 @@ void drawSprites(int scanline) // MOBs // draw sprite // if((Rx>>9)&1) // if sprite is visible { - CTX(scanBuffer)[x] = fgcolor; - CTX(scanBuffer)[x+1] = fgcolor; - CTX(scanBuffer)[x+2*sizeX] = fgcolor; // for double width - CTX(scanBuffer)[x+3*sizeX] = fgcolor; + scanBuffer[x] = fgcolor; + scanBuffer[x+1] = fgcolor; + scanBuffer[x+2*sizeX] = fgcolor; // for double width + scanBuffer[x+3*sizeX] = fgcolor; } } gdata = gdata2; // for second half-pixel row // - x = (CTX(delayH)-16) + 384 + (posX * 2); // for second half-pixel row // + x = (delayH-16) + 384 + (posX * 2); // for second half-pixel row // } } } @@ -464,72 +532,100 @@ void drawSprites(int scanline) // MOBs void STICDrawFrame(int enabled) { int row, offset; - int i, j; + int i; offset = 0; if (enabled == 0) { for (row = 0; row < 112; row++) { - int color = colors[CTX(Memory)[0x2C] & 0x0f]; // border color + int color = colors[Memory[0x2C] & 0x0f]; // border color for(i=0; i<352; i++) { - CTX(scanBuffer)[i] = color; - CTX(scanBuffer)[i+384] = color; + scanBuffer[i] = color; + scanBuffer[i+384] = color; } - memcpy(&CTX(frame)[offset], &CTX(scanBuffer)[0], 352 * sizeof(unsigned int)); - memcpy(&CTX(frame)[offset + 352], &CTX(scanBuffer)[384], 352 * sizeof(unsigned int)); + memcpy(&frame[offset], &scanBuffer[0], 352 * sizeof(unsigned int)); + memcpy(&frame[offset + 352], &scanBuffer[384], 352 * sizeof(unsigned int)); offset += 352 * 2; } } else { - CTX(extendTop) = (CTX(Memory)[0x32]>>1)&0x01; + extendTop = (Memory[0x32]>>1)&0x01; - CTX(extendLeft) = (CTX(Memory)[0x32])&0x01; + extendLeft = (Memory[0x32])&0x01; - CTX(delayV) = 8 + ((CTX(Memory)[0x31])&0x7); - CTX(delayH) = 8 + ((CTX(Memory)[0x30])&0x7); + delayV = 8 + ((Memory[0x31])&0x7); + delayH = 8 + ((Memory[0x30])&0x7); - CTX(delayH) = CTX(delayH) * 2; + delayH = delayH * 2; for(row=0; row<112; row++) { - memset(&CTX(collBuffer)[0], 0, sizeof(CTX(collBuffer))); + memset(&collBuffer[0], 0, sizeof(collBuffer)); // draw backtab - if(row>=CTX(delayV) && row<(96+CTX(delayV))) + if(row>=delayV && row<(96+delayV)) { - if(CTX(STICMode)==0) // Foreground/Background Mode + if(STICMode==0) // Foreground/Background Mode { - drawBackgroundFGBG(row-CTX(delayV)); + drawBackgroundFGBG(row-delayV); } else // Color Stack Modes { - drawBackgroundColorStack(row-CTX(delayV)); + drawBackgroundColorStack(row-delayV); } } - if (row>=CTX(delayV) - 1 && row<(97 + CTX(delayV))) { + if (row>=delayV - 1 && row<(97 + delayV)) { // draw MOBs - drawSprites((row-CTX(delayV))+8); + drawSprites((row-delayV)+8); } // draw border and set final collision bits drawBorder(row); - // clear collisions in column 167 // - CTX(collBuffer)[167 * 2] = 0; - CTX(collBuffer)[167 * 2 + 384] = 0; - - for (i = 14; i < 169 * 2; i += 2) { - if (CTX(collBuffer)[i] == 0) + + for (i = 1 * 2; i < 168 * 2; i += 2) { + if (collBuffer[i] == 0) continue; - for (j = 0; j < 8; j++) { - if (((CTX(collBuffer)[i] >> j) & 1) != 0) { - CTX(Memory)[0x18 + j] |= CTX(collBuffer)[i] & ~(1 << j); - } - } + if (collBuffer[i] & 0x01) + Memory[0x18] |= collBuffer[i]; + if (collBuffer[i] & 0x02) + Memory[0x19] |= collBuffer[i]; + if (collBuffer[i] & 0x04) + Memory[0x1a] |= collBuffer[i]; + if (collBuffer[i] & 0x08) + Memory[0x1b] |= collBuffer[i]; + if (collBuffer[i] & 0x10) + Memory[0x1c] |= collBuffer[i]; + if (collBuffer[i] & 0x20) + Memory[0x1d] |= collBuffer[i]; + if (collBuffer[i] & 0x40) + Memory[0x1e] |= collBuffer[i]; + if (collBuffer[i] & 0x80) + Memory[0x1f] |= collBuffer[i]; + } + for (i = 1 * 2 + 384; i < 168 * 2 + 384; i += 2) { + if (collBuffer[i] == 0) + continue; + if (collBuffer[i] & 0x01) + Memory[0x18] |= collBuffer[i]; + if (collBuffer[i] & 0x02) + Memory[0x19] |= collBuffer[i]; + if (collBuffer[i] & 0x04) + Memory[0x1a] |= collBuffer[i]; + if (collBuffer[i] & 0x08) + Memory[0x1b] |= collBuffer[i]; + if (collBuffer[i] & 0x10) + Memory[0x1c] |= collBuffer[i]; + if (collBuffer[i] & 0x20) + Memory[0x1d] |= collBuffer[i]; + if (collBuffer[i] & 0x40) + Memory[0x1e] |= collBuffer[i]; + if (collBuffer[i] & 0x80) + Memory[0x1f] |= collBuffer[i]; } - memcpy(&CTX(frame)[offset], &CTX(scanBuffer)[0], 352 * sizeof(unsigned int)); - memcpy(&CTX(frame)[offset + 352], &CTX(scanBuffer)[384], 352 * sizeof(unsigned int)); + memcpy(&frame[offset], &scanBuffer[0], 352 * sizeof(unsigned int)); + memcpy(&frame[offset + 352], &scanBuffer[384], 352 * sizeof(unsigned int)); offset += 352 * 2; } } diff --git a/src/stic.h b/src/stic.h index 6924cfb..4f39117 100644 --- a/src/stic.h +++ b/src/stic.h @@ -17,6 +17,47 @@ along with FreeIntv. If not, see http://www.gnu.org/licenses/ */ +extern unsigned int STICMode; // 0-foreground/background, 1-color stack/color squares + +extern int stic_phase; +extern int stic_vid_enable; +extern int stic_reg; +extern int stic_gram; +extern int phase_len; +extern int delayV; +extern int delayH; + +extern int DisplayEnabled; // determines if frame should be updated or not + +extern unsigned int frame[352*224]; // frame buffer + +struct STICserialized { + unsigned int STICMode; + + int stic_phase; + int stic_vid_enable; + int stic_reg; + int stic_gram; + int phase_len; + + int DisplayEnabled; + + int delayH; + int delayV; + + int extendTop; + int extendLeft; + + unsigned int CSP; + unsigned int fgcard[20]; + unsigned int bgcard[20]; + + unsigned int frame[352*224]; +}; + +void STICSerialize(struct STICserialized *); +void STICUnserialize(const struct STICserialized *); + void STICDrawFrame(int); void STICReset(void); From 3dda38a1b536b0de4a1cbd760121a8f55cb9e495 Mon Sep 17 00:00:00 2001 From: Blaze Entertainment Date: Mon, 10 Jan 2022 14:44:51 +0000 Subject: [PATCH 09/15] Add custom menu back --- src/libretro.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/libretro.c b/src/libretro.c index e33330a..13753dc 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -272,12 +272,61 @@ void retro_run(void) if(paused) { OSD_drawPaused(); +#ifdef XBOXPADSTYLEHELP + OSD_drawTextCenterBG(21, "HELP - PRESS B"); +#else OSD_drawTextCenterBG(21, "HELP - PRESS A"); +#endif } } if(paused) { +#ifdef XBOXPADSTYLE + // If core is being built for a device which uses XBOX style + // facebuttons instead of retropad. Reverse the mapping help + // help menu // + if(joypad0[4]==1 || joypad1[4]==1) + { + OSD_drawTextBG(3, 4, " "); + OSD_drawTextBG(3, 5, " - HELP - "); + OSD_drawTextBG(3, 6, " "); + OSD_drawTextBG(3, 7, " B - RIGHT ACTION BUTTON "); + OSD_drawTextBG(3, 8, " A - LEFT ACTION BUTTON "); + OSD_drawTextBG(3, 9, " X - TOP ACTION BUTTON "); + OSD_drawTextBG(3, 10, " Y - LAST SELECTED KEYPAD BUTTON "); + OSD_drawTextBG(3, 11, " L/R - SHOW KEYPAD "); + OSD_drawTextBG(3, 12, " LT/RT - KEYPAD CLEAR/ENTER "); + OSD_drawTextBG(3, 13, " "); + OSD_drawTextBG(3, 14, " START - PAUSE GAME "); + OSD_drawTextBG(3, 15, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); + OSD_drawTextBG(3, 16, " "); + OSD_drawTextBG(3, 17, " FREEINTV 1.3 LICENSE GPL V2 "); + OSD_drawTextBG(3, 18, " "); + } +#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) + // These games have special mappings so the mapping details + // are offputting so just tell them to refer to manual or overlay. + // help menu // + if(joypad0[4]==1 || joypad1[4]==1) + { + OSD_drawTextBG(3, 4, " "); + OSD_drawTextBG(3, 5, " - HELP - "); + OSD_drawTextBG(3, 6, " "); + OSD_drawTextBG(3, 7, " "); + OSD_drawTextBG(3, 8, " PLEASE CHECK MANUAL "); + OSD_drawTextBG(3, 9, " OR OVERLAY "); + OSD_drawTextBG(3, 10, " "); + OSD_drawTextBG(3, 11, " L/R - SHOW KEYPAD "); + OSD_drawTextBG(3, 12, " LT/RT - KEYPAD CLEAR/ENTER "); + OSD_drawTextBG(3, 13, " "); + OSD_drawTextBG(3, 14, " START - PAUSE GAME "); + OSD_drawTextBG(3, 15, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); + OSD_drawTextBG(3, 16, " "); + OSD_drawTextBG(3, 17, " FREEINTV 1.3 LICENSE GPL V2 "); + OSD_drawTextBG(3, 18, " "); + } +#else // help menu // if(joypad0[4]==1 || joypad1[4]==1) { @@ -294,9 +343,10 @@ void retro_run(void) OSD_drawTextBG(3, 14, " START - PAUSE GAME "); OSD_drawTextBG(3, 15, " SELECT - SWAP LEFT/RIGHT CONTROLLERS "); OSD_drawTextBG(3, 16, " "); - OSD_drawTextBG(3, 17, " FREEINTV 1.2 LICENSE GPL V3 "); + OSD_drawTextBG(3, 17, " FREEINTV 1.3 LICENSE GPL V2 "); OSD_drawTextBG(3, 18, " "); } +#endif } else { @@ -373,7 +423,7 @@ void retro_run(void) ivoiceBufferPos = 0.0; ivoice_frame(); } - +#ifndef SHARKSHARK // Swap Left/Right Controller if(joypad0[9]==1 || joypad1[9]==1) { @@ -390,7 +440,7 @@ void retro_run(void) OSD_drawRightLeft(); } } - +#endif if (intv_halt) OSD_drawTextBG(3, 5, "INTELLIVISION HALTED"); // send frame to libretro From fa942309fac0340c8094ae335fa88e38dc40e9d7 Mon Sep 17 00:00:00 2001 From: Blaze Entertainment Date: Mon, 10 Jan 2022 15:34:41 +0000 Subject: [PATCH 10/15] Fix issues with recent additions to control handling --- .DS_Store | Bin 6148 -> 0 bytes src/libretro.c | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Mon, 10 Jan 2022 15:47:59 +0000 Subject: [PATCH 11/15] fix up memory crap --- Makefile | 1 + src/cart.c | 2 +- src/controller.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 11dd7ef..2654748 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +CFLAGS += -DXBOXPADSTYLEHELP -DXBOXPADSTYLE STATIC_LINKING=0 AR := ar diff --git a/src/cart.c b/src/cart.c index 7baf07c..e6a8023 100644 --- a/src/cart.c +++ b/src/cart.c @@ -130,7 +130,7 @@ void loadRange(int start, int stop) { while(start<=stop && pos Date: Mon, 10 Jan 2022 16:51:40 +0000 Subject: [PATCH 12/15] Add Hoverforce and Controller Reverse flags --- src/controller.c | 8 +++++++- src/libretro.c | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/controller.c b/src/controller.c index 8255005..4b7c538 100644 --- a/src/controller.c +++ b/src/controller.c @@ -76,7 +76,7 @@ void controllerInit() // and input 1 maps to Left Controller (0x1FF) // pressing select (freeintv_libretro.c) will // swap the left and right controllers -#ifdef SHARKSHARK +#if defined(SHARKSHARK) || defined(REVCONTROLLERS) //These titles require controller swap from boot controllerSwap = 1; #else @@ -141,6 +141,12 @@ int getControllerState(int joypad[], int player) if(joypad[4]!=0) { state |= B_RIGHT; } // 0x3F - Button Right if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left if(joypad[6]!=0) { state |= K_0; } +#elif defined(HOVERFORCE) + if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= K_C; } + if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left + if(joypad[6]!=0) { state |= K_E; } + if(joypad[8]!=0) { state |= K_0; } // Map START to K0 instead of pause #else if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= B_LEFT; } // 0x9F - Button Left diff --git a/src/libretro.c b/src/libretro.c index d6d828b..fe9a7bd 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -265,6 +265,7 @@ void retro_run(void) joypad1[18] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3); joypad1[19] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3); +#ifndef HOVERFORCE // Pause if((joypad0[8]==1 && joypre0[8]==0) || (joypad1[8]==1 && joypre1[8]==0)) { @@ -279,6 +280,7 @@ void retro_run(void) #endif } } +#endif if(paused) { @@ -304,7 +306,7 @@ void retro_run(void) OSD_drawTextBG(3, 17, " FREEINTV 1.3 LICENSE GPL V2 "); OSD_drawTextBG(3, 18, " "); } -#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) +#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) || defined(HOVERFORCE) // These games have special mappings so the mapping details // are offputting so just tell them to refer to manual or overlay. // help menu // @@ -423,7 +425,7 @@ void retro_run(void) ivoiceBufferPos = 0.0; ivoice_frame(); } -#ifndef SHARKSHARK +#ifndef SHARKSHARK || !defined(REVCONTROLLERS) // Swap Left/Right Controller if(joypad0[9]==1 || joypad1[9]==1) { From 7c153215a5deef1ec6c3ed3561f369e166176f29 Mon Sep 17 00:00:00 2001 From: Blaze Entertainment Date: Mon, 10 Jan 2022 17:44:33 +0000 Subject: [PATCH 13/15] Add Mountain Madness and Decathlon mappings --- Makefile | 1 - freeintv_libretro.so | Bin 0 -> 83296 bytes src/controller.c | 12 ++++++++++++ src/libretro.c | 6 +++--- 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100755 freeintv_libretro.so diff --git a/Makefile b/Makefile index 2654748..11dd7ef 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ -CFLAGS += -DXBOXPADSTYLEHELP -DXBOXPADSTYLE STATIC_LINKING=0 AR := ar diff --git a/freeintv_libretro.so b/freeintv_libretro.so new file mode 100755 index 0000000000000000000000000000000000000000..9315dc89199dac71cba82a8c9c5f3ec53769aad1 GIT binary patch literal 83296 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 I|7p7a580d%WB>pF literal 0 HcmV?d00001 diff --git a/src/controller.c b/src/controller.c index 4b7c538..b9baa32 100644 --- a/src/controller.c +++ b/src/controller.c @@ -147,6 +147,18 @@ int getControllerState(int joypad[], int player) if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left if(joypad[6]!=0) { state |= K_E; } if(joypad[8]!=0) { state |= K_0; } // Map START to K0 instead of pause +#elif defined(MOUNTAINSKI) + if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= K_C; } + if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left + if(joypad[6]!=0) { state |= K_E; } + if(joypad[8]!=0) { state |= K_0; } // Map START to K0 instead of pause +#elif defined(DECATHLON) + //Slapshot needs choose player + if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= K_C; } // 0x3F - Button Right + if(joypad[5]!=0) { state |= K_E; } // 0x9F - Button Left + if(joypad[6]!=0) { state |= getQuickKeypadState(player); } #else if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= B_LEFT; } // 0x9F - Button Left diff --git a/src/libretro.c b/src/libretro.c index fe9a7bd..02349f8 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -265,7 +265,7 @@ void retro_run(void) joypad1[18] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3); joypad1[19] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3); -#ifndef HOVERFORCE +#if !defined(HOVERFORCE) && !defined(MOUNTAINSKI) // Pause if((joypad0[8]==1 && joypre0[8]==0) || (joypad1[8]==1 && joypre1[8]==0)) { @@ -306,7 +306,7 @@ void retro_run(void) OSD_drawTextBG(3, 17, " FREEINTV 1.3 LICENSE GPL V2 "); OSD_drawTextBG(3, 18, " "); } -#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) || defined(HOVERFORCE) +#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) || defined(HOVERFORCE) || defined(DECATHLON) // These games have special mappings so the mapping details // are offputting so just tell them to refer to manual or overlay. // help menu // @@ -425,7 +425,7 @@ void retro_run(void) ivoiceBufferPos = 0.0; ivoice_frame(); } -#ifndef SHARKSHARK || !defined(REVCONTROLLERS) +#if !defined(SHARKSHARK) || !defined(REVCONTROLLERS) // Swap Left/Right Controller if(joypad0[9]==1 || joypad1[9]==1) { From c79f0de8010ae3a6d4c53fe8161eff1903a9c877 Mon Sep 17 00:00:00 2001 From: Blaze Entertainment Date: Tue, 11 Jan 2022 13:33:09 +0000 Subject: [PATCH 14/15] Added Vectron and also fixed input to now support 8 way on dpad --- freeintv_libretro.so | Bin 83296 -> 0 bytes src/controller.c | 38 ++++++++++++++++++++++++++++---------- src/libretro.c | 2 +- 3 files changed, 29 insertions(+), 11 deletions(-) delete mode 100755 freeintv_libretro.so diff --git a/freeintv_libretro.so b/freeintv_libretro.so deleted file mode 100755 index 9315dc89199dac71cba82a8c9c5f3ec53769aad1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83296 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 I|7p7a580d%WB>pF diff --git a/src/controller.c b/src/controller.c index b9baa32..1ccc521 100644 --- a/src/controller.c +++ b/src/controller.c @@ -100,16 +100,26 @@ int getControllerState(int joypad[], int player) int norm; // theta, normalized int state = 0; //0xFF; - +#ifdef VECTRON if(joypad[0]!=0) { state |= D_N; } // 0xFB - Up if(joypad[1]!=0) { state |= D_S; } // 0xFE - Down - if(joypad[2]!=0) { state |= D_W; } // 0xF7 - Left - if(joypad[3]!=0) { state |= D_E; } // 0xFD - Right - - if(joypad[0]!=0 && joypad[2]!=0) { state |= D_NW; } // 0xE3 - Up+Left - if(joypad[0]!=0 && joypad[3]!=0) { state |= D_NE; } // 0xE9 - Up+Right - if(joypad[1]!=0 && joypad[2]!=0) { state |= D_SW; } // 0x36 - Down+Left - if(joypad[1]!=0 && joypad[3]!=0) { state |= D_SE; } // 0x3C - Down+Right + if(joypad[2]!=0) { state |= D_SW; } // 0xF7 - Left + if(joypad[3]!=0) { state |= D_SE; } // 0xFD - Right + if(joypad[0]!=0 && joypad[2]!=0) { state = D_WSW; } // 0xE3 - Up+Left + if(joypad[0]!=0 && joypad[3]!=0) { state = D_ESE; } // 0xE9 - Up+Right + if(joypad[1]!=0 && joypad[2]!=0) { state = D_SSW; } // 0x36 - Down+Left + if(joypad[1]!=0 && joypad[3]!=0) { state = D_SSE; } // 0x3C - Down+Right +#else + if(joypad[0]!=0) { state |= D_N; } // 0xFB - Up + if(joypad[1]!=0) { state |= D_S; } // 0xFE - Down + if(joypad[2]!=0) { state |= D_W; } // 0xF7 - Left + if(joypad[3]!=0) { state |= D_E; } // 0xFD - Right + + if(joypad[0]!=0 && joypad[2]!=0) { state = D_NW; } // 0xE3 - Up+Left + if(joypad[0]!=0 && joypad[3]!=0) { state = D_NE; } // 0xE9 - Up+Right + if(joypad[1]!=0 && joypad[2]!=0) { state = D_SW; } // 0x36 - Down+Left + if(joypad[1]!=0 && joypad[3]!=0) { state = D_SE; } // 0x3C - Down+Right +#endif #ifdef NIGHTSTALKER //Nightstalker requires keypad to play so map keypad to face buttons @@ -154,11 +164,20 @@ int getControllerState(int joypad[], int player) if(joypad[6]!=0) { state |= K_E; } if(joypad[8]!=0) { state |= K_0; } // Map START to K0 instead of pause #elif defined(DECATHLON) - //Slapshot needs choose player if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= K_C; } // 0x3F - Button Right if(joypad[5]!=0) { state |= K_E; } // 0x9F - Button Left if(joypad[6]!=0) { state |= getQuickKeypadState(player); } +#elif defined(TOWERDOOM) + if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= K_C; } // 0x3F - Button Right + if(joypad[5]!=0) { state |= K_E; } // 0x9F - Button Left + if(joypad[6]!=0) { state |= getQuickKeypadState(player); } +#elif defined(VECTRON) + if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top + if(joypad[4]!=0) { state |= B_RIGHT; } // 0x9F - Button Left + if(joypad[5]!=0) { state |= B_LEFT; } // 0x3F - Button Right + if(joypad[6]!=0) { state |= getQuickKeypadState(player); } #else if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= B_LEFT; } // 0x9F - Button Left @@ -204,7 +223,6 @@ int getControllerState(int joypad[], int player) // L/R triggers for Keypad Enter/Clear if(joypad[12]!=0) { state |= K_C; } // 0x88 - Keypad Clear if(joypad[13]!=0) { state |= K_E; } // 0x28 - Keypad Enter - return state; } diff --git a/src/libretro.c b/src/libretro.c index 02349f8..0444902 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -306,7 +306,7 @@ void retro_run(void) OSD_drawTextBG(3, 17, " FREEINTV 1.3 LICENSE GPL V2 "); OSD_drawTextBG(3, 18, " "); } -#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) || defined(HOVERFORCE) || defined(DECATHLON) +#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) || defined(HOVERFORCE) || defined(DECATHLON) || defined (TOWERDOOM) || defined (VECTRON) // These games have special mappings so the mapping details // are offputting so just tell them to refer to manual or overlay. // help menu // From 62fd14cea48c6c2023cb78390bb86abce523dce0 Mon Sep 17 00:00:00 2001 From: Blaze Entertainment Date: Tue, 11 Jan 2022 15:23:16 +0000 Subject: [PATCH 15/15] Added Cloudy Mountain Mapping (A D&D) --- src/controller.c | 36 +++++++++++++++++++++++++++--------- src/libretro.c | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/controller.c b/src/controller.c index 1ccc521..1e74bfc 100644 --- a/src/controller.c +++ b/src/controller.c @@ -91,13 +91,13 @@ void setControllerInput(int player, int state) int getControllerState(int joypad[], int player) { - // converts joypad input for use by system + // converts joypad input for use by system int Lx = 0; // left analog X int Ly = 0; // left analog Y int Rx = 0; // right analog X int Ry = 0; // right analog Y double theta; // analog joy angle - int norm; // theta, normalized + int norm; // theta, normalized int state = 0; //0xFF; #ifdef VECTRON @@ -121,20 +121,20 @@ int getControllerState(int joypad[], int player) if(joypad[1]!=0 && joypad[3]!=0) { state = D_SE; } // 0x3C - Down+Right #endif -#ifdef NIGHTSTALKER +#if defined(NIGHTSTALKER) //Nightstalker requires keypad to play so map keypad to face buttons if(joypad[7]!=0) { state |= K_4; } if(joypad[4]!=0) { state |= K_6; } if(joypad[5]!=0) { state |= K_8; } if(joypad[6]!=0) { state |= K_2; } -#elif SHARKSHARK +#elif defined(SHARKSHARK) //Add Dart moves to shark shark if(joypad[7]!=0) { state |= K_1; } // 0x5F - Button Top if(joypad[4]!=0) { state |= K_3; } // 0x9F - Button Left if(joypad[5]!=0) { state |= B_RIGHT; } // 0x3F - Button Right if(joypad[6]!=0) { state |= K_6; } #elif defined(ASTROSMASH) - //Astrosmash requires keypad to play so map Button Top to K3 (Hyper space) + //Astrosmash requires keypad to play so map Button Top to K3 (Hyper space) if(joypad[7]!=0) { state |= K_3; } // 0x5F - Button Top if(joypad[4]!=0) { state |= K_2; } // 0x3F - Button Right if(joypad[5]!=0) { state |= B_LEFT; } // 0x9F - Button Left @@ -178,6 +178,18 @@ int getControllerState(int joypad[], int player) if(joypad[4]!=0) { state |= B_RIGHT; } // 0x9F - Button Left if(joypad[5]!=0) { state |= B_LEFT; } // 0x3F - Button Right if(joypad[6]!=0) { state |= getQuickKeypadState(player); } +#elif defined(CLOUDY) + if(joypad[7]!=0) { state |= K_4; } + if(joypad[4]!=0) { state |= K_6; } + if(joypad[5]!=0) { state |= K_8; } + if(joypad[6]!=0) { state |= K_2; } + if(joypad[6]!=0 && joypad[7]!=0) { state = K_1; } // Up+Left facebuttons + if(joypad[6]!=0 && joypad[4]!=0) { state = K_3; } // Up+Right facebuttons + if(joypad[5]!=0 && joypad[7]!=0) { state = K_7; } // Down+Left facebuttons + if(joypad[5]!=0 && joypad[4]!=0) { state = K_9; } // Down+Right facebuttons + if(joypad[8]!=0) { state |= K_E; } + if(joypad[9]!=0) { state |= K_C; } + if(joypad[11]!=0) { state |= B_TOP; } #else if(joypad[7]!=0) { state |= B_TOP; } // 0x5F - Button Top if(joypad[4]!=0) { state |= B_LEFT; } // 0x9F - Button Left @@ -191,12 +203,12 @@ int getControllerState(int joypad[], int player) Ly = joypad[15] / 8192; if(Lx != 0 || Ly != 0) { - // find angle + // find angle theta = atan2((double)Ly, (double)Lx) + PI; // normalize if(theta<0.0) { theta = 0.0; } norm = floor((theta/(2*PI))*15.0); - norm -= 3; + norm -= 3; if(norm<0) { norm += 16; } state |= discDirections[norm & 0x0F]; } @@ -206,12 +218,12 @@ int getControllerState(int joypad[], int player) Ry = joypad[17] / 8192; if(Rx != 0 || Ry != 0) { - // find angle + // find angle theta = atan2((double)Ry, (double)Rx) + PI; // normalize if(theta<0.0) { theta = 0.0; } norm = floor((theta/(2*PI))*7.0); - norm -= 1; + norm -= 1; if(norm<0) { norm += 8; } state |= keypadDirections[norm & 0x07]; } @@ -220,10 +232,16 @@ int getControllerState(int joypad[], int player) if(joypad[18]!=0) { state |= K_0; } // 0x48 - Keypad 0 if(joypad[19]!=0) { state |= K_5; } // 0x42 - Keypad 5 +#ifdef CLOUDY + // L trigger used for in game gameplay (QOL - check arrows) + if(joypad[13]!=0) { state |= K_0; } + return state; +#else // L/R triggers for Keypad Enter/Clear if(joypad[12]!=0) { state |= K_C; } // 0x88 - Keypad Clear if(joypad[13]!=0) { state |= K_E; } // 0x28 - Keypad Enter return state; +#endif } // Mini keypads, displayed in the corner using a shoulder button, diff --git a/src/libretro.c b/src/libretro.c index 0444902..2ba0f81 100644 --- a/src/libretro.c +++ b/src/libretro.c @@ -265,7 +265,7 @@ void retro_run(void) joypad1[18] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3); joypad1[19] = InputState(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3); -#if !defined(HOVERFORCE) && !defined(MOUNTAINSKI) +#if !defined(HOVERFORCE) && !defined(MOUNTAINSKI) && !defined(CLOUDY) // Pause if((joypad0[8]==1 && joypre0[8]==0) || (joypad1[8]==1 && joypre1[8]==0)) { @@ -306,7 +306,8 @@ void retro_run(void) OSD_drawTextBG(3, 17, " FREEINTV 1.3 LICENSE GPL V2 "); OSD_drawTextBG(3, 18, " "); } -#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) || defined(HOVERFORCE) || defined(DECATHLON) || defined (TOWERDOOM) || defined (VECTRON) +#elif defined(NIGHTSTALKER) || defined(ASTROSMASH) || defined(PINBALL) || defined(SHARKSHARK) || defined(SLAPSHOT) || \ + defined(HOVERFORCE) || defined(DECATHLON) || defined (TOWERDOOM) || defined (VECTRON) || defined(CLOUDY) // These games have special mappings so the mapping details // are offputting so just tell them to refer to manual or overlay. // help menu // @@ -352,6 +353,18 @@ void retro_run(void) } else { +#ifdef CLOUDY + if(joypad0[10]) // left shoulder down (Right shoulder required for gameplay in A D&D Cloudy Mountain) + { + showKeypad0 = true; + setControllerInput(0, getKeypadState(0, joypad0, joypre0)); + } + else + { + showKeypad0 = false; + setControllerInput(0, getControllerState(joypad0, 0)); + } +#else if(joypad0[10] | joypad0[11]) // left/right shoulder down { showKeypad0 = true; @@ -362,8 +375,20 @@ void retro_run(void) showKeypad0 = false; setControllerInput(0, getControllerState(joypad0, 0)); } - - if(joypad1[10] | joypad1[11]) // left shoulder down +#endif +#ifdef CLOUDY + if(joypad1[10]) // left shoulder down (Right shoulder required for gameplay in A D&D Cloudy Mountain) + { + showKeypad1 = true; + setControllerInput(1, getKeypadState(1, joypad1, joypre1)); + } + else + { + showKeypad1 = false; + setControllerInput(1, getControllerState(joypad1, 1)); + } +#else + if(joypad1[10] | joypad1[11]) // left/right shoulder down { showKeypad1 = true; setControllerInput(1, getKeypadState(1, joypad1, joypre1)); @@ -373,7 +398,7 @@ void retro_run(void) showKeypad1 = false; setControllerInput(1, getControllerState(joypad1, 1)); } - +#endif if(keyboardDown || keyboardChange) { setControllerInput(0, keyboardState); @@ -425,7 +450,7 @@ void retro_run(void) ivoiceBufferPos = 0.0; ivoice_frame(); } -#if !defined(SHARKSHARK) || !defined(REVCONTROLLERS) +#ifndef CLOUDY // Swap Left/Right Controller if(joypad0[9]==1 || joypad1[9]==1) {