Steve2/src/dev/mem/mmio.c

1884 lines
55 KiB
C

//
// 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 <https://www.gnu.org/licenses/>.
//
#include <stdlib.h>
#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;
}