aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--src/1.s16
-rw-r--r--src/2.s12
-rw-r--r--test/1.lua13
-rw-r--r--test/2.lua13
-rw-r--r--test/defines.s11
-rw-r--r--test/run.sh44
-rw-r--r--test/utils.lua80
8 files changed, 193 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 37723c5..0784337 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,8 @@
CC65 ?= cl65
CCOPTS ?= --target nes
+ifeq "$(DEBUG)" "1"
+CCOPTS += -g -Ln out/labels.txt
+endif
QUIET = @echo ' ' CC65 $@;
SOURCES = $(shell find src/ -type f -name '*.s' -printf "%f\n")
@@ -10,6 +13,7 @@ all: clean deps build
.PHONY: clean
clean:
+ @sed -i 's/RUN_TESTS = 1/RUN_TESTS = 0/g' test/defines.s
@rm -rf out
@mkdir out
@find . -type f -name "*.o" -delete
@@ -24,3 +28,7 @@ build: $(ROMS)
out/%.nes: src/%.s
$(QUIET) $(CC65) $(CCOPTS) $< -o $@
+
+.PHONY: test
+test:
+ @bash test/run.sh
diff --git a/src/1.s b/src/1.s
index d4cffa2..4f4ae38 100644
--- a/src/1.s
+++ b/src/1.s
@@ -33,6 +33,8 @@
.include "../include/globals.s"
.include "../include/print.s"
+.include "../test/defines.s"
+
;; "Variables" used by this program.
.scope Vars
;; After each iteration, this will hold the tenth of the number that has
@@ -99,8 +101,18 @@
lda #%00011110
sta PPU::MASK
@loop:
+ ;; Skip everything if the `done` flag is set.
+ bit Globals::m_flags
+ bvs @end
+
jsr compute_next
jmp @loop
+
+@end:
+ ;; Run tests now that everything has been done.
+ CALL_TESTS_ON_DONE
+@halt:
+ jmp @halt
.endproc
;; Iterate over the row pointed by `m_address` and compute its value. The tenth
@@ -153,10 +165,6 @@
;; address to the next row. This function will also set the `done` flag whenever
;; we reach the $ED control byte.
.proc compute_next
- ;; Skip everything if the `done` flag is set.
- bit Globals::m_flags
- bvs @end
-
;; Call `compute` and `accumulate_number`, which work in tandem in order to
;; fetch the value for the current row and add it into the `m_sum`
;; accumulator.
diff --git a/src/2.s b/src/2.s
index e4f0361..353cb16 100644
--- a/src/2.s
+++ b/src/2.s
@@ -38,6 +38,8 @@
.include "../include/globals.s"
.include "../include/print.s"
+.include "../test/defines.s"
+
;; "Variables" used by this program.
.scope Vars
;; Maximum number for each color according to Part 1.
@@ -127,8 +129,18 @@
sta PPU::MASK
@loop:
+ ;; Skip everything if the `done` flag is set.
+ bit Globals::m_flags
+ bvs @end
+
jsr compute_next
jmp @loop
+
+@end:
+ ;; Run tests now that everything has been done.
+ CALL_TESTS_ON_DONE
+@halt:
+ jmp @halt
.endproc
;; Compute the next game if needed.
diff --git a/test/1.lua b/test/1.lua
new file mode 100644
index 0000000..b51f21e
--- /dev/null
+++ b/test/1.lua
@@ -0,0 +1,13 @@
+utils = require "utils"
+
+utils.StartRun("1")
+
+utils.MemTest("@test", {{0x08, "D6"}, {0x09, "A9"}})
+
+-- The test appears to be done in a few frames. Let's run this several times
+-- just in case.
+for i = 0, 20, 1 do
+ emu.frameadvance();
+end
+
+utils.EndRun()
diff --git a/test/2.lua b/test/2.lua
new file mode 100644
index 0000000..7cb8f40
--- /dev/null
+++ b/test/2.lua
@@ -0,0 +1,13 @@
+utils = require "utils"
+
+utils.StartRun("2")
+
+utils.MemTest("@test", {{0x0B, "00"}, {0x0C, "01"}, {0x0D, "0F"}, {0x0E, "FD"}})
+
+-- The test appears to be done in a few frames. Let's run this several times
+-- just in case.
+for i = 0, 20, 1 do
+ emu.frameadvance();
+end
+
+utils.EndRun()
diff --git a/test/defines.s b/test/defines.s
new file mode 100644
index 0000000..17c0221
--- /dev/null
+++ b/test/defines.s
@@ -0,0 +1,11 @@
+RUN_TESTS = 0
+
+.macro CALL_TESTS_ON_DONE
+ .ifdef RUN_TESTS
+ bit Globals::m_flags
+ bvc :+
+ @test:
+ nop
+ :
+ .endif
+.endmacro
diff --git a/test/run.sh b/test/run.sh
new file mode 100644
index 0000000..2142ad1
--- /dev/null
+++ b/test/run.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+set -e
+
+ROOT="$( cd "$( dirname "$0" )/.." && pwd )"
+cd "$ROOT"
+
+# Clean previous builds and prepare the test environment.
+rm -f "$ROOT/out/test-results.txt"
+make clean
+sed -i 's/RUN_TESTS = 0/RUN_TESTS = 1/g' $ROOT/test/defines.s
+
+# Github Actions do not allow GUI programs to be run. This means that the code
+# below will always fail (fceux won't be able to run). There is a way to emulate
+# an X server with tools like xvfb-run or xvncserver, but so far I've had no
+# luck on this front.
+if [ -n "${GITHUB_ACTION}" ]; then
+ exit 0
+fi
+
+# Run all the tests that we have on Lua.
+for name in $(seq 2); do
+ DEBUG=1 make out/$name.nes
+ fceux --loadlua "$ROOT/test/$name.lua" "$ROOT/out/$name.nes"
+done
+
+# Show the results.
+cat "$ROOT/out/test-results.txt"
+n=$(cat "$ROOT/out/test-results.txt" | grep FAIL | wc -l)
+echo ""
+case $n in
+ 0)
+ echo "All tests passed!"
+ exit 0
+ ;;
+ 1)
+ echo "1 test failed!"
+ exit 1
+ ;;
+ *)
+ echo "$n tests failed!"
+ exit 1
+ ;;
+esac
diff --git a/test/utils.lua b/test/utils.lua
new file mode 100644
index 0000000..c5ecd94
--- /dev/null
+++ b/test/utils.lua
@@ -0,0 +1,80 @@
+utils = {}
+
+-- Returns the root path for the project.
+function utils.RootPath()
+ local fullpath = debug.getinfo(1,"S").source:sub(2)
+ fullpath = io.popen("realpath '"..fullpath.."'", 'r'):read()
+ fullpath = fullpath:gsub('[\n\r]*$','')
+
+ local dirname, filename = fullpath:match('^(.*/)([^/]-)$')
+ dirname = dirname or ''
+ if dirname == '' then
+ return ''
+ end
+
+ return io.popen("realpath '"..dirname.."/..'", 'r'):read()
+end
+
+-- At a `label` that exists on the assembly code grab the values for the given
+-- addresses and write it all into the `test-results.txt` file. The `addresses`
+-- array is made up of two-sized arrays, where the first element contains the
+-- memory you are trying to test, and the second element is the value that we
+-- are expecting.
+function utils.MemTest(label, addresses)
+ local cmd = "cat ".. utils.RootPath() .. "/out/labels.txt | grep .".. label .." | awk '{ print $2; }' | cut -c3-"
+ local file = assert(io.popen(cmd, 'r'))
+ local result = file:read("*a")
+
+ -- Double check that the address that we grabbed has at least a good format.
+ if string.len(result) ~= 5 then
+ error("Error on '" .. label .. "': got a bad address! (".. result ..")")
+ end
+
+ -- Register a function to execute on the given test address. The function will
+ -- simply iterate over the given `addresses` and compare them with the
+ -- expected result. Everything will be saved into the `test-results.txt` file.
+ memory.registerexecute(tonumber(result, 16), function()
+ local expected = ""
+ local got = ""
+
+ for _, vals in ipairs(addresses) do
+ expected = expected .. "$" .. string.format("%04X", vals[1]) .. " -> " .. vals[2] .. "; "
+ got = got .. "$" .. string.format("%04X", vals[1]) .. " -> " .. string.format("%02X", memory.readbyte(vals[1])) .. "; "
+ end
+
+ file = io.open(utils.RootPath() .. "/out/test-results.txt", "a")
+ io.output(file)
+
+ if expected == got then
+ io.write("OK\n")
+ else
+ io.write("\n-> Test '".. label .."': FAIL\n")
+ io.write("Expected: ".. expected .. "\n")
+ io.write("Got: ".. got .. "\n")
+ io.write("\n")
+ end
+ io.close(file)
+ end)
+end
+
+function utils.StartRun(title)
+ file = io.open(utils.RootPath() .. "/out/test-results.txt", "a")
+ io.output(file)
+ io.write(title .. ": ")
+ io.close(file)
+end
+
+-- Ends the given test run. That is, it will exit from the emulator so we can
+-- turn back to the runner.
+function utils.EndRun()
+ -- I'm not entirely sure why this is needed, but if we don't advance for
+ -- several frames fceux won't exit. Thus, let's advance for some frames and
+ -- then quit.
+ for i = 0, 10, 1 do
+ emu.frameadvance();
+ end
+
+ emu.exit()
+end
+
+return utils