diff --git a/parts/z80/core.asm b/parts/z80/core.asm index 5a2473e..e71f3bc 100644 --- a/parts/z80/core.asm +++ b/parts/z80/core.asm @@ -167,70 +167,6 @@ fmtHexPair: pop af ret -; Parse the hex char at A and extract it's 0-15 numerical value. Put the result -; in A. -; -; On success, the carry flag is reset. On error, it is set. -parseHex: - ; First, let's see if we have an easy 0-9 case - cp '0' - jr c, .error ; if < '0', we have a problem - cp '9'+1 - jr nc, .alpha ; if >= '9'+1, we might have alpha - ; We are in the 0-9 range - sub '0' ; C is clear - ret - -.alpha: - call upcase - cp 'A' - jr c, .error ; if < 'A', we have a problem - cp 'F'+1 - jr nc, .error ; if >= 'F', we have a problem - ; We have alpha. - sub 'A'-10 ; C is clear - ret - -.error: - scf - ret - -; Parses 2 characters of the string pointed to by HL and returns the numerical -; value in A. If the second character is a "special" character (<0x21) we don't -; error out: the result will be the one from the first char only. -; HL is set to point to the last char of the pair. -; -; On success, the carry flag is reset. On error, it is set. -parseHexPair: - push bc - - ld a, (hl) - call parseHex - jr c, .end ; error? goto end, keeping the C flag on - rla \ rla \ rla \ rla ; let's push this in MSB - ld b, a - inc hl - ld a, (hl) - cp 0x21 - jr c, .single ; special char? single digit - call parseHex - jr c, .end ; error? - or b ; join left-shifted + new. we're done! - ; C flag was set on parseHex and is necessarily clear at this point - jr .end - -.single: - ; If we have a single digit, our result is already stored in B, but - ; we have to right-shift it back. - ld a, b - and 0xf0 - rra \ rra \ rra \ rra - dec hl - -.end: - pop bc - ret - ; Compares strings pointed to by HL and DE up to A count of characters. If ; equal, Z is set. If not equal, Z is reset. strncmp: diff --git a/parts/z80/parse.asm b/parts/z80/parse.asm new file mode 100644 index 0000000..4a2e313 --- /dev/null +++ b/parts/z80/parse.asm @@ -0,0 +1,63 @@ +; Parse the hex char at A and extract it's 0-15 numerical value. Put the result +; in A. +; +; On success, the carry flag is reset. On error, it is set. +parseHex: + ; First, let's see if we have an easy 0-9 case + cp '0' + jr c, .error ; if < '0', we have a problem + cp '9'+1 + jr nc, .alpha ; if >= '9'+1, we might have alpha + ; We are in the 0-9 range + sub '0' ; C is clear + ret + +.alpha: + call upcase + cp 'A' + jr c, .error ; if < 'A', we have a problem + cp 'F'+1 + jr nc, .error ; if >= 'F', we have a problem + ; We have alpha. + sub 'A'-10 ; C is clear + ret + +.error: + scf + ret + +; Parses 2 characters of the string pointed to by HL and returns the numerical +; value in A. If the second character is a "special" character (<0x21) we don't +; error out: the result will be the one from the first char only. +; HL is set to point to the last char of the pair. +; +; On success, the carry flag is reset. On error, it is set. +parseHexPair: + push bc + + ld a, (hl) + call parseHex + jr c, .end ; error? goto end, keeping the C flag on + rla \ rla \ rla \ rla ; let's push this in MSB + ld b, a + inc hl + ld a, (hl) + cp 0x21 + jr c, .single ; special char? single digit + call parseHex + jr c, .end ; error? + or b ; join left-shifted + new. we're done! + ; C flag was set on parseHex and is necessarily clear at this point + jr .end + +.single: + ; If we have a single digit, our result is already stored in B, but + ; we have to right-shift it back. + ld a, b + and 0xf0 + rra \ rra \ rra \ rra + dec hl + +.end: + pop bc + ret diff --git a/parts/z80/shell.asm b/parts/z80/shell.asm index b22f714..194d468 100644 --- a/parts/z80/shell.asm +++ b/parts/z80/shell.asm @@ -14,6 +14,7 @@ ; hexadecimal form, without prefix or suffix. ; *** REQUIREMENTS *** +; parse ; stdio ; blkdev diff --git a/recipes/rc2014/glue.asm b/recipes/rc2014/glue.asm index c05adbc..a1b8e19 100644 --- a/recipes/rc2014/glue.asm +++ b/recipes/rc2014/glue.asm @@ -12,6 +12,7 @@ jp init jp aciaInt #include "core.asm" +#include "parse.asm" ACIA_RAMSTART .equ RAMSTART #include "acia.asm" diff --git a/recipes/rc2014/sdcard/glue.asm b/recipes/rc2014/sdcard/glue.asm index 3294c44..b9856f1 100644 --- a/recipes/rc2014/sdcard/glue.asm +++ b/recipes/rc2014/sdcard/glue.asm @@ -25,6 +25,7 @@ jp init jp aciaInt #include "core.asm" +#include "parse.asm" ACIA_RAMSTART .equ RAMSTART #include "acia.asm" BLOCKDEV_RAMSTART .equ ACIA_RAMEND diff --git a/tools/emul/.gitignore b/tools/emul/.gitignore index b485ee4..d0ec041 100644 --- a/tools/emul/.gitignore +++ b/tools/emul/.gitignore @@ -1,7 +1,8 @@ -/shell -/zasm -/zasm-includes.h -/*-kernel.h -/*-user.h +/shell/shell +/zasm/zasm +/runbin/runbin +/zasm/includes.* +/*/kernel.h +/*/user.h /cfsin /cfsout diff --git a/tools/emul/Makefile b/tools/emul/Makefile index f189a08..514d0d7 100644 --- a/tools/emul/Makefile +++ b/tools/emul/Makefile @@ -1,4 +1,4 @@ -TARGETS = shell/shell zasm/zasm +TARGETS = shell/shell zasm/zasm runbin/runbin KERNEL_HEADERS = shell/kernel.h zasm/kernel.h USER_HEADERS = zasm/user.h CFSPACK = ../cfspack/cfspack @@ -25,6 +25,7 @@ zasm/includes.h: zasm/includes.cfs shell/shell: shell/shell.c libz80/libz80.o shell/kernel.h $(CFSPACK) zasm/zasm: zasm/zasm.c libz80/libz80.o zasm/kernel.h zasm/user.h zasm/includes.h +runbin/runbin: runbin/runbin.c libz80/libz80.o $(TARGETS): cc $< libz80/libz80.o -o $@ diff --git a/tools/emul/runbin/runbin.c b/tools/emul/runbin/runbin.c new file mode 100644 index 0000000..a8e6a34 --- /dev/null +++ b/tools/emul/runbin/runbin.c @@ -0,0 +1,56 @@ +#include +#include +#include "../libz80/z80.h" + +/* runbin loads binary from stdin directly in memory address 0 then runs it + * until it halts. The return code is the value of the register A at halt time. + */ + +static Z80Context cpu; +static uint8_t mem[0x10000]; + +static uint8_t io_read(int unused, uint16_t addr) +{ + addr &= 0xff; + fprintf(stderr, "Out of bounds I/O read: %d\n", addr); + return 0; +} + +static void io_write(int unused, uint16_t addr, uint8_t val) +{ + addr &= 0xff; + fprintf(stderr, "Out of bounds I/O write: %d / %d\n", addr, val); +} + +static uint8_t mem_read(int unused, uint16_t addr) +{ + return mem[addr]; +} + +static void mem_write(int unused, uint16_t addr, uint8_t val) +{ + mem[addr] = val; +} + +int main() +{ + // read stdin in mem + int i = 0; + int c = getchar(); + while (c != EOF) { + mem[i] = c & 0xff; + i++; + c = getchar(); + } + Z80RESET(&cpu); + cpu.ioRead = io_read; + cpu.ioWrite = io_write; + cpu.memRead = mem_read; + cpu.memWrite = mem_write; + + while (!cpu.halted) { + Z80Execute(&cpu); + } + return cpu.R1.br.A; +} + diff --git a/tools/emul/shell/shell_.asm b/tools/emul/shell/shell_.asm index df25dce..4d8fc58 100644 --- a/tools/emul/shell/shell_.asm +++ b/tools/emul/shell/shell_.asm @@ -9,6 +9,7 @@ FS_SEEKH_PORT .equ 0x03 jp init #include "core.asm" +#include "parse.asm" BLOCKDEV_RAMSTART .equ RAMSTART BLOCKDEV_COUNT .equ 4 diff --git a/tools/emul/zasm/glue.asm b/tools/emul/zasm/glue.asm index 851fd9e..405a882 100644 --- a/tools/emul/zasm/glue.asm +++ b/tools/emul/zasm/glue.asm @@ -25,6 +25,7 @@ jp fsSeek jp fsTell #include "core.asm" +#include "parse.asm" .equ BLOCKDEV_RAMSTART RAMSTART .equ BLOCKDEV_COUNT 3 #include "blockdev.asm" diff --git a/tools/tests/Makefile b/tools/tests/Makefile new file mode 100644 index 0000000..563aa51 --- /dev/null +++ b/tools/tests/Makefile @@ -0,0 +1,7 @@ +EMULDIR = ../../tools/emul + +.PHONY: run +run: + make -C $(EMULDIR) zasm runbin + cd unit && ./runtests.sh + cd zasm && ./runtests.sh diff --git a/tools/tests/unit/runtests.sh b/tools/tests/unit/runtests.sh new file mode 100755 index 0000000..59f954a --- /dev/null +++ b/tools/tests/unit/runtests.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e +set -o pipefail + +SCAS=scas +PARTS=../../../parts/z80 +RUNBIN=../../emul/runbin/runbin + +for fn in *.asm; do + echo "Running test ${fn}" + if ! ${SCAS} -I ${PARTS} -o - ${fn} | ${RUNBIN}; then + echo "failed with code ${PIPESTATUS[1]}" + exit 1 + fi +done + +echo "All tests passed!" diff --git a/tools/tests/unit/test_parse.asm b/tools/tests/unit/test_parse.asm new file mode 100644 index 0000000..12dac0a --- /dev/null +++ b/tools/tests/unit/test_parse.asm @@ -0,0 +1,67 @@ +jp test + +#include "core.asm" +#include "parse.asm" + +testNum: .db 1 + +sFoo: .db "Foo", 0 +saB: .db "aB", 0 +s99: .db "99", 0 + +test: + ld hl, 0xffff + ld sp, hl + + ld a, '8' + call parseHex + jp c, fail + cp 8 + jp nz, fail + call nexttest + + ld a, 'e' + call parseHex + jp c, fail + cp 0xe + jp nz, fail + call nexttest + + ld a, 'x' + call parseHex + jp nc, fail + call nexttest + + ld hl, s99 + call parseHexPair + jp c, fail + cp 0x99 + jp nz, fail + call nexttest + + ld hl, sab + call parseHexPair + jp c, fail + cp 0xab + jp nz, fail + call nexttest + + ld hl, sfoo + call parseHexPair + jp nc, fail + call nexttest + + ; success + xor a + halt + +nexttest: + ld a, (testNum) + inc a + ld (testNum), a + ret + +fail: + ld a, (testNum) + halt + diff --git a/tools/tests/zasm/Makefile b/tools/tests/zasm/Makefile deleted file mode 100644 index b9df97c..0000000 --- a/tools/tests/zasm/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -EMULDIR = ../../../tools/emul - -.PHONY: run -run: - make -C $(EMULDIR) zasm - ./runtests.sh