// // main.c // 6502 // // Created by Tamas Rudnai on 7/14/19. // Copyright © 2019, 2020 Tamas Rudnai. All rights reserved. // // This file is part of Steve ][ -- The Apple ][ Emulator. // // Steve ][ is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Steve ][ is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Steve ][. If not, see . // #include #include "mmio.h" #include "common.h" #include "6502.h" #include "6502_bp.h" #include "disk.h" #include "woz.h" #include "speaker.h" #include "paddle.h" videoMode_t videoMode = { 1 }; // 40 col text, page 1 uint8_t INT_64K_ROM[ 64 * KB ] = {0}; // ROM C0, C8, D0, D8, E0, E8, F0, F8 uint8_t AUX_64K_ROM[ 64 * KB ] = {0}; // ROM C0, C8, D0, D8, E0, E8, F0, F8 uint8_t EXP_64K_ROM[ 64 * KB ] = {0}; // ROM C0, C8, D0, D8, E0, E8, F0, F8 uint8_t Apple2_Dummy_Page[ 1 * PG ]; // Dummy Page to discard data uint8_t Apple2_Dummy_RAM[ 64 * KB ]; // Dummy RAM to discard data uint8_t Apple2_64K_AUX[ 64 * KB ] = {0}; // 64K Expansion Memory uint8_t Apple2_64K_RAM[ 64 * KB ] = {0}; // Main Memory uint8_t Apple2_64K_MEM[ 64 * KB ] = {0}; // Shadow Copy of Memory (or current memory content) //uint8_t * AUX_VID_RAM = Apple2_VID_AUX; // Pointer to Auxiliary Video Memory uint8_t * const AUX = Apple2_64K_AUX; // Pointer to the Auxiliary Memory so we can use this from Swift uint8_t * const RAM = Apple2_64K_RAM; // Pointer to the Main Memory so we can use this from Swift uint8_t * const MEM = Apple2_64K_MEM; // Pointer to the Shadow Memory Map so we can use this from Swift uint8_t * const RDLOMEM = Apple2_64K_MEM; // for Read $0000 - $BFFF (shadow memory) uint8_t * WRZEROPG= Apple2_64K_MEM; // for Write $0000 - $0200 (shadow memory) uint8_t * WRLOMEM = Apple2_64K_MEM; // for Write $0200 - $BFFF (shadow memory) uint8_t * const RDHIMEM = Apple2_64K_MEM; // for Read / Write $0000 - $BFFF (shadow memory) uint8_t * WRD0MEM = Apple2_Dummy_RAM; // for writing $D000 - $DFFF uint8_t * WRHIMEM = Apple2_Dummy_RAM; // for writing $E000 - $FFFF uint8_t writeState = 0; // 1 if $C08D was written uint8_t * current_RAM_bank = Apple2_64K_AUX + 0xC000; uint8_t activeTextAuxPage = 0; uint8_t * activeTextPage = Apple2_64K_RAM + 0x400; uint8_t * shadowTextPage = Apple2_64K_MEM + 0x400; #define INIT_MEMCFG { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } const MEMcfg_t initMEMcfg = INIT_MEMCFG; MEMcfg_t MEMcfg = INIT_MEMCFG; MEMcfg_t newMEMcfg = INIT_MEMCFG; uint8_t * shadowZPSTCKMEM = Apple2_64K_MEM; const uint8_t * currentZPSTCKMEM = Apple2_64K_RAM; const uint8_t * const shadowLowMEM = Apple2_64K_MEM + 0x200; const uint8_t * currentLowRDMEM = Apple2_64K_RAM + 0x200; uint8_t * currentLowWRMEM = Apple2_64K_RAM; /// No writing (Readonly), and mark it as NO need to commit from Shadow RAM INLINE void set_MEM_readonly(void) { dbgPrintf2("NOWR_AUX (pc:$%04X)\n", m6502.PC); MEMcfg.WR_RAM = 0; MEMcfg.WR_RAM_cntr = 0; WRD0MEM = Apple2_Dummy_RAM; // for Discarding any writes to $D000 - $DFFF - BANK X WRHIMEM = Apple2_Dummy_RAM; // for Discarding any writes to $E000 - $FFFF } /// Returns TRUE if already writeable or second of the "two consecutive" reads on appropriate soft switches INLINE int is_wr_enabled(void) { // uint64_t clk = m6502.clktime + m6502.clkfrm; // uint64_t elapsed = clk - m6502.clk_wrenable; // int is_enabled = ( elapsed < 16 ) || MEMcfg.WR_RAM; int is_enabled = ++MEMcfg.WR_RAM_cntr >= 1 || MEMcfg.WR_RAM; // printf("is_wr_enabled elapsed:%llu was_enabled:%i to_be_enabled:%i\n", elapsed, MEMcfg.WR_RAM, is_enabled); dbgPrintf2("is_wr_enabled WR_RAM_cntr:%u was_enabled:%i to_be_enabled:%i\n", MEMcfg.WR_RAM_cntr, MEMcfg.WR_RAM, is_enabled); // m6502.clk_wrenable = clk; return is_enabled; } /// Make AUX RAM writeable -- This is when AUX is also readable, othwrwise use set_AUX_write... /// Note: Need to save the content back from the shadow memory INLINE void set_AUX_read_write(void) { // two consecutive read or write needs for write enable // Note: if it is already writeable and was previously a ROM read + RAM write, then we also need to bound AUX to MEM if ( is_wr_enabled() ) { dbgPrintf2("WR_MEM (pc:$%04X)\n", m6502.PC); // will write to Shadow RAM, and mark it as need to commit from Shadow RAM MEMcfg.WR_RAM = 1; WRD0MEM = Apple2_64K_MEM; // for Write $D000 - $DFFF (shadow memory) - BANK X WRHIMEM = Apple2_64K_MEM; // for Write $E000 - $FFFF (shadow memory) } } /// Make AUX RAM writeable -- This is when ROM is readable, othwrwise use set_MEM_write... /// Note: NO NEED to write back the content since it writes everything directly to AUX memory INLINE void set_AUX_write(void) { // will write directly to Auxiliary RAM, and mark it as NO need to commit from Shadow RAM // Note: if it is already writeable and was previously a RAM read + RAM write, then we also need to bound AUX to MEM if ( is_wr_enabled() ) { dbgPrintf2("WR_AUX (pc:$%04X)\n", m6502.PC); MEMcfg.WR_RAM = 1; if ( MEMcfg.RAM_BANK_2 ) { WRD0MEM = Apple2_64K_AUX; // for Write $D000 - $DFFF (shadow memory) - BANK 2 at 0xD000 } else { WRD0MEM = Apple2_64K_AUX - 0x1000; // for Write $D000 - $DFFF (shadow memory) - BANK 1 at 0xC000 } WRHIMEM = Apple2_64K_AUX; // for Write $E000 - $FFFF (shadow memory) } } // save the content of Shadow Memory in needed INLINE void save_AUX(void) { if ( MEMcfg.WR_RAM && MEMcfg.RD_INT_RAM ) { dbgPrintf2("Saving RAM Bank %d to %d (pc:$%04X)\n", MEMcfg.RAM_BANK_2 + 1, (current_RAM_bank == Apple2_64K_AUX + 0xD000) + 1, m6502.PC); // save LC Bank 1 or 2 memcpy(current_RAM_bank, Apple2_64K_MEM + 0xD000, 0x1000); // save rest of LC RAM memcpy(Apple2_64K_AUX + 0xE000, Apple2_64K_MEM + 0xE000, 0x2000); } } /// Save entire void save_RAM(void) { // save the content of Shadow ZP + Stack memcpy( (void*) currentZPSTCKMEM, shadowZPSTCKMEM, 0x200); // save LoMem memcpy( (void*) currentLowWRMEM + 0x200, WRLOMEM + 0x200, 0xBE00); save_AUX(); if ( activeTextPage ) { // save the content of Shadow Memory memcpy(activeTextPage, shadowTextPage, 0x400); } } INLINE void select_RAM_BANK( uint16_t addr ) { // RAM Bank 1 or 2? switch ((uint8_t)addr) { case (uint8_t)io_MEM_RDRAM_NOWR_2: case (uint8_t)io_MEM_RDROM_WRAM_2: case (uint8_t)io_MEM_RDROM_NOWR_2: case (uint8_t)io_MEM_RDRAM_WRAM_2: case (uint8_t)io_MEM_RDRAM_NOWR_2_: case (uint8_t)io_MEM_RDROM_WRAM_2_: case (uint8_t)io_MEM_RDROM_NOWR_2_: case (uint8_t)io_MEM_RDRAM_WRAM_2_: dbgPrintf2("RAM_BANK_2 (pc:$%04X)\n", m6502.PC); MEMcfg.RAM_BANK_2 = 1; current_RAM_bank = Apple2_64K_AUX + 0xD000; break; default: dbgPrintf2("RAM_BANK_1 (pc:$%04X)\n", m6502.PC); MEMcfg.RAM_BANK_2 = 0; current_RAM_bank = Apple2_64K_AUX + 0xC000; break; } } INLINE void read_RAM_or_ROM( uint16_t addr ) { // is RAM to read or ROM? switch ((uint8_t)addr) { case (uint8_t)io_MEM_RDRAM_NOWR_2: case (uint8_t)io_MEM_RDRAM_WRAM_2: case (uint8_t)io_MEM_RDRAM_NOWR_1: case (uint8_t)io_MEM_RDRAM_WRAM_1: case (uint8_t)io_MEM_RDRAM_NOWR_2_: case (uint8_t)io_MEM_RDRAM_WRAM_2_: case (uint8_t)io_MEM_RDRAM_NOWR_1_: case (uint8_t)io_MEM_RDRAM_WRAM_1_: dbgPrintf2("RD_RAM (pc:$%04X)\n", m6502.PC); MEMcfg.RD_INT_RAM = 1; // load the content of Aux Memory memcpy(Apple2_64K_MEM + 0xD000, current_RAM_bank, 0x1000); memcpy(Apple2_64K_MEM + 0xE000, Apple2_64K_AUX + 0xE000, 0x2000); // set the RAM extension to read on the upper memory area break; default: dbgPrintf2("RD_ROM (pc:$%04X)\n", m6502.PC); MEMcfg.RD_INT_RAM = 0; // TODO: What about CX (Slot) ROM? // load the content of ROM Memory memcpy(Apple2_64K_MEM + 0xD000, INT_64K_ROM + 0xD000, 0x3000); // set the ROM to read on the upper memory area break; } } INLINE void write_RAM_or_NOT( uint16_t addr ) { // is RAM Writeable? switch ((uint8_t)addr) { case (uint8_t)io_MEM_RDROM_WRAM_2: case (uint8_t)io_MEM_RDROM_WRAM_1: case (uint8_t)io_MEM_RDROM_WRAM_2_: case (uint8_t)io_MEM_RDROM_WRAM_1_: dbgPrintf2("RD_ROM + WR_AUX (pc:$%04X)\n", m6502.PC); set_AUX_write(); break; case (uint8_t)io_MEM_RDRAM_WRAM_2: case (uint8_t)io_MEM_RDRAM_WRAM_1: case (uint8_t)io_MEM_RDRAM_WRAM_2_: case (uint8_t)io_MEM_RDRAM_WRAM_1_: dbgPrintf2("RD_RAM + WR_RAM (pc:$%04X)\n", m6502.PC); set_AUX_read_write(); break; default: dbgPrintf2("NO_WR (pc:$%04X)\n", m6502.PC); set_MEM_readonly(); break; } } /// Switch between Memory Banks and ROM and Internal / Aux RAM INLINE void io_RAM_EXP( uint16_t addr ) { if ( MEMcfg.RAM_16K || MEMcfg.RAM_128K ) { // TODO: store 0xD000 BANK 1 at 0xC000 -- might be a problem emulating 64k/128k Saturn cards save_AUX(); select_RAM_BANK(addr); read_RAM_or_ROM(addr); write_RAM_or_NOT(addr); } // if there is RAM expansion card installed } INLINE int is_io_interesting( uint16_t addr ) { switch(addr) { case io_KBD: case io_KBDSTRB: case io_TAPEOUT: case io_SPKR: case io_VID_ALTCHAR: case io_VID_RD80VID: case io_RDCXROM: // Ignore Disk IO case 0xC0E0: case 0xC0E1: case 0xC0E2: case 0xC0E3: case 0xC0E4: case 0xC0E5: case 0xC0E6: case 0xC0E7: case 0xC0E8: case 0xC0E9: case 0xC0EA: case 0xC0EB: case 0xC0EC: case 0xC0ED: case 0xC0EE: case 0xC0EF: return 0; default: break; } return 1; } const int pasteBufferSize = 1000; int pasteBufferIdx = 0; uint8_t pasteBuffer[pasteBufferSize]; uint8_t kbdCodeConvert( uint8_t code ) { // printf("kbdInput: %02X ('%c')\n", code, isprint(code) ? code : ' '); switch ( code ) { case '\n': code = 0x0D; break; case 0x7F: // BackSpace code = 0x08; break; default: break; } // mark as valie keyboard input code |= 1<<7; return code; } void kbdClearPasteBuffer(void) { pasteBufferIdx = 0; } void kbdPaste ( uint8_t code ) { if (pasteBufferIdx >= pasteBufferSize) { while (pasteBufferIdx) { usleep(100); } } pasteBuffer[pasteBufferIdx++] = code; } uint8_t pasted = 0; INLINE uint8_t kbdRead(void) { // if ( cpuMode == cpuMode_eco ) { // check if this is a busy keyboard poll (aka waiting for user input) // if ( IOframe < 16 ) { // clk_6502_per_frm_max = 6502; // Let it run for a bit to display character -- nerd number :-) // cpuState = cpuState_halting; // } // } // check pasted buffer if ( pasted ) { pasted--; } // check paste buffer else if (pasteBufferIdx) { if ( Apple2_64K_RAM[io_KBD] < 0x80 ) { kbdInput(pasteBuffer[0]); // memcpy(pasteBuffer, pasteBuffer + 1, pasteBufferSize -1); for (int i = 0; i < pasteBufferSize - 1; i++) { pasteBuffer[i] = pasteBuffer[i+1]; } pasteBufferIdx--; // delay keyboard input to avoid weird character loss (KBDSTRB called many times for example when RETURN pressed) pasted = 10; // to make paste even faster disk_accelerator_speedup(); } } // we have to return keybard not only for $C000 but for ports all the way till $C00F return Apple2_64K_RAM[io_KBD]; } INLINE uint8_t kbdStrobe(void) { Apple2_64K_RAM[io_KBD] &= ~(1 << 7); // if ( cpuMode == cpuMode_eco ) { // // check if this is a busy keyboard poll (aka waiting for user input) // clk_6502_per_frm_max = clk_6502_per_frm; // Absolute low mode // cpuState = cpuState_halting; // cpuState_running; // } return Apple2_64K_RAM[io_KBDSTRB]; } INLINE uint8_t ioRead( uint16_t addr ) { // if (outdev) fprintf(outdev, "ioRead:%04X\n", addr); // if ( is_io_interesting(addr) ) { // printf("ioRead:%04X (PC:%04X)\n", addr, m6502.PC); // } m6502.lastIO = m6502.clkfrm; // // TODO: This is for speed demo only, should be either removed or the entire ioRead should based on binary search, whatever is faster // if ( addr == io_KBD ) { // // clk_6502_per_frm_max = clk_6502_per_frm_max > 32768 ? clk_6502_per_frm_max - 32768 : 0; // ECO Mode! // // if ( cpuMode == cpuMode_eco ) { // // check if this is a busy keyboard poll (aka waiting for user input) // if ( IOframe < 16 ) { // clk_6502_per_frm_max = 6502; // Let it run for a bit to display character -- nerd number :-) // cpuState = cpuState_halting; // } // } // // return Apple2_64K_RAM[io_KBD]; // } switch ( (uint8_t)addr ) { case (uint8_t)io_KBD: case (uint8_t)io_80STOREON: case (uint8_t)io_SETSLOTCXROM: case (uint8_t)io_SETINTCXROM: case (uint8_t)io_SETSTDZP: case (uint8_t)io_SETALTZP: case (uint8_t)io_SETINTC3ROM: case (uint8_t)io_SETSLOTC3ROM: return kbdRead(); case (uint8_t)io_KBDSTRB: return kbdStrobe(); case (uint8_t)io_TAPEOUT: // TODO: 1. Implement Tape return rand(); // Floating I/O -- used for random number generation in Games case (uint8_t)io_SPKR: spkr_toggle(); return rand(); // Floating I/O -- used for random number generation in Games case (uint8_t)io_STROBE: case (uint8_t)io_CLRAN0: case (uint8_t)io_SETAN0: case (uint8_t)io_CLRAN1: case (uint8_t)io_SETAN1: case (uint8_t)io_CLRAN2: case (uint8_t)io_SETAN2: case (uint8_t)io_CLRAN3: case (uint8_t)io_SETAN3: // TODO: Simulate Attenuator return rand(); // Apple2_64K_RAM[io_SPKR]; case (uint8_t)io_VID_RDVBL: return (m6502.clkfrm > 4550 ? 0x80 : 0) | (kbdStrobe() & 0x7F); case (uint8_t)io_VID_RDTEXT: return (videoMode.text << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_VID_ALTCHAR: return (videoMode.altChr << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_VID_RD80VID: return (videoMode.col80 << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_TAPEIN: // TODO: this should be only on //c return MEMcfg.txt_page_2 << 7; case (uint8_t)io_RDCXROM: // TODO: Implement Reset Mouse X0 Interrupt (io_RSTXINT) return (MEMcfg.int_Cx_ROM << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_RDLCBNK2: return (MEMcfg.RAM_BANK_2 << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_RDLCRAM: return (MEMcfg.RD_INT_RAM << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_RDRAMRD: return (MEMcfg.RD_AUX_MEM << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_RDRAMWR: return (MEMcfg.WR_AUX_MEM << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_RDALTZP: // TODO: Implement Reset Mouse Y0 Interrupt (io_RSTYINT) return (MEMcfg.ALT_ZP << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_RDC3ROM: return MEMcfg.slot_C3_ROM << 7; case (uint8_t)io_RD80STORE: return (MEMcfg.is_80STORE << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_VID_TXTPAGE1: // printf("io_VID_TXTPAGE1\n"); MEMcfg.txt_page_2 = 0; textPageSelect(); break; case (uint8_t)io_VID_TXTPAGE2: // printf("io_VID_TXTPAGE2\n"); MEMcfg.txt_page_2 = 1; textPageSelect(); break; case (uint8_t)io_VID_RDPAGE2: return (MEMcfg.txt_page_2 << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_VID_Text_OFF: videoMode.text = 0; break; case (uint8_t)io_VID_Text_ON: videoMode.text = 1; break; case (uint8_t)io_VID_Mixed_OFF: videoMode.mixed = 0; break; case (uint8_t)io_VID_Mixed_ON: videoMode.mixed = 1; break; case (uint8_t)io_VID_RDMIXED: return (videoMode.mixed << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_VID_Hires_OFF: videoMode.hires = 0; break; case (uint8_t)io_VID_Hires_ON: videoMode.hires = 1; break; case (uint8_t)io_VID_RDHIRES: return (videoMode.hires << 7) | (kbdStrobe() & 0x7F); case (uint8_t)io_PDL0: case (uint8_t)io_PDL1: case (uint8_t)io_PDL2: case (uint8_t)io_PDL3: // printf("PDL%d: %d\n", addr - io_PDL0, pdl_read( addr - io_PDL0 )); return pdl_read( addr - io_PDL0 ); case (uint8_t)io_PDL_STROBE: return pdl_reset(); case (uint8_t)io_RDMAINRAM: dbgPrintf2("R:io_RDMAINRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.RD_AUX_MEM = 0; auxMemorySelect(newMEMcfg); // still need to return keyboard return kbdRead(); break; case (uint8_t)io_RDCARDRAM: dbgPrintf2("R:io_RDCARDRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.RD_AUX_MEM = 1; auxMemorySelect(newMEMcfg); // still need to return keyboard return kbdRead(); break; case (uint8_t)io_WRMAINRAM: dbgPrintf2("R:io_WRMAINRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.WR_AUX_MEM = 0; auxMemorySelect(newMEMcfg); // still need to return keyboard return kbdRead(); break; case (uint8_t)io_WRCARDRAM: dbgPrintf2("R:io_WRCARDRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.WR_AUX_MEM = 1; auxMemorySelect(newMEMcfg); // still need to return keyboard return kbdRead(); break; case (uint8_t)io_MEM_RDRAM_NOWR_2: case (uint8_t)io_MEM_RDROM_WRAM_2: case (uint8_t)io_MEM_RDROM_NOWR_2: case (uint8_t)io_MEM_RDRAM_WRAM_2: case (uint8_t)io_MEM_RDRAM_NOWR_2_: case (uint8_t)io_MEM_RDROM_WRAM_2_: case (uint8_t)io_MEM_RDROM_NOWR_2_: case (uint8_t)io_MEM_RDRAM_WRAM_2_: case (uint8_t)io_MEM_RDRAM_NOWR_1: case (uint8_t)io_MEM_RDROM_WRAM_1: case (uint8_t)io_MEM_RDROM_NOWR_1: case (uint8_t)io_MEM_RDRAM_WRAM_1: case (uint8_t)io_MEM_RDRAM_NOWR_1_: case (uint8_t)io_MEM_RDROM_WRAM_1_: case (uint8_t)io_MEM_RDROM_NOWR_1_: case (uint8_t)io_MEM_RDRAM_WRAM_1_: io_RAM_EXP(addr); break; // TODO: Make code "card insertable to slot" / aka slot independent and dynamically add/remove case (uint8_t)io_DISK_PHASE0_OFF + SLOT6: case (uint8_t)io_DISK_PHASE1_OFF + SLOT6: case (uint8_t)io_DISK_PHASE2_OFF + SLOT6: case (uint8_t)io_DISK_PHASE3_OFF + SLOT6: disk_phase_off( (addr - io_DISK_PHASE0_OFF - SLOT6) / 2 ); return disk_read(); case (uint8_t)io_DISK_PHASE0_ON + SLOT6: case (uint8_t)io_DISK_PHASE1_ON + SLOT6: case (uint8_t)io_DISK_PHASE2_ON + SLOT6: case (uint8_t)io_DISK_PHASE3_ON + SLOT6: disk_phase_on( (addr - io_DISK_PHASE0_ON - SLOT6) / 2 ); return disk_read(); case (uint8_t)io_DISK_POWER_OFF + SLOT6: dbgPrintf2("io_DISK_POWER_OFF (S%u)\n", 6); disk_motor_off(); return disk_read(); case (uint8_t)io_DISK_POWER_ON + SLOT6: dbgPrintf2("io_DISK_POWER_ON (S%u)\n", 6); disk_motor_on(); return disk_read(); case (uint8_t)io_DISK_SELECT_1 + SLOT6: dbgPrintf2("io_DISK_SELECT_1 (S%u)\n", 6); disk.drive = 0; return disk_read(); case (uint8_t)io_DISK_SELECT_2 + SLOT6: dbgPrintf2("io_DISK_SELECT_2 (S%u)\n", 6); disk.drive = 1; return disk_read(); case (uint8_t)io_DISK_READ + SLOT6: if ( writeState ) { writeState = 0; woz_write( Apple2_64K_RAM[io_DISK_WRITE + SLOT6] ); return Apple2_64K_RAM[io_DISK_WRITE + SLOT6]; } else { return disk_read(); } case (uint8_t)io_DISK_WRITE + SLOT6: dbgPrintf2("io_DISK_WRITE (S%u)\n", 6); // Apple2_64K_RAM[io_DISK_CLEAR + SLOT6] |= 1 << 7; // mark disk as write protected WOZwrite.latch = WOZread.latch = 0; Apple2_64K_RAM[io_DISK_CLEAR + SLOT6] &= ~(1 << 7); // mark disk as write enabled return Apple2_64K_RAM[io_DISK_WRITE + SLOT6]; case (uint8_t)io_DISK_CLEAR + SLOT6: dbgPrintf2("io_DISK_CLEAR (S%u)\n", 6); return Apple2_64K_RAM[io_DISK_CLEAR + SLOT6]; case (uint8_t)io_DISK_SHIFT + SLOT6: dbgPrintf2("io_DISK_SHIFT (S%u)\n", 6); return disk_read(); default: //printf("mmio read:%04X\n", addr); break; } return Apple2_64K_RAM[addr]; } INLINE void ioWrite( uint16_t addr, uint8_t val ) { // if (outdev) fprintf(outdev, "ioWrite:%04X (A:%02X)\n", addr, m6502.A); // if ( is_io_interesting(addr) ) { // printf("ioWrite:%04X (PC:%04X, val:%02X)\n", addr, m6502.PC, val); // } switch ( (uint8_t)addr ) { case (uint8_t)io_KBDSTRB: kbdStrobe(); newMEMcfg.is_80STORE = 0; break; case (uint8_t)io_TAPEOUT: // TODO: 1. Sound problem in Castle Wolfensein if we output this to speaker all the time // 2. Implement Tape break; case (uint8_t)io_SPKR: spkr_toggle(); // TODO: Theoretically it toggles the speaker twice // m6502.clkfrm++; // to simulate RMW // spkr_toggle(); break; case (uint8_t)io_RDMAINRAM: dbgPrintf2("W:io_RDMAINRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.RD_AUX_MEM = 0; auxMemorySelect(newMEMcfg); break; case (uint8_t)io_RDCARDRAM: dbgPrintf2("W:io_RDCARDRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.RD_AUX_MEM = 1; auxMemorySelect(newMEMcfg); break; case (uint8_t)io_WRMAINRAM: dbgPrintf2("W:io_WRMAINRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.WR_AUX_MEM = 0; auxMemorySelect(newMEMcfg); break; case (uint8_t)io_WRCARDRAM: dbgPrintf2("W:io_WRCARDRAM (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.WR_AUX_MEM = 1; auxMemorySelect(newMEMcfg); break; case (uint8_t)io_SETSTDZP: dbgPrintf2("INT ZP (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.ALT_ZP = 0; auxMemorySelect(newMEMcfg); break; case (uint8_t)io_SETALTZP: dbgPrintf2("AUX ZP (pc:$%04X)\n", m6502.PC); newMEMcfg = MEMcfg; newMEMcfg.ALT_ZP = 1; auxMemorySelect(newMEMcfg); break; case (uint8_t)io_SETSLOTCXROM: dbgPrintf2("io_SETSLOTCXROM\n"); newMEMcfg = MEMcfg; newMEMcfg.int_Cx_ROM = 0; auxMemorySelect(newMEMcfg); CxMemorySelect(newMEMcfg); break; case (uint8_t)io_SETINTCXROM: dbgPrintf2("io_SETINTCXROM\n"); newMEMcfg = MEMcfg; newMEMcfg.int_Cx_ROM = 1; auxMemorySelect(newMEMcfg); CxMemorySelect(newMEMcfg); break; case (uint8_t)io_SETSLOTC3ROM: dbgPrintf2("io_SETSLOTC3ROM\n"); newMEMcfg = MEMcfg; newMEMcfg.slot_C3_ROM = 1; auxMemorySelect(newMEMcfg); C3MemorySelect(newMEMcfg); break; case (uint8_t)io_SETINTC3ROM: dbgPrintf2("io_SETINTC3ROM\n"); newMEMcfg = MEMcfg; newMEMcfg.slot_C3_ROM = 0; auxMemorySelect(newMEMcfg); C3MemorySelect(newMEMcfg); break; case (uint8_t)io_VID_CLR80VID: dbgPrintf2("io_VID_CLR80VID\n"); videoMode.col80 = 0; break; case (uint8_t)io_VID_SET80VID: videoMode.col80 = 1; break; case (uint8_t)io_VID_CLRALTCHAR: videoMode.altChr = 0; break; case (uint8_t)io_VID_SETALTCHAR: videoMode.altChr = 1; break; case (uint8_t)io_80STOREOFF: dbgPrintf2("io_80STOREOFF (pc:$%04X)\n", m6502.PC); MEMcfg.is_80STORE = 0; textPageSelect(); break; case (uint8_t)io_80STOREON: dbgPrintf2("io_80STOREON (pc:$%04X)\n", m6502.PC); MEMcfg.is_80STORE = 1; textPageSelect(); break; case (uint8_t)io_VID_TXTPAGE1: dbgPrintf2("io_VID_TXTPAGE1 (pc:$%04X)\n", m6502.PC); MEMcfg.txt_page_2 = 0; textPageSelect(); break; case (uint8_t)io_VID_TXTPAGE2: dbgPrintf2("io_VID_TXTPAGE2 (pc:$%04X)\n", m6502.PC); MEMcfg.txt_page_2 = 1; textPageSelect(); break; case (uint8_t)io_VID_Text_OFF: videoMode.text = 0; break; case (uint8_t)io_VID_Text_ON: videoMode.text = 1; break; case (uint8_t)io_VID_Mixed_OFF: videoMode.mixed = 0; break; case (uint8_t)io_VID_Mixed_ON: videoMode.mixed = 1; break; case (uint8_t)io_VID_Hires_OFF: videoMode.hires = 0; break; case (uint8_t)io_VID_Hires_ON: videoMode.hires = 1; break; case (uint8_t)io_MEM_RDRAM_NOWR_2: case (uint8_t)io_MEM_RDROM_WRAM_2: case (uint8_t)io_MEM_RDROM_NOWR_2: case (uint8_t)io_MEM_RDRAM_WRAM_2: case (uint8_t)io_MEM_RDRAM_NOWR_2_: case (uint8_t)io_MEM_RDROM_WRAM_2_: case (uint8_t)io_MEM_RDROM_NOWR_2_: case (uint8_t)io_MEM_RDRAM_WRAM_2_: case (uint8_t)io_MEM_RDRAM_NOWR_1: case (uint8_t)io_MEM_RDROM_WRAM_1: case (uint8_t)io_MEM_RDROM_NOWR_1: case (uint8_t)io_MEM_RDRAM_WRAM_1: case (uint8_t)io_MEM_RDRAM_NOWR_1_: case (uint8_t)io_MEM_RDROM_WRAM_1_: case (uint8_t)io_MEM_RDROM_NOWR_1_: case (uint8_t)io_MEM_RDRAM_WRAM_1_: io_RAM_EXP(addr); break; // TODO: Make code "card insertable to slot" / aka slot independent and dynamically add/remove case (uint8_t)io_DISK_PHASE0_OFF + SLOT6: case (uint8_t)io_DISK_PHASE1_OFF + SLOT6: case (uint8_t)io_DISK_PHASE2_OFF + SLOT6: case (uint8_t)io_DISK_PHASE3_OFF + SLOT6: disk_phase_off( (addr - io_DISK_PHASE0_OFF - SLOT6) / 2 ); break; case (uint8_t)io_DISK_PHASE0_ON + SLOT6: case (uint8_t)io_DISK_PHASE1_ON + SLOT6: case (uint8_t)io_DISK_PHASE2_ON + SLOT6: case (uint8_t)io_DISK_PHASE3_ON + SLOT6: disk_phase_on( (addr - io_DISK_PHASE0_ON - SLOT6) / 2 ); break; case (uint8_t)io_DISK_POWER_OFF + SLOT6: dbgPrintf2("io_DISK_POWER_OFF (S%u)\n", 6); disk_motor_off(); break; case (uint8_t)io_DISK_POWER_ON + SLOT6: dbgPrintf2("io_DISK_POWER_ON (S%u)\n", 6); disk_motor_on(); break; case (uint8_t)io_DISK_SELECT_1 + SLOT6: dbgPrintf2("io_DISK_SELECT_1 (S%u)\n", 6); disk.drive = 0; break; case (uint8_t)io_DISK_SELECT_2 + SLOT6: dbgPrintf2("io_DISK_SELECT_2 (S%u)\n", 6); disk.drive = 1; break; case (uint8_t)io_DISK_READ + SLOT6: Apple2_64K_RAM[io_DISK_READ + SLOT6] = val; woz_write( Apple2_64K_RAM[io_DISK_WRITE + SLOT6] ); writeState = 0; break; case (uint8_t)io_DISK_WRITE + SLOT6: dbgPrintf2("io_DISK_WRITE (S%u)\n", 6); Apple2_64K_RAM[io_DISK_WRITE + SLOT6] = val; writeState = 1; break; case (uint8_t)io_DISK_CLEAR + SLOT6: dbgPrintf2("io_DISK_CLEAR (S%u)\n", 6); break; case (uint8_t)io_DISK_SHIFT + SLOT6: dbgPrintf2("io_DISK_SHIFT (S%u)\n", 6); break; default: break; } return; } INLINE uint8_t check_mem_rd_bp(uint16_t addr) { if (LAST_IDX(mem_read_breakpoints)) { if ( m6502_dbg_bp_exists(mem_read_breakpoints, addr) ) { // printf("MEM BP $%04X (bp:%04X)\n", addr, m6502.PC); // cpuState = cpuState_halted; m6502.interrupt = BREAKRDMEM; return 1; } } return 0; } INLINE uint8_t check_mem_wr_bp(uint16_t addr) { if (LAST_IDX(mem_write_breakpoints)) { if ( m6502_dbg_bp_exists(mem_write_breakpoints, addr) ) { // printf("MEM BP $%04X (bp:%04X)\n", addr, m6502.PC); // cpuState = cpuState_halted; m6502.interrupt = BREAKWRMEM; return 1; } } return 0; } /** Naive implementation of RAM read from address **/ INLINE uint8_t memread8_zp( uint16_t addr ) { return shadowZPSTCKMEM[addr]; } INLINE uint8_t memread8_low( uint16_t addr ) { return Apple2_64K_MEM[addr]; } INLINE uint8_t memread8_high( uint16_t addr ) { return RDHIMEM[addr]; } INLINE uint8_t memread8( uint16_t addr ) { if (addr >= 0xC000) { return memread8_high(addr); } if (addr >= 0x200) { return memread8_low(addr); } return memread8_zp(addr); } /** Naive implementation of RAM read from address **/ INLINE uint16_t memread16_zp( uint16_t addr ) { return * (uint16_t*) ( shadowZPSTCKMEM + addr ); } INLINE uint16_t memread16_low( uint16_t addr ) { return * (uint16_t*) ( Apple2_64K_MEM + addr ); // avoid unaligned memory access // return (uint16_t)Apple2_64K_MEM[addr] | (uint16_t)Apple2_64K_MEM[addr+1] << 8; } INLINE uint16_t memread16_high( uint16_t addr ) { return * (uint16_t*) ( RDHIMEM + addr ); } INLINE uint16_t memread16( uint16_t addr ) { if (addr >= 0xC000) { return memread16_high(addr); } if (addr >= 0x200) { return memread16_low(addr); } return memread16_zp(addr); } INLINE uint16_t _memread16_dbg( uint16_t addr ) { check_mem_rd_bp(addr); return memread16_low(addr); } INLINE uint16_t _memread16_wr_dbg( uint16_t addr ) { check_mem_wr_bp(addr); return addr; } INLINE uint8_t _memread( uint16_t addr ) { if (addr >= 0xC000) { if (addr < 0xC100) { return ioRead(addr); } return memread8_high(addr); } if (addr >= 0x200) { return memread8_low(addr); } return memread8_zp(addr); } INLINE uint8_t _memread_dbg( uint16_t addr ) { check_mem_rd_bp(addr); return _memread(addr); } INLINE uint8_t _memread_dis( uint16_t addr ) { if (addr >= 0xC000) { return memread8_high(addr); } if (addr >= 0x200) { return memread8_low(addr); } return memread8_zp(addr); } /** Naive implementation of RAM read from address **/ //INLINE uint16_t memioread16( uint16_t addr ) { // return (uint16_t)mmio_read[ addr ](addr); //} /** Naive implementation of RAM write to address **/ //static void memwrite_zp( uint8_t addr, uint8_t byte ) { // RAM[ addr ] = byte; //} /** Naive implementation of RAM write to address **/ INLINE void _memwrite8_zp( uint16_t addr, uint8_t data ) { shadowZPSTCKMEM[addr] = data; } INLINE void _memwrite8_low( uint16_t addr, uint8_t data ) { // if ((addr >= 0x400) && (addr < 0x800)) { // if ((data == 0x00) || (data == 0xFF)) { // m6502.interrupt = BREAK; // } // } // if ((addr >= 0x3F5) && (addr <= 0x3F7)) { // if (data == 0x4C) { // m6502.interrupt = BREAK; // } // } WRLOMEM[addr] = data; } INLINE void _memwrite8_bank( uint16_t addr, uint8_t data ) { WRD0MEM[addr] = data; } INLINE void _memwrite8_high( uint16_t addr, uint8_t data ) { WRHIMEM[addr] = data; } INLINE void _memwrite( uint16_t addr, uint8_t data ) { if (addr >= 0xC000) { if (addr < 0xC100) { ioWrite(addr, data); } else if (addr < 0xD000) { // this could be either Peripherial ROM or Internal ROM memwrite8_high(addr, data); } else if (addr < 0xE000) { // Aux RAM Bank 1 or 2 memwrite8_bank(addr, data); } else { // ROM (dummy memory to screape writings) or Aux RAM memwrite8_high(addr, data); } } else if (addr >= 0x200) { // RAM memwrite8_low(addr, data); } else { // RAM memwrite8_zp(addr, data); } } /** Fetching 1 byte from memory address pc (program counter) increase pc by one **/ INLINE uint8_t _fetch(void) { disHexB( disassembly.pOpcode, memread8_low(m6502.PC) ); #ifdef CLK_ABSOLUTE_PRECISE if ( (m6502.PC & 0xFF) >= 0xFF ) { m6502.clktime++; } #endif if (m6502.PC >= 0xC000) { return memread8_high( m6502.PC++ ); } // TODO: We might want to run code on the ZERO PAGE? // if (m6502.PC >= 0x200) { return memread8_low( m6502.PC++ ); // } // return memread8_zp( m6502.PC++ ); } INLINE uint8_t _fetch_dis(void) { _disHexB( &disassembly.pOpcode, memread8_low(m6502.PC) ); return memread8_low( m6502.PC++ ); } /** Fetching 2 bytes as a 16 bit number from memory address pc (program counter) increase pc by one **/ INLINE uint16_t _fetch16(void) { uint16_t word = memread16( m6502.PC ); // disPrintf(disassembly.comment, "fetch16:%04X", word); #ifdef CLK_ABSOLUTE_PRECISE if ( (m6502.PC & 0xFF) >= 0xFE ) { m6502.clktime++; } #endif m6502.PC += 2; // disHexW( disassembly.pOpcode, word ); // Virtual ][ Style disHexB( disassembly.pOpcode, (uint8_t)word ); disHexB( disassembly.pOpcode, (uint8_t)(word >> 8)); return word; } INLINE uint16_t _fetch16_dis(void) { uint16_t word = memread16( m6502.PC ); // disPrintf(disassembly.comment, "fetch16:%04X", word); m6502.PC += 2; _disHexB( &disassembly.pOpcode, (uint8_t)word ); _disHexB( &disassembly.pOpcode, (uint8_t)(word >> 8)); return word; } //INLINE uint8_t * dest( uint8_t * mem, uint16_t addr ) { // return mem + addr; //} /** abs .... absolute OPC $LLHH,X operand is address; effective address is address incremented by X with carry ** **/ INLINE uint16_t _addr_abs(void) { return _fetch16(); } INLINE uint16_t _addr_abs_dbg(void) { uint16_t addr = _fetch16(); check_mem_wr_bp(addr); return addr; } INLINE uint16_t _addr_abs_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%04X", memread16(m6502.PC)); return _fetch16_dis(); } INLINE uint8_t _src_abs(void) { return _memread( _addr_abs() ); } INLINE uint8_t _src_abs_dbg(void) { return _memread_dbg( _addr_abs() ); } INLINE uint8_t _src_abs_dis(void) { return _memread_dis( _addr_abs_dis() ); } //INLINE uint8_t * dest_abs() { // return WRLOMEM + addr_abs(); //} INLINE int8_t _rel_addr(void) { return _fetch(); } INLINE int8_t _rel_addr_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%04X", m6502.PC + 1 + (int8_t)memread8(m6502.PC)); return _fetch_dis(); } INLINE uint16_t _abs_addr(void) { return _fetch16(); } INLINE uint16_t _abs_addr_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%04X", memread16(m6502.PC)); return _fetch16_dis(); } INLINE uint16_t _ind_addr(void) { return memread16( _fetch16() ); } INLINE uint16_t _ind_addr_dbg(void) { return _memread16_dbg( _fetch16() ); } INLINE uint16_t _ind_addr_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "($%04X)", memread16(m6502.PC)); _disPrintf(disassembly.comment, sizeof(disassembly.comment), "ind_addr:%04X", memread16(memread16(m6502.PC))); return memread16( _fetch16_dis() ); } /** abs,X .... absolute, X-indexed OPC $LLHH,X operand is address; effective address is address incremented by X with carry ** **/ INLINE uint16_t _abs_addr_X(void) { return _fetch16() + m6502.X; } INLINE uint16_t _abs_addr_X_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%04X,X", memread16(m6502.PC)); return _fetch16_dis() + m6502.X; } INLINE uint16_t _addr_abs_X(void) { return _fetch16() + m6502.X; } INLINE uint16_t _addr_abs_X_dbg(void) { uint16_t addr = _fetch16() + m6502.X; check_mem_wr_bp(addr); return addr; } INLINE uint16_t _addr_abs_X_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%04X,X", memread16(m6502.PC)); return _fetch16_dis() + m6502.X; } INLINE uint8_t _src_abs_X(void) { return _memread( _addr_abs_X() ); } INLINE uint8_t _src_abs_X_dbg(void) { return _memread_dbg( _addr_abs_X() ); } INLINE uint8_t _src_abs_X_dis(void) { return _memread_dis( _addr_abs_X_dis() ); } //INLINE uint8_t * dest_abs_X() { // return WRLOMEM + addr_abs_X(); //} /** abs,Y .... absolute, Y-indexed OPC $LLHH,Y operand is address; effective address is address incremented by Y with carry ** **/ INLINE uint16_t _addr_abs_Y(void) { return _fetch16() + m6502.Y; } INLINE uint16_t _addr_abs_Y_dbg(void) { uint16_t addr = _fetch16() + m6502.Y; check_mem_wr_bp(addr); return addr; } INLINE uint16_t _addr_abs_Y_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%04X,Y", memread16(m6502.PC)); return _fetch16_dis() + m6502.Y; } INLINE uint8_t _src_abs_Y(void) { return _memread(_addr_abs_Y()); } INLINE uint8_t _src_abs_Y_dbg(void) { return _memread_dbg(_addr_abs_Y()); } INLINE uint8_t _src_abs_Y_dis(void) { return _memread_dis(_addr_abs_Y_dis()); } //INLINE uint8_t * dest_abs_Y() { // return WRLOMEM + addr_abs_Y(); //} INLINE uint8_t _imm(void) { return _fetch(); } INLINE uint8_t _imm_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "#$%02X", memread8(m6502.PC)); return _fetch_dis(); } /** zpg .... zeropage OPC $LL operand is zeropage address (hi-byte is zero, address = $00LL) **/ INLINE uint8_t _addr_zp(void) { uint16_t addr = _fetch(); check_mem_wr_bp(addr); return addr; } INLINE uint8_t _addr_zp_dbg(void) { uint16_t addr = _fetch(); check_mem_wr_bp(addr); return addr; } INLINE uint8_t _addr_zp_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%02X", memread8(m6502.PC)); return _fetch_dis(); } INLINE uint8_t _src_zp(void) { return memread8_zp(_addr_zp()); } INLINE uint8_t _src_zp_dbg(void) { return _memread_dbg(_addr_zp_dbg()); } INLINE uint8_t _src_zp_dis(void) { return memread8(_addr_zp_dis()); } //INLINE uint8_t * dest_zp() { // return WRLOMEM + addr_zp(); //} /** get a 16 bit address from the zp:zp+1 **/ //INLINE uint16_t addr_zp_ind( uint8_t addr ) { // dbgPrintf("zpi:%02X:%04X(%02X) ", RAM[m6502.PC], *((uint16_t*)&RAM[m6502.PC]), RAM[*((uint16_t*)&RAM[m6502.PC])]); // disPrintf(disassembly.oper, "($%02X)", memread8(m6502.PC) ); // disPrintf(disassembly.comment, "ind_addr:%04X", memread16( memread8(m6502.PC) ) ); // return memread16(addr); //} /** ind .... indirect OPC ($LL) operand is zeropage address; effective address is word in (LL, LL + 1), inc. without carry: C.w($00LL) **/ INLINE uint16_t _addr_ind(void) { return memread16_zp( _fetch() ); } INLINE uint16_t _addr_ind_dbg(void) { uint16_t addr = _memread16_dbg(_fetch()); check_mem_wr_bp(addr); // write debug on the target address return addr; } INLINE uint16_t _addr_ind_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "($%02X)", memread8(m6502.PC) ); _disPrintf(disassembly.comment, sizeof(disassembly.comment), "ind_addr:%04X", memread16( memread8(m6502.PC)) ); return memread16( _fetch_dis() ); } INLINE uint8_t _src_ind(void) { return _memread( _addr_ind() ); } INLINE uint8_t _src_ind_dbg(void) { return _memread_dbg( _addr_ind_dbg() ); } INLINE uint8_t _src_ind_dis(void) { return _memread_dis( _addr_ind_dis() ); } /** X,ind .... X-indexed, indirect OPC ($LL,X) operand is zeropage address; effective address is word in (LL + X, LL + X + 1), inc. without carry: C.w($00LL + X) **/ INLINE uint16_t _addr_ind_X(void) { return memread16_zp( (uint8_t)(_fetch() + m6502.X) ); } INLINE uint16_t _addr_ind_X_rd_dbg(void) { return _memread16_dbg( (uint8_t)(_fetch() + m6502.X) ); } INLINE uint16_t _addr_ind_X_dbg(void) { uint16_t addr = _memread16_dbg( (uint8_t)(_fetch() + m6502.X)); check_mem_wr_bp(addr); // write debug on the target address return addr; } INLINE uint16_t _addr_ind_X_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "($%02X,X)", memread8(m6502.PC) ); _disPrintf(disassembly.comment, sizeof(disassembly.comment), "ind_addr:%04X", memread16( memread8(m6502.PC) + m6502.X) ); return memread16_zp( (uint8_t)(_fetch_dis() + m6502.X) ); } INLINE uint8_t _src_X_ind(void) { return _memread( _addr_ind_X() ); } INLINE uint8_t _src_X_ind_dbg(void) { return _memread_dbg( _addr_ind_X_rd_dbg() ); } INLINE uint8_t _src_X_ind_dis(void) { return _memread_dis( _addr_ind_X_dis() ); } //INLINE uint8_t * dest_X_ind() { // return WRLOMEM + addr_ind_X(); //} /** Used only by [0x7C] JMP (ind,X) in 65C02) X,ind .... X-indexed, indirect OPC ($LL,X) operand is zeropage address; effective address is word in (LL + X, LL + X + 1), inc. without carry: C.w($00LL + X) **/ INLINE uint16_t _addr_ind_ind_X(void) { return memread16( (uint16_t)(_fetch16() + m6502.X) ); } INLINE uint16_t _addr_ind_ind_X_rd_dbg(void) { return _memread16_dbg( (uint16_t)(_fetch16() + m6502.X) ); } INLINE uint16_t _addr_ind_ind_X_dbg(void) { uint16_t addr = _memread16_dbg( (uint16_t)(_fetch16() + m6502.X)); check_mem_wr_bp(addr); // write debug on the target address return addr; } INLINE uint16_t _addr_ind_ind_X_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "($%02X,X)", memread16(m6502.PC) ); _disPrintf(disassembly.comment, sizeof(disassembly.comment), "ind_addr:%04X", memread16( memread16(m6502.PC) + m6502.X) ); return memread16( (uint16_t)(_fetch16_dis() + m6502.X) ); } /** ind,Y .... indirect, Y-indexed OPC ($LL),Y operand is zeropage address; effective address is word in (LL, LL + 1) incremented by Y with carry: C.w($00LL) + Y **/ INLINE uint16_t _addr_ind_Y(void) { return memread16_zp( _fetch() ) + m6502.Y; } INLINE uint16_t _addr_ind_Y_dbg(void) { uint16_t addr = _memread16_dbg( _fetch() ) + m6502.Y; check_mem_wr_bp(addr); return addr; } INLINE uint16_t _addr_ind_Y_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "($%02X),Y", memread8(m6502.PC) ); _disPrintf(disassembly.comment, sizeof(disassembly.comment), "ind_addr:%04X", memread16( memread8(m6502.PC) ) + m6502.Y ); return memread16( _fetch_dis() ) + m6502.Y; } INLINE uint8_t _src_ind_Y(void) { return _memread( _addr_ind_Y() ); } INLINE uint8_t _src_ind_Y_dbg(void) { return _memread_dbg( _addr_ind_Y() ); } INLINE uint8_t _src_ind_Y_dis(void) { return _memread_dis( _addr_ind_Y_dis() ); } //INLINE uint8_t * dest_ind_Y() { // return WRLOMEM + addr_ind_Y(); //} /** zpg,X .... zeropage, X-indexed OPC $LL,X operand is zeropage address; effective address is address incremented by X without carry ** **/ INLINE uint8_t _addr_zp_X(void) { return _fetch() + m6502.X; } INLINE uint8_t _addr_zp_X_dbg(void) { uint16_t addr = _fetch() + m6502.X; check_mem_wr_bp(addr); return addr; } INLINE uint8_t _addr_zp_X_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%02X,X", memread8(m6502.PC)); return _fetch_dis() + m6502.X; } INLINE uint8_t _src_zp_X(void) { return memread8_zp(_addr_zp_X()); } INLINE uint8_t _src_zp_X_dbg(void) { return _memread_dbg(_addr_zp_X()); } INLINE uint8_t _src_zp_X_dis(void) { return memread8_zp(_addr_zp_X_dis()); } //INLINE uint8_t * dest_zp_X() { // return WRLOMEM + addr_zp_X(); //} /** zpg,Y .... zeropage, Y-indexed OPC $LL,Y operand is zeropage address; effective address is address incremented by Y without carry ** **/ INLINE uint8_t _addr_zp_Y(void) { return _fetch() + m6502.Y; } INLINE uint8_t _addr_zp_Y_dbg(void) { uint16_t addr = _fetch() + m6502.Y; check_mem_wr_bp(addr); return addr; } INLINE uint8_t _addr_zp_Y_dis(void) { _disPrintf(disassembly.oper, sizeof(disassembly.oper), "$%02X,Y", memread8(m6502.PC)); return _fetch_dis() + m6502.Y; } INLINE uint8_t _src_zp_Y(void) { return memread8_zp(_addr_zp_Y()); } INLINE uint8_t _src_zp_Y_dbg(void) { return _memread_dbg(_addr_zp_Y()); } INLINE uint8_t _src_zp_Y_dis(void) { return memread8_zp(_addr_zp_Y_dis()); } //INLINE uint8_t * dest_zp_Y() { // return WRLOMEM + addr_zp_Y(); //} void auxMemorySelect( MEMcfg_t newMEMcfg ) { const uint8_t * newReadMEM = currentLowRDMEM; uint8_t * newWriteMEM = currentLowWRMEM; // TODO: Check if this is supposed to be the opposite // if ( newMEMcfg.is_80STORE ) { if ( newMEMcfg.RD_AUX_MEM ) { newReadMEM = Apple2_64K_AUX + 0x200; } else { newReadMEM = Apple2_64K_RAM + 0x200; } if ( newMEMcfg.WR_AUX_MEM ) { newWriteMEM = Apple2_64K_AUX; } else { newWriteMEM = Apple2_64K_RAM; } // } // else { // newReadMEM = Apple2_64K_RAM + 0x200; // newWriteMEM = Apple2_64K_RAM; // } // save old content to shadow memory if ( ( newWriteMEM != WRLOMEM ) && (WRLOMEM == Apple2_64K_MEM) ) { // save the content of Shadow Memory memcpy( (void*) currentLowWRMEM + 0x200, WRLOMEM + 0x200, 0xBE00); } // else { // // page in the new memory area // memcpy( (void*) shadowLowMEM + 0x200, newWriteMEM + 0x200, 0xBE00); // // mark new as the current one // } currentLowWRMEM = newWriteMEM; // printf("nrm:%p nwm:%p\n", newReadMEM, newWriteMEM + 0x200); // we are reading and writing to the same memory (either Internal or Aux) if ( newReadMEM == newWriteMEM + 0x200 ) { WRLOMEM = Apple2_64K_MEM; } else { WRLOMEM = newWriteMEM; } // load new content to shadow memory if ( newReadMEM != currentLowRDMEM ) { // page in the new memory area memcpy( (void*) shadowLowMEM, newReadMEM, 0xBE00); // mark new as the current one currentLowRDMEM = newReadMEM; } // save old content to shadow memory if ( newMEMcfg.ALT_ZP != MEMcfg.ALT_ZP ) { // save the content of Shadow ZP + Stack memcpy( (void*) currentZPSTCKMEM, shadowZPSTCKMEM, 0x200); // which ZP & Stack shall we use now? if ( newMEMcfg.ALT_ZP ) { currentZPSTCKMEM = Apple2_64K_AUX; } else { currentZPSTCKMEM = Apple2_64K_RAM; } // load content of SP & Stack memcpy( (void*) shadowZPSTCKMEM, (void*) currentZPSTCKMEM, 0x200); } // finally we can mark change MEMcfg = newMEMcfg; } void auxMemorySelect_old( MEMcfg_t newMEMcfg ) { const uint8_t * newLowMEM = currentLowRDMEM; if ( newMEMcfg.is_80STORE ) { if ( newMEMcfg.RD_AUX_MEM ) { newLowMEM = Apple2_64K_AUX + 0x200; } else { newLowMEM = Apple2_64K_RAM + 0x200; } if ( newMEMcfg.WR_AUX_MEM ) { if ( newMEMcfg.RD_AUX_MEM ) { WRLOMEM = Apple2_64K_MEM; } else { WRLOMEM = Apple2_64K_AUX; } } else { if ( newMEMcfg.RD_AUX_MEM ) { WRLOMEM = Apple2_64K_AUX; } else { WRLOMEM = Apple2_64K_MEM; } } } else { newLowMEM = Apple2_64K_RAM + 0x200; WRLOMEM = Apple2_64K_MEM; } // load new content to shadow memory if ( newLowMEM != currentLowRDMEM ) { // save the content of Shadow Memory memcpy( (void*) currentLowRDMEM, shadowLowMEM, 0xBE00); // page in the new memory area memcpy( (void*) shadowLowMEM, newLowMEM, 0xBE00); // mark new as the current one currentLowRDMEM = newLowMEM; } MEMcfg = newMEMcfg; } //void (*auxMemorySelect)( MEMcfg_t newMEMcfg ) = & auxMemorySelect_new; void C3MemorySelect( MEMcfg_t newMEMcfg ) { if ( newMEMcfg.slot_C3_ROM ) { // load peripheral ROM to memory memcpy(Apple2_64K_MEM + 0xC300, EXP_64K_ROM + 0xC300, 0x100); } else { // load internal ROM to memory memcpy(Apple2_64K_MEM + 0xC300, INT_64K_ROM + 0xC300, 0x100); } MEMcfg = newMEMcfg; } void CxMemorySelect( MEMcfg_t newMEMcfg ) { if ( newMEMcfg.int_Cx_ROM ) { // load internal ROM to memory memcpy(Apple2_64K_MEM + 0xC100, INT_64K_ROM + 0xC100, 0xF00); } else { // load peripheral ROM to memory memcpy(Apple2_64K_MEM + 0xC100, EXP_64K_ROM + 0xC100, 0xF00); // memcpy(Apple2_64K_MEM + 0xC600, Apple2_64K_RAM + 0xC600, 0x100); } C3MemorySelect( newMEMcfg ); MEMcfg = newMEMcfg; } void resetMemory(void) { newMEMcfg = initMEMcfg; WRZEROPG = Apple2_64K_MEM; // for Write $0000 - $0200 (shadow memory) WRLOMEM = Apple2_64K_MEM; // for Write $0200 - $BFFF (shadow memory) WRD0MEM = Apple2_Dummy_RAM; // for writing $D000 - $DFFF WRHIMEM = Apple2_Dummy_RAM; // for writing $E000 - $FFFF auxMemorySelect(MEMcfg); CxMemorySelect(MEMcfg); // initializing disk controller memcpy(Apple2_64K_MEM + 0xC600, EXP_64K_ROM + 0xC600, 0x100); MEMcfg = newMEMcfg; videoMode.text = 1; videoMode.col80 = 0; } void initMemory(void) { // Aux Video Memory memset( Apple2_64K_AUX, 0, sizeof(Apple2_64K_AUX) ); // 64K Main Memory Area memset( Apple2_64K_RAM, 0, sizeof(Apple2_64K_RAM) ); memset( Apple2_64K_MEM, 0, sizeof(Apple2_64K_MEM) ); // text memory should be filled by spaces memset( Apple2_64K_AUX + 0x400, 0xA0, 0x800 ); memset( Apple2_64K_RAM + 0x400, 0xA0, 0x800 ); memset( Apple2_64K_MEM + 0x400, 0xA0, 0x800 ); // I/O area should be 0 -- just in case we decide to init RAM with a different pattern... memset( Apple2_64K_RAM + 0xC000, 0, 0x1000 ); resetMemory(); } inline uint8_t *extracted(void) { uint8_t * shadow = Apple2_64K_MEM + 0x400; return shadow; } void textPageSelect(void) { uint8_t textAuxPage = MEMcfg.is_80STORE && MEMcfg.txt_page_2; if ( activeTextAuxPage != textAuxPage ) { activeTextAuxPage = textAuxPage; uint8_t * newTextPage = ( textAuxPage ? Apple2_64K_AUX : Apple2_64K_RAM ) + 0x400; if ( activeTextPage ) { // save the content of Shadow Memory memcpy(activeTextPage, shadowTextPage, 0x400); } // load the content of new Video Page memcpy(shadowTextPage, newTextPage, 0x400); activeTextPage = newTextPage; } } // TODO: uint8_t getIO ( uint16_t ioaddr ) { return Apple2_64K_RAM[ioaddr]; } void setIO ( uint16_t ioaddr, uint8_t val ) { Apple2_64K_RAM[ioaddr] = val; } uint8_t getMEM ( uint16_t addr ) { return memread8(addr); // return Apple2_64K_MEM[addr]; } uint16_t getMEM16 ( uint16_t addr ) { return *(uint16_t*)(&Apple2_64K_MEM[addr]); } uint32_t getMEM32 ( uint16_t addr ) { return *(uint32_t*)(&Apple2_64K_MEM[addr]); } void setMEM ( uint16_t addr, uint8_t val ) { Apple2_64K_MEM[addr] = val; } void setMEM16 ( uint16_t addr, uint16_t val ) { *(uint16_t*)(&Apple2_64K_MEM[addr]) = val; } void setMEM32 ( uint16_t addr, uint32_t val ) { *(uint32_t*)(&Apple2_64K_MEM[addr]) = val; } void setMEMarray ( uint16_t addr, uint8_t * arr, int len ) { while (len--) { Apple2_64K_MEM[addr++] = *arr++; } } void kbdInput ( uint8_t code ) { code = kbdCodeConvert(code); for (int i = 0; i <= 0xF; i++) { Apple2_64K_RAM[io_KBD + i] = code; // most significant bit is a status bit of other things Apple2_64K_RAM[io_KBDSTRB + i] = (Apple2_64K_RAM[io_KBDSTRB + i] & (1<<7)) | (code & ~(1<<7)); } // // mark key pressed // Apple2_64K_RAM[io_KBDSTRB] |= 1<<7; } void kbdUp (void) { // mark key depressed Apple2_64K_RAM[io_KBDSTRB] &= 0x7F; }