X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fapple2.cpp;h=1eeb7e88c3ed95378516e43052e57d1f2f129b11;hb=c0001155bc0909da61f6c849c0be9b16e9b7f4b6;hp=f50454d35678757cf8495c3b84bb2bd60ccb21d2;hpb=09edc12bc45312aac0be72e258c931b1fb37fb20;p=apple2 diff --git a/src/apple2.cpp b/src/apple2.cpp index f50454d..1eeb7e8 100755 --- a/src/apple2.cpp +++ b/src/apple2.cpp @@ -14,7 +14,7 @@ // WHO WHEN WHAT // --- ---------- ------------------------------------------------------------ // JLH 11/12/2005 Initial port to SDL -// JLH 11/18/2005 Wired up graphic soft switches +// JLH 11/18/2005 Wired up graphic soft switches // JLH 12/02/2005 Setup timer subsystem for more accurate time keeping // JLH 12/12/2005 Added preliminary state saving support // @@ -31,7 +31,7 @@ #include "apple2.h" -#include +#include #include #include #include @@ -54,38 +54,59 @@ #include "gui/draggablewindow2.h" #include "gui/textedit.h" -//using namespace std; +// Debug and misc. defines + +#define THREADED_65C02 +#define CPU_THREAD_OVERFLOW_COMPENSATION +#define DEBUG_LC +//#define CPU_CLOCK_CHECKING +//#define THREAD_DEBUGGING +#define SOFT_SWITCH_DEBUGGING // Global variables -uint8 ram[0x10000], rom[0x10000]; // RAM & ROM spaces -uint8 diskRom[0x100]; // Disk ROM space -V65C02REGS mainCPU; -uint8 appleType = APPLE_TYPE_II; +uint8_t ram[0x10000], rom[0x10000]; // RAM & ROM spaces +uint8_t ram2[0x10000]; // Auxillary RAM +uint8_t diskRom[0x100]; // Disk ROM space +V65C02REGS mainCPU; // v65C02 execution context +uint8_t appleType = APPLE_TYPE_IIE; +FloppyDrive floppyDrive; // Local variables -static uint8 lastKeyPressed = 0; +static uint8_t lastKeyPressed = 0; static bool keyDown = false; - -static FloppyDrive floppyDrive; +static bool openAppleDown = false; +static bool closedAppleDown = false; +static bool store80Mode = false; +static bool vbl = false; +static bool slotCXROM = false; +static bool slotC3ROM = false; +static bool ramrd = false; +static bool ramwrt = false; +static bool altzp = false; +static bool ioudis = true; +bool dhires = false; + +//static FloppyDrive floppyDrive; enum { LC_BANK_1, LC_BANK_2 }; -static uint8 visibleBank = LC_BANK_1; +static uint8_t visibleBank = LC_BANK_1; static bool readRAM = false; static bool writeRAM = false; static bool running = true; // Machine running state flag... -static uint32 startTicks; +static uint32_t startTicks; +static bool pauseMode = false; static GUI * gui = NULL; // Local functions (technically, they're global...) -bool LoadImg(char * filename, uint8 * ram, int size); -uint8 RdMem(uint16 addr); -void WrMem(uint16 addr, uint8 b); +bool LoadImg(char * filename, uint8_t * ram, int size); +uint8_t RdMem(uint16_t addr); +void WrMem(uint16_t addr, uint8_t b); static void SaveApple2State(const char * filename); static bool LoadApple2State(const char * filename); @@ -94,6 +115,132 @@ static bool LoadApple2State(const char * filename); static void FrameCallback(void); static void BlinkTimer(void); +#ifdef THREADED_65C02 +// Test of threaded execution of 6502 +static SDL_Thread * cpuThread = NULL; +//static SDL_mutex * cpuMutex = NULL; +static SDL_cond * cpuCond = NULL; +static SDL_sem * mainSem = NULL; +static bool cpuFinished = false; +static bool cpuSleep = false; + + +// NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz + +// Let's try a thread... +/* +Here's how it works: Execute 1 frame's worth, then sleep. +Other stuff wakes it up +*/ +int CPUThreadFunc(void * data) +{ + // Mutex must be locked for conditional to work... + // Also, must be created in the thread that uses it... + SDL_mutex * cpuMutex = SDL_CreateMutex(); + +// decrement mainSem... +//SDL_SemWait(mainSem); +#ifdef CPU_THREAD_OVERFLOW_COMPENSATION + float overflow = 0.0; +#endif + + do + { + if (cpuSleep) + SDL_CondWait(cpuCond, cpuMutex); + +// decrement mainSem... +#ifdef THREAD_DEBUGGING +WriteLog("CPU: SDL_SemWait(mainSem);\n"); +#endif +SDL_SemWait(mainSem); + +// There are exactly 800 slices of 21.333 cycles per frame, so it works out +// evenly. +#if 0 + uint32_t cycles = 17066; +#ifdef CPU_THREAD_OVERFLOW_COMPENSATION +// ODD! It's closer *without* this overflow compensation. ??? WHY ??? + overflow += 0.666666667; + + if (overflow > 1.0) + { + overflow -= 1.0; + cycles++; + } +#endif + +#ifdef THREAD_DEBUGGING +WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n"); +#endif + Execute65C02(&mainCPU, cycles); // how much? 1 frame (after 1 s, off by 40 cycles) not any more--it's off by as much as 240 now! + + // Adjust the sound routine's last cycle toggled time base + // Also, since we're finished executing, .clock is now valid +#ifdef THREAD_DEBUGGING +WriteLog("CPU: AdjustLastToggleCycles(mainCPU.clock);\n"); +#endif + AdjustLastToggleCycles(mainCPU.clock); +#else +#ifdef THREAD_DEBUGGING +WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n"); +#endif + for(int i=0; i<800; i++) + { + uint32_t cycles = 21; + overflow += 0.333333334; + + if (overflow > 1.0) + { + cycles++; + overflow -= 1.0; + } + + Execute65C02(&mainCPU, cycles); + WriteSampleToBuffer(); + } +#endif + +//WriteLog("CPUThread: Supposedly end of frame...\n"); + +#ifdef THREAD_DEBUGGING +WriteLog("CPU: SDL_mutexP(cpuMutex);\n"); +#endif + SDL_mutexP(cpuMutex); +#if 0 + if (SDL_CondWait(cpuCond, cpuMutex) != 0) + { + printf("SDL_CondWait != 0! (Error: '%s')\n", SDL_GetError()); + exit(-1); + } +#else +// increment mainSem... +#ifdef THREAD_DEBUGGING +WriteLog("CPU: SDL_SemPost(mainSem);\n"); +#endif +SDL_SemPost(mainSem); +// SDL_CondSignal(mainCond); // In case something is waiting on us... + +#ifdef THREAD_DEBUGGING +WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n"); +#endif + SDL_CondWait(cpuCond, cpuMutex); + +#endif +#ifdef THREAD_DEBUGGING +WriteLog("CPU: SDL_mutexV(cpuMutex);\n"); +#endif + SDL_mutexV(cpuMutex); + } + while (!cpuFinished); + + SDL_DestroyMutex(cpuMutex); + + return 0; +} +#endif + + // Test GUI function Element * TestWindow(void) @@ -104,6 +251,7 @@ Element * TestWindow(void) return win; } + Element * QuitEmulator(void) { gui->Stop(); @@ -112,6 +260,7 @@ Element * QuitEmulator(void) return NULL; } + /* Small Apple II memory map: @@ -135,9 +284,9 @@ $C0EE - Disk set read mode // V65C02 read/write memory functions // -uint8 RdMem(uint16 addr) +uint8_t RdMem(uint16_t addr) { - uint8 b; + uint8_t b; #if 0 if (addr >= 0xC000 && addr <= 0xC0FF) @@ -148,20 +297,129 @@ if (addr >= 0xC080 && addr <= 0xC08F) WriteLog("\n*** Read at I/O address %04X\n", addr); #endif - if ((addr & 0xFFF0) == 0xC000) + if ((addr & 0xFFF0) == 0xC000) // Read $C000-$C00F { return lastKeyPressed | (keyDown ? 0x80 : 0x00); } - else if ((addr & 0xFFF0) == 0xC010) +// else if ((addr & 0xFFF8) == 0xC010) // Read $C010-$C01F + else if (addr == 0xC010) { //This is bogus: keyDown is set to false, so return val NEVER is set... //Fixed... //Also, this is IIe/IIc only...! - uint8 retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00); + uint8_t retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00); keyDown = false; return retVal; } - else if ((addr & 0xFFF0) == 0xC030) + // These are //e locations + else if (addr == 0xC011) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RDBANK2 (read)\n"); +#endif + return (visibleBank == LC_BANK_2 ? 0x80 : 0x00); + } + else if (addr == 0xC012) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RDLCRAM (read)\n"); +#endif + return (readRAM ? 0x80 : 0x00); + } + else if (addr == 0xC013) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RAMRD (read)\n"); +#endif + return (ramrd ? 0x80 : 0x00); + } + else if (addr == 0xC014) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RAMWRT (read)\n"); +#endif + return (ramwrt ? 0x80 : 0x00); + } + else if (addr == 0xC015) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("SLOTCXROM (read)\n"); +#endif + return (slotCXROM ? 0x80 : 0x00); + } + else if (addr == 0xC016) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("ALTZP (read)\n"); +#endif + return (altzp ? 0x80 : 0x00); + } + else if (addr == 0xC017) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("SLOTC3ROM (read)\n"); +#endif + return (slotC3ROM ? 0x80 : 0x00); + } + else if (addr == 0xC018) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("80STORE (read)\n"); +#endif + return (store80Mode ? 0x80 : 0x00); + } + else if (addr == 0xC019) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("VBL (read)\n"); +#endif +// NB: The doco suggests that this signal goes LOW when in the VBI. +// Which means that we need to control this by counting lines somewhere. + return (vbl ? 0x80 : 0x00); + } + else if (addr == 0xC01A) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("TEXT (read)\n"); +#endif + return (textMode ? 0x80 : 0x00); + } + else if (addr == 0xC01B) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("MIXED (read)\n"); +#endif + return (mixedMode ? 0x80 : 0x00); + } + else if (addr == 0xC01C) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("PAGE2 (read)\n"); +#endif + return (displayPage2 ? 0x80 : 0x00); + } + else if (addr == 0xC01D) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("HIRES (read)\n"); +#endif + return (hiRes ? 0x80 : 0x00); + } + else if (addr == 0xC01E) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("ALTCHARSET (read)\n"); +#endif + return (alternateCharset ? 0x80 : 0x00); + } + else if (addr == 0xC01F) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("80COL (read)\n"); +#endif + return (col80Mode ? 0x80 : 0x00); + } + else if ((addr & 0xFFF0) == 0xC030) // Read $C030-$C03F { /* This is problematic, mainly because the v65C02 removes actual cycles run after each call. @@ -184,38 +442,122 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer //should it return something else here??? return 0x00; } - else if (addr == 0xC050) + else if (addr == 0xC050) // Read $C050 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("TEXT off (read)\n"); +#endif textMode = false; } - else if (addr == 0xC051) + else if (addr == 0xC051) // Read $C051 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("TEXT on (read)\n"); +#endif textMode = true; } - else if (addr == 0xC052) + else if (addr == 0xC052) // Read $C052 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("MIXED off (read)\n"); +#endif mixedMode = false; } - else if (addr == 0xC053) + else if (addr == 0xC053) // Read $C053 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("MIXED on (read)\n"); +#endif mixedMode = true; } - else if (addr == 0xC054) + else if (addr == 0xC054) // Read $C054 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("PAGE2 off (read)\n"); +#endif displayPage2 = false; } - else if (addr == 0xC055) + else if (addr == 0xC055) // Read $C055 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("PAGE2 on (read)\n"); +#endif displayPage2 = true; } - else if (addr == 0xC056) + else if (addr == 0xC056) // Read $C056 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("HIRES off (read)\n"); +#endif hiRes = false; } - else if (addr == 0xC057) + else if (addr == 0xC057) // Read $C057 { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("HIRES on (read)\n"); +#endif hiRes = true; } + else if (addr == 0xC05E) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("DHIRES on (read)\n"); +#endif + if (ioudis) + dhires = true; + } + else if (addr == 0xC05F) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("DHIRES off (read)\n"); +#endif + if (ioudis) + dhires = false; + } + else if (addr == 0xC061) // Read $C061 + { + // Open Apple key (or push button 0) + return (openAppleDown ? 0x80 : 0x00); + } + else if (addr == 0xC062) // Read $C062 + { + // Open Apple key (or push button 0) + return (closedAppleDown ? 0x80 : 0x00); + } + // The way the paddles work is that a strobe is written (or read) to $C070, + // then software counts down the time that it takes for the paddle outputs + // to have bit 7 return to 0. If there are no paddles connected, bit 7 + // stays at 1. + else if (addr == 0xC064) // Paddles 0-3 + { + return 0xFF; + } + else if (addr == 0xC065) + { + return 0xFF; + } + else if (addr == 0xC066) + { + return 0xFF; + } + else if (addr == 0xC067) + { + return 0xFF; + } + else if (addr == 0xC07E) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("IOUDIS (read)\n"); +#endif + return (ioudis ? 0x80 : 0x00); + } + else if (addr == 0xC07F) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("DHIRES (read)\n"); +#endif + return (dhires ? 0x80 : 0x00); + } //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank. @@ -223,8 +565,44 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it //visible, two makes it R/W. +/* +301 LDA $E000 +304 PHA +305 LDA $C081 +308 PLA +309 PHA +30A CMP $E000 +30D BNE $332 +30F LDA $C083 +312 LDA $C083 +315 LDA #$A5 +317 STA $D000 +31A CMP $D000 +31D BNE $332 +31F LSR A +320 STA $D000 +323 CMP $D000 +326 BNE $332 +328 LDA $C081 +32B LDA $C081 +32E LDA #$01 +330 BNE $334 +332 LDA #$00 +334 STA $300 +337 PLA +338 CMP $E000 +33B BEQ $340 +33D LDA $C080 +340 RTS + +A = PEEK($C082) +*/ + else if ((addr & 0xFFFB) == 0xC080) { +#ifdef DEBUG_LC +WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n"); +#endif //$C080 49280 OECG R Read RAM bank 2; no write visibleBank = LC_BANK_2; readRAM = true; @@ -232,6 +610,9 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer } else if ((addr & 0xFFFB) == 0xC081) { +#ifdef DEBUG_LC +WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n"); +#endif //$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2 visibleBank = LC_BANK_2; readRAM = false; @@ -239,6 +620,9 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer } else if ((addr & 0xFFFB) == 0xC082) { +#ifdef DEBUG_LC +WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n"); +#endif //$C082 49282 OECG R Read ROM; no write visibleBank = LC_BANK_2; readRAM = false; @@ -246,6 +630,9 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer } else if ((addr & 0xFFFB) == 0xC083) { +#ifdef DEBUG_LC +WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n"); +#endif //$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2 visibleBank = LC_BANK_2; readRAM = true; @@ -253,13 +640,21 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer } else if ((addr & 0xFFFB) == 0xC088) { +#ifdef DEBUG_LC +WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr); +#endif //$C088 49288 OECG R Read RAM bank 1; no write visibleBank = LC_BANK_1; readRAM = true; writeRAM = false; +//Hm. Some stuff seems to want this. +//nope, was looking at $C0E8... return 0xFF; } else if ((addr & 0xFFFB) == 0xC089) { +#ifdef DEBUG_LC +WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n"); +#endif //$C089 49289 OECG RR Read ROM; write RAM bank 1 visibleBank = LC_BANK_1; readRAM = false; @@ -267,6 +662,9 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer } else if ((addr & 0xFFFB) == 0xC08A) { +#ifdef DEBUG_LC +WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n"); +#endif //$C08A 49290 OECG R Read ROM; no write visibleBank = LC_BANK_1; readRAM = false; @@ -274,6 +672,9 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer } else if ((addr & 0xFFFB) == 0xC08B) { +#ifdef DEBUG_LC +WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n"); +#endif //$C08B 49291 OECG RR Read/write RAM bank 1 visibleBank = LC_BANK_1; readRAM = true; @@ -308,23 +709,113 @@ deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer floppyDrive.SetWriteMode(); } +//#define LC_DEBUGGING +#ifdef LC_DEBUGGING +bool showpath = false; +if (addr >= 0xD000 && addr <= 0xD00F) + showpath = true; +#endif //This sux... - if (addr >= 0xC100 && addr <= 0xCFFF) // The $C000-$CFFF block is *never* RAM + if (addr >= 0xC100 && addr <= 0xC7FF) // The $C000-$CFFF block is *never* RAM + { +// Looks like the ][e ref manual got this one wrong: slotCXROM set should mean +// use internal ROM, NOT slot ROM. :-/ +// (fixed now, by setting the switch correctly in the write mem section :-P) + if (!slotCXROM) +// if (slotCXROM) + b = rom[addr]; + else + { + if (addr >= 0xC100 && addr <= 0xC1FF) + b = parallelROM[addr & 0xFF]; + else if (addr >= 0xC600 && addr <= 0xC6FF) + b = diskROM[addr & 0xFF]; + else if (addr >= 0xC300 && addr <= 0xC3FF && !slotC3ROM) + b = rom[addr]; + else + b = 0xFF; +// b = rom[addr]; + } +#ifdef LC_DEBUGGING +if (showpath) + WriteLog("b is from $C100-$CFFF block...\n"); +#endif + } + else if (addr >= 0xC800 && addr <= 0xCFFF) // 2K peripheral or OS ROM + { b = rom[addr]; + } else if (addr >= 0xD000) { if (readRAM) { if (addr <= 0xDFFF && visibleBank == LC_BANK_1) - b = ram[addr - 0x1000]; +#ifdef LC_DEBUGGING + { +#endif +// b = ram[addr - 0x1000]; + b = (altzp ? ram2[addr - 0x1000] : ram[addr - 0x1000]); +#ifdef LC_DEBUGGING +if (showpath) + WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n"); + } +#endif else - b = ram[addr]; +#ifdef LC_DEBUGGING + { +#endif +// b = ram[addr]; + b = (altzp ? ram2[addr] : ram[addr]); +#ifdef LC_DEBUGGING +if (showpath) + WriteLog("b is from LC bank #2 (ram[addr])...\n"); + } +#endif } else +#ifdef LC_DEBUGGING + { +#endif b = rom[addr]; +#ifdef LC_DEBUGGING +if (showpath) + WriteLog("b is from LC ROM (rom[addr])...\n"); + } +#endif } else - b = ram[addr]; + { +// 80STORE only works for WRITING, not READING! +#if 0 + // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)... + if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode) + { + if (displayPage2) + b = ram2[addr]; + else + b = ram[addr]; + + return b; + } +#endif + + // Finally, check for auxillary/altzp write switches + if (addr < 0x0200) + b = (altzp ? ram2[addr] : ram[addr]); + else + b = (ramrd ? ram2[addr] : ram[addr]); +#ifdef LC_DEBUGGING +if (showpath) + WriteLog("b is from ram[addr]...\n"); +#endif + } + +#ifdef LC_DEBUGGING +if (addr >= 0xD000 && addr <= 0xD00F) +{ + WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]); +} +#endif return b; } @@ -348,7 +839,7 @@ APPENDIX F Assembly Language Program Listings 13 DDRB2 EQU $C482 ;DATA DIRECTION REGISTER (B) 14 DDRA2 EQU $C483 ;DATA DIRECTION REGISTER (A) */ -void WrMem(uint16 addr, uint8 b) +void WrMem(uint16_t addr, uint8_t b) { //temp... //extern V6809REGS regs; @@ -403,6 +894,8 @@ if (addr == 0x7F47) WriteLog("\n*** Byte %02X written at address %04X\n", b, addr); #endif /* +I think this is IIc/IIe only... + CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write) SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only) @@ -427,50 +920,298 @@ SET80VID = $C00D ;enable 80-column display mode (WR-only) CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only) SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only) */ - if (addr == 0xC00E) + if (addr == 0xC000) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("80STORE off (write)\n"); +#endif + store80Mode = false; + } + else if (addr == 0xC001) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("80STORE on (write)\n"); +#endif + store80Mode = true; + } + else if (addr == 0xC002) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RAMRD off (write)\n"); +#endif + ramrd = false; + } + else if (addr == 0xC003) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RAMRD on (write)\n"); +#endif + ramrd = true; + } + else if (addr == 0xC004) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RAMWRT off (write)\n"); +#endif + ramwrt = false; + } + else if (addr == 0xC005) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("RAMWRT on (write)\n"); +#endif + ramwrt = true; + } + else if (addr == 0xC006) + { + // This is the only soft switch that breaks the usual convention. +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("SLOTCXROM on (write)\n"); +#endif + slotCXROM = true; + } + else if (addr == 0xC007) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("SLOTCXROM off (write)\n"); +#endif + slotCXROM = false; + } + else if (addr == 0xC008) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("ALTZP off (write)\n"); +#endif + altzp = false; + } + else if (addr == 0xC009) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("ALTZP on (write)\n"); +#endif + altzp = true; + } + else if (addr == 0xC00A) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("SLOTC3ROM off (write)\n"); +#endif + slotC3ROM = false; + } + else if (addr == 0xC00B) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("SLOTC3ROM on (write)\n"); +#endif + slotC3ROM = true; + } + else if (addr == 0xC00C) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("80COL off (write)\n"); +#endif + col80Mode = false; + } + else if (addr == 0xC00D) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("80COL on (write)\n"); +#endif + col80Mode = true; + } + else if (addr == 0xC00E) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("ALTCHARSET off (write)\n"); +#endif alternateCharset = false; } else if (addr == 0xC00F) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("ALTCHARSET on (write)\n"); +#endif alternateCharset = true; } else if ((addr & 0xFFF0) == 0xC010) // Keyboard strobe { +//Actually, according to the A2 ref, this should do nothing since a write +//is immediately preceded by a read leaving it in the same state it was... +//But leaving this out seems to fuck up the key handling of some games... keyDown = false; } else if (addr == 0xC050) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("TEXT off (write)\n"); +#endif textMode = false; } else if (addr == 0xC051) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("TEXT on (write)\n"); +#endif textMode = true; } else if (addr == 0xC052) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("MIXED off (write)\n"); +#endif mixedMode = false; } else if (addr == 0xC053) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("MIXED on (write)\n"); +#endif mixedMode = true; } else if (addr == 0xC054) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("PAGE2 off (write)\n"); +#endif displayPage2 = false; } else if (addr == 0xC055) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("PAGE2 on (write)\n"); +#endif displayPage2 = true; } else if (addr == 0xC056) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("HIRES off (write)\n"); +#endif hiRes = false; } else if (addr == 0xC057) { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("HIRES on (write)\n"); +#endif hiRes = true; } + else if (addr == 0xC05E) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("DHIRES on (write)\n"); +#endif + if (ioudis) + dhires = true; + +//static int goDumpDis = 0; +//goDumpDis++; +//if (goDumpDis == 2) +// dumpDis = true; + } + else if (addr == 0xC05F) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("DHIRES off (write)\n"); +#endif + if (ioudis) + dhires = false; + } + else if (addr == 0xC07E) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("IOUDIS on (write)\n"); +#endif + ioudis = true; + } + else if (addr == 0xC07F) + { +#ifdef SOFT_SWITCH_DEBUGGING +WriteLog("IOUDIS off (write)\n"); +#endif + ioudis = false; + } + else if ((addr & 0xFFFB) == 0xC080) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n"); +#endif +//$C080 49280 OECG R Read RAM bank 2; no write + visibleBank = LC_BANK_2; + readRAM = true; + writeRAM = false; + } + else if ((addr & 0xFFFB) == 0xC081) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n"); +#endif +//$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2 + visibleBank = LC_BANK_2; + readRAM = false; + writeRAM = true; + } + else if ((addr & 0xFFFB) == 0xC082) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n"); +#endif +//$C082 49282 OECG R Read ROM; no write + visibleBank = LC_BANK_2; + readRAM = false; + writeRAM = false; + } + else if ((addr & 0xFFFB) == 0xC083) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n"); +#endif +//$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2 + visibleBank = LC_BANK_2; + readRAM = true; + writeRAM = true; + } + else if ((addr & 0xFFFB) == 0xC088) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n"); +#endif +//$C088 49288 OECG R Read RAM bank 1; no write + visibleBank = LC_BANK_1; + readRAM = true; + writeRAM = false; + } + else if ((addr & 0xFFFB) == 0xC089) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n"); +#endif +//$C089 49289 OECG RR Read ROM; write RAM bank 1 + visibleBank = LC_BANK_1; + readRAM = false; + writeRAM = true; + } + else if ((addr & 0xFFFB) == 0xC08A) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n"); +#endif +//$C08A 49290 OECG R Read ROM; no write + visibleBank = LC_BANK_1; + readRAM = false; + writeRAM = false; + } + else if ((addr & 0xFFFB) == 0xC08B) + { +#ifdef DEBUG_LC +WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n"); +#endif +//$C08B 49291 OECG RR Read/write RAM bank 1 + visibleBank = LC_BANK_1; + readRAM = true; + writeRAM = true; + } +//This is determined by which slot it is in--this assumes slot 6. !!! FIX !!! else if ((addr & 0xFFF8) == 0xC0E0) { floppyDrive.ControlStepper(addr & 0x07); @@ -486,6 +1227,7 @@ SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only) else if (addr == 0xC0EC) { //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still... +//or DoIO floppyDrive.ReadWrite(); } else if (addr == 0xC0ED) @@ -502,26 +1244,98 @@ SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only) } //Still need to add missing I/O switches here... +//DEEE: BD 10 BF LDA $BF10,X [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07] +#if 0 +if (addr >= 0xD000 && addr <= 0xD00F) +{ + WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]); +} +#endif + if (addr >= 0xC000 && addr <= 0xCFFF) + return; // Protect LC bank #1 from being written to! + if (addr >= 0xD000) { if (writeRAM) { +#if 0 if (addr <= 0xDFFF && visibleBank == LC_BANK_1) ram[addr - 0x1000] = b; else ram[addr] = b; +#else + if (addr <= 0xDFFF && visibleBank == LC_BANK_1) + { + if (altzp) + ram2[addr - 0x1000] = b; + else + ram[addr - 0x1000] = b; + } + else + { + if (altzp) + ram2[addr] = b; + else + ram[addr] = b; + } +#endif } return; } - ram[addr] = b; + // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)... + if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode) + { + if (displayPage2) + ram2[addr] = b; + else + ram[addr] = b; + + return; + } + + // Finally, check for auxillary/altzp write switches +#if 0 + if (ramwrt) + ram2[addr] = b; + else + { + if (altzp) + ram2[addr] = b; + else + ram[addr] = b; + } +#else + if (addr < 0x0200) +// if (addr < 0x0200 || addr >= 0xD000) + { +#if 0 +if (addr == 0x38) + WriteLog("Write $38: $%02X\n", b); +else if (addr == 0x39) + WriteLog("Write $39: $%02X\n", b); +#endif + if (altzp) + ram2[addr] = b; + else + ram[addr] = b; + } + else + { + if (ramwrt) + ram2[addr] = b; + else + ram[addr] = b; + } +#endif } + // // Load a file into RAM/ROM image space // -bool LoadImg(char * filename, uint8 * ram, int size) +bool LoadImg(char * filename, uint8_t * ram, int size) { FILE * fp = fopen(filename, "rb"); @@ -534,15 +1348,23 @@ bool LoadImg(char * filename, uint8 * ram, int size) return true; } + static void SaveApple2State(const char * filename) { } + static bool LoadApple2State(const char * filename) { return false; } + +#ifdef CPU_CLOCK_CHECKING +uint8_t counter = 0; +uint32_t totalCPU = 0; +uint64_t lastClock = 0; +#endif // // Main loop // @@ -553,9 +1375,9 @@ int main(int /*argc*/, char * /*argv*/[]) srand(time(NULL)); // Initialize RNG // Zero out memory -//Need to bankify this stuff for the IIe emulation... memset(ram, 0, 0x10000); memset(rom, 0, 0x10000); + memset(ram2, 0, 0x10000); // Set up V65C02 execution context memset(&mainCPU, 0, sizeof(V65C02REGS)); @@ -563,7 +1385,9 @@ int main(int /*argc*/, char * /*argv*/[]) mainCPU.WrMem = WrMem; mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET; - if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000)) +// alternateCharset = true; +// if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000)) + if (!LoadImg(settings.BIOSPath, rom + 0xC000, 0x4000)) { WriteLog("Could not open file '%s'!\n", settings.BIOSPath); return -1; @@ -583,9 +1407,11 @@ int main(int /*argc*/, char * /*argv*/[]) //Kill the DOS ROM in slot 6 for now... //not - memcpy(rom + 0xC600, diskROM, 0x100); +// memcpy(rom + 0xC600, diskROM, 0x100); +// memcpy(rom + 0xC700, diskROM, 0x100); // Slot 7??? WriteLog("About to initialize video...\n"); + if (!InitVideo()) { std::cout << "Aborting!" << std::endl; @@ -637,14 +1463,19 @@ memcpy(ram + 0xD000, ram + 0xC000, 0x1000); WriteLog("About to initialize audio...\n"); SoundInit(); - SDL_EnableUNICODE(1); // Needed to do key translation shit +//nope SDL_EnableUNICODE(1); // Needed to do key translation shit - gui = new GUI(surface); // Set up the GUI system object... +// gui = new GUI(surface); // Set up the GUI system object... +// gui = new GUI(mainSurface); // Set up the GUI system object... +// SDL 2... this will likely cause Apple 2 to crash +// gui = new GUI(NULL); // Set up the GUI system object... +#if 0 gui->AddMenuTitle("Apple2"); gui->AddMenuItem("Test!", TestWindow/*, hotkey*/); gui->AddMenuItem(""); gui->AddMenuItem("Quit", QuitEmulator, SDLK_q); gui->CommitItemsToMenu(); +#endif SetupBlurTable(); // Set up the color TV emulation blur table running = true; // Set running status... @@ -654,18 +1485,65 @@ memcpy(ram + 0xD000, ram + 0xC000, 0x1000); SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 s intervals startTicks = SDL_GetTicks(); +#ifdef THREADED_65C02 + cpuCond = SDL_CreateCond(); + mainSem = SDL_CreateSemaphore(1); + cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL); +//Hmm... CPU does POST (+1), wait, then WAIT (-1) +// SDL_sem * mainMutex = SDL_CreateMutex(); +#endif + WriteLog("Entering main loop...\n"); while (running) { double timeToNextEvent = GetTimeToNextEvent(); +#ifndef THREADED_65C02 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent)); +#endif //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!! //(Fix so that this is not a requirement!) //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well. // mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent); + +#ifdef CPU_CLOCK_CHECKING +#ifndef THREADED_65C02 +totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent); +#endif +#endif + // Handle CPU time delta for sound... +//Don't need this anymore now that we use absolute time... +// AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent)); HandleNextEvent(); } +#ifdef THREADED_65C02 +WriteLog("Main: cpuFinished = true;\n"); +cpuFinished = true; +//#warning "If sound thread is behind, CPU thread will never wake up... !!! FIX !!!" [DONE] +//What to do? How do you know when the CPU is sleeping??? +//USE A CONDITIONAL!!! OF COURSE!!!!!!11!11!11!!!1! +#if 0 +SDL_mutexP(mainMutex); +SDL_CondWait(mainCond, mainMutex); // Wait for CPU thread to get to signal point... +SDL_mutexV(mainMutex); +#else +//Nope, use a semaphore... +WriteLog("Main: SDL_SemWait(mainSem);\n"); +SDL_SemWait(mainSem);//should lock until CPU thread is waiting... +#endif + +WriteLog("Main: SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up\n"); +SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up +WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n"); +SDL_WaitThread(cpuThread, NULL); +//nowok:SDL_WaitThread(CPUThreadFunc, NULL); +WriteLog("Main: SDL_DestroyCond(cpuCond);\n"); +SDL_DestroyCond(cpuCond); + +//SDL_DestroyMutex(mainMutex); +SDL_DestroySemaphore(mainSem); +#endif + if (settings.autoStateSaving) { // Save state here... @@ -681,6 +1559,7 @@ floppyDrive.SaveImage(); return 0; } + /* Apple II keycodes ----------------- @@ -736,6 +1615,8 @@ Z $DA $9A $DA $9A ESC $9B $9B $9B $9B No xlation */ +//static uint64_t lastCPUCycles = 0; +static uint32_t frameCount = 0; static void FrameCallback(void) { SDL_Event event; @@ -744,21 +1625,24 @@ static void FrameCallback(void) { switch (event.type) { - case SDL_KEYDOWN: - if (event.key.keysym.unicode != 0) - { + case SDL_TEXTINPUT: //Need to do some key translation here, and screen out non-apple keys as well... - if (event.key.keysym.sym == SDLK_TAB) // Prelim key screening... - break; - - lastKeyPressed = event.key.keysym.unicode; - keyDown = true; - //kludge: should have a caps lock thingy here... - //or all uppercase for ][+... - if (lastKeyPressed >= 'a' && lastKeyPressed <='z') - lastKeyPressed &= 0xDF; // Convert to upper case... - } +//(really, could do it all in SDL_KEYDOWN, would just have to get symbols & +// everything else done separately. this is slightly easier. :-P) +// if (event.key.keysym.sym == SDLK_TAB) // Prelim key screening... + if (event.edit.text[0] == '\t') // Prelim key screening... + break; + + lastKeyPressed = event.edit.text[0]; + keyDown = true; + + //kludge: should have a caps lock thingy here... + //or all uppercase for ][+... +// if (lastKeyPressed >= 'a' && lastKeyPressed <='z') +// lastKeyPressed &= 0xDF; // Convert to upper case... + break; + case SDL_KEYDOWN: // CTRL+RESET key emulation (mapped to CTRL+`) // This doesn't work... // if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL)) @@ -772,15 +1656,58 @@ static void FrameCallback(void) lastKeyPressed = 0x15, keyDown = true; else if (event.key.keysym.sym == SDLK_LEFT) lastKeyPressed = 0x08, keyDown = true; + else if (event.key.keysym.sym == SDLK_UP) + lastKeyPressed = 0x0B, keyDown = true; + else if (event.key.keysym.sym == SDLK_DOWN) + lastKeyPressed = 0x0A, keyDown = true; + else if (event.key.keysym.sym == SDLK_RETURN) + lastKeyPressed = 0x0D, keyDown = true; + else if (event.key.keysym.sym == SDLK_ESCAPE) + lastKeyPressed = 0x1B, keyDown = true; + else if (event.key.keysym.sym == SDLK_BACKSPACE) + lastKeyPressed = 0x7F, keyDown = true; + + // Fix CTRL+key combo... + if (event.key.keysym.mod & KMOD_CTRL) + { + if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) + { + lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1; + keyDown = true; +//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40); + } + } // Use ALT+Q to exit, as well as the usual window decoration method if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT)) running = false; - if (event.key.keysym.sym == SDLK_F12) + if (event.key.keysym.sym == SDLK_PAUSE) + { + pauseMode = !pauseMode; + + if (pauseMode) + { + SoundPause(); + SpawnMessage("*** PAUSED ***"); + } + else + { + SoundResume(); + SpawnMessage("*** RESUME ***"); + } + } + + // Paddle buttons 0 & 1 + if (event.key.keysym.sym == SDLK_INSERT) + openAppleDown = true; + if (event.key.keysym.sym == SDLK_PAGEUP) + closedAppleDown = true; + + if (event.key.keysym.sym == SDLK_F11) dumpDis = !dumpDis; // Toggle the disassembly process - else if (event.key.keysym.sym == SDLK_F11) - floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk... +// else if (event.key.keysym.sym == SDLK_F11) +// floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk... else if (event.key.keysym.sym == SDLK_F9) { floppyDrive.CreateBlankImage(); @@ -803,25 +1730,139 @@ else if (event.key.keysym.sym == SDLK_F10) // to quit completely... !!! FIX !!! gui->Run(); + if (event.key.keysym.sym == SDLK_F5) + { + VolumeDown(); + char volStr[19] = "[****************]"; +// volStr[GetVolume()] = 0; + for(int i=GetVolume(); i<16; i++) + volStr[1 + i] = '-'; + SpawnMessage("Volume: %s", volStr); + } + else if (event.key.keysym.sym == SDLK_F6) + { + VolumeUp(); + char volStr[19] = "[****************]"; +// volStr[GetVolume()] = 0; + for(int i=GetVolume(); i<16; i++) + volStr[1 + i] = '-'; + SpawnMessage("Volume: %s", volStr); + } + + static bool fullscreenDebounce = false; + + if (event.key.keysym.sym == SDLK_F12) + { + if (!fullscreenDebounce) + { + ToggleFullScreen(); + fullscreenDebounce = true; + } + } +// else + + break; + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_F12) + fullscreenDebounce = false; + + // Paddle buttons 0 & 1 + if (event.key.keysym.sym == SDLK_INSERT) + openAppleDown = false; + if (event.key.keysym.sym == SDLK_PAGEUP) + closedAppleDown = false; + +// if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) +// keyDown = false; + break; case SDL_QUIT: running = false; } } - HandleSoundAtFrameEdge(); // Sound stuff... (ick) +//#warning "!!! Taking MAJOR time hit with the video frame rendering !!!" RenderVideoFrame(); SetCallbackTime(FrameCallback, 16666.66666667); -//Instead of this, we should yield remaining time to other processes... !!! FIX !!! +#ifdef CPU_CLOCK_CHECKING +//We know it's stopped, so we can get away with this... +counter++; +if (counter == 60) +{ + uint64_t clock = GetCurrentV65C02Clock(); +//totalCPU += (uint32_t)(clock - lastClock); + + printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock)); + lastClock = clock; +// totalCPU = 0; + counter = 0; +} +#endif +//Instead of this, we should yield remaining time to other processes... !!! FIX !!! [DONE] //lessee... -//nope. SDL_Delay(10); - while (SDL_GetTicks() - startTicks < 16); // Wait for next frame... +//nope. +//Actually, slows things down too much... +//SDL_Delay(10); +// while (SDL_GetTicks() - startTicks < 16); // Wait for next frame... + +// This is the problem: If you set the interval to 16, it runs faster than +// 1/60s per frame. If you set it to 17, it runs slower. What we need is to +// have it do 16 for one frame, then 17 for two others. Then it should average +// out to 1/60s per frame every 3 frames. + frameCount = (frameCount + 1) % 3; + + uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0); + + while (SDL_GetTicks() - startTicks < waitFrameTime) + SDL_Delay(1); // Wait for next frame... + startTicks = SDL_GetTicks(); +#if 0 + uint64_t cpuCycles = GetCurrentV65C02Clock(); + uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles); + WriteLog("FrameCallback: used %i cycles\n", cyclesBurned); + lastCPUCycles = cpuCycles; +#endif + +//let's wait, then signal... +//works longer, but then still falls behind... +#ifdef THREADED_65C02 + if (!pauseMode) + SDL_CondSignal(cpuCond);//OK, let the CPU go another frame... +#endif } + static void BlinkTimer(void) { flash = !flash; SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 sec intervals } + + +/* +Next problem is this: How to have events occur and synchronize with the rest +of the threads? + + o Have the CPU thread manage the timer mechanism? (need to have a method of carrying + remainder CPU cycles over...) + +One way would be to use a fractional accumulator, then subtract 1 every +time it overflows. Like so: + +double overflow = 0; +uint32_t time = 20; +while (!done) +{ + Execute6808(&soundCPU, time); + overflow += 0.289115646; + if (overflow > 1.0) + { + overflow -= 1.0; + time = 21; + } + else + time = 20; +} +*/