1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-10-31 13:07:14 +00:00

Add pc file for peripheral card ROM

This commit is contained in:
Peter Evans 2018-01-15 23:50:33 -06:00
parent 2db5c791ba
commit a0a5132099
6 changed files with 256 additions and 6 deletions

View File

@ -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
View 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

View File

@ -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

View File

@ -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

View File

@ -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];
}
if (mach->memory_mode & MEMORY_SLOTCXROM) {
segment = mach->rom;
/*
* 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)
? 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
View 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);
}