mirror of
https://github.com/pevans/erc-c.git
synced 2025-02-04 13:30:30 +00:00
Switch bank code into its own file
This commit is contained in:
parent
208c909fc2
commit
3738d70a43
13
include/apple2.bank.h
Normal file
13
include/apple2.bank.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef _APPLE2_BANK_H_
|
||||
#define _APPLE2_BANK_H_
|
||||
|
||||
#include "apple2.mem.h"
|
||||
#include "vm_segment.h"
|
||||
|
||||
extern SEGMENT_READER(apple2_bank_read);
|
||||
extern SEGMENT_READER(apple2_bank_switch_read);
|
||||
extern SEGMENT_WRITER(apple2_bank_switch_write);
|
||||
extern SEGMENT_WRITER(apple2_bank_write);
|
||||
extern void apple2_bank_map(vm_segment *);
|
||||
|
||||
#endif
|
@ -56,13 +56,8 @@
|
||||
*/
|
||||
#define APPLE2_BANK_OFFSET 0xD000
|
||||
|
||||
extern SEGMENT_READER(apple2_mem_read_bank);
|
||||
extern SEGMENT_WRITER(apple2_mem_write_bank);
|
||||
extern int apple2_mem_init_peripheral_rom(apple2 *);
|
||||
extern int apple2_mem_init_sys_rom(apple2 *);
|
||||
extern void apple2_mem_map(apple2 *, vm_segment *);
|
||||
extern void apple2_mem_map_bank_switch(vm_segment *);
|
||||
extern SEGMENT_READER(apple2_mem_read_bank_switch);
|
||||
extern SEGMENT_WRITER(apple2_mem_write_bank_switch);
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,6 @@
|
||||
set(erc_sources
|
||||
apple2.c
|
||||
apple2.bank.c
|
||||
apple2.dd.c
|
||||
apple2.draw.c
|
||||
apple2.mem.c
|
||||
|
253
src/apple2.bank.c
Normal file
253
src/apple2.bank.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* apple2.mem.c
|
||||
*/
|
||||
|
||||
#include "apple2.h"
|
||||
#include "apple2.bank.h"
|
||||
#include "apple2.mem.h"
|
||||
#include "objstore.h"
|
||||
|
||||
/*
|
||||
* These are the addresses that need to be mapped to the
|
||||
* bank_switch_read function.
|
||||
*/
|
||||
static size_t switch_reads[] = {
|
||||
0xC080,
|
||||
0xC081,
|
||||
0xC082,
|
||||
0xC083,
|
||||
0xC088,
|
||||
0xC089,
|
||||
0xC08A,
|
||||
0xC08B,
|
||||
0xC011,
|
||||
0xC012,
|
||||
0xC016,
|
||||
};
|
||||
|
||||
/*
|
||||
* These will be mapped to the bank_switch_write function.
|
||||
*/
|
||||
static size_t switch_writes[] = {
|
||||
0xC008,
|
||||
0xC009,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return a byte of memory from a bank-switchable address. This may be
|
||||
* from ROM, from main memory, or from the "extra" 4k bank of RAM.
|
||||
*/
|
||||
SEGMENT_READER(apple2_bank_read)
|
||||
{
|
||||
apple2 *mach;
|
||||
|
||||
mach = (apple2 *)_mach;
|
||||
|
||||
// In the case of bank-switchable memory, BANK_ALTZP is the ultimate
|
||||
// arbitrator; if it's on, we have to use aux, and if not, we have
|
||||
// to use main. Whatever the segment was that was passed in will
|
||||
// turn out to be immaterial.
|
||||
segment = (mach->bank_switch & BANK_ALTZP) ? mach->aux : mach->main;
|
||||
|
||||
if (~mach->bank_switch & BANK_RAM) {
|
||||
// We need to account for the difference in address location
|
||||
// before we can successfully get any data from ROM.
|
||||
return vm_segment_get(mach->rom, addr - APPLE2_BANK_OFFSET);
|
||||
}
|
||||
|
||||
// Each memory bank (main or auxiliary) have an additional 4k of RAM
|
||||
// that you can access through bank-switching in the $D000 - $DFFF
|
||||
// range, which is actually held at the _end_ of memory beyond the
|
||||
// 64k mark.
|
||||
if (addr < 0xE000 && mach->bank_switch & BANK_RAM2) {
|
||||
// The same caution holds for getting data from the
|
||||
// second RAM bank.
|
||||
return segment->memory[addr + 0x3000];
|
||||
}
|
||||
|
||||
// Otherwise, the byte is returned from bank 1 RAM, which is the
|
||||
// literal memory available in the segment.
|
||||
return segment->memory[addr];
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a byte into bank-switchable memory. Many of the same cautions,
|
||||
* notes, etc. written for the read function apply here as well.
|
||||
*/
|
||||
SEGMENT_WRITER(apple2_bank_write)
|
||||
{
|
||||
apple2 *mach;
|
||||
|
||||
mach = (apple2 *)_mach;
|
||||
|
||||
// No writes are allowed... sorry!
|
||||
if (~mach->bank_switch & BANK_WRITE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// See my spiel in the read bank mapper; the same applies here.
|
||||
segment = (mach->bank_switch & BANK_ALTZP) ? mach->aux : mach->main;
|
||||
|
||||
// You will note, if we've gotten here, that it's possible to write
|
||||
// to the bank-switch addresses even if the ROM flag is 1. It's
|
||||
// true! Except that writes never go to ROM. That is to say, it's
|
||||
// possible to read from ROM and write to RAM at the same
|
||||
// time--well, nearly the same time, considering the 6502 does not
|
||||
// allow parallel actions!
|
||||
|
||||
// In this case, we need to assign the value at the 64-68k range at
|
||||
// the end of memory; this is just a simple offset from the given
|
||||
// address.
|
||||
if (addr < 0xE000 && mach->bank_switch & BANK_RAM2) {
|
||||
segment->memory[addr + 0x3000] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
// But if bank 2 RAM is not turned on, or the address is between
|
||||
// $E000 - $FFFF, then writes go to bank 1 RAM, which is our main
|
||||
// memory.
|
||||
segment->memory[addr] = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will establish all of the mapper functions to handle
|
||||
* the soft switches for memory bank-switching.
|
||||
*/
|
||||
void
|
||||
apple2_bank_map(vm_segment *segment)
|
||||
{
|
||||
size_t addr;
|
||||
int i, rlen, wlen;
|
||||
|
||||
for (addr = APPLE2_BANK_OFFSET; addr < MOS6502_MEMSIZE; addr++) {
|
||||
vm_segment_read_map(segment, addr, apple2_bank_read);
|
||||
vm_segment_write_map(segment, addr, apple2_bank_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(segment, switch_reads[i],
|
||||
apple2_bank_switch_read);
|
||||
}
|
||||
|
||||
for (i = 0; i < wlen; i++) {
|
||||
vm_segment_write_map(segment, switch_writes[i],
|
||||
apple2_bank_switch_write);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle reads to the soft switches that handle bank-switching. Note
|
||||
* that some of these "reads" actually modify how banks are switched
|
||||
* between ROM, RAM, or bank 2 RAM. Sorry about that -- it's just the
|
||||
* way it worked on the Apple II.
|
||||
*/
|
||||
SEGMENT_READER(apple2_bank_switch_read)
|
||||
{
|
||||
apple2 *mach;
|
||||
vm_16bit last_addr;
|
||||
|
||||
mach = (apple2 *)_mach;
|
||||
|
||||
// We need to know the last opcode and address, because some of our
|
||||
// soft switches require two consecutive reads
|
||||
mos6502_last_executed(mach->cpu, NULL, NULL, &last_addr);
|
||||
|
||||
switch (addr) {
|
||||
// The $C080 - $C083 range all control memory access while using
|
||||
// bank 2 RAM for the $Dnnn range. Note that here and in the
|
||||
// $C088 range, the returns are zero; I'm not exactly sure
|
||||
// that's what they should be, but the purpose of reading from
|
||||
// these soft switches is not actually to read anything useful,
|
||||
// but simply to change the bank switch mode.
|
||||
case 0xC080:
|
||||
apple2_set_bank_switch(mach, BANK_RAM | BANK_RAM2);
|
||||
return 0;
|
||||
|
||||
case 0xC081:
|
||||
if (last_addr == addr) {
|
||||
apple2_set_bank_switch(mach, BANK_WRITE | BANK_RAM2);
|
||||
}
|
||||
return 0;
|
||||
case 0xC082:
|
||||
apple2_set_bank_switch(mach, BANK_RAM2);
|
||||
return 0;
|
||||
|
||||
case 0xC083:
|
||||
if (last_addr == addr) {
|
||||
apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE | BANK_RAM2);
|
||||
}
|
||||
return 0;
|
||||
|
||||
// Conversely, the $C088 - $C08B range control memory access
|
||||
// while using bank 1 RAM.
|
||||
case 0xC088:
|
||||
apple2_set_bank_switch(mach, BANK_RAM);
|
||||
return 0;
|
||||
|
||||
case 0xC089:
|
||||
if (last_addr == addr) {
|
||||
apple2_set_bank_switch(mach, BANK_WRITE);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case 0xC08A:
|
||||
apple2_set_bank_switch(mach, BANK_DEFAULT);
|
||||
return 0;
|
||||
|
||||
case 0xC08B:
|
||||
if (last_addr == addr) {
|
||||
apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE);
|
||||
}
|
||||
return 0;
|
||||
|
||||
// Return high on the 7th bit if we're using bank 2 memory
|
||||
case 0xC011:
|
||||
return mach->bank_switch & BANK_RAM2
|
||||
? 0x80
|
||||
: 0x00;
|
||||
|
||||
// Return high on 7th bit if we're reading RAM
|
||||
case 0xC012:
|
||||
return mach->bank_switch & BANK_RAM
|
||||
? 0x80
|
||||
: 0x00;
|
||||
|
||||
// Return high on the 7th bit if we are using the zero page and
|
||||
// stack from aux memory.
|
||||
case 0xC016:
|
||||
return mach->bank_switch & BANK_ALTZP
|
||||
? 0x80
|
||||
: 0x00;
|
||||
}
|
||||
|
||||
log_critical("Bank switch read mapper called with an unexpected address: %x", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle writes to the soft switches that modify bank-switching
|
||||
* behavior.
|
||||
*/
|
||||
SEGMENT_WRITER(apple2_bank_switch_write)
|
||||
{
|
||||
apple2 *mach = (apple2 *)_mach;
|
||||
|
||||
switch (addr) {
|
||||
// Turn on auxiliary memory for zero page + stack
|
||||
case 0xC008:
|
||||
apple2_set_bank_switch(mach,
|
||||
mach->bank_switch | BANK_ALTZP);
|
||||
return;
|
||||
|
||||
// Disable auxiliary memory for zero page + stack
|
||||
case 0xC009:
|
||||
apple2_set_bank_switch(mach,
|
||||
mach->bank_switch & ~BANK_ALTZP);
|
||||
return;
|
||||
}
|
||||
|
||||
log_critical("Bank switch write mapper called with an unexpected address: %x", addr);
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "apple2.h"
|
||||
#include "apple2.mem.h"
|
||||
#include "apple2.bank.h"
|
||||
#include "objstore.h"
|
||||
|
||||
/*
|
||||
@ -92,34 +93,7 @@ apple2_mem_map(apple2 *mach, vm_segment *segment)
|
||||
|
||||
vm_segment_set_map_machine(mach);
|
||||
|
||||
for (addr = APPLE2_BANK_OFFSET; addr < MOS6502_MEMSIZE; addr++) {
|
||||
vm_segment_read_map(segment, addr, apple2_mem_read_bank);
|
||||
vm_segment_write_map(segment, addr, apple2_mem_write_bank);
|
||||
}
|
||||
|
||||
apple2_mem_map_bank_switch(segment);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will establish all of the mapper functions to handle
|
||||
* the soft switches for memory bank-switching.
|
||||
*/
|
||||
void
|
||||
apple2_mem_map_bank_switch(vm_segment *segment)
|
||||
{
|
||||
vm_segment_read_map(segment, 0xC080, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC081, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC082, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC083, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC088, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC089, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC08A, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC08B, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC011, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC012, apple2_mem_read_bank_switch);
|
||||
vm_segment_read_map(segment, 0xC016, apple2_mem_read_bank_switch);
|
||||
vm_segment_write_map(segment, 0xC008, apple2_mem_write_bank_switch);
|
||||
vm_segment_write_map(segment, 0xC009, apple2_mem_write_bank_switch);
|
||||
apple2_bank_map(segment);
|
||||
}
|
||||
|
||||
/*
|
||||
|
193
tests/apple2.bank.c
Normal file
193
tests/apple2.bank.c
Normal file
@ -0,0 +1,193 @@
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include "apple2.h"
|
||||
#include "apple2.bank.h"
|
||||
#include "vm_segment.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_bank, .init = setup, .fini = teardown);
|
||||
|
||||
Test(apple2_bank, map)
|
||||
{
|
||||
// mach already had the mem_map function run, so we just need to
|
||||
// test the results.
|
||||
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 = APPLE2_BANK_OFFSET; addr < MOS6502_MEMSIZE; addr++) {
|
||||
cr_assert_eq(segments[i]->read_table[addr], apple2_bank_read);
|
||||
cr_assert_eq(segments[i]->write_table[addr], apple2_bank_write);
|
||||
}
|
||||
|
||||
cr_assert_eq(segments[i]->read_table[0xC080], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC081], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC082], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC083], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC088], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC089], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC08A], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC08B], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC088], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC011], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC012], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->read_table[0xC016], apple2_bank_switch_read);
|
||||
cr_assert_eq(segments[i]->write_table[0xC008], apple2_bank_switch_write);
|
||||
cr_assert_eq(segments[i]->write_table[0xC009], apple2_bank_switch_write);
|
||||
}
|
||||
}
|
||||
|
||||
Test(apple2_bank, read)
|
||||
{
|
||||
vm_8bit val;
|
||||
|
||||
// Test that setting a value in the rom segment is returned to us
|
||||
// when addressing from main memory
|
||||
apple2_set_bank_switch(mach, BANK_WRITE);
|
||||
val = 123;
|
||||
vm_segment_set(mach->rom, 0x77, val);
|
||||
val = vm_segment_get(mach->rom, 0x77);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), val);
|
||||
|
||||
// In RAM1 bank mode, setting a value in memory should return thaty
|
||||
// value in memory... but, as a twist, also check that the value is
|
||||
// not set in ROM nor in RAM2.
|
||||
val = 222;
|
||||
apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE);
|
||||
vm_segment_set(mach->main, 0xD077, val);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), val);
|
||||
cr_assert_neq(vm_segment_get(mach->rom, 0x77), val);
|
||||
cr_assert_neq(vm_segment_get(mach->main, 0x10077), val);
|
||||
|
||||
// Finally, in RAM2 bank mode, we test similarly to ROM mode. Set
|
||||
// the value directly in ram2 (which is at $10000 - $1FFFF) and see
|
||||
// if it's there when addressing from main memory in the $Dnnn
|
||||
// range.
|
||||
val = 111;
|
||||
apple2_set_bank_switch(mach, mach->bank_switch | BANK_RAM2);
|
||||
vm_segment_set(mach->main, 0x10077, val);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), val);
|
||||
}
|
||||
|
||||
/*
|
||||
* The write_bank test will look a bit similar to the read_bank one,
|
||||
* except in logic it should be written somewhat as an inverse. That is,
|
||||
* we want our writes to all go to memory, and double-check that the
|
||||
* right location is being updated (or not being updated).
|
||||
*/
|
||||
Test(apple2_bank, write)
|
||||
{
|
||||
vm_8bit right, wrong;
|
||||
|
||||
// In BANK_DEFAULT mode, we expect that updates to ROM will never be
|
||||
// successful (after all, it wouldn't be read-only memory if they
|
||||
// were).
|
||||
right = 123;
|
||||
wrong = 222;
|
||||
vm_segment_set(mach->rom, 0x77, right);
|
||||
vm_segment_set(mach->main, 0xD077, wrong);
|
||||
cr_assert_eq(vm_segment_get(mach->rom, 0x77), right);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), right);
|
||||
|
||||
// RAM1 is the main bank; it's all 64k RAM in one chunk.
|
||||
right = 111;
|
||||
wrong = 232;
|
||||
apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE);
|
||||
vm_segment_set(mach->main, 0xD078, right);
|
||||
vm_segment_set(mach->main, 0x10078, wrong);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD078), right);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0x10078), wrong);
|
||||
|
||||
// RAM2 is most of the 64k, except the first 4k of the last 12
|
||||
// ($D000..$DFFF) is in ram2.
|
||||
right = 210;
|
||||
wrong = 132;
|
||||
apple2_set_bank_switch(mach, mach->bank_switch | BANK_RAM2);
|
||||
vm_segment_set(mach->main, 0x10073, wrong);
|
||||
vm_segment_set(mach->main, 0xD073, right);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0x10073), right);
|
||||
}
|
||||
|
||||
Test(apple2_bank, switch_read)
|
||||
{
|
||||
vm_segment_get(mach->main, 0xC080);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_RAM2);
|
||||
|
||||
// This (and a few others) are trickier to test, as they require
|
||||
// consecutive reads to trigger.
|
||||
vm_segment_get(mach->main, 0xC081);
|
||||
cr_assert_neq(mach->bank_switch, BANK_WRITE | BANK_RAM2);
|
||||
mach->cpu->last_addr = 0xC081;
|
||||
vm_segment_get(mach->main, 0xC081);
|
||||
cr_assert_eq(mach->bank_switch, BANK_WRITE | BANK_RAM2);
|
||||
|
||||
vm_segment_get(mach->main, 0xC082);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM2);
|
||||
|
||||
// Another that needs consecutives
|
||||
vm_segment_get(mach->main, 0xC083);
|
||||
cr_assert_neq(mach->bank_switch, BANK_RAM | BANK_WRITE | BANK_RAM2);
|
||||
mach->cpu->last_addr = 0xC083;
|
||||
vm_segment_get(mach->main, 0xC083);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_WRITE | BANK_RAM2);
|
||||
|
||||
vm_segment_get(mach->main, 0xC088);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM);
|
||||
|
||||
// You get the idea
|
||||
vm_segment_get(mach->main, 0xC089);
|
||||
cr_assert_neq(mach->bank_switch, BANK_WRITE);
|
||||
mach->cpu->last_addr = 0xC089;
|
||||
vm_segment_get(mach->main, 0xC089);
|
||||
cr_assert_eq(mach->bank_switch, BANK_WRITE);
|
||||
|
||||
vm_segment_get(mach->main, 0xC08A);
|
||||
cr_assert_eq(mach->bank_switch, BANK_DEFAULT);
|
||||
|
||||
vm_segment_get(mach->main, 0xC08B);
|
||||
cr_assert_neq(mach->bank_switch, BANK_RAM | BANK_WRITE);
|
||||
mach->cpu->last_addr = 0xC08B;
|
||||
vm_segment_get(mach->main, 0xC08B);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_WRITE);
|
||||
|
||||
mach->bank_switch = BANK_RAM | BANK_RAM2;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC011), 0x80);
|
||||
mach->bank_switch = BANK_DEFAULT;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC011), 0x00);
|
||||
mach->bank_switch = BANK_RAM;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC012), 0x80);
|
||||
mach->bank_switch = BANK_DEFAULT;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC012), 0x00);
|
||||
mach->bank_switch = BANK_ALTZP;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC016), 0x80);
|
||||
mach->bank_switch = BANK_DEFAULT;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC016), 0x00);
|
||||
}
|
||||
|
||||
Test(apple2_bank, switch_write)
|
||||
{
|
||||
vm_segment_set(mach->main, 0xC008, 1);
|
||||
cr_assert_eq(mach->bank_switch & BANK_ALTZP, BANK_ALTZP);
|
||||
vm_segment_set(mach->main, 0xC009, 1);
|
||||
cr_assert_eq(mach->bank_switch & BANK_ALTZP, 0);
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "apple2.h"
|
||||
#include "apple2.mem.h"
|
||||
#include "apple2.bank.h"
|
||||
|
||||
static apple2 *mach = NULL;
|
||||
|
||||
@ -21,178 +22,6 @@ teardown()
|
||||
|
||||
TestSuite(apple2_mem, .init = setup, .fini = teardown);
|
||||
|
||||
Test(apple2_mem, map)
|
||||
{
|
||||
// mach already had the mem_map function run, so we just need to
|
||||
// test the results.
|
||||
size_t addr;
|
||||
|
||||
for (addr = APPLE2_BANK_OFFSET; addr < MOS6502_MEMSIZE; addr++) {
|
||||
cr_assert_eq(mach->main->read_table[addr], apple2_mem_read_bank);
|
||||
cr_assert_eq(mach->main->write_table[addr], apple2_mem_write_bank);
|
||||
}
|
||||
}
|
||||
|
||||
Test(apple2_mem, map_bank_switch)
|
||||
{
|
||||
vm_segment *segments[2];
|
||||
|
||||
segments[0] = mach->main;
|
||||
segments[1] = mach->aux;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
cr_assert_eq(segments[i]->read_table[0xC080], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC081], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC082], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC083], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC088], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC089], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC08A], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC08B], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC088], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC011], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC012], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->read_table[0xC016], apple2_mem_read_bank_switch);
|
||||
cr_assert_eq(segments[i]->write_table[0xC008], apple2_mem_write_bank_switch);
|
||||
cr_assert_eq(segments[i]->write_table[0xC009], apple2_mem_write_bank_switch);
|
||||
}
|
||||
}
|
||||
|
||||
Test(apple2_mem, read_bank)
|
||||
{
|
||||
vm_8bit val;
|
||||
|
||||
// Test that setting a value in the rom segment is returned to us
|
||||
// when addressing from main memory
|
||||
apple2_set_bank_switch(mach, BANK_WRITE);
|
||||
val = 123;
|
||||
vm_segment_set(mach->rom, 0x77, val);
|
||||
val = vm_segment_get(mach->rom, 0x77);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), val);
|
||||
|
||||
// In RAM1 bank mode, setting a value in memory should return thaty
|
||||
// value in memory... but, as a twist, also check that the value is
|
||||
// not set in ROM nor in RAM2.
|
||||
val = 222;
|
||||
apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE);
|
||||
vm_segment_set(mach->main, 0xD077, val);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), val);
|
||||
cr_assert_neq(vm_segment_get(mach->rom, 0x77), val);
|
||||
cr_assert_neq(vm_segment_get(mach->main, 0x10077), val);
|
||||
|
||||
// Finally, in RAM2 bank mode, we test similarly to ROM mode. Set
|
||||
// the value directly in ram2 (which is at $10000 - $1FFFF) and see
|
||||
// if it's there when addressing from main memory in the $Dnnn
|
||||
// range.
|
||||
val = 111;
|
||||
apple2_set_bank_switch(mach, mach->bank_switch | BANK_RAM2);
|
||||
vm_segment_set(mach->main, 0x10077, val);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), val);
|
||||
}
|
||||
|
||||
/*
|
||||
* The write_bank test will look a bit similar to the read_bank one,
|
||||
* except in logic it should be written somewhat as an inverse. That is,
|
||||
* we want our writes to all go to memory, and double-check that the
|
||||
* right location is being updated (or not being updated).
|
||||
*/
|
||||
Test(apple2_mem, write_bank)
|
||||
{
|
||||
vm_8bit right, wrong;
|
||||
|
||||
// In BANK_DEFAULT mode, we expect that updates to ROM will never be
|
||||
// successful (after all, it wouldn't be read-only memory if they
|
||||
// were).
|
||||
right = 123;
|
||||
wrong = 222;
|
||||
vm_segment_set(mach->rom, 0x77, right);
|
||||
vm_segment_set(mach->main, 0xD077, wrong);
|
||||
cr_assert_eq(vm_segment_get(mach->rom, 0x77), right);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD077), right);
|
||||
|
||||
// RAM1 is the main bank; it's all 64k RAM in one chunk.
|
||||
right = 111;
|
||||
wrong = 232;
|
||||
apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE);
|
||||
vm_segment_set(mach->main, 0xD078, right);
|
||||
vm_segment_set(mach->main, 0x10078, wrong);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xD078), right);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0x10078), wrong);
|
||||
|
||||
// RAM2 is most of the 64k, except the first 4k of the last 12
|
||||
// ($D000..$DFFF) is in ram2.
|
||||
right = 210;
|
||||
wrong = 132;
|
||||
apple2_set_bank_switch(mach, mach->bank_switch | BANK_RAM2);
|
||||
vm_segment_set(mach->main, 0x10073, wrong);
|
||||
vm_segment_set(mach->main, 0xD073, right);
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0x10073), right);
|
||||
}
|
||||
|
||||
Test(apple2_mem, read_bank_switch)
|
||||
{
|
||||
vm_segment_get(mach->main, 0xC080);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_RAM2);
|
||||
|
||||
// This (and a few others) are trickier to test, as they require
|
||||
// consecutive reads to trigger.
|
||||
vm_segment_get(mach->main, 0xC081);
|
||||
cr_assert_neq(mach->bank_switch, BANK_WRITE | BANK_RAM2);
|
||||
mach->cpu->last_addr = 0xC081;
|
||||
vm_segment_get(mach->main, 0xC081);
|
||||
cr_assert_eq(mach->bank_switch, BANK_WRITE | BANK_RAM2);
|
||||
|
||||
vm_segment_get(mach->main, 0xC082);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM2);
|
||||
|
||||
// Another that needs consecutives
|
||||
vm_segment_get(mach->main, 0xC083);
|
||||
cr_assert_neq(mach->bank_switch, BANK_RAM | BANK_WRITE | BANK_RAM2);
|
||||
mach->cpu->last_addr = 0xC083;
|
||||
vm_segment_get(mach->main, 0xC083);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_WRITE | BANK_RAM2);
|
||||
|
||||
vm_segment_get(mach->main, 0xC088);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM);
|
||||
|
||||
// You get the idea
|
||||
vm_segment_get(mach->main, 0xC089);
|
||||
cr_assert_neq(mach->bank_switch, BANK_WRITE);
|
||||
mach->cpu->last_addr = 0xC089;
|
||||
vm_segment_get(mach->main, 0xC089);
|
||||
cr_assert_eq(mach->bank_switch, BANK_WRITE);
|
||||
|
||||
vm_segment_get(mach->main, 0xC08A);
|
||||
cr_assert_eq(mach->bank_switch, BANK_DEFAULT);
|
||||
|
||||
vm_segment_get(mach->main, 0xC08B);
|
||||
cr_assert_neq(mach->bank_switch, BANK_RAM | BANK_WRITE);
|
||||
mach->cpu->last_addr = 0xC08B;
|
||||
vm_segment_get(mach->main, 0xC08B);
|
||||
cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_WRITE);
|
||||
|
||||
mach->bank_switch = BANK_RAM | BANK_RAM2;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC011), 0x80);
|
||||
mach->bank_switch = BANK_DEFAULT;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC011), 0x00);
|
||||
mach->bank_switch = BANK_RAM;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC012), 0x80);
|
||||
mach->bank_switch = BANK_DEFAULT;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC012), 0x00);
|
||||
mach->bank_switch = BANK_ALTZP;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC016), 0x80);
|
||||
mach->bank_switch = BANK_DEFAULT;
|
||||
cr_assert_eq(vm_segment_get(mach->main, 0xC016), 0x00);
|
||||
}
|
||||
|
||||
Test(apple2_mem, write_bank_switch)
|
||||
{
|
||||
vm_segment_set(mach->main, 0xC008, 1);
|
||||
cr_assert_eq(mach->bank_switch & BANK_ALTZP, BANK_ALTZP);
|
||||
vm_segment_set(mach->main, 0xC009, 1);
|
||||
cr_assert_eq(mach->bank_switch & BANK_ALTZP, 0);
|
||||
}
|
||||
|
||||
Test(apple2_mem, init_peripheral_rom)
|
||||
{
|
||||
// FIXME: this isn't working, _and_ it's pretty tightly coupled into
|
||||
|
Loading…
x
Reference in New Issue
Block a user