Compare commits

...

6 Commits

Author SHA1 Message Date
~d6 1debdb2ba8 add -raw mode for vm.scm 2025-02-04 23:11:58 -05:00
~d6 e63f77f98e add test case for NIP 2025-02-04 23:11:45 -05:00
~d6 6f9fe3a1fd more tests 2025-02-04 00:20:49 -05:00
~d6 bd510a228e parameterize emulator 2025-02-03 23:25:42 -05:00
~d6 f7812bb19d add some more tests 2025-02-03 23:21:41 -05:00
~d6 a2b807922e add basic test harness 2025-02-03 22:47:30 -05:00
2 changed files with 169 additions and 30 deletions

104
test.scm Normal file
View File

@ -0,0 +1,104 @@
(import (chicken bitwise))
(import (chicken file))
(import (chicken io))
(import (chicken process))
(define tests-ran 0)
(define tests-passed 0)
(define tests-failed 0)
(define (create-rom prog-text)
(let* ((tal-path (create-temporary-file ".tal"))
(rom-path (string-append tal-path ".rom"))
(asm-cmd (string-append "uxnasm " tal-path " " rom-path ">/dev/null"))
(port (open-output-file tal-path)))
(display prog-text port)
(close-output-port port)
(system* asm-cmd)
rom-path))
(define (read-output port)
(let ((s (read-string #f port)))
(if (eq? s #!eof) "" s)))
(define emulator "./vm")
;(define emulator "uxncli")
(define (run-rom prog-text emu-args input-text)
(let*-values (((rom-path) (create-rom prog-text))
((args) (cons rom-path emu-args))
((c-out c-in c-pid c-err) (process* emulator args)))
(display input-text c-in)
(close-output-port c-in)
(let*-values (((out-text) (read-output c-out))
((err-text) (read-output c-err))
((pid ok status) (process-wait c-pid))
((exit-code) (if ok status (- status))))
(values exit-code out-text err-text))))
(define (it-failed test-name caption got expected)
(if (equal? got expected) #f
(begin
(display test-name)
(display ": ")
(display caption)
(display " failed; got '")
(display got)
(display "' but expected '")
(display expected)
(display "'\n")
#t)))
(define (test-rom test-name prog-text emu-args input-text expect-code expect-out expect-err)
(let-values (((exit-code out-text err-text) (run-rom prog-text emu-args input-text))
((ok) (values #t)))
(if (it-failed test-name "stdout" out-text expect-out) (set! ok #f) '())
(if (it-failed test-name "stderr" err-text expect-err) (set! ok #f) '())
(if (it-failed test-name "exit-code" exit-code expect-code) (set! ok #f) '())
ok))
(define (run-test test-name prog-text emu-args input-text expect-code expect-out expect-err)
(let ((ok (test-rom test-name prog-text emu-args input-text expect-code expect-out expect-err)))
(set! tests-ran (+ tests-ran 1))
(if ok
(set! tests-passed (+ tests-passed 1))
(set! tests-failed (+ tests-failed 1)))
(display (if ok " + " " E "))
(display test-name)
(display "\n")))
(define (display-system* cmd)
(display "$ ")
(display cmd)
(display "\n")
(system* cmd))
(display-system* "csc -o vm vm.scm")
(display "running tests...\n\n")
(run-test "zero exit" "|100 #800f DEO BRK" '() "" 0 "" "")
(run-test "non-zero exit" "|100 #110f DEO BRK" '() "" 17 "" "")
(run-test "echo A to stdout" "|100 #4118 DEO #800f DEO BRK" '() "" 0 "A" "")
(run-test "echo A to stderr" "|100 #4119 DEO #800f DEO BRK" '() "" 0 "" "A")
(run-test "test SWP" "|100 #40 #4142 SWP #18 DEO #18 DEO #18 DEO #800f DEO BRK" '() "" 0 "AB@" "")
(run-test "test DUP" "|100 #40 #41 DUP #18 DEO #18 DEO #18 DEO #800f DEO BRK" '() "" 0 "AA@" "")
(run-test "test DUPk" "|100 #4042 DUPk #18 DEO #18 DEO #18 DEO #18 DEO #800f DEO BRK" '() "" 0 "BBB@" "")
(run-test "test DUP2" "|100 #40 #4318 DUP2 DEO DEO #18 DEO #800f DEO BRK" '() "" 0 "CC@" "")
(run-test "test DEOk" "|100 #4041 #18 DEOk DEO #18 DEO #800f DEO BRK" '() "" 0 "AA@" "")
(run-test "test DEOkr" "|100 #40 LIT2r 4218 DEOkr DEOr #18 DEO #800f DEO BRK" '() "" 0 "BB@" "")
(run-test "test ROT" "|100 #4041 #4342 ROT #18 DEO #18 DEO #18 DEO #18 DEO #800f DEO BRK" '() "" 0 "ABC@" "")
(run-test "test SWP2" "|100 #40 #4241 #4443 SWP2 #18 DEO #18 DEO #18 DEO #18 DEO #18 DEO #800f DEO BRK" '() "" 0 "ABCD@" "")
(run-test "test NIP" "|100 #40 #4241 NIP #18 DEO #18 DEO #800f DEO BRK" '() "" 0 "A@" "")
(display "\nsummary: ran ")
(display tests-ran)
(display " tests\n")
(display " ")
(display tests-passed)
(display " passed\n")
(display " ")
(display tests-failed)
(display " failed\n")

95
vm.scm
View File

@ -7,15 +7,18 @@
;; === implementation-dependent functions; uncomment one stanza ;; === implementation-dependent functions; uncomment one stanza
;; chicken scheme ;; chicken scheme
(import (chicken bitwise) (chicken process-context) (chicken time posix)) (import (chicken bitwise))
(define (get-u8 port) (import (chicken file))
(let ((c (read-char port))) (if (eof-object? c) c (char->integer c)))) (import (chicken process))
(import (chicken process-context))
(import (chicken time posix))
(import (chicken io))
(define (get-u8 port) (read-byte port))
(define (open-rom path) (open-input-file path #:binary)) (define (open-rom path) (open-input-file path #:binary))
(define (get-date-time) (define (get-date-time)
(let* ((v (seconds->local-time)) (let* ((v (seconds->local-time))
(y (vector-ref v 5))) (y (vector-ref v 5)))
(vector-set! v 5 (+ y 1900)) (vector-set! v 5 (+ y 1900)) v))
v))
;; ;; guile, invoke with guile vm.scm ROM ... ;; ;; guile, invoke with guile vm.scm ROM ...
;; (use-modules (srfi srfi-60) (ice-9 binary-ports)) ;; (use-modules (srfi srfi-60) (ice-9 binary-ports))
@ -69,6 +72,8 @@
(define rst (cons 0 (make-vector 256 0))) (define rst (cons 0 (make-vector 256 0)))
(define pc 256) (define pc 256)
(define done #f) (define done #f)
(define raw-tty #f)
(define saved-tty '())
;; instruction modes ;; instruction modes
(define _2 #f) (define _2 #f)
@ -245,8 +250,8 @@
((= 4 port) (set-car! wst byte)) ((= 4 port) (set-car! wst byte))
((= 5 port) (set-car! rst byte)) ((= 5 port) (set-car! rst byte))
((= 14 port) (u:debug)) ((= 14 port) (u:debug))
((= 24 port) (write-char (integer->char byte) (current-output-port))) ((= 24 port) (write-byte byte (current-output-port)))
((= 25 port) (write-char (integer->char byte) (current-error-port))) ((= 25 port) (write-byte byte (current-error-port)))
(* '()))) (* '())))
(define (u:debug) (define (u:debug)
@ -345,26 +350,28 @@
(fn) (fn)
(u:eval)))) (u:eval))))
(define (u:exit n) (u:restore-tty) (exit n))
(define (u:run addr) (define (u:run addr)
(set! done #f) (set! done #f)
(set! pc addr) (set! pc addr)
(u:eval) (u:eval)
(let ((n (vector-ref dev 15))) (let ((n (vector-ref dev 15)))
(if (= n 0) '() (if (= n 0) '()
(exit (bitwise-and n 127))))) (u:exit (bitwise-and n 127)))))
(define (u:read-args args) (define (u:read-args args)
(if (null? args) (if (null? args)
(u:send-input #\newline 4) (u:send-input 10 4)
(begin (begin
(u:read-arg (string->list (car args))) (u:read-arg (string->list (car args)))
(if (null? (cdr args)) '() (u:send-input #\newline 3)) (if (null? (cdr args)) '() (u:send-input 10 3))
(u:read-args (cdr args))))) (u:read-args (cdr args)))))
(define (u:read-arg chars) (define (u:read-arg chars)
(if (null? chars) '() (if (null? chars) '()
(begin (begin
(u:send-input (car chars) 2) (u:send-input (char->integer (car chars)) 2)
(u:read-arg (cdr chars))))) (u:read-arg (cdr chars)))))
;; send a character of console input with type ;; send a character of console input with type
@ -373,22 +380,22 @@
;; - 2 argument data ;; - 2 argument data
;; - 3 spacer between arguments ;; - 3 spacer between arguments
;; - 4 spacer after arguments, or end of stdin ;; - 4 spacer after arguments, or end of stdin
(define (u:send-input c type) (define (u:send-input n type)
(let* ((hi (vector-ref dev 16)) (let* ((hi (vector-ref dev 16))
(lo (vector-ref dev 17)) (lo (vector-ref dev 17))
(addr (hilo->u16 hi lo))) (addr (hilo->u16 hi lo)))
(if (= 0 addr) '() (if (= 0 addr) '()
(begin (begin
(vector-set! dev 18 (u8 (char->integer c))) (vector-set! dev 18 (u8 n))
(vector-set! dev 23 (u8 type)) (vector-set! dev 23 (u8 type))
(u:run addr))))) (u:run addr)))))
(define (u:read-stdin port) (define (u:read-stdin port)
(let ((c (read-char port))) (let ((byte (read-byte port)))
(if (eof-object? c) (if (eof-object? byte)
(u:send-input #\newline 4) (u:send-input 10 4)
(begin (begin
(u:send-input c 1) (u:send-input byte 1)
(u:read-stdin port))))) (u:read-stdin port)))))
;; load a ROM from a file, byte-by-byte ;; load a ROM from a file, byte-by-byte
@ -399,16 +406,44 @@
(begin (vector-set! mem addr (u8 byte)) (begin (vector-set! mem addr (u8 byte))
(u:load-rom rom (+ addr 1)))))) (u:load-rom rom (+ addr 1))))))
(let ((prog-args (command-line-arguments))) (define (u:read-cmd cmd)
(if (null? prog-args) (with-input-from-pipe cmd (lambda () (read-string #f (current-input-port)))))
(begin (display "usage: ./vm ROM [ARGS...]\n") (exit))
(let* ((rom-path (car prog-args)) (define (u:usage)
(rom-args (cdr prog-args)) (display "usage: ./vm [-raw] ROM [ARGS...]\n")
(rom (open-rom rom-path)) (exit))
(start 256))
(vector-set! dev 23 (if (null? rom-args) 0 1)) (define (u:parse-args args)
(u:load-rom rom start) (if (null? args) (u:usage))
(u:run start) (if (equal? (car args) "-raw")
(if (null? rom-args) '() (u:read-args rom-args)) (begin (set! raw-tty #t)
(u:read-stdin (current-input-port)) (u:parse-args (cdr args)))
(exit 0)))) args))
(define (u:check-rom path)
(if (and (file-exists? path) (file-readable? path)) '()
(begin (display "ERROR: cannot open ROM '")
(display path)
(display "'\n")
(u:usage))))
(define (u:set-tty)
(if raw-tty (begin (set! saved-tty (u:read-cmd "stty -g"))
(system* "stty raw -echo"))))
(define (u:restore-tty)
(if raw-tty (system* (string-append "stty " saved-tty))))
(let* ((prog-args (u:parse-args (command-line-arguments)))
(_ (u:set-tty))
(rom-path (car prog-args))
(rom-args (cdr prog-args))
(_ (u:check-rom rom-path))
(rom (open-rom rom-path))
(start 256))
(vector-set! dev 23 (if (null? rom-args) 0 1))
(u:load-rom rom start)
(u:run start)
(if (null? rom-args) '() (u:read-args rom-args))
(u:read-stdin (current-input-port))
(u:exit 0))