mirror of
https://github.com/pevans/erc-c.git
synced 2024-11-23 23:32:45 +00:00
Add pc file for peripheral card ROM
This commit is contained in:
parent
2db5c791ba
commit
a0a5132099
@ -128,6 +128,30 @@ enum memory_mode {
|
||||
*/
|
||||
MEMORY_PAGE2 = 0x8,
|
||||
MEMORY_HIRES = 0x10,
|
||||
|
||||
/*
|
||||
* When this is high, expansion ROM is considered in use. That means
|
||||
* that the $C800..$CFFF range will be mapped to the expansion ROM
|
||||
* area of the rom segment (which is at the end), vs. the internal
|
||||
* ROM area, which is at the $0800..$0FFF range within the rom
|
||||
* segment.
|
||||
*/
|
||||
MEMORY_EXPROM = 0x20,
|
||||
|
||||
/*
|
||||
* When SLOTCXROM is high, the entire range of $C100..$C7FF will be
|
||||
* mapped to the peripheral ROM area of the rom segment (which is in
|
||||
* the $4100..$47FF address range there); otherwise, $C100...$C7FF
|
||||
* is mapped to internal ROM, located at $0100..$07FF within the
|
||||
* same rom segment.
|
||||
*
|
||||
* It's not possible to map a single peripheral ROM page, with the
|
||||
* exception of slot 3 (via SLOTC3ROM). That page is special because
|
||||
* of its use by the 80-column text card. You can have SLOTC3ROM
|
||||
* high but SLOTCXROM low.
|
||||
*/
|
||||
MEMORY_SLOTCXROM = 0x40,
|
||||
MEMORY_SLOTC3ROM = 0x80,
|
||||
};
|
||||
|
||||
enum bank_switch {
|
||||
|
14
include/apple2.pc.h
Normal file
14
include/apple2.pc.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _APPLE2_PC_H_
|
||||
#define _APPLE2_PC_H_
|
||||
|
||||
#include "apple2.h"
|
||||
#include "vm_segment.h"
|
||||
|
||||
extern SEGMENT_READER(apple2_pc_read);
|
||||
extern SEGMENT_READER(apple2_pc_switch_read);
|
||||
extern SEGMENT_WRITER(apple2_pc_switch_write);
|
||||
extern SEGMENT_WRITER(apple2_pc_write);
|
||||
extern size_t apple2_pc_rom_addr(size_t, vm_8bit);
|
||||
extern void apple2_pc_map(vm_segment *);
|
||||
|
||||
#endif
|
@ -5,6 +5,7 @@ set(erc_sources
|
||||
apple2.dd.c
|
||||
apple2.draw.c
|
||||
apple2.mem.c
|
||||
apple2.pc.c
|
||||
log.c
|
||||
mos6502.c
|
||||
mos6502.addr.c
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "apple2.bank.h"
|
||||
#include "apple2.dbuf.h"
|
||||
#include "apple2.pc.h"
|
||||
#include "apple2.h"
|
||||
#include "apple2.mem.h"
|
||||
#include "objstore.h"
|
||||
@ -25,6 +26,8 @@ apple2_mem_map(apple2 *mach, vm_segment *segment)
|
||||
// Here we handle the 80STORE bit for our display buffers.
|
||||
apple2_dbuf_map(segment);
|
||||
|
||||
apple2_pc_map(segment);
|
||||
|
||||
// We will do the mapping for the zero page and stack addresses.
|
||||
// Accessing those addresses can be affected by bank-switching, but
|
||||
// those addresses do not actually exist in the capital
|
||||
|
162
src/apple2.pc.c
162
src/apple2.pc.c
@ -7,8 +7,19 @@
|
||||
* (apple2.dd.c).
|
||||
*/
|
||||
|
||||
#include "apple2.h"
|
||||
#include "vm_segment.h"
|
||||
#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
|
||||
@ -17,24 +28,163 @@
|
||||
* $C800..$CFFF.
|
||||
*/
|
||||
void
|
||||
apple2_pc_map(apple2 *mach, vm_segment *seg)
|
||||
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;
|
||||
|
||||
if (mach->memory_mode & MEMORY_EXPROM) {
|
||||
// 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 (mach->memory_mode & MEMORY_SLOTCXROM) {
|
||||
segment = mach->rom;
|
||||
// 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)
|
||||
? 0x80
|
||||
: 0x00;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
58
tests/apple2.pc.c
Normal file
58
tests/apple2.pc.c
Normal file
@ -0,0 +1,58 @@
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include "apple2.h"
|
||||
#include "apple2.pc.h"
|
||||
|
||||
static apple2 *mach = NULL;
|
||||
|
||||
static void
|
||||
setup()
|
||||
{
|
||||
mach = apple2_create(100, 100);
|
||||
vm_segment_set_map_machine(mach);
|
||||
}
|
||||
|
||||
static void
|
||||
teardown()
|
||||
{
|
||||
apple2_free(mach);
|
||||
vm_segment_set_map_machine(NULL);
|
||||
}
|
||||
|
||||
TestSuite(apple2_pc, .init = setup, .fini = teardown);
|
||||
|
||||
Test(apple2_pc, map)
|
||||
{
|
||||
size_t addr;
|
||||
int i;
|
||||
vm_segment *segments[2];
|
||||
|
||||
segments[0] = mach->main;
|
||||
segments[1] = mach->aux;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (addr = 0xC100; addr < 0xD000; addr++) {
|
||||
cr_assert_eq(segments[i]->read_table[addr], apple2_pc_read);
|
||||
cr_assert_eq(segments[i]->write_table[addr], apple2_pc_write);
|
||||
}
|
||||
|
||||
cr_assert_eq(segments[i]->read_table[0xC015], apple2_pc_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC017], apple2_pc_switch_read);
|
||||
cr_assert_eq(segments[i]->write_table[0xC00B], apple2_pc_switch_write);
|
||||
cr_assert_eq(segments[i]->write_table[0xC00A], apple2_pc_switch_write);
|
||||
cr_assert_eq(segments[i]->write_table[0xC006], apple2_pc_switch_write);
|
||||
cr_assert_eq(segments[i]->write_table[0xC007], apple2_pc_switch_write);
|
||||
}
|
||||
}
|
||||
|
||||
Test(apple2_pc, rom_addr)
|
||||
{
|
||||
cr_assert_eq(apple2_pc_rom_addr(0xC832, MEMORY_DEFAULT), 0x832);
|
||||
cr_assert_eq(apple2_pc_rom_addr(0xC832, MEMORY_SLOTCXROM), 0x832);
|
||||
cr_assert_eq(apple2_pc_rom_addr(0xC832, MEMORY_SLOTC3ROM), 0x832);
|
||||
cr_assert_eq(apple2_pc_rom_addr(0xC232, MEMORY_EXPROM), 0x0232);
|
||||
|
||||
cr_assert_eq(apple2_pc_rom_addr(0xC832, MEMORY_EXPROM), 0x4832);
|
||||
cr_assert_eq(apple2_pc_rom_addr(0xC732, MEMORY_SLOTCXROM), 0x4732);
|
||||
cr_assert_eq(apple2_pc_rom_addr(0xC332, MEMORY_SLOTC3ROM), 0x4332);
|
||||
}
|
Loading…
Reference in New Issue
Block a user