mirror of
https://github.com/st3fan/ewm.git
synced 2025-01-15 12:31:09 +00:00
Fixes #4 - Write a basic framework for unit tests
This commit is contained in:
parent
2094597e34
commit
15be191d05
49
Makefile
49
Makefile
@ -1,18 +1,49 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2015 Stefan Arentz - http:#github.com/st3fan/ewm
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-O3 -std=c99 -Werror -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes
|
||||
SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c
|
||||
OBJECTS=$(SOURCES:.c=.o)
|
||||
LIBS=-lcurses
|
||||
EXECUTABLE=ewm
|
||||
CFLAGS=-std=c11 -O2 -Wpedantic -Wall -Wshadow -Werror -Wshadow -Wno-gnu-binary-literal -g
|
||||
LDFLAGS=-g
|
||||
|
||||
all: $(SOURCES) $(EXECUTABLE)
|
||||
EWM_EXECUTABLE=ewm
|
||||
EWM_SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c mmu.c dsk.c
|
||||
EWM_OBJECTS=$(EWM_SOURCES:.c=.o)
|
||||
EWM_LIBS=-lcurses
|
||||
|
||||
CPU_TEST_EXECUTABLE=cpu_test
|
||||
CPU_TEST_SOURCES=cpu.c ins.c mem.c fmt.c cpu_test.c
|
||||
CPU_TEST_OBJECTS=$(CPU_TEST_SOURCES:.c=.o)
|
||||
CPU_TEST_LIBS=
|
||||
|
||||
all: $(EWM_SOURCES) $(EWM_EXECUTABLE) $(CPU_TEST_SOURCES) $(CPU_TEST_EXECUTABLE)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) $(EXECUTABLE)
|
||||
rm -f $(EWM_OBJECTS) $(EWM_EXECUTABLE) $(CPU_TEST_OBJECTS) $(CPU_TEST_EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $@
|
||||
$(EWM_EXECUTABLE): $(EWM_OBJECTS)
|
||||
$(CC) $(LDFLAGS) $(EWM_OBJECTS) $(EWM_LIBS) -o $@
|
||||
|
||||
$(CPU_TEST_EXECUTABLE): $(CPU_TEST_OBJECTS)
|
||||
$(CC) $(LDFLAGS) $(CPU_TEST_OBJECTS) $(CPU_TEST_LIBS) -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -c -o $@
|
||||
|
31
cpu.c
31
cpu.c
@ -204,7 +204,7 @@ void cpu_add_mem(struct cpu_t *cpu, struct mem_t *mem) {
|
||||
// fine for the Apple I and Apple II emulators.
|
||||
|
||||
if (mem->type == MEM_TYPE_RAM) {
|
||||
if (mem->start == 0x0000 && mem->length >= 0x200) {
|
||||
if (mem->start == 0x0000 && mem->end >= 0x0200) {
|
||||
cpu->memory = mem->obj;
|
||||
}
|
||||
}
|
||||
@ -220,16 +220,16 @@ static void _ram_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint
|
||||
((uint8_t*) mem->obj)[addr - mem->start] = b;
|
||||
}
|
||||
|
||||
void cpu_add_ram(struct cpu_t *cpu, uint16_t start, uint16_t length) {
|
||||
cpu_add_ram_data(cpu, start, length, calloc(length, 0x01));
|
||||
void cpu_add_ram(struct cpu_t *cpu, uint16_t start, uint16_t end) {
|
||||
cpu_add_ram_data(cpu, start, end, calloc(end-start+1, 0x01));
|
||||
}
|
||||
|
||||
void cpu_add_ram_data(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data) {
|
||||
void 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->obj = data;
|
||||
mem->start = start;
|
||||
mem->length = length;
|
||||
mem->end = end;
|
||||
mem->read_handler = _ram_read;
|
||||
mem->write_handler = _ram_write;
|
||||
mem->next = NULL;
|
||||
@ -253,7 +253,7 @@ void cpu_add_ram_file(struct cpu_t *cpu, uint16_t start, char *path) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *data = malloc(file_info.st_size);
|
||||
char *data = calloc(file_info.st_size, 1);
|
||||
if (read(fd, data, file_info.st_size) != file_info.st_size) {
|
||||
close(fd);
|
||||
return;
|
||||
@ -261,7 +261,7 @@ void cpu_add_ram_file(struct cpu_t *cpu, uint16_t start, char *path) {
|
||||
|
||||
close(fd);
|
||||
|
||||
cpu_add_ram_data(cpu, start, file_info.st_size, (uint8_t*) data);
|
||||
cpu_add_ram_data(cpu, start, start + file_info.st_size - 1, (uint8_t*) data);
|
||||
}
|
||||
|
||||
// ROM Memory
|
||||
@ -270,12 +270,12 @@ static uint8_t _rom_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
|
||||
return ((uint8_t*) mem->obj)[addr - mem->start];
|
||||
}
|
||||
|
||||
void cpu_add_rom_data(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data) {
|
||||
void 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->obj = data;
|
||||
mem->start = start;
|
||||
mem->length = length;
|
||||
mem->end = end;
|
||||
mem->read_handler = _rom_read;
|
||||
mem->write_handler = NULL;
|
||||
mem->next = NULL;
|
||||
@ -299,7 +299,7 @@ void cpu_add_rom_file(struct cpu_t *cpu, uint16_t start, char *path) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *data = malloc(file_info.st_size);
|
||||
char *data = calloc(file_info.st_size, 1);
|
||||
if (read(fd, data, file_info.st_size) != file_info.st_size) {
|
||||
close(fd);
|
||||
return;
|
||||
@ -307,17 +307,17 @@ void cpu_add_rom_file(struct cpu_t *cpu, uint16_t start, char *path) {
|
||||
|
||||
close(fd);
|
||||
|
||||
cpu_add_rom_data(cpu, start, file_info.st_size, (uint8_t*) data);
|
||||
cpu_add_rom_data(cpu, start, start + file_info.st_size - 1, (uint8_t*) data);
|
||||
}
|
||||
|
||||
// IO Memory
|
||||
|
||||
void cpu_add_iom(struct cpu_t *cpu, uint16_t start, uint16_t length, void *obj, mem_read_handler_t read_handler, mem_write_handler_t write_handler) {
|
||||
void 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->obj = obj;
|
||||
mem->start = start;
|
||||
mem->length = length;
|
||||
mem->end = end;
|
||||
mem->read_handler = read_handler;
|
||||
mem->write_handler = write_handler;
|
||||
mem->next = NULL;
|
||||
@ -335,7 +335,6 @@ int cpu_trace(struct cpu_t *cpu, char *path) {
|
||||
}
|
||||
|
||||
if (path != NULL) {
|
||||
printf("MOO Tracing to %s\n", path);
|
||||
cpu->trace = fopen(path, "w");
|
||||
if (cpu->trace == NULL) {
|
||||
return errno;
|
||||
@ -365,7 +364,7 @@ int cpu_irq(struct cpu_t *cpu) {
|
||||
return EWM_CPU_ERR_STACK_OVERFLOW;
|
||||
}
|
||||
|
||||
_cpu_push_word(cpu, cpu->state.pc);
|
||||
_cpu_push_word(cpu, cpu->state.pc + 1); // TODO +1?? Spec says +2 but test fails then
|
||||
_cpu_push_byte(cpu, _cpu_get_status(cpu));
|
||||
cpu->state.i = 1;
|
||||
cpu->state.pc = mem_get_word(cpu, EWM_VECTOR_IRQ);
|
||||
@ -378,7 +377,7 @@ int cpu_nmi(struct cpu_t *cpu) {
|
||||
return EWM_CPU_ERR_STACK_OVERFLOW;
|
||||
}
|
||||
|
||||
_cpu_push_word(cpu, cpu->state.pc);
|
||||
_cpu_push_word(cpu, cpu->state.pc + 1); // TODO +1?? Spec says +2 but test fails then
|
||||
_cpu_push_byte(cpu, _cpu_get_status(cpu));
|
||||
cpu->state.i = 1;
|
||||
cpu->state.pc = mem_get_word(cpu, EWM_VECTOR_NMI);
|
||||
|
11
cpu.h
11
cpu.h
@ -25,6 +25,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EWM_CPU_ERR_UNIMPLEMENTED_INSTRUCTION (-1)
|
||||
#define EWM_CPU_ERR_STACK_OVERFLOW (-2)
|
||||
@ -58,7 +59,7 @@ typedef void (*mem_write_handler_t)(struct cpu_t *cpu, struct mem_t *mem, uint16
|
||||
struct mem_t {
|
||||
uint8_t type;
|
||||
uint16_t start;
|
||||
uint16_t length;
|
||||
uint16_t end;
|
||||
void *obj;
|
||||
mem_read_handler_t read_handler;
|
||||
mem_write_handler_t write_handler;
|
||||
@ -82,12 +83,12 @@ void cpu_init(struct cpu_t *cpu);
|
||||
void cpu_shutdown(struct cpu_t *cpu);
|
||||
|
||||
void cpu_add_mem(struct cpu_t *cpu, struct mem_t *mem);
|
||||
void cpu_add_ram(struct cpu_t *cpu, uint16_t start, uint16_t length);
|
||||
void cpu_add_ram_data(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data);
|
||||
void cpu_add_ram(struct cpu_t *cpu, uint16_t start, uint16_t end);
|
||||
void cpu_add_ram_data(struct cpu_t *cpu, uint16_t start, uint16_t end, uint8_t *data);
|
||||
void cpu_add_ram_file(struct cpu_t *cpu, uint16_t start, char *path);
|
||||
void cpu_add_rom_data(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data);
|
||||
void cpu_add_rom_data(struct cpu_t *cpu, uint16_t start, uint16_t end, uint8_t *data);
|
||||
void cpu_add_rom_file(struct cpu_t *cpu, uint16_t start, char *path);
|
||||
void cpu_add_iom(struct cpu_t *cpu, uint16_t start, uint16_t length, void *obj, mem_read_handler_t read_handler, mem_write_handler_t write_handler);
|
||||
void 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);
|
||||
|
||||
void cpu_strict(struct cpu_t *cpu, bool strict);
|
||||
int cpu_trace(struct cpu_t *cpu, char *path);
|
||||
|
71
cpu_test.c
Normal file
71
cpu_test.c
Normal file
@ -0,0 +1,71 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct cpu_t cpu;
|
||||
cpu_init(&cpu);
|
||||
cpu_add_ram_file(&cpu, 0x0000, "roms/6502_functional_test.bin");
|
||||
|
||||
cpu_reset(&cpu);
|
||||
cpu.state.pc = 0x0400;
|
||||
|
||||
uint16_t last_pc = cpu.state.pc;
|
||||
|
||||
while (true) {
|
||||
int ret = cpu_step(&cpu);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "TEST Unexpected error %d\n", ret);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// End of the tests is at 0x3399. This is hard coded and not
|
||||
// ideal. Is there a better way to detect this?
|
||||
|
||||
if (cpu.state.pc == 0x3399) {
|
||||
fprintf(stderr, "TEST Success\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// We detect a test failure because we are in a branch deadlock,
|
||||
// which we can easily detect by remembering the previous pc and
|
||||
// then looking at what we are about to execute.
|
||||
|
||||
if (cpu.state.pc == last_pc) {
|
||||
uint8_t i = mem_get_byte(&cpu, cpu.state.pc);
|
||||
if (i == 0x10 || i == 0x30 || i == 0x50 || i == 0x70 || i == 0x90 || i == 0xb0 || i == 0xd0 || i == 0xf0) {
|
||||
if (mem_get_byte(&cpu, cpu.state.pc + 1) == 0xfe) {
|
||||
fprintf(stderr, "TEST Failure at 0x%.4x \n", cpu.state.pc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_pc = cpu.state.pc;
|
||||
}
|
||||
}
|
14
ewm.c
14
ewm.c
@ -36,10 +36,9 @@
|
||||
static int setup_apple1(struct cpu_t *cpu) {
|
||||
struct pia_t *pia = malloc(sizeof(struct pia_t));
|
||||
pia_init(pia);
|
||||
pia_trace(pia, 0);
|
||||
cpu_add_ram(cpu, 0x0000, 8 * 1024);
|
||||
cpu_add_ram(cpu, 0x0000, 8 * 1024 - 1);
|
||||
cpu_add_rom_file(cpu, 0xff00, "roms/apple1.com");
|
||||
cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_LENGTH, pia, pia_read, pia_write);
|
||||
cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_ADDR + EWM_A1_PIA6820_LENGTH - 1, pia, pia_read, pia_write);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -48,10 +47,9 @@ static int setup_apple1(struct cpu_t *cpu) {
|
||||
static int setup_replica1(struct cpu_t *cpu) {
|
||||
struct pia_t *pia = malloc(sizeof(struct pia_t));
|
||||
pia_init(pia);
|
||||
pia_trace(pia, 0);
|
||||
cpu_add_ram(cpu, 0x0000, 32 * 1024);
|
||||
cpu_add_ram(cpu, 0x0000, 32 * 1024 - 1);
|
||||
cpu_add_rom_file(cpu, 0xe000, "roms/krusader.rom");
|
||||
cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_LENGTH, pia, pia_read, pia_write);
|
||||
cpu_add_iom(cpu, EWM_A1_PIA6820_ADDR, EWM_A1_PIA6820_ADDR + EWM_A1_PIA6820_LENGTH - 1, pia, pia_read, pia_write);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -131,7 +129,9 @@ int main(int argc, char **argv) {
|
||||
|
||||
machine->setup(&cpu);
|
||||
|
||||
switch (cpu_boot(&cpu)) {
|
||||
cpu_reset(&cpu);
|
||||
|
||||
switch (cpu_run(&cpu)) {
|
||||
case EWM_CPU_ERR_UNIMPLEMENTED_INSTRUCTION:
|
||||
fprintf(stderr, "CPU: Exited because of unimplemented instructions 0x%.2x at 0x%.4x\n",
|
||||
mem_get_byte(&cpu, cpu.state.pc), cpu.state.pc);
|
||||
|
4
mem.c
4
mem.c
@ -34,7 +34,7 @@
|
||||
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->start + mem->length))) {
|
||||
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 {
|
||||
@ -57,7 +57,7 @@ uint8_t mem_get_byte(struct cpu_t *cpu, uint16_t addr) {
|
||||
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->start + mem->length))) {
|
||||
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 {
|
||||
|
BIN
roms/6502_functional_test.bin
Normal file
BIN
roms/6502_functional_test.bin
Normal file
Binary file not shown.
BIN
roms/65C02_extended_opcodes_test.bin
Normal file
BIN
roms/65C02_extended_opcodes_test.bin
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user