Fixes #4 - Write a basic framework for unit tests

This commit is contained in:
Stefan Arentz 2016-11-22 15:55:35 -05:00
parent 2094597e34
commit 15be191d05
8 changed files with 141 additions and 39 deletions

View File

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

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

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

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

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

Binary file not shown.

Binary file not shown.