Fixes #48 Implement the Apple Language Card

This commit is contained in:
Stefan Arentz 2016-12-02 16:13:09 -05:00
parent 653217f5c0
commit 7cb4a4fd30
6 changed files with 221 additions and 90 deletions

185
a2p.c
View File

@ -131,6 +131,184 @@ void a2p_screen_txt_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, u
//printf("[A2P] $%.4X = $%.2X\n", addr, b);
}
// Apple Language Card
struct ewm_alc_t {
struct mem_t *ram1; // $D000 - $DFFF RAM Bank #1
struct mem_t *ram2; // $D000 - $DFFF RAM Bank #2
struct mem_t *ram3; // $E000 - $FFFF RAM Bank #3
struct mem_t *rom; // $F800 - $FFFF Autostart ROM
struct mem_t *iom; // $C080 - $C08F
int wrtcount;
};
uint8_t alc_iom_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
struct ewm_alc_t *alc = (struct ewm_alc_t*) mem->obj;
// Always enable the right banks
if (addr & 0b00001000) {
alc->ram1->enabled = true;
alc->ram2->enabled = false;
alc->ram3->enabled = true;
} else {
alc->ram1->enabled = false;
alc->ram2->enabled = true;
alc->ram3->enabled = true;
}
switch (addr) {
// WRTCOUNT = 0, WRITE DISABLE, READ ENABLE
case 0xc080:
case 0xc084:
alc->wrtcount = 0;
alc->ram1->flags = MEM_FLAGS_READ;
alc->ram2->flags = MEM_FLAGS_READ;
alc->ram3->flags = MEM_FLAGS_READ;
break;
// WRTCOUNT++, READ DISABLE, WRITE ENABLE IF WRTCOUNT >= 2
case 0xc081:
case 0xc085:
alc->wrtcount = alc->wrtcount + 1;
alc->ram1->flags &= ~MEM_FLAGS_READ;
alc->ram2->flags &= ~MEM_FLAGS_READ;
alc->ram3->flags &= ~MEM_FLAGS_READ;
if (alc->wrtcount >= 2) {
alc->ram1->flags |= MEM_FLAGS_WRITE;
alc->ram2->flags |= MEM_FLAGS_WRITE;
alc->ram3->flags |= MEM_FLAGS_WRITE;
}
break;
// WRTCOUNT = 0, WRITE DISABLE, READ DISABLE
case 0xc082:
case 0xc086:
alc->wrtcount = 0;
alc->ram1->flags &= ~MEM_FLAGS_WRITE;
alc->ram2->flags &= ~MEM_FLAGS_WRITE;
alc->ram3->flags &= ~MEM_FLAGS_WRITE;
alc->ram1->flags &= MEM_FLAGS_WRITE;
alc->ram2->flags &= MEM_FLAGS_WRITE;
alc->ram3->flags &= MEM_FLAGS_WRITE;
break;
// WRTCOUNT++, READ ENABLE, WRITE ENABLE IF WRTCOUNT >= 2
case 0xc083:
case 0xc08b:
alc->wrtcount = alc->wrtcount + 1;
alc->ram1->flags |= MEM_FLAGS_READ;
alc->ram2->flags |= MEM_FLAGS_READ;
alc->ram3->flags |= MEM_FLAGS_READ;
if (alc->wrtcount >= 2) {
alc->ram1->flags |= MEM_FLAGS_WRITE;
alc->ram2->flags |= MEM_FLAGS_WRITE;
alc->ram3->flags |= MEM_FLAGS_WRITE;
}
break;
default:
fprintf(stderr, "[ALC] Unexpected read at $%.4X\n", addr);
break;
}
return 0;
}
static void alc_iom_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) {
struct ewm_alc_t *alc = (struct ewm_alc_t*) mem->obj;
// Always enable the right banks
if (addr & 0b00001000) {
alc->ram1->enabled = true;
alc->ram2->enabled = false;
alc->ram3->enabled = true;
} else {
alc->ram1->enabled = false;
alc->ram2->enabled = true;
alc->ram3->enabled = true;
}
switch (addr) {
// WRTCOUNT = 0, WRITE DISABLE, READ ENABLE
case 0xc080:
case 0xc084:
alc->wrtcount = 0;
alc->ram1->flags = MEM_FLAGS_READ;
alc->ram2->flags = MEM_FLAGS_READ;
alc->ram3->flags = MEM_FLAGS_READ;
break;
// WRTCOUNT = 0, READ DISABLE
case 0xc081:
case 0xc085:
alc->wrtcount = 0;
alc->ram1->flags &= ~MEM_FLAGS_READ;
alc->ram2->flags &= ~MEM_FLAGS_READ;
alc->ram3->flags &= ~MEM_FLAGS_READ;
break;
// WRTCOUNT = 0, WRITE DISABLE, READ DISABLE
case 0xc082:
case 0xc086:
alc->wrtcount = 0;
alc->ram1->flags &= ~MEM_FLAGS_WRITE;
alc->ram2->flags &= ~MEM_FLAGS_WRITE;
alc->ram3->flags &= ~MEM_FLAGS_WRITE;
alc->ram1->flags &= MEM_FLAGS_WRITE;
alc->ram2->flags &= MEM_FLAGS_WRITE;
alc->ram3->flags &= MEM_FLAGS_WRITE;
break;
// WRTCOUNT = 0, READ ENABLE
case 0xc083:
case 0xc08b:
alc->wrtcount = 0;
alc->ram1->flags |= MEM_FLAGS_READ;
alc->ram2->flags |= MEM_FLAGS_READ;
alc->ram3->flags |= MEM_FLAGS_READ;
break;
default:
fprintf(stderr, "[ALC] Unexpected write at $%.4X\n", addr);
break;
}
}
int ewm_alc_init(struct ewm_alc_t *alc, struct cpu_t *cpu) {
memset(alc, 0x00, sizeof(struct ewm_alc_t));
// Order is important. First added is last tried when looking up
// addresses. So we register the ROM first, which means we never
// have to disable it.
alc->rom = cpu_add_rom_file(cpu, 0xf800, "roms/341-0020.bin");
alc->ram3 = cpu_add_ram(cpu, 0xe000, 0xe000 + 8192 - 1);
alc->ram2 = cpu_add_ram(cpu, 0xd000, 0xd000 + 4096 - 1);
alc->ram1 = cpu_add_ram(cpu, 0xd000, 0xd000 + 4096 - 1);
alc->iom = cpu_add_iom(cpu, 0xc080, 0xc08f, alc, alc_iom_read, alc_iom_write);
// TODO Is this correct? Is everyting disabled at boot?
alc->ram1->enabled = false;
alc->ram2->enabled = false;
alc->ram3->enabled = false;
//cpu_mem_disable(cpu, alc->ram1, MEM_ENABLED_READ | MEM_ENABLED_WRITE);
//cpu_mem_disable(cpu, alc->ram2, MEM_ENABLED_READ | MEM_ENABLED_WRITE);
//cpu_mem_disable(cpu, alc->ram3, MEM_ENABLED_READ | MEM_ENABLED_WRITE);
//cpu_mem_disable(cpu, alc->rom, MEM_ENABLED_READ | MEM_ENABLED_WRITE);
return 0;
}
struct ewm_alc_t *ewm_alc_create(struct cpu_t *cpu) {
struct ewm_alc_t *alc = malloc(sizeof(struct ewm_alc_t));
if (ewm_alc_init(alc, cpu) != 0) {
free(alc);
alc = NULL;
}
return alc;
}
void a2p_init(struct a2p_t *a2p, struct cpu_t *cpu) {
memset(a2p, 0x00, sizeof(struct a2p_t));
@ -141,10 +319,15 @@ void a2p_init(struct a2p_t *a2p, struct cpu_t *cpu) {
a2p->rom = cpu_add_rom_file(cpu, 0xe800, "roms/341-0014.bin"); // AppleSoft BASIC E800
a2p->rom = cpu_add_rom_file(cpu, 0xf000, "roms/341-0015.bin"); // AppleSoft BASIC E800
a2p->rom = cpu_add_rom_file(cpu, 0xf800, "roms/341-0020.bin"); // AppleSoft BASIC Autostart Monitor F8000
a2p->iom = cpu_add_iom(cpu, 0xc000, 0xc0ff, a2p, a2p_iom_read, a2p_iom_write);
a2p->iom = cpu_add_iom(cpu, 0xc000, 0xc07f, a2p, a2p_iom_read, a2p_iom_write);
a2p->dsk = ewm_dsk_create(cpu);
struct ewm_alc_t *alc = ewm_alc_create(cpu);
if (alc == NULL) {
fprintf(stderr, "[A2P] Could not create Apple Language Card\n");
}
// TODO Introduce ewm_scr_t that captures everything related to the apple 2 screen so that it can be re-used.
a2p->screen_txt_data = malloc(2 * 1024);

28
cpu.c
View File

@ -46,7 +46,7 @@ typedef void (*cpu_instruction_handler_word_t)(struct cpu_t *cpu, uint16_t oper)
// Stack management.
void _cpu_push_byte(struct cpu_t *cpu, uint8_t b) {
_mem_set_byte_direct(cpu, 0x0100 + cpu->state.sp, b);
mem_set_byte(cpu, 0x0100 + cpu->state.sp, b);
cpu->state.sp -= 1;
}
@ -57,7 +57,7 @@ void _cpu_push_word(struct cpu_t *cpu, uint16_t w) {
uint8_t _cpu_pull_byte(struct cpu_t *cpu) {
cpu->state.sp += 1;
return _mem_get_byte_direct(cpu, 0x0100 + cpu->state.sp);
return mem_get_byte(cpu, 0x0100 + cpu->state.sp);
}
uint16_t _cpu_pull_word(struct cpu_t *cpu) {
@ -204,21 +204,6 @@ struct mem_t *cpu_add_mem(struct cpu_t *cpu, struct mem_t *mem) {
mem->next = cpu->mem;
cpu->mem = mem;
}
// If this is RAM mapped to the zero-page and to the stack then we
// keep a shortcut to it so that we can do direct and fast access
// with our _mem_get/set_byte/word_direct functions.
//
// This makes two assumptions: when RAM is added, it covers both
// pages. And that mem->obj points to a block of memory. This is
// fine for the Apple I and Apple II emulators.
if (mem->type == MEM_TYPE_RAM) {
if (mem->start == 0x0000 && mem->end >= 0x0200) {
cpu->memory = mem->obj;
}
}
return mem;
}
@ -238,7 +223,8 @@ struct mem_t *cpu_add_ram(struct cpu_t *cpu, uint16_t start, uint16_t end) {
struct mem_t *cpu_add_ram_data(struct cpu_t *cpu, uint16_t start, uint16_t end, uint8_t *data) {
struct mem_t *mem = (struct mem_t*) malloc(sizeof(struct mem_t));
mem->type = MEM_TYPE_RAM;
mem->enabled = true;
mem->flags = MEM_FLAGS_READ | MEM_FLAGS_WRITE;
mem->obj = data;
mem->start = start;
mem->end = end;
@ -284,7 +270,8 @@ static uint8_t _rom_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
struct mem_t *cpu_add_rom_data(struct cpu_t *cpu, uint16_t start, uint16_t end, uint8_t *data) {
struct mem_t *mem = (struct mem_t*) malloc(sizeof(struct mem_t));
mem->type = MEM_TYPE_ROM;
mem->enabled = true;
mem->flags = MEM_FLAGS_READ;
mem->obj = data;
mem->start = start;
mem->end = end;
@ -326,7 +313,8 @@ struct mem_t *cpu_add_rom_file(struct cpu_t *cpu, uint16_t start, char *path) {
struct mem_t *cpu_add_iom(struct cpu_t *cpu, uint16_t start, uint16_t end, void *obj, mem_read_handler_t read_handler, mem_write_handler_t write_handler) {
struct mem_t *mem = (struct mem_t*) malloc(sizeof(struct mem_t));
mem->type = MEM_TYPE_IOM;
mem->enabled = true;
mem->flags = MEM_FLAGS_READ | MEM_FLAGS_WRITE;
mem->obj = obj;
mem->start = start;
mem->end = end;

9
cpu.h
View File

@ -52,19 +52,18 @@ struct cpu_t {
FILE *trace;
bool strict;
struct mem_t *mem;
uint8_t *memory; // This is pointing to the first 2 pages of memory, zero page and stack.
struct cpu_instruction_t *instructions;
};
#define MEM_TYPE_RAM 0
#define MEM_TYPE_ROM 1
#define MEM_TYPE_IOM 2
#define MEM_FLAGS_READ 0x01
#define MEM_FLAGS_WRITE 0x02
typedef uint8_t (*mem_read_handler_t)(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr);
typedef void (*mem_write_handler_t)(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b);
struct mem_t {
uint8_t type;
bool enabled;
uint8_t flags;
uint16_t start;
uint16_t end;
void *obj;

2
fmt.c
View File

@ -53,7 +53,7 @@ void cpu_format_stack(struct cpu_t *cpu, char buffer[764]) {
p = strcat(p, " ");
}
char tmp[8];
sprintf(tmp, "%.2X", _mem_get_byte_direct(cpu, 0x0100 + sp + 1));
sprintf(tmp, "%.2X", mem_get_byte(cpu, 0x0100 + sp + 1));
p = strcat(p, tmp);
}
strcat(p, "]");

81
mem.c
View File

@ -32,47 +32,38 @@
// take more time but do the right thing.
uint8_t mem_get_byte(struct cpu_t *cpu, uint16_t addr) {
struct mem_t *mem = cpu->mem;
while (mem != NULL) {
if (addr >= mem->start && addr <= mem->end) {
if (mem->read_handler) {
return ((mem_read_handler_t) mem->read_handler)((struct cpu_t*) cpu, mem, addr);
} else {
if (cpu->strict) {
// TODO: Signal an error about reading to write-only region (does that even exist?)
}
return 0;
struct mem_t *mem = cpu->mem;
while (mem != NULL) {
if (mem->enabled && addr >= mem->start && addr <= mem->end) {
if (mem->read_handler != NULL && mem->flags & MEM_FLAGS_READ) {
return ((mem_read_handler_t) mem->read_handler)((struct cpu_t*) cpu, mem, addr);
}
}
}
mem = mem->next;
}
mem = mem->next;
}
if (cpu->strict) {
// TODO: Signal an error about reading non-existent memory
}
if (cpu->strict) {
// TODO: Signal an error about reading non-existent memory?
}
return 0; // TODO What should the default be if we read from non-existent memory?
return 0;
}
void mem_set_byte(struct cpu_t *cpu, uint16_t addr, uint8_t v) {
struct mem_t *mem = cpu->mem;
while (mem != NULL) {
if (addr >= mem->start && addr <= mem->end) {
if (mem->write_handler) {
((mem_write_handler_t) mem->write_handler)((struct cpu_t*) cpu, mem, addr, v);
} else {
if (cpu->strict) {
// TODO: Signal an error about writing to read-only region
}
struct mem_t *mem = cpu->mem;
while (mem != NULL) {
if (mem->enabled && addr >= mem->start && addr <= mem->end) {
if (mem->write_handler && mem->flags & MEM_FLAGS_WRITE) {
((mem_write_handler_t) mem->write_handler)((struct cpu_t*) cpu, mem, addr, v);
}
return;
}
return;
}
mem = mem->next;
}
mem = mem->next;
}
if (cpu->strict) {
// TODO: Signal an error about writing non-existent memory
}
if (cpu->strict) {
// TODO: Signal an error about writing non-existent memory?
}
}
// Getters
@ -195,27 +186,3 @@ void mem_mod_byte_indx(struct cpu_t *cpu, uint8_t addr, mem_mod_t op) {
void mem_mod_byte_indy(struct cpu_t *cpu, uint8_t addr, mem_mod_t op) {
mem_set_byte_indy(cpu, addr, op(cpu, mem_get_byte_indy(cpu, addr)));
}
// The following get and set memory directly. There are no checks, so
// make sure you are doing the right thing. Mainly used for managing
// the stack, reading instructions, reading vectors and tracing code.
uint8_t _mem_get_byte_direct(struct cpu_t *cpu, uint16_t addr) {
assert(addr <= 0x200);
return cpu->memory[addr];
}
uint16_t _mem_get_word_direct(struct cpu_t *cpu, uint16_t addr) {
assert(addr <= 0x200);
return *((uint16_t*) &cpu->memory[addr]);
}
void _mem_set_byte_direct(struct cpu_t *cpu, uint16_t addr, uint8_t v) {
assert(addr <= 0x200);
cpu->memory[addr] = v;
}
void _mem_set_word_direct(struct cpu_t *cpu, uint16_t addr, uint16_t v) {
assert(addr <= 0x200);
*((uint16_t*) &cpu->memory[addr]) = v;
}

6
mem.h
View File

@ -63,10 +63,4 @@ void mem_mod_byte_indy(struct cpu_t *cpu, uint8_t addr, mem_mod_t op);
uint16_t mem_get_word(struct cpu_t *cpu, uint16_t addr);
void mem_set_word(struct cpu_t *cpu, uint16_t addr, uint16_t v);
// Private. How do we keep them private?
uint8_t _mem_get_byte_direct(struct cpu_t *cpu, uint16_t addr);
uint16_t _mem_get_word_direct(struct cpu_t *cpu, uint16_t addr);
void _mem_set_byte_direct(struct cpu_t *cpu, uint16_t addr, uint8_t v);
void _mem_set_word_direct(struct cpu_t *cpu, uint16_t addr, uint16_t v);
#endif