mirror of
https://github.com/hsoft/collapseos.git
synced 2024-12-24 14:38:05 +11:00
Add the concept of unit tests
Will be much much easier to tests new core routines without having to re-create their context first. Also, extract parse.asm from core.asm
This commit is contained in:
parent
e4ffe669a1
commit
013a3b74c8
@ -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:
|
||||
|
63
parts/z80/parse.asm
Normal file
63
parts/z80/parse.asm
Normal file
@ -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
|
@ -14,6 +14,7 @@
|
||||
; hexadecimal form, without prefix or suffix.
|
||||
|
||||
; *** REQUIREMENTS ***
|
||||
; parse
|
||||
; stdio
|
||||
; blkdev
|
||||
|
||||
|
@ -12,6 +12,7 @@ jp init
|
||||
jp aciaInt
|
||||
|
||||
#include "core.asm"
|
||||
#include "parse.asm"
|
||||
ACIA_RAMSTART .equ RAMSTART
|
||||
#include "acia.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
|
||||
|
11
tools/emul/.gitignore
vendored
11
tools/emul/.gitignore
vendored
@ -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
|
||||
|
@ -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 $@
|
||||
|
||||
|
56
tools/emul/runbin/runbin.c
Normal file
56
tools/emul/runbin/runbin.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
7
tools/tests/Makefile
Normal file
7
tools/tests/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
EMULDIR = ../../tools/emul
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
make -C $(EMULDIR) zasm runbin
|
||||
cd unit && ./runtests.sh
|
||||
cd zasm && ./runtests.sh
|
18
tools/tests/unit/runtests.sh
Executable file
18
tools/tests/unit/runtests.sh
Executable file
@ -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!"
|
67
tools/tests/unit/test_parse.asm
Normal file
67
tools/tests/unit/test_parse.asm
Normal file
@ -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
|
||||
|
@ -1,6 +0,0 @@
|
||||
EMULDIR = ../../../tools/emul
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
make -C $(EMULDIR) zasm
|
||||
./runtests.sh
|
Loading…
Reference in New Issue
Block a user