mirror of
https://github.com/pevans/erc-c.git
synced 2024-09-29 11:55:01 +00:00
b2bfee7f96
Which seems weird, but I've verified this in some other places.
191 lines
5.4 KiB
C
191 lines
5.4 KiB
C
/*
|
|
* apple2.pc.c
|
|
*
|
|
* No, not "personal computer"; pc as in "peripheral card". The code
|
|
* here is all about support for peripherals in general, excusing
|
|
* support written for specific peripherals such as disk drives
|
|
* (apple2.dd.c).
|
|
*/
|
|
|
|
#include "apple2.pc.h"
|
|
|
|
static size_t switch_reads[] = {
|
|
0xC015,
|
|
0xC017,
|
|
};
|
|
|
|
static size_t switch_writes[] = {
|
|
0xC006,
|
|
0xC007,
|
|
0xC00A,
|
|
0xC00B,
|
|
};
|
|
|
|
/*
|
|
* Map all of the peripheral ROM space (and peripheral expansion ROM
|
|
* space) to our read and write mappers for that. For reference, normal
|
|
* peripheral ROM space is $C100..$C7FF, and expansion ROM is
|
|
* $C800..$CFFF.
|
|
*/
|
|
void
|
|
apple2_pc_map(vm_segment *seg)
|
|
{
|
|
size_t addr;
|
|
int i;
|
|
int rlen, wlen;
|
|
|
|
for (addr = 0xC100; addr < 0xD000; addr++) {
|
|
vm_segment_read_map(seg, addr, apple2_pc_read);
|
|
vm_segment_write_map(seg, addr, apple2_pc_write);
|
|
}
|
|
|
|
rlen = sizeof(switch_reads) / sizeof(size_t);
|
|
wlen = sizeof(switch_writes) / sizeof(size_t);
|
|
|
|
for (i = 0; i < rlen; i++) {
|
|
vm_segment_read_map(seg, switch_reads[i], apple2_pc_switch_read);
|
|
}
|
|
|
|
for (i = 0; i < wlen; i++) {
|
|
vm_segment_write_map(seg, switch_writes[i], apple2_pc_switch_write);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This mapper handles the entire potential peripheral ROM space; we
|
|
* either need to return an address from the beginning of memory, or
|
|
* near the end.
|
|
*/
|
|
SEGMENT_READER(apple2_pc_read)
|
|
{
|
|
apple2 *mach = (apple2 *)_mach;
|
|
|
|
// The address in the rom segment gets translated through a number
|
|
// of factors
|
|
addr = apple2_pc_rom_addr(addr, mach->memory_mode);
|
|
|
|
// No matter what we do, the segment we return from will always be
|
|
// the rom segment. This part is non-negotiable.
|
|
return mach->rom->memory[addr];
|
|
}
|
|
|
|
/*
|
|
* Since all of our address spaces are mapped to ROM, we can't write
|
|
* anything!
|
|
*/
|
|
SEGMENT_WRITER(apple2_pc_write)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Given an address from program code, return the corresponding address
|
|
* in our rom segment. The machine's memory mode is also given as a
|
|
* factor.
|
|
*/
|
|
size_t
|
|
apple2_pc_rom_addr(size_t addr, vm_8bit mode)
|
|
{
|
|
size_t rom_addr;
|
|
|
|
// This mapper should only be called from the $C000..$CFFF range;
|
|
// in the rom segment, this group of pages is addressed at
|
|
// $0000..$0FFF. We can assume that regardless of what addr is, a
|
|
// subtraction of $C000 would result in a valid address.
|
|
rom_addr = addr - 0xC000;
|
|
|
|
// However, if the EXPROM bit is high, then we want to use expansion
|
|
// ROM which is located at the _end_ of the rom segment; specifically
|
|
// in the $4800..$4FFF range.
|
|
if (rom_addr >= 0x0800 && rom_addr < 0x1000 &&
|
|
mode & MEMORY_EXPROM
|
|
) {
|
|
rom_addr += 0x4000;
|
|
}
|
|
|
|
// If SLOTCXROM is high, then we want any byte addressed in this
|
|
// space to reference peripheral ROM (which is again located at the
|
|
// end of the segment, in exactly the same way that expansion ROM is
|
|
// placed).
|
|
if (rom_addr >= 0x0100 && rom_addr < 0x0800 &&
|
|
mode & MEMORY_SLOTCXROM
|
|
) {
|
|
rom_addr += 0x4000;
|
|
}
|
|
|
|
// One final thing to account for is if the SLOTC3ROM bit is high.
|
|
// If it is, and if the address has not already been incremented
|
|
// because SLOTCXROM was _also_ high, then we need to increment just
|
|
// for this specific page of memory.
|
|
if (rom_addr >= 0x0300 && rom_addr < 0x0400 &&
|
|
mode & MEMORY_SLOTC3ROM
|
|
) {
|
|
rom_addr += 0x4000;
|
|
}
|
|
|
|
return rom_addr;
|
|
}
|
|
|
|
/*
|
|
* Handle reads to the slot / peripheral ROM switches. I'm not
|
|
* _entirely_ sure what Apple expects to be returned here, since they
|
|
* don't indicate as clearly as they did with bank-switch modes; there,
|
|
* they said you need to return a byte with the 7 bit high, which you
|
|
* can then use a BPL or a BMI to branch with. Here, they don't specify
|
|
* bit 7, but I'm going to go with that until I see otherwise.
|
|
*/
|
|
SEGMENT_READER(apple2_pc_switch_read)
|
|
{
|
|
apple2 *mach = (apple2 *)_mach;
|
|
|
|
switch (addr) {
|
|
case 0xC015:
|
|
return (mach->memory_mode & MEMORY_SLOTCXROM)
|
|
? 0x00
|
|
: 0x80;
|
|
case 0xC017:
|
|
return (mach->memory_mode & MEMORY_SLOTC3ROM)
|
|
? 0x80
|
|
: 0x00;
|
|
}
|
|
|
|
// We shouldn't get here for any practical reason, but...
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Handle writes to the slot switches. There are separate writes to turn
|
|
* these switches on and off, for each switch, respectively. The value
|
|
* being written doesn't matter (indeed--you won't see us reference the
|
|
* value parameter).
|
|
*/
|
|
SEGMENT_WRITER(apple2_pc_switch_write)
|
|
{
|
|
apple2 *mach = (apple2 *)_mach;
|
|
|
|
// We don't care what the value is; if a write happens to any of the
|
|
// following addresses, we must change the peripheral memory
|
|
// behavior accordingly.
|
|
switch (addr) {
|
|
case 0xC00B:
|
|
apple2_set_memory_mode(mach,
|
|
mach->memory_mode | MEMORY_SLOTC3ROM);
|
|
break;
|
|
case 0xC00A:
|
|
apple2_set_memory_mode(mach,
|
|
mach->memory_mode & ~MEMORY_SLOTC3ROM);
|
|
break;
|
|
|
|
case 0xC006:
|
|
apple2_set_memory_mode(mach,
|
|
mach->memory_mode | MEMORY_SLOTCXROM);
|
|
break;
|
|
|
|
case 0xC007:
|
|
apple2_set_memory_mode(mach,
|
|
mach->memory_mode & ~MEMORY_SLOTCXROM);
|
|
break;
|
|
}
|
|
}
|