diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f82a79a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,38 @@ +name: build + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +jobs: + build: + name: ${{ matrix.os }} / SDL ${{ matrix.sdl }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + sdl: [1, 2] + steps: + - uses: actions/checkout@v4 + + - name: Install SDL (Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update -qq + sudo apt-get install -y libsdl1.2-dev libsdl2-dev + + - name: Install SDL (macOS) + if: runner.os == 'macOS' + run: brew install sdl12-compat sdl2 + + - name: Build + run: make SDL=${{ matrix.sdl }} + + - name: Build z80emu CPU core + run: make clean && make CPU=z80emu SDL=${{ matrix.sdl }} + + - name: CPU tests + run: make test diff --git a/.gitignore b/.gitignore index c12294e..33ca572 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ obj +objhtml zxem -maketables \ No newline at end of file +zxem.html +zxem.js +zxem.data +maketables +tests/zxtest +tests/zxtest.log diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ce2a5b0..0000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: c - -before_script: - - sudo apt-get update -qq - - sudo apt-get install libsdl1.2-dev - -script: make diff --git a/INSTALL b/INSTALL index 0272a8f..92d9531 100644 --- a/INSTALL +++ b/INSTALL @@ -1,4 +1,5 @@ -For Linux or Mac, just type "make". It will compile the example zextest. +For Linux or Mac, install SDL 1.2 or SDL2 and type "make" to build zxem. +See README.md for the build options (SDL=2, DEBUG=1, PLAT=HTML, make test). For Windows, open a Visual C++ command prompt and then type: diff --git a/Makefile b/Makefile index a271e55..5038d26 100644 --- a/Makefile +++ b/Makefile @@ -1,136 +1,132 @@ # ZXEM MAKEFILE -OLEVEL = -O3 -CFLAGS = -Isrc/zxem -Isrc/osdep -Isrc/cpu -Wall -pedantic $(OLEVEL) -fomit-frame-pointer -SDL = 1 -ZCC = $(ZCC) -#CPU = z80emu -#CPU = mz80 -#CPU = deadz80 -CPU = z80core +# +# make native build (SDL 1.2, z80core CPU) +# make SDL=2 build against SDL2 +# make SDL=0 build with the dummy (no video) backend +# make DEBUG=1 debug build (-O0 -g) +# make PLAT=HTML Emscripten build (zxem.html) +# make test build and run the CPU test suite (z80emu core) + +## Config + +# CPU core: z80core (default), z80emu, mz80 (mz80 has issues on 64-bit) +CPU ?= z80core +SDL ?= 1 +PLAT ?= NATIVE TARGET = zxem TEST_TARGET = tests/zxtest -OBJDIR = obj -ifndef PLAT -PLAT = NATIVE -endif + +CPPFLAGS += -Isrc/zxem -Isrc/osdep -Isrc/cpu +WARNINGS = -Wall -pedantic +CFLAGS += $(WARNINGS) -fomit-frame-pointer +CXXFLAGS += $(WARNINGS) -std=c++11 -fomit-frame-pointer ifdef DEBUG OLEVEL = -O0 -g +else +OLEVEL = -O2 endif +CFLAGS += $(OLEVEL) +CXXFLAGS += $(OLEVEL) -OSD_SOURCE = src/osdep/dummy.c - -ifdef SDL -ifeq ($(SDL), 1) -OSD_SOURCE = src/osdep/sdl1.2.c -OSDFLAGS = $(shell sdl-config --cflags) -OSDLIBS = $(shell sdl-config --libs) -endif -ifeq ($(SDL), 2) -OSD_SOURCE = src/osdep/sdl2.c -OSDFLAGS = $(shell sdl2-config --cflags) -OSDLIBS = $(shell sdl2-config --libs) -endif -endif - +## Platform specific config ifeq ($(PLAT), HTML) -ZCC = emcc +CC = emcc +CXX = em++ AR = emar -OSDFLAGS = -s USE_SDL=$(SDL) -OSDLIBS = -OLEVEL += -s ASM_JS=1 -LINKFLAGS = $(OSDFLAGS) $(OLEVEL) --preload-files roms/ --preload-files scr/ --emrun -TARGET := $(TARGET).html OBJDIR = objhtml +SDLFLAGS = -s USE_SDL=$(SDL) +OSDLIBS = +LINKFLAGS += $(SDLFLAGS) --preload-file roms --preload-file scr --emrun +TARGET := $(TARGET).html +else +OBJDIR = obj endif -ifeq ($(CPU), z80emu) -CPUOBJ = $(OBJDIR)/z80emu.o -CPUINTC = src/cpu/z80emu/cpuintf.c -endif +## OS dependent (video/input) backend -ifeq ($(CPU), mz80) -CPUOBJ = $(OBJDIR)/mz80.o -CPUINTC = src/cpu/mz80/cpuintf.c +ifeq ($(SDL), 1) +OSD_SOURCE = src/osdep/sdl1.2.c +ifneq ($(PLAT), HTML) +SDLFLAGS = $(shell sdl-config --cflags) +OSDLIBS = $(shell sdl-config --libs) endif - -ifeq ($(CPU), deadz80) -CPUOBJ = $(OBJDIR)/deadz80.o -CPUINTC = src/cpu/deadz80/cpuintf.c +else ifeq ($(SDL), 2) +OSD_SOURCE = src/osdep/sdl2.c +ifneq ($(PLAT), HTML) +SDLFLAGS = $(shell sdl2-config --cflags) +OSDLIBS = $(shell sdl2-config --libs) endif - -ifeq ($(CPU), z80core) -CPUOBJ = $(OBJDIR)/z80core_Z80Core.o $(OBJDIR)/z80core_Z80Core_CBOpcodes.o $(OBJDIR)/z80core_Z80Core_DDCB_FDCBOpcodes.o $(OBJDIR)/z80core_Z80Core_DDOpcodes.o $(OBJDIR)/z80core_Z80Core_EDOpcodes.o $(OBJDIR)/z80core_Z80Core_FDOpcodes.o $(OBJDIR)/z80core_Z80Core_MainOpcodes.o $(OBJDIR)/z80core_Z80Core_CInterface.o -CPUINTC = src/cpu/z80core/cpuintf.c - -# add permissive for initialising members in class definitions (Z80Core.h) -CFLAGS += -fpermissive - -#compile as c++ when using z80 core -ifeq ($(PLAT), HTML) -ZCC = em++ else -ZCC = $(CXX) -endif - +OSD_SOURCE = src/osdep/dummy.c endif +## Sources: every .c/.cpp in the selected CPU directory plus src/zxem. +## Helper programs (maketables, zextest) live in src/cpu/z80emu/util so +## they are not picked up here. -all: $(OBJDIR) $(TARGET) +CPUSRC := $(wildcard src/cpu/$(CPU)/*.c) $(wildcard src/cpu/$(CPU)/*.cpp) +ZXEMSRC := $(wildcard src/zxem/*.c) -$(OBJDIR): - mkdir $(OBJDIR) +obj_of = $(addprefix $(OBJDIR)/,$(addsuffix .o,$(subst /,_,$(patsubst src/%,%,$(basename $(1)))))) -tables.h: maketables.c - $(ZCC) -Wall $< -o maketables - ./maketables > $@ +CPUOBJ := $(call obj_of,$(CPUSRC)) +ZXEMOBJ := $(call obj_of,$(ZXEMSRC)) +OSDOBJ := $(OBJDIR)/osdep.o +OBJECTS := $(ZXEMOBJ) $(CPUOBJ) $(OSDOBJ) -# MULTI CPU CORE! -# Z80EMU - BROKEN -$(OBJDIR)/z80emu.o: src/cpu/z80emu/z80emu.c src/cpu/z80emu/z80emu.h src/cpu/z80emu/instructions.h src/cpu/z80emu/macros.h src/cpu/z80emu/tables.h - $(ZCC) $(CFLAGS) -c $< -o $@ +HEADERS := $(wildcard src/*/*.h src/cpu/*/*.h) -# MZ80 - Issues on 64bit :( -$(OBJDIR)/mz80.o: src/cpu/mz80/mz80.c src/cpu/mz80/mz80.h src/cpu/cpuintf.h - $(ZCC) $(CFLAGS) -c $< -o $@ +## Rules -# DEADZ80 - Dead? -$(OBJDIR)/deadz80.o: src/cpu/deadz80/deadz80.c src/cpu/deadz80/deadz80.h src/cpu/deadz80/opcodes.h src/cpu/cpuintf.h - $(ZCC) $(CFLAGS) -c $< -o $@ +all: $(TARGET) -# Z80CORE - SUCCESS! -$(OBJDIR)/z80core_%.o: src/cpu/z80core/%.cpp - $(ZCC) $(CFLAGS) -c $< -o $@ +$(OBJDIR): + mkdir -p $(OBJDIR) -#$(OBJDIR)/z80core.a: src/cpu/z80core/Z80Core.o src/cpu/z80core/Z80Core_CBOpcodes.o src/cpu/z80core/Z80Core_DDCB_FDCBOpcodes.o src/cpu/z80core/Z80Core_DDOpcodes.o src/cpu/z80core/Z80Core_EDOpcodes.o src/cpu/z80core/Z80Core_FDOpcodes.o src/cpu/z80core/Z80Core_MainOpcodes.o -# $(AR) cr $@ src/cpu/z80core/*.o +define compile_c +$(call obj_of,$(1)): $(1) $(HEADERS) | $(OBJDIR) + $$(CC) $$(CPPFLAGS) $$(CFLAGS) -c $$< -o $$@ +endef +define compile_cxx +$(call obj_of,$(1)): $(1) $(HEADERS) | $(OBJDIR) + $$(CXX) $$(CPPFLAGS) $$(CXXFLAGS) -c $$< -o $$@ +endef -$(OBJDIR)/zxem.o: src/zxem/zxem.c src/zxem/zxem.h src/osdep/osdep.h - $(ZCC) $(CFLAGS) $(OSDFLAGS) -c $< -o $@ +$(foreach src,$(filter %.c,$(CPUSRC)) $(ZXEMSRC),$(eval $(call compile_c,$(src)))) +$(foreach src,$(filter %.cpp,$(CPUSRC)),$(eval $(call compile_cxx,$(src)))) -$(OBJDIR)/zxvid.o: src/zxem/zxvid.c src/zxem/zxem.h src/osdep/osdep.h - $(ZCC) $(CFLAGS) -c $< -o $@ +$(OSDOBJ): $(OSD_SOURCE) $(HEADERS) | $(OBJDIR) + $(CC) $(CPPFLAGS) $(CFLAGS) $(SDLFLAGS) -c $< -o $@ -$(OBJDIR)/zxio.o: src/zxem/zxio.c src/zxem/zxem.h src/osdep/osdep.h - $(ZCC) $(CFLAGS) -c $< -o $@ +# Link with the C++ driver so the C++ runtime is pulled in for z80core. +$(TARGET): $(OBJECTS) + $(CXX) $(CXXFLAGS) $(OBJECTS) $(OSDLIBS) $(LINKFLAGS) -o $@ -$(OBJDIR)/osdep.o: $(OSD_SOURCE) src/zxem/zxem.h src/osdep/osdep.h - $(ZCC) $(CFLAGS) $(OSDFLAGS) -c $(OSD_SOURCE) -o $@ +## CPU test suite (exercises the z80emu core, whichever CPU zxem uses) -$(OBJDIR)/cpuintf.o: $(CPUINTC) - $(ZCC) $(CFLAGS) -c $< -o $@ +$(OBJDIR)/test_z80emu.o: src/cpu/z80emu/z80emu.c $(wildcard src/cpu/z80emu/*.h) | $(OBJDIR) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -OBJECT_FILES = $(OBJDIR)/zxem.o $(CPUOBJ) $(OBJDIR)/zxvid.o $(OBJDIR)/zxio.o $(OBJDIR)/osdep.o $(OBJDIR)/cpuintf.o +$(TEST_TARGET): tests/cputest.c src/cpu/z80emu/z80emu.h $(OBJDIR)/test_z80emu.o + $(CC) $(CPPFLAGS) $(CFLAGS) $< $(OBJDIR)/test_z80emu.o -o $@ -$(TARGET): $(OBJECT_FILES) - $(ZCC) $(CFLAGS) $(OBJECT_FILES) $(OSDLIBS) $(LINKFLAGS) -o $@ +# The suite stops at the first failure. The z80emu core currently passes the +# first $(KNOWN_PASSES) tests and fails at cb46 (BIT 0,(HL): undocumented +# flag bits 3/5 come from MEMPTR, which z80emu does not model). Fail the +# build if that number goes down, so new regressions are caught. +KNOWN_PASSES ?= 286 -$(TEST_TARGET): tests/cputest.c src/cpu/z80emu/z80emu.h $(OBJDIR)/z80emu.o - $(ZCC) $(CFLAGS) $< $(OBJDIR)/z80emu.o -o $@ +test: $(TEST_TARGET) + @(cd tests && ./zxtest > zxtest.log); \ + passed=$$(grep -c PASSED tests/zxtest.log); \ + echo "cputest: $$passed tests passed (expected at least $(KNOWN_PASSES))"; \ + grep -B1 -A15 FAILED tests/zxtest.log | grep -v '^[ 0-9]*M[RW] ' ; \ + test $$passed -ge $(KNOWN_PASSES) +clean: + rm -rf obj objhtml $(TARGET) zxem.html zxem.js zxem.data $(TEST_TARGET) tests/zxtest.log -clean: - rm $(OBJDIR)/*.o - rm $(TARGET) +.PHONY: all clean test diff --git a/README.md b/README.md index 8271376..505e3f3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,26 @@ ZXEMU is a free open source cross platform ZX Spectrum emulator, currently work [Play online](http://js.mikedx.co.uk/zxem.html) -[![Build Status](https://travis-ci.org/MikeDX/z80emu.svg?branch=master)](https://travis-ci.org/MikeDX/z80emu) +[![build](https://github.com/MikeDX/z80emu/actions/workflows/build.yml/badge.svg)](https://github.com/MikeDX/z80emu/actions/workflows/build.yml) + +## Building + +Install SDL 1.2 (or SDL2) first: + +- Ubuntu/Debian: `sudo apt-get install libsdl1.2-dev libsdl2-dev` +- macOS (Homebrew): `brew install sdl12-compat sdl2` + +Then: + +``` +make # zxem with SDL 1.2 +make SDL=2 # zxem with SDL2 +make DEBUG=1 # debug build +make PLAT=HTML # Emscripten build (zxem.html) +make test # build and run the CPU test suite +``` + +Run `./zxem` from the repository root (it loads `roms/48k.rom`). ## TODO list - [x] Working Z80 CPU Core diff --git a/src/cpu/z80core/Z80Core.h b/src/cpu/z80core/Z80Core.h index ac64ca3..7226f3e 100644 --- a/src/cpu/z80core/Z80Core.h +++ b/src/cpu/z80core/Z80Core.h @@ -146,7 +146,7 @@ class CZ80Core typedef struct { void (CZ80Core::*function)(unsigned char opcode); - char* format; + const char* format; } Z80Opcode; typedef struct diff --git a/src/cpu/z80core/Z80Core_CInterface.h b/src/cpu/z80core/Z80Core_CInterface.h index ead9be5..264b446 100644 --- a/src/cpu/z80core/Z80Core_CInterface.h +++ b/src/cpu/z80core/Z80Core_CInterface.h @@ -64,7 +64,7 @@ typedef enum //----------------------------------------------------------------------------------------- -void *Z80CORE_Create(); +void *Z80CORE_Create(void); void Z80CORE_Destroy(void *core); void Z80CORE_Initialise(void *core, Z80CoreRead mem_read, Z80CoreWrite mem_write, Z80CoreRead io_read, Z80CoreWrite io_write, Z80CoreContention mem_contention_handling, Z80CoreContention io_contention_handling, int member_class); void Z80CORE_Reset(void *core); diff --git a/src/cpu/z80emu/maketables.c b/src/cpu/z80emu/util/maketables.c similarity index 100% rename from src/cpu/z80emu/maketables.c rename to src/cpu/z80emu/util/maketables.c diff --git a/src/cpu/z80emu/zextest.c b/src/cpu/z80emu/util/zextest.c similarity index 100% rename from src/cpu/z80emu/zextest.c rename to src/cpu/z80emu/util/zextest.c diff --git a/src/cpu/z80emu/zextest.h b/src/cpu/z80emu/util/zextest.h similarity index 100% rename from src/cpu/z80emu/zextest.h rename to src/cpu/z80emu/util/zextest.h diff --git a/src/cpu/z80emu/z80emu.h b/src/cpu/z80emu/z80emu.h index fe73e7c..9e9eb56 100644 --- a/src/cpu/z80emu/z80emu.h +++ b/src/cpu/z80emu/z80emu.h @@ -248,13 +248,6 @@ typedef struct Z80_STATE_ { * register_table for 0xdd or 0xfd prefixes. */ -/* Here are macros for the zextest example. Read/write memory macros have been - * written for a linear 64k RAM. Input/output port macros are used to simulate - * system calls. - */ - -#include "zextest.h" - #define Z80_FETCH_BYTE(address, x) \ { \ (x) = state->readbyte(address); \ diff --git a/src/osdep/sdl2.c b/src/osdep/sdl2.c index 680354d..f31de5e 100644 --- a/src/osdep/sdl2.c +++ b/src/osdep/sdl2.c @@ -7,7 +7,7 @@ * * OSDEP file for SDL2 */ -#include +#include #include "osdep.h" #include "zxem.h" diff --git a/tests/cputest.c b/tests/cputest.c index 0683bab..03acc9b 100644 --- a/tests/cputest.c +++ b/tests/cputest.c @@ -13,6 +13,7 @@ uint8_t writes[65536]; uint8_t reads[65536]; int total, running, debug; +int failed = 0; uint8_t readbyte(uint16_t addr) { // printf("Reading 0x%X\n",addr); @@ -273,6 +274,7 @@ int main(int argc, char *argv[]) { printf("TSTATES: %d : %d\n", etstates, tstates); + failed = 1; break; } } @@ -281,6 +283,6 @@ int main(int argc, char *argv[]) { printf("DONE\n"); free(zxmem); - return 0; + return failed; }