From 604bf07b4f0a5cce5fd4164c921ebad1e1503e47 Mon Sep 17 00:00:00 2001 From: Brad Grantham Date: Sun, 27 Nov 2016 17:10:03 -0800 Subject: [PATCH] Partially implement floppy disks Floppy disks partially work: apple2e -noaudio -fast -diskII disk2romfile {floppy1name,"none"} {floppy2name,"none"} apple2e.rom Import nybblizer from Shamus but only DOS Support floppy hardware track motor stepper enable read Add -noaudio (otherwise execution is throttled by waits once audio buffer is filled) Only read ROM at D000 and E000 if C08X_read_RAM isn't turned on RAMRD, RAMWRT, and ALTZP are implemented, note them as such in constructor Correctly decode C08X soft switches Implement a bunch more instructions INC abs LDY abs, X SBC abs, Y ADC abs ASL abs LSR abs, X ORA abs ORA abs, X AND abs, Y STA (ind, X) LDX abs CPX abs EOR (ind, X) EOR abs EOR abs, X STX abs Throttle to actual number of clocks per instruction Temporarily enable "Fake6502" instead of my CPU because there's an obvious difference Better blink speed in interface.cpp --- apple2e.cpp | 502 +++++++++++++++++++++++++++++++++++++++++++++----- fake6502.h | 2 + interface.cpp | 4 +- switches.txt | 2 + 4 files changed, 459 insertions(+), 51 deletions(-) diff --git a/apple2e.cpp b/apple2e.cpp index a1bd96e..d482e18 100644 --- a/apple2e.cpp +++ b/apple2e.cpp @@ -28,6 +28,8 @@ const unsigned int DEBUG_DECODE = 0x04; const unsigned int DEBUG_STATE = 0x08; const unsigned int DEBUG_RW = 0x10; const unsigned int DEBUG_BUS = 0x20; +const unsigned int DEBUG_FLOPPY = 0x40; +const unsigned int DEBUG_SWITCH = 0x80; volatile unsigned int debug = DEBUG_ERROR | DEBUG_WARN ; // | DEBUG_DECODE | DEBUG_STATE | DEBUG_RW; volatile bool exit_on_banking = false; @@ -162,22 +164,157 @@ struct backed_region : region const region io_region = {"io", 0xC000, 0x100}; +unsigned char floppy_header[21] = { + 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xDE, 0xAA, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xD5, 0xAA, 0xAD }; +unsigned char floppy_doSector[16] = { + 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF }; +unsigned char floppy_poSector[16] = { + 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF }; + +void floppy_NybblizeImage(unsigned char *image, unsigned char *nybblized) +{ + // Format of a sector is header (23) + nybbles (343) + footer (30) = 396 + // (short by 20 bytes of 416 [413 if 48 byte header is one time only]) + // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector + // (not incl. 64 byte track marker) + + static unsigned char footer[48] = { + 0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + static unsigned char diskbyte[0x40] = { + 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, + 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, + 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3, + 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, + 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, + 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, + 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; + + memset(nybblized, 0xFF, 232960); // Doesn't matter if 00s or FFs... + + unsigned char *p = nybblized; + + for(unsigned char trk=0; trk<35; trk++) + { + memset(p, 0xFF, 64); // Write gap 1, 64 bytes (self-sync) + p += 64; + + for(unsigned char sector=0; sector<16; sector++) + { + memcpy(p, floppy_header, 21); // Set up the sector header + + p[5] = ((trk >> 1) & 0x55) | 0xAA; + p[6] = (trk & 0x55) | 0xAA; + p[7] = ((sector >> 1) & 0x55) | 0xAA; + p[8] = (sector & 0x55) | 0xAA; + p[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA; + p[10] = ((trk ^ sector ^ 0xFE) & 0x55) | 0xAA; + + p += 21; + unsigned char * bytes = image; + + // if (diskType[driveNum] == DT_DOS33) + bytes += (floppy_doSector[sector] * 256) + (trk * 256 * 16); + // else if (diskType[driveNum] == DT_PRODOS) + // bytes += (poSector[sector] * 256) + (trk * 256 * 16); + // else + // bytes += (sector * 256) + (trk * 256 * 16); + + // Convert the 256 8-bit bytes into 342 6-bit bytes. + + for(int i=0; i<0x56; i++) + { + p[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7) + | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5) + | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5) + | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3) + | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3) + | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1); + } + + p[0x54] &= 0x3F; + p[0x55] &= 0x3F; + memcpy(p + 0x56, bytes, 256); + + // XOR the data block with itself, offset by one byte, + // creating a 343rd byte which is used as a cheksum. + + p[342] = 0x00; + + for(int i=342; i>0; i--) + p[i] = p[i] ^ p[i - 1]; + + // Using a lookup table, convert the 6-bit bytes into disk bytes. + + for(int i=0; i<343; i++) + p[i] = diskbyte[p[i] >> 2]; + p += 343; + + // Done with the nybblization, now for the epilogue... + + memcpy(p, footer, 48); + p += 48; + } + } +} + + struct DISKIIboard : board_base { - // CA0 EQU $C0E0 ;stepper phase 0 / control line 0 - // CA1 EQU $C0E2 ;stepper phase 1 / control line 1 - // CA2 EQU $C0E4 ;stepper phase 2 / control line 2 - // LSTRB EQU $C0E6 ;stepper phase 3 / control strobe - // ENABLE EQU $C0E8 ;disk drive off/on - // SELECT EQU $C0EA ;select drive 1/2 - // Q6 EQU $C0EC - // Q7 EQU $C0EE + const unsigned int CA0 = 0xC0E0; // stepper phase 0 / control line 0 + const unsigned int CA1 = 0xC0E2; // stepper phase 1 / control line 1 + const unsigned int CA2 = 0xC0E4; // stepper phase 2 / control line 2 + const unsigned int CA3 = 0xC0E6; // stepper phase 3 / control strobe + const unsigned int ENABLE = 0xC0E8; // disk drive off/on + const unsigned int SELECT = 0xC0EA; // select drive 1/2 + const unsigned int Q6L = 0xC0EC; // IO strobe for read + const unsigned int Q6H = 0xC0ED; // IO strobe for write + const unsigned int Q7L = 0xC0EE; // IO strobe for clear + const unsigned int Q7H = 0xC0EF; // IO strobe for shift + + map io = { + {0xC0E0, "CA0OFF"}, + {0xC0E1, "CA0ON"}, + {0xC0E2, "CA1OFF"}, + {0xC0E3, "CA1ON"}, + {0xC0E4, "CA2OFF"}, + {0xC0E5, "CA2ON"}, + {0xC0E6, "CA3OFF"}, + {0xC0E7, "CA3ON"}, + {0xC0E8, "DISABLE"}, + {0xC0E9, "ENABLE"}, + {0xC0EA, "SELECT0"}, + {0xC0EB, "SELECT1"}, + {0xC0EC, "Q6L"}, + {0xC0ED, "Q6H"}, + {0xC0EE, "Q7L"}, + {0xC0EF, "Q7H"}, + }; backed_region rom_C600 = {"rom_C600", 0xC600, 0x0100, ROM, nullptr, [&]{return true;}}; unsigned char floppy_image[2][143360]; + unsigned char floppy_nybblized[2][232960]; + const unsigned int bytes_per_nybblized_track = 6656; bool floppy_present[2]; + int drive_selected = 0; + bool drive_motor_enabled[2]; + enum {READ, WRITE} head_mode = READ; + unsigned char data_latch = 0x00; + int head_stepper_phase[4] = {0, 0, 0, 0}; + int head_stepper_most_recent_phase = 0; + int track_number = 0; // physical track number - DOS and ProDOS only use even tracks + unsigned int track_byte = 0; + void set_floppy(int number, char *name) // number 0 or 1; name = NULL to eject { floppy_present[number] = false; @@ -186,6 +323,7 @@ struct DISKIIboard : board_base throw "Couldn't read floppy"; floppy_present[number] = true; + floppy_NybblizeImage(floppy_image[number], floppy_nybblized[number]); } } @@ -196,14 +334,109 @@ struct DISKIIboard : board_base set_floppy(1, floppy1_name); } - virtual bool write(int addr, unsigned char data) { return false; } + unsigned char read_next_nybblized_byte() + { + if(head_mode != READ || !drive_motor_enabled[drive_selected] || !floppy_present[drive_selected]) + return 0x00; + int i = track_byte; + track_byte = (track_byte + 1) % bytes_per_nybblized_track; + return floppy_nybblized[drive_selected][(track_number / 2) * bytes_per_nybblized_track + i]; + } + + void control_track_motor(unsigned int addr) + { + int phase = (addr & 0x7) >> 1; + int state = addr & 0x1; + head_stepper_phase[phase] = state; + if(debug & DEBUG_FLOPPY) printf("stepper %04X, phase %d, state %d, so stepper motor state now: %d, %d, %d, %d\n", + addr, phase, state, + head_stepper_phase[0], head_stepper_phase[1], + head_stepper_phase[2], head_stepper_phase[3]); + if(state == 1) { // turn stepper motor phase on + if(head_stepper_most_recent_phase == (((phase - 1) + 4) % 4)) { // stepping up + track_number = min(track_number + 1, 70); + if(debug & DEBUG_FLOPPY) printf("track number now %d\n", track_number); + } else if(head_stepper_most_recent_phase == ((phase + 1) % 4)) { // stepping down + track_number = max(0, track_number - 1); + if(debug & DEBUG_FLOPPY) printf("track number now %d\n", track_number); + } else if(head_stepper_most_recent_phase == phase) { // unexpected condition + if(debug & DEBUG_FLOPPY) printf("track head stepper no change\n"); + } else { // unexpected condition + if(debug & DEBUG_WARN) fprintf(stderr, "unexpected track stepper motor state: %d, %d, %d, %d\n", + head_stepper_phase[0], head_stepper_phase[1], + head_stepper_phase[2], head_stepper_phase[3]); + if(debug & DEBUG_WARN) fprintf(stderr, "most recent phase: %d\n", head_stepper_most_recent_phase); + } + head_stepper_most_recent_phase = phase; + } + } + + virtual bool write(int addr, unsigned char data) + { + if(addr < 0xC0E0 || addr > 0xC0EF) + return false; + if(debug & DEBUG_RW) printf("DISK II unhandled write of %02X to %04X (%s)\n", data, addr, io[addr].c_str()); + return false; + } virtual bool read(int addr, unsigned char &data) { if(rom_C600.read(addr, data)) { - if(debug & DEBUG_RW) printf("DiskII read 0x%04X -> 0x%02X\n", addr, data); + if(debug & DEBUG_RW) printf("DiskII read 0x%04X -> %02X\n", addr, data); return true; } - return false; + + if(addr < 0xC0E0 || addr > 0xC0EF) { + return false; + } + + if(addr >= CA0 && addr <= (CA3 + 1)) { + if(debug & DEBUG_FLOPPY) printf("floppy control track motor\n"); + control_track_motor(addr); + data = 0; + return true; + } else if(addr == Q6L) { // 0xC0EC + data = read_next_nybblized_byte(); + if(debug & DEBUG_FLOPPY) printf("floppy read byte : %02X\n", data); + return true; + } else if(addr == Q6H) { // 0xC0ED + if(debug & DEBUG_FLOPPY) printf("floppy read latch\n"); + data = data_latch; // XXX do something with the latch - e.g. set write-protect bit + data = 0; + return true; + } else if(addr == Q7L) { // 0xC0EE + if(debug & DEBUG_FLOPPY) printf("floppy set read\n"); + head_mode = READ; + data = 0; + return true; + } else if(addr == Q7H) { // 0xC0EF + if(debug & DEBUG_FLOPPY) printf("floppy set write\n"); + head_mode = WRITE; + data = 0; + return true; + } else if(addr == SELECT) { + if(debug & DEBUG_FLOPPY) printf("floppy select first drive\n"); + drive_selected = 0; + return true; + } else if(addr == SELECT + 1) { + if(debug & DEBUG_FLOPPY) printf("floppy select second drive\n"); + drive_selected = 1; + return true; + } else if(addr == ENABLE) { + if(debug & DEBUG_FLOPPY) printf("floppy switch off\n"); + drive_motor_enabled[drive_selected] = false; + // go disable reading + // disable other drive? + return true; + } else if(addr == ENABLE + 1) { + if(debug & DEBUG_FLOPPY) printf("floppy switch on\n"); + drive_motor_enabled[drive_selected] = true; + // go enable reading + // disable other drive? + return true; + } + printf("DISK II unhandled read from %04X (%s)\n", addr, io[addr].c_str()); + data = 0; + return true; } virtual void reset(void) {} }; @@ -218,9 +451,9 @@ struct MAINboard : board_base SoftSwitch* switches_by_address[256]; SoftSwitch CXROM {"CXROM", 0xC006, 0xC007, 0xC015, false, switches, true}; SoftSwitch STORE80 {"STORE80", 0xC000, 0xC001, 0xC018, false, switches, true}; - SoftSwitch RAMRD {"RAMRD", 0xC002, 0xC003, 0xC013, false, switches}; - SoftSwitch RAMWRT {"RAMWRT", 0xC004, 0xC005, 0xC014, false, switches}; - SoftSwitch ALTZP {"ALTZP", 0xC008, 0xC009, 0xC016, false, switches}; + SoftSwitch RAMRD {"RAMRD", 0xC002, 0xC003, 0xC013, false, switches, true}; + SoftSwitch RAMWRT {"RAMWRT", 0xC004, 0xC005, 0xC014, false, switches, true}; + SoftSwitch ALTZP {"ALTZP", 0xC008, 0xC009, 0xC016, false, switches, true}; SoftSwitch C3ROM {"C3ROM", 0xC00A, 0xC00B, 0xC017, false, switches, true}; SoftSwitch ALTCHAR {"ALTCHAR", 0xC00E, 0xC00F, 0xC01E, false, switches}; SoftSwitch VID80 {"VID80", 0xC00C, 0xC00D, 0xC01F, false, switches}; @@ -235,9 +468,6 @@ struct MAINboard : board_base backed_region szp = {"szp", 0x0000, 0x0200, RAM, ®ions, [&](){return !ALTZP;}}; // stack and zero page backed_region aszp = {"aszp", 0x0000, 0x0200, RAM, ®ions, [&](){return ALTZP;}}; // alternate stack and zero page - backed_region rom_D000 = {"rom_D000", 0xD000, 0x1000, ROM, ®ions, [&]{return true;}}; - backed_region rom_E000 = {"rom_E000", 0xE000, 0x2000, ROM, ®ions, [&]{return true;}}; - bool internal_C800_ROM_selected; backed_region rom_C100 = {"rom_C100", 0xC100, 0x0200, ROM, ®ions, [&]{return CXROM;}}; backed_region rom_C300 = {"rom_C300", 0xC300, 0x0100, ROM, ®ions, [&]{return CXROM || (!CXROM && !C3ROM);}}; @@ -281,12 +511,15 @@ struct MAINboard : board_base bool C08X_read_RAM; bool C08X_write_RAM; + backed_region rom_D000 = {"rom_D000", 0xD000, 0x1000, ROM, ®ions, [&]{return !C08X_read_RAM;}}; + backed_region rom_E000 = {"rom_E000", 0xE000, 0x2000, ROM, ®ions, [&]{return !C08X_read_RAM;}}; + backed_region ram1_main_D000 = {"ram1_main_D000", 0xD000, 0x1000, RAM, ®ions, [&]{return !ALTZP && C08X_read_RAM && (C08X_bank == BANK1);}, [&]{return !ALTZP && C08X_write_RAM && (C08X_bank == BANK1);}}; backed_region ram2_main_D000 = {"ram2_main_D000", 0xD000, 0x1000, RAM, ®ions, [&]{return !ALTZP && C08X_read_RAM && (C08X_bank == BANK2);}, [&]{return !ALTZP && C08X_write_RAM && (C08X_bank == BANK2);}}; backed_region ram_main_E000 = {"ram1_main_E000", 0xE000, 0x2000, RAM, ®ions, [&]{return C08X_read_RAM;}, [&]{return !ALTZP && C08X_write_RAM;}}; backed_region ram1_main_D000_x = {"ram1_main_D000_x", 0xD000, 0x1000, RAM, ®ions, [&]{return ALTZP && C08X_read_RAM && (C08X_bank == BANK1);}, [&]{return ALTZP && C08X_write_RAM && (C08X_bank == BANK1);}}; backed_region ram2_main_D000_x = {"ram2_main_D000_x", 0xD000, 0x1000, RAM, ®ions, [&]{return ALTZP && C08X_read_RAM && (C08X_bank == BANK2);}, [&]{return ALTZP && C08X_write_RAM && (C08X_bank == BANK2);}}; - backed_region ram_main_E000_x = {"ram1_main_E000_x", 0xE000, 0x2000, RAM, ®ions, [&]{return C08X_read_RAM;}, [&]{return ALTZP && C08X_write_RAM;}}; + backed_region ram_main_E000_x = {"ram1_main_E000_x", 0xE000, 0x2000, RAM, ®ions, [&]{return ALTZP && C08X_read_RAM;}, [&]{return ALTZP && C08X_write_RAM;}}; set ignore_mmio = {0xC058, 0xC05A, 0xC05D, 0xC05F, 0xC061, 0xC062}; set banking_read_switches = { @@ -318,6 +551,7 @@ struct MAINboard : board_base audio_buffer[i % audio_buffer_size] = speaker_energized ? 128 - 32 : 128 + 32; if(i - audio_buffer_start_sample == audio_buffer_size - 1) { audio_flush(audio_buffer, audio_buffer_size); + audio_buffer_start_sample = i + 1; } } @@ -406,19 +640,19 @@ struct MAINboard : board_base if(sw != NULL) { if(addr == sw->read_address) { data = sw->enabled ? 0x80 : 0x00; - if(debug & DEBUG_RW) printf("Read status of %s = %02X\n", sw->name.c_str(), data); + if(debug & DEBUG_SWITCH) printf("Read status of %s = %02X\n", sw->name.c_str(), data); return true; } else if(sw->read_also_changes && addr == sw->set_address) { if(!sw->implemented) { printf("%s ; set is unimplemented\n", sw->name.c_str()); fflush(stdout); exit(0); } data = 0xff; sw->enabled = true; - if(debug & DEBUG_RW) printf("Set %s\n", sw->name.c_str()); + if(debug & DEBUG_SWITCH) printf("Set %s\n", sw->name.c_str()); return true; } else if(sw->read_also_changes && addr == sw->clear_address) { if(!sw->implemented) { printf("%s ; unimplemented\n", sw->name.c_str()); fflush(stdout); exit(0); } data = 0xff; sw->enabled = false; - if(debug & DEBUG_RW) printf("Clear %s\n", sw->name.c_str()); + if(debug & DEBUG_SWITCH) printf("Clear %s\n", sw->name.c_str()); return true; } } @@ -429,19 +663,22 @@ struct MAINboard : board_base } if((addr & 0xFFF0) == 0xC080) { C08X_bank = ((addr >> 3) & 1) ? BANK1 : BANK2; - C08X_write_RAM = (addr >> 2) & 1; - C08X_read_RAM = ((addr >> 1) & 1) ^ C08X_write_RAM; // RAM flag is inverted by ROM! - if(debug & DEBUG_RW) printf("write C08X switch, return 0x%02X\n", data); + C08X_write_RAM = addr & 1; + int read_ROM = ((addr >> 1) & 1) ^ C08X_write_RAM; + C08X_read_RAM = !read_ROM; + if(debug & DEBUG_SWITCH) printf("write %04X switch, %s, %d write_RAM, %d read_RAM\n", addr, (C08X_bank == BANK1) ? "BANK1" : "BANK2", C08X_write_RAM, C08X_read_RAM); + data = 0x00; return true; } if(addr == 0xC011) { data = (C08X_bank == BANK2) ? 0x80 : 0x0; - if(debug & DEBUG_RW) printf("read BSRBANK2, return 0x%02X\n", data); + data = 0x00; + if(debug & DEBUG_SWITCH) printf("read BSRBANK2, return 0x%02X\n", data); return true; } if(addr == 0xC012) { data = C08X_read_RAM ? 0x80 : 0x0; - if(debug & DEBUG_RW) printf("read BSRREADRAM, return 0x%02X\n", data); + if(debug & DEBUG_SWITCH) printf("read BSRREADRAM, return 0x%02X\n", data); return true; } if(addr == 0xC000) { @@ -481,11 +718,11 @@ struct MAINboard : board_base } } if((addr & 0xFF00) == 0xC300) { - if(debug & DEBUG_RW) printf("read 0x%04X, enabling internal C800 ROM\n", addr); + if(debug & DEBUG_SWITCH) printf("read 0x%04X, enabling internal C800 ROM\n", addr); internal_C800_ROM_selected = true; } if(addr == 0xCFFF) { - if(debug & DEBUG_RW) printf("read 0xCFFF, disabling internal C800 ROM\n"); + if(debug & DEBUG_SWITCH) printf("read 0xCFFF, disabling internal C800 ROM\n"); internal_C800_ROM_selected = false; } if(debug & DEBUG_WARN) printf("unhandled memory read at %04X\n", addr); @@ -534,13 +771,13 @@ struct MAINboard : board_base if(!sw->implemented) { printf("%s ; set is unimplemented\n", sw->name.c_str()); fflush(stdout); exit(0); } data = 0xff; sw->enabled = true; - if(debug & DEBUG_RW) printf("Set %s\n", sw->name.c_str()); + if(debug & DEBUG_SWITCH) printf("Set %s\n", sw->name.c_str()); return true; } else if(addr == sw->clear_address) { // if(!sw->implemented) { printf("%s ; unimplemented\n", sw->name.c_str()); fflush(stdout); exit(0); } data = 0xff; sw->enabled = false; - if(debug & DEBUG_RW) printf("Clear %s\n", sw->name.c_str()); + if(debug & DEBUG_SWITCH) printf("Clear %s\n", sw->name.c_str()); return true; } } @@ -902,6 +1139,13 @@ struct CPU6502 break; } + case 0xEE: { // INC abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + set_flags(N | Z, m = bus.read(addr) + 1); + bus.write(addr, m); + break; + } + case 0xE6: { // INC zpg int zpg = read_pc_inc(bus); set_flags(N | Z, m = bus.read(zpg) + 1); @@ -1072,6 +1316,14 @@ struct CPU6502 break; } + case 0xBC: { // LDY abs, X + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + set_flags(N | Z, y = bus.read(addr + x)); + if((addr + x) / 256 != addr / 256) + clk++; + break; + } + case 0xBD: { // LDA abs, X int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; set_flags(N | Z, a = bus.read(addr + x)); @@ -1113,6 +1365,18 @@ struct CPU6502 break; } + case 0xF9: { // SBC abs, Y + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256 + y; + if((addr - y) / 256 != addr / 256) + clk++; + unsigned char m = bus.read(addr); + int borrow = isset(C) ? 0 : 1; + flag_change(C, !(a < (m + borrow))); + flag_change(V, sbc_overflow(a, m, borrow)); + set_flags(N | Z, a = a - (m + borrow)); + break; + } + case 0xFD: { // SBC abs, X int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256 + x; if((addr - x) / 256 != addr / 256) @@ -1144,6 +1408,16 @@ struct CPU6502 break; } + case 0x6D: { // ADC abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256 + y; + m = bus.read(addr); + int carry = isset(C) ? 1 : 0; + flag_change(C, (int)(a + m + carry) > 0xFF); + flag_change(V, adc_overflow(a, m, carry)); + set_flags(N | Z, a = a + m + carry); + break; + } + case 0x65: { // ADC unsigned char zpg = read_pc_inc(bus); m = bus.read(zpg); @@ -1175,6 +1449,15 @@ struct CPU6502 break; } + case 0x0E: { // ASL abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr); + flag_change(C, m & 0x80); + set_flags(N | Z, m = m << 1); + bus.write(addr, m); + break; + } + case 0x06: { // ASL unsigned char zpg = read_pc_inc(bus); m = bus.read(zpg); @@ -1199,6 +1482,15 @@ struct CPU6502 break; } + case 0x5E: { // LSR abs, X + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr + x); + flag_change(C, m & 0x01); + set_flags(N | Z, m = m >> 1); + bus.write(addr + x, m); + break; + } + case 0x46: { // LSR unsigned char zpg = read_pc_inc(bus); m = bus.read(zpg); @@ -1242,6 +1534,22 @@ struct CPU6502 break; } + case 0x0D: { // ORA abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr); + set_flags(N | Z, a = a | m); + break; + } + + case 0x1D: { // ORA abs, X + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr + x); + if((addr + x) / 256 != addr / 256) + clk++; + set_flags(N | Z, a = a | m); + break; + } + case 0x11: { // ORA (ind), Y unsigned char zpg = read_pc_inc(bus); int addr = bus.read(zpg) + bus.read((zpg + 1) & 0xFF) * 256 + y; @@ -1252,32 +1560,40 @@ struct CPU6502 break; } - case 0x05: { // ORA + case 0x05: { // ORA zpg unsigned char zpg = read_pc_inc(bus); m = bus.read(zpg); set_flags(N | Z, a = a | m); break; } - case 0x09: { // ORA + case 0x09: { // ORA imm unsigned char imm = read_pc_inc(bus); set_flags(N | Z, a = a | imm); break; } - case 0x2D: { // AND + case 0x39: { // AND abs, y + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + set_flags(N | Z, a = a & bus.read(addr + y)); + if((addr + y) / 256 != addr / 256) + clk++; + break; + } + + case 0x2D: { // AND abs int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; set_flags(N | Z, a = a & bus.read(addr)); break; } - case 0x25: { // AND + case 0x25: { // AND zpg unsigned char zpg = read_pc_inc(bus); set_flags(N | Z, a = a & bus.read(zpg)); break; } - case 0x29: { // AND + case 0x29: { // AND imm unsigned char imm = read_pc_inc(bus); set_flags(N | Z, a = a & imm); break; @@ -1347,7 +1663,7 @@ struct CPU6502 break; } - case 0x9D: { // STA + case 0x9D: { // STA abs, x int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; bus.write(addr + x, a); break; @@ -1366,6 +1682,13 @@ struct CPU6502 break; } + case 0x81: { // STA (ind, X) + unsigned char zpg = (read_pc_inc(bus) + x) & 0xFF; + int addr = bus.read(zpg) + bus.read((zpg + 1) & 0xFF) * 256; + bus.write(addr, a); + break; + } + case 0x8D: { // STA int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; bus.write(addr, a); @@ -1406,6 +1729,12 @@ struct CPU6502 break; } + case 0xAE: { // LDX abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + set_flags(N | Z, x = bus.read(addr)); + break; + } + case 0xBE: { // LDX int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256 + y; if((addr - y) / 256 != addr / 256) @@ -1456,7 +1785,7 @@ struct CPU6502 break; } - case 0xCC: { // CPY + case 0xCC: { // CPY abs int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; m = bus.read(addr); flag_change(C, m <= y); @@ -1464,6 +1793,14 @@ struct CPU6502 break; } + case 0xEC: { // CPX abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr); + flag_change(C, m <= x); + set_flags(N | Z, m = x - m); + break; + } + case 0xE0: { // CPX unsigned char imm = read_pc_inc(bus); flag_change(C, imm <= x); @@ -1478,6 +1815,39 @@ struct CPU6502 break; } + case 0x41: { // EOR (ind, X) + unsigned char zpg = (read_pc_inc(bus) + x) & 0xFF; + int addr = bus.read(zpg) + bus.read((zpg + 1) & 0xFF) * 256; + m = bus.read(addr); + set_flags(N | Z, a = a ^ m); + break; + } + + case 0x4D: { // EOR abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr); + set_flags(N | Z, a = a ^ m); + break; + } + + case 0x5D: { // EOR abs, X + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr + x); + if((addr + x) / 256 != addr / 256) + clk++; + set_flags(N | Z, a = a ^ m); + break; + } + + case 0x59: { // EOR abs, Y + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + m = bus.read(addr + y); + if((addr + y) / 256 != addr / 256) + clk++; + set_flags(N | Z, a = a ^ m); + break; + } + case 0x45: { // EOR unsigned char zpg = read_pc_inc(bus); set_flags(N | Z, a = a ^ bus.read(zpg)); @@ -1583,6 +1953,12 @@ struct CPU6502 break; } + case 0x8E: { // STX abs + int addr = read_pc_inc(bus) + read_pc_inc(bus) * 256; + bus.write(addr, x); + break; + } + case 0x86: { // STX unsigned char zpg = read_pc_inc(bus); bus.write(zpg, x); @@ -1654,7 +2030,7 @@ void cleanup(void) fflush(stderr); } -bool use_fake6502 = false; +bool use_fake6502 = true; string read_bus_and_disassemble(bus_frontend &bus, int pc) { @@ -1833,7 +2209,9 @@ int main(int argc, char **argv) char *progname = argv[0]; argc -= 1; argv += 1; - char *diskII_rom_name = NULL, *floppy1_name, *floppy2_name; + char *diskII_rom_name = NULL, *floppy1_name = NULL, *floppy2_name = NULL; + + bool have_audio = true; while((argc > 0) && (argv[0][0] == '-')) { if(strcmp(argv[0], "-debugger") == 0) { @@ -1850,6 +2228,10 @@ int main(int argc, char **argv) floppy2_name = argv[3]; argv += 4; argc -= 4; + } else if(strcmp(argv[0], "-noaudio") == 0) { + have_audio = false; + argv += 1; + argc -= 1; } else if(strcmp(argv[0], "-fast") == 0) { run_fast = true; argv += 1; @@ -1902,12 +2284,27 @@ int main(int argc, char **argv) MAINboard* mainboard; MAINboard::display_write_func display = [](int addr, unsigned char data)->bool{return APPLE2Einterface::write(addr, data);}; - MAINboard::audio_flush_func audio = [aodev](char *buf, size_t sz){ao_play(aodev, buf, sz);}; + MAINboard::audio_flush_func audio; + if(have_audio) + audio = [aodev](char *buf, size_t sz){ao_play(aodev, buf, sz);}; + else + audio = [](char *buf, size_t sz){}; mainboard = new MAINboard(clk, b, display, audio); bus.board = mainboard; bus.reset(); if(diskII_rom_name != NULL) { + + if((strcmp(floppy1_name, "-") == 0) || + (strcmp(floppy1_name, "none") == 0) || + (strcmp(floppy1_name, "") == 0) ) + floppy1_name = NULL; + + if((strcmp(floppy2_name, "-") == 0) || + (strcmp(floppy2_name, "none") == 0) || + (strcmp(floppy2_name, "") == 0) ) + floppy2_name = NULL; + try { DISKIIboard *diskII = new DISKIIboard(diskII_rom, floppy1_name, floppy2_name); mainboard->boards.push_back(diskII); @@ -1955,24 +2352,28 @@ int main(int argc, char **argv) } chrono::time_point then; - int inst_per_slice; + int clocks_per_slice; if(pause_cpu) - inst_per_slice = 0; + clocks_per_slice = 0; else { if(run_fast) - inst_per_slice = 320000; + clocks_per_slice = machine_clock_rate; else - inst_per_slice = 255750 * millis_per_slice / 1000 * 2; + clocks_per_slice = millis_per_slice * machine_clock_rate / 1000; } - for(int i = 0; i < inst_per_slice; i++) { + clk_t prev_clock = clk; + while(clk - prev_clock < clocks_per_slice) { if(debug & DEBUG_DECODE) { string dis = read_bus_and_disassemble(bus, cpu.pc); printf("%s\n", dis.c_str()); } - if(use_fake6502) + if(use_fake6502) { + clockticks6502 = 0; step6502(); - else + clk += clockticks6502; + } else { cpu.cycle(bus); + } } mainboard->sync(); APPLE2Einterface::DisplayMode mode = mainboard->TEXT ? APPLE2Einterface::TEXT : (mainboard->HIRES ? APPLE2Einterface::HIRES : APPLE2Einterface::LORES); @@ -2030,10 +2431,13 @@ int main(int argc, char **argv) printf("%s\n", dis.c_str()); } - if(use_fake6502) + if(use_fake6502) { + clockticks6502 = 0; step6502(); - else + clk += clockticks6502; + } else { cpu.cycle(bus); + } mainboard->sync(); APPLE2Einterface::DisplayMode mode = mainboard->TEXT ? APPLE2Einterface::TEXT : (mainboard->HIRES ? APPLE2Einterface::HIRES : APPLE2Einterface::LORES); diff --git a/fake6502.h b/fake6502.h index 5ad656e..c7e3da6 100644 --- a/fake6502.h +++ b/fake6502.h @@ -2,6 +2,8 @@ extern "C" { +extern uint32_t clockticks6502; + void reset6502(); void nmi6502(); void irq6502(); diff --git a/interface.cpp b/interface.cpp index 6424e01..c502334 100644 --- a/interface.cpp +++ b/interface.cpp @@ -863,14 +863,14 @@ static void redraw(GLFWwindow *window) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - set_shader(to_screen_transform, display_mode, false, (elapsed_millis / 1870) % 2, 0, 0); + set_shader(to_screen_transform, display_mode, false, (elapsed_millis / 300) % 2, 0, 0); glBindVertexArray(upper_screen_area); CheckOpenGL(__FILE__, __LINE__); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); CheckOpenGL(__FILE__, __LINE__); - set_shader(to_screen_transform, display_mode, mixed_mode, (elapsed_millis / 1870) % 2, 0, 0); + set_shader(to_screen_transform, display_mode, mixed_mode, (elapsed_millis / 300) % 2, 0, 0); glBindVertexArray(lower_screen_area); CheckOpenGL(__FILE__, __LINE__); diff --git a/switches.txt b/switches.txt index c77326d..759bb7d 100644 --- a/switches.txt +++ b/switches.txt @@ -23,9 +23,11 @@ soft switch with W 2 addresses, read high bit C00C 49164 CLR80VID ECG W 40 Columns C00D 49165 SET80VID ECG W 80 Columns C01F 49183 RD80VID ECG R7 Status of 40/80 Columns + ??? C011 49169 RDLCBNK2 ECG R7 Status of Selected $Dx Bank C012 49170 RDLCRAM ECG R7 Status of $Dx ROM / $Dx RAM + soft switch with R/W 2 addresses, read high bit C050 49232 TXTCLR OECG WR Display Graphics C051 49233 TXTSET OECG WR Display Text