mirror of
https://github.com/bradgrantham/apple2e.git
synced 2025-04-18 16:37:38 +00:00
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
This commit is contained in:
parent
fb476db138
commit
604bf07b4f
502
apple2e.cpp
502
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<unsigned int, string> 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<int> ignore_mmio = {0xC058, 0xC05A, 0xC05D, 0xC05F, 0xC061, 0xC062};
|
||||
set<int> 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<chrono::system_clock> 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);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern uint32_t clockticks6502;
|
||||
|
||||
void reset6502();
|
||||
void nmi6502();
|
||||
void irq6502();
|
||||
|
@ -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__);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user