mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-23 22:38:06 +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
|
pop af
|
||||||
ret
|
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
|
; 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.
|
; equal, Z is set. If not equal, Z is reset.
|
||||||
strncmp:
|
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.
|
; hexadecimal form, without prefix or suffix.
|
||||||
|
|
||||||
; *** REQUIREMENTS ***
|
; *** REQUIREMENTS ***
|
||||||
|
; parse
|
||||||
; stdio
|
; stdio
|
||||||
; blkdev
|
; blkdev
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ jp init
|
|||||||
jp aciaInt
|
jp aciaInt
|
||||||
|
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
|
#include "parse.asm"
|
||||||
ACIA_RAMSTART .equ RAMSTART
|
ACIA_RAMSTART .equ RAMSTART
|
||||||
#include "acia.asm"
|
#include "acia.asm"
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ jp init
|
|||||||
jp aciaInt
|
jp aciaInt
|
||||||
|
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
|
#include "parse.asm"
|
||||||
ACIA_RAMSTART .equ RAMSTART
|
ACIA_RAMSTART .equ RAMSTART
|
||||||
#include "acia.asm"
|
#include "acia.asm"
|
||||||
BLOCKDEV_RAMSTART .equ ACIA_RAMEND
|
BLOCKDEV_RAMSTART .equ ACIA_RAMEND
|
||||||
|
11
tools/emul/.gitignore
vendored
11
tools/emul/.gitignore
vendored
@ -1,7 +1,8 @@
|
|||||||
/shell
|
/shell/shell
|
||||||
/zasm
|
/zasm/zasm
|
||||||
/zasm-includes.h
|
/runbin/runbin
|
||||||
/*-kernel.h
|
/zasm/includes.*
|
||||||
/*-user.h
|
/*/kernel.h
|
||||||
|
/*/user.h
|
||||||
/cfsin
|
/cfsin
|
||||||
/cfsout
|
/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
|
KERNEL_HEADERS = shell/kernel.h zasm/kernel.h
|
||||||
USER_HEADERS = zasm/user.h
|
USER_HEADERS = zasm/user.h
|
||||||
CFSPACK = ../cfspack/cfspack
|
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)
|
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
|
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):
|
$(TARGETS):
|
||||||
cc $< libz80/libz80.o -o $@
|
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
|
jp init
|
||||||
|
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
|
#include "parse.asm"
|
||||||
|
|
||||||
BLOCKDEV_RAMSTART .equ RAMSTART
|
BLOCKDEV_RAMSTART .equ RAMSTART
|
||||||
BLOCKDEV_COUNT .equ 4
|
BLOCKDEV_COUNT .equ 4
|
||||||
|
@ -25,6 +25,7 @@ jp fsSeek
|
|||||||
jp fsTell
|
jp fsTell
|
||||||
|
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
|
#include "parse.asm"
|
||||||
.equ BLOCKDEV_RAMSTART RAMSTART
|
.equ BLOCKDEV_RAMSTART RAMSTART
|
||||||
.equ BLOCKDEV_COUNT 3
|
.equ BLOCKDEV_COUNT 3
|
||||||
#include "blockdev.asm"
|
#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