From: Shamus Hammons Date: Fri, 12 Aug 2022 16:12:40 +0000 (-0500) Subject: Miscellaneous bugfixes. X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?p=apple2;a=commitdiff_plain;h=695e6c22a404fc7e4141a5c8e5ceda10d48c7fd3 Miscellaneous bugfixes. Once again, 4am's Total Replay uncovered a bug in the emulation when its replay of Firebug would lock up the emulation. This was caused by the MMU not returning floating bus values from certain $C0xx soft switches. Also, there was a bug in floppydrive.cpp which would cause the bitstream to slip even when the simulated drive head never moved. This allows Bard's Tale II to boot. Also, some minor fixes to allow the LSS to honor a WOZ2 disk's "optimal bit timing" field. Border Zone & other similar disks *still* don't work, so there's work still to be done there... Again, almost a year since the last commit. How the time does fly. :-/ --- diff --git a/src/apple2.cpp b/src/apple2.cpp index e97363a..be5a16d 100644 --- a/src/apple2.cpp +++ b/src/apple2.cpp @@ -278,7 +278,6 @@ WriteLog("CPU: SDL_mutexV(cpuMutex);\n"); } #endif - // // Request a change in the power state of the emulated Apple // @@ -287,7 +286,6 @@ void SetPowerState(void) powerStateChangeRequested = true; } - // // Load a file into RAM/ROM image space // @@ -304,7 +302,6 @@ bool LoadImg(char * filename, uint8_t * ram, int size) return true; } - const uint8_t stateHeader[19] = "APPLE2SAVESTATE1.2"; static void SaveApple2State(const char * filename) { @@ -358,7 +355,6 @@ static void SaveApple2State(const char * filename) fclose(file); } - static bool LoadApple2State(const char * filename) { WriteLog("Main: Loading Apple2 state...\n"); @@ -427,7 +423,6 @@ static bool LoadApple2State(const char * filename) return true; } - static void ResetApple2State(void) { keyDown = false; @@ -453,7 +448,6 @@ static void ResetApple2State(void) mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET; } - static double cyclesForSample = 0; static void AppleTimer(uint16_t cycles) { @@ -481,7 +475,6 @@ lastSampleClock = sampleClock; #endif } - #ifdef CPU_CLOCK_CHECKING uint8_t counter = 0; uint32_t totalCPU = 0; @@ -666,8 +659,8 @@ WriteLog("Main: SDL_DestroyCond(cpuCond);\n"); #if 0 #include "dis65c02.h" static char disbuf[80]; -uint16_t pc=0x801; -while (pc < 0x9FF) +uint16_t pc=0xA000; +while (pc < 0xA3FF) { pc += Decode65C02(&mainCPU, disbuf, pc); WriteLog("%s\n", disbuf); @@ -682,7 +675,6 @@ while (pc < 0x9FF) return 0; } - // // Apple //e scancodes. Tables are normal (0), +CTRL (1), +SHIFT (2), // +CTRL+SHIFT (3). Order of keys is: @@ -1147,7 +1139,6 @@ if (counter == 60) #endif } - static void BlinkTimer(void) { // Set up blinking at 1/4 sec intervals @@ -1155,7 +1146,6 @@ static void BlinkTimer(void) SetCallbackTime(BlinkTimer, 250000); } - /* Next problem is this: How to have events occur and synchronize with the rest of the threads? @@ -1181,4 +1171,3 @@ while (!done) time = 20; } */ - diff --git a/src/fileio.cpp b/src/fileio.cpp index cc2ef48..1048be9 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -205,7 +205,8 @@ bool CheckWOZIntegrity(const uint8_t * wozData, uint32_t wozSize) else if (wozCRC == 0) WriteLog("FILEIO: Warning--WOZ file has no CRC...\n"); -#if 0 // Need to fix this so it works with both 1 & 2 (works with only 1 ATM) +#if 1 + // Need to fix this so it works with both 1 & 2 (works with only 1 ATM) WriteLog("Track map:\n"); WriteLog(" 1 1 1 1 1 1 1 1\n"); WriteLog("0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.\n"); @@ -248,6 +249,7 @@ bool CheckWOZIntegrity(const uint8_t * wozData, uint32_t wozSize) WriteLog("\n"); +#if 0 uint8_t numTracks = woz.trksSize / sizeof(WOZ1Track); // N.B.: Need to check the track in tmap[] to have this tell the correct track... Right now, it doesn't @@ -255,6 +257,7 @@ bool CheckWOZIntegrity(const uint8_t * wozData, uint32_t wozSize) { WriteLog("WOZ: Stream %u: %d bits (packed into %d bytes)\n", i, woz.track[i].bitCount, (woz.track[i].bitCount + 7) / 8); } +#endif #endif WriteLog("FILEIO: Well formed WOZ file found\n"); diff --git a/src/firmware/firmware.cpp b/src/firmware/firmware.cpp index 54d2510..fbd0f28 100644 --- a/src/firmware/firmware.cpp +++ b/src/firmware/firmware.cpp @@ -4,26 +4,83 @@ #include "firmware.h" +// +// 16-sector bootstrap driver for disk controller +// +uint8_t diskROM[0x100] = { + 0xA2, 0x20, 0xA0, 0x00, 0xA2, 0x03, 0x86, 0x3C, + 0x8A, 0x0A, 0x24, 0x3C, 0xF0, 0x10, 0x05, 0x3C, + 0x49, 0xFF, 0x29, 0x7E, 0xB0, 0x08, 0x4A, 0xD0, + 0xFB, 0x98, 0x9D, 0x56, 0x03, 0xC8, 0xE8, 0x10, + 0xE5, 0x20, 0x58, 0xFF, 0xBA, 0xBD, 0x00, 0x01, + 0x0A, 0x0A, 0x0A, 0x0A, 0x85, 0x2B, 0xAA, 0xBD, + 0x8E, 0xC0, 0xBD, 0x8C, 0xC0, 0xBD, 0x8A, 0xC0, + 0xBD, 0x89, 0xC0, 0xA0, 0x50, 0xBD, 0x80, 0xC0, + 0x98, 0x29, 0x03, 0x0A, 0x05, 0x2B, 0xAA, 0xBD, + 0x81, 0xC0, 0xA9, 0x56, 0x20, 0xA8, 0xFC, 0x88, + 0x10, 0xEB, 0x85, 0x26, 0x85, 0x3D, 0x85, 0x41, + 0xA9, 0x08, 0x85, 0x27, 0x18, 0x08, 0xBD, 0x8C, + 0xC0, 0x10, 0xFB, 0x49, 0xD5, 0xD0, 0xF7, 0xBD, + 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0xAA, 0xD0, 0xF3, + 0xEA, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0x96, + 0xF0, 0x09, 0x28, 0x90, 0xDF, 0x49, 0xAD, 0xF0, + 0x25, 0xD0, 0xD9, 0xA0, 0x03, 0x85, 0x40, 0xBD, + 0x8C, 0xC0, 0x10, 0xFB, 0x2A, 0x85, 0x3C, 0xBD, + 0x8C, 0xC0, 0x10, 0xFB, 0x25, 0x3C, 0x88, 0xD0, + 0xEC, 0x28, 0xC5, 0x3D, 0xD0, 0xBE, 0xA5, 0x40, + 0xC5, 0x41, 0xD0, 0xB8, 0xB0, 0xB7, 0xA0, 0x56, + 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10, 0xFB, 0x59, + 0xD6, 0x02, 0xA4, 0x3C, 0x88, 0x99, 0x00, 0x03, + 0xD0, 0xEE, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10, + 0xFB, 0x59, 0xD6, 0x02, 0xA4, 0x3C, 0x91, 0x26, + 0xC8, 0xD0, 0xEF, 0xBC, 0x8C, 0xC0, 0x10, 0xFB, + 0x59, 0xD6, 0x02, 0xD0, 0x87, 0xA0, 0x00, 0xA2, + 0x56, 0xCA, 0x30, 0xFB, 0xB1, 0x26, 0x5E, 0x00, + 0x03, 0x2A, 0x5E, 0x00, 0x03, 0x2A, 0x91, 0x26, + 0xC8, 0xD0, 0xEE, 0xE6, 0x27, 0xE6, 0x3D, 0xA5, + 0x3D, 0xCD, 0x00, 0x08, 0xA6, 0x2B, 0x90, 0xDB, + 0x4C, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 +}; -uint8_t diskROM[0x100] = { // Loads at $C600 (slot 6) - 0xA2, 0x20, 0xA0, 0x00, 0xA2, 0x03, 0x86, 0x3C, 0x8A, 0x0A, 0x24, 0x3C, 0xF0, 0x10, 0x05, 0x3C, - 0x49, 0xFF, 0x29, 0x7E, 0xB0, 0x08, 0x4A, 0xD0, 0xFB, 0x98, 0x9D, 0x56, 0x03, 0xC8, 0xE8, 0x10, - 0xE5, 0x20, 0x58, 0xFF, 0xBA, 0xBD, 0x00, 0x01, 0x0A, 0x0A, 0x0A, 0x0A, 0x85, 0x2B, 0xAA, 0xBD, - 0x8E, 0xC0, 0xBD, 0x8C, 0xC0, 0xBD, 0x8A, 0xC0, 0xBD, 0x89, 0xC0, 0xA0, 0x50, 0xBD, 0x80, 0xC0, - 0x98, 0x29, 0x03, 0x0A, 0x05, 0x2B, 0xAA, 0xBD, 0x81, 0xC0, 0xA9, 0x56, 0x20, 0xA8, 0xFC, 0x88, - 0x10, 0xEB, 0x85, 0x26, 0x85, 0x3D, 0x85, 0x41, 0xA9, 0x08, 0x85, 0x27, 0x18, 0x08, 0xBD, 0x8C, - 0xC0, 0x10, 0xFB, 0x49, 0xD5, 0xD0, 0xF7, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0xAA, 0xD0, 0xF3, - 0xEA, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0x96, 0xF0, 0x09, 0x28, 0x90, 0xDF, 0x49, 0xAD, 0xF0, - 0x25, 0xD0, 0xD9, 0xA0, 0x03, 0x85, 0x40, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0x2A, 0x85, 0x3C, 0xBD, - 0x8C, 0xC0, 0x10, 0xFB, 0x25, 0x3C, 0x88, 0xD0, 0xEC, 0x28, 0xC5, 0x3D, 0xD0, 0xBE, 0xA5, 0x40, - 0xC5, 0x41, 0xD0, 0xB8, 0xB0, 0xB7, 0xA0, 0x56, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10, 0xFB, 0x59, - 0xD6, 0x02, 0xA4, 0x3C, 0x88, 0x99, 0x00, 0x03, 0xD0, 0xEE, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10, - 0xFB, 0x59, 0xD6, 0x02, 0xA4, 0x3C, 0x91, 0x26, 0xC8, 0xD0, 0xEF, 0xBC, 0x8C, 0xC0, 0x10, 0xFB, - 0x59, 0xD6, 0x02, 0xD0, 0x87, 0xA0, 0x00, 0xA2, 0x56, 0xCA, 0x30, 0xFB, 0xB1, 0x26, 0x5E, 0x00, - 0x03, 0x2A, 0x5E, 0x00, 0x03, 0x2A, 0x91, 0x26, 0xC8, 0xD0, 0xEE, 0xE6, 0x27, 0xE6, 0x3D, 0xA5, - 0x3D, 0xCD, 0x00, 0x08, 0xA6, 0x2B, 0x90, 0xDB, 0x4C, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 +// +// 13-sector bootstrap driver for disk controller +// +uint8_t diskROM13[0x100] = { + 0xA2, 0x20, 0xA0, 0x00, 0xA9, 0x03, 0x85, 0x3C, + 0x18, 0x88, 0x98, 0x24, 0x3C, 0xF0, 0xF5, 0x26, + 0x3C, 0x90, 0xF8, 0xC0, 0xD5, 0xF0, 0xED, 0xCA, + 0x8A, 0x99, 0x00, 0x08, 0xD0, 0xE6, 0x20, 0x58, + 0xFF, 0xBA, 0xBD, 0x00, 0x01, 0x48, 0x0A, 0x0A, + 0x0A, 0x0A, 0x85, 0x2B, 0xAA, 0xA9, 0xD0, 0x48, + 0xBD, 0x8E, 0xC0, 0xBD, 0x8C, 0xC0, 0xBD, 0x8A, + 0xC0, 0xBD, 0x89, 0xC0, 0xA0, 0x50, 0xBD, 0x80, + 0xC0, 0x98, 0x29, 0x03, 0x0A, 0x05, 0x2B, 0xAA, + 0xBD, 0x81, 0xC0, 0xA9, 0x56, 0x20, 0xA8, 0xFC, + 0x88, 0x10, 0xEB, 0xA9, 0x03, 0x85, 0x27, 0xA9, + 0x00, 0x85, 0x26, 0x85, 0x3D, 0x18, 0x08, 0xBD, + 0x8C, 0xC0, 0x10, 0xFB, 0x49, 0xD5, 0xD0, 0xF7, + 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0xAA, 0xD0, + 0xF3, 0xEA, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, + 0xB5, 0xF0, 0x09, 0x28, 0x90, 0xDF, 0x49, 0xAD, + 0xF0, 0x1F, 0xD0, 0xD9, 0xA0, 0x03, 0x84, 0x2A, + 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0x2A, 0x85, 0x3C, + 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0x25, 0x3C, 0x88, + 0xD0, 0xEE, 0x28, 0xC5, 0x3D, 0xD0, 0xBE, 0xB0, + 0xBD, 0xA0, 0x9A, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, + 0x10, 0xFB, 0x59, 0x00, 0x08, 0xA4, 0x3C, 0x88, + 0x99, 0x00, 0x08, 0xD0, 0xEE, 0x84, 0x3C, 0xBC, + 0x8C, 0xC0, 0x10, 0xFB, 0x59, 0x00, 0x08, 0xA4, + 0x3C, 0x91, 0x26, 0xC8, 0xD0, 0xEF, 0xBC, 0x8C, + 0xC0, 0x10, 0xFB, 0x59, 0x00, 0x08, 0xD0, 0x8D, + 0x60, 0xA8, 0xA2, 0x00, 0xB9, 0x00, 0x08, 0x4A, + 0x3E, 0xCC, 0x03, 0x4A, 0x3E, 0x99, 0x03, 0x85, + 0x3C, 0xB1, 0x26, 0x0A, 0x0A, 0x0A, 0x05, 0x3C, + 0x91, 0x26, 0xC8, 0xE8, 0xE0, 0x33, 0xD0, 0xE4, + 0xC6, 0x2A, 0xD0, 0xDE, 0xCC, 0x00, 0x03, 0xD0, + 0x03, 0x4C, 0x01, 0x03, 0x4C, 0x2D, 0xFF, 0xFF }; +// Looks like this is unused... uint8_t hdROM[0x100] = { // Loads at $C700 (slot 7) 0xA9, 0x20, 0xA9, 0x00, 0xA9, 0x03, 0xA9, 0x3C, 0xA9, 0x00, 0x8D, 0xF2, 0xC0, 0xA9, 0x70, 0x8D, 0xF3, 0xC0, 0xAD, 0xF0, 0xC0, 0x48, 0xAD, 0xF1, 0xC0, 0x18, 0xC9, 0x01, 0xD0, 0x01, 0x38, 0x68, diff --git a/src/firmware/firmware.h b/src/firmware/firmware.h index caac1ab..3a869f8 100644 --- a/src/firmware/firmware.h +++ b/src/firmware/firmware.h @@ -3,8 +3,9 @@ #include -extern uint8_t diskROM[0x100]; // Loads at $C600 (slot 6) -extern uint8_t hdROM[0x100]; // Loads at $C700 (slot 7) +extern uint8_t diskROM[0x100]; +extern uint8_t diskROM13[0x100]; +extern uint8_t hdROM[0x100]; // Loads at $C700 (slot 7) [unused] extern uint8_t parallelROM[0x100];// (slot 1) //Not sure what the heck this is... extern uint8_t slot2e[0x100]; diff --git a/src/floppydrive.cpp b/src/floppydrive.cpp index ffae063..9291719 100644 --- a/src/floppydrive.cpp +++ b/src/floppydrive.cpp @@ -26,6 +26,9 @@ #include "video.h" // For message spawning... Though there's probably a // better approach than this! +// For testing 13-sector disk FW +//#define TEST13 + // Useful enums enum { IO_MODE_READ, IO_MODE_WRITE }; @@ -33,28 +36,153 @@ enum { IO_MODE_READ, IO_MODE_WRITE }; // Misc. arrays (read only) that are needed static const uint8_t bitMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + static const uint8_t sequencerROM[256] = { - 0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x2D, 0x38, 0x2D, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x38, 0x28, 0xD8, 0x08, 0x0A, 0x0A, 0x0A, 0x0A, 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, - 0x48, 0x48, 0xD8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, - 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, - 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, - 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, - 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x29, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, - 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, - 0x59, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, - 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, - 0x08, 0xE8, 0xD8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, - 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, - 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08 + 0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // $00 + 0x2D, 0x38, 0x2D, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // $10 + 0x38, 0x28, 0xD8, 0x08, 0x0A, 0x0A, 0x0A, 0x0A, + 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, // $20 + 0x48, 0x48, 0xD8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // $30 + 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // $40 + 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // $50 + 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // $60 + 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, // $70 + 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // $80 + 0x29, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // $90 + 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, // $A0 + 0x59, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, // $B0 + 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, // $C0 + 0x08, 0xE8, 0xD8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // $D0 + 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // $E0 + 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, + 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08 // $F0 }; +#if 1 // From UTA2E, but need to double check... +static const uint8_t sequencerROM13[256] = { +// vvvv (2, 4, 1, 3) + 0x18, 0x08, 0xD8, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // $00 + 0x28, 0x28, 0xD8, 0x28, 0x0A, 0x0A, 0x0A, 0x0A, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // $10 + 0x38, 0x38, 0xD8, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, + 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, // $20 + 0x48, 0x48, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // $30 + 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // $40 + 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // $50 + 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // $60 + 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, // $70 + 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // $80 + 0x09, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // $90 + 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, // $A0 + 0x39, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, // $B0 + 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, // $C0 + 0x0D, 0xE8, 0x1D, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // $D0 + 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // $E0 + 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, + 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08 // $F0 +}; +#endif +#if 0 +static const uint8_t sequencerROM13[256] = { + 0x18, 0x08, 0xD8, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x38, 0x38, 0xD8, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, + 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, + 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, + 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, + 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0x28, 0x28, 0xD8, 0x28, 0x0A, 0x0A, 0x0A, 0x0A, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x09, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0x48, 0x48, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x39, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, + 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, + 0x0D, 0xE8, 0x1D, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, + 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, + 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, + 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08 +}; +#endif +#if 0 +static const uint8_t sequencerROM13[256] = { + 0x88, 0x08, 0xB8, 0x88, 0x0A, 0x0A, 0x0A, 0x0A, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, // $00 + 0x98, 0x98, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // $10 + 0xC8, 0xC8, 0xB8, 0xC8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xC9, 0xC9, 0xC9, 0xC9, 0xCB, 0xCB, 0xCB, 0xCB, // $20 + 0xDD, 0xD8, 0x3D, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xD9, 0xD9, 0xD9, 0xD9, 0xDB, 0xDB, 0xDB, 0xDB, // $30 + 0xA8, 0xA8, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // $40 + 0xB9, 0x50, 0xB9, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, // $50 + 0xE8, 0xE8, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // $60 + 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // $70 + 0x48, 0x48, 0xB8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // $80 + 0x09, 0x58, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // $90 + 0x28, 0x28, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // $A0 + 0xC9, 0x38, 0xB9, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, // $B0 + 0x68, 0x68, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // $C0 + 0x0D, 0x78, 0x8D, 0x78, 0x0A, 0x0A, 0x0A, 0x0A, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // $D0 + 0x18, 0x18, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, + 0x08, 0x18, 0x08, 0x18, 0x08, 0x18, 0x08, 0x18, // $E0 + 0x2D, 0x70, 0xBD, 0x70, 0x0A, 0x0A, 0x0A, 0x0A, + 0x18, 0x08, 0x18, 0x08, 0x18, 0x08, 0x18, 0x08 // $F0 +}; +#endif static char nameBuf[MAX_PATH]; - // Static in-line functions, for clarity & speed, for swapping variables static inline void Swap(uint8_t & a, uint8_t & b) { @@ -63,7 +191,6 @@ static inline void Swap(uint8_t & a, uint8_t & b) b = t; } - static inline void Swap(uint32_t & a, uint32_t & b) { uint32_t t = a; @@ -71,7 +198,6 @@ static inline void Swap(uint32_t & a, uint32_t & b) b = t; } - static inline void Swap(bool & a, bool & b) { bool t = a; @@ -79,7 +205,6 @@ static inline void Swap(bool & a, bool & b) b = t; } - static inline void Swap(uint8_t * & a, uint8_t * & b) { uint8_t * t = a; @@ -87,9 +212,9 @@ static inline void Swap(uint8_t * & a, uint8_t * & b) b = t; } - +// // FloppyDrive class implementation... - +// FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), ioHappened(false), diskImageReady(false) { phase[0] = phase[1] = 0; @@ -102,7 +227,6 @@ FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), i imageName[0][0] = imageName[1][0] = 0; // Zero out filenames } - FloppyDrive::~FloppyDrive() { if (disk[0]) @@ -112,7 +236,6 @@ FloppyDrive::~FloppyDrive() free(disk[1]); } - bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/) { WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum); @@ -149,7 +272,6 @@ bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/) return true; } - bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/) { // Various sanity checks... @@ -179,7 +301,6 @@ bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/) return SaveWOZ(imageName[driveNum], (WOZ2 *)disk[driveNum], diskSize[driveNum]); } - bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/) { strncpy(imageName[driveNum], filename, MAX_PATH); @@ -189,7 +310,6 @@ bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/) return SaveImage(driveNum); } - void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/) { if (disk[driveNum] != NULL) @@ -201,7 +321,6 @@ void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/) SpawnMessage("New blank image inserted in drive %u...", driveNum); } - void FloppyDrive::SwapImages(void) { char imageNameTmp[MAX_PATH]; @@ -221,7 +340,6 @@ void FloppyDrive::SwapImages(void) SpawnMessage("Drive 0: %s...", imageName[0]); } - /* Need to add some type of error checking here, so we can report back on bad images, etc. (basically, it does by returning DFT_UNKNOWN, but we could do better) */ @@ -248,6 +366,7 @@ void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum) WriteLog("FLOPPY: Upconverted WOZ type 1 to type 2...\n"); } + WriteLog("FLOPPY: OBT is %d\n", ((WOZ2 *)disk[driveNum])->optimalBitTmg); diskType[driveNum] = DT_WOZ; } else if (diskSize[driveNum] == 143360) @@ -310,7 +429,6 @@ void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum) "DOS 3.3 (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS" : (diskType[driveNum] == DT_WOZ ? "WOZ" : "unknown"))))); } - // // Write a bitstream (source left justified to bit 7) to destination buffer. // Writes 'bits' number of bits to 'dest', starting at bit position 'dstPtr', @@ -334,7 +452,6 @@ void FloppyDrive::WriteBits(uint8_t * dest, const uint8_t * src, uint16_t bits, } } - void FloppyDrive::WOZifyImage(uint8_t driveNum) { // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector @@ -458,7 +575,6 @@ void FloppyDrive::WOZifyImage(uint8_t driveNum) free(tmpDisk); } - const char * FloppyDrive::ImageName(uint8_t driveNum/*= 0*/) { // Set up a zero-length string for return value @@ -495,7 +611,6 @@ const char * FloppyDrive::ImageName(uint8_t driveNum/*= 0*/) return nameBuf; } - void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/) { // Sanity check @@ -516,7 +631,6 @@ void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/) imageName[driveNum][0] = 0; // Zero out filenames } - bool FloppyDrive::IsEmpty(uint8_t driveNum/*= 0*/) { if (driveNum > 1) @@ -528,7 +642,6 @@ bool FloppyDrive::IsEmpty(uint8_t driveNum/*= 0*/) return (diskType[driveNum] == DT_EMPTY ? true : false); } - bool FloppyDrive::IsWriteProtected(uint8_t driveNum/*= 0*/) { if (driveNum > 1) @@ -541,7 +654,6 @@ bool FloppyDrive::IsWriteProtected(uint8_t driveNum/*= 0*/) return (bool)woz.writeProtected; } - void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/) { if (driveNum > 1) @@ -554,7 +666,6 @@ void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/) woz.writeProtected = (uint8_t)state; } - int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/) { int retval = DLS_OFF; @@ -569,7 +680,6 @@ int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/) return retval; } - void FloppyDrive::SaveState(FILE * file) { // Internal state vars @@ -610,7 +720,6 @@ void FloppyDrive::SaveState(FILE * file) WriteLong(file, 0); } - void FloppyDrive::LoadState(FILE * file) { // Eject images if they're loaded @@ -653,7 +762,6 @@ void FloppyDrive::LoadState(FILE * file) } } - uint32_t FloppyDrive::ReadLong(FILE * file) { uint32_t r = 0; @@ -664,7 +772,6 @@ uint32_t FloppyDrive::ReadLong(FILE * file) return r; } - void FloppyDrive::WriteLong(FILE * file, uint32_t l) { for(int i=0; i<4; i++) @@ -674,7 +781,6 @@ void FloppyDrive::WriteLong(FILE * file, uint32_t l) } } - // Memory mapped I/O functions + Logic State Sequencer /* @@ -754,11 +860,13 @@ If it ever *does* become a problem, doing the physical modeling of the head movi else phase[activeDrive] &= ~phaseBit; - uint8_t oldHeadPos = headPos[activeDrive] & 0x07; - int16_t newStep = step[phase[activeDrive]][oldHeadPos]; + int16_t oldHeadPos = headPos[activeDrive]; + int16_t newStep = step[phase[activeDrive]][oldHeadPos & 0x07]; int16_t newHeadPos = (int16_t)headPos[activeDrive] + newStep; +WriteLog("\nFLOPPY: oldHeadPos=%u, newHeadPos=%i, newStep=%i\n", oldHeadPos, newHeadPos, newStep); // Sanity check + // N.B.: This is wrong for 3.5" disks if ((newHeadPos >= 0) && (newHeadPos <= 140)) headPos[activeDrive] = (uint8_t)newHeadPos; @@ -772,7 +880,9 @@ If it ever *does* become a problem, doing the physical modeling of the head movi uint8_t oldTIdx = woz.tmap[oldHeadPos]; float oldBitLen = (oldTIdx == 0xFF ? 51200.0f : Uint16LE(woz.track[oldTIdx].bitCount)); +WriteLog("FLOPPY: Current pos pre: %u, ", currentPos[activeDrive]); currentPos[activeDrive] = (uint32_t)((float)currentPos[activeDrive] * (newBitLen / oldBitLen)); +WriteLog("post: %u; newBitLen/old = %.1f/%.1f\n", currentPos[activeDrive], newBitLen, oldBitLen); trackLength[activeDrive] = (uint16_t)newBitLen; SpawnMessage("Stepping to track %u...", headPos[activeDrive] >> 2); @@ -787,7 +897,6 @@ if (addr & 0x01) WriteLog("FLOPPY: Stepper phase %d set to %s [%c%c%c%c] (track=%2.2f) [%u]\n", (addr >> 1) & 0x03, (addr & 0x01 ? "ON " : "off"), (phase[activeDrive] & 0x08 ? '|' : '.'), (phase[activeDrive] & 0x04 ? '|' : '.'), (phase[activeDrive] & 0x02 ? '|' : '.'), (phase[activeDrive] & 0x01 ? '|' : '.'), (float)headPos[activeDrive] / 4.0f, mainCPU.clock & 0xFFFFFFFF); } - void FloppyDrive::ControlMotor(uint8_t addr) { // $C0E8 - 9 @@ -801,7 +910,6 @@ void FloppyDrive::ControlMotor(uint8_t addr) WriteLog("FLOPPY: Turning drive motor %s\n", (motorOn ? "ON" : "off")); } - void FloppyDrive::DriveEnable(uint8_t addr) { // $C0EA - B @@ -809,7 +917,6 @@ void FloppyDrive::DriveEnable(uint8_t addr) WriteLog("FLOPPY: Selecting drive #%hhd\n", addr + 1); } - /* So for $C08C-F, we have two switches (Q6 & Q7) which combine to make four states ($C-D is off/on for Q6, $E-F is off/on for Q7). @@ -825,21 +932,18 @@ Looks like reads from even addresses in $C080-F block transfer data from the seq */ - void FloppyDrive::SetShiftLoadSwitch(uint8_t state) { // $C0EC - D slSwitch = state; } - void FloppyDrive::SetReadWriteSwitch(uint8_t state) { // $C0EE - F rwSwitch = state; } - // MMIO: Reads from $C08x to $C0XX on even addresses uint8_t FloppyDrive::DataRegister(void) { @@ -858,15 +962,14 @@ uint8_t FloppyDrive::DataRegister(void) if ((seenReadSinceStep == false) && (slSwitch == false) && (rwSwitch == false) && ((iorAddr & 0x0F) == 0x0C)) { seenReadSinceStep = true; - WriteLog("%u:Reading $%02X from track %u, sector %u (delta since seek: %lu cycles) [%u]...\n", - activeDrive, dataRegister, headPos[activeDrive] >> 2, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f), mainCPU.clock - stepperTime, mainCPU.clock & 0xFFFFFFFF); + WriteLog("%u:Reading $%02X from track %2.2f, sector %u (delta since seek: %lu cycles) [%u]...\n", + activeDrive, dataRegister, (float)headPos[activeDrive] / 4.0f, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f), mainCPU.clock - stepperTime, mainCPU.clock & 0xFFFFFFFF); } } return dataRegister; } - // MMIO: Writes from $C08x to $C0XX on odd addresses void FloppyDrive::DataRegister(uint8_t data) { @@ -875,7 +978,6 @@ void FloppyDrive::DataRegister(uint8_t data) ioHappened = true; } - /* OFF switches ON switches Switch Addr Func Addr Func @@ -948,9 +1050,11 @@ $5A: File structure damaged // which will make the simulated drive run in the neighborhood of around // 306 RPM. Should be close enough to get away with it. :-) (And it // seems to run OK, for the most part.) - +// +// According to EDD 4 the drive is running at 299.1 RPM... :-/ static bool logSeq = false; +char sequence[1024]; // // Logic State Sequencer & Data Register // @@ -974,6 +1078,9 @@ void FloppyDrive::RunSequencer(uint32_t cyclesToRun) WOZ2 & woz = *((WOZ2 *)disk[activeDrive]); uint8_t tIdx = woz.tmap[headPos[activeDrive]]; uint8_t * tdata = disk[activeDrive] + (Uint16LE(woz.track[tIdx].startingBlock) * 512); + // We have to divide the optimal bit timing by 4 because we only have a 0.5µs granularity here (with the doubling of the "cyclesToRun"). The OBT has a granularity of 0.125µs. Not sure how to fix that--have to separate the pulse handling from the sequencer? + // N.B.: Border Zone has an OBT of 28, when divides evenly by 4, but it still fails... + uint8_t pulseTiming = woz.optimalBitTmg / 4; // It's x2 because the sequencer clock runs twice as fast as the CPU clock. cyclesToRun *= 2; @@ -990,7 +1097,8 @@ if (logSeq) while (cyclesToRun-- > 0) { // pulseClock = (pulseClock + 1) & 0x07; - pulseClock = (pulseClock + 1) % 8; +// pulseClock = (pulseClock + 1) % 8; + pulseClock = (pulseClock + 1) % pulseTiming; // 7 doesn't work... Is that 3.5µs? Seems to be. Which means to get a 0.25µs granularity here, we need to double the # of cycles to run... // pulseClock = (pulseClock + 1) % 7; @@ -998,6 +1106,7 @@ if (logSeq) { uint16_t bytePos = currentPos[activeDrive] / 8; uint8_t bitPos = currentPos[activeDrive] % 8; + window <<= 1; if (tIdx != 0xFF) { @@ -1005,14 +1114,17 @@ if (logSeq) { // According to Jim Sather (Understanding the Apple II), // the Read Pulse, when it happens, is 1µs long, which is 2 - // sequencer clock pulses long. - readPulse = 2; + // sequencer clock pulses long. (Not sure where, elsewhere, on pg. 9-29 it says it lasts one *sequencer* clock pulse.) + readPulse = 1;//2; zeroBitCount = 0; + window |= 0x01; } else zeroBitCount++; +//WriteLog("[%d]", tdata[bytePos] & bitMask[bitPos] ? 1 : 0); } + readPulse = (window >> 1) & 0x01; // Read pulse delayed by about 5µs currentPos[activeDrive] = (currentPos[activeDrive] + 1) % trackLength[activeDrive]; // If we hit more than 2 zero bits in a row, simulate the disk head @@ -1022,13 +1134,14 @@ if (logSeq) /* N.B.: Had to up this to 3 because Up N' Down had some weird sync bytes (FE10). May have to up it some more. */ - if ((zeroBitCount > 3) || (tIdx == 0xFF)) +// if ((zeroBitCount > 3) || (tIdx == 0xFF)) + if (((window & 0x0F) == 0) || (tIdx == 0xFF)) { if (prng & 0x00001) { // This PRNG is called the "Galois configuration". prng ^= 0x24000; - readPulse = 2; + readPulse = 1;//2; } prng >>= 1; @@ -1042,23 +1155,40 @@ N.B.: Had to up this to 3 because Up N' Down had some weird sync bytes (FE10). if (logSeq) WriteLog("[%02X:%02X]%s", sequencerState, nextState, (chop == 15 ? "\n" : "")); chop = (chop + 1) % 20; +#ifndef TEST13 + sequencerState = sequencerROM[nextState]; +#else +// sequencerState = sequencerROM13[nextState]; sequencerState = sequencerROM[nextState]; +#endif + +#if 0 +uint32_t seqLen = strlen(sequence); + +if (seqLen + 7 > 1023) +{ + sequence[0] = 0; + seqLen = 0; +} + +sprintf(&sequence[seqLen], "(%02X)%02X ", nextState, sequencerState); +#endif switch (sequencerState & 0x0F) { case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - // CLR (clear data register) +// case 0x01: +// case 0x02: +// case 0x03: +// case 0x04: +// case 0x05: +// case 0x06: +// case 0x07: + // CLR (clear data register; 0) dataRegister = 0; break; case 0x08: - case 0x0C: +// case 0x0C: // NOP (no operation) break; case 0x09: @@ -1096,13 +1226,13 @@ lastPos = currentPos[activeDrive]; } break; case 0x0A: - case 0x0E: +// case 0x0E: // SR (shift right write protect bit) dataRegister >>= 1; dataRegister |= (woz.writeProtected ? 0x80 : 0x00); break; case 0x0B: - case 0x0F: +// case 0x0F: // LD (load data register from data bus) dataRegister = cpuDataBus; //if (!stopWriting) @@ -1133,6 +1263,9 @@ lastPos = currentPos[activeDrive]; dataRegister <<= 1; dataRegister |= 0x01; break; + default: + // 1-7, $C, $E, & $F are all invalid opcodes + WriteLog("Invalid LSS state encountered! (opcode: $%X [full state: $%02X])\n", sequencerState & 0x0F, sequencerState); } if (readPulse > 0) @@ -1143,7 +1276,6 @@ if (logSeq) WriteLog("\n"); } - FloppyDrive floppyDrive[2]; static uint8_t SlotIOR(uint16_t address) @@ -1187,7 +1319,6 @@ iorAddr = address; return (address & 0x01 ? ReadFloatingBus(0) : floppyDrive[0].DataRegister()); } - static void SlotIOW(uint16_t address, uint8_t byte) { uint8_t state = address & 0x0F; @@ -1233,13 +1364,15 @@ static void SlotIOW(uint16_t address, uint8_t byte) // of FloppyDrive static uint8_t SlotROM(uint16_t address) { +#ifndef TEST13 return diskROM[address]; +#else + return diskROM13[address]; +#endif } - void InstallFloppy(uint8_t slot) { SlotData disk = { SlotIOR, SlotIOW, SlotROM, 0, 0, 0 }; InstallSlotHandler(slot, &disk); } - diff --git a/src/floppydrive.h b/src/floppydrive.h index d463a79..15290d3 100644 --- a/src/floppydrive.h +++ b/src/floppydrive.h @@ -76,7 +76,9 @@ class FloppyDrive uint8_t motorOn; uint8_t activeDrive; uint8_t ioMode; + public: // temp, for testing... :-P uint8_t dataRegister; + private: uint8_t phase[2]; uint8_t headPos[2]; bool ioHappened; @@ -85,19 +87,22 @@ class FloppyDrive uint32_t currentPos[2]; uint8_t cpuDataBus; - uint8_t slSwitch; // Shift/Load soft switch - uint8_t rwSwitch; // Read/Write soft switch - uint8_t readPulse; // Disk read head "pulse" signal - uint8_t pulseClock; // Disk read head bitstream "pulse clock" + uint8_t slSwitch; // Shift/Load soft switch + uint8_t rwSwitch; // Read/Write soft switch + uint8_t readPulse; // Disk read head "pulse" signal + uint8_t pulseClock; // Disk read head bitstream "pulse clock" uint8_t sequencerState; uint32_t driveOffTimeout; uint8_t zeroBitCount; uint16_t trackLength[2]; +// uint8_t pulseTiming; // WOZ disk "optimal" pulse timing (in 0.25µs) + uint8_t window; }; // Exported functions/variables void InstallFloppy(uint8_t slot); extern FloppyDrive floppyDrive[]; +extern char sequence[]; #endif // __FLOPPY_H__ diff --git a/src/gui/diskselector.cpp b/src/gui/diskselector.cpp index 57014b5..f73e4cd 100644 --- a/src/gui/diskselector.cpp +++ b/src/gui/diskselector.cpp @@ -210,7 +210,7 @@ void DiskSelector::FindDisks(const char * path) && (strcmp(ent->d_name, ".") != 0)) { // Check to see if this is a special directory with a manifest - char buf2[0x10000]; + char buf2[0x10000 + 13]; sprintf(buf2, "%s/manifest.txt", buf); FILE * fp = fopen(buf2, "r"); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 6960a96..b0386ac 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -249,6 +249,7 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons) case 1: SpawnMessage("*** DISK #1 ***"); +#if 0 if (disk1EjectHovered && !floppyDrive[0].IsEmpty(0)) { floppyDrive[0].EjectImage(0); @@ -261,6 +262,32 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons) Config::HideWindow(); DiskSelector::ShowWindow(0); } +#else + if (disk1EjectHovered) + { + if (!floppyDrive[0].IsEmpty(0)) + { + floppyDrive[0].EjectImage(0); + SpawnMessage("*** DISK #1 EJECTED ***"); + } + } + else + { + if (disk1NewDiskHovered) + { + if (!floppyDrive[0].IsEmpty(0)) + floppyDrive[0].EjectImage(0); + + floppyDrive[0].CreateBlankImage(0); + } + else + { + // Load the disk selector + Config::HideWindow(); + DiskSelector::ShowWindow(0); + } + } +#endif break; // Disk #2 @@ -574,7 +601,7 @@ void GUI::DrawString(SDL_Renderer * renderer, int x, int y, const char * s, bool // -// N.B.: This draws a char at an abosulte X/Y position, not on a grid +// N.B.: This draws a char at an absolute X/Y position, not on a grid // void GUI::DrawStringVert(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/) { diff --git a/src/harddrive.cpp b/src/harddrive.cpp index e0fae03..8dede0d 100644 --- a/src/harddrive.cpp +++ b/src/harddrive.cpp @@ -618,7 +618,7 @@ void InstallHardDrive(uint8_t slot) { SlotData hd = { SlotIOR, SlotIOW, SlotROM, 0, SlotIOExtraR, SlotIOExtraW }; InstallSlotHandler(slot, &hd); - char fnBuf[MAX_PATH + 1]; + char fnBuf[(MAX_PATH * 2) + 1]; // If this fails to read the file, the pointer is set to NULL uint32_t size = 0, skip = (uint32_t)-1; diff --git a/src/mmu.cpp b/src/mmu.cpp index 77578d4..3264510 100644 --- a/src/mmu.cpp +++ b/src/mmu.cpp @@ -829,7 +829,9 @@ void WriteKeyStrobe(uint16_t, uint8_t) uint8_t ReadSpeaker(uint16_t) { ToggleSpeaker(); - return 0; +// return 0; + // Seems this is needed for some things... + return ReadFloatingBus(0); } void WriteSpeaker(uint16_t, uint8_t) @@ -841,7 +843,9 @@ uint8_t SwitchLCR(uint16_t address) { lcState = address & 0x0B; SwitchLC(); - return 0; +// return 0; + // Seems this is needed for some things... + return ReadFloatingBus(0); } void SwitchLCW(uint16_t address, uint8_t) @@ -929,7 +933,9 @@ uint8_t SwitchTEXTR(uint16_t address) { WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off")); textMode = (bool)(address & 0x01); - return 0; +// return 0; + // Seems this is needed for some things... + return ReadFloatingBus(0); } void SwitchTEXTW(uint16_t address, uint8_t) @@ -942,7 +948,9 @@ uint8_t SwitchMIXEDR(uint16_t address) { WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off")); mixedMode = (bool)(address & 0x01); - return 0; +// return 0; + // Seems this is needed for some things... + return ReadFloatingBus(0); } void SwitchMIXEDW(uint16_t address, uint8_t) @@ -951,6 +959,9 @@ WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off")); mixedMode = (bool)(address & 0x01); } +/* +80STORE, PAGE2, and HIRES bank switch the primary display pages, $400--$7FF and $2000--$3FFF, between motherboard RAM and auxiliary card RAM. If 80STORE is set and HIRES is reset, then PAGE2 switches between motherboard RAM and auxiliary card RAM for reading and writing in the $400--$7FF range. If 80STORE is set and HIRES is set, then PAGE2 switches between motherboard RAM and auxiliary card RAM for reading and writing in the $400--$7FF and $2000--$3FFF ranges. PAGE2 set selects auxiliary card RAM, and PAGE2 reset selects motherboard RAM. If 80STORE is reset, then RAMRD and RAMWRT will bank switch the $400-$7FF and $2000--$3FFF ranges along with the rest of the $200--$BFFF range. +*/ uint8_t SwitchPAGE2R(uint16_t address) { WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off")); @@ -960,11 +971,16 @@ WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off")); { mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]); mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]); - mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); - mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); + + if (hiRes) + { + mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); + mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); + } } - return 0; + // Seems this is needed for some things... + return ReadFloatingBus(0); } void SwitchPAGE2W(uint16_t address, uint8_t) @@ -976,8 +992,12 @@ WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off")); { mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]); mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]); - mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); - mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); + + if (hiRes) + { + mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); + mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]); + } } } @@ -985,7 +1005,9 @@ uint8_t SwitchHIRESR(uint16_t address) { WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off")); hiRes = (bool)(address & 0x01); - return 0; +// return 0; + // Seems this is needed for some things... + return ReadFloatingBus(0); } void SwitchHIRESW(uint16_t address, uint8_t) @@ -1001,7 +1023,9 @@ WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? " if (ioudis) dhires = !((bool)(address & 0x01)); - return 0; +// return 0; + // Seems this is needed for some things... + return ReadFloatingBus(0); } void SwitchDHIRESW(uint16_t address, uint8_t) diff --git a/src/v65c02.cpp b/src/v65c02.cpp index f0a589d..82158c8 100644 --- a/src/v65c02.cpp +++ b/src/v65c02.cpp @@ -28,6 +28,7 @@ #ifdef __DEBUG__ #include #include "dis65c02.h" +#include "floppydrive.h" #include "log.h" #endif @@ -2218,7 +2219,6 @@ static void Op__(void) regs->cpuFlags |= V65C02_STATE_ILLEGAL_INST; } - // // Ok, the exec_op[] array is globally defined here basically to save // a LOT of unnecessary typing. Sure it's ugly, but hey, it works! @@ -2283,7 +2283,6 @@ V65C02REGS btQueue[BACKTRACE_SIZE]; uint8_t btQueueInst[BACKTRACE_SIZE][4]; #endif - // // Function to execute 65C02 for "cycles" cycles // @@ -2297,6 +2296,196 @@ void Execute65C02(V65C02REGS * context, uint32_t cycles) while (regs->clock < endCycles) { +// Ultima I (WTF? This *used* to work! >:-U) [Now it does... :-P] +// Turns out it was a problem with PAGE2 changing too much (it ignored the HIRES switch when switching memory, causing code at $2141 to be swapped out with zeroes). +/*if (regs->pc == 0xC311) + dumpDis = true; +else if (regs->pc == 0x2141) + dumpDis = false;*/ + +#if 1 +// Bard's Tale II +static bool hitGo = false; + +if (regs->pc == 0xA000) +{ + dumpDis = true; + hitGo = true; +} + +// $FCA8 also needs to be silenced too ($FCB3 is exit point) +if (regs->pc == 0xA181 && hitGo) +{ + dumpDis = false; + WriteLog("*** BT2 DELAY\n"); +} +else if (regs->pc == 0xA18B && hitGo) +{ + dumpDis = true; +} +else if (regs->pc == 0xFCA8 && hitGo) +{ + dumpDis = false; + WriteLog("*** MONITOR DELAY ($FCA8)\n"); +} +else if (regs->pc == 0xFCB3 && hitGo) +{ + dumpDis = true; +} +else if (regs->pc == 0xBD11 && hitGo) +{ + dumpDis = false; + WriteLog("*** BT2 DELAY $BD11\n"); +} +else if (regs->pc == 0xDB1E && hitGo) +{ + dumpDis = true; +} +else if (regs->pc == 0xA003 && hitGo) +{ + dumpDis = false; + WriteLog("*** BT2 CHECK FOR $B7s\n"); +} +else if (regs->pc == 0xA063 && hitGo) +{ + dumpDis = true; +} +else if (regs->pc == 0xA0FE && hitGo) +{ + dumpDis = false; + WriteLog("*** BT2 CHECK FOR $D5 $AA $96 HEADER\n"); +} +else if (regs->pc == 0xA112 && hitGo) +{ + dumpDis = true; + WriteLog("*** BT2 LOOK FOR HEADER FAILED\n"); +} +else if (regs->pc == 0xA14B && hitGo) +{ + dumpDis = true; +} +else if (regs->pc == 0xA254 && hitGo) +{ + // This is where it hits a BRK and goes BOOM + dumpDis = false; + hitGo = false; +} +else if (regs->pc == 0xA155) +{ + WriteLog("*** $A2E2 is %02X...\n", regs->RdMem(0xA2E2)); +} +else if (regs->pc == 0xA1C2) +{ + static char bcName[13][5] = { "JMP", "JMPA", "BNE", "LDI", "JSR", "LDA", "SUB", "STA", "RTS", "JMPA", "INC", "CRSH", "ILDA" }; + static int bcLen[13] = { 2, 2, 2, 1, 2, 2, 1, 2, 0, 2, 2, 0, 2 }; + + uint16_t addr = RdMemWZP(0x52) + regs->y; + uint8_t bytecode = regs->RdMem(addr); + uint16_t bcAddr = ((regs->RdMem(addr + 2) ^ 0xD9) << 8) | (regs->RdMem(addr + 1) ^ 0x03); + uint8_t bcVal = regs->RdMem(addr + 1) ^ 0x4C; + + WriteLog("\n*** bc %04X: %s ", addr, bcName[bytecode]); + + if (bcLen[bytecode] == 1) + WriteLog("$%02X", bcVal); + else if (bcLen[bytecode] == 2) + WriteLog("$%04X", bcAddr); + + WriteLog("\n\n"); +} +#endif + +#if 0 +// Border Zone timing... +static bool inDelay1 = false; +static bool inDelay2 = false; +static bool inRead1 = false; +static bool hitGo = false; +if (regs->pc == 0xF0B1) + WriteLog("*** $F09C ($6F,70) -> $%02X%02X\n", regs->RdMem(0x70), regs->RdMem(0x6F)); + +if (regs->pc == 0xC8F2) + hitGo = true; + +// Delay is $D20D to $D21D... +if (regs->pc == 0xD20D && hitGo && !inDelay1) +{ + dumpDis = false; + inDelay1 = true; + WriteLog("*** DELAY\n"); +} +else if (regs->pc == 0xD21D && inDelay1) +{ + dumpDis = true; + inDelay1 = false; +} + +// Next delay starts @ $D356 - $D36A +else if (regs->pc == 0xD356 && hitGo && !inDelay2) +{ + dumpDis = false; + inDelay2 = true; + WriteLog("*** DELAY #2\n"); +} +else if (regs->pc == 0xD36A && inDelay2) +{ + dumpDis = true; + inDelay2 = false; +} +else if (regs->pc == 0xD486 && hitGo && !inRead1) +{ + dumpDis = false; + inRead1 = true; + WriteLog("\n*** FAST READ ROUTINE (!!!)\n\n"); +} +else if (regs->pc == 0xD4B1 && inRead1) +{ + dumpDis = true; + inRead1 = false; +} +#endif +#if 0 +// 13-sector disk debugging +// start with the slot ROM +static bool inDelay = false; +static bool inBell = false; +static bool inReadSector = false; +static bool inSlotROM = false; +if (regs->pc == 0xFCA8)// && !inSlotROM)//!inBell && !inReadSector) +{ + dumpDis = false; + inDelay = true; + WriteLog("*** DELAY\n"); +} +else if (regs->pc == 0xFCB3 && inDelay && inSlotROM)//&& !inBell && !inReadSector) +{ + dumpDis = true; + inDelay = false; +} +if (regs->pc == 0xFBD9) +{ + dumpDis = false; + inBell = true; + WriteLog("*** BELL1\n"); +} +else if (regs->pc == 0xFBEF && inBell) +{ +// dumpDis = true; + inBell = false; +} +//else if (regs->pc == 0xC664) +else if (regs->pc == 0xC663) +{ + dumpDis = true; + inSlotROM = true; + WriteLog("*** DISK @ $C600\n"); +} +else if (regs->pc == 0x801) +{ + WriteLog("*** DISK @ $801\n"); + dumpDis = true; +} +#endif // Hard disk debugging #if 0 if (first && (regs->pc == 0x801)) @@ -2651,6 +2840,10 @@ btQueuePtr = (btQueuePtr + 1) % BACKTRACE_SIZE; #endif #endif #ifdef __DEBUG__ +static uint16_t spc, ppc = 0; +static bool seenHi = false; +static uint64_t oldClock = 0; +spc = regs->pc; static char disbuf[80]; if (dumpDis) { @@ -2662,9 +2855,15 @@ if (dumpDis) #if 0 if (opcode == 0) +//if (regs->pc == 0xA255) +//static bool seenBT = false; +//if (hitGo && !seenBT) +//if (dobacktrace) { +//seenBT = true; static char disbuf[80]; - uint32_t btStart = btQueuePtr - 12 + (btQueuePtr < 12 ? BACKTRACE_SIZE : 0); +// uint32_t btStart = btQueuePtr - 12 + (btQueuePtr < 12 ? BACKTRACE_SIZE : 0); + uint32_t btStart = 0; for(uint32_t i=btStart; icpuFlags & V65C02_STATE_ILLEGAL_INST)) //instCount[opcode]++; + if (regs->Timer) + regs->Timer(CPUCycles[opcode]); + + uint64_t clockSave = regs->clock + CPUCycles[opcode]; + // We need this because the opcode function could add 1 or 2 cycles - // which aren't accounted for in CPUCycles[]. - uint64_t clockSave = regs->clock; + // to regs->clock which aren't accounted for in CPUCycles[]. +// uint64_t clockSave = regs->clock; // Execute that opcode... exec_op[opcode](); regs->clock += CPUCycles[opcode]; // Tell the timer function (if any) how many PHI2s have elapsed... - if (regs->Timer) +// if (regs->Timer) + if (regs->Timer && (regs->clock - clockSave) > 0) regs->Timer(regs->clock - clockSave); #ifdef __DEBUG__ if (dumpDis) - WriteLog(" [SP=01%02X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X](%d)\n", +{ + WriteLog(" [SP=01%02X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X](%d)[%02X]\n",//<%s>\n", regs->sp, (regs->cc & FLAG_N ? "N" : "-"), (regs->cc & FLAG_V ? "V" : "-"), (regs->cc & FLAG_B ? "B" : "-"), (regs->cc & FLAG_D ? "D" : "-"), (regs->cc & FLAG_I ? "I" : "-"), (regs->cc & FLAG_Z ? "Z" : "-"), - (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y, regs->clock - clockSave); + (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y, regs->clock - clockSave + CPUCycles[opcode], floppyDrive[0].dataRegister);//, sequence); + sequence[0] = 0; + + if (((spc == 0xD4D1) || (spc == 0xD4E2)) && (floppyDrive[0].dataRegister & 0x80)) + seenHi = true; + + if ((spc == 0xD4CE) || (spc == 0xD4DF)) + { + WriteLog(" (%d)\n", regs->clock - oldClock); + + if ((regs->y & 0x80) == 0 && seenHi && ((ppc == 0xD4D1) || (ppc == 0xD4E2))) + WriteLog("\n***** MISS! *****\n\n"); + + seenHi = false; + oldClock = regs->clock; + } + + ppc = spc; +} #endif #ifdef __DEBUGMON__ @@ -2777,4 +3001,3 @@ WriteLog("Clock=$%X\n", regs->clock); // to exit from it. regs->overflow = regs->clock - endCycles; } -