diff --git a/README.md b/README.md index 919467b..b1b5bd4 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,12 @@ # CHIP-8 test suite _A collection of ROM images with tests that will aid you in developing your own -CHIP-8, SCHIP or XO-CHIP interpreter (or "emulator")_ +CHIP-8, SUPER-CHIP or XO-CHIP interpreter (or "emulator")_ ## Table of contents * [Introduction](#introduction) + * [Baseline](#baseline) * [Available tests](#available-tests) * [CHIP-8 splash screen](#chip-8-splash-screen) * [IBM logo](#ibm-logo) @@ -15,6 +16,8 @@ CHIP-8, SCHIP or XO-CHIP interpreter (or "emulator")_ * [Flags test](#flags-test) * [Quirks test](#quirks-test) * [Keypad test](#keypad-test) + * [Beep test](#beep-test) + * [Scrolling test](#scrolling-test) * [Contributing](#contributing) * [Community response 😄](#community-response-) @@ -22,9 +25,9 @@ CHIP-8, SCHIP or XO-CHIP interpreter (or "emulator")_ I found it hard to find reliable sources on what is the right behaviour and what is not, especially with the subtle differences between the original "Cosmac VIP" -CHIP-8 and the HP84's SCHIP (or "superchip"). Now that I have written and ported -a couple of interpreters as well as a few programs and games for the platform, I -thought it was time to put that knowledge into code. +CHIP-8 and the HP48's SUPER-CHIP (or "S-CHIP"). Now that I have written and +ported a couple of interpreters as well as a few programs and games for the +platform, I thought it was time to put that knowledge into code. If you're having issues with your interpreter, you can find help in the [EmuDev discord channel `#chip-8`](https://discord.gg/dkmJAes). Every test has a clearly @@ -33,6 +36,40 @@ share a screenshot. If you discover a problem with this test ROM itself, feel free to file an issue or open a pull request. It's open source, licensed under the GPLv3, and you're welcome to [contribute](#contributing). +# Baseline + +Most tests have been written to run equally well on all three major CHIP-8 +platforms, unless otherwise specified. The [quirks test](#quirks-test) is the +most interesting one, since it was designed to test the differences between +those three platforms. If the test suite itself is wrong, we're not helping +anyone. So what are we testing the test suite against? + +## CHIP-8 + +There are several good Cosmac VIP emulators, so we can quite faithfully run the +original CHIP-8 interpreter and check the results. We use [Emma +O2](https://www.emma02.hobby-site.com/) and/or +[Cadmium](https://github.com/gulrak/cadmium) in `VIP-CHIP-8` mode to validate +the test suite. + +If you have an actual, physical Cosmac VIP and would like to verify the test +suite, let me know! 😄 + +## SUPER-CHIP + +For SUPER-CHIP, the test suite has been tested against real HP48 graphing +calculators, in the various interpreters that exist for that system. +[Gulrak](https://github.com/gulrak) from the CHIP-8 community has both an HP48SX +and an HP48GX, and has been so kind as to check if the test suite ROMs behave as +expected. + +## XO-CHIP + +The XO-CHIP extension was written by [John +Earnest](https://github.com/johnearnest) and was first implemented in his IDE +slash interpreter Octo. As such, we treat [Octo](http://octo-ide.com/) as the +gold standard for how an XO-CHIP system should behave, and test against that. + # Available tests ## CHIP-8 splash screen @@ -242,20 +279,22 @@ for more information on the arithmetic operations and the flags. * [Run this ROM in Octo](https://timendus.github.io/chip8-test-suite/5-quirks.html) to see what's supposed to happen -CHIP-8, SCHIP and XO-CHIP have subtle differences in the way they interpret the -bytecode. We often call these differences quirks. This test detects which quirks -your interpreter implements, and if those quirks match the platform you're -trying to target. This is one of the hardest parts to "get right" and often a -reason why "some games work, but some don't". +CHIP-8, SUPER-CHIP and XO-CHIP have subtle differences in the way they interpret +the bytecode. We often call these differences quirks. This test detects which +quirks your interpreter implements, and if those quirks match the platform +you're trying to target. This is one of the hardest parts to "get right" and +often a reason why "some games work, but some don't". ### The menu -The test asks you to choose the platform you are targeting: +The test asks you to choose the platform you are targeting. If you select +SUPER-CHIP, if will then also ask you if you want to test for the "modern" or +the "legacy" behaviour. When in doubt, go for the "modern" one. ![Choosing a target platform in the quirks test](./pictures/quirks-platform.png) -You can press any of the numbers `1` to `3` on the CHIP-8 keypad to jump to the -corresponding test. +You can press any of the numbers `1` to `3` on the CHIP-8 keypad to make the +corresponding selection. Alternatively, you can move the cursor up and down with CHIP-8 keys `E` and `F` and select an item with `A`. This feature mainly exists so people implementing @@ -264,9 +303,14 @@ can map their buttons to those CHIP-8 keys and have an intuitive interface too. If you want to repeat a test often or even automate them, having to use the graphical menu just gets in the way. In that case, you can force this ROM to -select a specific platform by loading a value between `1` and `3` into memory at +select a specific platform by loading a value between `1` and `4` into memory at the address `0x1FF` (`512`). +* `1` - CHIP-8 +* `2` - SUPER-CHIP with modern behaviour +* `3` - XO-CHIP +* `4` - SUPER-CHIP with legacy behaviour + ### The test The test will now run through a couple of steps, which you will see on the @@ -280,14 +324,15 @@ or an error) and if that matches your chosen target platform (a checkmark or a cross). * `vF reset` - The AND, OR and XOR opcodes (`8xy1`, `8xy2` and `8xy3`) reset the - flags register to zero. Test will show `E1` if the AND and OR tests don't - behave the same and `E2` if the AND and XOR tests don't behave the same. + flags register to zero. Test will show `ERR1` if the AND and OR tests don't + behave the same and `ERR2` if the AND and XOR tests don't behave the same. * `Memory` - The save and load opcodes (`Fx55` and `Fx65`) increment the index - register. More information [here](https://laurencescotford.net/chip-8-on-the-cosmac-vip-loading-and-saving-variables/) and [here](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/#fx55-and-fx65-store-and-load-memory). + register. More information [here](https://laurencescotford.net/chip-8-on-the-cosmac-vip-loading-and-saving-variables/) + and [here](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/#fx55-and-fx65-store-and-load-memory). * `Display wait` - Drawing sprites to the display waits for the vertical blank interrupt, limiting their speed to max 60 sprites per second. More information [here](https://laurencescotford.net/chip-8-on-the-cosmac-vip-drawing-sprites/). - Test will show `LOW` if the number of cycles per frame is too low for the test + Test will show `SLOW` if the number of cycles per frame is too low for the test to be deterministic (this is not necessarily an error, but a suggestion to rerun the test with a higher number of cycles per frame). * `Clipping` - Sprites drawn at the bottom edge of the screen get clipped @@ -296,18 +341,43 @@ cross). of the screen. This also tests that sprites drawn at coordinates of `x > 63` and/or `y > 31` wrap around to `x % 64` and `y % 32`. More information [here](https://laurencescotford.net/chip-8-on-the-cosmac-vip-drawing-sprites/). - Test will show `E1` if the clipping is inconsistent in different dimensions or - wrapping to the wrong coordinates and `E2` if sprites don't wrap around as - expected. + Test will show `ERR1` if the clipping is inconsistent in different dimensions + or wrapping to the wrong coordinates and `ERR2` if sprites don't wrap around + as expected. * `Shifting` - The shift opcodes (`8xy6` and `8xyE`) only operate on `vX` instead of storing the shifted version of `vY` in `vX` (more information - [here](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/#8xy6-and-8xye-shift)). Test will show `E1` if the shift opcodes behave differently. + [here](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/#8xy6-and-8xye-shift)). + Test will show `ERR1` if the shift opcodes behave differently. * `Jumping` - The "jump to some address plus `v0`" instruction (`Bnnn`) doesn't use `v0`, but `vX` instead where `X` is the highest nibble of `nnn` (more information [here](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/#bnnn-jump-with-offset)) Note that you need timer support for this test to run. +### SUPER-CHIP / XO-CHIP + +If you select SUPER-CHIP or XO-CHIP in the menu, half of the test will be +executed in `hires` mode, and the behaviour of `Display wait` and `Clipping` +will be tested in both `lores` and `hires` modes. This means you will not just +see "on" or "off" for these quirks, but instead one of these values: + +* `NONE` - The quirk is disabled in both modes +* `HRES` - The quirk is only enabled in `hires` mode +* `LRES` - The quirk is only enabled in `lores` mode +* `BOTH` - The quirk is enabled in both modes + +If the test finds different errors for the `Clipping` test in `hires` mode +compared to `lores` mode, it will show `ERR3`. In that case, first make sure +your modes produce the same wrapping and clipping results, and see which errors +pop up after that. + +Wondering why testing `hires` has been added, or if a quirk can ever be enabled +in only one of the modes? Or just wondering why SUPER-CHIP has a "legacy" and a +"modern" version of the test? You can [read the full story +here](./legacy-superchip.md). + +### More information + See this [excellent table](https://chip8.gulrak.net) by Gulrak for an overview of all the known quirks for the relatively popular CHIP-8 versions. @@ -383,7 +453,84 @@ See [this article](https://laurencescotford.net/chip-8-on-the-cosmac-vip-keyboard-input/) for more information. -## Contributing +## Beep test + +* [Download ROM](https://github.com/Timendus/chip8-test-suite/raw/main/bin/7-beep.ch8) + (source code available [here](./src/tests/7-beep.8o)) +* [Run this ROM in Octo](https://timendus.github.io/chip8-test-suite/7-beep.html) + to see what's supposed to happen (sound may not actually play due to browser + restrictions) + +This test allows you to test if your buzzer is working. It will beep SOS in +morse code and flash a speaker icon on the display in the same pattern. If you +press the CHIP-8 button `B` it will give you manual control over the buzzer. +Press `B` to beep. + +![The beep test beeping](./pictures/beep.png) + +## Scrolling test + +* [Download ROM](https://github.com/Timendus/chip8-test-suite/raw/main/bin/8-scrolling.ch8) + (source code available [here](./src/tests/8-scrolling.8o)) +* [Run this ROM in Octo](https://timendus.github.io/chip8-test-suite/8-scrolling.html) + to see what's supposed to happen + +This test is only applicable to SUPER-CHIP and XO-CHIP interpreters, since +regular CHIP-8 does not have scrolling instructions. It will test to see if your +scrolling opcodes scroll the display in the right directions by the right +amounts of pixels. One literal "edge"-case that it does **not** cover is what +happens at the edges of the screen. + +### The menu + +The test asks you to choose the platform and resolution you are targeting. For +SUPER-CHIP `lores`, it will also ask you to choose between "modern" or "legacy" +behaviour. When in doubt, go for the "modern" one. + +![Choosing a target platform in the scrolling test](./pictures/scrolling-platform.png) + +You can press any of the numbers `1` or `2` on the CHIP-8 keypad to select the +corresponding entry. + +Alternatively, you can move the cursor up and down with CHIP-8 keys `E` and `F` +and select an item with `A`. This feature mainly exists so people implementing +interpreters for platforms with limited input devices (like a game controller) +can map their buttons to those CHIP-8 keys and have an intuitive interface too. + +If you want to repeat a test often or even automate them, having to use the +graphical menu just gets in the way. In that case, you can force this ROM to +select a specific platform by loading a value between `1` and `5` into memory at +the address `0x1FF` (`512`). + +* `1` - SUPER-CHIP `lores` with modern behaviour +* `2` - SUPER-CHIP `lores` with legacy behaviour +* `3` - SUPER-CHIP `hires` +* `4` - XO-CHIP `lores` +* `5` - XO-CHIP `hires` + +### The test + +The test will show you a visual with arrows and boxes. If everything works as +expected for the target you have selected, all the arrows will end up in their +boxes, like so: + +![Result of the scrolling test for `lores` SUPER-CHIP](./pictures/lores-scrolling.png) + +If you have issues with one or more of your scrolling instructions, some arrows +will be (partially) outside their boxes. The arrows all point in the directions +that they should be scrolled in, so the ones that have not moved in the +direction that they point in represent the scrolling instructions that are not +working properly. + +For example, this is what you see if none of the scrolling instructions have +been implemented: + +![Result of the scrolling test with no implemented scrolling](./pictures/lores-no-scrolling.png) + +A note on legacy versus modern behaviour for SUPER-CHIP's `lores` mode can be +found in the document [Legacy SUPER-CHIP](./legacy-superchip.md). + +# Contributing Do you find an issue in this test suite that you think you can fix? Feel free to submit a PR! Here's how to build the project, assuming you have Nodejs and NPM @@ -399,12 +546,14 @@ npm install npm start # Build a specific test: -npm run build-logo -npm run build-ibm -npm run build-corax -npm run build-flags -npm run build-quirks -npm run build-keypad +TEST=1-chip8-logo npm run build-test +TEST=2-ibm-logo npm run build-test +TEST=3-corax+ npm run build-test +TEST=4-flags npm run build-test +TEST=5-quirks npm run build-test +TEST=6-keypad npm run build-test +TEST=7-scrolling npm run build-test +TEST=8-beep npm run build-test ``` Note that the `npm` scripts use the MacOS command `pbcopy` to copy @@ -416,7 +565,7 @@ not work properly. Edit `package.json` and remove this part from the end of `scr && cat bin/${TEST}.8o | pbcopy ``` -## Community response 😄 +# Community response 😄 [![DUDE THANKS! I was writing a CHIP-8 emulator and THIS HELPED ME SO FRICKING MUCH, THANKS! / same here, this is amazing](./pictures/testimonial1.png)](https://github.com/Timendus/chip8-test-suite/issues/1) diff --git a/bin/1-chip8-logo.8o b/bin/1-chip8-logo.8o index 8fd459b..da2b4e9 100644 --- a/bin/1-chip8-logo.8o +++ b/bin/1-chip8-logo.8o @@ -61,7 +61,7 @@ : splash-3-1 0xce 0xfc 0xf8 0xc0 0xd4 0xdc 0xc4 0xc5 0x00 0x00 0x30 0x44 0x24 0x14 0x63 : splash-4-1 - 0xf1 0x03 0x07 0x07 0x77 0x57 0x53 0x71 0x00 0x00 0x28 0x8e 0xa8 0xa8 0xa6 + 0xf1 0x03 0x07 0x07 0x27 0x67 0x23 0x71 0x00 0x00 0x28 0x8e 0xa8 0xa8 0xa6 : splash-5-1 0xce 0x87 0x03 0x03 0x03 0x87 0xfe 0xfc 0x00 0x00 0x60 0x90 0xf0 0x80 0x70 diff --git a/bin/1-chip8-logo.ch8 b/bin/1-chip8-logo.ch8 index 4213638..8f83105 100644 Binary files a/bin/1-chip8-logo.ch8 and b/bin/1-chip8-logo.ch8 differ diff --git a/bin/2-ibm-logo.8o b/bin/2-ibm-logo.8o index 384e58d..3470889 100644 --- a/bin/2-ibm-logo.8o +++ b/bin/2-ibm-logo.8o @@ -58,5 +58,5 @@ : ibm-4-0 0x03 0x00 0x07 0x00 0x0f 0x00 0xbf 0x00 0xfb 0x00 0xf3 0x00 0xe3 0x00 0x43 : ibm-5-0 - 0xe5 0x05 0xe2 0x00 0x85 0x07 0x81 0x01 0x80 0x02 0x80 0x07 0xe5 0x05 0xe7 + 0xe5 0x05 0xe2 0x00 0x85 0x07 0x81 0x01 0x80 0x02 0x80 0x02 0xe6 0x02 0xe7 diff --git a/bin/2-ibm-logo.ch8 b/bin/2-ibm-logo.ch8 index 6835560..d60dac8 100644 Binary files a/bin/2-ibm-logo.ch8 and b/bin/2-ibm-logo.ch8 differ diff --git a/bin/3-corax+.8o b/bin/3-corax+.8o index ca0608d..6d23690 100644 --- a/bin/3-corax+.8o +++ b/bin/3-corax+.8o @@ -416,5 +416,5 @@ : version-0-0 0x0a 0xae 0xa2 0x42 : version-1-0 - 0x38 0x28 0x28 0xb8 + 0x10 0x30 0x10 0xb8 diff --git a/bin/3-corax+.ch8 b/bin/3-corax+.ch8 index 60ae95b..317029b 100644 Binary files a/bin/3-corax+.ch8 and b/bin/3-corax+.ch8 differ diff --git a/bin/4-flags.8o b/bin/4-flags.8o index ad44936..c496d7a 100644 --- a/bin/4-flags.8o +++ b/bin/4-flags.8o @@ -187,15 +187,28 @@ # Subtraction in one direction (no carry) opcode-digit 0x5 # 0x85 + # Edge cases: + # Check that vF -= vX results in no carry vF := 20 vF -= 15_in_a_register # 5 (0x05), but should be overwritten by flag, so 1 v4 := vF + # Check that vX -= vF results in the correct result and no carry v3 := 20 vF := 15 v3 -= vF # 5 (0x05) + # Check that N - N (for the same N) does not result in carry + v5 := 10 + vF := 10 + v5 -= vF + v5 := vF + # Base case: check that subtracting two regular registers results in the + # correct value and no carry set, and that the carry register actually gets + # overwritten. vF := 0xAA v2 := 50 v2 -= 15_in_a_register # 35 (0x23) + # Check all our assertions + if v5 != 1 then vF := 2 expect-v2-vf-v3-v4 35 1 5 1 y += 5 @@ -214,16 +227,29 @@ # Subtraction in the other direction (no carry) opcode-digit 0x7 # 0x87 + # Edge cases: + # Check that vF =- vX results in no carry vF := 10 vF =- 15_in_a_register # 5 (0x5), but should be overwritten by flag, so 1 v4 := vF + # Check that vX =- vF results in the correct result and no carry v3 := 15 vF := 20 v3 =- vF # 5 (0x05) + # Check that N - N (for the same N) does not result in carry + v5 := 10 + vF := 10 + v5 =- vF + v5 := vF + # Base case: check that subtracting two regular registers results in the + # correct value and no carry set, and taht the carry register actually gets + # overwritten. vF := 0xAA v2 := 15 v1 := 50 v2 =- v1 # 35 (0x23) + # Check all our assertions + if v5 != 1 then vF := 2 expect-v2-vf-v3-v4 35 1 5 1 x += 1 @@ -528,5 +554,5 @@ : version-0-0 0x0a 0xae 0xa2 0x42 : version-1-0 - 0x38 0x28 0x28 0xb8 + 0x10 0x30 0x10 0xb8 diff --git a/bin/4-flags.ch8 b/bin/4-flags.ch8 index 97a73c6..431f3b0 100644 Binary files a/bin/4-flags.ch8 and b/bin/4-flags.ch8 differ diff --git a/bin/5-quirks.8o b/bin/5-quirks.8o index f3cf962..5d9eade 100644 --- a/bin/5-quirks.8o +++ b/bin/5-quirks.8o @@ -158,26 +158,52 @@ return -:macro show X address { - v0 := X - i := address - sprite v0 v1 15 -} +:const OFF 0 +:const ON 1 +:const BOTH 2 +:const NONE 3 +:const LORES 4 +:const HIRES 5 +:const ERR1 6 +:const ERR2 7 +:const ERR3 8 + +:const CHIP8 1 +:const SCHIP_MODERN 2 +:const XOCHIP 3 +:const SCHIP_LEGACY 4 : main clear i := 0x1FF load v0 - if v0 == 1 then jump quirks-chip8 - if v0 == 2 then jump quirks-schip - if v0 == 3 then jump quirks-xochip + if v0 == CHIP8 then jump quirks-chip8 + if v0 == SCHIP_MODERN then jump quirks-schip + if v0 == SCHIP_LEGACY then jump quirks-schip-legacy + if v0 == XOCHIP then jump quirks-xochip text 6 2 quirks-choose - text 16 10 quirks-str-chip8 - text 16 15 quirks-str-schip - text 16 20 quirks-str-xochip + text 10 10 quirks-str-chip8 + text 10 15 quirks-str-schip + text 10 20 quirks-str-xochip + render-version + + :unpack 0xA quirks-menu + v2 := 2 + jump menu-start + +: superchip-menu + clear + text 10 2 quirks-choose-superchip + text 18 12 quirks-str-modern + text 18 17 quirks-str-legacy + render-version - # Show version number in bottom right corner + :unpack 0xA superchip-target-menu + v2 := 1 + jump menu-start + +: render-version x := 50 y := 27 i := version-0-0 @@ -185,81 +211,94 @@ x := 58 i := version-1-0 sprite x y 4 + return - :unpack 0xA quirks-menu - v2 := 2 - jump menu-start : quirks-chip8 i := scratchpad - v0 := 1 + v0 := CHIP8 save v0 jump quirks-run-tests : quirks-schip i := scratchpad - v0 := 2 + v0 := SCHIP_MODERN + save v0 + jump quirks-run-tests + +: quirks-schip-legacy + i := scratchpad + v0 := SCHIP_LEGACY save v0 jump quirks-run-tests : quirks-xochip i := scratchpad - v0 := 3 + v0 := XOCHIP save v0 : quirks-run-tests waitKeyRelease + clear # Determine frames per second for dispQuirk - clear + i := scratchpad + load v0 + if v0 == CHIP8 begin + get-frames-per-second-chip8 - v1 := 0 - show 8 splash-0-0 - show 16 splash-1-0 - show 24 splash-2-0 - show 32 splash-3-0 - show 40 splash-4-0 - show 48 splash-5-0 - - v1 := 15 - show 8 splash-0-1 - show 16 splash-1-1 - show 24 splash-2-1 - show 32 splash-3-1 - show 40 splash-4-1 - show 48 splash-5-1 + # We expect the inner loop with 30 `sprite`s to have been able to run six + # times in the timespan of 180 interrupts + v0 := ON + if v3 != 0 then v0 := OFF + if v2 > 6 then v0 := OFF + if v2 < 6 then v0 := ERR1 + i := scratchpad-plus-1 + save v0 + else # Superchip & XO-CHIP + get-frames-per-second-schip-xochip - i := quirks-values - load v6 - i := quirks-image - delay := v5 - loop - v5 := 30 - loop - sprite v0 v1 1 - if vF == 0 begin - v0 := 54 - v6 := delay - v6 >>= v6 - v6 >>= v6 - v0 -= v6 - end - v5 -= 1 - if v5 != 0 then - again - v2 += v4 - v3 += vF - vE := delay - if vE != 0 then again + # We expect the inner loop with 30 `sprite`s to have been able to run 3 + # times in the timespan of 90 interrupts + v0 := ON + if v3 != 0 then v0 := OFF + if v2 > 3 then v0 := OFF + if v2 < 3 then v0 := ERR1 + i := scratchpad-plus-1 + save v0 - # We expect the inner loop with 30 `sprite`s to have been able to run six - # times in the timespan of 180 interrupts - v0 := 1 - if v3 != 0 then v0 := 0 - if v2 > 6 then v0 := 0 - if v2 < 6 then v0 := 2 - i := scratchpad-plus-1 - save v0 + # Do the test again, but now in hires mode + hires + clear + get-frames-per-second-hires + + # We expect the inner loop with 30 `sprite`s to have been able to run 3 + # times in the timespan of 90 interrupts + v1 := ON + if v3 != 0 then v1 := OFF + if v2 > 3 then v1 := OFF + if v2 < 3 then v1 := ERR1 + i := scratchpad-plus-1 + load v0 + if v0 == ERR1 then jump quirks-combined-vblank-skip + if v1 == ERR1 begin + v0 := ERR1 + jump quirks-combined-vblank-skip + end + if v0 == OFF begin + if v1 == OFF then v0 := NONE + if v1 == ON then v0 := HIRES + end + if v0 == ON begin + if v1 == OFF then v0 := LORES + if v1 == ON then v0 := BOTH + end +: quirks-combined-vblank-skip + i := scratchpad-plus-1 + save v0 + + lores + end # Determine if sprites get clipped vertically clear @@ -309,17 +348,116 @@ v9 += vF # Save result - v0 := 0 + v0 := OFF # Clipping - if v5 == 0 then v0 := 1 - if v5 != v6 then v0 := 2 - if v5 != v7 then v0 := 2 - if v5 != v8 then v0 := 2 + if v5 == 0 then v0 := ON + if v5 != v6 then v0 := ERR1 + if v5 != v7 then v0 := ERR1 + if v5 != v8 then v0 := ERR1 # Wrapping - if v9 != 4 then v0 := 3 + if v9 != 4 then v0 := ERR2 i := scratchpad-plus-2 save v0 + # If SuperCHIP or XO-CHIP, do it again in hires + i := scratchpad + load v0 + if v0 != CHIP8 begin + hires + # Determine if sprites get clipped vertically + clear + i := cursor + v0 := 60 + v1 := 61 + sprite v0 v1 6 + v0 := 54 + v1 := 2 + sprite v0 v1 2 + v5 := vF + v0 := 66 + sprite v0 v1 2 + v6 := vF + + # Determine if sprites get clipped horizontally + clear + i := cursor + v0 := 125 + v1 := 5 + sprite v0 v1 6 + v0 := 3 + v1 := 4 + sprite v0 v1 2 + v7 := vF + v1 := 10 + sprite v0 v1 2 + v8 := vF + + # Determine if sprites get wrapped (both directions) + clear + v0 := 174 + v1 := 82 + sprite v0 v1 6 # Should draw at 46,18 + v0 := 40 + v1 := 17 + sprite v0 v1 2 + v9 := vF + v0 := 52 + sprite v0 v1 2 + v9 += vF + v1 := 23 + sprite v0 v1 2 + v9 += vF + v0 := 40 + sprite v0 v1 2 + v9 += vF + + # Determine result + v1 := OFF + # Clipping + if v5 == 0 then v1 := ON + if v5 != v6 then v1 := ERR1 + if v5 != v7 then v1 := ERR1 + if v5 != v8 then v1 := ERR1 + # Wrapping + if v9 != 4 then v1 := ERR2 + + # Compare with previous result + i := scratchpad-plus-2 + load v0 + + if v0 == ERR1 begin + if v1 == ERR1 then jump quirks-combined-wrapping-skip + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + if v0 == ERR2 begin + if v1 == ERR2 then jump quirks-combined-wrapping-skip + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + if v1 == ERR1 begin + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + if v1 == ERR2 begin + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + + if v0 == ON begin + if v1 == ON then v0 := BOTH + if v1 == OFF then v0 := LORES + end + if v0 == OFF begin + if v1 == ON then v0 := HIRES + if v1 == OFF then v0 := NONE + end +: quirks-combined-wrapping-skip + i := scratchpad-plus-2 + save v0 + lores + end + # Present results clear @@ -342,8 +480,7 @@ i := scratchpad load v0 i := flag-err - if v0 == 1 begin - # Selected CHIP-8 + if v0 == CHIP8 begin if v5 == 1 then i := flag-ok else # Selected SCHIP or XO-CHIP @@ -356,7 +493,7 @@ if v5 == 1 then i := quirks-on if v5 != v6 then i := quirks-inconsistent-1 if v5 != v7 then i := quirks-inconsistent-2 - vD := 44 + vD := 42 vE := 1 drawText @@ -371,11 +508,16 @@ i := scratchpad load v0 i := flag-err - if v0 == 2 begin - # Selected SCHIP + if v0 == CHIP8 begin + if v5 != 5 then i := flag-ok + end + if v0 == SCHIP_MODERN begin if v5 == 5 then i := flag-ok - else - # Selected CHIP-8 or XO-CHIP + end + if v0 == SCHIP_LEGACY begin + if v5 == 5 then i := flag-ok + end + if v0 == XOCHIP begin if v5 != 5 then i := flag-ok end x := 59 @@ -383,7 +525,7 @@ sprite x y 3 i := quirks-on if v5 == 5 then i := quirks-off - vD := 44 + vD := 42 vE := 6 drawText @@ -393,20 +535,29 @@ i := scratchpad load v1 i := flag-err - if v0 == 1 begin - # Selected CHIP-8 - if v1 == 1 then i := flag-ok - else - # Selected SCHIP or XO-CHIP - if v1 == 0 then i := flag-ok + if v0 == CHIP8 begin + if v1 == ON then i := flag-ok + end + if v0 == SCHIP_MODERN begin + if v1 == NONE then i := flag-ok + end + if v0 == SCHIP_LEGACY begin + if v1 == LORES then i := flag-ok + end + if v0 == XOCHIP begin + if v1 == NONE then i := flag-ok end x := 59 y := 12 sprite x y 3 i := quirks-off - if v1 == 1 then i := quirks-on - if v1 == 2 then i := quirks-low - vD := 44 + if v1 == ON then i := quirks-on + if v1 == ERR1 then i := quirks-slow + if v1 == LORES then i := quirks-lores + if v1 == HIRES then i := quirks-hires + if v1 == BOTH then i := quirks-both + if v1 == NONE then i := quirks-none + vD := 42 vE := 11 drawText @@ -416,21 +567,31 @@ i := scratchpad load v2 i := flag-err - if v0 == 3 begin - # Selected XO-CHIP - if v2 == 0 then i := flag-ok - else - # Selected CHIP-8 or SCHIP - if v2 == 1 then i := flag-ok + if v0 == CHIP8 begin + if v2 == ON then i := flag-ok + end + if v0 == SCHIP_MODERN begin + if v2 == BOTH then i := flag-ok + end + if v0 == SCHIP_LEGACY begin + if v2 == BOTH then i := flag-ok + end + if v0 == XOCHIP begin + if v2 == NONE then i := flag-ok end x := 59 y := 17 sprite x y 3 i := quirks-off - if v2 == 1 then i := quirks-on - if v2 == 2 then i := quirks-inconsistent-1 - if v2 == 3 then i := quirks-inconsistent-2 - vD := 44 + if v2 == ON then i := quirks-on + if v2 == LORES then i := quirks-lores + if v2 == HIRES then i := quirks-hires + if v2 == BOTH then i := quirks-both + if v2 == NONE then i := quirks-none + if v2 == ERR1 then i := quirks-inconsistent-1 + if v2 == ERR2 then i := quirks-inconsistent-2 + if v2 == ERR3 then i := quirks-inconsistent-3 + vD := 42 vE := 16 drawText @@ -447,11 +608,16 @@ i := scratchpad load v0 i := flag-err - if v0 == 2 begin - # Selected SCHIP + if v0 == CHIP8 begin + if v5 != 0 then i := flag-ok + end + if v0 == SCHIP_MODERN begin if v5 == 0 then i := flag-ok - else - # Selected CHIP-8 or XO-CHIP + end + if v0 == SCHIP_LEGACY begin + if v5 == 0 then i := flag-ok + end + if v0 == XOCHIP begin if v5 != 0 then i := flag-ok end x := 59 @@ -460,7 +626,7 @@ i := quirks-off if v5 == 0 then i := quirks-on if v5 != v7 then i := quirks-inconsistent-1 - vD := 44 + vD := 42 vE := 21 drawText @@ -475,11 +641,16 @@ i := scratchpad load v0 i := flag-err - if v0 == 2 begin - # Selected SCHIP + if v0 == CHIP8 begin + if v5 == 0 then i := flag-ok + end + if v0 == SCHIP_MODERN begin if v5 != 0 then i := flag-ok - else - # Selected CHIP-8 or XO-CHIP + end + if v0 == SCHIP_LEGACY begin + if v5 != 0 then i := flag-ok + end + if v0 == XOCHIP begin if v5 == 0 then i := flag-ok end x := 59 @@ -487,7 +658,7 @@ sprite x y 3 i := quirks-off if v5 == 1 then i := quirks-on - vD := 44 + vD := 42 vE := 26 drawText @@ -495,6 +666,152 @@ waitKeyRelease jump main + +: get-frames-per-second-chip8 + quirks-show-splash-lores + i := quirks-values + load v6 + # v0 := 10 + # v1 := 31 + # v2 := 0 + # v3 := 0 + # v4 := 1 + # v5 := 180 + # v6 := 0 + i := quirks-image + delay := v5 + loop + v5 := 30 + loop + sprite v0 v1 1 + if vF == 0 begin + v0 := 54 + v6 := delay + v6 >>= v6 + v6 >>= v6 + v0 -= v6 + end + v5 -= 1 + if v5 != 0 then + again + v2 += v4 + v3 += vF + vE := delay + if vE != 0 then again + return + +: get-frames-per-second-schip-xochip + quirks-show-splash-lores + i := quirks-values + load v6 + # v0 := 10 + # v1 := 31 + # v2 := 0 + # v3 := 0 + # v4 := 1 + # v5 := 180 + # v6 := 0 + v5 >>= v5 + i := quirks-image + delay := v5 + loop + v5 := 30 + loop + sprite v0 v1 1 + if vF == 0 begin + v0 := 32 + v6 := delay + v6 >>= v6 + v6 >>= v6 + v0 -= v6 + end + v5 -= 1 + if v5 != 0 then + again + v2 += v4 + v3 += vF + vE := delay + if vE != 0 then again + return + +: quirks-show-splash-lores + v0 := 8 + v1 := 0 + v2 := 15 + i := splash-0-0 + loop + sprite v0 v1 15 + i += v2 + v0 += 8 + if v0 == 56 begin + v0 := 8 + v1 += 15 + end + if v1 != 30 then + again + return + +: get-frames-per-second-hires + v0 := 16 + v1 := 0 + v2 := 32 + i := splash2x-0-0 + loop + sprite v0 v1 0 + i += v2 + v0 += 16 + if v0 == 112 begin + v0 := 16 + v1 += 16 + end + if v1 != 64 then + again + + # 11th pixel + i := quirks-values + load v6 + # v0 := 10 + # v1 := 31 + # v2 := 0 + # v3 := 0 + # v4 := 1 + # v5 := 180 + # v6 := 0 + v0 += v0 + v1 += v1 + v5 >>= v5 + i := quirks-line + loop + sprite v0 v1 2 + v0 += 8 + if v0 != 60 then + again + i := quirks-image + loop + sprite v0 v1 2 + v0 += 1 + if v0 != 64 then + again + delay := v5 + loop + v5 := 15 + loop + sprite v0 v1 2 + if vF == 0 begin + v0 := 108 + v6 := delay + v6 >>= v6 + v0 -= v6 + end + v5 -= 1 + if v5 != 0 then + again + v2 += v4 + v3 += vF + vE := delay + if vE != 0 then again + return + : scratchpad 0 : scratchpad-plus-1 @@ -680,19 +997,31 @@ : quirks-menu - 12 11 :pointer quirks-chip8 - 12 16 :pointer quirks-schip - 12 21 :pointer quirks-xochip + 6 11 :pointer quirks-chip8 + 6 16 :pointer superchip-menu + 6 21 :pointer quirks-xochip : quirks-choose str "PICK PLATFORM" 0 : quirks-str-chip8 str "1 CHIP-8" 0 : quirks-str-schip - str "2 SCHIP" 0 + str "2 SUPER-CHIP" 0 : quirks-str-xochip str "3 XO-CHIP" 0 +: superchip-target-menu + 14 13 :pointer quirks-schip + 14 18 :pointer quirks-schip-legacy + +: quirks-choose-superchip + str "PICK TARGET" 0 +: quirks-str-modern + str "1 MODERN" 0 +: quirks-str-legacy + str "2 LEGACY" 0 + + : quirks-vf str "VF RESET" 0 : quirks-mem @@ -710,17 +1039,22 @@ str "ON" 0 : quirks-off str "OFF" 0 +: quirks-slow + str "SLOW" 0 +: quirks-lores + str "LRES" 0 +: quirks-hires + str "HRES" 0 +: quirks-both + str "BOTH" 0 +: quirks-none + str "NONE" 0 : quirks-inconsistent-1 - str "E1" 0 + str "ERR1" 0 : quirks-inconsistent-2 - str "E2" 0 -: quirks-low - str "LOW" 0 - -: quirks-values - 10 31 0 0 1 180 0 -: quirks-image - 0b10000000 + str "ERR2" 0 +: quirks-inconsistent-3 + str "ERR3" 0 : cursor 0b11111110 @@ -730,10 +1064,20 @@ 0b11111110 0b11111110 + +: quirks-values + 10 31 0 0 1 180 0 +: quirks-image + 0b10000000 + 0b10000000 +: quirks-line + 0b11111111 + 0b11111111 + : version-0-0 0x0a 0xae 0xa2 0x42 : version-1-0 - 0x38 0x28 0x28 0xb8 + 0x10 0x30 0x10 0xb8 : splash-0-0 0x0f 0x02 0x02 0x02 0x02 0x02 0x00 0x00 0x1f 0x3f 0x71 0xe0 0xe5 0xe0 0xe8 @@ -756,10 +1100,83 @@ : splash-3-1 0xce 0xfc 0xf8 0xc0 0xd4 0xdc 0xc4 0xc5 0x00 0x00 0x30 0x44 0x24 0x14 0x63 : splash-4-1 - 0xf1 0x03 0x07 0x07 0x77 0x57 0x53 0x71 0x00 0x00 0x28 0x8e 0xa8 0xa8 0xa6 + 0xf1 0x03 0x07 0x07 0x27 0x67 0x23 0x71 0x00 0x00 0x28 0x8e 0xa8 0xa8 0xa6 : splash-5-1 0xce 0x87 0x03 0x03 0x03 0x87 0xfe 0xfc 0x00 0x00 0x60 0x90 0xf0 0x80 0x70 +: splash2x-0-0 + 0x00 0xff 0x00 0xff 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x0c + 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-2-0 + 0xcc 0x00 0xcc 0x00 0x00 0xf3 0x00 0xf3 0x0c 0xcc 0x0c 0xcc 0x0c 0xc0 0x0c 0xc0 + 0x0c 0xc0 0x0c 0xc0 0x0c 0xc0 0x0c 0xc0 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-4-0 + 0x00 0x00 0x00 0x00 0x03 0xc3 0x03 0xc3 0xcc 0x33 0xcc 0x33 0xcf 0xf3 0xcf 0xf3 + 0xcc 0x03 0xcc 0x03 0xc3 0xf3 0xc3 0xf3 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-6-0 + 0x00 0x03 0x00 0x03 0xf0 0x3f 0xf0 0x3f 0x0c 0xc3 0x0c 0xc3 0x0c 0xc3 0x0c 0xc3 + 0x0c 0xc3 0x0c 0xc3 0x0c 0x3f 0x0c 0x3f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-8-0 + 0x00 0x00 0x00 0x00 0x30 0xc3 0x30 0xc3 0x30 0xcc 0x30 0xcc 0x30 0xc3 0x30 0xc3 + 0x30 0xc0 0x30 0xc0 0x0f 0xcf 0x0f 0xcf 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-10-0 + 0x0f 0x00 0x0f 0x00 0xc3 0x00 0xc3 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0xc0 0x00 0xc0 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-0-1 + 0x03 0xff 0x03 0xff 0x0f 0xff 0x0f 0xff 0x3f 0x03 0x3f 0x03 0xfc 0x00 0xfc 0x00 + 0xfc 0x33 0xfc 0x33 0xfc 0x00 0xfc 0x00 0xfc 0xc0 0xfc 0xc0 0xfc 0x3f 0xfc 0x3f +: splash2x-2-1 + 0x03 0xc0 0x03 0xc0 0xcf 0xc0 0xcf 0xc0 0xcf 0xc0 0xcf 0xc0 0x0f 0xc0 0x0f 0xc0 + 0x0f 0xc0 0x0f 0xc0 0x0f 0xff 0x0f 0xff 0xcf 0xff 0xcf 0xff 0x0f 0xc3 0x0f 0xc3 +: splash2x-4-1 + 0x00 0xf0 0x00 0xf0 0x03 0xf3 0x03 0xf3 0x03 0xf3 0x03 0xf3 0x00 0x03 0x00 0x03 + 0x00 0xf3 0x00 0xf3 0x03 0xf3 0x03 0xf3 0xc3 0xf3 0xc3 0xf3 0xf3 0xf3 0xf3 0xf3 +: splash2x-6-1 + 0xff 0xc0 0xff 0xc0 0xff 0xf0 0xff 0xf0 0xf0 0xfc 0xf0 0xfc 0xf0 0x3c 0xf0 0x3c + 0xf0 0x3c 0xf0 0x3c 0xf0 0x3c 0xf0 0x3c 0xf0 0x3c 0xf0 0x3c 0xf0 0xfc 0xf0 0xfc +: splash2x-8-1 + 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x03 0x00 0x0f 0x00 0x0f 0x00 0x0f 0x00 0x0f + 0x00 0x0f 0x00 0x0f 0x00 0x03 0x00 0x03 0xff 0x00 0xff 0x00 0xff 0x03 0xff 0x03 +: splash2x-10-1 + 0xff 0xfc 0xff 0xfc 0xf0 0x3f 0xf0 0x3f 0xc0 0x0f 0xc0 0x0f 0xc0 0x0f 0xc0 0x0f + 0xc0 0x0f 0xc0 0x0f 0xf0 0x3c 0xf0 0x3c 0xff 0xf0 0xff 0xf0 0xf0 0xfc 0xf0 0xfc +: splash2x-0-2 + 0xfc 0x00 0xfc 0x00 0xfc 0x00 0xfc 0x00 0xfc 0x00 0xfc 0x00 0xfc 0x00 0xfc 0x00 + 0x3f 0x03 0x3f 0x03 0x0f 0xff 0x0f 0xff 0x03 0xff 0x03 0xff 0x00 0x00 0x00 0x00 +: splash2x-2-2 + 0x0f 0xc0 0x0f 0xc0 0x0f 0xc0 0x0f 0xc0 0x0f 0xc0 0x0f 0xc0 0x0f 0xc0 0x0f 0xc0 + 0xcf 0xc0 0xcf 0xc0 0xcf 0xc0 0xcf 0xc0 0x0f 0xc0 0x0f 0xc0 0x00 0x00 0x00 0x00 +: splash2x-4-2 + 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 + 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0xf3 0x00 0x00 0x00 0x00 +: splash2x-6-2 + 0xff 0xf0 0xff 0xf0 0xff 0xc0 0xff 0xc0 0xf0 0x00 0xf0 0x00 0xf3 0x30 0xf3 0x30 + 0xf3 0xf0 0xf3 0xf0 0xf0 0x30 0xf0 0x30 0xf0 0x33 0xf0 0x33 0x00 0x00 0x00 0x00 +: splash2x-8-2 + 0x00 0x0f 0x00 0x0f 0x00 0x3f 0x00 0x3f 0x00 0x3f 0x00 0x3f 0x0c 0x3f 0x0c 0x3f + 0x3c 0x3f 0x3c 0x3f 0x0c 0x0f 0x0c 0x0f 0x3f 0x03 0x3f 0x03 0x00 0x00 0x00 0x00 +: splash2x-10-2 + 0xc0 0x3f 0xc0 0x3f 0x00 0x0f 0x00 0x0f 0x00 0x0f 0x00 0x0f 0x00 0x0f 0x00 0x0f + 0xc0 0x3f 0xc0 0x3f 0xff 0xfc 0xff 0xfc 0xff 0xf0 0xff 0xf0 0x00 0x00 0x00 0x00 +: splash2x-0-3 + 0x00 0x00 0x00 0x00 0x00 0x3f 0x00 0x3f 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x0c + 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x0c 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-2-3 + 0x00 0x00 0x00 0x00 0x0f 0x03 0x0f 0x03 0x30 0xcc 0x30 0xcc 0x3f 0xc3 0x3f 0xc3 + 0x30 0x00 0x30 0x00 0x0f 0xcf 0x0f 0xcf 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-4-3 + 0x00 0x00 0x00 0x00 0xcc 0x00 0xcc 0x00 0x0f 0xc0 0x0f 0xc0 0x0c 0x00 0x0c 0x00 + 0xcc 0x00 0xcc 0x00 0x03 0xc0 0x03 0xc0 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-6-3 + 0x00 0x00 0x00 0x00 0x0f 0x00 0x0f 0x00 0x30 0x30 0x30 0x30 0x0c 0x30 0x0c 0x30 + 0x03 0x30 0x03 0x30 0x3c 0x0f 0x3c 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-8-3 + 0x00 0x00 0x00 0x00 0x0c 0xc0 0x0c 0xc0 0xc0 0xfc 0xc0 0xfc 0xcc 0xc0 0xcc 0xc0 + 0xcc 0xc0 0xcc 0xc0 0xcc 0x3c 0xcc 0x3c 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +: splash2x-10-3 + 0x00 0x00 0x00 0x00 0x3c 0x00 0x3c 0x00 0xc3 0x00 0xc3 0x00 0xff 0x00 0xff 0x00 + 0xc0 0x00 0xc0 0x00 0x3f 0x00 0x3f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + # Jump quirk targets: :org 0xE98 diff --git a/bin/5-quirks.ch8 b/bin/5-quirks.ch8 index c4bb488..5206652 100644 Binary files a/bin/5-quirks.ch8 and b/bin/5-quirks.ch8 differ diff --git a/bin/6-keypad.8o b/bin/6-keypad.8o index cc70a49..daaea0f 100644 --- a/bin/6-keypad.8o +++ b/bin/6-keypad.8o @@ -517,5 +517,5 @@ : version-0-0 0x0a 0xae 0xa2 0x42 : version-1-0 - 0x38 0x28 0x28 0xb8 + 0x10 0x30 0x10 0xb8 diff --git a/bin/6-keypad.ch8 b/bin/6-keypad.ch8 index db148da..88250bf 100644 Binary files a/bin/6-keypad.ch8 and b/bin/6-keypad.ch8 differ diff --git a/bin/7-beep.8o b/bin/7-beep.8o new file mode 100644 index 0000000..87badd4 --- /dev/null +++ b/bin/7-beep.8o @@ -0,0 +1,72 @@ +# Beep test + +# This is a new test for this test suite, that allows you to test if your buzzer +# is working. It will beep SOS in morse code and flash a speaker icon on the +# display in the same pattern. If you press the CHIP-8 B button it will give you +# manual control over the buzzer. + +: main + i := pattern + load v0 + v2 := 1 + v3 := v0 + v4 := 0xB + vA := 28 + vB := 12 + loop + i := pattern + i += v2 + load v1 + i := speaker + sprite vA vB 7 + buzzer := v0 + delay := v0 + wait-for-delay + sprite vA vB 7 + delay := v1 + wait-for-delay + v2 += 2 + if v2 != v3 then + again + jump main + +: wait-for-delay + if v4 key then jump manual-control + v0 := delay + if v0 != 0 then jump wait-for-delay + return + +: manual-control + v0 := 0 + v1 := 60 + v2 := 0xB + clear + loop + i := speaker + sprite vA vB 7 + loop + buzzer := v1 + if v2 key then + again + sprite vA vB 7 + loop + buzzer := v0 + if v2 -key then + again + again + +: pattern + 19 + 10 5 10 5 10 20 + 30 5 30 5 30 20 + 10 5 10 5 10 60 + +: speaker + 0b00011001 + 0b00101010 + 0b11001000 + 0b10001011 + 0b11001000 + 0b00101010 + 0b00011001 + diff --git a/bin/7-beep.ch8 b/bin/7-beep.ch8 new file mode 100644 index 0000000..27c205f Binary files /dev/null and b/bin/7-beep.ch8 differ diff --git a/bin/8-scrolling.8o b/bin/8-scrolling.8o new file mode 100644 index 0000000..bcb4aae --- /dev/null +++ b/bin/8-scrolling.8o @@ -0,0 +1,830 @@ +# Scrolling test + +# This is a new test for this test suite, that allows you to test if your +# scrolling opcodes do the right thing. All the arrows should land in their +# boxes in the direction they point in. + + + +:stringmode str "$0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -." { + :byte { 4 * VALUE } +} + +:macro text X Y STR { + vD := X + vE := Y + i := STR + drawText +} + +:alias x vA +:alias y vB + +: waitKeyRelease + v0 := 0 +: - + if v0 key then jump - + v0 += 1 + if v0 == 16 then return + jump - + +# A cute little menu to select a test + +# Input: +# * v0 v1 point to 0xA + menu struct +# * v2 holds the length (zero-indexed) +: menu-start + :alias cursorX v0 + :alias cursorY v1 + :alias numItems v2 + :alias showing v3 + :alias temp v4 + :alias currentItem v5 + :alias selectedItem v6 + + currentItem := 0 + i := menu-draw-cursor + save v1 + i := menu-choose-load + save v1 + jump menu-draw-cursor +: menu-move-cursor + if showing == 1 then sprite cursorX cursorY 2 # i should still be correct + waitKeyRelease +: menu-draw-cursor + 0 0 # i := + i += currentItem + i += currentItem + i += currentItem + i += currentItem + load cursorY # and cursorX + showing := 0 + delay := showing + + loop + # Blink cursor + temp := delay + if temp == 0 begin + i := menu-cursor + sprite cursorX cursorY 2 + temp := 10 + delay := temp + temp := 1 + showing ^= temp + end + + # Move cursor up + temp := 0xE + if temp key begin + if currentItem != 0 begin + currentItem -= 1 + jump menu-move-cursor + end + end + # Move cursor down + temp := 0xF + if temp key begin + if currentItem != numItems begin + currentItem += 1 + jump menu-move-cursor + end + end + # Start test under cursor + selectedItem := currentItem + temp := 0xA + if temp key then jump menu-choose + + # Use numbers to jump to tests directly + temp := 0 + numItems += 1 + loop + temp += 1 + if temp key begin + selectedItem := temp + selectedItem -= 1 + jump menu-choose + end + if temp != numItems then again + numItems -= 1 + again + +: menu-choose + waitKeyRelease +: menu-choose-load + 0 0 # i := + i += selectedItem + i += selectedItem + i += selectedItem + i += selectedItem + temp := 2 + i += temp + load v1 + temp := 0x10 + v0 |= temp + i := menu-choose-jump + save v1 +: menu-choose-jump + 0 0 # jump + +# Font rendering code and character data +# Kept this very simplistic and fast + +:macro drawCharacter REG { + if REG == 0 then return + v0 := REG + drawChar +} + +: drawText + load vC + drawChar + drawCharacter v1 + drawCharacter v2 + drawCharacter v3 + drawCharacter v4 + drawCharacter v5 + drawCharacter v6 + drawCharacter v7 + drawCharacter v8 + drawCharacter v9 + drawCharacter vA + drawCharacter vB + drawCharacter vC + return + +: drawChar + i := characters + i += v0 + sprite vD vE 4 + vD += 4 + return + + +: main + # Jump to the right test based on magic values + i := 0x1FF + load v0 + if v0 == 1 then jump scrolling-superchip-lores-full-pixels + if v0 == 2 then jump scrolling-superchip-lores-half-pixels + if v0 == 3 then jump scrolling-superchip-hires + if v0 == 4 then jump scrolling-xochip-lores + if v0 == 5 then jump scrolling-xochip-hires + + + +: menu-1 + lores + clear + text 6 2 scrolling-choose-1 + text 12 12 scrolling-str-superchip + text 12 17 scrolling-str-xochip + render-version + :unpack 0xA scrolling-menu-1 + v2 := 1 + jump menu-start + +: menu-2 + lores + clear + text 7 2 scrolling-choose-2 + text 15 12 scrolling-str-lores + text 15 17 scrolling-str-hires + render-version + :unpack 0xA scrolling-menu-2 + v2 := 1 + jump menu-start + +: menu-3 + lores + clear + text 10 2 scrolling-choose-3 + text 18 12 scrolling-str-modern + text 18 17 scrolling-str-legacy + render-version + :unpack 0xA scrolling-menu-3 + v2 := 1 + jump menu-start + +: render-version + x := 50 + y := 27 + i := version-0-0 + sprite x y 4 + x := 58 + i := version-1-0 + sprite x y 4 + return + + + +: scrolling-superchip + i := selection + v0 := 0 + save v0 + jump menu-2 + +: scrolling-xochip + i := selection + v0 := 1 + save v0 + jump menu-2 + +: scrolling-lores + i := selection + load v0 + if v0 == 0 then jump menu-3 + jump scrolling-xochip-lores + +: scrolling-hires + i := selection + load v0 + if v0 == 0 then jump scrolling-superchip-hires + jump scrolling-xochip-hires + + + +: scrolling-superchip-lores-full-pixels + clear + + i := scrolling-arrow-left + v0 := 46 + v1 := 11 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 10 + v1 := 11 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-down + v0 := 28 + v1 := 0 + sprite v0 v1 8 + scroll-down 6 + + jump scrolling-draw-borders-lores + +: scrolling-superchip-lores-half-pixels + clear + + i := scrolling-arrow-left + v0 := 46 + v1 := 11 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 10 + v1 := 11 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-down + v0 := 28 + v1 := 0 + sprite v0 v1 8 + scroll-down 12 + +: scrolling-draw-borders-lores + i := scrolling-pixel + v0 := 21 # X + v1 := 33 + v2 := 27 + v3 := 26 # Y + v4 := 15 + v5 := 4 + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v0 v4 1 + sprite v1 v4 1 + sprite v2 v5 1 + v0 += 1 + v1 += 1 + v2 += 1 + if v0 != 31 then + again + v0 := 20 # X + v1 := 31 + v2 := 32 + v3 := 43 + v4 := 26 + v5 := 37 + v6 := 16 # Y + v7 := 5 + loop + sprite v0 v6 1 + sprite v1 v6 1 + sprite v2 v6 1 + sprite v3 v6 1 + sprite v4 v7 1 + sprite v5 v7 1 + v6 += 1 + v7 += 1 + if v6 != 26 then + again + + v0 := key + waitKeyRelease + jump main + + +: scrolling-superchip-hires + clear + hires + + i := scrolling-arrow-left + v0 := 78 + v1 := 22 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 42 + v1 := 22 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-down + v0 := 60 + v1 := 11 + sprite v0 v1 8 + scroll-down 12 + + i := scrolling-pixel + v0 := 53 # X + v1 := 65 + v2 := 59 + v3 := 43 # Y + v4 := 32 + v5 := 21 + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v0 v4 1 + sprite v1 v4 1 + sprite v2 v5 1 + v0 += 1 + v1 += 1 + v2 += 1 + if v0 != 63 then + again + v0 := 52 # X + v1 := 63 + v2 := 64 + v3 := 75 + v4 := 58 + v5 := 69 + v6 := 33 # Y + v7 := 22 + loop + sprite v0 v6 1 + sprite v1 v6 1 + sprite v2 v6 1 + sprite v3 v6 1 + sprite v4 v7 1 + sprite v5 v7 1 + v6 += 1 + v7 += 1 + if v7 != 32 then + again + + v0 := key + waitKeyRelease + jump main + + + +: scrolling-xochip-lores + clear + + i := scrolling-arrow-left + v0 := 45 + v1 := 23 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 10 + v1 := 12 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-up + v0 := 22 + v1 := 23 + sprite v0 v1 8 + scroll-up 12 + + i := scrolling-arrow-down + v0 := 33 + v1 := 0 + sprite v0 v1 8 + scroll-down 6 + + i := scrolling-pixel + v0 := 21 # X + v1 := 26 # Y + v2 := 15 + v3 := 4 + loop + sprite v0 v1 1 + sprite v0 v2 1 + sprite v0 v3 1 + v0 += 1 + if v0 != 42 then + again + v0 := 20 # X + v1 := 31 + v2 := 42 + v3 := 5 # Y + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v2 v3 1 + v3 += 1 + if v3 != 26 then + again + + v0 := key + waitKeyRelease + jump main + + +: scrolling-xochip-hires + clear + hires + + i := scrolling-arrow-left + v0 := 77 + v1 := 46 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 42 + v1 := 35 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-up + v0 := 54 + v1 := 46 + sprite v0 v1 8 + scroll-up 12 + scroll-up 12 + + i := scrolling-arrow-down + v0 := 65 + v1 := 11 + sprite v0 v1 8 + scroll-down 12 + + i := scrolling-pixel + v0 := 53 # X + v1 := 21 # Y + v2 := 32 + v3 := 43 + loop + sprite v0 v1 1 + sprite v0 v2 1 + sprite v0 v3 1 + v0 += 1 + if v0 != 74 then + again + v0 := 52 # X + v1 := 63 + v2 := 74 + v3 := 22 # Y + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v2 v3 1 + v3 += 1 + if v3 != 43 then + again + + v0 := key + waitKeyRelease + jump main + + + +: scratchpad + 0 +: scratchpad-plus-1 + 0 +: scratchpad-plus-2 + 0 0 0 0 0 + 0 0 0 0 0 + 0 0 0 0 +: scratchpad-plus-16 + 0 + + +: menu-cursor + 0b11000000 + 0b11000000 + + +# Positive and negative images +: flag-ok + 0b10100000 + 0b11000000 +: characters + 0b10000000 +: flag-err + 0b10100000 + 0b01000000 + 0b10100000 + +# Individual characters, some taken from Corax89' test. +: im0 + 0xE0 0xA0 0xA0 0xE0 +: im1 + 0xC0 0x40 0x40 0xE0 +: im2 + 0xE0 0x20 0xC0 0xE0 +: im3 + 0xE0 0x60 0x20 0xE0 +: im4 + 0xA0 0xE0 0x20 0x20 +: im5 + 0b11100000 + 0b11000000 + 0b00100000 + 0b11000000 +: im6 + 0xE0 0x80 0xE0 0xE0 +: im7 + 0xE0 0x20 0x20 0x20 +: im8 + 0xE0 0xE0 0xA0 0xE0 +: im9 + 0xE0 0xE0 0x20 0xE0 +: imA + 0x40 0xA0 0xE0 0xA0 +: imB + 0b11000000 + 0b11100000 + 0b10100000 + 0b11100000 +: imC + 0b11100000 + 0b10000000 + 0b10000000 + 0b11100000 +: imD + 0b11000000 + 0b10100000 + 0b10100000 + 0b11000000 +: imE + 0xE0 0xC0 0x80 0xE0 +: imF + 0xE0 0x80 0xC0 0x80 +: imG + 0b01100000 + 0b10000000 + 0b10100000 + 0b01100000 +: imH + 0b10100000 + 0b11100000 + 0b10100000 + 0b10100000 +: imI + 0b11100000 + 0b01000000 + 0b01000000 + 0b11100000 +: imJ + 0b01100000 + 0b00100000 + 0b00100000 + 0b11000000 +: imK + 0b10100000 + 0b11000000 + 0b10100000 + 0b10100000 +: imL + 0b10000000 + 0b10000000 + 0b10000000 + 0b11100000 +: imM + 0b11100000 + 0b11100000 + 0b10100000 + 0b10100000 +: imN + 0b11000000 + 0b10100000 + 0b10100000 + 0b10100000 +: imO + 0b11100000 + 0b10100000 + 0b10100000 + 0b11100000 +: imP + 0b11000000 + 0b10100000 + 0b11000000 + 0b10000000 +: imQ + 0b01000000 + 0b10100000 + 0b11100000 + 0b01100000 +: imR + 0b11000000 + 0b10100000 + 0b11000000 + 0b10100000 +: imS + 0b01100000 + 0b11000000 + 0b00100000 + 0b11000000 +: imT + 0b11100000 + 0b01000000 + 0b01000000 + 0b01000000 +: imU + 0b10100000 + 0b10100000 + 0b10100000 + 0b01100000 +: imV + 0b10100000 + 0b10100000 + 0b10100000 + 0b01000000 +: imW + 0b10100000 + 0b10100000 + 0b11100000 + 0b11100000 +: imX + 0xA0 0x40 0xA0 0xA0 +: imY + 0b10100000 + 0b10100000 + 0b01000000 + 0b01000000 +: imZ + 0b11100000 + 0b01100000 + 0b10000000 + 0b11100000 +: imSpace + 0 0 0 0 +: imDash + 0b00000000 + 0b11100000 + 0b00000000 + 0b00000000 +: imPeriod + 0b00000000 + 0b00000000 + 0b00000000 + 0b01000000 + + + +: scrolling-menu-1 + 8 13 :pointer scrolling-superchip + 8 18 :pointer scrolling-xochip + +: scrolling-choose-1 + str "PICK PLATFORM" 0 +: scrolling-str-superchip + str "1 SUPERCHIP" 0 +: scrolling-str-xochip + str "2 XO-CHIP" 0 + +: scrolling-menu-2 + 11 13 :pointer scrolling-lores + 11 18 :pointer scrolling-hires + +: scrolling-choose-2 + str "PICK SCR SIZE" 0 +: scrolling-str-lores + str "1 LOW RES" 0 +: scrolling-str-hires + str "2 HIGH RES" 0 + +: scrolling-menu-3 + 14 13 :pointer scrolling-superchip-lores-full-pixels + 14 18 :pointer scrolling-superchip-lores-half-pixels + +: scrolling-choose-3 + str "PICK TARGET" 0 +: scrolling-str-modern + str "1 MODERN" 0 +: scrolling-str-legacy + str "2 LEGACY" 0 + + + +: scrolling-arrow-left + 0b11111111 + 0b11100111 + 0b11001111 + 0b10000001 + 0b10000001 + 0b11001111 + 0b11100111 + 0b11111111 + +: scrolling-arrow-right + 0b11111111 + 0b11100111 + 0b11110011 + 0b10000001 + 0b10000001 + 0b11110011 + 0b11100111 + 0b11111111 + +: scrolling-arrow-up + 0b11111111 + 0b11100111 + 0b11000011 + 0b10000001 + 0b10100101 + 0b11100111 + 0b11100111 + 0b11111111 + +: scrolling-arrow-down + 0b11111111 + 0b11100111 + 0b11100111 + 0b10100101 + 0b10000001 + 0b11000011 + 0b11100111 + 0b11111111 + +: scrolling-pixel + 0b10000000 + +: selection + 0 + +: version-0-0 + 0x0a 0xae 0xa2 0x42 +: version-1-0 + 0x10 0x30 0x10 0xb8 + + diff --git a/bin/8-scrolling.ch8 b/bin/8-scrolling.ch8 new file mode 100644 index 0000000..eb546e7 Binary files /dev/null and b/bin/8-scrolling.ch8 differ diff --git a/docs/1-chip8-logo.html b/docs/1-chip8-logo.html index 18865fb..3787790 100644 --- a/docs/1-chip8-logo.html +++ b/docs/1-chip8-logo.html @@ -1,5 +1,5 @@ - + + + + + + + + + + + + diff --git a/docs/8-scrolling.html b/docs/8-scrolling.html new file mode 100644 index 0000000..cce578e --- /dev/null +++ b/docs/8-scrolling.html @@ -0,0 +1,1488 @@ + + + + + + + + diff --git a/legacy-superchip.md b/legacy-superchip.md new file mode 100644 index 0000000..6365b29 --- /dev/null +++ b/legacy-superchip.md @@ -0,0 +1,146 @@ +# Legacy SUPER-CHIP + +When CHIP-8 was ported to the HP48 graphical calculators in the nineties, many +mistakes were made. The CHIP48 and SUPER-CHIP interpreters introduced all kinds +of larger and smaller differences with the original Cosmac VIP interpreter that +we have kindly come to call "quirks". + +But later developers trying to replicate the unusual behaviours of SUPER-CHIP +have themselves also introduced new discrepancies. SUPER-CHIP used to work quite +a bit different on those HP48 calculators than most modern "SUPER-CHIP" capable +interpreters do these days. + +So different in fact, that for the purpose of this test suite we've had to split +SUPER-CHIP in a "legacy" and a "modern" flavour. Because ROMs written for +SUPER-CHIP interpreters that don't actually run on an HP calculator depend on +the modern behaviour, even though that is techically "wrong". + +The good news for interpreter developers is that most known ROMs that have been +written for the legacy interpreters also run fine in the modern version of +SUPER-CHIP. So if being able to run ROMs is what you care about, you will get +99% of the way there by just implementing the modern version. + +If you like a challenge though, this document will describe the differences +between modern SUPER-CHIP interpreters and the original ones — at least those +that this test suite is actually capable of testing. + +## The "display wait" quirk + +The `Display wait` quirk from the quirks test works different in legacy +SUPER-CHIP mode. Why is that? It has to do with how SUPER-CHIP low resolution +mode works, and a feature of that mode that's long been overlooked. + +### The "hidden feature" of `lores` mode + +We've all come to know the high and low resolution modes of SUPER-CHIP as +`hires` and `lores`, thanks to the instructions with those same names in Octo +and the descriptions of these opcodes in various sources. However, in [the +original description](https://groups.google.com/g/comp.sys.handhelds/c/fPUzuAkDdVs?pli=1) +of SUPER-CHIP's opcodes, the author Erik Bryntse calls `hires` "extended screen +mode" and describes that it is used for "enabling **higher speed** and full +screen (64x128) resolution" (emphasis mine). + +So `lores` mode, being the inverse of that, uses the lower resolution and also +_restricts the speed of the interpreter_ to 60 sprites drawn per second +(technically maybe 64, but let's not go there now 😉). + +[Gulrak](https://github.com/gulrak) discovered this when running the test suite +on his HP48SX and HP48GX calculators, with the original SUPER-CHIP interpreter, +and not passing the SUPER-CHIP quirks test: + +![The test suite version 4 breaking on an HP48SX](./pictures/broken-on-hp48.jpg) + +It works this way with the SUPER-CHIP 1.0 and SUPER-CHIP 1.1 interpreters, and +on multiple devices, so it's definitely a feature, not a bug. + +Probably a better way to describe `lores` mode would be "compatibility mode", as +SUPER-CHIP tries to be compatible (at least in the way of resolution and speed) +with the original Cosmac VIP interpreter in this mode. + +Since the test suite ran entirely in `lores` mode before version 4.1, it +accidentally triggered the "compatibility mode" of SUPER-CHIP and enabled the +`Display wait` quirk in the interpreter. The test would then report an error, +because SUPER-CHIP wasn't supposed to have `Display wait` enabled, according +to conventional wisdom. + +### Fixing the problem + +After discovering this issue, I could not possibly pretend that the quirks test +of the test suite was correct for SUPER-CHIP, since it gave an error on the +original platform. So I decided to extend the quirks test by running the +`Display wait` and `Clipping` tests in both `lores` and `hires` modes. + +For legacy SUPER-CHIP, it now expects the `Display wait` quirk to be enabled in +`lores` mode and to be disabled in `hires` mode. Otherwise, the test will fail. +For modern SUPER-CHIP it still expects the `Display wait` quirk to be disabled +in both modes. The `Clipping` quirk, it turns out, should be enabled in all +cases. + +This gives us the expected result with the SUPER-CHIP 1.0 and 1.1 interpreters +on the original hardware: + +![Test suite version 4.1 working on HP48SX](./pictures/HP48SX-quirks.JPG) + +## The scrolling test + +When you run the scrolling test and select legacy SUPER-CHIP and `lores`, it +will probably show a pretty messed up image if you haven't explicitly developed +your interpreter to handle that case. What's the deal with that? + +### How a design flaw morphed over time + +Since the HP48 calculators had exactly twice the resolution of CHIP-8, the +original interpreters for the platform implemented `lores` such that each pixel +was 2x2 physical pixels. This was an easy trick to make the low resolution +CHIP-8 screen fill up the entire display of the calculator. + +When the scrolling instructions were added with the release of SUPER-CHIP 1.1, +they were not really intended to be used by the boring old original CHIP-8 ROMs. +Only fancy new SUPER-CHIP ROMs, intended for the calculator, would even know +those existed! So when those opcodes were implemented for SUPER-CHIP, the author +probably only expected people to use those new opcodes in "Extended screen +mode", which allows the developer to use all the pixels of the calculator's +screen, which we now know as `hires` mode. Because why wouldn't you want to use +all available pixels? + +So the author probably didn't really consider what would happen if ROMs would +use the scrolling instructions when in `lores` "compatibility" mode. He +implemented scrolling to operate on the physical pixels of the device, but still +allowed `lores` ROMs to use the opcodes. Which means that in `lores` mode +scrolling happens per "half pixel". This behaviour is what the "legacy" version +of the test is intended for: + +![Scrolling test showing half-scrolling working on the HP48SX](./pictures/HP48SX-scrolling-lores.JPG) + +However, modern interpreter developers reading the SUPER-CHIP spec (but not +having an HP calculator available for testing) implemented the "modern" version +of the opcodes, in the way most people would expect: scrolling one pixel just +scrolls one pixel, independent of wether you are in `hires` or `lores` mode. +When [Octo](http://octo-ide.com) came out, if followed suit. And thanks to the +awesome yearly Octojam game jams, people have been writing brand new SUPER-CHIP +games in the last couple of years, including `lores` ROMs that expect single +pixel scrolling. + +So even though scrolling per whole pixel is technically "wrong" for `lores` +SUPER-CHIP, most `lores` games that use scrolling depend on this "modern" +interpretation of the opcodes. There are almost no ROMs that depend on the +legacy version. + +## Tip of the iceberg + +These are just two "features" of SUPER-CHIP that have pretty much been "lost" +over time that we've run into when developing this test suite. I'm aware that +there are more, but that's for a later time. And there are probably more issues +that I'm not yet aware of. + +One of the secret goals of this test suite is to keep the CHIP-8 interpreters in +our community from diverging even further. Every interpreter that _tries_ to be +compatible but accidentally introduces a new incompatibility, and then has ROMs +written for it, fragments the space further. Making CHIP-8 development even +harder for future generations trying to run those ROMs. + +That's why we need to discover and document these things properly, and write +easy to run and easy to interpret tests for all those new people coming to the +platform every week, and for those who simply don't have access to the original +systems. + diff --git a/package.json b/package.json index aeb09bf..f19dc96 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,10 @@ { "name": "Timendus' CHIP-8 Test Suite", "scripts": { - "start": "npm run build-logo && npm run build-ibm && npm run build-corax && npm run build-flags && npm run build-quirks && npm run build-keypad", - "build-logo": "export TEST='1-chip8-logo' && npm run build-test", - "build-ibm": "export TEST='2-ibm-logo' && npm run build-test", - "build-corax": "export TEST='3-corax+' && npm run build-test", - "build-flags": "export TEST='4-flags' && npm run build-test", - "build-quirks": "export TEST='5-quirks' && npm run build-test", - "build-keypad": "export TEST='6-keypad' && npm run build-test", - "build-test": "octopus src/tests/${TEST}.8o bin/${TEST}.8o && octo bin/${TEST}.8o bin/${TEST}.ch8 && octo --options octo-config.json bin/${TEST}.8o docs/${TEST}.html && cat bin/${TEST}.8o | pbcopy" + "start": "npm run scale-splash && npm run build-tests", + "scale-splash": "ffmpeg -y -i pictures/splash.png -vf scale=96:-1 -sws_flags neighbor pictures/splash2x.png", + "build-tests": "for TEST in ./src/tests/*.8o; do export TEST=`basename ${TEST%.*}`; npm run build-test; done", + "build-test": "echo \"Building ${TEST}.ch8\" && octopus src/tests/${TEST}.8o bin/${TEST}.8o && octo bin/${TEST}.8o bin/${TEST}.ch8 && octo --options octo-config.json bin/${TEST}.8o docs/${TEST}.html && cat bin/${TEST}.8o | pbcopy" }, "dependencies": { "@chipcode/image-loader": "^0.2.1", diff --git a/pictures/Cosmac-VIP-quirks.png b/pictures/Cosmac-VIP-quirks.png new file mode 100644 index 0000000..3a6ea4d Binary files /dev/null and b/pictures/Cosmac-VIP-quirks.png differ diff --git a/pictures/HP48SX-quirks.JPG b/pictures/HP48SX-quirks.JPG new file mode 100644 index 0000000..4b9270d Binary files /dev/null and b/pictures/HP48SX-quirks.JPG differ diff --git a/pictures/HP48SX-scrolling-hires.JPG b/pictures/HP48SX-scrolling-hires.JPG new file mode 100644 index 0000000..f96d486 Binary files /dev/null and b/pictures/HP48SX-scrolling-hires.JPG differ diff --git a/pictures/HP48SX-scrolling-lores.JPG b/pictures/HP48SX-scrolling-lores.JPG new file mode 100644 index 0000000..5f99ff6 Binary files /dev/null and b/pictures/HP48SX-scrolling-lores.JPG differ diff --git a/pictures/beep.png b/pictures/beep.png new file mode 100644 index 0000000..7707d0f Binary files /dev/null and b/pictures/beep.png differ diff --git a/pictures/broken-on-hp48.jpg b/pictures/broken-on-hp48.jpg new file mode 100644 index 0000000..9f5908c Binary files /dev/null and b/pictures/broken-on-hp48.jpg differ diff --git a/pictures/chip-8-logo.png b/pictures/chip-8-logo.png index 979e922..0b5366c 100644 Binary files a/pictures/chip-8-logo.png and b/pictures/chip-8-logo.png differ diff --git a/pictures/corax+.png b/pictures/corax+.png index 92ef658..9effa1e 100644 Binary files a/pictures/corax+.png and b/pictures/corax+.png differ diff --git a/pictures/flags.png b/pictures/flags.png index 37ee96c..0deaa6d 100644 Binary files a/pictures/flags.png and b/pictures/flags.png differ diff --git a/pictures/hires-no-scrolling.png b/pictures/hires-no-scrolling.png new file mode 100644 index 0000000..33f51ff Binary files /dev/null and b/pictures/hires-no-scrolling.png differ diff --git a/pictures/hires-scrolling.png b/pictures/hires-scrolling.png new file mode 100644 index 0000000..e35399f Binary files /dev/null and b/pictures/hires-scrolling.png differ diff --git a/pictures/ibm-logo.png b/pictures/ibm-logo.png index 97d59c0..e8d6186 100644 Binary files a/pictures/ibm-logo.png and b/pictures/ibm-logo.png differ diff --git a/pictures/ibm.png b/pictures/ibm.png index 3f6acde..637c259 100644 Binary files a/pictures/ibm.png and b/pictures/ibm.png differ diff --git a/pictures/keypad-menu.png b/pictures/keypad-menu.png index 1eacbbb..c73b2b7 100644 Binary files a/pictures/keypad-menu.png and b/pictures/keypad-menu.png differ diff --git a/pictures/lores-no-half-scrolling.png b/pictures/lores-no-half-scrolling.png new file mode 100644 index 0000000..eb3df2c Binary files /dev/null and b/pictures/lores-no-half-scrolling.png differ diff --git a/pictures/lores-no-scrolling.png b/pictures/lores-no-scrolling.png new file mode 100644 index 0000000..1c2d17e Binary files /dev/null and b/pictures/lores-no-scrolling.png differ diff --git a/pictures/lores-scrolling.png b/pictures/lores-scrolling.png new file mode 100644 index 0000000..ab668a2 Binary files /dev/null and b/pictures/lores-scrolling.png differ diff --git a/pictures/quirks-platform.png b/pictures/quirks-platform.png index 4d8b9a2..b29e4d5 100644 Binary files a/pictures/quirks-platform.png and b/pictures/quirks-platform.png differ diff --git a/pictures/quirks.png b/pictures/quirks.png index 7e56a49..0cd0df7 100644 Binary files a/pictures/quirks.png and b/pictures/quirks.png differ diff --git a/pictures/scrolling-platform.png b/pictures/scrolling-platform.png new file mode 100644 index 0000000..4ab26fb Binary files /dev/null and b/pictures/scrolling-platform.png differ diff --git a/pictures/splash.png b/pictures/splash.png index 161c286..2b073fd 100644 Binary files a/pictures/splash.png and b/pictures/splash.png differ diff --git a/pictures/splash2x.png b/pictures/splash2x.png new file mode 100644 index 0000000..0e158e9 Binary files /dev/null and b/pictures/splash2x.png differ diff --git a/pictures/version.png b/pictures/version.png index 231bffa..179079f 100644 Binary files a/pictures/version.png and b/pictures/version.png differ diff --git a/src/tests/4-flags.8o b/src/tests/4-flags.8o index e1ede42..fede264 100644 --- a/src/tests/4-flags.8o +++ b/src/tests/4-flags.8o @@ -134,15 +134,28 @@ # Subtraction in one direction (no carry) opcode-digit 0x5 # 0x85 + # Edge cases: + # Check that vF -= vX results in no carry vF := 20 vF -= 15_in_a_register # 5 (0x05), but should be overwritten by flag, so 1 v4 := vF + # Check that vX -= vF results in the correct result and no carry v3 := 20 vF := 15 v3 -= vF # 5 (0x05) + # Check that N - N (for the same N) does not result in carry + v5 := 10 + vF := 10 + v5 -= vF + v5 := vF + # Base case: check that subtracting two regular registers results in the + # correct value and no carry set, and that the carry register actually gets + # overwritten. vF := 0xAA v2 := 50 v2 -= 15_in_a_register # 35 (0x23) + # Check all our assertions + if v5 != 1 then vF := 2 expect-v2-vf-v3-v4 35 1 5 1 y += 5 @@ -161,16 +174,29 @@ # Subtraction in the other direction (no carry) opcode-digit 0x7 # 0x87 + # Edge cases: + # Check that vF =- vX results in no carry vF := 10 vF =- 15_in_a_register # 5 (0x5), but should be overwritten by flag, so 1 v4 := vF + # Check that vX =- vF results in the correct result and no carry v3 := 15 vF := 20 v3 =- vF # 5 (0x05) + # Check that N - N (for the same N) does not result in carry + v5 := 10 + vF := 10 + v5 =- vF + v5 := vF + # Base case: check that subtracting two regular registers results in the + # correct value and no carry set, and taht the carry register actually gets + # overwritten. vF := 0xAA v2 := 15 v1 := 50 v2 =- v1 # 35 (0x23) + # Check all our assertions + if v5 != 1 then vF := 2 expect-v2-vf-v3-v4 35 1 5 1 x += 1 diff --git a/src/tests/5-quirks.8o b/src/tests/5-quirks.8o index e9f22b0..a5a7899 100644 --- a/src/tests/5-quirks.8o +++ b/src/tests/5-quirks.8o @@ -8,26 +8,52 @@ :include "../utils/menu.8o" :include "../utils/text-rendering.8o" -:macro show X address { - v0 := X - i := address - sprite v0 v1 15 -} +:const OFF 0 +:const ON 1 +:const BOTH 2 +:const NONE 3 +:const LORES 4 +:const HIRES 5 +:const ERR1 6 +:const ERR2 7 +:const ERR3 8 + +:const CHIP8 1 +:const SCHIP_MODERN 2 +:const XOCHIP 3 +:const SCHIP_LEGACY 4 : main clear i := 0x1FF load v0 - if v0 == 1 then jump quirks-chip8 - if v0 == 2 then jump quirks-schip - if v0 == 3 then jump quirks-xochip + if v0 == CHIP8 then jump quirks-chip8 + if v0 == SCHIP_MODERN then jump quirks-schip + if v0 == SCHIP_LEGACY then jump quirks-schip-legacy + if v0 == XOCHIP then jump quirks-xochip text 6 2 quirks-choose - text 16 10 quirks-str-chip8 - text 16 15 quirks-str-schip - text 16 20 quirks-str-xochip + text 10 10 quirks-str-chip8 + text 10 15 quirks-str-schip + text 10 20 quirks-str-xochip + render-version + + :unpack 0xA quirks-menu + v2 := 2 + jump menu-start + +: superchip-menu + clear + text 10 2 quirks-choose-superchip + text 18 12 quirks-str-modern + text 18 17 quirks-str-legacy + render-version + + :unpack 0xA superchip-target-menu + v2 := 1 + jump menu-start - # Show version number in bottom right corner +: render-version x := 50 y := 27 i := version-0-0 @@ -35,81 +61,122 @@ x := 58 i := version-1-0 sprite x y 4 + return - :unpack 0xA quirks-menu - v2 := 2 - jump menu-start +:segment data + +: quirks-menu + 6 11 :pointer quirks-chip8 + 6 16 :pointer superchip-menu + 6 21 :pointer quirks-xochip + +: quirks-choose + str "PICK PLATFORM" 0 +: quirks-str-chip8 + str "1 CHIP-8" 0 +: quirks-str-schip + str "2 SUPER-CHIP" 0 +: quirks-str-xochip + str "3 XO-CHIP" 0 + +: superchip-target-menu + 14 13 :pointer quirks-schip + 14 18 :pointer quirks-schip-legacy + +: quirks-choose-superchip + str "PICK TARGET" 0 +: quirks-str-modern + str "1 MODERN" 0 +: quirks-str-legacy + str "2 LEGACY" 0 + +:segment code : quirks-chip8 i := scratchpad - v0 := 1 + v0 := CHIP8 save v0 jump quirks-run-tests : quirks-schip i := scratchpad - v0 := 2 + v0 := SCHIP_MODERN + save v0 + jump quirks-run-tests + +: quirks-schip-legacy + i := scratchpad + v0 := SCHIP_LEGACY save v0 jump quirks-run-tests : quirks-xochip i := scratchpad - v0 := 3 + v0 := XOCHIP save v0 : quirks-run-tests waitKeyRelease - - # Determine frames per second for dispQuirk clear - v1 := 0 - show 8 splash-0-0 - show 16 splash-1-0 - show 24 splash-2-0 - show 32 splash-3-0 - show 40 splash-4-0 - show 48 splash-5-0 - - v1 := 15 - show 8 splash-0-1 - show 16 splash-1-1 - show 24 splash-2-1 - show 32 splash-3-1 - show 40 splash-4-1 - show 48 splash-5-1 - - i := quirks-values - load v6 - i := quirks-image - delay := v5 - loop - v5 := 30 - loop - sprite v0 v1 1 - if vF == 0 begin - v0 := 54 - v6 := delay - v6 >>= v6 - v6 >>= v6 - v0 -= v6 - end - v5 -= 1 - if v5 != 0 then - again - v2 += v4 - v3 += vF - vE := delay - if vE != 0 then again - - # We expect the inner loop with 30 `sprite`s to have been able to run six - # times in the timespan of 180 interrupts - v0 := 1 - if v3 != 0 then v0 := 0 - if v2 > 6 then v0 := 0 - if v2 < 6 then v0 := 2 - i := scratchpad-plus-1 - save v0 + # Determine frames per second for dispQuirk + i := scratchpad + load v0 + if v0 == CHIP8 begin + get-frames-per-second-chip8 + + # We expect the inner loop with 30 `sprite`s to have been able to run six + # times in the timespan of 180 interrupts + v0 := ON + if v3 != 0 then v0 := OFF + if v2 > 6 then v0 := OFF + if v2 < 6 then v0 := ERR1 + i := scratchpad-plus-1 + save v0 + else # Superchip & XO-CHIP + get-frames-per-second-schip-xochip + + # We expect the inner loop with 30 `sprite`s to have been able to run 3 + # times in the timespan of 90 interrupts + v0 := ON + if v3 != 0 then v0 := OFF + if v2 > 3 then v0 := OFF + if v2 < 3 then v0 := ERR1 + i := scratchpad-plus-1 + save v0 + + # Do the test again, but now in hires mode + hires + clear + get-frames-per-second-hires + + # We expect the inner loop with 30 `sprite`s to have been able to run 3 + # times in the timespan of 90 interrupts + v1 := ON + if v3 != 0 then v1 := OFF + if v2 > 3 then v1 := OFF + if v2 < 3 then v1 := ERR1 + i := scratchpad-plus-1 + load v0 + if v0 == ERR1 then jump quirks-combined-vblank-skip + if v1 == ERR1 begin + v0 := ERR1 + jump quirks-combined-vblank-skip + end + if v0 == OFF begin + if v1 == OFF then v0 := NONE + if v1 == ON then v0 := HIRES + end + if v0 == ON begin + if v1 == OFF then v0 := LORES + if v1 == ON then v0 := BOTH + end +: quirks-combined-vblank-skip + i := scratchpad-plus-1 + save v0 + + lores + end # Determine if sprites get clipped vertically clear @@ -159,17 +226,116 @@ v9 += vF # Save result - v0 := 0 + v0 := OFF # Clipping - if v5 == 0 then v0 := 1 - if v5 != v6 then v0 := 2 - if v5 != v7 then v0 := 2 - if v5 != v8 then v0 := 2 + if v5 == 0 then v0 := ON + if v5 != v6 then v0 := ERR1 + if v5 != v7 then v0 := ERR1 + if v5 != v8 then v0 := ERR1 # Wrapping - if v9 != 4 then v0 := 3 + if v9 != 4 then v0 := ERR2 i := scratchpad-plus-2 save v0 + # If SuperCHIP or XO-CHIP, do it again in hires + i := scratchpad + load v0 + if v0 != CHIP8 begin + hires + # Determine if sprites get clipped vertically + clear + i := cursor + v0 := 60 + v1 := 61 + sprite v0 v1 6 + v0 := 54 + v1 := 2 + sprite v0 v1 2 + v5 := vF + v0 := 66 + sprite v0 v1 2 + v6 := vF + + # Determine if sprites get clipped horizontally + clear + i := cursor + v0 := 125 + v1 := 5 + sprite v0 v1 6 + v0 := 3 + v1 := 4 + sprite v0 v1 2 + v7 := vF + v1 := 10 + sprite v0 v1 2 + v8 := vF + + # Determine if sprites get wrapped (both directions) + clear + v0 := 174 + v1 := 82 + sprite v0 v1 6 # Should draw at 46,18 + v0 := 40 + v1 := 17 + sprite v0 v1 2 + v9 := vF + v0 := 52 + sprite v0 v1 2 + v9 += vF + v1 := 23 + sprite v0 v1 2 + v9 += vF + v0 := 40 + sprite v0 v1 2 + v9 += vF + + # Determine result + v1 := OFF + # Clipping + if v5 == 0 then v1 := ON + if v5 != v6 then v1 := ERR1 + if v5 != v7 then v1 := ERR1 + if v5 != v8 then v1 := ERR1 + # Wrapping + if v9 != 4 then v1 := ERR2 + + # Compare with previous result + i := scratchpad-plus-2 + load v0 + + if v0 == ERR1 begin + if v1 == ERR1 then jump quirks-combined-wrapping-skip + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + if v0 == ERR2 begin + if v1 == ERR2 then jump quirks-combined-wrapping-skip + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + if v1 == ERR1 begin + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + if v1 == ERR2 begin + v0 := ERR3 + jump quirks-combined-wrapping-skip + end + + if v0 == ON begin + if v1 == ON then v0 := BOTH + if v1 == OFF then v0 := LORES + end + if v0 == OFF begin + if v1 == ON then v0 := HIRES + if v1 == OFF then v0 := NONE + end +: quirks-combined-wrapping-skip + i := scratchpad-plus-2 + save v0 + lores + end + # Present results clear @@ -192,8 +358,7 @@ i := scratchpad load v0 i := flag-err - if v0 == 1 begin - # Selected CHIP-8 + if v0 == CHIP8 begin if v5 == 1 then i := flag-ok else # Selected SCHIP or XO-CHIP @@ -206,7 +371,7 @@ if v5 == 1 then i := quirks-on if v5 != v6 then i := quirks-inconsistent-1 if v5 != v7 then i := quirks-inconsistent-2 - vD := 44 + vD := 42 vE := 1 drawText @@ -221,11 +386,16 @@ i := scratchpad load v0 i := flag-err - if v0 == 2 begin - # Selected SCHIP + if v0 == CHIP8 begin + if v5 != 5 then i := flag-ok + end + if v0 == SCHIP_MODERN begin if v5 == 5 then i := flag-ok - else - # Selected CHIP-8 or XO-CHIP + end + if v0 == SCHIP_LEGACY begin + if v5 == 5 then i := flag-ok + end + if v0 == XOCHIP begin if v5 != 5 then i := flag-ok end x := 59 @@ -233,7 +403,7 @@ sprite x y 3 i := quirks-on if v5 == 5 then i := quirks-off - vD := 44 + vD := 42 vE := 6 drawText @@ -243,20 +413,29 @@ i := scratchpad load v1 i := flag-err - if v0 == 1 begin - # Selected CHIP-8 - if v1 == 1 then i := flag-ok - else - # Selected SCHIP or XO-CHIP - if v1 == 0 then i := flag-ok + if v0 == CHIP8 begin + if v1 == ON then i := flag-ok + end + if v0 == SCHIP_MODERN begin + if v1 == NONE then i := flag-ok + end + if v0 == SCHIP_LEGACY begin + if v1 == LORES then i := flag-ok + end + if v0 == XOCHIP begin + if v1 == NONE then i := flag-ok end x := 59 y := 12 sprite x y 3 i := quirks-off - if v1 == 1 then i := quirks-on - if v1 == 2 then i := quirks-low - vD := 44 + if v1 == ON then i := quirks-on + if v1 == ERR1 then i := quirks-slow + if v1 == LORES then i := quirks-lores + if v1 == HIRES then i := quirks-hires + if v1 == BOTH then i := quirks-both + if v1 == NONE then i := quirks-none + vD := 42 vE := 11 drawText @@ -266,21 +445,31 @@ i := scratchpad load v2 i := flag-err - if v0 == 3 begin - # Selected XO-CHIP - if v2 == 0 then i := flag-ok - else - # Selected CHIP-8 or SCHIP - if v2 == 1 then i := flag-ok + if v0 == CHIP8 begin + if v2 == ON then i := flag-ok + end + if v0 == SCHIP_MODERN begin + if v2 == BOTH then i := flag-ok + end + if v0 == SCHIP_LEGACY begin + if v2 == BOTH then i := flag-ok + end + if v0 == XOCHIP begin + if v2 == NONE then i := flag-ok end x := 59 y := 17 sprite x y 3 i := quirks-off - if v2 == 1 then i := quirks-on - if v2 == 2 then i := quirks-inconsistent-1 - if v2 == 3 then i := quirks-inconsistent-2 - vD := 44 + if v2 == ON then i := quirks-on + if v2 == LORES then i := quirks-lores + if v2 == HIRES then i := quirks-hires + if v2 == BOTH then i := quirks-both + if v2 == NONE then i := quirks-none + if v2 == ERR1 then i := quirks-inconsistent-1 + if v2 == ERR2 then i := quirks-inconsistent-2 + if v2 == ERR3 then i := quirks-inconsistent-3 + vD := 42 vE := 16 drawText @@ -297,11 +486,16 @@ i := scratchpad load v0 i := flag-err - if v0 == 2 begin - # Selected SCHIP + if v0 == CHIP8 begin + if v5 != 0 then i := flag-ok + end + if v0 == SCHIP_MODERN begin if v5 == 0 then i := flag-ok - else - # Selected CHIP-8 or XO-CHIP + end + if v0 == SCHIP_LEGACY begin + if v5 == 0 then i := flag-ok + end + if v0 == XOCHIP begin if v5 != 0 then i := flag-ok end x := 59 @@ -310,7 +504,7 @@ i := quirks-off if v5 == 0 then i := quirks-on if v5 != v7 then i := quirks-inconsistent-1 - vD := 44 + vD := 42 vE := 21 drawText @@ -325,11 +519,16 @@ i := scratchpad load v0 i := flag-err - if v0 == 2 begin - # Selected SCHIP + if v0 == CHIP8 begin + if v5 == 0 then i := flag-ok + end + if v0 == SCHIP_MODERN begin if v5 != 0 then i := flag-ok - else - # Selected CHIP-8 or XO-CHIP + end + if v0 == SCHIP_LEGACY begin + if v5 != 0 then i := flag-ok + end + if v0 == XOCHIP begin if v5 == 0 then i := flag-ok end x := 59 @@ -337,7 +536,7 @@ sprite x y 3 i := quirks-off if v5 == 1 then i := quirks-on - vD := 44 + vD := 42 vE := 26 drawText @@ -347,20 +546,6 @@ :segment data -: quirks-menu - 12 11 :pointer quirks-chip8 - 12 16 :pointer quirks-schip - 12 21 :pointer quirks-xochip - -: quirks-choose - str "PICK PLATFORM" 0 -: quirks-str-chip8 - str "1 CHIP-8" 0 -: quirks-str-schip - str "2 SCHIP" 0 -: quirks-str-xochip - str "3 XO-CHIP" 0 - : quirks-vf str "VF RESET" 0 : quirks-mem @@ -378,17 +563,22 @@ str "ON" 0 : quirks-off str "OFF" 0 +: quirks-slow + str "SLOW" 0 +: quirks-lores + str "LRES" 0 +: quirks-hires + str "HRES" 0 +: quirks-both + str "BOTH" 0 +: quirks-none + str "NONE" 0 : quirks-inconsistent-1 - str "E1" 0 + str "ERR1" 0 : quirks-inconsistent-2 - str "E2" 0 -: quirks-low - str "LOW" 0 - -: quirks-values - 10 31 0 0 1 180 0 -: quirks-image - 0b10000000 + str "ERR2" 0 +: quirks-inconsistent-3 + str "ERR3" 0 : cursor 0b11111110 @@ -398,8 +588,167 @@ 0b11111110 0b11111110 +:segment code + +: get-frames-per-second-chip8 + quirks-show-splash-lores + i := quirks-values + load v6 + # v0 := 10 + # v1 := 31 + # v2 := 0 + # v3 := 0 + # v4 := 1 + # v5 := 180 + # v6 := 0 + i := quirks-image + delay := v5 + loop + v5 := 30 + loop + sprite v0 v1 1 + if vF == 0 begin + v0 := 54 + v6 := delay + v6 >>= v6 + v6 >>= v6 + v0 -= v6 + end + v5 -= 1 + if v5 != 0 then + again + v2 += v4 + v3 += vF + vE := delay + if vE != 0 then again + return + +: get-frames-per-second-schip-xochip + quirks-show-splash-lores + i := quirks-values + load v6 + # v0 := 10 + # v1 := 31 + # v2 := 0 + # v3 := 0 + # v4 := 1 + # v5 := 180 + # v6 := 0 + v5 >>= v5 + i := quirks-image + delay := v5 + loop + v5 := 30 + loop + sprite v0 v1 1 + if vF == 0 begin + v0 := 32 + v6 := delay + v6 >>= v6 + v6 >>= v6 + v0 -= v6 + end + v5 -= 1 + if v5 != 0 then + again + v2 += v4 + v3 += vF + vE := delay + if vE != 0 then again + return + +: quirks-show-splash-lores + v0 := 8 + v1 := 0 + v2 := 15 + i := splash-0-0 + loop + sprite v0 v1 15 + i += v2 + v0 += 8 + if v0 == 56 begin + v0 := 8 + v1 += 15 + end + if v1 != 30 then + again + return + +: get-frames-per-second-hires + v0 := 16 + v1 := 0 + v2 := 32 + i := splash2x-0-0 + loop + sprite v0 v1 0 + i += v2 + v0 += 16 + if v0 == 112 begin + v0 := 16 + v1 += 16 + end + if v1 != 64 then + again + + # 11th pixel + i := quirks-values + load v6 + # v0 := 10 + # v1 := 31 + # v2 := 0 + # v3 := 0 + # v4 := 1 + # v5 := 180 + # v6 := 0 + v0 += v0 + v1 += v1 + v5 >>= v5 + i := quirks-line + loop + sprite v0 v1 2 + v0 += 8 + if v0 != 60 then + again + i := quirks-image + loop + sprite v0 v1 2 + v0 += 1 + if v0 != 64 then + again + delay := v5 + loop + v5 := 15 + loop + sprite v0 v1 2 + if vF == 0 begin + v0 := 108 + v6 := delay + v6 >>= v6 + v0 -= v6 + end + v5 -= 1 + if v5 != 0 then + again + v2 += v4 + v3 += vF + vE := delay + if vE != 0 then again + return + +:segment data + +: quirks-values + 10 31 0 0 1 180 0 +: quirks-image + 0b10000000 + 0b10000000 +: quirks-line + 0b11111111 + 0b11111111 + :include "../../pictures/version.png" :include "../../pictures/splash.png" +:include "../../pictures/splash2x.png" 16x16 # Jump quirk targets: :org 0xE98 diff --git a/src/tests/7-beep.8o b/src/tests/7-beep.8o new file mode 100644 index 0000000..e976783 --- /dev/null +++ b/src/tests/7-beep.8o @@ -0,0 +1,74 @@ +# Beep test + +# This is a new test for this test suite, that allows you to test if your buzzer +# is working. It will beep SOS in morse code and flash a speaker icon on the +# display in the same pattern. If you press the CHIP-8 B button it will give you +# manual control over the buzzer. + +: main + i := pattern + load v0 + v2 := 1 + v3 := v0 + v4 := 0xB + vA := 28 + vB := 12 + loop + i := pattern + i += v2 + load v1 + i := speaker + sprite vA vB 7 + buzzer := v0 + delay := v0 + wait-for-delay + sprite vA vB 7 + delay := v1 + wait-for-delay + v2 += 2 + if v2 != v3 then + again + jump main + +: wait-for-delay + if v4 key then jump manual-control + v0 := delay + if v0 != 0 then jump wait-for-delay + return + +: manual-control + v0 := 0 + v1 := 60 + v2 := 0xB + clear + loop + i := speaker + sprite vA vB 7 + loop + buzzer := v1 + if v2 key then + again + sprite vA vB 7 + loop + buzzer := v0 + if v2 -key then + again + again + +:segment data + +: pattern + 19 + 10 5 10 5 10 20 + 30 5 30 5 30 20 + 10 5 10 5 10 60 + +: speaker + 0b00011001 + 0b00101010 + 0b11001000 + 0b10001011 + 0b11001000 + 0b00101010 + 0b00011001 + diff --git a/src/tests/8-scrolling.8o b/src/tests/8-scrolling.8o new file mode 100644 index 0000000..591d7a7 --- /dev/null +++ b/src/tests/8-scrolling.8o @@ -0,0 +1,494 @@ +# Scrolling test + +# This is a new test for this test suite, that allows you to test if your +# scrolling opcodes do the right thing. All the arrows should land in their +# boxes in the direction they point in. + + + +:include "../utils/helpers.8o" +:include "../utils/menu.8o" +:include "../utils/text-rendering.8o" + +: main + # Jump to the right test based on magic values + i := 0x1FF + load v0 + if v0 == 1 then jump scrolling-superchip-lores-full-pixels + if v0 == 2 then jump scrolling-superchip-lores-half-pixels + if v0 == 3 then jump scrolling-superchip-hires + if v0 == 4 then jump scrolling-xochip-lores + if v0 == 5 then jump scrolling-xochip-hires + + + +: menu-1 + lores + clear + text 6 2 scrolling-choose-1 + text 12 12 scrolling-str-superchip + text 12 17 scrolling-str-xochip + render-version + :unpack 0xA scrolling-menu-1 + v2 := 1 + jump menu-start + +: menu-2 + lores + clear + text 7 2 scrolling-choose-2 + text 15 12 scrolling-str-lores + text 15 17 scrolling-str-hires + render-version + :unpack 0xA scrolling-menu-2 + v2 := 1 + jump menu-start + +: menu-3 + lores + clear + text 10 2 scrolling-choose-3 + text 18 12 scrolling-str-modern + text 18 17 scrolling-str-legacy + render-version + :unpack 0xA scrolling-menu-3 + v2 := 1 + jump menu-start + +: render-version + x := 50 + y := 27 + i := version-0-0 + sprite x y 4 + x := 58 + i := version-1-0 + sprite x y 4 + return + + + +: scrolling-superchip + i := selection + v0 := 0 + save v0 + jump menu-2 + +: scrolling-xochip + i := selection + v0 := 1 + save v0 + jump menu-2 + +: scrolling-lores + i := selection + load v0 + if v0 == 0 then jump menu-3 + jump scrolling-xochip-lores + +: scrolling-hires + i := selection + load v0 + if v0 == 0 then jump scrolling-superchip-hires + jump scrolling-xochip-hires + + + +: scrolling-superchip-lores-full-pixels + clear + + i := scrolling-arrow-left + v0 := 46 + v1 := 11 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 10 + v1 := 11 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-down + v0 := 28 + v1 := 0 + sprite v0 v1 8 + scroll-down 6 + + jump scrolling-draw-borders-lores + +: scrolling-superchip-lores-half-pixels + clear + + i := scrolling-arrow-left + v0 := 46 + v1 := 11 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 10 + v1 := 11 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-down + v0 := 28 + v1 := 0 + sprite v0 v1 8 + scroll-down 12 + +: scrolling-draw-borders-lores + i := scrolling-pixel + v0 := 21 # X + v1 := 33 + v2 := 27 + v3 := 26 # Y + v4 := 15 + v5 := 4 + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v0 v4 1 + sprite v1 v4 1 + sprite v2 v5 1 + v0 += 1 + v1 += 1 + v2 += 1 + if v0 != 31 then + again + v0 := 20 # X + v1 := 31 + v2 := 32 + v3 := 43 + v4 := 26 + v5 := 37 + v6 := 16 # Y + v7 := 5 + loop + sprite v0 v6 1 + sprite v1 v6 1 + sprite v2 v6 1 + sprite v3 v6 1 + sprite v4 v7 1 + sprite v5 v7 1 + v6 += 1 + v7 += 1 + if v6 != 26 then + again + + v0 := key + waitKeyRelease + jump main + + +: scrolling-superchip-hires + clear + hires + + i := scrolling-arrow-left + v0 := 78 + v1 := 22 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 42 + v1 := 22 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-down + v0 := 60 + v1 := 11 + sprite v0 v1 8 + scroll-down 12 + + i := scrolling-pixel + v0 := 53 # X + v1 := 65 + v2 := 59 + v3 := 43 # Y + v4 := 32 + v5 := 21 + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v0 v4 1 + sprite v1 v4 1 + sprite v2 v5 1 + v0 += 1 + v1 += 1 + v2 += 1 + if v0 != 63 then + again + v0 := 52 # X + v1 := 63 + v2 := 64 + v3 := 75 + v4 := 58 + v5 := 69 + v6 := 33 # Y + v7 := 22 + loop + sprite v0 v6 1 + sprite v1 v6 1 + sprite v2 v6 1 + sprite v3 v6 1 + sprite v4 v7 1 + sprite v5 v7 1 + v6 += 1 + v7 += 1 + if v7 != 32 then + again + + v0 := key + waitKeyRelease + jump main + + + +: scrolling-xochip-lores + clear + + i := scrolling-arrow-left + v0 := 45 + v1 := 23 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 10 + v1 := 12 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-up + v0 := 22 + v1 := 23 + sprite v0 v1 8 + scroll-up 12 + + i := scrolling-arrow-down + v0 := 33 + v1 := 0 + sprite v0 v1 8 + scroll-down 6 + + i := scrolling-pixel + v0 := 21 # X + v1 := 26 # Y + v2 := 15 + v3 := 4 + loop + sprite v0 v1 1 + sprite v0 v2 1 + sprite v0 v3 1 + v0 += 1 + if v0 != 42 then + again + v0 := 20 # X + v1 := 31 + v2 := 42 + v3 := 5 # Y + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v2 v3 1 + v3 += 1 + if v3 != 26 then + again + + v0 := key + waitKeyRelease + jump main + + +: scrolling-xochip-hires + clear + hires + + i := scrolling-arrow-left + v0 := 77 + v1 := 46 + sprite v0 v1 8 + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + scroll-left + + i := scrolling-arrow-right + v0 := 42 + v1 := 35 + sprite v0 v1 8 + scroll-right + scroll-right + scroll-right + + i := scrolling-arrow-up + v0 := 54 + v1 := 46 + sprite v0 v1 8 + scroll-up 12 + scroll-up 12 + + i := scrolling-arrow-down + v0 := 65 + v1 := 11 + sprite v0 v1 8 + scroll-down 12 + + i := scrolling-pixel + v0 := 53 # X + v1 := 21 # Y + v2 := 32 + v3 := 43 + loop + sprite v0 v1 1 + sprite v0 v2 1 + sprite v0 v3 1 + v0 += 1 + if v0 != 74 then + again + v0 := 52 # X + v1 := 63 + v2 := 74 + v3 := 22 # Y + loop + sprite v0 v3 1 + sprite v1 v3 1 + sprite v2 v3 1 + v3 += 1 + if v3 != 43 then + again + + v0 := key + waitKeyRelease + jump main + + + +:segment data + + +: scrolling-menu-1 + 8 13 :pointer scrolling-superchip + 8 18 :pointer scrolling-xochip + +: scrolling-choose-1 + str "PICK PLATFORM" 0 +: scrolling-str-superchip + str "1 SUPERCHIP" 0 +: scrolling-str-xochip + str "2 XO-CHIP" 0 + +: scrolling-menu-2 + 11 13 :pointer scrolling-lores + 11 18 :pointer scrolling-hires + +: scrolling-choose-2 + str "PICK SCR SIZE" 0 +: scrolling-str-lores + str "1 LOW RES" 0 +: scrolling-str-hires + str "2 HIGH RES" 0 + +: scrolling-menu-3 + 14 13 :pointer scrolling-superchip-lores-full-pixels + 14 18 :pointer scrolling-superchip-lores-half-pixels + +: scrolling-choose-3 + str "PICK TARGET" 0 +: scrolling-str-modern + str "1 MODERN" 0 +: scrolling-str-legacy + str "2 LEGACY" 0 + + + +: scrolling-arrow-left + 0b11111111 + 0b11100111 + 0b11001111 + 0b10000001 + 0b10000001 + 0b11001111 + 0b11100111 + 0b11111111 + +: scrolling-arrow-right + 0b11111111 + 0b11100111 + 0b11110011 + 0b10000001 + 0b10000001 + 0b11110011 + 0b11100111 + 0b11111111 + +: scrolling-arrow-up + 0b11111111 + 0b11100111 + 0b11000011 + 0b10000001 + 0b10100101 + 0b11100111 + 0b11100111 + 0b11111111 + +: scrolling-arrow-down + 0b11111111 + 0b11100111 + 0b11100111 + 0b10100101 + 0b10000001 + 0b11000011 + 0b11100111 + 0b11111111 + +: scrolling-pixel + 0b10000000 + +: selection + 0 + +:include "../../pictures/version.png" +