├── .gitignore ├── src ├── roms │ ├── dvd.rom │ ├── piano.rom │ ├── catclock.rom │ ├── cube3d.rom │ ├── screen.rom │ ├── hello-line.rom │ ├── hello-pong.rom │ ├── mandelbrot.rom │ ├── hello-screen.rom │ ├── hello-animation.rom │ ├── tests_extended.rom │ ├── hello-2bpp-sprite.rom │ └── hello-animated-sprite.rom ├── res │ ├── comic8x8_linear.png │ ├── build_date.asm │ └── dmg_palette_lookup_generated.asm ├── emu_tile │ ├── tile_functions.asm │ └── tile_devices.asm ├── emu_cli │ ├── cli_functions.asm │ └── cli_devices.asm ├── misc │ └── rand.asm ├── tools │ ├── pb8.py │ ├── pb16.py │ └── generate_dmg_palette_lookup.py ├── emu_varvara │ ├── varvara_functions.asm │ └── varvara_devices.asm ├── include │ └── defines.asm ├── unpb16.asm ├── sgb.asm ├── misc.asm ├── header.asm ├── intro.asm ├── vectors.asm └── uxn_instr.asm ├── .gitmodules ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── project.mk ├── README.md └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /obj/ 3 | /dep/ 4 | /res/ 5 | -------------------------------------------------------------------------------- /src/roms/dvd.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/dvd.rom -------------------------------------------------------------------------------- /src/roms/piano.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/piano.rom -------------------------------------------------------------------------------- /src/roms/catclock.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/catclock.rom -------------------------------------------------------------------------------- /src/roms/cube3d.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/cube3d.rom -------------------------------------------------------------------------------- /src/roms/screen.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/screen.rom -------------------------------------------------------------------------------- /src/roms/hello-line.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/hello-line.rom -------------------------------------------------------------------------------- /src/roms/hello-pong.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/hello-pong.rom -------------------------------------------------------------------------------- /src/roms/mandelbrot.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/mandelbrot.rom -------------------------------------------------------------------------------- /src/roms/hello-screen.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/hello-screen.rom -------------------------------------------------------------------------------- /src/res/comic8x8_linear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/res/comic8x8_linear.png -------------------------------------------------------------------------------- /src/roms/hello-animation.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/hello-animation.rom -------------------------------------------------------------------------------- /src/roms/tests_extended.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/tests_extended.rom -------------------------------------------------------------------------------- /src/roms/hello-2bpp-sprite.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/hello-2bpp-sprite.rom -------------------------------------------------------------------------------- /src/roms/hello-animated-sprite.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbsp/uxngb/HEAD/src/roms/hello-animated-sprite.rom -------------------------------------------------------------------------------- /src/res/build_date.asm: -------------------------------------------------------------------------------- 1 | 2 | SECTION "Build date", ROM0 3 | 4 | db "Built " 5 | BuildDate:: 6 | db __ISO_8601_UTC__ 7 | db 0 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/include/hardware.inc"] 2 | path = src/include/hardware.inc 3 | url = https://github.com/gbdev/hardware.inc.git 4 | [submodule "src/include/rgbds-structs"] 5 | path = src/include/rgbds-structs 6 | url = https://github.com/ISSOtm/rgbds-structs.git 7 | -------------------------------------------------------------------------------- /src/res/dmg_palette_lookup_generated.asm: -------------------------------------------------------------------------------- 1 | SECTION "DMG Palette Lookup", ROM0 2 | DMGPaletteLookup: 3 | db $03,$03,$03,$03,$03,$03,$03,$02,$02,$02,$02,$02,$01,$01,$01,$01 4 | db $03,$03,$03,$03,$02,$02,$02,$02,$02,$02,$01,$01,$01,$01,$01,$01 5 | db $03,$03,$02,$02,$02,$02,$02,$02,$01,$01,$01,$01,$01,$01,$00,$00 6 | db $02,$02,$02,$02,$02,$02,$01,$01,$01,$01,$01,$01,$00,$00,$00,$00 7 | -------------------------------------------------------------------------------- /src/emu_tile/tile_functions.asm: -------------------------------------------------------------------------------- 1 | 2 | include "defines.asm" 3 | 4 | SECTION "Tile Functions", ROM0 5 | 6 | ModeInit: 7 | ; Zero all tiles + tilemap (with the screen on, just to be super inefficient) 8 | ld hl, $8000 9 | ld bc, $9c00 - $8000 10 | xor a 11 | call LCDMemset 12 | 13 | ld [wOAMIndex], a 14 | 15 | ; Clear shadow OAM 16 | ld hl, wShadowOAM 17 | ld c, $a0 18 | xor a 19 | rst MemsetSmall 20 | 21 | ret 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "emulicious-debugger", 9 | "request": "launch", 10 | "name": "Launch in Emulicious", 11 | "program": "${workspaceFolder}/${command:AskForProgramName}", 12 | "port": 58870, 13 | "stopOnEntry": true 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Eldred Habert 4 | Copyright (c) 2022 Dave VanEe 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/emu_cli/cli_functions.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; CLI functions 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | 24 | SECTION "UXNCLI Functions", ROM0 25 | 26 | ModeInit: 27 | ; Load console font 28 | ld de, FontTiles 29 | ld hl, $8000 30 | ld bc, FontTiles.end - FontTiles 31 | call LCDMemcpy 32 | 33 | ; Setup console cursor 34 | ld a, HIGH($9800) 35 | ldh [hCursorAddr], a 36 | ld a, LOW($9800) 37 | ldh [hCursorAddr+1], a 38 | 39 | ; Clear console 40 | ld hl, $9800 41 | ld bc, $0400 42 | xor a 43 | call LCDMemset 44 | 45 | ret 46 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build", 8 | "type": "shell", 9 | "command": "make all", 10 | "problemMatcher": [], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | }, 16 | { 17 | "label": "Rebuild", 18 | "type": "shell", 19 | "command": "make rebuild", 20 | "problemMatcher": [], 21 | "group": { 22 | "kind": "build", 23 | "isDefault": true 24 | } 25 | }, 26 | { 27 | "label": "Run Emulicious", 28 | "type": "shell", 29 | "command": "make runEmulicious", 30 | "problemMatcher": [], 31 | "group": { 32 | "kind": "build", 33 | "isDefault": true 34 | } 35 | }, 36 | { 37 | "label": "Run BGB", 38 | "type": "shell", 39 | "command": "make runBGB", 40 | "problemMatcher": [], 41 | "group": { 42 | "kind": "build", 43 | "isDefault": true 44 | } 45 | }, 46 | { 47 | "label": "Run romusage", 48 | "type": "shell", 49 | "command": "make romusage", 50 | "problemMatcher": [], 51 | "group": { 52 | "kind": "build", 53 | "isDefault": true 54 | } 55 | } 56 | ] 57 | } -------------------------------------------------------------------------------- /project.mk: -------------------------------------------------------------------------------- 1 | # This file contains project configuration 2 | 3 | 4 | # Value that the ROM will be filled with 5 | PADVALUE := 0xFF 6 | 7 | ## Header constants (passed to RGBFIX) 8 | 9 | # ROM version (typically starting at 0 and incremented for each published version) 10 | VERSION := 0 11 | 12 | # 4-ASCII letter game ID 13 | GAMEID := _uxn 14 | 15 | # Game title, up to 11 ASCII chars 16 | TITLE := uxnemu 17 | 18 | # New licensee, 2 ASCII chars 19 | # Homebrew games FTW! 20 | LICENSEE := HB 21 | # Old licensee, please set to 0x33 (required to get SGB compatibility) 22 | OLDLIC := 0x33 23 | 24 | # MBC type, tells which hardware is in the cart 25 | # See https://gbdev.io/pandocs/#_0147-cartridge-type or consult any copy of Pan Docs 26 | # If using no MBC, consider enabling `-t` below 27 | MBC := 0x1A 28 | 29 | # ROM size is set automatically by RGBFIX 30 | 31 | # Size of the on-board SRAM; MBC type should indicate the presence of RAM 32 | # See https://gbdev.io/pandocs/#_0149-ram-size or consult any copy of Pan Docs 33 | # Set this to 0 when using MBC2's built-in SRAM 34 | SRAMSIZE := 0x02 35 | 36 | # ROM name 37 | ROMNAME := uxnemu 38 | ROMEXT := gbc 39 | 40 | 41 | # Compilation parameters, uncomment to apply, comment to cancel 42 | # "Sensible defaults" are included 43 | 44 | # Disable automatic `nop` after `halt` 45 | ASFLAGS += -h 46 | 47 | # Export all labels 48 | # This means they must all have unique names, but they will all show up in the .sym and .map files 49 | # ASFLAGS += -E 50 | 51 | # Game Boy Color compatible 52 | FIXFLAGS += -c 53 | # Game Boy Color required 54 | # FIXFLAGS += -C 55 | 56 | # Super Game Boy compatible 57 | # FIXFLAGS += -s 58 | 59 | # Game Boy mode 60 | LDFLAGS += -d 61 | 62 | # No banked WRAM mode 63 | # LDFLAGS += -w 64 | 65 | # 32k mode 66 | # LDFLAGS += -t 67 | -------------------------------------------------------------------------------- /src/misc/rand.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Pseudorandom number generator 3 | ; 4 | ; Copyright 2018 Damian Yerrick 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | section "rand_ram",WRAM0 23 | randstate: ds 4 24 | 25 | section "rand",ROM0 26 | 27 | ;; 28 | ; Generates a pseudorandom 16-bit integer in BC 29 | ; using the LCG formula from cc65 rand(): 30 | ; x[i + 1] = x[i] * 0x01010101 + 0x31415927 31 | ; @return A=B=state bits 31-24 (which have the best entropy), 32 | ; C=state bits 23-16, DHL trashed 33 | rand:: 34 | ; Load bits 31-8 of the current value to BCA 35 | ld hl,randstate+3 36 | ld a,[hl-] 37 | ld b,a 38 | ld a,[hl-] 39 | ld c,a 40 | ld a,[hl-] ; skip D; thanks ISSOtm for the idea 41 | ; Used to load bits 7-0 to E. Reading [HL] each time turned out 42 | ; no slower and saved 1 byte. 43 | 44 | ; Multiply by 0x01010101 45 | add [hl] 46 | ld d,a 47 | adc c 48 | ld c,a 49 | adc b 50 | ld b,a 51 | 52 | ; Add 0x31415927 and write back 53 | ld a,[hl] 54 | add $27 55 | ld [hl+],a 56 | ld a,d 57 | adc $59 58 | ld [hl+],a 59 | ld a,c 60 | adc $41 61 | ld [hl+],a 62 | ld c,a 63 | ld a,b 64 | adc $31 65 | ld [hl],a 66 | ld b,a 67 | ret 68 | -------------------------------------------------------------------------------- /src/tools/pb8.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | PB8 encoder 4 | Copyright 2019 Damian Yerrick 5 | [License: zlib] 6 | 7 | PB8 can be thought of as either of two things: 8 | 9 | - Run-length encoding (RLE) of a stream, with unary-coded run lengths 10 | - LZSS with a fixed distance of 1 and a fixed copy length of 1 11 | 12 | Each packet represents 8 bytes of uncompressed data. The bits 13 | of the first byte in a packet, ordered from MSB to LSB, encode 14 | which of the following eight bytes repeat the previous byte. 15 | A 0 means a literal byte follows; a 1 means a repeat. 16 | """ 17 | import itertools 18 | import sys 19 | import argparse 20 | 21 | def ichunk(data, count): 22 | """Turn an iterable into lists of a fixed length.""" 23 | data = iter(data) 24 | while True: 25 | b = list(itertools.islice(data, count)) 26 | if len(b) == 0: break 27 | yield b 28 | 29 | def pb8(data): 30 | """Compress an iterable of bytes into a generator of PB8 packets.""" 31 | prev = 0 32 | for unco in ichunk(data, 8): 33 | # Pad the chunk to a multiple of 8 bytes 34 | if len(unco) < 8: 35 | unco.extend(unco[-1:]*(8 - len(unco))) 36 | 37 | packet = bytearray(1) 38 | for i, value in enumerate(unco): 39 | if value == prev: 40 | packet[0] |= 0x80 >> i 41 | else: 42 | packet.append(value) 43 | prev = value 44 | yield packet 45 | 46 | def parse_argv(argv): 47 | p = argparse.ArgumentParser() 48 | p.add_argument("infile") 49 | p.add_argument("outfile") 50 | return p.parse_args(argv[1:]) 51 | 52 | def main(argv=None): 53 | args = parse_argv(argv or sys.argv) 54 | with open(args.infile, "rb") as infp: 55 | data = infp.read() 56 | with open(args.outfile, "wb") as outfp: 57 | outfp.writelines(pb8(data)) 58 | 59 | def test(): 60 | tests = [ 61 | () 62 | ] 63 | s = b"ABAHBHCHCECEFEFE" 64 | print(b''.join(pb8(s)).hex()) 65 | 66 | if __name__=='__main__': 67 | main() 68 | ## test() 69 | -------------------------------------------------------------------------------- /src/tools/pb16.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | PB16 encoder 4 | Copyright 2018 Damian Yerrick 5 | [License: zlib] 6 | 7 | PB16 can be thought of as either of two things: 8 | 9 | - Run-length encoding (RLE) of two interleaved streams, with 10 | unary-coded run lengths 11 | - LZSS with a fixed distance of 2 and a fixed copy length of 1 12 | 13 | Each packet represents 8 bytes of uncompressed data. The bits 14 | of the first byte in a packet, ordered from MSB to LSB, encode 15 | which of the following eight bytes repeat the byte 2 bytes back. 16 | A 0 means a literal byte follows; a 1 means a repeat. 17 | 18 | It's similar to the PB8 codec that I've used for NES CHR data, 19 | adapted to the interleaving of Game Boy and Super NES CHR data. 20 | """ 21 | import itertools 22 | import sys 23 | import argparse 24 | 25 | def ichunk(data, count): 26 | """Turn an iterable into lists of a fixed length.""" 27 | data = iter(data) 28 | while True: 29 | b = list(itertools.islice(data, count)) 30 | if len(b) == 0: break 31 | yield b 32 | 33 | def pb16(data): 34 | """Compress an iterable of bytes into a generator of PB16 packets.""" 35 | prev = [0, 0] 36 | for unco in ichunk(data, 8): 37 | # Pad the chunk to a multiple of 2 then to 8 bytes 38 | if len(unco) < 8: 39 | if len(unco) == 1: 40 | unco.append(prev[1]) 41 | elif len(unco) % 2: 42 | unco.append(unco[-2]) 43 | unco.extend(unco[-2:]*(8 - len(unco))) 44 | 45 | packet = bytearray(1) 46 | for i, value in enumerate(unco): 47 | if value == prev[i % 2]: 48 | packet[0] |= 0x80 >> i 49 | else: 50 | packet.append(value) 51 | prev[i % 2] = value 52 | yield packet 53 | 54 | def parse_argv(argv): 55 | p = argparse.ArgumentParser() 56 | p.add_argument("infile") 57 | p.add_argument("outfile") 58 | return p.parse_args(argv[1:]) 59 | 60 | def main(argv=None): 61 | args = parse_argv(argv or sys.argv) 62 | with open(args.infile, "rb") as infp: 63 | data = infp.read() 64 | with open(args.outfile, "wb") as outfp: 65 | outfp.writelines(pb16(data)) 66 | 67 | def test(): 68 | tests = [ 69 | () 70 | ] 71 | s = b"ABAHBHCHCECEFEFE" 72 | print(b''.join(pb16(s)).hex()) 73 | 74 | if __name__=='__main__': 75 | main() 76 | ## test() 77 | -------------------------------------------------------------------------------- /src/emu_varvara/varvara_functions.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Varvara functions 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | include "defines.asm" 24 | 25 | SECTION "Varvara Functions", ROM0 26 | 27 | ModeInit: 28 | ; Zero all tiles (with the screen on, just to be super inefficient) 29 | ld hl, $8000 30 | ld bc, $9800 - $8000 31 | xor a 32 | call LCDMemset 33 | 34 | ld [wOAMIndex], a 35 | 36 | ; Clear foreground object source address table 37 | ld hl, wObjSourceAddrs 38 | ld c, wObjSourceAddrs.end - wObjSourceAddrs + 1 ; also clear trailing index 39 | rst MemsetSmall 40 | 41 | ; Setup static tilemap for background 'layer' 42 | ld hl, $9800 43 | ld de, SCRN_VX_B - SCRN_X_B 44 | xor a 45 | ld c, 12 46 | call TilemapIncFill 47 | 48 | ; second chunk (after scanline tile bank swap) 49 | xor a 50 | ld c, SCRN_Y_B - 12 51 | call TilemapIncFill 52 | 53 | ; setup mid-screen tile bank swap interrupt 54 | ld a, STATF_LYC 55 | ldh [rSTAT], a 56 | ld a, 96 ; switch background tiles on line 96 57 | ldh [rLYC], a 58 | 59 | ; Clear shadow OAM for foreground 'layer' 60 | ld hl, wShadowOAM 61 | ld c, $A0 62 | xor a 63 | rst MemsetSmall 64 | 65 | ret 66 | 67 | TilemapIncFill: 68 | .y 69 | ld b, 10 70 | .x 71 | push af 72 | wait_vram 73 | pop af 74 | ld [hli], a ; two safe bytes per STAT check 75 | inc a 76 | ld [hli], a 77 | inc a 78 | dec b 79 | jr nz, .x 80 | add hl, de 81 | dec c 82 | jr nz, .y 83 | ret 84 | 85 | -------------------------------------------------------------------------------- /src/include/defines.asm: -------------------------------------------------------------------------------- 1 | 2 | ; First, let's include libraries 3 | 4 | INCLUDE "hardware.inc/hardware.inc" 5 | rev_Check_hardware_inc 3.0 6 | 7 | INCLUDE "rgbds-structs/structs.asm" 8 | 9 | 10 | ; A couple more hardware defines 11 | 12 | NB_SPRITES equ 40 13 | 14 | 15 | ; I generally discourage the use of pseudo-instructions for a variety of reasons, 16 | ; but this one includes a label, and manually giving them different names is tedious. 17 | wait_vram: MACRO 18 | .waitVRAM\@ 19 | ldh a, [rSTAT] 20 | and STATF_BUSY 21 | jr nz, .waitVRAM\@ 22 | ENDM 23 | 24 | ; `ld b, X` followed by `ld c, Y` is wasteful (same with other reg pairs). 25 | ; This writes to both halves of the pair at once, without sacrificing readability 26 | ; Example usage: `lb bc, X, Y` 27 | lb: MACRO 28 | assert -128 <= (\2) && (\2) <= 255, "Second argument to `lb` must be 8-bit!" 29 | assert -128 <= (\3) && (\3) <= 255, "Third argument to `lb` must be 8-bit!" 30 | ld \1, (LOW(\2) << 8) | LOW(\3) 31 | ENDM 32 | 33 | 34 | ; SGB packet types 35 | RSRESET 36 | PAL01 rb 1 37 | PAL23 rb 1 38 | PAL12 rb 1 39 | PAL03 rb 1 40 | ATTR_BLK rb 1 41 | ATTR_LIN rb 1 42 | ATTR_DIV rb 1 43 | ATTR_CHR rb 1 44 | SOUND rb 1 ; $08 45 | SOU_TRN rb 1 46 | PAL_SET rb 1 47 | PAL_TRN rb 1 48 | ATRC_EN rb 1 49 | TEST_EN rb 1 50 | ICON_EN rb 1 51 | DATA_SND rb 1 52 | DATA_TRN rb 1 ; $10 53 | MLT_REQ rb 1 54 | JUMP rb 1 55 | CHR_TRN rb 1 56 | PCT_TRN rb 1 57 | ATTR_TRN rb 1 58 | ATTR_SET rb 1 59 | MASK_EN rb 1 60 | OBJ_TRN rb 1 ; $18 61 | PAL_PRI rb 1 62 | 63 | SGB_PACKET_SIZE equ 16 64 | 65 | ; sgb_packet packet_type, nb_packets, data... 66 | sgb_packet: MACRO 67 | PACKET_SIZE equ _NARG - 1 ; Size of what's below 68 | db (\1 << 3) | (\2) 69 | REPT _NARG - 2 70 | SHIFT 71 | db \2 72 | ENDR 73 | 74 | ds SGB_PACKET_SIZE - PACKET_SIZE, 0 75 | ENDM 76 | 77 | 78 | ; 64 bytes, should be sufficient for most purposes. If you're really starved on 79 | ; check your stack usage and consider setting this to 32 instead. 16 is probably not enough. 80 | STACK_SIZE equ $40 81 | 82 | 83 | ; Use this to cause a crash. 84 | ; I don't recommend using this unless you want a condition: 85 | ; `call cc, Crash` is 3 bytes (`cc` being a condition); `error cc` is only 2 bytes 86 | ; This should help minimize the impact of error checking 87 | error: MACRO 88 | IF _NARG == 0 89 | rst Crash 90 | ELSE 91 | assert Crash == $0038 92 | ; This assembles to XX FF (with XX being the `jr` instruction) 93 | ; If the condition is fulfilled, this jumps to the operand: $FF 94 | ; $FF encodes the instruction `rst $38`! 95 | jr \1, @+1 96 | ENDC 97 | ENDM 98 | -------------------------------------------------------------------------------- /src/tools/generate_dmg_palette_lookup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 4 | # DMG Palette Lookup Generator 5 | # 6 | # Copyright 2022 Dave VanEe 7 | # 8 | # This software is provided 'as-is', without any express or implied 9 | # warranty. In no event will the authors be held liable for any damages 10 | # arising from the use of this software. 11 | # 12 | # Permission is granted to anyone to use this software for any purpose, 13 | # including commercial applications, and to alter it and redistribute it 14 | # freely, subject to the following restrictions: 15 | # 16 | # 1. The origin of this software must not be misrepresented; you must not 17 | # claim that you wrote the original software. If you use this software 18 | # in a product, an acknowledgment in the product documentation would be 19 | # appreciated but is not required. 20 | # 2. Altered source versions must be plainly marked as such, and must not be 21 | # misrepresented as being the original software. 22 | # 3. This notice may not be removed or altered from any source distribution. 23 | # 24 | 25 | import math 26 | 27 | output = 'res/dmg_palette_lookup_generated.asm' 28 | 29 | def chunks(lst, n): 30 | """Yield successive n-sized chunks from lst.""" 31 | for i in range(0, len(lst), n): 32 | yield lst[i:i + n] 33 | 34 | with open(output, 'w') as f: 35 | VALUES = 64 36 | 37 | f.write('SECTION "DMG Palette Lookup", ROM0\n') 38 | f.write('DMGPaletteLookup:\n') 39 | 40 | lookupValues = [] 41 | for value in range(VALUES): 42 | # Break out into RGB components 43 | red = (value & 0b00110000) << 2 44 | green = (value & 0b00001100) << 4 45 | blue = (value & 0b00000011) << 6 46 | 47 | intensity = (0.2989*red + 0.5870*green + 0.1140*blue) 48 | 49 | # Scale intensity back up because we're only using values from 0-191, 50 | # and if we don't the 2bit intensity won't be full range. 51 | #intensity = intensity * 256./192. 52 | 53 | # Further scale and then cap to avoid light colors not showing up 54 | #intensity = min(intensity * 0.8, 255) 55 | 56 | intensity = min(intensity * 1.2, 255) 57 | 58 | # Invert because of how DMG palette values work 59 | intensity = 255 - intensity 60 | 61 | lookupValues.append(int(intensity) >> 6) 62 | 63 | print(len(lookupValues)) 64 | for chunk in chunks(lookupValues, 16): 65 | f.write(' db {}\n'.format(','.join(['${:02X}'.format(0xFF & int(item)) for item in chunk]))) 66 | print(' db {}'.format(','.join(['${:02X}'.format(0xFF & int(item)) for item in chunk]))) 67 | -------------------------------------------------------------------------------- /src/unpb16.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; PB16 decompression for Game Boy 3 | ; Copyright 2018 Damian Yerrick 4 | ; 5 | ; This software is provided 'as-is', without any express or implied 6 | ; warranty. In no event will the authors be held liable for any damages 7 | ; arising from the use of this software. 8 | ; 9 | ; Permission is granted to anyone to use this software for any purpose, 10 | ; including commercial applications, and to alter it and redistribute it 11 | ; freely, subject to the following restrictions: 12 | ; 13 | ; 1. The origin of this software must not be misrepresented; you must not 14 | ; claim that you wrote the original software. If you use this software 15 | ; in a product, an acknowledgment in the product documentation would be 16 | ; appreciated but is not required. 17 | ; 2. Altered source versions must be plainly marked as such, and must not be 18 | ; misrepresented as being the original software. 19 | ; 3. This notice may not be removed or altered from any source distribution. 20 | ; 21 | ; This software has been modified by Eldred Habert (ISSOtm): 22 | ; - Removal of .inc file 23 | ; - Different variable allocation 24 | 25 | 26 | section "pb16 temp byte", HRAM 27 | 28 | pb16_byte0: db 29 | 30 | section "pb16", ROM0 31 | 32 | ; The PB16 format is a starting point toward efficient RLE image 33 | ; codecs on Game Boy and Super NES. 34 | ; 35 | ; 0: Load a literal byte 36 | ; 1: Repeat from 2 bytes ago 37 | 38 | pb16_unpack_packet: 39 | ; Read first bit of control byte. Treat B as a ring counter with 40 | ; a 1 bit as the sentinel. Once the 1 bit reaches carry, B will 41 | ; become 0, meaning the 8-byte packet is complete. 42 | ld a,[de] 43 | inc de 44 | scf 45 | rla 46 | ld b,a 47 | .byteloop: 48 | ; If the bit from the control byte is clear, plane 0 is is literal 49 | jr nc,.p0_is_literal 50 | ldh a,[pb16_byte0] 51 | jr .have_p0 52 | .p0_is_literal: 53 | ld a,[de] 54 | inc de 55 | ldh [pb16_byte0],a 56 | .have_p0: 57 | ld [hl+],a 58 | 59 | ; Read next bit. If it's clear, plane 1 is is literal. 60 | ld a,c 61 | sla b 62 | jr c,.have_p1 63 | .p1_is_copy: 64 | ld a,[de] 65 | inc de 66 | ld c,a 67 | .have_p1: 68 | ld [hl+],a 69 | 70 | ; Read next bit of control byte 71 | sla b 72 | jr nz,.byteloop 73 | ret 74 | 75 | ;; 76 | ; Unpacks 2*B packets from DE to HL, producing 8 bytes per packet. 77 | ; About 127 cycles (2 scanlines) per 8-byte packet; filling CHR RAM 78 | ; thus takes (6144/8)*127 = about 97536 cycles or 93 ms 79 | pb16_unpack_block:: 80 | ; Prefill with zeroes 81 | xor a 82 | ldh [pb16_byte0],a 83 | ld c,a 84 | .packetloop: 85 | push bc 86 | call pb16_unpack_packet 87 | call pb16_unpack_packet 88 | ld a,c 89 | pop bc 90 | ld c,a 91 | dec b 92 | jr nz,.packetloop 93 | ret 94 | -------------------------------------------------------------------------------- /src/sgb.asm: -------------------------------------------------------------------------------- 1 | 2 | INCLUDE "defines.asm" 3 | 4 | SECTION "SGB routines", ROM0 5 | 6 | ; Sends SGB packets to the SGB, assuming we're running on one. 7 | ; @param hl Pointer to the packet data to be sent (can send any number of packets btw) 8 | ; @return hl Points to the end of the packet data 9 | ; @return de Zero 10 | ; @return bc Zero 11 | ; @return a Zero 12 | SendPackets:: 13 | ld a, [hl] ; Length 14 | and %111 15 | ret z 16 | ld c, a 17 | 18 | .sendPacket 19 | call SendPacketNoDelay 20 | call SGBDelay ; Let the ICD chip rest a bit 21 | dec c 22 | jr nz, .sendPacket 23 | ret 24 | 25 | 26 | ; Sends a SGB packet to the SGB to freeze the screen, assuming we're running on one. 27 | ; Does not perform any delay after sending the packet. 28 | ; Use only if you're not going to send another SGB packet in the next few frames. 29 | ; You're likely to perform some decompression or smth after this 30 | ; @param hl Pointer to the packet data to be sent. 31 | ; @return hl Points to the end of the packet data 32 | ; @return b Zero 33 | ; @return d Zero 34 | ; @return a $30 35 | ; @destroy e 36 | FreezeSGBScreen:: 37 | ld hl, FreezeScreenPacket 38 | ; Sends a SGB packet to the SGB, assuming it's running on one. 39 | ; Does not perform any delay after sending the packet. 40 | ; Unsuitable to send multi-packet packets. 41 | ; Use only if you're not going to send another SGB packet in the next four frames. 42 | ; Assumes the joypad won't be polled by interrupts during this time, but since the VBlank handler doesn't poll except when waited for, this is fine. 43 | ; @param hl Pointer to the packet data to be sent. 44 | ; @return hl Points to the end of the packet data 45 | ; @return b Zero 46 | ; @return d Zero 47 | ; @return a $30 48 | ; @destroy e 49 | SendPacketNoDelay:: 50 | ; Packet transmission begins by sending $00 then $30 51 | xor a 52 | ldh [rP1], a 53 | ld a, $30 54 | ldh [rP1], a 55 | 56 | ld b, SGB_PACKET_SIZE 57 | .sendByte 58 | ld d, 8 ; 8 bits in a byte 59 | ld a, [hli] ; Read byte to send 60 | ld e, a 61 | 62 | .sendBit 63 | ld a, $10 ; 1 bits are sent with $10 64 | rr e ; Rotate d and get its lower bit, two birds in one stone! 65 | jr c, .bitSet 66 | add a, a ; 0 bits are sent with $20 67 | .bitSet 68 | ldh [rP1], a 69 | ld a, $30 ; Terminate pulse 70 | ldh [rP1], a 71 | dec d 72 | jr nz, .sendBit 73 | 74 | dec b 75 | jr nz, .sendByte 76 | 77 | ; Packets are terminated by a "STOP" 0 bit 78 | ld a, $20 79 | ldh [rP1], a 80 | ld a, $30 81 | ldh [rP1], a 82 | ret 83 | 84 | SGBDelay:: 85 | ld de, 7000 ; Magic value, apparently 86 | .loop 87 | nop 88 | nop 89 | nop 90 | dec de 91 | ld a, d 92 | or e 93 | jr nz, .loop 94 | ret 95 | 96 | FreezeScreenPacket: 97 | sgb_packet MASK_EN, 1, 1 98 | 99 | 100 | ; Fill the $9C00 tilemap with a pattern suitable for SGB _TRN 101 | ; Also sets up the rendering parameters for the transfer 102 | ; Finally, assumes the LCD is **off** 103 | ; @return hl 104 | FillScreenWithSGBMap:: 105 | xor a 106 | ldh [hSCY], a 107 | ldh [hSCX], a 108 | ld b, a ; ld b, 0 109 | ld hl, $9C00 110 | .writeRow 111 | ld c, SCRN_X_B 112 | .writeTile 113 | ld a, b 114 | ld [hli], a 115 | inc b 116 | jr z, .done 117 | dec c 118 | jr nz, .writeTile 119 | ld a, l 120 | add a, SCRN_VX_B - SCRN_X_B 121 | ld l, a 122 | jr nc, .writeRow 123 | inc h 124 | jr .writeRow 125 | .done 126 | ld a, %11100100 127 | ldh [hBGP], a 128 | SetupSGBLCDC:: 129 | ld a, LCDCF_ON | LCDCF_WINOFF | LCDCF_BG8000 | LCDCF_BG9C00 | LCDCF_OBJOFF | LCDCF_BGON 130 | ldh [rLCDC], a 131 | ldh [hLCDC], a 132 | ret 133 | -------------------------------------------------------------------------------- /src/emu_cli/cli_devices.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; CLI Devices 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | SECTION "Device Handlers", ROM0, ALIGN[7] 24 | DeviceHandlers:: 25 | dw DevSystemDEI, DevSystemDEI2, DevSystemDEO, DevSystemDEO2 ; system 26 | dw DevNil, DevNil, DevConsoleDEO, DevConsoleDEO2 ; console 27 | dw DevNil, DevNil, DevNil, DevNil ; empty 28 | dw DevNil, DevNil, DevNil, DevNil ; empty 29 | dw DevNil, DevNil, DevNil, DevNil ; empty 30 | dw DevNil, DevNil, DevNil, DevNil ; empty 31 | dw DevNil, DevNil, DevNil, DevNil ; empty 32 | dw DevNil, DevNil, DevNil, DevNil ; empty 33 | dw DevNil, DevNil, DevNil, DevNil ; empty 34 | dw DevNil, DevNil, DevNil, DevNil ; empty 35 | dw DevNil, DevNil, DevNil, DevNil ; empty (file0) 36 | dw DevNil, DevNil, DevNil, DevNil ; empty (file1) 37 | dw DevNil, DevNil, DevNil, DevNil ; empty (datetime) 38 | dw DevNil, DevNil, DevNil, DevNil ; empty 39 | dw DevNil, DevNil, DevNil, DevNil ; empty 40 | dw DevNil, DevNil, DevNil, DevNil ; empty 41 | 42 | SECTION "UXNCLI Device Defaults", ROM0 43 | DeviceDefaults:: 44 | ds 256, 0 45 | 46 | SECTION "UXNCLI WRAM", HRAM 47 | hCursorAddr:: ds 2 48 | 49 | SECTION "Font Tiles", ROM0 50 | FontTiles: 51 | incbin "res/comic8x8_linear.2bpp" 52 | .end 53 | 54 | SECTION "UXNCLI Vectors", ROM0 55 | 56 | VectorHandlers:: 57 | ret 58 | 59 | SECTION "UXNCLI Devices", ROM0 60 | 61 | DevSystemDEI:: 62 | ret 63 | 64 | DevSystemDEI2:: 65 | ret 66 | 67 | ; d = device 68 | ; b = data 69 | DevSystemDEO:: 70 | ; TODO: "Special handling" (set wst/rst?) 71 | ret 72 | 73 | ; d = device 74 | ; bc = data 75 | DevSystemDEO2:: 76 | ret 77 | 78 | ; d = device 79 | ; b = data 80 | DevConsoleDEO:: 81 | ld a, d 82 | cp $18 83 | jr nz, .notWrite 84 | 85 | ; write character 86 | ldh a, [hCursorAddr] 87 | ld h, a 88 | ldh a, [hCursorAddr+1] 89 | ld l, a 90 | 91 | ; special characters 92 | ld a, b ; value of char 93 | cp $0a ; newline 94 | jr nz, :+ 95 | ld a, l 96 | and %11100000 ; mask off fractional portion 97 | ld l, a 98 | ld de, $0020 99 | add hl, de 100 | jr .storeCursor 101 | : 102 | 103 | sub $20 ; ASCII offset 104 | ld c, 1 ; char count 105 | call LCDMemsetSmall 106 | 107 | .storeCursor 108 | ld a, h 109 | ldh [hCursorAddr], a 110 | ld a, l 111 | ldh [hCursorAddr+1], a 112 | .notWrite 113 | ret 114 | 115 | ; d = device 116 | ; bc = data 117 | DevConsoleDEO2:: 118 | ; TODO: write to console 119 | ret 120 | 121 | DevNil:: 122 | ret -------------------------------------------------------------------------------- /src/misc.asm: -------------------------------------------------------------------------------- 1 | 2 | INCLUDE "defines.asm" 3 | 4 | SECTION "LCDMemsetSmallFromB", ROM0 5 | 6 | ; Writes a value to all bytes in an area of memory 7 | ; Works when the destination is in VRAM, even while the LCD is on 8 | ; @param hl Beginning of area to fill 9 | ; @param c Amount of bytes to write (0 causes 256 bytes to be written) 10 | ; @param a Value to write 11 | ; @return c 0 12 | ; @return hl Pointer to the byte after the last written one 13 | ; @return b Equal to a 14 | ; @return f Z set, C reset 15 | LCDMemsetSmall:: 16 | ld b, a 17 | ; Writes a value to all bytes in an area of memory 18 | ; Works when the destination is in VRAM, even while the LCD is on 19 | ; Protip: you may want to use `lb bc,` to set both B and C at the same time 20 | ; @param hl Beginning of area to fill 21 | ; @param c Amount of bytes to write (0 causes 256 bytes to be written) 22 | ; @param b Value to write 23 | ; @return c 0 24 | ; @return hl Pointer to the byte after the last written one 25 | ; @return b Equal to a 26 | ; @return f Z set, C reset 27 | LCDMemsetSmallFromB:: 28 | wait_vram 29 | ld a, b 30 | ld [hli], a 31 | dec c 32 | jr nz, LCDMemsetSmallFromB 33 | ret 34 | 35 | SECTION "LCDMemset", ROM0 36 | 37 | ; Writes a value to all bytes in an area of memory 38 | ; Works when the destination is in VRAM, even while the LCD is on 39 | ; @param hl Beginning of area to fill 40 | ; @param bc Amount of bytes to write (0 causes 65536 bytes to be written) 41 | ; @param a Value to write 42 | ; @return bc 0 43 | ; @return hl Pointer to the byte after the last written one 44 | ; @return d Equal to parameter passed in a 45 | ; @return a 0 46 | ; @return f Z set, C reset 47 | LCDMemset:: 48 | ld d, a 49 | ; Writes a value to all bytes in an area of memory 50 | ; Works when the destination is in VRAM, even while the LCD is on 51 | ; @param hl Beginning of area to fill 52 | ; @param bc Amount of bytes to write (0 causes 65536 bytes to be written) 53 | ; @param d Value to write 54 | ; @return bc 0 55 | ; @return hl Pointer to the byte after the last written one 56 | ; @return a 0 57 | ; @return f Z set, C reset 58 | LCDMemsetFromD:: 59 | wait_vram 60 | ld a, d 61 | ld [hli], a 62 | dec bc 63 | ld a, b 64 | or c 65 | jr nz, LCDMemsetFromD 66 | ret 67 | 68 | SECTION "LCDMemcpySmall", ROM0 69 | 70 | ; Copies a block of memory somewhere else 71 | ; Works when the source or destination is in VRAM, even while the LCD is on 72 | ; @param de Pointer to beginning of block to copy 73 | ; @param hl Pointer to where to copy (bytes will be written from there onwards) 74 | ; @param c Amount of bytes to copy (0 causes 256 bytes to be copied) 75 | ; @return de Pointer to byte after last copied one 76 | ; @return hl Pointer to byte after last written one 77 | ; @return c 0 78 | ; @return a Last byte copied 79 | ; @return f Z set, C reset 80 | LCDMemcpySmall:: 81 | wait_vram 82 | ld a, [de] 83 | ld [hli], a 84 | inc de 85 | dec c 86 | jr nz, LCDMemcpySmall 87 | ret 88 | 89 | SECTION "LCDMemcpy", ROM0 90 | 91 | ; Copies a block of memory somewhere else 92 | ; Works when the source or destination is in VRAM, even while the LCD is on 93 | ; @param de Pointer to beginning of block to copy 94 | ; @param hl Pointer to where to copy (bytes will be written from there onwards) 95 | ; @param bc Amount of bytes to copy (0 causes 65536 bytes to be copied) 96 | ; @return de Pointer to byte after last copied one 97 | ; @return hl Pointer to byte after last written one 98 | ; @return bc 0 99 | ; @return a 0 100 | ; @return f Z set, C reset 101 | LCDMemcpy:: 102 | wait_vram 103 | ld a, [de] 104 | ld [hli], a 105 | inc de 106 | dec bc 107 | ld a, b 108 | or c 109 | jr nz, LCDMemcpy 110 | ret 111 | 112 | SECTION "Memcpy", ROM0 113 | 114 | ; Copies a block of memory somewhere else 115 | ; @param de Pointer to beginning of block to copy 116 | ; @param hl Pointer to where to copy (bytes will be written from there onwards) 117 | ; @param bc Amount of bytes to copy (0 causes 65536 bytes to be copied) 118 | ; @return de Pointer to byte after last copied one 119 | ; @return hl Pointer to byte after last written one 120 | ; @return bc 0 121 | ; @return a 0 122 | ; @return f Z set, C reset 123 | Memcpy:: 124 | ld a, [de] 125 | ld [hli], a 126 | inc de 127 | dec bc 128 | ld a, b 129 | or c 130 | jr nz, Memcpy 131 | ret 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uxngb 2 | 3 | A highly experimental port of the [uxn virtual machine](https://wiki.xxiivv.com/site/uxn.html) to the Game Boy and Game Boy Color gaming handheld. I knew this was a fairly ridiculous project from the start, but wanted to see how it might perform and was pleased to see [compudanza's pong tutorial](https://compudanzas.net/uxn_tutorial_day_6.html) run at slow-motion playable speeds. 4 | 5 | Some effort has gone into optimizing for performance. The MUL and DIV instructions are obviously very slow due to the lack of hardware support for those operations. They have been unrolled for speed, but additional optimizations are likely possible. In addition, as the Game Boy lacks a bitmap graphics mode, the background layer is used to mimic one for "background" pixel/sprite writes (which is slow), and the hardware objects (sprites) are used for foreground sprites (which leads to quite a few limitations). 6 | 7 | You can download a binary build [here](https://github.com/tbsp/uxngb/releases). Binaries with a variety of UXN ROMs appended are also available there. 8 | 9 | - `hello-*` UXN ROMs built using code from [compudanza's uxn tutorial series](https://compudanzas.net/uxn_tutorial.html), used under the [peer production license](https://wiki.p2pfoundation.net/Peer_Production_License). 10 | - All other UXN ROMs built (some with minor modifications to fit the GB screen) using code from [100 Rabbits](https://git.sr.ht/~rabbits/uxn/tree/main), copyright (c) Devine Lu Linvega under the [MIT license](https://opensource.org/licenses/MIT). 11 | 12 | ## Screenshots 13 | 14 | ![uxnemu_catclock](https://user-images.githubusercontent.com/10489588/176349457-68669912-c901-4946-8060-08c3a0110e2c.png) 15 | ![uxnemu_cube3d](https://user-images.githubusercontent.com/10489588/176349461-5277d505-db2a-4ffa-af0c-b0430a1ed340.png) 16 | ![uxnemu_hello-pong-1](https://user-images.githubusercontent.com/10489588/176349464-3727bd2c-67e9-4df0-92a6-a40ab6a4e89b.png) 17 | ![uxnemu_mandelbrot-1](https://user-images.githubusercontent.com/10489588/176349474-a9f18eaa-e7d2-4f73-9778-4a9923cf7ae8.png) 18 | ![uxnemu_mandelbrot](https://user-images.githubusercontent.com/10489588/176349469-3e76b4fc-2706-45e8-88cc-ae6aaf0a76fa.png) 19 | ![uxnemu_screen](https://user-images.githubusercontent.com/10489588/176349485-183ba2f7-24dc-4623-a9db-0d9f5aadd741.png) 20 | 21 | 22 | 23 | 24 | *Note: Changes to sprite tile/OAM cycling in 0.1.2 will yield slightly different results than the Screen.tal results shown here.* 25 | 26 | ## Performance 27 | 28 | The mandelbrot ROM currently takes ~1h24m to render fully on an original Game Boy, and ~42min on a Game Boy Color (using double-speed mode). 29 | 30 | ## Running your own ROMs 31 | 32 | The emulator is contained in the base `uxnemu.gbc` ROM, and the UXN ROM to be executed must be appended to it. To create a properly formed GB/GBC ROM, it will then need to be padded and have the header fixed, which can be performed using `rgbfix` from [RGBDS](https://rgbds.gbdev.io/). This is an example of combining the emulator with a ROM and fixing it from a Linux command prompt: 33 | ``` 34 | cat uxnemu.gbc dvd.rom > uxnemu_dvd.gbc 35 | rgbfix -O -v -p 0xFF -t dvd uxnemu_dvd.gbc 36 | ``` 37 | 38 | ## Implemented 39 | 40 | - All 253 uxn CPU instructions 41 | - Controller device 42 | - Screen device: 43 | - Background pixel drawing 44 | - Background sprite drawing 45 | - Auto byte for all supported drawing operations 46 | - Foreground sprites are limited to 16 unique tile/blend combinations, and will begin to overwrite old tiles once this is exceeded (sprites are flipped in hardware, so flip variations don't count toward this limit) 47 | - Foreground sprites are limited by the 10 sprites/line limit of the hardware (no attempt is made to overcome this via flickering) 48 | - Very basic Datetime device (fixed startup date/time, HH:MM:SS will advance) 49 | - Limited console output (only when built in CLI mode to run CPU instruction test ROM) 50 | 51 | ## Unsupported 52 | - UXN ROMs larger than ~8 KiB, or UXN memory beyond $2000 53 | - I originally intended to support the full 64 KiB memory space, using 8 banks of swapped external cartridge RAM, but as the performance I was going to actually end up with revealed itself that dropped in priority 54 | - Screen resizing (fixed at 160x144 pixels) 55 | - Setting the System RGB bytes has no effect on Game Boy 56 | - Foreground pixel drawing operations aren't currently supported 57 | - Certain blending combinations may not render correctly, and the opaque lookup is not applied 58 | - Stack over/underflow and divide-by-zero are not detected 59 | - No keyboard/mouse/audio/midi/file device support 60 | - No support for relocatable stacks (seen in uxn11) 61 | 62 | ## Tools Used for Development 63 | 64 | - RGBDS (https://rgbds.gbdev.io/) 65 | - Emulicious (https://emulicious.net/) 66 | - uxn32 (https://github.com/randrew/uxn32) 67 | - Visual Studio Code (https://code.visualstudio.com/) 68 | -------------------------------------------------------------------------------- /src/header.asm: -------------------------------------------------------------------------------- 1 | 2 | INCLUDE "defines.asm" 3 | 4 | 5 | SECTION "Header", ROM0[$100] 6 | 7 | ; This is your ROM's entry point 8 | ; You have 4 bytes of code to do... something 9 | sub $11 ; This helps check if we're on CGB more efficiently 10 | jr EntryPoint 11 | 12 | ; Make sure to allocate some space for the header, so no important 13 | ; code gets put there and later overwritten by RGBFIX. 14 | ; RGBFIX is designed to operate over a zero-filled header, so make 15 | ; sure to put zeros regardless of the padding value. (This feature 16 | ; was introduced in RGBDS 0.4.0, but the -MG etc flags were also 17 | ; introduced in that version.) 18 | ds $150 - @, 0 19 | 20 | EntryPoint: 21 | ldh [hConsoleType], a 22 | 23 | Reset:: 24 | di ; Disable interrupts while we set up 25 | 26 | ; Kill sound 27 | xor a 28 | ldh [rNR52], a 29 | 30 | ; Wait for VBlank and turn LCD off 31 | .waitVBlank 32 | ldh a, [rLY] 33 | cp SCRN_Y 34 | jr c, .waitVBlank 35 | xor a 36 | ldh [rLCDC], a 37 | ; Goal now: set up the minimum required to turn the LCD on again 38 | ; A big chunk of it is to make sure the VBlank handler doesn't crash 39 | 40 | ld sp, wStackBottom 41 | 42 | ld hl, OAMDMA 43 | lb bc, OAMDMA.end - OAMDMA, LOW(hOAMDMA) 44 | .copyOAMDMA 45 | ld a, [hli] 46 | ldh [c], a 47 | inc c 48 | dec b 49 | jr nz, .copyOAMDMA 50 | 51 | ldh a, [hConsoleType] 52 | or a 53 | jr z, .cgb 54 | 55 | ; Set Palettes 56 | ld a, %00011011 57 | ldh [rBGP], a 58 | ldh [rOBP0], a 59 | ldh [rOBP1], a 60 | 61 | jr .doneConsoleCheck 62 | .cgb 63 | ; Enable double-speed mode 64 | ; Note: hello-pong gets a stray missing tile in the background when double-speed mode is enabled 65 | ; -> Without double speed mode there's a single missing line instead (also seen in BGB), but only in CGB mode 66 | ld a, KEY1F_PREPARE 67 | ldh [rKEY1], a 68 | stop 69 | 70 | .doneConsoleCheck 71 | 72 | ; You will also need to reset your handlers' variables below 73 | ; I recommend reading through, understanding, and customizing this file 74 | ; in its entirety anyways. This whole file is the "global" game init, 75 | ; so it's strongly tied to your own game. 76 | ; I don't recommend clearing large amounts of RAM, nor to init things 77 | ; here that can be initialized later. 78 | 79 | ; Reset variables necessary for the VBlank handler to function correctly 80 | ; But only those for now 81 | xor a 82 | ldh [hVBlankFlag], a 83 | ldh [hOAMHigh], a 84 | ldh [hCanSoftReset], a 85 | ldh [hPalettePending], a 86 | dec a ; ld a, $FF 87 | ldh [hHeldKeys], a 88 | ldh [hPriorKeys], a 89 | 90 | ; Initialize frame counter (counts down 60 frames to measure a ~second) 91 | ld a, 60 92 | ldh [hFrameCounter], a 93 | 94 | ; Select wanted interrupts here 95 | ; You can also enable them later if you want 96 | ld a, IEF_VBLANK | IEF_LCDC 97 | ldh [rIE], a 98 | xor a 99 | ei ; Only takes effect after the following instruction 100 | ldh [rIF], a ; Clears "accumulated" interrupts 101 | 102 | ; Init shadow regs 103 | ; xor a 104 | ldh [hSCY], a 105 | ldh [hSCX], a 106 | ld a, LCDCF_ON | LCDCF_BGON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJON | LCDCF_OBJ8 107 | ldh [hLCDC], a 108 | ; And turn the LCD on! 109 | ldh [rLCDC], a 110 | 111 | ; Clear OAM, so it doesn't display garbage 112 | ; This will get committed to hardware OAM after the end of the first 113 | ; frame, but the hardware doesn't display it, so that's fine. 114 | ld hl, wShadowOAM 115 | ld c, NB_SPRITES * 4 116 | xor a 117 | rst MemsetSmall 118 | ld a, h ; ld a, HIGH(wShadowOAM) 119 | ldh [hOAMHigh], a 120 | 121 | ; `Intro`'s bank has already been loaded earlier 122 | jp Intro 123 | 124 | SECTION "OAM DMA routine", ROM0 125 | 126 | ; OAM DMA prevents access to most memory, but never HRAM. 127 | ; This routine starts an OAM DMA transfer, then waits for it to complete. 128 | ; It gets copied to HRAM and is called there from the VBlank handler 129 | OAMDMA: 130 | ldh [rDMA], a 131 | ld a, NB_SPRITES 132 | .wait 133 | dec a 134 | jr nz, .wait 135 | ret 136 | .end 137 | 138 | SECTION "Global vars", HRAM 139 | 140 | ; 0 if CGB (including DMG mode and GBA), non-zero for other models 141 | hConsoleType:: db 142 | 143 | SECTION "OAM DMA", HRAM 144 | 145 | hOAMDMA:: 146 | ds OAMDMA.end - OAMDMA 147 | 148 | 149 | ; Manually positioned to maximize WRAM space for Uxn 150 | SECTION UNION "Shadow OAM", WRAM0[$E000 - 1024] 151 | 152 | wShadowOAM:: 153 | ds NB_SPRITES * 4 154 | wOAMIndex:: ; Index of next free entry in OAM for dynamically generated objects 155 | ds 1 156 | wOAMEndIndex:: ; Index of last enty used in the previous frame 157 | ds 1 158 | 159 | ; This ensures that the stack is packed into a safe portion of WRAM 160 | SECTION "Stack", WRAM0[$E000 - 512 - 256 - STACK_SIZE] 161 | 162 | ds STACK_SIZE 163 | wStackBottom: 164 | 165 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | .SUFFIXES: 3 | 4 | ################################################ 5 | # # 6 | # CONSTANT DEFINITIONS # 7 | # # 8 | ################################################ 9 | 10 | # Directory constants 11 | SRCDIR := src 12 | BINDIR := bin 13 | OBJDIR := obj 14 | DEPDIR := dep 15 | RESDIR := res 16 | 17 | # Program constants 18 | ifneq ($(shell which rm),) 19 | # POSIX OSes 20 | RM_RF := rm -rf 21 | MKDIR_P := mkdir -p 22 | PY := 23 | filesize = echo 'NB_PB$2_BLOCKS equ (' `wc -c $1 | cut -d ' ' -f 1` ' + $2 - 1) / $2' 24 | else 25 | # Windows outside of a POSIX env (Cygwin, MSYS2, etc.) 26 | # We need Powershell to get any sort of decent functionality 27 | $(warning Powershell is required to get basic functionality) 28 | RM_RF := -del /q 29 | MKDIR_P := -mkdir 30 | PY := python 31 | filesize = powershell Write-Output $$('NB_PB$2_BLOCKS equ ' + [string] [int] (([IO.File]::ReadAllBytes('$1').Length + $2 - 1) / $2)) 32 | endif 33 | 34 | # Shortcut if you want to use a local copy of RGBDS 35 | RGBDS := ../rgbds/ 36 | RGBASM := $(RGBDS)rgbasm.exe 37 | RGBLINK := $(RGBDS)rgblink.exe 38 | RGBFIX := $(RGBDS)rgbfix.exe 39 | RGBGFX := $(RGBDS)rgbgfx.exe 40 | 41 | EMULATOR_EMULICIOUS = ../../Emulicious/Emulicious.exe 42 | EMULATOR_BGB = ../../bgb64/bgb64.exe 43 | 44 | ROMUSAGE := ../../Tools/romusage.exe 45 | 46 | ROM = $(BINDIR)/$(ROMNAME).$(ROMEXT) 47 | 48 | # Argument constants 49 | INCDIRS = $(SRCDIR)/ $(SRCDIR)/include/ 50 | WARNINGS = all extra 51 | ASFLAGS = $(addprefix -i,$(INCDIRS)) $(addprefix -W,$(WARNINGS)) 52 | LDFLAGS = -p $(PADVALUE) --nopad 53 | FIXFLAGS = -v -i "$(GAMEID)" -k "$(LICENSEE)" -l $(OLDLIC) -m $(MBC) -n $(VERSION) -r $(SRAMSIZE) -t $(TITLE) 54 | 55 | # The list of "root" ASM files that RGBASM will be invoked on 56 | SRCS = $(wildcard $(SRCDIR)/*.asm) 57 | INCDIRS = $(SRCDIR)/ $(SRCDIR)/include/ 58 | 59 | ## Project-specific configuration 60 | # Use this to override the above 61 | include project.mk 62 | 63 | # The list of assembled UXN roms which will be created 64 | UXNROMS = $(patsubst $(SRCDIR)/roms/%.rom,$(BINDIR)/$(ROMNAME)_%.$(ROMEXT),$(wildcard $(SRCDIR)/roms/*.rom)) 65 | 66 | ################################################ 67 | # # 68 | # TARGETS # 69 | # # 70 | ################################################ 71 | 72 | # `all` (Default target): build the ROM and all assembled ROMs 73 | all: $(ROM) $(UXNROMS) 74 | .PHONY: all 75 | 76 | # `clean`: Clean temp and bin files 77 | clean: 78 | $(RM_RF) $(BINDIR) 79 | $(RM_RF) $(OBJDIR) 80 | $(RM_RF) $(DEPDIR) 81 | $(RM_RF) $(RESDIR) 82 | .PHONY: clean 83 | 84 | # `rebuild`: Build everything from scratch 85 | # It's important to do these two in order if we're using more than one job 86 | rebuild: 87 | $(MAKE) clean 88 | $(MAKE) all 89 | .PHONY: rebuild 90 | 91 | runEmulicious: 92 | $(EMULATOR_EMULICIOUS) $(ROM) 93 | .PHONY: runEmulicious 94 | 95 | runBGB: 96 | $(EMULATOR_BGB) $(ROM) -watch 97 | .PHONY: runBGB 98 | 99 | romusage: 100 | $(ROMUSAGE) $(BINDIR)/$(ROMNAME).map -g 101 | .PHONY: romusage 102 | 103 | ################################################ 104 | # # 105 | # GIT SUBMODULES # 106 | # # 107 | ################################################ 108 | 109 | # By default, cloning the repo does not init submodules 110 | # If that happens, warn the user 111 | # Note that the real paths aren't used! 112 | # Since RGBASM fails to find the files, it outputs the raw paths, not the actual ones. 113 | hardware.inc/hardware.inc rgbds-structs/structs.asm: 114 | @echo 'hardware.inc is not present; have you initialized submodules?' 115 | @echo 'Run `git submodule update --init`, then `make clean`, then `make` again.' 116 | @echo 'Tip: to avoid this, use `git clone --recursive` next time!' 117 | @exit 1 118 | 119 | ################################################ 120 | # # 121 | # RESOURCE FILES # 122 | # # 123 | ################################################ 124 | 125 | # By default, asset recipes convert files in `res/` into other files in `res/` 126 | # This line causes assets not found in `res/` to be also looked for in `src/res/` 127 | # "Source" assets can thus be safely stored there without `make clean` removing them 128 | VPATH := $(SRCDIR) 129 | 130 | $(RESDIR)/%.1bpp: $(RESDIR)/%.png 131 | @$(MKDIR_P) $(@D) 132 | $(RGBGFX) -d 1 -o $@ $< 133 | 134 | $(RESDIR)/%_linear.2bpp: $(RESDIR)/%_linear.png 135 | @$(MKDIR_P) $(@D) 136 | $(RGBGFX) -d 2 -o $@ $< 137 | 138 | $(RESDIR)/%_map.2bpp $(RESDIR)/%_map.tilemap: $(RESDIR)/%_map.png 139 | @$(MKDIR_P) $(@D) 140 | $(RGBGFX) -u -T -d 2 -o $@ $< 141 | 142 | $(RESDIR)/%.2bpp: $(RESDIR)/%.png 143 | @$(MKDIR_P) $(@D) 144 | $(RGBGFX) -h -d 2 -o $@ $< 145 | 146 | # Define how to compress files using the PackBits16 codec 147 | # Compressor script requires Python 3 148 | $(RESDIR)/%.pb16: $(RESDIR)/% $(SRCDIR)/tools/pb16.py 149 | @$(MKDIR_P) $(@D) 150 | $(PY) $(SRCDIR)/tools/pb16.py $< $(RESDIR)/$*.pb16 151 | 152 | $(RESDIR)/%.pb16.size: $(RESDIR)/% 153 | @$(MKDIR_P) $(@D) 154 | $(call filesize,$<,16) > $(RESDIR)/$*.pb16.size 155 | 156 | # Define how to compress files using the PackBits8 codec 157 | # Compressor script requires Python 3 158 | $(RESDIR)/%.pb8: $(RESDIR)/% $(SRCDIR)/tools/pb8.py 159 | @$(MKDIR_P) $(@D) 160 | $(PY) $(SRCDIR)/tools/pb8.py $< $(RESDIR)/$*.pb8 161 | 162 | $(RESDIR)/%.pb8.size: $(RESDIR)/% 163 | @$(MKDIR_P) $(@D) 164 | $(call filesize,$<,8) > $(RESDIR)/$*.pb8.size 165 | 166 | ############################################### 167 | # # 168 | # COMPILATION # 169 | # # 170 | ############################################### 171 | 172 | # How to build the ROM (explicitly just the main ROMNAME, so we can also have the appended UXN roms use the same ROMEXT) 173 | $(BINDIR)/$(ROMNAME).$(ROMEXT) $(BINDIR)/$(ROMNAME).sym $(BINDIR)/$(ROMNAME).map: $(patsubst $(SRCDIR)/%.asm,$(OBJDIR)/%.o,$(SRCS)) 174 | @$(MKDIR_P) $(@D) 175 | $(RGBASM) $(ASFLAGS) -o $(OBJDIR)/build_date.o $(SRCDIR)/res/build_date.asm 176 | $(RGBLINK) $(LDFLAGS) -m $(BINDIR)/$(ROMNAME).map -n $(BINDIR)/$(ROMNAME).sym -o $(BINDIR)/$(ROMNAME).$(ROMEXT) $^ $(OBJDIR)/build_date.o \ 177 | && $(RGBFIX) -v $(FIXFLAGS) $(BINDIR)/$(ROMNAME).$(ROMEXT) 178 | 179 | # `.mk` files are auto-generated dependency lists of the "root" ASM files, to save a lot of hassle. 180 | # Also add all obj dependencies to the dep file too, so Make knows to remake it 181 | # Caution: some of these flags were added in RGBDS 0.4.0, using an earlier version WILL NOT WORK 182 | # (and produce weird errors) 183 | $(OBJDIR)/%.o $(DEPDIR)/%.mk: $(SRCDIR)/%.asm 184 | @$(MKDIR_P) $(patsubst %/,%,$(dir $(OBJDIR)/$* $(DEPDIR)/$*)) 185 | $(RGBASM) $(ASFLAGS) -M $(DEPDIR)/$*.mk -MG -MP -MQ $(OBJDIR)/$*.o -MQ $(DEPDIR)/$*.mk -o $(OBJDIR)/$*.o $< 186 | 187 | # How to build a ROM with an appended UXN ROM 188 | $(BINDIR)/$(ROMNAME)_%.$(ROMEXT): $(BINDIR)/$(ROMNAME).$(ROMEXT) $(SRCDIR)/roms/%.rom 189 | @$(MKDIR_P) $(@D) 190 | cat $(BINDIR)/$(ROMNAME).$(ROMEXT) $(SRCDIR)/roms/$*.rom > $(BINDIR)/$(ROMNAME)_$*.$(ROMEXT) 191 | $(RGBFIX) -O -p $(PADVALUE) -v -i "$(GAMEID)" -k "$(LICENSEE)" -l $(OLDLIC) -m $(MBC) -n $(VERSION) -r $(SRAMSIZE) -t $* $(BINDIR)/$(ROMNAME)_$*.$(ROMEXT) 192 | 193 | ifneq ($(MAKECMDGOALS),clean) 194 | -include $(patsubst $(SRCDIR)/%.asm,$(DEPDIR)/%.mk,$(SRCS)) 195 | endif 196 | 197 | # Catch non-existent files 198 | # KEEP THIS LAST!! 199 | %: 200 | @false 201 | -------------------------------------------------------------------------------- /src/intro.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Core Emulator for uxnemu 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | DEF MODE_CLI EQU $00 ; mode with console input/output devices 24 | DEF MODE_VARVARA EQU $01 ; mode with varvara compliant devices (very slow Screen due to lack of bitmap graphical hardware support) 25 | DEF MODE_TILE EQU $02 ; mode with tile-based devices for improved performance on tile-based hardware (notional) 26 | 27 | DEF MODE EQU MODE_TILE 28 | 29 | ; Include different files based on active mode 30 | IF MODE == MODE_CLI 31 | include "emu_cli/cli_devices.asm" 32 | include "emu_cli/cli_functions.asm" 33 | ELIF MODE == MODE_VARVARA 34 | include "emu_varvara/varvara_devices.asm" 35 | include "emu_varvara/varvara_functions.asm" 36 | ELIF MODE == MODE_TILE 37 | include "emu_tile/tile_devices.asm" 38 | include "emu_tile/tile_functions.asm" 39 | ELSE 40 | 41 | ENDC 42 | 43 | SECTION "UXN HRAM", HRAM 44 | hPC:: ds 2 45 | hPendingPalettes:: ds 16 ; one full BG and OBJ palette for CGB 46 | hFrameCounter:: ds 1 ; used to increment datetime seconds every 60 frames 47 | 48 | ; The stacks/devices are explicitly stored at the end of WRAM to allow UXN memory to overflow from SRAM to WRAM, which 49 | ; allows UXN to access 15616 (16384-512-256) bytes of RAM (instead of just 8192 bytes). 50 | SECTION "UXN Stacks", WRAM0[$E000-512] 51 | wWST:: ds 255 52 | wWSTPtr:: ds 1 53 | wRST:: ds 255 54 | wRSTPtr:: ds 1 55 | 56 | SECTION "UXN Devices", WRAM0[$E000-512-256] 57 | wDevices:: ds 16*16 58 | 59 | SECTION "UXN Memory", SRAM[$A000] 60 | eUXNMemory:: 61 | eZeroPage:: ds 256 62 | ePageProgram:: 63 | 64 | SECTION "Intro", ROM0 65 | 66 | Intro:: 67 | ; call mode-specific initialization 68 | call ModeInit 69 | 70 | ; Initialize emulator state 71 | 72 | ; enable external RAM to hold UXN 64KB memory 73 | xor a 74 | ld [$4000], a ; set to ram bank 0 75 | ld a, $0A 76 | ld [$00], a ; enable SRAM access 77 | 78 | ; Initialize device memory 79 | ld de, DeviceDefaults 80 | ld hl, wDevices 81 | ld c, 0 82 | rst MemcpySmall 83 | 84 | ; uxn_boot 85 | ; initialize the single bank of external RAM we're currently using 86 | ; TODO: If we expand to support the full 64KB UXN memory space, clear all 8 banks 87 | xor a 88 | ld hl, eUXNMemory 89 | ld bc, $2000 90 | rst Memset 91 | 92 | ; Initialize stack pointers 93 | ld [wWSTPtr], a 94 | ld [wRSTPtr], a 95 | 96 | ; Copy entire ROM external RAM 97 | ; TODO: Copy in banks 98 | ld de, staticROM 99 | ld hl, ePageProgram 100 | ld bc, $3A00-$100 ; Blindly copy the 0x3A00 maximum supported ROM size minus the zero page 101 | call Memcpy 102 | 103 | ; initialize PC 104 | ld a, HIGH(ePageProgram) 105 | ldh [hPC], a 106 | ld a, LOW(ePageProgram) 107 | ldh [hPC+1], a 108 | 109 | ; initial eval loop 110 | call uxn_eval 111 | 112 | brk_loop: 113 | ; BRK idles, calling vector handlers as required once per VBlank 114 | rst WaitVBlank 115 | call VectorHandlers 116 | jr brk_loop 117 | 118 | system_halt: 119 | rst WaitVBlank 120 | jr system_halt 121 | 122 | 123 | uxn_eval: 124 | 125 | ; TODO: check shutdown vector (dev:$0f) 126 | ; TODO: check wWSTPtr 127 | 128 | ; Thanks jvsTSX! Saved one cycle! 129 | ld hl, hPC ; get current PC 130 | ld a, [hli] 131 | ld c, [hl] 132 | ld b, a 133 | 134 | ld a, [bc] ; read next instruction 135 | inc bc ; increment PC 136 | 137 | ld [hl], c ; save updated PC 138 | dec l 139 | ld [hl], b 140 | 141 | ; obtain instruction handler address 142 | ld h, HIGH(instr_jump_table) 143 | add a 144 | jr nc, :+ 145 | inc h 146 | : ld l, a 147 | 148 | ld a, [hli] 149 | ld h, [hl] 150 | ld l, a 151 | rst CallHL ; call handler 152 | 153 | jr uxn_eval 154 | 155 | 156 | SECTION "Instruction Jump Table", ROM0, ALIGN[8] ; Aligned to speed up instruction dispatch 157 | instr_jump_table: 158 | ; 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 159 | ; 0x00 160 | dw _BRK, _INC, _POP, _NIP, _SWP, _ROT, _DUP, _OVR, _EQU, _NEQ, _GTH, _LTH, _JMP, _JCN, _JSR, _STH 161 | ; 0x10 162 | dw _LDZ, _STZ, _LDR, _STR, _LDA, _STA, _DEI, _DEO, _ADD, _SUB, _MUL, _DIV, _AND, _ORA, _EOR, _SFT 163 | ; 0x20 164 | dw _JCI, _INC2, _POP2, _NIP2, _SWP2, _ROT2, _DUP2, _OVR2, _EQU2, _NEQ2, _GTH2, _LTH2, _JMP2, _JCN2, _JSR2, _STH2 165 | ; 0x30 166 | dw _LDZ2, _STZ2, _LDR2, _STR2, _LDA2, _STA2, _DEI2, _DEO2, _ADD2, _SUB2, _MUL2, _DIV2, _AND2, _ORA2, _EOR2, _SFT2 167 | ; 0x40 168 | dw _JMI, _INCr, _POPr, _NIPr, _SWPr, _ROTr, _DUPr, _OVRr, _EQUr, _NEQr, _GTHr, _LTHr, _JMPr, _JCNr, _JSRr, _STHr 169 | ; 0x50 170 | dw _LDZr, _STZr, _LDRr, _STRr, _LDAr, _STAr, _DEIr, _DEOr, _ADDr, _SUBr, _MULr, _DIVr, _ANDr, _ORAr, _EORr, _SFTr 171 | ; 0x60 172 | dw _JSI, _INC2r, _POP2r, _NIP2r, _SWP2r, _ROT2r, _DUP2r, _OVR2r, _EQU2r, _NEQ2r, _GTH2r, _LTH2r, _JMP2r, _JCN2r, _JSR2r, _STH2r 173 | ; 0x70 174 | dw _LDZ2r, _STZ2r, _LDR2r, _STR2r, _LDA2r, _STA2r, _DEI2r, _DEO2r, _ADD2r, _SUB2r, _MUL2r, _DIV2r, _AND2r, _ORA2r, _EOR2r, _SFT2r 175 | ; 0x80 176 | dw _LIT, _INCk, _POPk, _NIPk, _SWPk, _ROTk, _DUPk, _OVRk, _EQUk, _NEQk, _GTHk, _LTHk, _JMPk, _JCNk, _JSRk, _STHk 177 | ; 0x90 178 | dw _LDZk, _STZk, _LDRk, _STRk, _LDAk, _STAk, _DEIk, _DEOk, _ADDk, _SUBk, _MULk, _DIVk, _ANDk, _ORAk, _EORk, _SFTk 179 | ; 0xA0 180 | dw _LIT2, _INC2k, _POP2k, _NIP2k, _SWP2k, _ROT2k, _DUP2k, _OVR2k, _EQU2k, _NEQ2k, _GTH2k, _LTH2k, _JMP2k, _JCN2k, _JSR2k, _STH2k 181 | ; 0xB0 182 | dw _LDZ2k, _STZ2k, _LDR2k, _STR2k, _LDA2k, _STA2k, _DEI2k, _DEO2k, _ADD2k, _SUB2k, _MUL2k, _DIV2k, _AND2k, _ORA2k, _EOR2k, _SFT2k 183 | ; 0xC0 184 | dw _LITr, _INCkr, _POPkr, _NIPkr, _SWPkr, _ROTkr, _DUPkr, _OVRkr, _EQUkr, _NEQkr, _GTHkr, _LTHkr, _JMPkr, _JCNkr, _JSRkr, _STHkr 185 | ; 0xD0 186 | dw _LDZkr, _STZkr, _LDRkr, _STRkr, _LDAkr, _STAkr, _DEIkr, _DEOkr, _ADDkr, _SUBkr, _MULkr, _DIVkr, _ANDkr, _ORAkr, _EORkr, _SFTkr 187 | ; 0xE0 188 | dw _LIT2r, _INC2kr, _POP2kr, _NIP2kr, _SWP2kr, _ROT2kr, _DUP2kr, _OVR2kr, _EQU2kr, _NEQ2kr, _GTH2kr, _LTH2kr, _JMP2kr, _JCN2kr, _JSR2kr, _STH2kr 189 | ; 0xF0 190 | dw _LDZ2kr, _STZ2kr, _LDR2kr, _STR2kr, _LDA2kr, _STA2kr, _DEI2kr, _DEO2kr, _ADD2kr, _SUB2kr, _MUL2kr, _DIV2kr, _AND2kr, _ORA2kr, _EOR2kr, _SFT2kr 191 | 192 | ; Single byte so the output ROM runs right up to this point 193 | SECTION "End Pad", ROM0[$3FFF] 194 | db $FF 195 | 196 | ; Attachment point for appended UXN ROM 197 | SECTION "UXN ROM", ROMX[$4000] 198 | staticROM: 199 | -------------------------------------------------------------------------------- /src/vectors.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Vectors and Handlers 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | INCLUDE "defines.asm" 24 | 25 | SECTION "Rst $00", ROM0[$00] 26 | 27 | NULL:: 28 | ; This traps jumps to $0000, which is a common "default" pointer 29 | ; $FFFF is another one, but reads rIE as the instruction byte 30 | ; Thus, we put two `nop`s that may serve as operands, before soft-crashing 31 | ; The operand will always be 0, so even jumps will work fine. Nice! 32 | nop 33 | nop 34 | rst Crash 35 | 36 | SECTION "Rst $08", ROM0[$08] 37 | 38 | ; Waits for the next VBlank beginning 39 | ; Requires the VBlank handler to be able to trigger, otherwise will loop infinitely 40 | ; This means IME should be set, the VBlank interrupt should be selected in IE, 41 | ; and the LCD should be turned on. 42 | ; WARNING: Be careful if calling this with IME reset (`di`), if this was compiled 43 | ; with the `-h` flag, then a hardware bug is very likely to cause this routine to 44 | ; go horribly wrong. 45 | ; Note: the VBlank handler recognizes being called from this function (through `hVBlankFlag`), 46 | ; and does not try to save registers if so. To be safe, consider all registers to be destroyed. 47 | ; @destroy Possibly every register. The VBlank handler stops preserving anything when executed from this function 48 | WaitVBlank:: 49 | ld a, 1 50 | ldh [hVBlankFlag], a 51 | .wait 52 | halt 53 | jr .wait 54 | 55 | SECTION "Rst $10", ROM0[$10 - 1] 56 | 57 | MemsetLoop: 58 | ld a, d 59 | 60 | assert @ == $10 61 | ; You probably don't want to use this for writing to VRAM while the LCD is on. See LCDMemset. 62 | Memset:: 63 | ld [hli], a 64 | ld d, a 65 | dec bc 66 | ld a, b 67 | or c 68 | jr nz, MemsetLoop 69 | ret 70 | 71 | SECTION "Rst $18", ROM0[$18] 72 | 73 | MemcpySmall:: 74 | ld a, [de] 75 | ld [hli], a 76 | inc de 77 | dec c 78 | jr nz, MemcpySmall 79 | ret 80 | 81 | SECTION "Rst $20", ROM0[$20] 82 | 83 | MemsetSmall:: 84 | ld [hli], a 85 | dec c 86 | jr nz, MemsetSmall 87 | ret 88 | 89 | SECTION "Rst $28", ROM0[$28 - 3] 90 | 91 | ; Dereferences `hl` and jumps there 92 | ; All other registers are passed to the called code intact, except Z is reset 93 | ; Soft-crashes if the jump target is in RAM 94 | ; @param hl Pointer to an address to jump to 95 | JumpToPtr:: 96 | ld a, [hli] 97 | ld h, [hl] 98 | ld l, a 99 | 100 | assert @ == $28 101 | ; Jump to some address 102 | ; All registers are passed to the called code intact, except Z is reset 103 | ; (`jp CallHL` is equivalent to `jp hl`, but with the extra error checking on top) 104 | ; Soft-crashes if attempting to jump to RAM 105 | ; @param hl The address of the code to jump to 106 | CallHL:: 107 | ; Note: Error checking disabled to speed up instruction dispatch 108 | ;bit 7, h 109 | ;error nz 110 | jp hl 111 | 112 | SECTION "Rst $30", ROM0[$30] 113 | 114 | ; Jumps to some address 115 | ; All registers are passed to the target code intact, except Z is reset 116 | ; (`jp CallDE` would be equivalent to `jp de` if that instruction existed) 117 | ; Soft-crashes if attempting to jump to RAM 118 | ; @param de The address of the code to jump to 119 | CallDE:: 120 | bit 7, d 121 | push de 122 | ret z ; No jumping to RAM, boy! 123 | rst Crash 124 | 125 | SECTION "Rst $38", ROM0[$38] 126 | ; Loop forever on a crash 127 | Crash:: 128 | di ; Doing this as soon as possible to avoid interrupts messing up 129 | : jr :- 130 | 131 | SECTION "Handlers", ROM0[$40] 132 | 133 | ; VBlank handler 134 | push af 135 | ldh a, [hLCDC] 136 | ldh [rLCDC], a 137 | jp VBlankHandler 138 | ds $48 - @ 139 | 140 | ; STAT handler (only used by varvara, but included here for performance reasons) 141 | push af 142 | ld a, LCDCF_ON | LCDCF_BG8800 | LCDCF_BG9800 | LCDCF_BGON | LCDCF_OBJON | LCDCF_OBJ8 143 | ldh [rLCDC], a 144 | 145 | ; Ensure VRAM is writeable when we return from this handler, as DMG runs into issues otherwise 146 | : ldh a, [rSTAT] 147 | and STATF_BUSY 148 | jr nz, :- 149 | 150 | pop af 151 | reti 152 | ;ds $50 - @ 153 | 154 | ; Timer handler 155 | ;rst $38 156 | ds $58 - @ 157 | 158 | ; Serial handler 159 | rst $38 160 | ds $60 - @ 161 | 162 | ; Joypad handler (useless) 163 | rst $38 164 | 165 | SECTION "VBlank handler", ROM0 166 | 167 | VBlankHandler: 168 | ldh a, [hSCY] 169 | ldh [rSCY], a 170 | ldh a, [hSCX] 171 | ldh [rSCX], a 172 | ;ldh a, [hBGP] 173 | ;ldh [rBGP], a 174 | ;ldh a, [hOBP0] 175 | ;ldh [rOBP0], a 176 | ;ldh a, [hOBP1] 177 | ;ldh [rOBP1], a 178 | 179 | ; OAM DMA can occur late in the handler, because it will still work even 180 | ; outside of VBlank. Sprites just will not appear on the scanline(s) 181 | ; during which it's running. 182 | ldh a, [hOAMHigh] 183 | and a 184 | jr z, .noOAMTransfer 185 | call hOAMDMA 186 | xor a 187 | ldh [hOAMHigh], a 188 | .noOAMTransfer 189 | 190 | push hl 191 | 192 | ; Update datetime device time 193 | ; TODO: Only run if datetime device is present 194 | ld hl, hFrameCounter 195 | dec [hl] 196 | jr nz, .notASecond 197 | ld a, 60 198 | ld [hl], a 199 | ld hl, wDevices + $c6 200 | ld a, [hl] 201 | inc a 202 | cp 60 203 | jr nz, .done 204 | xor a 205 | ld [hld], a 206 | ld a, [hl] 207 | inc a 208 | cp 60 209 | jr nz, .done 210 | xor a 211 | ld [hld], a 212 | ld a, [hl] 213 | inc a 214 | cp 24 215 | jr nz, .done 216 | xor a 217 | ld [hld], a 218 | ; TODO: Bother beyond days 219 | .done 220 | ld [hl], a 221 | .notASecond 222 | 223 | ldh a, [hPalettePending] 224 | or a 225 | jr z, .noPalettePending 226 | xor a 227 | ldh [hPalettePending], a 228 | 229 | push bc 230 | 231 | ; Apply the pending palettes 232 | ld hl, hPendingPalettes 233 | ld a, BCPSF_AUTOINC 234 | ldh [rBCPS], a 235 | ld b, 8 236 | .bgPal 237 | ld a, [hli] 238 | ldh [rBCPD], a 239 | dec b 240 | jr nz, .bgPal 241 | 242 | ld hl, hPendingPalettes 243 | ld a, OCPSF_AUTOINC 244 | ldh [rOCPS], a 245 | ld b, 8 246 | .objPal 247 | ld a, [hli] 248 | ldh [rOCPD], a 249 | dec b 250 | jr nz, .objPal 251 | 252 | pop bc 253 | 254 | .noPalettePending 255 | 256 | pop hl 257 | 258 | ; Put all operations that cannot be interrupted above this line 259 | ; For example, OAM DMA (can't jump to ROM in the middle of it), 260 | ; VRAM accesses (can't screw up timing), etc 261 | ei 262 | 263 | ldh a, [hVBlankFlag] 264 | and a 265 | jr z, .lagFrame 266 | xor a 267 | ldh [hVBlankFlag], a 268 | 269 | push bc 270 | 271 | ld c, LOW(rP1) 272 | ld a, $20 ; Select D-pad 273 | ldh [c], a 274 | REPT 6 275 | ldh a, [c] 276 | ENDR 277 | or $F0 ; Set 4 upper bits (give them consistency) 278 | ld b, a 279 | 280 | ; Filter impossible D-pad combinations 281 | and $0C ; Filter only Down and Up 282 | ld a, b 283 | jr nz, .notUpAndDown 284 | or $0C ; If both are pressed, "unpress" them 285 | ld b, a 286 | .notUpAndDown 287 | and $03 ; Filter only Left and Right 288 | jr nz, .notLeftAndRight 289 | ; If both are pressed, "unpress" them 290 | inc b 291 | inc b 292 | inc b 293 | .notLeftAndRight 294 | swap b ; Put D-pad buttons in upper nibble 295 | 296 | ld a, $10 ; Select buttons 297 | ldh [c], a 298 | REPT 6 299 | ldh a, [c] 300 | ENDR 301 | ; On SsAB held, soft-reset 302 | and $0F 303 | jr z, .perhapsReset 304 | .dontReset 305 | 306 | or $F0 ; Set 4 upper bits 307 | xor b ; Mix with D-pad bits, and invert all bits (such that pressed=1) thanks to "or $F0" 308 | ld b, a 309 | 310 | ; Release joypad 311 | ld a, $30 312 | ldh [c], a 313 | 314 | ldh a, [hHeldKeys] 315 | cpl 316 | and b 317 | ldh [hPressedKeys], a 318 | ld a, b 319 | ldh [hHeldKeys], a 320 | 321 | 322 | ; TODO: Find a way to cache the spare bit without another register so we can avoid push/pop 323 | push hl 324 | ; Inject UXN button byte into device memory 325 | ; Note: Done here instead of in the controller vector because programs might just read the 326 | ; state elsewhere (such as the screen vector). 327 | 328 | ; Change high nibble GB order DULR to UXN order of RLDU 329 | ; TODO: There has to be a faster way to do this! 330 | ld b, a ; cache original byte 331 | swap a 332 | rrca ; get R bit 333 | rl c ; push R bit 334 | rrca ; get L bit 335 | rl c ; push L bit 336 | rrca ; get U bit 337 | rl h ; cache U bit 338 | rrca ; get D bit 339 | rl c ; push D bit 340 | srl h ; recover U bit 341 | rl c ; push U bit 342 | swap c 343 | ld a, c 344 | and $f0 ; only keep dpad bits 345 | ld c, a 346 | ld a, b 347 | and $0f ; only keep button bits 348 | or c ; merge dpad+buttons 349 | pop hl 350 | 351 | ld [wDevices + $82], a 352 | 353 | pop bc 354 | 355 | pop af ; Pop off return address as well to exit infinite loop 356 | .lagFrame 357 | pop af 358 | ret 359 | 360 | .perhapsReset 361 | ldh a, [hCanSoftReset] 362 | and a 363 | jr z, .dontReset 364 | jp Reset 365 | 366 | SECTION "VBlank HRAM", HRAM 367 | 368 | ; DO NOT TOUCH THIS 369 | ; When this flag is set, the VBlank handler will assume the caller is `WaitVBlank`, 370 | ; and attempt to exit it. You don't want that to happen outside of that function. 371 | hVBlankFlag:: db 372 | 373 | ; High byte of the address of the OAM buffer to use. 374 | ; When this is non-zero, the VBlank handler will write that value to rDMA, and 375 | ; reset it. 376 | hOAMHigh:: db 377 | 378 | ; Shadow registers for a bunch of hardware regs. 379 | ; Writing to these causes them to take effect more or less immediately, so these 380 | ; are copied to the hardware regs by the VBlank handler, taking effect between frames. 381 | ; They also come in handy for "resetting" them if modifying them mid-frame for raster FX. 382 | hLCDC:: db 383 | hSCY:: db 384 | hSCX:: db 385 | hBGP:: db 386 | hOBP0:: db 387 | hOBP1:: db 388 | 389 | ; Indicate if a UXN palette change is pending 390 | hPalettePending:: db 391 | 392 | ; Keys that are currently being held, and that became held just this frame, respectively. 393 | ; Each bit represents a button, with that bit set == button pressed 394 | ; Button order: Down, Up, Left, Right, Start, select, B, A 395 | ; U+D and L+R are filtered out by software, so they will never happen 396 | hHeldKeys:: db 397 | hPressedKeys:: db 398 | hPriorKeys:: db 399 | 400 | ; If this is 0, pressing SsAB at the same time will not reset the game 401 | hCanSoftReset:: db 402 | -------------------------------------------------------------------------------- /src/emu_tile/tile_devices.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Tile Devices 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | ; I've attempted a rough RGB->Intensity conversion, tuned to values 24 | ; which look good for screen.tal, but in other cases the result is not 25 | ; very desireable, so for now it's disabled. I'm not sure if the solution 26 | ; is just a different table of values, or an entirely different approach. 27 | DEF ENABLE_DMG_PALETTES EQU 0 28 | 29 | SECTION "Device Handlers", ROM0, ALIGN[7] 30 | DeviceHandlers:: 31 | dw DevSystemDEI, DevSystemDEI2, DevSystemDEO, DevSystemDEO2 ; system 32 | dw DevNil, DevNil, DevConsoleDEO, DevConsoleDEO2 ; console 33 | dw DevTileDEI, DevTileDEI2, DevTileDEO, DevTileDEO2 ; tile 34 | dw DevNil, DevNil, DevNil, DevNil ; audio 35 | dw DevNil, DevNil, DevNil, DevNil ; empty 36 | dw DevNil, DevNil, DevNil, DevNil ; empty 37 | dw DevNil, DevNil, DevNil, DevNil ; empty 38 | dw DevNil, DevNil, DevNil, DevNil ; empty 39 | dw DevNil, DevNil, DevNil, DevNil ; controller 40 | dw DevNil, DevNil, DevNil, DevNil ; mouse 41 | dw DevNil, DevNil, DevNil, DevNil ; empty (file0) 42 | dw DevNil, DevNil, DevNil, DevNil ; empty (file1) 43 | dw DevNil, DevNil, DevNil, DevNil ; empty (datetime) 44 | dw DevNil, DevNil, DevNil, DevNil ; empty 45 | dw DevNil, DevNil, DevNil, DevNil ; empty 46 | dw DevNil, DevNil, DevNil, DevNil ; empty 47 | 48 | SECTION "Tile Device Defaults", ROM0 49 | DeviceDefaults:: 50 | ; system (0x00) 51 | dw 0 ; vector 52 | db 0 ; wst 53 | db 0 ; rst 54 | ds 4, 0 ; pad 55 | dw 0 ; red 56 | dw 0 ; blue 57 | dw 0 ; green 58 | db 0 ; debug 59 | db 0 ; state 60 | ; console (0x10) 61 | ds 16, 0 62 | 63 | ; tile (0x20) 64 | ; Note: Unfinished 65 | dw 0 ; vector 66 | db 20 ; screen tile width (0-255 tiles) 67 | db 18 ; screen tile height (0-255 tiles) 68 | db 0 ; x (0-255) 69 | db 0 ; y (0-255) 70 | dw 0 ; addr (for tile/tilemap copy source) 71 | db 0 ; auto (autoY, autoX, autoSrc, 5bit length (32 max)) 72 | ; 76543210 73 | ; |||+++++- length 74 | ; ||+------ autoSrc 75 | ; |+------- autoX 76 | ; +-------- autoY 77 | db 0 ; repeat (number of times to automatically repeat write operations) 78 | db 0 ; tileID (tileID for tilemap/sprite, target tile for tile copies) 79 | db 0 ; write operation 80 | ; - 00 tiles: autoBlend, mode, blending [4] TODO: Remove blending, add bit to set BG or sprite tiles target? 81 | ; 76543210 82 | ; ||||++++- blending 83 | ; |||+----- mode (1bpp/2bpp) 84 | ; ||+------ autoBlend 85 | ; ++------- operation 86 | ; - 01 tilemap: ?????, src -> Set tilemap based on tileID or copy from add 87 | ; 76543210 88 | ; |||||||+- source (0: tileID, 1: addr) 89 | ; ||+++++-- unused 90 | ; ++------- operation 91 | ; - 10 sprite: ???, 2x, flipY, flipX -> Create sprite at x/y/tileID with given flip flags 92 | ; |||||||+- flipX 93 | ; ||||||+-- flipY 94 | ; ||++++--- unused 95 | ; ++------- operation 96 | ; - 11 unused 97 | db 0 ; scrollY (0-255) 98 | db 0 ; scrollX (0-255) 99 | db 0 ; unused 100 | db 0 ; unused 101 | 102 | ; audio 103 | ds 16, 0 104 | ; the rest 105 | ds 192, 0 106 | 107 | SECTION "Tile WRAM", WRAM0[$DA00] 108 | wPixelBlend: ds 4 ; 4 blend bytes for the current blend mode 109 | 110 | SECTION "Tile HRAM", HRAM 111 | hDeviceByte: ds 1 ; copy of the device byte for fast access 112 | hDataByte: ds 1 ; copy of the data byte for fast access 113 | ;hWorkingBytes: ds 2 ; stores both the input bytes (initially) and the final pixel data (after blending) 114 | 115 | SECTION UNION "Tile Scratch HRAM", HRAM 116 | hX: ds 1 117 | hY: ds 1 118 | hDX: ds 1 119 | hDY: ds 1 120 | hAutoSrc: ds 1 121 | 122 | SECTION "Tile Vectors", ROM0 123 | 124 | VectorHandlers:: 125 | 126 | ; controller vector 127 | ld hl, wDevices + $80 128 | ld a, [hli] 129 | ld c, [hl] 130 | ld b, a 131 | or c 132 | jr z, .noControllerVector 133 | 134 | ; Check for change in controller state 135 | ldh a, [hPriorKeys] 136 | ld d, a 137 | ldh a, [hHeldKeys] 138 | cp d 139 | jr z, .noControllerVector 140 | ldh [hPriorKeys], a 141 | 142 | ld hl, eUXNMemory 143 | add hl, bc 144 | 145 | ld a, h 146 | ldh [hPC], a 147 | ld a, l 148 | ldh [hPC+1], a 149 | 150 | call uxn_eval ; eval instructions until a BRK is hit 151 | .noControllerVector 152 | 153 | ; screen vector 154 | ld hl, wDevices + $20 155 | ld a, [hli] 156 | ld c, [hl] 157 | ld b, a 158 | or c 159 | jr z, .noScreenVector 160 | 161 | ld hl, eUXNMemory 162 | add hl, bc 163 | 164 | ld a, h 165 | ldh [hPC], a 166 | ld a, l 167 | ldh [hPC+1], a 168 | 169 | call uxn_eval ; eval instructions until a BRK is hit 170 | 171 | ; There's a decent chance the screen vector performed OAM updates, so 172 | ; queue up an OAM DMA once all updates have been performed (to prevent 173 | ; tearing in the likely case updates span more than one vblank). 174 | ld a, HIGH(wShadowOAM) 175 | ldh [hOAMHigh], a 176 | 177 | .noScreenVector 178 | 179 | ret 180 | 181 | 182 | SECTION "Tile Devices", ROM0 183 | 184 | DevSystemDEI:: 185 | ret 186 | 187 | DevSystemDEI2:: 188 | ret 189 | 190 | ; d = device 191 | ; b = data 192 | DevSystemDEO:: 193 | ret 194 | 195 | IF ENABLE_DMG_PALETTES 196 | include "res/dmg_palette_lookup_generated.asm" 197 | ENDC 198 | 199 | ; Convert the color values stored in the system device to a host-compatible palette 200 | ; and then queue up a palette update for the next VBlank 201 | UpdatePalette: 202 | 203 | ldh a, [hConsoleType] 204 | or a 205 | jr z, .gbc 206 | 207 | IF ENABLE_DMG_PALETTES 208 | ; For DMG convert the RGB to a 2 bit value 209 | ; Input: 4bit RGB for each of 4 channels, stored in 6 bytes 210 | ; Output: 2bit brightness value for each of 4 channels 211 | ld hl, wDevices + $08 + 1 212 | call ConvertTwoShades 213 | call ConvertTwoShades 214 | 215 | ld a, c 216 | ldh [rBGP], a 217 | ldh [rOBP0], a 218 | ENDC 219 | 220 | ret 221 | .gbc 222 | 223 | ; For CGB convert from 12 bit to 15 bit RGB 224 | ; CGB: xBBBBBGG_GGGRRRRR 225 | ld hl, wDevices + $08 226 | ld bc, hPendingPalettes 227 | call ConvertTwoColors 228 | call ConvertTwoColors 229 | 230 | ld a, 1 231 | ldh [hPalettePending], a 232 | .done 233 | ret 234 | 235 | IF ENABLE_DMG_PALETTES 236 | ConvertTwoShades: 237 | ; color 0 238 | ld a, [hli] ; red 239 | inc l 240 | and %00001100 241 | add a 242 | add a 243 | ld b, a 244 | ld a, [hli] ; green 245 | inc l 246 | and %00001100 247 | or b 248 | ld b, a 249 | ld a, [hld] ; blue 250 | dec l 251 | dec l 252 | dec l 253 | and %00001100 254 | srl a 255 | srl a 256 | or b 257 | ; Now we have this color in the form: %00rrggbb 258 | push hl 259 | ld hl, DMGPaletteLookup 260 | add l ; offset to table value for this RGB value 261 | ld l, a 262 | adc h 263 | sub l 264 | ld h, a 265 | ld a, [hl] 266 | pop hl 267 | 268 | rrca ; shift intensity bits into final palette in C 269 | rl c 270 | rrca 271 | rl c 272 | 273 | ; color 1 274 | ld a, [hli] ; red 275 | inc l 276 | and %11000000 277 | ld b, a 278 | ld a, [hli] ; green 279 | inc l 280 | and %11000000 281 | srl a 282 | srl a 283 | or b 284 | ld b, a 285 | ld a, [hld] ; blue 286 | dec l 287 | dec l 288 | dec l 289 | dec l 290 | and %11000000 291 | swap a 292 | or b 293 | srl a 294 | srl a 295 | ; Now we have this color in the form: %00rrggbb 296 | push hl 297 | ld hl, DMGPaletteLookup 298 | add l ; offset to table value for this RGB value 299 | ld l, a 300 | adc h 301 | sub l 302 | ld h, a 303 | ld a, [hl] 304 | pop hl 305 | 306 | rrca ; shift intensity bits into final palette in C 307 | rl c 308 | rrca 309 | rl c 310 | 311 | ret 312 | ENDC 313 | 314 | ConvertTwoColors: 315 | ; color 0 316 | ld a, [hli] ; red 317 | inc l 318 | and $f0 319 | swap a 320 | add a 321 | ld e, a 322 | ld a, [hli] ; green 323 | inc l 324 | and $f0 325 | ld d, a 326 | add a 327 | or e 328 | ld [bc], a ; low byte of color 0 329 | inc bc 330 | ld a, d 331 | swap a 332 | sra a 333 | sra a 334 | ld d, a 335 | ld a, [hld] ; blue 336 | dec l 337 | dec l 338 | dec l 339 | and $f0 340 | sra a 341 | or d 342 | ld [bc], a ; high byte of color 0 343 | inc bc 344 | 345 | ; color 1 346 | ld a, [hli] ; red 347 | inc l 348 | and $0f 349 | add a 350 | ld e, a 351 | ld a, [hli] ; green 352 | inc l 353 | and $0f 354 | ld d, a 355 | swap a 356 | add a 357 | or e 358 | ld [bc], a ; low byte of color 1 359 | inc bc 360 | ld a, d 361 | sra a 362 | sra a 363 | ld d, a 364 | ld a, [hld] ; blue 365 | dec l 366 | dec l 367 | and $0f 368 | swap a 369 | sra a 370 | or d 371 | ld [bc], a ; high byte of color 1 372 | inc bc 373 | 374 | ret 375 | 376 | ; d = device 377 | ; bc = data 378 | DevSystemDEO2:: 379 | 380 | ld a, d 381 | cp $08 382 | jr nz, .notRed 383 | call UpdatePalette 384 | .notRed 385 | cp $0a 386 | jr nz, .notGreen 387 | call UpdatePalette 388 | .notGreen 389 | cp $0c 390 | jr nz, .notBlue 391 | call UpdatePalette 392 | .notBlue 393 | 394 | ret 395 | 396 | ; d = device 397 | ; b = data 398 | DevConsoleDEO:: 399 | ret 400 | 401 | ; d = device 402 | ; bc = data 403 | DevConsoleDEO2:: 404 | ; TODO: write to console 405 | ret 406 | 407 | ; d = device 408 | ; bc = data 409 | DevTileDEI:: 410 | ret 411 | 412 | ; d = device 413 | ; bc = data 414 | DevTileDEI2:: 415 | ret 416 | 417 | ; d = device 418 | ; b = data 419 | DevTileDEO:: 420 | ld a, b 421 | ldh [hDataByte], a 422 | ; Determine operation 423 | ld a, d 424 | ldh [hDeviceByte], a 425 | sub $2b 426 | jr z, .write 427 | dec a 428 | jr z, .scrollY 429 | dec a 430 | jr z, .scrollX 431 | 432 | ret 433 | 434 | .scrollY 435 | ld a, b 436 | ldh [rSCY], a 437 | ret 438 | 439 | .scrollX 440 | ld a, b 441 | ldh [rSCX], a 442 | ret 443 | 444 | .write 445 | ld a, b 446 | and %11000000 447 | jr z, .tileCopy 448 | cp $40 449 | jr z, .tilemap 450 | cp $80 451 | ret nz 452 | 453 | .sprite 454 | ld hl, wDevices + $24 455 | ld a, [hli] ; x 456 | ldh [hX], a 457 | ld a, [hli] ; y 458 | ldh [hY], a 459 | inc l ; addr high 460 | inc l ; addr low 461 | ld a, [hli] ; auto (x, y, src, length) 462 | ld a, [hli] ; repeat 463 | inc a 464 | ld b, a 465 | ld c, [hl] ; tileID 466 | 467 | ; TODO: Handle auto x/y/src/length 468 | 469 | ld hl, wOAMIndex 470 | ld l, [hl] 471 | .repeatSprite 472 | ldh a, [hY] 473 | add $10 474 | ld [hli], a 475 | ldh a, [hX] 476 | add $08 477 | ld [hli], a 478 | ld a, c 479 | ld [hli], a 480 | xor a ; TODO: flips 481 | ld [hli], a 482 | 483 | dec b 484 | jr nz, .repeatSprite 485 | 486 | ; Ensure OAM is updated 487 | ld a, HIGH(wShadowOAM) 488 | ldh [hOAMHigh], a 489 | 490 | ; TODO: Handle 'reset OAM every frame .sprite is called' 491 | 492 | ret 493 | 494 | .tileCopy 495 | ; Copy tiles from Uxn RAM/ROM to VRAM [blending to be applied later, maybe] 496 | ld hl, wDevices + $26 497 | push hl 498 | ld a, [hli] ; source addr high 499 | ld d, a 500 | ld a, [hli] ; source addr low 501 | ld e, a 502 | ld a, [hli] ; auto TODO: do we need autoAddr? Always advance if no blending 503 | ld a, [hli] ; repeat 504 | inc a 505 | ld b, a 506 | ld a, [hl] ; tileID 507 | swap a 508 | ld c, a 509 | 510 | ld hl, $A000 ; offset source addr to GB address 511 | add hl, de ; TODO: Look for "past Uxn space" values 512 | ld d, h 513 | ld e, l 514 | 515 | ld h, HIGH($8000) 516 | ld l, c 517 | 518 | .repeatTiles 519 | ; Convert CHR to GB 2bpp 520 | ld c, 8 521 | .tileLoop 522 | : ldh a, [rSTAT] 523 | and STATF_BUSY 524 | jr nz, :- 525 | ld a, [de] 526 | ld [hli], a 527 | push hl 528 | ld hl, $0008 529 | add hl, de 530 | ld a, [hl] 531 | pop hl 532 | ld [hli], a 533 | inc de 534 | dec c 535 | jr nz, .tileLoop 536 | dec b 537 | jr nz, .repeatTiles 538 | 539 | ld hl, -$A000 ; offset back to Uxn address 540 | add hl, de 541 | ld d, h 542 | ld e, l 543 | 544 | pop hl 545 | ld a, d ; final addr value 546 | ld [hli], a 547 | ld a, e 548 | ld [hl], a 549 | ret 550 | 551 | .tilemap 552 | ld a, b 553 | and %00000001 554 | jr z, .tileIDSource 555 | ; Copy from addr 556 | ret 557 | 558 | .tileIDSource 559 | ; Set tilemap to tileID 560 | ld hl, wDevices + $24 561 | ld a, [hli] ; x 562 | ld c, [hl] ; y 563 | push hl 564 | ld h, HIGH($9800) ; calculate target VRAM addr 565 | ld l, a 566 | ld a, c 567 | or a 568 | jr z, .noY 569 | ld de, $0020 570 | .yLoop 571 | add hl, de 572 | dec c 573 | jr nz, .yLoop 574 | ld d, h 575 | ld e, l 576 | .noY 577 | pop hl 578 | inc l 579 | inc l ; addr high 580 | inc l ; addr low 581 | ld a, [hli] ; auto (x, y, src, length) 582 | ld a, [hli] ; repeat 583 | inc a 584 | ld b, a 585 | ld c, [hl] ; tileID 586 | 587 | ; TODO: Handle auto x/y/src/length 588 | 589 | .repeatTilemap 590 | : ldh a, [rSTAT] 591 | and STATF_BUSY 592 | jr nz, :- 593 | ld a, c 594 | ld [de], a 595 | 596 | dec b 597 | jr nz, .repeatTilemap 598 | 599 | ; store new x/y/id(?) 600 | 601 | ret 602 | 603 | ; d = device 604 | ; bc = data 605 | DevTileDEO2: 606 | ; Prevent width/height from exceeding 160x144 screen size by resetting width/height after they're set 607 | ; (I tried handling this in DEI2, but it's handled after the values are pushed to the stack) 608 | ld a, d 609 | sub $22 610 | jr z, .width 611 | dec a 612 | jr z, .height 613 | ret 614 | .width 615 | ld a, 20 616 | ld [wDevices + $22], a 617 | ret 618 | .height 619 | ld a, 18 620 | ld [wDevices + $23], a 621 | ret 622 | 623 | DevNil:: 624 | ret 625 | 626 | SECTION "Tile Blending", ROM0, ALIGN[8] ; TODO: May not need align[8] 627 | 628 | PixelBlendingTable: 629 | db 0, 0, 1, 2 630 | db 0, 1, 2, 3 631 | db 0, 2, 3, 1 632 | db 0, 3, 1, 1 633 | db 1, 0, 1, 2 634 | db 0, 1, 2, 3 635 | db 1, 2, 3, 1 636 | db 1, 3, 1, 2 637 | db 2, 0, 1, 2 638 | db 2, 1, 2, 3 639 | db 0, 2, 3, 1 640 | db 2, 3, 1, 2 641 | db 3, 0, 1, 2 642 | db 3, 1, 2, 3 643 | db 3, 2, 3, 1 644 | db 0, 3, 1, 2 645 | -------------------------------------------------------------------------------- /src/emu_varvara/varvara_devices.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Varvara Devices 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | ; I've attempted a rough RGB->Intensity conversion, tuned to values 24 | ; which look good for screen.tal, but in other cases the result is not 25 | ; very desireable, so for now it's disabled. I'm not sure if the solution 26 | ; is just a different table of values, or an entirely different approach. 27 | DEF ENABLE_DMG_PALETTES EQU 0 28 | 29 | SECTION "Device Handlers", ROM0, ALIGN[7] 30 | DeviceHandlers:: 31 | dw DevSystemDEI, DevSystemDEI2, DevSystemDEO, DevSystemDEO2 ; system 32 | dw DevNil, DevNil, DevConsoleDEO, DevConsoleDEO2 ; console 33 | dw DevScreenDEI, DevScreenDEI2, DevScreenDEO, DevScreenDEO2 ; screen 34 | dw DevNil, DevNil, DevNil, DevNil ; audio 35 | dw DevNil, DevNil, DevNil, DevNil ; empty 36 | dw DevNil, DevNil, DevNil, DevNil ; empty 37 | dw DevNil, DevNil, DevNil, DevNil ; empty 38 | dw DevNil, DevNil, DevNil, DevNil ; empty 39 | dw DevNil, DevNil, DevNil, DevNil ; controller 40 | dw DevNil, DevNil, DevNil, DevNil ; mouse 41 | dw DevNil, DevNil, DevNil, DevNil ; empty (file0) 42 | dw DevNil, DevNil, DevNil, DevNil ; empty (file1) 43 | dw DevNil, DevNil, DevNil, DevNil ; empty (datetime) 44 | dw DevNil, DevNil, DevNil, DevNil ; empty 45 | dw DevNil, DevNil, DevNil, DevNil ; empty 46 | dw DevNil, DevNil, DevNil, DevNil ; empty 47 | 48 | SECTION "Varvara Device Defaults", ROM0 49 | DeviceDefaults:: 50 | ; system (0x00) 51 | dw 0 ; vector 52 | db 0 ; wst 53 | db 0 ; rst 54 | ds 4, 0 ; pad 55 | dw 0 ; red 56 | dw 0 ; blue 57 | dw 0 ; green 58 | db 0 ; debug 59 | db 0 ; state 60 | ; console (0x10) 61 | ds 16, 0 62 | ; screen (0x20) 63 | dw 0 ; vector 64 | db HIGH(160), LOW(160) ; width (big endian) 65 | db HIGH(144), LOW(144) ; height (big endian) 66 | db 0 ; auto 67 | db 0 ; pad 68 | dw 0 ; x 69 | dw 0 ; y 70 | dw 0 ; addr 71 | db 0 ; pixel 72 | db 0 ; sprite 73 | ; audio 74 | ds 16*4, 0 75 | ; midi 76 | ds 16, 0 77 | ; controller 78 | ds 16, 0 79 | ; mouse 80 | ds 16, 0 81 | ; file 82 | ds 32, 0 83 | ; datetime 84 | dw 2022 ; year 85 | db 1 ; month 86 | db 1 ; day 87 | db 3 ; hour 88 | db 30 ; minute 89 | db 0 ; second 90 | db 0 ; dotw 91 | dw 0 ; doty 92 | db 0 ; isdst 93 | ds 5, 0 94 | ; the rest 95 | ds 16*3, 0 96 | 97 | SECTION "Varvara WRAM", WRAM0[$DA00] 98 | wPixelBlend: ds 4 ; 4 blend bytes for the current blend mode 99 | wTileBuffer: ds 16 ; buffer for tile data during blit 100 | wSpriteTileID: ds 1 ; tileID to use for sprite 101 | wSpriteTileAddr:ds 2 ; address to render sprite tile to 102 | 103 | SECTION "Varvara WRAM FG Sprites", WRAM0[$DB00] 104 | wObjSourceAddrs:: ds 16*4 ; blend byte, source UXN address, pad 105 | .end:: 106 | wObjSourceEmpty:: ds 1 ; low byte of next free entry 107 | 108 | SECTION "Varvara HRAM", HRAM 109 | hDeviceByte: ds 1 ; copy of the device byte for fast access 110 | hDataByte: ds 1 ; copy of the data byte for fast access 111 | hWorkingBytes: ds 2 ; stores both the input bytes (initially) and the final pixel data (after blending) 112 | hWorkingX: ds 1 ; working X coordinate for repeated auto sprite writes 113 | hDeltaY: ds 1 ; delta Y to apply each auto sprite write 114 | hWorkingY: ds 1 ; working Y coordinate for repeated auto sprite writes 115 | hDeltaX: ds 1 ; delta X to apply each auto sprite write 116 | hAutoLen: ds 1 ; length of auto writes 117 | hAutoAddr: ds 1 ; auto addr flag 118 | hPixelX: ds 1 ; current x/y coordinates for pixel drawing (used by pixel and unaligned BG sprite drawinng) 119 | hPixelY: ds 1 120 | hPixelData: ds 1 121 | hSpriteUnaligned: ds 1 ; flag indicating if BG sprite being drawn is unaligned to the grid 122 | 123 | SECTION "Varvara Vectors", ROM0 124 | 125 | VectorHandlers:: 126 | 127 | ; controller vector 128 | ld hl, wDevices + $80 129 | ld a, [hli] 130 | ld c, [hl] 131 | ld b, a 132 | or c 133 | jr z, .noControllerVector 134 | 135 | ; Check for change in controller state 136 | ldh a, [hPriorKeys] 137 | ld d, a 138 | ldh a, [hHeldKeys] 139 | cp d 140 | jr z, .noControllerVector 141 | ldh [hPriorKeys], a 142 | 143 | ld hl, eUXNMemory 144 | add hl, bc 145 | 146 | ld a, h 147 | ldh [hPC], a 148 | ld a, l 149 | ldh [hPC+1], a 150 | 151 | call uxn_eval ; eval instructions until a BRK is hit 152 | .noControllerVector 153 | 154 | ; screen vector 155 | ld hl, wDevices + $20 156 | ld a, [hli] 157 | ld c, [hl] 158 | ld b, a 159 | or c 160 | jr z, .noScreenVector 161 | 162 | ld hl, eUXNMemory 163 | add hl, bc 164 | 165 | ld a, h 166 | ldh [hPC], a 167 | ld a, l 168 | ldh [hPC+1], a 169 | 170 | call uxn_eval ; eval instructions until a BRK is hit 171 | 172 | ; There's a decent chance the screen vector performed OAM updates, so 173 | ; queue up an OAM DMA once all updates have been performed (to prevent 174 | ; tearing in the likely case updates span more than one vblank). 175 | ld a, HIGH(wShadowOAM) 176 | ldh [hOAMHigh], a 177 | 178 | .noScreenVector 179 | 180 | ret 181 | 182 | 183 | SECTION "Varvara Devices", ROM0 184 | 185 | DevSystemDEI:: 186 | ret 187 | 188 | DevSystemDEI2:: 189 | ret 190 | 191 | ; d = device 192 | ; b = data 193 | DevSystemDEO:: 194 | 195 | ; TODO: Check for any writes to RGB range 196 | 197 | ; ld a, d 198 | ; cp $08 199 | ; jr nz, .notRed 200 | ; call UpdatePalette 201 | ; .notRed 202 | ; cp $0a 203 | ; jr nz, .notGreen 204 | ; call UpdatePalette 205 | ; .notGreen 206 | ; cp $0c 207 | ; jr nz, .notBlue 208 | ; call UpdatePalette 209 | ; .notBlue 210 | 211 | ret 212 | 213 | IF ENABLE_DMG_PALETTES 214 | include "res/dmg_palette_lookup_generated.asm" 215 | ENDC 216 | 217 | ; Convert the color values stored in the system device to a host-compatible palette 218 | ; and then queue up a palette update for the next VBlank 219 | UpdatePalette: 220 | 221 | ldh a, [hConsoleType] 222 | or a 223 | jr z, .gbc 224 | 225 | IF ENABLE_DMG_PALETTES 226 | ; For DMG convert the RGB to a 2 bit value 227 | ; Input: 4bit RGB for each of 4 channels, stored in 6 bytes 228 | ; Output: 2bit brightness value for each of 4 channels 229 | ld hl, wDevices + $08 + 1 230 | call ConvertTwoShades 231 | call ConvertTwoShades 232 | 233 | ld a, c 234 | ldh [rBGP], a 235 | ldh [rOBP0], a 236 | ENDC 237 | 238 | ret 239 | .gbc 240 | 241 | ; For CGB convert from 12 bit to 15 bit RGB 242 | ; CGB: xBBBBBGG_GGGRRRRR 243 | ld hl, wDevices + $08 244 | ld bc, hPendingPalettes 245 | call ConvertTwoColors 246 | call ConvertTwoColors 247 | 248 | ld a, 1 249 | ldh [hPalettePending], a 250 | .done 251 | ret 252 | 253 | IF ENABLE_DMG_PALETTES 254 | ConvertTwoShades: 255 | ; color 0 256 | ld a, [hli] ; red 257 | inc l 258 | and %00001100 259 | add a 260 | add a 261 | ld b, a 262 | ld a, [hli] ; green 263 | inc l 264 | and %00001100 265 | or b 266 | ld b, a 267 | ld a, [hld] ; blue 268 | dec l 269 | dec l 270 | dec l 271 | and %00001100 272 | srl a 273 | srl a 274 | or b 275 | ; Now we have this color in the form: %00rrggbb 276 | push hl 277 | ld hl, DMGPaletteLookup 278 | add l ; offset to table value for this RGB value 279 | ld l, a 280 | adc h 281 | sub l 282 | ld h, a 283 | ld a, [hl] 284 | pop hl 285 | 286 | rrca ; shift intensity bits into final palette in C 287 | rl c 288 | rrca 289 | rl c 290 | 291 | ; color 1 292 | ld a, [hli] ; red 293 | inc l 294 | and %11000000 295 | ld b, a 296 | ld a, [hli] ; green 297 | inc l 298 | and %11000000 299 | srl a 300 | srl a 301 | or b 302 | ld b, a 303 | ld a, [hld] ; blue 304 | dec l 305 | dec l 306 | dec l 307 | dec l 308 | and %11000000 309 | swap a 310 | or b 311 | srl a 312 | srl a 313 | ; Now we have this color in the form: %00rrggbb 314 | push hl 315 | ld hl, DMGPaletteLookup 316 | add l ; offset to table value for this RGB value 317 | ld l, a 318 | adc h 319 | sub l 320 | ld h, a 321 | ld a, [hl] 322 | pop hl 323 | 324 | rrca ; shift intensity bits into final palette in C 325 | rl c 326 | rrca 327 | rl c 328 | 329 | ret 330 | ENDC 331 | 332 | ConvertTwoColors: 333 | ; color 0 334 | ld a, [hli] ; red 335 | inc l 336 | and $f0 337 | swap a 338 | add a 339 | ld e, a 340 | ld a, [hli] ; green 341 | inc l 342 | and $f0 343 | ld d, a 344 | add a 345 | or e 346 | ld [bc], a ; low byte of color 0 347 | inc bc 348 | ld a, d 349 | swap a 350 | sra a 351 | sra a 352 | ld d, a 353 | ld a, [hld] ; blue 354 | dec l 355 | dec l 356 | dec l 357 | and $f0 358 | sra a 359 | or d 360 | ld [bc], a ; high byte of color 0 361 | inc bc 362 | 363 | ; color 1 364 | ld a, [hli] ; red 365 | inc l 366 | and $0f 367 | add a 368 | ld e, a 369 | ld a, [hli] ; green 370 | inc l 371 | and $0f 372 | ld d, a 373 | swap a 374 | add a 375 | or e 376 | ld [bc], a ; low byte of color 1 377 | inc bc 378 | ld a, d 379 | sra a 380 | sra a 381 | ld d, a 382 | ld a, [hld] ; blue 383 | dec l 384 | dec l 385 | and $0f 386 | swap a 387 | sra a 388 | or d 389 | ld [bc], a ; high byte of color 1 390 | inc bc 391 | 392 | ret 393 | 394 | ; d = device 395 | ; bc = data 396 | DevSystemDEO2:: 397 | 398 | ld a, d 399 | cp $08 400 | jr nz, .notRed 401 | call UpdatePalette 402 | .notRed 403 | cp $0a 404 | jr nz, .notGreen 405 | call UpdatePalette 406 | .notGreen 407 | cp $0c 408 | jr nz, .notBlue 409 | call UpdatePalette 410 | .notBlue 411 | 412 | ret 413 | 414 | ; d = device 415 | ; b = data 416 | DevConsoleDEO:: 417 | ret 418 | 419 | ; d = device 420 | ; bc = data 421 | DevConsoleDEO2:: 422 | ; TODO: write to console 423 | ret 424 | 425 | ; d = device 426 | ; bc = data 427 | DevScreenDEI:: 428 | ret 429 | 430 | ; d = device 431 | ; bc = data 432 | DevScreenDEI2:: 433 | ret 434 | 435 | ; d = device 436 | ; b = data 437 | DevScreenDEO:: 438 | ld a, b 439 | ldh [hDataByte], a 440 | ; Determine operation 441 | ld a, d 442 | ldh [hDeviceByte], a 443 | cp $2e 444 | jr z, .pixel 445 | cp $2f 446 | jp z, .sprite 447 | ret 448 | 449 | .pixel 450 | ld a, b 451 | bit 6, a 452 | jp nz, .pixelFG 453 | 454 | ; background pixel 455 | ld de, wDevices + $2b ; low(y) 456 | ld a, [de] 457 | ldh [hPixelY], a 458 | dec e 459 | dec e 460 | ld a, [de] ; low(x) 461 | ldh [hPixelX], a 462 | ldh a, [hDataByte] 463 | ldh [hPixelData], a 464 | 465 | call PixelDraw 466 | 467 | ; auto-advance based on auto flag 468 | ld a, [wDevices + $26] 469 | bit 0, a 470 | jr z, .pixel_noAutoX 471 | ld hl, wDevices + $28 472 | ld a, [hli] 473 | ld b, a 474 | ld a, [hl] 475 | ld c, a 476 | inc bc 477 | ld a, c 478 | ld [hld], a 479 | ld a, b 480 | ld [hl], a 481 | .pixel_noAutoX 482 | ld a, [wDevices + $26] 483 | bit 1, a 484 | jr z, .pixel_noAutoY 485 | ld hl, wDevices + $2a 486 | ld a, [hli] 487 | ld b, a 488 | ld a, [hl] 489 | ld c, a 490 | inc bc 491 | ld a, c 492 | ld [hld], a 493 | ld a, b 494 | ld [hl], a 495 | .pixel_noAutoY 496 | 497 | ret 498 | 499 | .pixelFG 500 | ; TODO: Find a way to efficiently use the limited tile VRAM and hardware objects 501 | ; to render foreground pixels. 502 | ret 503 | 504 | .sprite 505 | 506 | ; Get BlendingTable values for current blend value combination 507 | ldh a, [hDataByte] 508 | and $0f ; only retain blend nibble 509 | ld hl, PixelBlendingTable 510 | add a 511 | add a 512 | add l 513 | ld l, a 514 | adc h 515 | sub l 516 | ld h, a 517 | ld a, [hli] 518 | ld [wPixelBlend], a 519 | ld a, [hli] 520 | ld [wPixelBlend+1], a 521 | ld a, [hli] 522 | ld [wPixelBlend+2], a 523 | ld a, [hl] 524 | ld [wPixelBlend+3], a 525 | 526 | ld a, [wDevices + $26] ; auto 527 | ld c, a 528 | and $f0 529 | swap a 530 | inc a ; inc to make loop simpler 531 | ldh [hAutoLen], a 532 | ld a, c 533 | and %00000100 ; auto addr 534 | ldh [hAutoAddr], a 535 | ld a, c 536 | and %00000001 ; autoX * 8 for deltaX 537 | add a 538 | add a 539 | add a 540 | ldh [hDeltaX], a 541 | ld a, c 542 | and %00000010 ; autoX * 8 for deltaY 543 | add a 544 | add a 545 | ldh [hDeltaY], a 546 | 547 | ld a, b ; get hDataByte 548 | bit 6, a 549 | jp nz, .spriteFG 550 | 551 | .spriteBG 552 | ; background 'sprite' 553 | 554 | ; initialize working x/y coordinates 555 | ; TODO: Check high byte to see if we should just not render this at all 556 | ld hl, wDevices + $29 557 | ld a, [hli] ; low(x) 558 | ldh [hWorkingX], a 559 | and %00000111 ; keep unaligned portion 560 | ld d, a 561 | inc l 562 | ld a, [hli] ; low(y) 563 | ldh [hWorkingY], a 564 | and %00000111 ; keep unaligned portion 565 | or d ; combine with unaligned Y coordinate 566 | ldh [hSpriteUnaligned], a ; flag to trigger use of (much) slower unaligned sprite drawing approach 567 | 568 | ; TODO: Make 1bpp/2bpp both copy the prepared bytes to a buffer which is then 569 | ; processed the same, and results in a prepared blob to copy to VRAM either 570 | ; aligned or unaligned as needed (handling transparency properly may complicate 571 | ; that). 572 | ldh a, [hDataByte] 573 | bit 7, a 574 | call z, BGSprite1bpp 575 | ldh a, [hDataByte] 576 | bit 7, a 577 | call nz, BGSprite2bpp 578 | 579 | ; perform final auto adjustments 580 | 581 | ; Note: The auto x/y functions in a somewhat unintuitive manner, but is quite clever in allowing 582 | ; automatic sprite layout over both dimensions since the opposite deltas are applied during a single 583 | ; auto write, and then applied to the named dimensions afterwards, setting up subsequent writes. 584 | ld hl, wDevices + $29 ; low(x) 585 | ldh a, [hDeltaX] 586 | add [hl] 587 | ld [hli], a 588 | inc l 589 | ldh a, [hDeltaY] 590 | add [hl] 591 | ld [hl], a 592 | 593 | ret 594 | 595 | .spriteFG 596 | ; initialize working x/y coordinates 597 | ; TODO: Check high byte to see if we should just not render this at all 598 | ld hl, wDevices + $29 599 | ld a, [hli] ; low(x) 600 | ldh [hWorkingX], a 601 | inc l 602 | ld a, [hli] ; low(y) 603 | ldh [hWorkingY], a 604 | 605 | ldh a, [hDataByte] 606 | bit 7, a 607 | call z, FGSprite1bpp 608 | ldh a, [hDataByte] 609 | bit 7, a 610 | call nz, FGSprite2bpp 611 | 612 | ; perform final auto adjustments 613 | 614 | ld hl, wDevices + $29 ; low(x) 615 | ldh a, [hDeltaX] 616 | add [hl] 617 | ld [hli], a 618 | inc l 619 | ldh a, [hDeltaY] 620 | add [hl] 621 | ld [hl], a 622 | 623 | ret 624 | 625 | ; Draw a pixel at hPixelX/hPixelY with color based on hDataByte 626 | ; Destroys: AF, BC, HL, DE 627 | PixelDraw: 628 | ldh a, [hPixelY] 629 | cp 144 630 | ret nc ; out of Y range 631 | 632 | ; aligned tile address is: 633 | ; y/8*20*16 634 | srl a ; y/8 635 | srl a 636 | and %11111110 637 | ld hl, Y_TIMES_320_VRAM 638 | add l 639 | ld l, a 640 | adc h 641 | sub l 642 | ld h, a 643 | ld a, [hli] ; y/8*320 644 | ld b, [hl] 645 | ld c, a ; bc=$8000+y/8*320 646 | 647 | ldh a, [hPixelX] 648 | cp 160 649 | ret nc ; out of X range 650 | srl a ; x/8 651 | srl a 652 | srl a 653 | swap a ; x/8*16, overflow bit in lsb 654 | ld l, a 655 | ld h, 0 656 | srl l ; move possible overflow bit to carry 657 | rl h ; move possible overflow bit to H 658 | sla l ; restore L minus bit 659 | add hl, bc 660 | 661 | ; Add non-aligned Y 662 | ldh a, [hPixelY] 663 | and %00000111 ; retain only sub-tile component 664 | add a ; double (2 bytes per pixel row) 665 | ld c, a 666 | ld b, 0 667 | add hl, bc 668 | 669 | ldh a, [hPixelX] 670 | and %00000111 ; retain only sub-tile component 671 | ld c, a ; number of bits to shift before pixel insertion 672 | 673 | ld d, %01111111 ; mask to clear bit 674 | ld a, c 675 | or a 676 | jr z, .mask_ready 677 | scf 678 | .loop 679 | rr d 680 | dec c 681 | jr nz, .loop 682 | .mask_ready 683 | ld a, %11111111 684 | xor d 685 | ld e, a ; mask to set bit is the inverse of the mask to clear a bit 686 | 687 | ldh a, [hPixelData] 688 | and $03 ; TODO: What do values above 3 do? 689 | ld c, a ; 0-3 value to write 690 | 691 | : ldh a, [rSTAT] 692 | and STATF_BUSY 693 | jr nz, :- 694 | ld b, [hl] ; read current byte value 695 | 696 | srl c ; shift lsb into carry to see if we should set or clear bit 697 | ld a, b 698 | jr c, .set0 699 | and d ; and with clear mask 700 | jr .done0 701 | .set0 702 | or e ; and with set mask 703 | .done0 704 | ld b, a 705 | 706 | : ldh a, [rSTAT] 707 | and STATF_BUSY 708 | jr nz, :- 709 | ld [hl], b ; write new byte value 710 | 711 | ; second byte! 712 | inc l 713 | ld b, [hl] ; read current byte value 714 | 715 | srl c ; shift lsb into carry to see if we should set or clear bit 716 | ld a, b 717 | jr c, .set1 718 | and d ; and with clear mask 719 | jr .done1 720 | .set1 721 | or e ; and with set mask 722 | .done1 723 | ld b, a 724 | 725 | : ldh a, [rSTAT] 726 | and STATF_BUSY 727 | jr nz, :- 728 | ld [hl], b ; write new byte value 729 | 730 | ret 731 | 732 | LocateTargetSpriteTileVRAM: 733 | ; Current sprite hiding approach: 734 | ; - When blend=0, return the tileID of the first entry which matches the source addr 735 | ; - The calling code will then hide the first sprite at the given x/y coordinates which 736 | ; uses that tileID 737 | ; - This will fail if the same tile is blended differently but used at the same x/y 738 | ; location, but will at least clear out the invisible sprites 739 | 740 | ; First check for matching entry, while noting the low byte of the last empty entry found 741 | ldh a, [hDataByte] 742 | and %10001111 ; only keep the bits that affect tile uniqueness 743 | ld b, a 744 | ld hl, wDevices + $2c ; get source addr of UXN tile data 745 | ld a, [hli] 746 | ld d, a 747 | ld e, [hl] 748 | 749 | ld hl, wObjSourceAddrs 750 | 751 | .matchLoop 752 | ld a, b ; check if blend==0 (erase sprite) 753 | or a 754 | jr z, .skipBlendCheck 755 | ld a, [hl] 756 | cp b ; compare to our target blend value 757 | jr nz, .blendDifferent 758 | .skipBlendCheck 759 | inc l 760 | ld a, [hli] 761 | cp d 762 | jr nz, .addrDifferent 763 | ld a, [hl] 764 | cp e 765 | jr nz, .addrDifferent 766 | ; blend and addr match, use the existing VRAM entry! 767 | 768 | ; TileID of sprite to hide 769 | ld a, l 770 | srl a 771 | srl a 772 | add $f0 773 | ld [wSpriteTileID], a 774 | 775 | ; Also returned as non-zero A to indicate no tile copy required 776 | 777 | ret 778 | 779 | .blendDifferent 780 | .addrDifferent 781 | ; advance to next entry 782 | ld a, l 783 | or 4-1 784 | inc a 785 | ld l, a 786 | cp LOW(wObjSourceAddrs.end) 787 | jr nz, .matchLoop 788 | 789 | ; Reached end without finding a match! 790 | 791 | ld a, b 792 | or a 793 | jr nz, .notErasing 794 | ; If we failed to find a matching entry for an erase blend tile it's likely 795 | ; the first pass of a 'standard' erase/draw cycle. Don't bother rendering 796 | ; an emptry tile and wasting a slot. 797 | 798 | ld [wSpriteTileID], a ; indicate no sprite should be drawn 799 | 800 | ; Return 1 to bypass tile rendering 801 | inc a 802 | ret 803 | 804 | .notErasing 805 | 806 | ; Store our blend/addr values in the next empty entry 807 | ld hl, wObjSourceEmpty 808 | ld l, [hl] 809 | 810 | ; Store table entry 811 | ld a, b ; blend value 812 | ld [hli], a 813 | ld a, d 814 | ld [hli], a ; addr 815 | ld [hl], e 816 | 817 | ; Set the following entry as the next empty entry 818 | inc l 819 | ld a, l 820 | inc a 821 | and %00111111 ; wrap to stay within table 822 | ld [wObjSourceEmpty], a ; update new empty entry 823 | 824 | ; Calculate tileID from table low byte value 825 | ld a, l 826 | srl a 827 | srl a 828 | add $f0 829 | ld [wSpriteTileID], a 830 | 831 | ; Calculate VRAM address from table low byte value 832 | ld a, l 833 | and %11111100 ; remove extra bits 834 | add a 835 | add a 836 | ld c, a 837 | ld b, 0 838 | ld hl, vForegroundTiles 839 | add hl, bc 840 | 841 | ld a, l 842 | ld [wSpriteTileAddr], a 843 | ld a, h 844 | ld [wSpriteTileAddr+1], a 845 | 846 | ; return A=0 to indicate copy is required 847 | xor a 848 | 849 | ret 850 | 851 | CreateOAMEntry: 852 | ld e, a ; cache tileID 853 | 854 | ; Prepare OAM values both for hideSeek (blend=0) or creation 855 | ldh a, [hWorkingY] 856 | add $10 857 | ld b, a 858 | ldh a, [hWorkingX] 859 | add $08 860 | ld c, a 861 | ldh a, [hDataByte] ; apply tile flips to hardware object 862 | ld l, a 863 | and %00110000 ; only retain flip bits 864 | add a ; UXN flip bits are shifted one over from GB 865 | ld d, a 866 | 867 | ld a, l 868 | and $0f ; get blend value, 0=hidden 869 | jr nz, .spriteVisible 870 | ; sprite blended to zero, locate sprite at this x/y and tileID, 871 | ; and hide it 872 | 873 | ld hl, wShadowOAM 874 | : ld a, [hli] 875 | cp b 876 | jr nz, .hideSeek 877 | ld a, [hli] 878 | cp c 879 | jr nz, .hideSeek 880 | ld a, [hli] 881 | cp e 882 | jr nz, .hideSeek 883 | ld a, [hld] 884 | cp d 885 | jr nz, .hideSeek 886 | 887 | ; Hide this sprite! 888 | dec l 889 | dec l 890 | xor a ; set y=0 to hide the sprite without affecting the 10spr/line limit 891 | ld [hl], a 892 | jr .resume 893 | .hideSeek 894 | ld a, l 895 | or 4-1 896 | inc a 897 | ld l, a 898 | cp $A0 ; loop until the end of OAM 899 | jr nz, :- 900 | ; Match not found, UXN software is clearing something it didn't draw, 901 | ; or our convoluted hide mechanism got confused. Reset to 0th OAM entry. 902 | ld l, 0 903 | jr .resume 904 | 905 | 906 | .spriteVisible 907 | ld hl, wOAMIndex ; point to next unused OAM entry 908 | ld l, [hl] 909 | 910 | ld a, b 911 | ld [hli], a 912 | ld a, c 913 | ld [hli], a 914 | ld a, e 915 | ld [hli], a 916 | ld a, d 917 | ld [hli], a 918 | 919 | .resume 920 | ld a, l 921 | cp $A0 922 | jr c, .inRange 923 | xor a 924 | .inRange 925 | ld [wOAMIndex], a ; update next unused OAM entry index 926 | 927 | ret 928 | 929 | FGSprite1bpp: 930 | ldh a, [hWorkingX] 931 | cp 161 932 | jr nc, .outOfRange 933 | ldh a, [hWorkingY] 934 | cp 144 935 | jr nc, .outOfRange 936 | 937 | call LocateTargetSpriteTileVRAM 938 | or a 939 | jr nz, .CreateOAMEntry 940 | 941 | ld b, 0 ; disable software flipping of tiles 942 | call TileToBuffer1bpp 943 | ld a, [wSpriteTileAddr] 944 | ld l, a 945 | ld a, [wSpriteTileAddr+1] 946 | ld h, a 947 | call Render1bppTile 948 | 949 | .CreateOAMEntry 950 | ld a, [wSpriteTileID] 951 | or a 952 | jr z, .skipOAM 953 | call CreateOAMEntry 954 | .skipOAM 955 | 956 | .outOfRange 957 | ld d, 8 ; 8 byte offset between 1bpp tiles 958 | call ApplyAutoAdjustments 959 | jp nz, FGSprite1bpp 960 | 961 | ret 962 | 963 | 964 | FGSprite2bpp: 965 | ldh a, [hWorkingX] 966 | cp 161 967 | jr nc, .outOfRange 968 | ldh a, [hWorkingY] 969 | cp 144 970 | jr nc, .outOfRange 971 | 972 | call LocateTargetSpriteTileVRAM 973 | or a 974 | jr nz, .CreateOAMEntry 975 | 976 | ld b, 0 ; disable software flipping of tiles 977 | call TileToBuffer2bpp 978 | ld a, [wSpriteTileAddr] 979 | ld l, a 980 | ld a, [wSpriteTileAddr+1] 981 | ld h, a 982 | call Render2bppTile 983 | 984 | .CreateOAMEntry 985 | ld a, [wSpriteTileID] 986 | or a 987 | jr z, .skipOAM 988 | call CreateOAMEntry 989 | .skipOAM 990 | 991 | .outOfRange 992 | ld d, 16 993 | call ApplyAutoAdjustments 994 | jp nz, FGSprite2bpp 995 | 996 | ret 997 | 998 | 999 | ; Copy a 1bpp UXN tile pointed to by Screen.addr to the wTileBuffer, including flips 1000 | ; Args: B contains the data byte (passed to allow foreground sprites to bypaass 1001 | ; software tile flipping) 1002 | TileToBuffer1bpp: 1003 | ; Locate UXN addr in SRAM (TODO: Account for banks) 1004 | ld hl, wDevices + $2c ; addr 1005 | ld d, [hl] 1006 | inc l 1007 | ld e, [hl] 1008 | ld hl, eUXNMemory 1009 | add hl, de 1010 | ld d, h 1011 | ld e, l 1012 | ; TODO: Handle bank spanning during tile data copy 1013 | 1014 | ; Copy tile bytes to WRAM buffer to simplify pointer management during blit 1015 | ld a, b 1016 | and %00110000 ; keep only flip bits 1017 | or a 1018 | jr z, .noFlip 1019 | cp %00100000 ; flipy only? 1020 | jr z, .flipy 1021 | cp %00010000 ; flipx only? 1022 | jr z, .flipx 1023 | ; flipx & flipy 1024 | ld hl, wTileBuffer + $07 1025 | ld c, 8 1026 | : ld a, [de] 1027 | ld b, a 1028 | rlca 1029 | rlca 1030 | xor b 1031 | and $AA 1032 | xor b 1033 | ld b, a 1034 | rlca 1035 | rlca 1036 | rlca 1037 | rrc b 1038 | xor b 1039 | and $66 1040 | xor b 1041 | ld [hld], a 1042 | inc de 1043 | dec c 1044 | jr nz, :- 1045 | 1046 | jr .tile_ready 1047 | .flipx 1048 | ; flip bit order for bytes as we copy 1049 | ; Based on: http://www.retroprogramming.com/2014/01/fast-z80-bit-reversal.html 1050 | ld hl, wTileBuffer 1051 | ld c, 8 1052 | : ld a, [de] 1053 | ld b, a 1054 | rlca 1055 | rlca 1056 | xor b 1057 | and $AA 1058 | xor b 1059 | ld b, a 1060 | rlca 1061 | rlca 1062 | rlca 1063 | rrc b 1064 | xor b 1065 | and $66 1066 | xor b 1067 | ld [hli], a 1068 | inc de 1069 | dec c 1070 | jr nz, :- 1071 | jr .tile_ready 1072 | .flipy 1073 | ; Copy bytes backwards 1074 | ld c, 8 1075 | ld hl, wTileBuffer + $07 1076 | : ld a, [de] 1077 | ld [hld], a 1078 | inc de 1079 | dec c 1080 | jr nz, :- 1081 | jr .tile_ready 1082 | .noFlip 1083 | ld hl, wTileBuffer 1084 | ld c, 16 1085 | rst MemcpySmall 1086 | .tile_ready 1087 | 1088 | ret 1089 | 1090 | ; Render a 1bpp tile from wTileBuffer to HL (tile VRAM) 1091 | Render1bppTile: 1092 | ld de, wTileBuffer 1093 | ld b, 8 ; byte counter 1094 | .vLoop 1095 | ld a, [de] ; setup working bytes for this 8-pixel row 1096 | inc de 1097 | push de 1098 | ld d, a 1099 | ld e, 0 ; high byte is always zero for 1bpp 1100 | 1101 | push hl 1102 | 1103 | ld h, HIGH(wPixelBlend) 1104 | ld c, 8 ; bit counter 1105 | .bitLoop 1106 | xor a 1107 | sla e ; shift high bit into carry 1108 | rl a ; shift carry into A 1109 | sla d ; shift low bit into carry 1110 | rl a ; shift carry into A (now 'ch' 0-3 for a given pixel) 1111 | 1112 | ld l, a 1113 | ld l, [hl] ; H = blended pixel value (0-3) 1114 | 1115 | rr d ; shimmy bits back 1116 | rr l ; shift low bit into carry 1117 | rl d ; shift low bit into low working byte 1118 | rr e ; shimmy bits back 1119 | rr l ; shift high bit into carry 1120 | rl e ; shift hit bit into high working byte 1121 | 1122 | dec c 1123 | jr nz, .bitLoop 1124 | 1125 | pop hl 1126 | 1127 | ; working byte is now ready, copy to VRAM 1128 | : ldh a, [rSTAT] 1129 | and STATF_BUSY 1130 | jr nz, :- 1131 | ld [hl], d 1132 | inc l 1133 | 1134 | : ldh a, [rSTAT] 1135 | and STATF_BUSY 1136 | jr nz, :- 1137 | ld [hl], e 1138 | inc l 1139 | 1140 | pop de 1141 | 1142 | dec b 1143 | jr nz, .vLoop 1144 | 1145 | ret 1146 | 1147 | 1148 | BGSprite1bpp: 1149 | ldh a, [hDataByte] 1150 | ld b, a 1151 | call TileToBuffer1bpp 1152 | 1153 | ldh a, [hSpriteUnaligned] 1154 | or a 1155 | jr z, .aligned 1156 | 1157 | ; unaligned sprites perform 64 subsequent PixelDraw calls! (this is lethally inefficient) 1158 | ldh a, [hWorkingY] 1159 | ldh [hPixelY], a 1160 | ldh a, [hWorkingX] 1161 | ldh [hPixelX], a 1162 | 1163 | ld de, wTileBuffer 1164 | ld b, 8 ; byte counter 1165 | .vLoop 1166 | 1167 | ld a, [de] ; setup working bytes for this 8-pixel row 1168 | inc de 1169 | ldh [hWorkingBytes], a 1170 | xor a ; high byte is always zero for 1bpp 1171 | ldh [hWorkingBytes+1], a 1172 | 1173 | push de 1174 | ld c, 8 ; bit counter 1175 | .bitLoop 1176 | push bc 1177 | ldh a, [hWorkingBytes] 1178 | ld b, a 1179 | rla ; bump bits over a bit for next pass 1180 | ldh [hWorkingBytes], a 1181 | ldh a, [hWorkingBytes+1] 1182 | ld c, a 1183 | rla ; bump bits over a bit for next pass 1184 | ldh [hWorkingBytes+1], a 1185 | 1186 | xor a 1187 | sla c ; shift high bit into carry 1188 | rl a ; shift carry into A 1189 | sla b ; shift low bit into carry 1190 | rl a ; shift carry into A (now 'ch' 0-3 for a given pixel) 1191 | 1192 | ld h, HIGH(wPixelBlend) 1193 | ld l, a 1194 | ld h, [hl] ; H = blended pixel value (0-3) 1195 | 1196 | ld a, h 1197 | ldh [hPixelData], a 1198 | 1199 | call PixelDraw 1200 | 1201 | ldh a, [hPixelX] 1202 | inc a 1203 | ldh [hPixelX], a 1204 | 1205 | pop bc 1206 | 1207 | .nextPixel 1208 | dec c 1209 | jr nz, .bitLoop 1210 | pop de 1211 | 1212 | ldh a, [hWorkingX] 1213 | ldh [hPixelX], a 1214 | ldh a, [hPixelY] 1215 | inc a 1216 | ldh [hPixelY], a 1217 | 1218 | dec b 1219 | jr nz, .vLoop 1220 | jr .autoAdvance 1221 | 1222 | .aligned 1223 | ; Locate target tile VRAM address 1224 | 1225 | ldh a, [hWorkingY] 1226 | cp 144 1227 | jp nc, .yOutOfRange 1228 | ; aligned tile address is: 1229 | ; y/8*20*16 1230 | srl a ; y/8 1231 | srl a 1232 | and %11111110 1233 | ld hl, Y_TIMES_320_VRAM 1234 | add l 1235 | ld l, a 1236 | adc h 1237 | sub l 1238 | ld h, a 1239 | ld a, [hli] ; y/8*320 1240 | ld b, [hl] 1241 | ld c, a ; bc=$8000+y/8*320 1242 | 1243 | ldh a, [hWorkingX] 1244 | cp 160 1245 | jp nc, .xOutOfRange 1246 | srl a ; x/8 1247 | srl a 1248 | srl a 1249 | swap a ; x/8*16, overflow bit in lsb 1250 | ld l, a 1251 | ld h, 0 1252 | srl l ; move possible overflow bit to carry 1253 | rl h ; move possible overflow bit to H 1254 | sla l ; restore L minus bit 1255 | add hl, bc 1256 | 1257 | call Render1bppTile 1258 | 1259 | .yOutOfRange 1260 | .xOutOfRange 1261 | .autoAdvance 1262 | 1263 | ld d, 8 ; 8 byte offset between 1bpp tiles 1264 | call ApplyAutoAdjustments 1265 | jp nz, BGSprite1bpp 1266 | 1267 | ret 1268 | 1269 | ; Apply auto x/y/addr: 1270 | ; Args: D is addr delta to apply in bytes 1271 | ApplyAutoAdjustments: 1272 | ; apply auto adjustments 1273 | ld hl, hWorkingX 1274 | ld a, [hli] ; get hWorkingX 1275 | add [hl] ; add hDeltaY (yes) 1276 | dec l 1277 | ld [hli], a ; store new hWorkingX 1278 | inc l 1279 | ld a, [hli] ; get hWorkingY 1280 | add [hl] ; add hDeltaX (yes) 1281 | dec l 1282 | ld [hli], a ; store new hWorkingY 1283 | 1284 | ldh a, [hAutoAddr] 1285 | or a 1286 | jr z, .doneAutoAddr 1287 | ld hl, wDevices + $2c 1288 | ld a, [hli] 1289 | ld c, [hl] 1290 | ld b, a 1291 | ld a, d 1292 | add c 1293 | ld c, a 1294 | adc b 1295 | sub c 1296 | ld b, a 1297 | ld [hl], c 1298 | dec l 1299 | ld [hl], b 1300 | .doneAutoAddr 1301 | 1302 | ldh a, [hAutoLen] 1303 | dec a 1304 | ldh [hAutoLen], a 1305 | 1306 | ret 1307 | 1308 | ; Copy a 2bpp UXN tile pointed to by Screen.addr to the wTileBuffer, including flips 1309 | ; Args: B contains the data byte (passed to allow foreground sprites to bypaass 1310 | ; software tile flipping) 1311 | TileToBuffer2bpp: 1312 | 1313 | ; Locate UXN addr in SRAM (TODO: Account for banks) 1314 | ld hl, wDevices + $2c ; addr 1315 | ld d, [hl] 1316 | inc l 1317 | ld e, [hl] 1318 | ld hl, eUXNMemory 1319 | add hl, de 1320 | ld d, h 1321 | ld e, l 1322 | 1323 | ; Copy tile bytes to WRAM buffer to simplify pointer management during blit 1324 | ld a, b 1325 | and %00110000 ; keep flip bits 1326 | or a 1327 | jr z, .noFlip 1328 | cp %00100000 ; flipy only? 1329 | jr z, .flipy 1330 | cp %00010000 ; flipx only? 1331 | jr z, .flipx 1332 | ; flipx & flipy 1333 | ld hl, wTileBuffer + $07 1334 | ld c, 8 1335 | : ld a, [de] 1336 | ld b, a 1337 | rlca 1338 | rlca 1339 | xor b 1340 | and $AA 1341 | xor b 1342 | ld b, a 1343 | rlca 1344 | rlca 1345 | rlca 1346 | rrc b 1347 | xor b 1348 | and $66 1349 | xor b 1350 | ld [hld], a 1351 | inc de 1352 | dec c 1353 | jr nz, :- 1354 | 1355 | ASSERT(HIGH(wTileBuffer) == HIGH(wTileBuffer+$0f)) 1356 | ld l, LOW(wTileBuffer) + $0f 1357 | ld c, 8 1358 | 1359 | : ld a, [de] 1360 | ld b, a 1361 | rlca 1362 | rlca 1363 | xor b 1364 | and $AA 1365 | xor b 1366 | ld b, a 1367 | rlca 1368 | rlca 1369 | rlca 1370 | rrc b 1371 | xor b 1372 | and $66 1373 | xor b 1374 | ld [hld], a 1375 | inc de 1376 | dec c 1377 | jr nz, :- 1378 | 1379 | jr .tile_ready 1380 | .flipx 1381 | ; flip bit order for bytes as we copy 1382 | ; Based on: http://www.retroprogramming.com/2014/01/fast-z80-bit-reversal.html 1383 | ld hl, wTileBuffer 1384 | ld c, 16 1385 | : ld a, [de] 1386 | ld b, a 1387 | rlca 1388 | rlca 1389 | xor b 1390 | and $AA 1391 | xor b 1392 | ld b, a 1393 | rlca 1394 | rlca 1395 | rlca 1396 | rrc b 1397 | xor b 1398 | and $66 1399 | xor b 1400 | ld [hli], a 1401 | inc de 1402 | dec c 1403 | jr nz, :- 1404 | jr .tile_ready 1405 | .flipy 1406 | ; Copy 8 bytes backwards twice (low and high bytes are split, UXN style) 1407 | ld c, 8 1408 | ld hl, wTileBuffer + $07 1409 | : ld a, [de] 1410 | ld [hld], a 1411 | inc de 1412 | dec c 1413 | jr nz, :- 1414 | ASSERT(HIGH(wTileBuffer) == HIGH(wTileBuffer+$0f)) 1415 | ld l, LOW(wTileBuffer) + $0f 1416 | ld c, 8 1417 | : ld a, [de] 1418 | ld [hld], a 1419 | inc de 1420 | dec c 1421 | jr nz, :- 1422 | jr .tile_ready 1423 | .noFlip 1424 | ld hl, wTileBuffer 1425 | ld c, 16 1426 | rst MemcpySmall 1427 | .tile_ready 1428 | 1429 | ret 1430 | 1431 | ; Render a 2bpp tile from DE (wTileBuffer) to HL (tile VRAM) 1432 | Render2bppTile: 1433 | ld de, wTileBuffer 1434 | ld b, 8 ; byte counter 1435 | .vLoop 1436 | 1437 | ld a, [de] ; setup working bytes for this 8-pixel row 1438 | inc de 1439 | push de ; setup for next 2bpp byte 1440 | 1441 | push hl ; cache target VRAM address 1442 | 1443 | ld hl, $0007 ; UXN tile data isn't interlaced like GB, so we have to span 8 bytes 1444 | add hl, de 1445 | ld d, a ; store low byte 1446 | ld e, [hl] ; get high byte 1447 | 1448 | ; Note: The bit loop is currently the same for 1bpp and 2bpp once DE is loaded 1449 | 1450 | ld h, HIGH(wPixelBlend) 1451 | ld c, 8 ; bit counter 1452 | .bitLoop 1453 | xor a 1454 | sla e ; shift high bit into carry 1455 | rl a ; shift carry into A 1456 | sla d ; shift low bit into carry 1457 | rl a ; shift carry into A (now 'ch' 0-3 for a given pixel) 1458 | 1459 | ld l, a 1460 | ld l, [hl] ; H = blended pixel value (0-3) 1461 | 1462 | rr d ; shimmy bits back 1463 | rr l ; shift low bit into carry 1464 | rl d ; shift low bit into low working byte 1465 | rr e ; shimmy bits back 1466 | rr l ; shift high bit into carry 1467 | rl e ; shift hit bit into high working byte 1468 | 1469 | dec c 1470 | jr nz, .bitLoop 1471 | 1472 | pop hl 1473 | 1474 | ; working byte is now ready, copy to VRAM 1475 | : ldh a, [rSTAT] 1476 | and STATF_BUSY 1477 | jr nz, :- 1478 | ld [hl], d 1479 | inc l 1480 | 1481 | : ldh a, [rSTAT] 1482 | and STATF_BUSY 1483 | jr nz, :- 1484 | ld [hl], e 1485 | inc l 1486 | 1487 | pop de 1488 | 1489 | dec b 1490 | jr nz, .vLoop 1491 | 1492 | ret 1493 | 1494 | BGSprite2bpp: 1495 | ldh a, [hDataByte] 1496 | ld b, a 1497 | call TileToBuffer2bpp 1498 | 1499 | ldh a, [hSpriteUnaligned] 1500 | or a 1501 | jr z, .aligned 1502 | 1503 | ; unaligned sprites perform 64 subsequent PixelDraw calls! (this is lethally inefficient) 1504 | ldh a, [hWorkingY] 1505 | ldh [hPixelY], a 1506 | ldh a, [hWorkingX] 1507 | ldh [hPixelX], a 1508 | 1509 | ld de, wTileBuffer 1510 | ld b, 8 ; byte counter 1511 | .vLoop 1512 | 1513 | ld a, [de] ; setup working bytes for this 8-pixel row 1514 | ld hl, $0008 ; UXN tile data isn't interlaced like GB, so we have to span 8 bytes 1515 | add hl, de 1516 | ldh [hWorkingBytes], a 1517 | ld a, [hl] 1518 | ld de, -$0007 ; setup for next 2bpp byte 1519 | add hl, de 1520 | ld d, h 1521 | ld e, l 1522 | ldh [hWorkingBytes+1], a 1523 | 1524 | push de 1525 | ld c, 8 ; bit counter 1526 | .bitLoop 1527 | push bc 1528 | ldh a, [hWorkingBytes] 1529 | ld b, a 1530 | rla ; bump bits over a bit for next pass 1531 | ldh [hWorkingBytes], a 1532 | ldh a, [hWorkingBytes+1] 1533 | ld c, a 1534 | rla ; bump bits over a bit for next pass 1535 | ldh [hWorkingBytes+1], a 1536 | 1537 | xor a 1538 | sla c ; shift high bit into carry 1539 | rl a ; shift carry into A 1540 | sla b ; shift low bit into carry 1541 | rl a ; shift carry into A (now 'ch' 0-3 for a given pixel) 1542 | 1543 | ld h, HIGH(wPixelBlend) 1544 | ld l, a 1545 | ld h, [hl] ; H = blended pixel value (0-3) 1546 | 1547 | ld a, h 1548 | ldh [hPixelData], a 1549 | 1550 | call PixelDraw 1551 | 1552 | ldh a, [hPixelX] 1553 | inc a 1554 | ldh [hPixelX], a 1555 | 1556 | pop bc 1557 | 1558 | .nextPixel 1559 | dec c 1560 | jr nz, .bitLoop 1561 | 1562 | ldh a, [hWorkingX] 1563 | ldh [hPixelX], a 1564 | ldh a, [hPixelY] 1565 | inc a 1566 | ldh [hPixelY], a 1567 | 1568 | pop de 1569 | 1570 | dec b 1571 | jr nz, .vLoop 1572 | jp .autoAdvance 1573 | 1574 | .aligned 1575 | ; Locate target tile VRAM address 1576 | 1577 | ldh a, [hWorkingY] 1578 | cp 144 1579 | jp nc, .yOutOfRange 1580 | ; aligned tile address is: 1581 | ; y/8*20*16 1582 | srl a ; y/8 1583 | srl a 1584 | and %11111110 1585 | ld hl, Y_TIMES_320_VRAM 1586 | add l 1587 | ld l, a 1588 | adc h 1589 | sub l 1590 | ld h, a 1591 | ld a, [hli] ; y/8*320 1592 | ld b, [hl] 1593 | ld c, a ; bc=$8000+y/8*320 1594 | 1595 | ldh a, [hWorkingX] 1596 | cp 160 1597 | jp nc, .xOutOfRange 1598 | srl a ; x/8 1599 | srl a 1600 | srl a 1601 | swap a ; x/8*16, overflow bit in lsb 1602 | ld l, a 1603 | ld h, 0 1604 | srl l ; move possible overflow bit to carry 1605 | rl h ; move possible overflow bit to H 1606 | sla l ; restore L minus bit 1607 | add hl, bc 1608 | 1609 | call Render2bppTile 1610 | 1611 | .yOutOfRange 1612 | .xOutOfRange 1613 | .autoAdvance 1614 | 1615 | ld d, 16 ; 16 byte offset between 2bpp tiles 1616 | call ApplyAutoAdjustments 1617 | jp nz, BGSprite2bpp 1618 | 1619 | ret 1620 | 1621 | 1622 | ; d = device 1623 | ; bc = data 1624 | DevScreenDEO2: 1625 | ; Prevent width/height from exceeding 160x144 screen size by resetting width/height after they're set 1626 | ; (I tried handling this in DEI2, but it's handled after the values are pushed to the stack) 1627 | ld a, d 1628 | sub $22 1629 | jr z, .width 1630 | dec a 1631 | dec a 1632 | jr z, .height 1633 | ret 1634 | .width 1635 | ld a, HIGH(160) 1636 | ld [wDevices + $22], a 1637 | ld a, LOW(160) 1638 | ld [wDevices + $23], a 1639 | ret 1640 | .height 1641 | ld a, HIGH(144) 1642 | ld [wDevices + $24], a 1643 | ld a, LOW(144) 1644 | ld [wDevices + $25], a 1645 | ret 1646 | 1647 | DevNil:: 1648 | ret 1649 | 1650 | SECTION "Y*320 VRAM Table", ROM0, ALIGN[6] 1651 | ; y*320+$8000 table - used to calculate tile VRAM addresses for 18 possible input values 1652 | Y_TIMES_320_VRAM: 1653 | dw 0+$8000, 1*320+$8000, 2*320+$8000, 3*320+$8000, 4*320+$8000, 5*320+$8000, 6*320+$8000, 7*320+$8000, 8*320+$8000, 9*320+$8000, 10*320+$8000, 11*320+$8000 1654 | ; Shift in offset pattern for tiles below the split 1655 | dw 13*320+$7FC0, 14*320+$7FC0, 15*320+$7FC0, 16*320+$7FC0, 17*320+$7FC0, 18*320+$7FC0 1656 | 1657 | SECTION "Varvara Blending", ROM0, ALIGN[8] ; TODO: May not need align[8] 1658 | 1659 | PixelBlendingTable: 1660 | db 0, 0, 1, 2 1661 | db 0, 1, 2, 3 1662 | db 0, 2, 3, 1 1663 | db 0, 3, 1, 1 1664 | db 1, 0, 1, 2 1665 | db 0, 1, 2, 3 1666 | db 1, 2, 3, 1 1667 | db 1, 3, 1, 2 1668 | db 2, 0, 1, 2 1669 | db 2, 1, 2, 3 1670 | db 0, 2, 3, 1 1671 | db 2, 3, 1, 2 1672 | db 3, 0, 1, 2 1673 | db 3, 1, 2, 3 1674 | db 3, 2, 3, 1 1675 | db 0, 3, 1, 2 1676 | 1677 | ; TODO: Apply opaque table! 1678 | ; OpaqueTable: 1679 | ; db 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0 1680 | 1681 | SECTION "Foreground Tiles", VRAM[$8000] 1682 | 1683 | vBackgroundTilesPrimary: 1684 | ds 16*240 1685 | 1686 | vForegroundTiles: 1687 | ds 16*24 1688 | 1689 | vBackgroundTilesSecondary: 1690 | ds 16*120 -------------------------------------------------------------------------------- /src/uxn_instr.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Core CPU Instructions for uxnemu 3 | ; 4 | ; Copyright 2022 Dave VanEe 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | SECTION "DIV2 WRAM", WRAM0[$DCA2] 24 | _MD16temp: ds 2 25 | _MD16count: db 26 | 27 | SECTION "UXN Instructions", ROM0 28 | 29 | ; We take advantage of the fact these are next to eachother for some optimizations 30 | ASSERT(HIGH(wRST) == HIGH(wWST) + 1) 31 | 32 | ; UXN Instruction Implementations 33 | 34 | ; Support macros 35 | WST_HL: MACRO ; instructions which add to the stack start at ptr 36 | ld hl, wWSTPtr 37 | ld l, [hl] 38 | ENDM 39 | 40 | WST_PTR_L: MACRO 41 | ld a, l 42 | ld [wWSTPtr], a 43 | ENDM 44 | 45 | RST_HL: MACRO 46 | ld hl, wRSTPtr 47 | ld l, [hl] 48 | ENDM 49 | 50 | RST_HL_dec: MACRO ; instructions which consume the stack start at ptr-1 51 | ld hl, wRSTPtr 52 | ld l, [hl] 53 | dec l 54 | ENDM 55 | 56 | RST_PTR_L: MACRO 57 | ld a, l 58 | ld [wRSTPtr], a 59 | ENDM 60 | 61 | WBIT_2K_SETUP: MACRO 62 | ld hl, wWSTPtr 63 | inc [hl] 64 | inc [hl] 65 | ld a, [hl] 66 | .continue 67 | sub 6 68 | ld l, a 69 | ENDM 70 | 71 | WMATH_2K_SETUP: MACRO 72 | WBIT_2K_SETUP 73 | ld d, [hl] 74 | inc l 75 | ld e, [hl] 76 | inc l 77 | ld b, [hl] 78 | inc l 79 | ld c, [hl] 80 | inc l 81 | ENDM 82 | 83 | RBIT_2K_SETUP: MACRO 84 | ld hl, wRSTPtr 85 | inc [hl] 86 | inc [hl] 87 | ld a, [hl] 88 | .continue 89 | sub 6 90 | ld l, a 91 | ENDM 92 | 93 | RMATH_2K_SETUP: MACRO 94 | RBIT_2K_SETUP 95 | ld d, [hl] 96 | inc l 97 | ld e, [hl] 98 | inc l 99 | ld b, [hl] 100 | inc l 101 | ld c, [hl] 102 | inc l 103 | ENDM 104 | 105 | PC_to_HL: MACRO 106 | ldh a, [hPC] 107 | ld h, a 108 | ldh a, [hPC+1] 109 | ld l, a 110 | ENDM 111 | 112 | HL_to_PC: MACRO 113 | ld a, h 114 | ldh [hPC], a 115 | ld a, l 116 | ldh [hPC+1], a 117 | ENDM 118 | 119 | PC_to_B: MACRO 120 | PC_to_HL 121 | ld b, [hl] ; literal value 122 | ENDM 123 | 124 | ; Convert BC in UXN space to GB memory space, accounting for banks 125 | ; Note: BC is in UXN memory space ($0000-$ffff), and needs to be converted to our version of that ($a000-$bfff, banked) 126 | ; TODO: Optimize this, and account for banks 127 | BC_to_UXN_Banked: MACRO 128 | push hl 129 | ld hl, $A000 130 | add hl, bc 131 | ld b, h 132 | ld c, l 133 | pop hl 134 | ENDM 135 | 136 | 137 | _BRK:: 138 | pop af ; pop off return address so we break out of uxn_eval on ret 139 | ret 140 | 141 | ; LIT -- a 142 | _LIT:: 143 | ; Thanks jvsTSX for help optimizing the PC increment code! 144 | 145 | ld hl, hPC ; get current PC 146 | ld a, [hli] 147 | ld c, [hl] 148 | ld b, a 149 | 150 | ld d, HIGH(wWST) 151 | ld a, [wWSTPtr] 152 | ld e, a 153 | 154 | ld a, [bc] ; literal value 155 | 156 | inc bc ; increment PC 157 | ld [hl], c ; save updated PC 158 | dec l 159 | ld [hl], b 160 | 161 | ld [de], a ; push onto wst 162 | inc e ; inc stack ptr 163 | 164 | ld a, e 165 | ld [wWSTPtr], a 166 | 167 | ret 168 | 169 | ; LITr -- a 170 | _LITr:: 171 | ld hl, hPC ; get current PC 172 | ld a, [hli] 173 | ld c, [hl] 174 | ld b, a 175 | 176 | ld d, HIGH(wRST) 177 | ld a, [wRSTPtr] 178 | ld e, a 179 | 180 | ld a, [bc] ; literal value 181 | 182 | inc bc ; increment PC 183 | ld [hl], c ; save updated PC 184 | dec l 185 | ld [hl], b 186 | 187 | ld [de], a ; push onto wst 188 | inc e ; inc stack ptr 189 | 190 | ld a, e 191 | ld [wRSTPtr], a 192 | 193 | ret 194 | 195 | ; LIT2 -- a 196 | _LIT2:: 197 | ld hl, hPC ; get current PC 198 | ld a, [hli] 199 | ld c, [hl] 200 | ld b, a 201 | 202 | ld a, [bc] ; read literal short into DE 203 | ld d, a 204 | inc bc 205 | ld a, [bc] 206 | ld e, a 207 | inc bc 208 | 209 | ld [hl], c ; save updated PC 210 | dec l 211 | ld [hl], b 212 | 213 | ld hl, wWSTPtr 214 | ld l, [hl] 215 | 216 | ld [hl], d 217 | inc l 218 | ld [hl], e 219 | inc l 220 | 221 | ld a, l 222 | ld [wWSTPtr], a 223 | 224 | ret 225 | 226 | ; LIT2r -- a 227 | _LIT2r:: 228 | ld hl, hPC ; get current PC 229 | ld a, [hli] 230 | ld c, [hl] 231 | ld b, a 232 | 233 | ld a, [bc] ; read literal short into DE 234 | ld d, a 235 | inc bc 236 | ld a, [bc] 237 | ld e, a 238 | inc bc 239 | 240 | ld [hl], c ; save updated PC 241 | dec l 242 | ld [hl], b 243 | 244 | ld h, HIGH(wRST) 245 | ld a, [wRSTPtr] 246 | ld l, a 247 | 248 | ld [hl], d 249 | inc l 250 | ld [hl], e 251 | inc l 252 | 253 | ld a, l 254 | ld [wRSTPtr], a 255 | ret 256 | 257 | ; INC a -- b 258 | _INC:: 259 | ld hl, wWSTPtr 260 | .continue 261 | ld l, [hl] 262 | dec l 263 | inc [hl] 264 | ret 265 | 266 | ; INCr a -- b 267 | _INCr:: 268 | ld hl, wRSTPtr 269 | jr _INC.continue 270 | 271 | ; INCk a -- a b 272 | _INCk:: 273 | ld hl, wWSTPtr 274 | .continue 275 | ld a, [hl] 276 | inc [hl] 277 | ld l, a 278 | dec l 279 | ld a, [hli] 280 | inc a 281 | ld [hl], a 282 | ret 283 | 284 | ; INCkr a -- a b 285 | _INCkr:: 286 | ld hl, wRSTPtr 287 | jr _INCk.continue 288 | 289 | ; INC2 a -- b 290 | _INC2:: 291 | ld hl, wWSTPtr 292 | .continue 293 | ld l, [hl] 294 | dec l 295 | ld c, [hl] 296 | dec l 297 | ld b, [hl] 298 | inc bc 299 | ld [hl], b 300 | inc l 301 | ld [hl], c 302 | ret 303 | 304 | ; INC2r a -- b 305 | _INC2r:: 306 | ld hl, wRSTPtr 307 | jr _INC2.continue 308 | 309 | ; INC2k a -- a b 310 | _INC2k:: 311 | ld hl, wWSTPtr 312 | .continue 313 | ld a, [hl] 314 | inc [hl] 315 | inc [hl] 316 | ld l, a 317 | dec l 318 | ld c, [hl] 319 | dec l 320 | ld b, [hl] 321 | inc bc ; inc value 322 | inc l ; seek to new wst position 323 | inc l 324 | ld [hl], b ; store new value 325 | inc l 326 | ld [hl], c 327 | ret 328 | 329 | ; INC2kr a -- a b 330 | _INC2kr:: 331 | ld hl, wRSTPtr 332 | jr _INC2k.continue 333 | 334 | ; POP a -- 335 | _POP:: 336 | ld hl, wWSTPtr 337 | dec [hl] 338 | ret 339 | 340 | ; POPr a -- 341 | _POPr:: 342 | ld hl, wRSTPtr 343 | dec [hl] 344 | ret 345 | 346 | ; POPk a -- a 347 | _POPk:: 348 | ; TODO: Underflow error if stack if empty 349 | ret 350 | 351 | ; POPkr a -- a 352 | _POPkr:: 353 | ; TODO: Underflow error if stack if empty 354 | ret 355 | 356 | ; POP2 a b -- 357 | _POP2:: 358 | ld hl, wWSTPtr 359 | dec [hl] 360 | dec [hl] 361 | ret 362 | 363 | ; POP2r a b -- 364 | _POP2r:: 365 | ld hl, wRSTPtr 366 | dec [hl] 367 | dec [hl] 368 | ret 369 | 370 | ; POP2k a b -- a b 371 | _POP2k:: 372 | ; TODO: Underflow error if stack if empty 373 | ret 374 | 375 | ; POP2kr a b -- a b 376 | _POP2kr:: 377 | ; TODO: Underflow error if rst if empty 378 | ret 379 | 380 | ; NIP a b -- b 381 | _NIP:: 382 | ld hl, wWSTPtr 383 | .continue 384 | dec [hl] 385 | ld l, [hl] 386 | ld a, [hld] 387 | ld [hl], a 388 | ret 389 | 390 | ; NIPr a b -- b 391 | _NIPr:: 392 | ld hl, wRSTPtr 393 | jr _NIP.continue 394 | 395 | ; NIPk a b -- a b b 396 | _NIPk:: 397 | jp _DUP 398 | 399 | ; NIPkr a b -- a b b 400 | _NIPkr:: 401 | jp _DUPr 402 | 403 | ; NIP2 a b c d -- c d 404 | _NIP2:: 405 | ld hl, wWSTPtr 406 | .continue 407 | dec [hl] 408 | ld a, [hl] 409 | dec [hl] 410 | ld l, a 411 | ld b, [hl] 412 | dec l 413 | ld c, [hl] 414 | dec l 415 | ld [hl], b 416 | dec l 417 | ld [hl], c 418 | ret 419 | 420 | ; NIP2r a b c d -- c d 421 | _NIP2r:: 422 | ld hl, wRSTPtr 423 | jr _NIP2.continue 424 | 425 | ; NIP2k a b -- a b b 426 | _NIP2k:: 427 | jp _DUP2 428 | 429 | ; NIP2kr a b -- a b b 430 | _NIP2kr:: 431 | jp _DUP2r 432 | 433 | ; SWP a b -- b a 434 | _SWP:: 435 | ld hl, wWSTPtr 436 | .continue 437 | ld l, [hl] 438 | dec l 439 | ld a, [hld] 440 | ld b, [hl] 441 | ld [hli], a 442 | ld [hl], b 443 | ret 444 | 445 | ; SWPr a b -- b a 446 | _SWPr:: 447 | ld hl, wRSTPtr 448 | jr _SWP.continue 449 | 450 | ; SWPk a b -- a b b a 451 | _SWPk:: 452 | ld hl, wWSTPtr 453 | .continue 454 | ld a, [hl] 455 | inc [hl] 456 | inc [hl] 457 | ld l, a 458 | dec l 459 | 460 | ld a, [hld] 461 | ld b, [hl] 462 | inc l 463 | inc l 464 | ld [hli], a 465 | ld [hl], b 466 | ret 467 | 468 | ; SWPkr a b -- a b b a 469 | _SWPkr:: 470 | ld hl, wRSTPtr 471 | jr _SWPk.continue 472 | 473 | ; SWP2 a b c d -- c d a b 474 | _SWP2:: 475 | ld hl, wWSTPtr 476 | .continue 477 | ld l, [hl] 478 | dec l 479 | ld a, [hld] 480 | ld c, [hl] 481 | dec l 482 | ld d, [hl] 483 | dec l 484 | ld e, [hl] 485 | ld [hl], c 486 | inc l 487 | ld [hli], a 488 | ld [hl], e 489 | inc l 490 | ld [hl], d 491 | ret 492 | 493 | ; SWP2r a b c d -- c d a b 494 | _SWP2r:: 495 | ld hl, wRSTPtr 496 | jr _SWP2.continue 497 | 498 | ; SWP2k a b c d -- a b c d c d a b 499 | _SWP2k:: 500 | ld hl, wWSTPtr 501 | .continue 502 | ld a, [hl] 503 | add 4 504 | ld [hl], a 505 | sub 5 506 | ld l, a 507 | ld b, [hl] 508 | dec l 509 | ld c, [hl] 510 | dec l 511 | ld d, [hl] 512 | dec l 513 | ld e, [hl] 514 | 515 | ld l, a 516 | inc l 517 | ld [hl], c 518 | inc l 519 | ld [hl], b 520 | inc l 521 | ld [hl], e 522 | inc l 523 | ld [hl], d 524 | ret 525 | 526 | ; SWP2kr a b c d -- a b c d c d a b 527 | _SWP2kr:: 528 | ld hl, wRSTPtr 529 | jr _SWP2k.continue 530 | 531 | ; ROT a b c -- b c a 532 | _ROT:: 533 | ld hl, wWSTPtr 534 | .continue 535 | ld l, [hl] 536 | dec l 537 | ld b, [hl] 538 | dec l 539 | ld a, [hld] 540 | ld c, [hl] 541 | ld [hli], a 542 | ld [hl], b 543 | inc l 544 | ld [hl], c 545 | ret 546 | 547 | ; ROTr a b c -- b c a 548 | _ROTr:: 549 | ld hl, wRSTPtr 550 | jr _ROT.continue 551 | 552 | ; ROTk a b c -- a b c b c a 553 | _ROTk:: 554 | ld hl, wWSTPtr 555 | .continue 556 | ld a, [hl] 557 | inc [hl] 558 | inc [hl] 559 | inc [hl] 560 | ld l, a 561 | dec l 562 | 563 | ld b, [hl] 564 | dec l 565 | ld a, [hld] 566 | ld c, [hl] 567 | inc l 568 | inc l 569 | inc l 570 | ld [hli], a 571 | ld [hl], b 572 | inc l 573 | ld [hl], c 574 | ret 575 | 576 | ; ROTkr a b c -- a b c b c a 577 | _ROTkr:: 578 | ld hl, wRSTPtr 579 | jr _ROTk.continue 580 | 581 | ; ROT2 a b c d e f -- c d e f a b 582 | _ROT2:: 583 | ld hl, wWSTPtr 584 | .continue 585 | ld l, [hl] 586 | dec l 587 | ld b, [hl] 588 | dec l 589 | ld c, [hl] 590 | dec l 591 | ld d, [hl] 592 | ld [hl], b 593 | dec l 594 | ld a, [hl] ; use A so we can hli later 595 | ld [hl], c 596 | dec l 597 | ld b, [hl] 598 | ld [hl], d 599 | dec l 600 | ld c, [hl] 601 | ld [hli], a 602 | inc l 603 | inc l 604 | inc l 605 | ld [hl], c 606 | inc l 607 | ld [hl], b 608 | ret 609 | 610 | ; ROT2r a b c d e f -- c d e f a b 611 | _ROT2r:: 612 | ld hl, wRSTPtr 613 | jr _ROT2.continue 614 | 615 | ; ROT2k a b c d e f -- a b c d e f c d e f a b 616 | ; TODO: Try to speed up this register-pressure traversal 617 | _ROT2k:: 618 | ld hl, wWSTPtr 619 | .continue 620 | ld a, [hl] 621 | add 6 622 | ld [hl], a 623 | sub 12 ; jump to the start of a, b 624 | ld l, a 625 | 626 | ld b, [hl] ; get a, b 627 | inc l 628 | ld c, [hl] 629 | ld a, l ; jump to the end 630 | add 9 631 | ld l, a 632 | ld [hl], b ; store a, b 633 | inc l 634 | ld [hl], c 635 | 636 | ld a, l ; jump to the start of c, d, e, f 637 | sub 9 638 | ld l, a 639 | ld b, [hl] ; get c, d, e, f 640 | inc l 641 | ld c, [hl] 642 | inc l 643 | ld d, [hl] 644 | inc l 645 | ld e, [hl] 646 | inc l 647 | 648 | ld [hl], b ; store c, d, e, f 649 | inc l 650 | ld [hl], c 651 | inc l 652 | ld [hl], d 653 | inc l 654 | ld [hl], e 655 | inc l 656 | 657 | ret 658 | 659 | ; ROT2kr a b c d e f -- a b c d e f c d e f a b 660 | ; TODO: Try to speed up this register-pressure traversal 661 | _ROT2kr:: 662 | ld hl, wRSTPtr 663 | jr _ROT2k.continue 664 | 665 | ; DUP a -- a a 666 | _DUP:: 667 | ld hl, wWSTPtr 668 | .continue 669 | ld a, [hl] 670 | inc [hl] 671 | ld l, a 672 | dec l 673 | ld a, [hli] 674 | ld [hl], a 675 | ret 676 | 677 | ; DUPr a -- a a 678 | _DUPr:: 679 | ld hl, wRSTPtr 680 | jr _DUP.continue 681 | 682 | ; DUPk a -- a a a 683 | _DUPk:: 684 | ld hl, wWSTPtr 685 | .continue 686 | ld a, [hl] 687 | inc [hl] 688 | inc [hl] 689 | ld l, a 690 | dec l 691 | ld a, [hli] 692 | ld [hli], a 693 | ld [hl], a 694 | ret 695 | 696 | ; DUPkr a -- a a a 697 | _DUPkr:: 698 | ld hl, wRSTPtr 699 | jr _DUPk.continue 700 | 701 | ; DUP2 a b -- a b a b 702 | _DUP2:: 703 | ld hl, wWSTPtr 704 | .continue 705 | ld a, [hl] 706 | inc [hl] 707 | inc [hl] 708 | ld l, a 709 | dec l 710 | ld b, [hl] 711 | dec l 712 | ld a, [hli] 713 | inc l 714 | ld [hli], a 715 | ld [hl], b 716 | ret 717 | 718 | ; DUP2r a b -- a b a b 719 | _DUP2r:: 720 | ld hl, wRSTPtr 721 | jr _DUP2.continue 722 | 723 | ; DUP2k a b -- a b a b a b 724 | _DUP2k:: 725 | ld hl, wWSTPtr 726 | .continue 727 | ld a, [hl] 728 | inc [hl] 729 | inc [hl] 730 | inc [hl] 731 | inc [hl] 732 | ld l, a 733 | dec l 734 | ld b, [hl] 735 | dec l 736 | ld a, [hli] 737 | inc l 738 | ld [hli], a 739 | ld [hl], b 740 | inc l 741 | ld [hli], a 742 | ld [hl], b 743 | ret 744 | 745 | ; DUP2kr a b -- a b a b a b 746 | _DUP2kr:: 747 | ld hl, wRSTPtr 748 | jr _DUP2k.continue 749 | 750 | ; OVR a b -- a b a 751 | _OVR:: 752 | ld hl, wWSTPtr 753 | .continue 754 | ld a, [hl] 755 | inc [hl] 756 | ld l, a 757 | dec l 758 | dec l 759 | ld a, [hli] 760 | inc l 761 | ld [hl], a 762 | ret 763 | 764 | ; OVRr a b -- a b a 765 | _OVRr:: 766 | ld hl, wRSTPtr 767 | jr _OVR.continue 768 | 769 | ; OVRk a b -- a b a b a 770 | _OVRk:: 771 | ld hl, wWSTPtr 772 | .continue 773 | ld a, [hl] 774 | inc [hl] 775 | inc [hl] 776 | inc [hl] 777 | ld l, a 778 | dec l 779 | ld b, [hl] 780 | dec l 781 | ld a, [hli] 782 | inc l 783 | ld [hli], a 784 | ld [hl], b 785 | inc l 786 | ld [hl], a 787 | ret 788 | 789 | ; OVRkr a b -- a b a b a 790 | _OVRkr:: 791 | ld hl, wRSTPtr 792 | jr _OVRk.continue 793 | 794 | ; OVR2 a b c d -- a b c d a b 795 | _OVR2:: 796 | ld hl, wWSTPtr 797 | .continue 798 | ld a, [hl] 799 | inc [hl] 800 | inc [hl] 801 | ld l, a 802 | dec l 803 | dec l 804 | dec l 805 | ld b, [hl] 806 | dec l 807 | ld a, [hli] 808 | inc l 809 | inc l 810 | inc l 811 | ld [hli], a 812 | ld [hl], b 813 | ret 814 | 815 | ; OVR2r a b c d -- a b c d a b 816 | _OVR2r:: 817 | ld hl, wRSTPtr 818 | jr _OVR2.continue 819 | 820 | ; OVR2k a b c d -- a b c d a b c d a b 821 | _OVR2k:: 822 | ld hl, wWSTPtr 823 | .continue 824 | ld a, [hl] 825 | add 6 826 | ld [hl], a 827 | sub 10 828 | ld l, a 829 | 830 | ld a, [hli] ; read starting values 831 | ld c, [hl] 832 | inc l 833 | ld d, [hl] 834 | inc l 835 | ld e, [hl] 836 | inc l 837 | 838 | ld [hli], a ; store keep copy 839 | ld [hl], c 840 | inc l 841 | ld [hl], d 842 | inc l 843 | ld [hl], e 844 | inc l 845 | 846 | ld [hli], a ; store OVR short 847 | ld [hl], c 848 | 849 | ret 850 | 851 | ; OVR2kr a b c d -- a b c d a b c d a b 852 | _OVR2kr:: 853 | ld hl, wRSTPtr 854 | jr _OVR2k.continue 855 | 856 | ; EQU a b -- bool8 857 | _EQU:: 858 | ld hl, wWSTPtr 859 | .continue 860 | dec [hl] 861 | ld l, [hl] 862 | ld a, [hld] 863 | ld b, [hl] 864 | cp b 865 | ld a, 0 866 | jr nz, .notEqual 867 | inc a 868 | .notEqual 869 | ld [hl], a 870 | ret 871 | 872 | ; EQUr a b -- bool8 873 | _EQUr:: 874 | ld hl, wRSTPtr 875 | jr _EQU.continue 876 | 877 | ; EQUk a b -- a b bool8 878 | _EQUk:: 879 | ld hl, wWSTPtr 880 | .continue 881 | ld a, [hl] 882 | inc [hl] 883 | ld l, a 884 | dec l 885 | ld a, [hld] 886 | ld b, [hl] 887 | cp b 888 | ld a, 0 889 | jr nz, .notEqual 890 | inc a 891 | .notEqual 892 | inc l 893 | inc l 894 | ld [hl], a 895 | ret 896 | 897 | ; EQUkr a b -- a b bool8 898 | _EQUkr:: 899 | ld hl, wRSTPtr 900 | jr _EQUk.continue 901 | 902 | ; EQU2 a b c d -- bool8 903 | _EQU2:: 904 | ld hl, wWSTPtr 905 | .continue 906 | dec [hl] 907 | ld a, [hl] 908 | dec [hl] 909 | dec [hl] 910 | ld l, a 911 | 912 | ld b, [hl] 913 | dec l 914 | ld c, [hl] 915 | dec l 916 | ld d, [hl] 917 | dec l 918 | ld e, [hl] 919 | push hl 920 | ld h, 0 921 | ld a, b 922 | cp d 923 | jr nz, .notEqual 924 | ld a, c 925 | cp e 926 | jr nz, .notEqual 927 | inc h 928 | .notEqual 929 | ld a, h 930 | pop hl 931 | ld [hl], a 932 | ret 933 | 934 | ; EQU2r a b c d -- bool8 935 | _EQU2r:: 936 | ld hl, wRSTPtr 937 | jr _EQU2.continue 938 | 939 | ; EQU2k a b c d -- a b c d bool8 940 | _EQU2k:: 941 | ld hl, wWSTPtr 942 | .continue 943 | ld a, [hl] 944 | inc [hl] 945 | sub 4 946 | ld l, a 947 | 948 | ld b, [hl] 949 | inc l 950 | ld c, [hl] 951 | inc l 952 | ld d, [hl] 953 | inc l 954 | ld e, [hl] 955 | inc l 956 | push hl 957 | ld h, 0 958 | ld a, b 959 | cp d 960 | jr nz, .notEqual 961 | ld a, c 962 | cp e 963 | jr nz, .notEqual 964 | inc h 965 | .notEqual 966 | ld a, h 967 | pop hl 968 | ld [hl], a 969 | ret 970 | 971 | ; EQU2kr a b c d -- a b c d bool8 972 | _EQU2kr:: 973 | ld hl, wRSTPtr 974 | jr _EQU2k.continue 975 | 976 | ; NEQ a b -- bool8 977 | _NEQ:: 978 | ld hl, wWSTPtr 979 | .continue 980 | dec [hl] 981 | ld l, [hl] 982 | ld a, [hld] 983 | ld b, [hl] 984 | cp b 985 | ld a, 0 986 | jr z, .equal 987 | inc a 988 | .equal 989 | ld [hl], a 990 | ret 991 | 992 | ; NEQr a b -- bool8 993 | ; TODO: Optimize to RSL_PTR_L earlier and jump to _NEQ 994 | _NEQr:: 995 | ld hl, wRSTPtr 996 | jr _NEQ.continue 997 | 998 | ; NEQk a b -- a b bool8 999 | _NEQk:: 1000 | ld hl, wWSTPtr 1001 | .continue 1002 | ld a, [hl] 1003 | inc [hl] 1004 | ld l, a 1005 | dec l 1006 | ld a, [hld] 1007 | ld b, [hl] 1008 | cp b 1009 | ld a, 0 1010 | jr z, .equal 1011 | inc a 1012 | .equal 1013 | inc l 1014 | inc l 1015 | ld [hl], a 1016 | ret 1017 | 1018 | ; NEQkr a b -- a b bool8 1019 | _NEQkr:: 1020 | ld hl, wRSTPtr 1021 | jr _NEQk.continue 1022 | 1023 | ; NEQ2 a b c d -- bool8 1024 | _NEQ2:: 1025 | ld hl, wWSTPtr 1026 | .continue 1027 | dec [hl] 1028 | ld a, [hl] 1029 | dec [hl] 1030 | dec [hl] 1031 | ld l, a 1032 | 1033 | ld b, [hl] 1034 | dec l 1035 | ld c, [hl] 1036 | dec l 1037 | ld d, [hl] 1038 | dec l 1039 | ld e, [hl] 1040 | push hl 1041 | ld h, 1 1042 | ld a, b 1043 | cp d 1044 | jr nz, .notEqual 1045 | ld a, c 1046 | cp e 1047 | jr nz, .notEqual 1048 | dec h 1049 | .notEqual 1050 | ld a, h 1051 | pop hl 1052 | ld [hl], a 1053 | ret 1054 | 1055 | ; NEQ2r a b c d -- bool8 1056 | _NEQ2r:: 1057 | ld hl, wRSTPtr 1058 | jr _NEQ2.continue 1059 | 1060 | ; NEQ2k a b c d -- a b c d bool8 1061 | _NEQ2k:: 1062 | ld hl, wWSTPtr 1063 | .continue 1064 | ld a, [hl] 1065 | inc a 1066 | ld [hl], a 1067 | sub 5 1068 | ld l, a 1069 | 1070 | ld b, [hl] 1071 | inc l 1072 | ld c, [hl] 1073 | inc l 1074 | ld d, [hl] 1075 | inc l 1076 | ld e, [hl] 1077 | inc l 1078 | push hl 1079 | ld h, 0 1080 | ld a, b 1081 | cp d 1082 | jr z, .equal 1083 | ld a, c 1084 | cp e 1085 | jr z, .equal 1086 | inc h 1087 | .equal 1088 | ld a, h 1089 | pop hl 1090 | ld [hl], a 1091 | ret 1092 | 1093 | ; NEQ2kr a b c d -- a b c d bool8 1094 | _NEQ2kr:: 1095 | ld hl, wRSTPtr 1096 | jr _NEQ2k.continue 1097 | 1098 | ; GTH a b -- bool8 1099 | _GTH:: 1100 | ld hl, wWSTPtr 1101 | .continue 1102 | dec [hl] 1103 | ld l, [hl] 1104 | 1105 | ld a, [hld] 1106 | ld b, [hl] 1107 | cp b 1108 | ld a, 0 1109 | jr nc, .notGreater 1110 | inc a 1111 | .notGreater 1112 | ld [hl], a 1113 | ret 1114 | 1115 | ; GTHr a b -- bool8 1116 | _GTHr:: 1117 | ld hl, wRSTPtr 1118 | jr _GTH.continue 1119 | 1120 | ; GTHk a b -- a b bool8 1121 | _GTHk:: 1122 | ld hl, wWSTPtr 1123 | .continue 1124 | ld a, [hl] 1125 | inc [hl] 1126 | ld l, a 1127 | dec l 1128 | 1129 | ld a, [hld] 1130 | ld b, [hl] 1131 | cp b 1132 | ld a, 0 1133 | jr nc, .notGreater 1134 | inc a 1135 | .notGreater 1136 | inc l 1137 | inc l 1138 | ld [hl], a 1139 | ret 1140 | 1141 | ; GTHkr a b -- a b bool8 1142 | _GTHkr:: 1143 | ld hl, wRSTPtr 1144 | jr _GTHk.continue 1145 | 1146 | ; GTH2 a b c d -- bool8 1147 | _GTH2:: 1148 | ld hl, wWSTPtr 1149 | .continue 1150 | dec [hl] 1151 | ld a, [hl] 1152 | dec [hl] 1153 | dec [hl] 1154 | ld l, a 1155 | 1156 | ld c, [hl] 1157 | dec l 1158 | ld b, [hl] 1159 | dec l 1160 | ld e, [hl] 1161 | dec l 1162 | ld d, [hl] 1163 | push hl 1164 | ld h, 0 1165 | ld a, b 1166 | cp d 1167 | jr c, .greaterThan ; if d > b, de > bc 1168 | jr nz, .notGreaterThan 1169 | ld a, c 1170 | cp e 1171 | jr nc, .notGreaterThan 1172 | .greaterThan 1173 | inc h 1174 | .notGreaterThan 1175 | ld a, h 1176 | pop hl 1177 | ld [hl], a 1178 | ret 1179 | 1180 | ; GTH2r a b c d -- bool8 1181 | _GTH2r:: 1182 | ld hl, wRSTPtr 1183 | jr _GTH2.continue 1184 | 1185 | ; GTH2k a b c d -- a b c d bool8 1186 | _GTH2k:: 1187 | ld hl, wWSTPtr 1188 | .continue 1189 | ld a, [hl] 1190 | inc a 1191 | ld [hl], a 1192 | sub 5 1193 | ld l, a 1194 | 1195 | ld d, [hl] 1196 | inc l 1197 | ld e, [hl] 1198 | inc l 1199 | ld b, [hl] 1200 | inc l 1201 | ld c, [hl] 1202 | inc l 1203 | push hl 1204 | ld h, 0 1205 | ld a, b 1206 | cp d 1207 | jr c, .greaterThan ; if d > b, de > bc 1208 | jr nz, .notGreaterThan 1209 | ld a, c 1210 | cp e 1211 | jr nc, .notGreaterThan 1212 | .greaterThan 1213 | inc h 1214 | .notGreaterThan 1215 | ld a, h 1216 | pop hl 1217 | ld [hl], a 1218 | ret 1219 | 1220 | ; GTH2kr a b c d -- a b c d bool8 1221 | _GTH2kr:: 1222 | ld hl, wRSTPtr 1223 | jr _GTH2k.continue 1224 | 1225 | ; LTH a b -- bool8 1226 | _LTH:: 1227 | ld hl, wWSTPtr 1228 | .continue 1229 | dec [hl] 1230 | ld l, [hl] 1231 | 1232 | ld a, [hld] 1233 | ld b, [hl] 1234 | cp b 1235 | ld a, 0 1236 | jr c, .notLesser 1237 | jr z, .notLesser 1238 | inc a 1239 | .notLesser 1240 | ld [hl], a 1241 | ret 1242 | 1243 | ; LTHr a b -- bool8 1244 | _LTHr:: 1245 | ld hl, wRSTPtr 1246 | jr _LTH.continue 1247 | 1248 | ; LTHk a b -- a b bool8 1249 | _LTHk:: 1250 | ld hl, wWSTPtr 1251 | .continue 1252 | ld a, [hl] 1253 | inc [hl] 1254 | ld l, a 1255 | dec l 1256 | 1257 | ld a, [hld] 1258 | ld b, [hl] 1259 | cp b 1260 | ld a, 0 1261 | jr c, .notLesser 1262 | jr z, .notLesser 1263 | inc a 1264 | .notLesser 1265 | inc l 1266 | inc l 1267 | ld [hl], a 1268 | ret 1269 | 1270 | ; LTHkr a b -- a b bool8 1271 | _LTHkr:: 1272 | ld hl, wRSTPtr 1273 | jr _LTHk.continue 1274 | 1275 | ; LTH2 a b d c -- bool8 1276 | _LTH2:: 1277 | ld hl, wWSTPtr 1278 | .continue 1279 | dec [hl] 1280 | ld a, [hl] 1281 | dec [hl] 1282 | dec [hl] 1283 | ld l, a 1284 | 1285 | ld c, [hl] 1286 | dec l 1287 | ld b, [hl] 1288 | dec l 1289 | ld e, [hl] 1290 | dec l 1291 | ld d, [hl] 1292 | push hl 1293 | ld h, 0 1294 | ld a, d 1295 | cp b 1296 | jr c, .lessThan 1297 | jr nz, .notLessThan 1298 | ld a, e 1299 | cp c 1300 | jr nc, .notLessThan 1301 | .lessThan 1302 | inc h 1303 | .notLessThan 1304 | ld a, h 1305 | pop hl 1306 | ld [hl], a 1307 | ret 1308 | 1309 | ; LTH2r a b d c -- bool8 1310 | _LTH2r:: 1311 | ld hl, wRSTPtr 1312 | jr _LTH2.continue 1313 | 1314 | ; LTH2k a b d c -- a b d c bool8 1315 | _LTH2k:: 1316 | ld hl, wWSTPtr 1317 | .continue 1318 | ld a, [hl] 1319 | inc a 1320 | ld [hl], a 1321 | sub 5 1322 | ld l, a 1323 | 1324 | ld d, [hl] 1325 | inc l 1326 | ld e, [hl] 1327 | inc l 1328 | ld b, [hl] 1329 | inc l 1330 | ld c, [hl] 1331 | inc l 1332 | push hl 1333 | ld h, 0 1334 | ld a, d 1335 | cp b 1336 | jr c, .lessThan 1337 | jr nz, .notLessThan 1338 | ld a, e 1339 | cp c 1340 | jr nc, .notLessThan 1341 | .lessThan 1342 | inc h 1343 | .notLessThan 1344 | ld a, h 1345 | pop hl 1346 | ld [hl], a 1347 | ret 1348 | 1349 | ; LTH2kr a b d c -- a b d c bool8 1350 | _LTH2kr:: 1351 | ld hl, wRSTPtr 1352 | jr _LTH2k.continue 1353 | 1354 | ; JCI cond -- 1355 | _JCI:: 1356 | ld hl, wWSTPtr 1357 | dec [hl] 1358 | ld l, [hl] 1359 | ld c, [hl] 1360 | ld a, c 1361 | or a 1362 | ret z ; condition not met 1363 | ; Fall through to JMI 1364 | 1365 | ; JMI -- 1366 | _JMI:: 1367 | ld hl, hPC ; get current PC 1368 | ld a, [hli] 1369 | ld c, [hl] 1370 | ld b, a 1371 | 1372 | ld a, [bc] ; get 16bit relative jump 1373 | ld h, a 1374 | inc bc 1375 | ld a, [bc] 1376 | ld l, a 1377 | inc bc 1378 | 1379 | add hl, bc ; offset PC 1380 | 1381 | ld a, h ; store updated PC 1382 | ldh [hPC], a 1383 | ld a, l 1384 | ldh [hPC+1], a 1385 | ret 1386 | 1387 | ; JSI -- 1388 | _JSI:: 1389 | ld hl, hPC ; get current PC 1390 | ld a, [hli] 1391 | ld c, [hl] 1392 | ld b, a 1393 | 1394 | ld a, [bc] ; get 16bit relative jump 1395 | ld h, a 1396 | inc bc 1397 | ld a, [bc] 1398 | ld l, a 1399 | inc bc 1400 | 1401 | push hl ; store return address on return stack 1402 | push bc 1403 | 1404 | ld hl, $ffff & -eUXNMemory ; shift back to Uxn value, to handle stack manipulation 1405 | add hl, bc 1406 | ld b, h 1407 | ld c, l 1408 | 1409 | ld hl, wRSTPtr 1410 | ld a, [hl] 1411 | inc [hl] 1412 | inc [hl] 1413 | ld l, a 1414 | ld [hl], b 1415 | inc l 1416 | ld [hl], c 1417 | 1418 | pop bc 1419 | pop hl 1420 | 1421 | add hl, bc ; offset PC 1422 | 1423 | ld a, h ; store updated PC 1424 | ldh [hPC], a 1425 | ld a, l 1426 | ldh [hPC+1], a 1427 | ret 1428 | 1429 | ; The JCN set of instructions is somewhat oddly positioned since it's expected 1430 | ; to be a high-use instruction and being within JR range of _JMP is nice 1431 | 1432 | ; JCN cond8 addr -- 1433 | _JCN:: 1434 | ld hl, wWSTPtr 1435 | dec [hl] 1436 | dec [hl] 1437 | ld l, [hl] 1438 | .continue 1439 | ld c, [hl] 1440 | ld a, c 1441 | or a 1442 | ret z ; condition not met 1443 | inc l 1444 | ld c, [hl] 1445 | jr _JMP.jump 1446 | 1447 | ; JCNr cond8 addr -- 1448 | _JCNr:: 1449 | ld hl, wRSTPtr 1450 | dec [hl] 1451 | dec [hl] 1452 | ld l, [hl] 1453 | jr _JCN.continue 1454 | 1455 | ; JCNk cond8 addr -- cond8 addr 1456 | _JCNk:: 1457 | ld hl, wWSTPtr 1458 | ld l, [hl] 1459 | dec l 1460 | dec l 1461 | jr _JCN.continue 1462 | 1463 | ; JCNkr cond8 addr -- cond8 addr 1464 | _JCNkr:: 1465 | ld hl, wRSTPtr 1466 | ld a, [hl] 1467 | dec l 1468 | dec l 1469 | jr _JCN.continue 1470 | 1471 | ; JSR addr -- 1472 | _JSR:: 1473 | ldh a, [hPC] 1474 | ld b, a 1475 | ldh a, [hPC+1] 1476 | ld c, a 1477 | 1478 | ; Offset back to UXN address, in case someone does some direct manipulation 1479 | ; TODO: Account for banking! 1480 | ld hl, $ffff & -eUXNMemory 1481 | add hl, bc 1482 | ld b, h 1483 | ld c, l 1484 | 1485 | RST_HL 1486 | ld [hl], b 1487 | inc l 1488 | ld [hl], c 1489 | inc l 1490 | RST_PTR_L 1491 | ; UXN return address now on RST, continue with normal JMP 1492 | ; fall through to _JMP 1493 | ;jr _JMP 1494 | 1495 | ; JMP addr -- 1496 | _JMP:: 1497 | ld hl, wWSTPtr 1498 | .continue0 1499 | ld a, [hl] 1500 | dec a 1501 | ld [hl], a 1502 | .continue1 1503 | ld l, a 1504 | ld c, [hl] 1505 | .jump 1506 | ; sign extension 1507 | ld a, c 1508 | add a ; push sign into carry 1509 | sbc a ; turn into 0 or -1 1510 | ld b, a ; high byte 1511 | PC_to_HL 1512 | add hl, bc 1513 | ld a, h 1514 | ldh [hPC], a 1515 | ld a, l 1516 | ldh [hPC+1], a 1517 | ret 1518 | 1519 | ; JSRr addr -- 1520 | _JSRr:: 1521 | ldh a, [hPC] 1522 | ld b, a 1523 | ldh a, [hPC+1] 1524 | ld c, a 1525 | 1526 | ; Offset back to UXN address, in case someone does some direct manipulation 1527 | ; TODO: Account for banking! 1528 | ld hl, $ffff & -eUXNMemory 1529 | add hl, bc 1530 | ld b, h 1531 | ld c, l 1532 | 1533 | WST_HL 1534 | ld [hl], b 1535 | inc l 1536 | ld [hl], c 1537 | inc l 1538 | WST_PTR_L 1539 | ; UXN return address now on WST, continue with normal JMPr 1540 | ; Fall through to _JMPr 1541 | ;jr _JMPr 1542 | 1543 | ; JMPr addr -- 1544 | _JMPr:: 1545 | ld hl, wRSTPtr 1546 | ld a, [hl] 1547 | dec a 1548 | ld [hl], a 1549 | jr _JMP.continue0 1550 | 1551 | ; JSRk addr -- addr 1552 | _JSRk:: 1553 | ldh a, [hPC] 1554 | ld b, a 1555 | ldh a, [hPC+1] 1556 | ld c, a 1557 | 1558 | ; Offset back to UXN address, in case someone does some direct manipulation 1559 | ; TODO: Account for banking! 1560 | ld hl, $ffff & -eUXNMemory 1561 | add hl, bc 1562 | ld b, h 1563 | ld c, l 1564 | 1565 | RST_HL 1566 | ld [hl], b 1567 | inc l 1568 | ld [hl], c 1569 | inc l 1570 | RST_PTR_L 1571 | ; UXN return address now on RST, continue with normal JMPk 1572 | ; Fall through to _JMPk 1573 | ;jr _JMPk 1574 | 1575 | ; JMPk addr -- addr 1576 | _JMPk:: 1577 | ld hl, wWSTPtr 1578 | ld a, [hl] 1579 | dec a 1580 | jr _JMP.continue1 1581 | 1582 | ; JSRkr addr -- addr 1583 | _JSRkr:: 1584 | ldh a, [hPC] 1585 | ld b, a 1586 | ldh a, [hPC+1] 1587 | ld c, a 1588 | 1589 | ; Offset back to UXN address, in case someone does some direct manipulation 1590 | ; TODO: Account for banking! 1591 | ld hl, $ffff & -eUXNMemory 1592 | add hl, bc 1593 | ld b, h 1594 | ld c, l 1595 | 1596 | WST_HL 1597 | ld [hl], b 1598 | inc l 1599 | ld [hl], c 1600 | inc l 1601 | WST_PTR_L 1602 | ; UXN return address now on RST, continue with normal JMPkr 1603 | ; Fall through to _JMPkr 1604 | ;jr _JMPkr 1605 | 1606 | ; JMPkr addr -- addr 1607 | _JMPkr:: 1608 | ld hl, wRSTPtr 1609 | ld a, [hl] 1610 | dec a 1611 | jr _JMP.continue1 1612 | 1613 | ; JCN2 cond addr -- 1614 | _JCN2:: 1615 | ld hl, wWSTPtr 1616 | .continue0 1617 | dec [hl] 1618 | ld a, [hl] 1619 | dec [hl] 1620 | dec [hl] 1621 | ld l, a 1622 | dec l 1623 | dec l 1624 | ld c, [hl] 1625 | .continue1 1626 | ld a, c 1627 | or a 1628 | ret z ; condition not met 1629 | inc l 1630 | ld b, [hl] 1631 | inc l 1632 | ld c, [hl] 1633 | jr _JMP2.jump 1634 | 1635 | ; JCN2r cond addr -- 1636 | _JCN2r:: 1637 | ld hl, wRSTPtr 1638 | jr _JCN2.continue0 1639 | 1640 | ; JCN2k cond addr -- cond addr 1641 | _JCN2k:: 1642 | ld hl, wWSTPtr 1643 | ld l, [hl] 1644 | dec l 1645 | dec l 1646 | dec l 1647 | ld c, [hl] 1648 | jr _JCN2.continue1 1649 | 1650 | ; JCN2kr cond addr -- cond addr 1651 | _JCN2kr:: 1652 | RST_HL_dec 1653 | dec l 1654 | dec l 1655 | ld c, [hl] 1656 | jr _JCN2.continue1 1657 | 1658 | ; JSR2 addr -- 1659 | _JSR2:: 1660 | RST_HL 1661 | ldh a, [hPC] 1662 | ld b, a 1663 | ldh a, [hPC+1] 1664 | ld c, a 1665 | 1666 | push hl 1667 | ; Offset back to UXN address, in case someone does some direct manipulation 1668 | ; TODO: Account for banking! 1669 | ld hl, $ffff & -eUXNMemory 1670 | add hl, bc 1671 | ld b, h 1672 | ld c, l 1673 | pop hl 1674 | 1675 | ld [hl], b 1676 | inc l 1677 | ld [hl], c 1678 | inc l 1679 | RST_PTR_L 1680 | ; Fall through to _JMP2 1681 | ;jr _JMP2 1682 | 1683 | ; JMP2 addr -- 1684 | _JMP2:: 1685 | ld hl, wWSTPtr 1686 | .continue0 1687 | dec [hl] 1688 | ld a, [hl] 1689 | dec [hl] 1690 | ld l, a 1691 | ld c, [hl] 1692 | dec l 1693 | .continue1 1694 | ld b, [hl] 1695 | .jump 1696 | ld hl, eUXNMemory 1697 | add hl, bc 1698 | ld a, h 1699 | ldh [hPC], a 1700 | ld a, l 1701 | ldh [hPC+1], a 1702 | ret 1703 | 1704 | ; JSR2r addr -- 1705 | _JSR2r:: 1706 | WST_HL 1707 | ldh a, [hPC] 1708 | ld b, a 1709 | ldh a, [hPC+1] 1710 | ld c, a 1711 | 1712 | push hl 1713 | ; Offset back to UXN address, in case someone does some direct manipulation 1714 | ; TODO: Account for banking! 1715 | ld hl, $ffff & -eUXNMemory 1716 | add hl, bc 1717 | ld b, h 1718 | ld c, l 1719 | pop hl 1720 | 1721 | ld [hl], b 1722 | inc l 1723 | ld [hl], c 1724 | inc l 1725 | WST_PTR_L 1726 | ; Fall through to _JMP2r 1727 | ;jr _JMP2r 1728 | 1729 | ; JMP2r addr -- 1730 | _JMP2r:: 1731 | ld hl, wRSTPtr 1732 | jr _JMP2.continue0 1733 | 1734 | ; JSR2k addr -- addr 1735 | _JSR2k:: 1736 | ; TODO: Find a way to reuse _JSR2, which is identical except for where it jumps at the end 1737 | RST_HL 1738 | ldh a, [hPC] 1739 | ld b, a 1740 | ldh a, [hPC+1] 1741 | ld c, a 1742 | 1743 | push hl 1744 | ; Offset back to UXN address, in case someone does some direct manipulation 1745 | ; TODO: Account for banking! 1746 | ld hl, $ffff & -eUXNMemory 1747 | add hl, bc 1748 | ld b, h 1749 | ld c, l 1750 | pop hl 1751 | 1752 | ld [hl], b 1753 | inc l 1754 | ld [hl], c 1755 | inc l 1756 | RST_PTR_L 1757 | ; Fall through to JMP2k 1758 | ;jr _JMP2k 1759 | 1760 | ; JMP2k addr -- addr 1761 | _JMP2k:: 1762 | ld hl, wWSTPtr 1763 | ld l, [hl] 1764 | dec l 1765 | ld c, [hl] 1766 | dec l 1767 | jr _JMP2.continue1 1768 | 1769 | ; JSR2kr addr -- addr 1770 | _JSR2kr:: 1771 | ; TODO: Find a way to reuse _JSR2?, which is identical except for where it jumps at the end 1772 | WST_HL 1773 | ldh a, [hPC] 1774 | ld b, a 1775 | ldh a, [hPC+1] 1776 | ld c, a 1777 | 1778 | push hl 1779 | ; Offset back to UXN address, in case someone does some direct manipulation 1780 | ; TODO: Account for banking! 1781 | ld hl, $ffff & -eUXNMemory 1782 | add hl, bc 1783 | ld b, h 1784 | ld c, l 1785 | pop hl 1786 | 1787 | ld [hl], b 1788 | inc l 1789 | ld [hl], c 1790 | inc l 1791 | WST_PTR_L 1792 | ; Fall through to _JMP2kr 1793 | ;jr _JMP2kr 1794 | 1795 | ; JMP2kr addr -- addr 1796 | _JMP2kr:: 1797 | RST_HL_dec 1798 | ld c, [hl] 1799 | dec l 1800 | jr _JMP2.continue1 1801 | 1802 | ; STH a -- 1803 | _STH:: 1804 | ld hl, wWSTPtr 1805 | dec [hl] 1806 | ld l, [hl] 1807 | ld b, [hl] 1808 | ld hl, wRSTPtr 1809 | ld a, [hl] 1810 | inc [hl] 1811 | ld l, a 1812 | ld [hl], b 1813 | ret 1814 | 1815 | ; STHr a -- 1816 | _STHr:: 1817 | ld hl, wRSTPtr 1818 | dec [hl] 1819 | ld l, [hl] 1820 | ld b, [hl] 1821 | ld hl, wWSTPtr 1822 | ld a, [hl] 1823 | inc [hl] 1824 | ld l, a 1825 | ld [hl], b 1826 | ret 1827 | 1828 | ; STHk a -- a 1829 | _STHk:: 1830 | ld hl, wWSTPtr 1831 | ld l, [hl] 1832 | dec l 1833 | ld b, [hl] 1834 | ld hl, wRSTPtr 1835 | ld a, [hl] 1836 | inc [hl] 1837 | ld l, a 1838 | ld [hl], b 1839 | ret 1840 | 1841 | ; STHkr a -- a 1842 | _STHkr:: 1843 | RST_HL_dec 1844 | ld b, [hl] 1845 | ld hl, wWSTPtr 1846 | ld a, [hl] 1847 | inc [hl] 1848 | ld l, a 1849 | ld [hl], b 1850 | ret 1851 | 1852 | ; STH2 a b -- 1853 | _STH2:: 1854 | ld hl, wWSTPtr 1855 | dec [hl] 1856 | ld a, [hl] 1857 | dec [hl] 1858 | ld l, a 1859 | .continue 1860 | ld b, [hl] 1861 | dec l 1862 | ld c, [hl] 1863 | ld hl, wRSTPtr 1864 | ld a, [hl] 1865 | inc [hl] 1866 | inc [hl] 1867 | ld l, a 1868 | ld [hl], c 1869 | inc l 1870 | ld [hl], b 1871 | ret 1872 | 1873 | ; STH2r a b -- 1874 | _STH2r:: 1875 | ld hl, wRSTPtr 1876 | dec [hl] 1877 | ld a, [hl] 1878 | dec [hl] 1879 | ld l, a 1880 | .continue 1881 | ld b, [hl] 1882 | dec l 1883 | ld c, [hl] 1884 | ld hl, wWSTPtr 1885 | ld a, [hl] 1886 | inc [hl] 1887 | inc [hl] 1888 | ld l, a 1889 | ld [hl], c 1890 | inc l 1891 | ld [hl], b 1892 | ret 1893 | 1894 | ; STH2k a b -- a b 1895 | _STH2k:: 1896 | ld hl, wWSTPtr 1897 | ld l, [hl] 1898 | dec l 1899 | jr _STH2.continue 1900 | 1901 | ; STH2kr a b -- a b 1902 | _STH2kr:: 1903 | ld hl, wRSTPtr 1904 | ld l, [hl] 1905 | dec l 1906 | jr _STH2r.continue 1907 | 1908 | ; LDZ addr8 -- value 1909 | _LDZ:: 1910 | ld hl, wWSTPtr 1911 | .continue 1912 | ld l, [hl] 1913 | dec l 1914 | ld b, HIGH(eZeroPage) 1915 | ld c, [hl] 1916 | ld a, [bc] 1917 | ld [hl], a 1918 | ret 1919 | 1920 | ; LDZr addr8 -- value 1921 | _LDZr:: 1922 | ld hl, wRSTPtr 1923 | jr _LDZ.continue 1924 | 1925 | ; LDZk addr8 -- addr8 value 1926 | _LDZk:: 1927 | ld hl, wWSTPtr 1928 | .continue 1929 | ld a, [hl] 1930 | inc [hl] 1931 | ld l, a 1932 | dec l 1933 | ld b, HIGH(eZeroPage) 1934 | ld c, [hl] 1935 | ld a, [bc] 1936 | inc l 1937 | ld [hl], a 1938 | ret 1939 | 1940 | ; LDZkr addr8 -- addr8 value 1941 | _LDZkr:: 1942 | ld hl, wRSTPtr 1943 | jr _LDZk.continue 1944 | 1945 | ; LDZ addr8 -- value 1946 | _LDZ2:: 1947 | ld hl, wWSTPtr 1948 | .continue 1949 | ld a, [hl] 1950 | inc [hl] 1951 | ld l, a 1952 | dec l 1953 | ld b, HIGH(eZeroPage) 1954 | ld c, [hl] 1955 | ld a, [bc] 1956 | ld [hli], a 1957 | inc c 1958 | ld a, [bc] 1959 | ld [hl], a 1960 | ret 1961 | 1962 | ; LDZ2r addr8 -- value 1963 | ; TODO: Consider all RST tweaking at the start so we can jump to _LDZ2 to save bytes 1964 | _LDZ2r:: 1965 | ld hl, wRSTPtr 1966 | jr _LDZ2.continue 1967 | 1968 | ; LDZk addr8 -- addr8 value 1969 | _LDZ2k:: 1970 | ld hl, wWSTPtr 1971 | .continue 1972 | ld a, [hl] 1973 | inc [hl] 1974 | inc [hl] 1975 | ld l, a 1976 | dec l 1977 | ld b, HIGH(eZeroPage) 1978 | ld c, [hl] 1979 | ld a, [bc] 1980 | inc l 1981 | ld [hli], a 1982 | inc c 1983 | ld a, [bc] 1984 | ld [hl], a 1985 | ret 1986 | 1987 | ; LDZkr addr8 -- addr8 value 1988 | _LDZ2kr:: 1989 | ld hl, wRSTPtr 1990 | jr _LDZ2k.continue 1991 | 1992 | ; STZ value addr8 -- 1993 | _STZ:: 1994 | ld hl, wWSTPtr 1995 | .continue 1996 | dec [hl] 1997 | ld a, [hl] 1998 | dec [hl] 1999 | ld l, a 2000 | ld b, HIGH(eZeroPage) 2001 | ld c, [hl] 2002 | dec l 2003 | ld a, [hl] 2004 | ld [bc], a 2005 | ret 2006 | 2007 | ; STZr value addr8 -- 2008 | _STZr:: 2009 | ld hl, wRSTPtr 2010 | jr _STZ.continue 2011 | 2012 | ; STZk value addr8 -- value addr8 2013 | _STZk:: 2014 | ld hl, wWSTPtr 2015 | .continue 2016 | ld l, [hl] 2017 | dec l 2018 | ld b, HIGH(eZeroPage) 2019 | ld c, [hl] 2020 | dec l 2021 | ld a, [hl] 2022 | ld [bc], a 2023 | ret 2024 | 2025 | ; STZkr value addr8 -- value addr8 2026 | _STZkr:: 2027 | ld hl, wRSTPtr 2028 | jr _STZk.continue 2029 | 2030 | ; STZ value addr8 -- 2031 | _STZ2:: 2032 | ld hl, wWSTPtr 2033 | .continue0 2034 | dec [hl] 2035 | ld a, [hl] 2036 | dec [hl] 2037 | dec [hl] 2038 | ld l, a 2039 | .continue1 2040 | ld b, HIGH(eZeroPage) 2041 | ld c, [hl] 2042 | inc c 2043 | dec l 2044 | ld a, [hld] 2045 | ld [bc], a 2046 | dec c 2047 | ld a, [hl] 2048 | ld [bc], a 2049 | ret 2050 | 2051 | ; STZr value addr8 -- 2052 | _STZ2r:: 2053 | ld hl, wRSTPtr 2054 | jr _STZ2.continue0 2055 | 2056 | ; STZk value addr8 -- value addr8 2057 | _STZ2k:: 2058 | ld hl, wWSTPtr 2059 | ld l, [hl] 2060 | dec l 2061 | jr _STZ2.continue1 2062 | 2063 | ; STZkr value addr8 -- value addr8 2064 | _STZ2kr:: 2065 | ld hl, wRSTPtr 2066 | ld l, [hl] 2067 | dec l 2068 | jr _STZ2.continue1 2069 | 2070 | ; LDR addr8 -- value 2071 | _LDR:: 2072 | ld hl, wWSTPtr 2073 | .continue 2074 | ld l, [hl] 2075 | dec l 2076 | ld c, [hl] 2077 | ; sign extension 2078 | ld a, c 2079 | add a ; push sign into carry 2080 | sbc a ; turn into 0 or -1 2081 | ld b, a ; high byte 2082 | push hl 2083 | PC_to_HL 2084 | add hl, bc 2085 | ld a, [hl] 2086 | pop hl 2087 | ld [hl], a 2088 | ret 2089 | 2090 | ; LDRr addr8 -- value 2091 | _LDRr:: 2092 | ld hl, wRSTPtr 2093 | jr _LDR.continue 2094 | 2095 | ; LDRk addr8 -- addr8 value 2096 | _LDRk:: 2097 | ld hl, wWSTPtr 2098 | .continue 2099 | ld a, [hl] 2100 | inc [hl] 2101 | ld l, a 2102 | dec l 2103 | ld c, [hl] 2104 | ; sign extension 2105 | ld a, c 2106 | add a ; push sign into carry 2107 | sbc a ; turn into 0 or -1 2108 | ld b, a ; high byte 2109 | push hl 2110 | PC_to_HL 2111 | add hl, bc 2112 | ld a, [hl] 2113 | pop hl 2114 | inc l 2115 | ld [hl], a 2116 | ret 2117 | 2118 | ; LDRkr addr8 -- addr8 value 2119 | _LDRkr:: 2120 | ld hl, wRSTPtr 2121 | jr _LDRk.continue 2122 | 2123 | ; LDR2 addr8 -- value 2124 | _LDR2:: 2125 | ld hl, wWSTPtr 2126 | .continue 2127 | ld a, [hl] 2128 | inc [hl] 2129 | ld l, a 2130 | dec l 2131 | ld c, [hl] 2132 | ; sign extension 2133 | ld a, c 2134 | add a ; push sign into carry 2135 | sbc a ; turn into 0 or -1 2136 | ld b, a ; high byte 2137 | push hl 2138 | PC_to_HL 2139 | add hl, bc 2140 | ld a, [hli] ; TODO: Deal with SRAM banks 2141 | ld b, [hl] 2142 | pop hl 2143 | ld [hli], a 2144 | ld [hl], b 2145 | ret 2146 | 2147 | ; LDR2r addr8 -- value 2148 | _LDR2r:: 2149 | ld hl, wRSTPtr 2150 | jr _LDR2.continue 2151 | 2152 | ; LDR2k addr8 -- addr8 value 2153 | _LDR2k:: 2154 | ld hl, wWSTPtr 2155 | .continue 2156 | ld a, [hl] 2157 | inc [hl] 2158 | inc [hl] 2159 | ld l, a 2160 | dec l 2161 | ld c, [hl] 2162 | ; sign extension 2163 | ld a, c 2164 | add a ; push sign into carry 2165 | sbc a ; turn into 0 or -1 2166 | ld b, a ; high byte 2167 | push hl 2168 | PC_to_HL 2169 | add hl, bc 2170 | ld a, [hli] ; TODO: Deal with SRAM banks 2171 | ld b, [hl] 2172 | pop hl 2173 | inc l 2174 | ld [hli], a 2175 | ld [hl], b 2176 | ret 2177 | 2178 | ; LDR2kr addr8 -- addr8 value 2179 | _LDR2kr:: 2180 | ld hl, wRSTPtr 2181 | jr _LDR2k.continue 2182 | 2183 | ; STR value addr8 -- 2184 | _STR:: 2185 | ld hl, wWSTPtr 2186 | .continue 2187 | dec [hl] 2188 | ld a, [hl] 2189 | dec [hl] 2190 | ld l, a 2191 | ld c, [hl] 2192 | dec l 2193 | ld d, [hl] 2194 | ; sign extension 2195 | ld a, c 2196 | add a ; push sign into carry 2197 | sbc a ; turn into 0 or -1 2198 | ld b, a ; high byte 2199 | PC_to_HL 2200 | add hl, bc 2201 | ld [hl], d 2202 | ret 2203 | 2204 | ; STRr value addr8 -- 2205 | _STRr:: 2206 | ld hl, wRSTPtr 2207 | jr _STR.continue 2208 | 2209 | ; STRk value addr8 -- value addr8 2210 | _STRk:: 2211 | ld hl, wWSTPtr 2212 | .continue 2213 | ld l, [hl] 2214 | dec l 2215 | ld c, [hl] 2216 | dec l 2217 | ld d, [hl] 2218 | ; sign extension 2219 | ld a, c 2220 | add a ; push sign into carry 2221 | sbc a ; turn into 0 or -1 2222 | ld b, a ; high byte 2223 | PC_to_HL 2224 | add hl, bc 2225 | ld [hl], d 2226 | ret 2227 | 2228 | ; STRkr value addr8 -- value addr8 2229 | _STRkr:: 2230 | ld hl, wRSTPtr 2231 | jr _STRk.continue 2232 | 2233 | ; STR2 value addr8 -- 2234 | _STR2:: 2235 | ld hl, wWSTPtr 2236 | .continue0 2237 | dec [hl] 2238 | ld a, [hl] 2239 | dec [hl] 2240 | dec [hl] 2241 | ld l, a 2242 | ld c, [hl] 2243 | dec l 2244 | ld e, [hl] 2245 | dec l 2246 | ld d, [hl] 2247 | .continue1 2248 | ; sign extension 2249 | ld a, c 2250 | add a ; push sign into carry 2251 | sbc a ; turn into 0 or -1 2252 | ld b, a ; high byte 2253 | PC_to_HL 2254 | add hl, bc 2255 | ld [hl], d ; TODO: Deal with SRAM banks! 2256 | inc hl 2257 | ld [hl], e 2258 | ret 2259 | 2260 | ; STR2r value addr8 -- 2261 | _STR2r:: 2262 | ld hl, wRSTPtr 2263 | jr _STR2.continue0 2264 | 2265 | ; STR2k value addr8 -- value addr8 2266 | _STR2k:: 2267 | ld hl, wWSTPtr 2268 | ld l, [hl] 2269 | dec l 2270 | ld c, [hl] 2271 | dec l 2272 | ld e, [hl] 2273 | dec l 2274 | ld d, [hl] 2275 | jr _STR2.continue1 2276 | 2277 | ; STR2kr value addr8 -- value addr8 2278 | _STR2kr:: 2279 | ld hl, wRSTPtr 2280 | ld l, [hl] 2281 | dec l 2282 | ld c, [hl] 2283 | dec l 2284 | ld e, [hl] 2285 | dec l 2286 | ld d, [hl] 2287 | jr _STR2.continue1 2288 | 2289 | ; LDA addr16 -- value 2290 | _LDA:: 2291 | ld hl, wWSTPtr 2292 | .continue 2293 | dec [hl] 2294 | ld l, [hl] 2295 | ld c, [hl] 2296 | dec l 2297 | ld b, [hl] 2298 | BC_to_UXN_Banked 2299 | ld a, [bc] 2300 | ld [hl], a 2301 | ret 2302 | 2303 | ; LDAr addr16 -- value 2304 | _LDAr:: 2305 | ld hl, wRSTPtr 2306 | jr _LDA.continue 2307 | 2308 | ; LDAk addr16 -- addr16 value 2309 | _LDAk:: 2310 | ld hl, wWSTPtr 2311 | .continue 2312 | ld a, [hl] 2313 | inc [hl] 2314 | ld l, a 2315 | dec l 2316 | dec l 2317 | ld b, [hl] 2318 | inc l 2319 | ld c, [hl] 2320 | inc l 2321 | BC_to_UXN_Banked 2322 | ld a, [bc] 2323 | ld [hl], a 2324 | ret 2325 | 2326 | ; LDAkr addr16 -- addr16 value 2327 | _LDAkr:: 2328 | ld hl, wRSTPtr 2329 | jr _LDAk.continue 2330 | 2331 | ; LDA2 addr16 -- value 2332 | _LDA2:: 2333 | ld hl, wWSTPtr 2334 | .continue 2335 | ld l, [hl] 2336 | dec l 2337 | ld c, [hl] 2338 | dec l 2339 | ld b, [hl] 2340 | BC_to_UXN_Banked 2341 | ld a, [bc] 2342 | ld [hli], a 2343 | inc bc ; TODO: Handle bank wrapping 2344 | ld a, [bc] 2345 | ld [hl], a 2346 | ret 2347 | 2348 | ; LDA2r addr16 -- value 2349 | _LDA2r:: 2350 | ld hl, wRSTPtr 2351 | jr _LDA2.continue 2352 | 2353 | ; LDA2k addr16 -- addr16 value 2354 | _LDA2k:: 2355 | ld hl, wWSTPtr 2356 | .continue 2357 | ld a, [hl] 2358 | inc [hl] 2359 | inc [hl] 2360 | ld l, a 2361 | dec l 2362 | dec l 2363 | ld b, [hl] 2364 | inc l 2365 | ld c, [hl] 2366 | inc l 2367 | BC_to_UXN_Banked 2368 | ld a, [bc] 2369 | ld [hli], a 2370 | inc bc ; TODO: Handle bank wrapping 2371 | ld a, [bc] 2372 | ld [hl], a 2373 | ret 2374 | 2375 | ; LDA2kr addr16 -- addr16 value 2376 | _LDA2kr:: 2377 | ld hl, wRSTPtr 2378 | jr _LDA2k.continue 2379 | 2380 | ; STA value addr16 -- 2381 | _STA:: 2382 | ld hl, wWSTPtr 2383 | .continue 2384 | dec [hl] 2385 | ld a, [hl] 2386 | dec [hl] 2387 | dec [hl] 2388 | ld l, a 2389 | ld c, [hl] 2390 | dec l 2391 | ld b, [hl] 2392 | dec l 2393 | ld d, [hl] 2394 | BC_to_UXN_Banked 2395 | ld a, d 2396 | ld [bc], a 2397 | ret 2398 | 2399 | ; STAr value addr16 -- 2400 | _STAr:: 2401 | ld hl, wRSTPtr 2402 | jr _STA.continue 2403 | 2404 | ; STAk value addr16 -- value addr16 2405 | _STAk:: 2406 | ld hl, wWSTPtr 2407 | .continue 2408 | ld l, [hl] 2409 | dec l 2410 | ld c, [hl] 2411 | dec l 2412 | ld b, [hl] 2413 | dec l 2414 | ld d, [hl] 2415 | BC_to_UXN_Banked 2416 | ld a, d 2417 | ld [bc], a 2418 | ret 2419 | 2420 | ; STAkr value addr16 -- value addr16 2421 | _STAkr:: 2422 | ld hl, wRSTPtr 2423 | jr _STAk.continue 2424 | 2425 | ; STA2 value addr16 -- 2426 | _STA2:: 2427 | ld hl, wWSTPtr 2428 | .continue 2429 | dec [hl] 2430 | ld a, [hl] 2431 | dec [hl] 2432 | dec [hl] 2433 | dec [hl] 2434 | ld l, a 2435 | ld c, [hl] 2436 | dec l 2437 | ld b, [hl] 2438 | dec l 2439 | ld e, [hl] 2440 | dec l 2441 | ld d, [hl] 2442 | BC_to_UXN_Banked 2443 | ld a, d 2444 | ld [bc], a 2445 | inc bc ; TODO: Handle bank wrapping 2446 | ld a, e 2447 | ld [bc], a 2448 | ret 2449 | 2450 | ; STA2r value addr16 -- 2451 | _STA2r:: 2452 | ld hl, wRSTPtr 2453 | jr _STA2.continue 2454 | 2455 | ; STA2k value addr16 -- value addr16 2456 | _STA2k:: 2457 | ld hl, wWSTPtr 2458 | .continue 2459 | ld l, [hl] 2460 | dec l 2461 | ld c, [hl] 2462 | dec l 2463 | ld b, [hl] 2464 | dec l 2465 | ld e, [hl] 2466 | dec l 2467 | ld d, [hl] 2468 | BC_to_UXN_Banked 2469 | ld a, d 2470 | ld [bc], a 2471 | inc bc ; TODO: Handle bank wrapping 2472 | ld a, e 2473 | ld [bc], a 2474 | ret 2475 | 2476 | ; STA2kr value addr16 -- value addr16 2477 | _STA2kr:: 2478 | ld hl, wRSTPtr 2479 | jr _STA2k.continue 2480 | 2481 | ; DEI device8 -- value 2482 | _DEI:: 2483 | ld hl, wWSTPtr 2484 | ld a, [hl] 2485 | .early_continue 2486 | ld e, a 2487 | dec e 2488 | .continue 2489 | ld l, a 2490 | dec l 2491 | ld d, [hl] 2492 | 2493 | ; DEVPEEK8 2494 | push hl 2495 | ld hl, wDevices 2496 | ld a, d 2497 | add l 2498 | ld l, a 2499 | 2500 | ld a, [hl] 2501 | pop hl 2502 | ld l, e ; stack store location 2503 | ld [hl], a 2504 | 2505 | ; get handler address 2506 | ld a, d 2507 | and $F0 2508 | rrca 2509 | ;add 0 ; DEI handler offset 2510 | ld hl, DeviceHandlers 2511 | add l 2512 | ld l, a ; LUT uses ALIGN[7], so no need to worry about carry 2513 | ld a, [hli] 2514 | ld h, [hl] 2515 | ld l, a 2516 | rst CallHL 2517 | 2518 | ret 2519 | 2520 | ; DEIr device8 -- value 2521 | _DEIr:: 2522 | ld hl, wRSTPtr 2523 | ld a, [hl] 2524 | jr _DEI.early_continue 2525 | 2526 | ; DEIk device8 -- device8 value 2527 | _DEIk:: 2528 | ld hl, wWSTPtr 2529 | ld a, [hl] 2530 | ld e, a 2531 | inc a 2532 | ld [hl], a 2533 | sub 2 2534 | jr _DEI.continue 2535 | 2536 | ; DEIkr device8 -- device8 value 2537 | _DEIkr:: 2538 | ld hl, wRSTPtr 2539 | ld a, [hl] 2540 | ld e, a 2541 | inc a 2542 | ld [hl], a 2543 | sub 2 2544 | jr _DEI.continue 2545 | 2546 | ; DEI2 device8 -- value 2547 | _DEI2:: 2548 | ld hl, wWSTPtr 2549 | ld a, [hl] 2550 | inc [hl] 2551 | .continue0 2552 | ld e, a 2553 | dec e 2554 | .continue1 2555 | ; e = stack store pointer 2556 | ld l, a 2557 | dec l 2558 | ld d, [hl] 2559 | 2560 | ; DEVPEEK16 2561 | push hl 2562 | ld hl, wDevices 2563 | ld a, d 2564 | add l 2565 | ld l, a 2566 | 2567 | ld a, [hli] 2568 | ld b, [hl] 2569 | pop hl 2570 | ld l, e ; stack store location 2571 | ld [hli], a 2572 | ld [hl], b 2573 | 2574 | ; get handler address 2575 | ld a, d 2576 | and $F0 2577 | rrca 2578 | add 2 ; DEI2 handler offset 2579 | ld hl, DeviceHandlers 2580 | add l 2581 | ld l, a ; LUT uses ALIGN[7], so no need to worry about carry 2582 | ld a, [hli] 2583 | ld h, [hl] 2584 | ld l, a 2585 | rst CallHL 2586 | 2587 | ret 2588 | 2589 | ; DEI2r device8 -- value 2590 | _DEI2r:: 2591 | ld hl, wRSTPtr 2592 | ld a, [hl] 2593 | inc [hl] 2594 | jr _DEI2.continue0 2595 | 2596 | ; DEI2k device8 -- device8 value 2597 | _DEI2k:: 2598 | ld hl, wWSTPtr 2599 | ld a, [hl] 2600 | inc [hl] 2601 | inc [hl] 2602 | ld e, a 2603 | jr _DEI2.continue1 2604 | 2605 | ; DEI2kr device8 -- device8 value 2606 | _DEI2kr:: 2607 | ld hl, wRSTPtr 2608 | ld a, [hl] 2609 | inc [hl] 2610 | inc [hl] 2611 | ld e, a 2612 | jr _DEI2.continue1 2613 | 2614 | ; DEO value device8 -- 2615 | _DEO:: 2616 | ld hl, wWSTPtr 2617 | .continue0 2618 | dec [hl] 2619 | ld a, [hl] 2620 | dec [hl] 2621 | ld l, a 2622 | 2623 | .continue1 2624 | ld d, [hl] 2625 | dec l 2626 | ld b, [hl] 2627 | 2628 | ; DEVPOKE8 2629 | ASSERT(LOW(wDevices) == 0) 2630 | ;ld hl, wDevices 2631 | ;ld a, d 2632 | ;add l 2633 | ;ld l, a 2634 | ld h, HIGH(wDevices) 2635 | ld l, d 2636 | 2637 | ld [hl], b 2638 | 2639 | ; get handler address 2640 | ld a, d 2641 | and $F0 2642 | rrca 2643 | add 4 ; DEO handler offset 2644 | ld hl, DeviceHandlers 2645 | add l 2646 | ld l, a ; LUT uses ALIGN[7], so no need to worry about carry 2647 | ld a, [hli] 2648 | ld h, [hl] 2649 | ld l, a 2650 | rst CallHL 2651 | 2652 | ret 2653 | 2654 | ; DEOr val device8 -- 2655 | _DEOr:: 2656 | ld hl, wRSTPtr 2657 | jr _DEO.continue0 2658 | 2659 | ; DEOk value device8 -- value device8 2660 | _DEOk:: 2661 | ld hl, wWSTPtr 2662 | ld l, [hl] 2663 | dec l 2664 | jr _DEO.continue1 2665 | 2666 | ; DEOkr value device8 -- value device8 2667 | _DEOkr:: 2668 | ld hl, wRSTPtr 2669 | ld l, [hl] 2670 | dec l 2671 | jr _DEO.continue1 2672 | 2673 | ; DEO value device8 -- 2674 | _DEO2:: 2675 | ld hl, wWSTPtr 2676 | .continue0 2677 | dec [hl] 2678 | ld a, [hl] 2679 | dec [hl] 2680 | dec [hl] 2681 | ld l, a 2682 | 2683 | .continue1 2684 | ld d, [hl] 2685 | dec l 2686 | ld c, [hl] 2687 | dec l 2688 | ld b, [hl] 2689 | ; DEVPOKE16 2690 | ld hl, wDevices 2691 | ld a, d 2692 | add l 2693 | ld l, a 2694 | 2695 | ld [hl], b 2696 | inc l 2697 | ld [hl], c 2698 | 2699 | ; get handler address 2700 | ld a, d 2701 | and $F0 2702 | rrca 2703 | add 6 ; DEO2 handler offset 2704 | ld hl, DeviceHandlers 2705 | add l 2706 | ld l, a ; LUT uses ALIGN[7], so no need to worry about carry 2707 | ld a, [hli] 2708 | ld h, [hl] 2709 | ld l, a 2710 | rst CallHL 2711 | 2712 | ret 2713 | 2714 | ; DEOr val device8 -- 2715 | _DEO2r:: 2716 | ld hl, wRSTPtr 2717 | jr _DEO2.continue0 2718 | 2719 | ; DEO2k value device8 -- value device8 2720 | _DEO2k:: 2721 | ld hl, wWSTPtr 2722 | ld l, [hl] 2723 | dec l 2724 | jr _DEO2.continue1 2725 | 2726 | ; DEO2kr value device8 -- value device8 2727 | _DEO2kr:: 2728 | ld hl, wRSTPtr 2729 | ld l, [hl] 2730 | dec l 2731 | jr _DEO2.continue1 2732 | 2733 | ; ADD a b -- c 2734 | _ADD:: 2735 | ld hl, wWSTPtr 2736 | .continue 2737 | dec [hl] 2738 | ld l, [hl] 2739 | ld a, [hld] 2740 | ld b, [hl] 2741 | add b 2742 | ld [hl], a 2743 | ret 2744 | 2745 | ; ADDr a b -- c 2746 | _ADDr:: 2747 | ld hl, wRSTPtr 2748 | jr _ADD.continue 2749 | 2750 | ; ADDk a b -- a b c 2751 | _ADDk:: 2752 | ld hl, wWSTPtr 2753 | .continue 2754 | ld a, [hl] 2755 | inc [hl] 2756 | ld l, a 2757 | dec l 2758 | 2759 | ld a, [hld] 2760 | ld b, [hl] 2761 | add b 2762 | inc l 2763 | inc l 2764 | ld [hl], a 2765 | ret 2766 | 2767 | ; ADDkr a b -- a b c 2768 | _ADDkr:: 2769 | ld hl, wRSTPtr 2770 | jr _ADDk.continue 2771 | 2772 | ; ADD2 a b -- c 2773 | _ADD2:: 2774 | ld hl, wWSTPtr 2775 | .continue 2776 | dec [hl] 2777 | ld a, [hl] 2778 | dec [hl] 2779 | ld l, a 2780 | ld c, [hl] 2781 | dec l 2782 | ld b, [hl] 2783 | dec l 2784 | ld a, [hld] 2785 | ld d, [hl] 2786 | push hl 2787 | ld h, d 2788 | ld l, a 2789 | add hl, bc 2790 | ld a, h 2791 | ld e, l 2792 | pop hl 2793 | ld [hli], a 2794 | ld [hl], e 2795 | ret 2796 | 2797 | ; ADD2r a b -- c 2798 | _ADD2r:: 2799 | ld hl, wRSTPtr 2800 | jr _ADD2.continue 2801 | 2802 | ; ADD2k a b -- a b c 2803 | _ADD2k:: 2804 | WMATH_2K_SETUP 2805 | .finally 2806 | push hl 2807 | ld h, d 2808 | ld l, e 2809 | add hl, bc 2810 | ld a, h 2811 | ld e, l 2812 | pop hl 2813 | ld [hli], a 2814 | ld [hl], e 2815 | ret 2816 | 2817 | ; ADD2kr a b -- a b c 2818 | _ADD2kr:: 2819 | RMATH_2K_SETUP 2820 | jr _ADD2k.finally 2821 | 2822 | ; SUB a b -- c 2823 | _SUB:: 2824 | ld hl, wWSTPtr 2825 | .continue 2826 | dec [hl] 2827 | ld l, [hl] 2828 | dec l 2829 | ld a, [hli] 2830 | sub [hl] 2831 | dec l 2832 | ld [hl], a 2833 | ret 2834 | 2835 | ; SUBr a b -- c 2836 | _SUBr:: 2837 | ld hl, wRSTPtr 2838 | jr _SUB.continue 2839 | 2840 | ; SUBk a b -- a b c 2841 | _SUBk:: 2842 | ld hl, wWSTPtr 2843 | .continue 2844 | ld a, [hl] 2845 | inc [hl] 2846 | ld l, a 2847 | dec l 2848 | 2849 | dec l 2850 | ld a, [hli] 2851 | sub [hl] 2852 | inc l 2853 | ld [hl], a 2854 | ret 2855 | 2856 | ; SUBkr a b -- a b c 2857 | _SUBkr:: 2858 | ld hl, wRSTPtr 2859 | jr _SUBk.continue 2860 | 2861 | ; SUB2 a b -- c 2862 | _SUB2:: 2863 | ld hl, wWSTPtr 2864 | .continue 2865 | dec [hl] 2866 | ld a, [hl] 2867 | dec [hl] 2868 | ld l, a 2869 | ld e, [hl] 2870 | dec l 2871 | ld d, [hl] 2872 | dec l 2873 | ld c, [hl] 2874 | dec l 2875 | ld b, [hl] 2876 | ld a, c 2877 | sub e 2878 | ld c, a 2879 | ld a, b 2880 | sbc d 2881 | ld [hli], a 2882 | ld [hl], c 2883 | ret 2884 | 2885 | ; SUB2r a b -- c 2886 | _SUB2r:: 2887 | ld hl, wRSTPtr 2888 | jr _SUB2.continue 2889 | 2890 | ; SUB2k a b -- a b c 2891 | _SUB2k:: 2892 | WMATH_2K_SETUP 2893 | .finally 2894 | ld a, e 2895 | sub c 2896 | ld e, a 2897 | ld a, d 2898 | sbc b 2899 | ld [hli], a 2900 | ld [hl], e 2901 | ret 2902 | 2903 | ; SUB2kr a b -- a b c 2904 | _SUB2kr:: 2905 | RMATH_2K_SETUP 2906 | jr _SUB2k.finally 2907 | 2908 | ; MUL a b -- c 2909 | _MUL:: 2910 | ld hl, wWSTPtr 2911 | .continue 2912 | dec [hl] 2913 | ld l, [hl] 2914 | ld a, [hld] 2915 | ld e, [hl] 2916 | push hl 2917 | ld h, a 2918 | ld l, 0 2919 | ld d, l 2920 | 2921 | ; Taken from: https://www.cpcwiki.eu/index.php/Programming:Integer_Multiplication#Classic_8bit_.2A_8bit_Unsigned 2922 | ; TODO: Compare to https://github.com/pinobatch/little-things-gb/blob/f0d7ae77e6b6beebfd4a740f5f8f0ace5e330a11/bdos/src/math.z80#L227 2923 | sla h 2924 | jr nc, :+ 2925 | ld l, e 2926 | : 2927 | REPT 7 2928 | add hl, hl 2929 | jr nc, :+ 2930 | add hl, de 2931 | : 2932 | ENDR 2933 | 2934 | ld a, l 2935 | pop hl 2936 | ld [hl], a 2937 | ret 2938 | 2939 | ; MUrL a b -- c 2940 | _MULr:: 2941 | ld hl, wRSTPtr 2942 | jr _MUL.continue 2943 | 2944 | ; MULk a b -- a b c 2945 | _MULk:: 2946 | ld hl, wWSTPtr 2947 | ld a, [hl] 2948 | inc [hl] 2949 | .continue 2950 | dec a 2951 | ld l, a 2952 | ld a, [hld] 2953 | ld e, [hl] 2954 | push hl 2955 | ld h, a 2956 | ld l, 0 2957 | ld d, l 2958 | 2959 | ; Taken from: https://www.cpcwiki.eu/index.php/Programming:Integer_Multiplication#Classic_8bit_.2A_8bit_Unsigned 2960 | ; TODO: Compare to https://github.com/pinobatch/little-things-gb/blob/f0d7ae77e6b6beebfd4a740f5f8f0ace5e330a11/bdos/src/math.z80#L227 2961 | sla h 2962 | jr nc, :+ 2963 | ld l, e 2964 | : 2965 | REPT 7 2966 | add hl, hl 2967 | jr nc, :+ 2968 | add hl, de 2969 | : 2970 | ENDR 2971 | 2972 | ld a, l 2973 | pop hl 2974 | inc l 2975 | inc l 2976 | ld [hl], a 2977 | ret 2978 | 2979 | ; MULkr a b -- a b c 2980 | _MULkr:: 2981 | ld hl, wRSTPtr 2982 | jr _MULk.continue 2983 | 2984 | ; MUL2r a b c d -- e f 2985 | _MUL2r:: 2986 | ld hl, wRSTPtr 2987 | jr _MUL2.continue 2988 | 2989 | ; MUL2 a b c d -- e f 2990 | _MUL2:: 2991 | ld hl, wWSTPtr 2992 | .continue 2993 | dec [hl] 2994 | ld a, [hl] 2995 | dec [hl] 2996 | ld l, a 2997 | ld c, [hl] 2998 | dec l 2999 | 3000 | ld a, [hld] 3001 | ld e, [hl] 3002 | dec l 3003 | ld d, [hl] 3004 | push hl 3005 | 3006 | ; http://map.grauw.nl/articles/mult_div_shifts.php 3007 | 3008 | REPT 16 3009 | add hl, hl 3010 | sla c 3011 | rla 3012 | jr nc, :+ 3013 | add hl,de 3014 | : 3015 | ENDR 3016 | 3017 | ld a, h 3018 | ld b, l 3019 | 3020 | pop hl 3021 | ld [hli], a 3022 | ld [hl], b 3023 | ret 3024 | 3025 | ; MUL2kr a b c d -- a b c d e f 3026 | _MUL2kr:: 3027 | RBIT_2K_SETUP 3028 | ld d, [hl] 3029 | inc l 3030 | ld e, [hl] 3031 | inc l 3032 | ld a, [hli] 3033 | ld c, [hl] 3034 | inc l 3035 | jr _MUL2k.finally 3036 | 3037 | ; MUL2k a b c d -- a b c d e f 3038 | _MUL2k:: 3039 | ; Macro unrolled to optimize 3040 | WBIT_2K_SETUP 3041 | ld d, [hl] 3042 | inc l 3043 | ld e, [hl] 3044 | inc l 3045 | ld a, [hli] 3046 | ld c, [hl] 3047 | inc l 3048 | .finally 3049 | push hl 3050 | 3051 | ; http://map.grauw.nl/articles/mult_div_shifts.php 3052 | 3053 | REPT 16 3054 | add hl, hl 3055 | sla c 3056 | rla 3057 | jr nc, :+ 3058 | add hl,de 3059 | : 3060 | ENDR 3061 | 3062 | ld a, h 3063 | ld b, l 3064 | 3065 | pop hl 3066 | ld [hli], a 3067 | ld [hl], b 3068 | ret 3069 | 3070 | ; DIV a b -- c 3071 | _DIV:: 3072 | ld hl, wWSTPtr 3073 | .continue 3074 | dec [hl] 3075 | ld l, [hl] 3076 | ld c, [hl] 3077 | dec l 3078 | ld e, [hl] 3079 | 3080 | ; Source: http://map.grauw.nl/articles/mult_div_shifts.php 3081 | xor a 3082 | 3083 | REPT 8 3084 | rl e 3085 | rla 3086 | sub c 3087 | jr nc, :+ 3088 | add a, c 3089 | : 3090 | ENDR 3091 | 3092 | ld a, e 3093 | rla 3094 | cpl 3095 | 3096 | ld [hl], a 3097 | ret 3098 | 3099 | ; DIVr a b -- c 3100 | _DIVr:: 3101 | ld hl, wRSTPtr 3102 | jr _DIV.continue 3103 | 3104 | ; DIVk a b -- a b c 3105 | _DIVk:: 3106 | ld hl, wWSTPtr 3107 | .continue 3108 | ld a, [hl] 3109 | inc [hl] 3110 | dec a 3111 | ld l, a 3112 | ld c, [hl] 3113 | dec l 3114 | ld e, [hl] 3115 | 3116 | ; Source: http://map.grauw.nl/articles/mult_div_shifts.php 3117 | xor a 3118 | 3119 | REPT 8 3120 | rl e 3121 | rla 3122 | sub c 3123 | jr nc, :+ 3124 | add a, c 3125 | : 3126 | ENDR 3127 | 3128 | ld a, e 3129 | rla 3130 | cpl 3131 | 3132 | inc l 3133 | inc l 3134 | ld [hl], a 3135 | ret 3136 | 3137 | ; DIVkr a b -- a b c 3138 | _DIVkr:: 3139 | ld hl, wRSTPtr 3140 | jr _DIVk.continue 3141 | 3142 | ; DIV2 a b c d -- e f 3143 | _DIV2:: 3144 | ld hl, wWSTPtr 3145 | .continue 3146 | dec [hl] 3147 | ld a, [hl] 3148 | dec [hl] 3149 | ld l, a 3150 | ld c, [hl] 3151 | dec l 3152 | 3153 | ld b, [hl] 3154 | dec l 3155 | ld e, [hl] 3156 | dec l 3157 | ld d, [hl] 3158 | push hl 3159 | 3160 | ; http://www.devrs.com/gb/asmcode.php (U161616a v1.0 by Jeff Frohwein) 3161 | ld hl, _MD16temp 3162 | ld [hl], c 3163 | inc l 3164 | ld [hl], b 3165 | inc l 3166 | ld [hl], 17 3167 | ld bc, 0 3168 | .nextBit: 3169 | ld l, LOW(_MD16count) 3170 | ld a, e 3171 | rla 3172 | ld e, a 3173 | ld a, d 3174 | rla 3175 | ld d, a 3176 | dec [hl] 3177 | jr z, .done 3178 | ld a, c 3179 | rla 3180 | ld c, a 3181 | ld a, b 3182 | rla 3183 | ld b, a 3184 | dec l 3185 | dec l 3186 | ld a, c 3187 | sub [hl] 3188 | ld c, a 3189 | inc l 3190 | ld a, b 3191 | sbc a, [hl] 3192 | ld b, a 3193 | jr nc, .noAdd 3194 | 3195 | dec hl 3196 | ld a, c 3197 | add a, [hl] 3198 | ld c, a 3199 | inc l 3200 | ld a, b 3201 | adc a, [hl] 3202 | ld b, a 3203 | .noAdd: 3204 | ccf 3205 | jr .nextBit 3206 | .done 3207 | 3208 | pop hl 3209 | ld [hl], d 3210 | inc l 3211 | ld [hl], e 3212 | ret 3213 | 3214 | ; DIV2r a b c d -- e f 3215 | _DIV2r:: 3216 | ld hl, wRSTPtr 3217 | jr _DIV2.continue 3218 | 3219 | ; DIV2k a b c d -- a b c d e f 3220 | _DIV2k:: 3221 | WMATH_2K_SETUP 3222 | .finally 3223 | push hl 3224 | 3225 | ; http://www.devrs.com/gb/asmcode.php (U161616a v1.0 by Jeff Frohwein) 3226 | ld hl, _MD16temp 3227 | ld [hl], c 3228 | inc l 3229 | ld [hl], b 3230 | inc l 3231 | ld [hl], 17 3232 | ld bc, 0 3233 | .nextBit: 3234 | ld l, LOW(_MD16count) 3235 | ld a, e 3236 | rla 3237 | ld e, a 3238 | ld a, d 3239 | rla 3240 | ld d, a 3241 | dec [hl] 3242 | jr z, .done 3243 | ld a, c 3244 | rla 3245 | ld c, a 3246 | ld a, b 3247 | rla 3248 | ld b, a 3249 | dec l 3250 | dec l 3251 | ld a, c 3252 | sub [hl] 3253 | ld c, a 3254 | inc l 3255 | ld a, b 3256 | sbc a, [hl] 3257 | ld b, a 3258 | jr nc, .noAdd 3259 | 3260 | dec hl 3261 | ld a, c 3262 | add a, [hl] 3263 | ld c, a 3264 | inc l 3265 | ld a, b 3266 | adc a, [hl] 3267 | ld b, a 3268 | .noAdd: 3269 | ccf 3270 | jr .nextBit 3271 | .done 3272 | 3273 | pop hl 3274 | ld [hl], d 3275 | inc l 3276 | ld [hl], e 3277 | ret 3278 | 3279 | ; DIV2kr a b c d -- a b c d e f 3280 | _DIV2kr:: 3281 | RMATH_2K_SETUP 3282 | jr _DIV2k.finally 3283 | 3284 | ; AND a b -- c 3285 | _AND:: 3286 | ld hl, wWSTPtr 3287 | .continue 3288 | dec [hl] 3289 | ld l, [hl] 3290 | ld a, [hld] 3291 | and [hl] 3292 | ld [hl], a 3293 | ret 3294 | 3295 | ; ANDr a b -- c 3296 | _ANDr:: 3297 | ld hl, wRSTPtr 3298 | jr _AND.continue 3299 | 3300 | ; ANDk a b -- a b c 3301 | _ANDk:: 3302 | ld hl, wWSTPtr 3303 | .continue 3304 | ld a, [hl] 3305 | inc [hl] 3306 | ld l, a 3307 | dec l 3308 | 3309 | ld a, [hld] 3310 | and [hl] 3311 | inc l 3312 | inc l 3313 | ld [hl], a 3314 | ret 3315 | 3316 | ; ANDkr a b -- a b c 3317 | _ANDkr:: 3318 | ld hl, wRSTPtr 3319 | jr _ANDk.continue 3320 | 3321 | ; AND2 a b -- c 3322 | _AND2:: 3323 | ld hl, wWSTPtr 3324 | .continue 3325 | dec [hl] 3326 | ld a, [hl] 3327 | dec [hl] 3328 | ld l, a 3329 | ld a, [hld] 3330 | ld b, [hl] 3331 | dec l 3332 | and [hl] 3333 | ld [hld], a 3334 | ld a, b 3335 | and [hl] 3336 | ld [hl], a 3337 | ret 3338 | 3339 | ; AND2r a b -- c 3340 | _AND2r:: 3341 | ld hl, wRSTPtr 3342 | jr _AND2.continue 3343 | 3344 | ; AND2k a b -- a b c 3345 | _AND2k:: 3346 | WBIT_2K_SETUP 3347 | ld a, [hli] 3348 | ld b, [hl] 3349 | inc l 3350 | and [hl] 3351 | ld c, a ; result of high byte OP in C 3352 | inc l 3353 | ld a, b 3354 | and [hl] ; result of low byte OP in A 3355 | inc l 3356 | ld [hl], c 3357 | inc l 3358 | ld [hl], a 3359 | ret 3360 | 3361 | ; AND2kr a b -- a b c 3362 | _AND2kr:: 3363 | RBIT_2K_SETUP 3364 | jr _AND2k.continue 3365 | 3366 | ; ORA a b -- c 3367 | _ORA:: 3368 | ld hl, wWSTPtr 3369 | .continue 3370 | dec [hl] 3371 | ld l, [hl] 3372 | ld a, [hld] 3373 | or [hl] 3374 | ld [hl], a 3375 | ret 3376 | 3377 | ; ORAr a b -- c 3378 | _ORAr:: 3379 | ld hl, wRSTPtr 3380 | jr _ORA.continue 3381 | 3382 | ; ORAk a b -- a b c 3383 | _ORAk:: 3384 | ld hl, wWSTPtr 3385 | .continue 3386 | ld a, [hl] 3387 | inc [hl] 3388 | ld l, a 3389 | dec l 3390 | 3391 | ld a, [hld] 3392 | or [hl] 3393 | inc l 3394 | inc l 3395 | ld [hl], a 3396 | ret 3397 | 3398 | ; ORAkr a b -- a b c 3399 | _ORAkr:: 3400 | ld hl, wRSTPtr 3401 | jr _ORAk.continue 3402 | 3403 | ; ORA2 a b -- c 3404 | _ORA2:: 3405 | ld hl, wWSTPtr 3406 | .continue 3407 | dec [hl] 3408 | ld a, [hl] 3409 | dec [hl] 3410 | ld l, a 3411 | ld a, [hld] 3412 | ld b, [hl] 3413 | dec l 3414 | or [hl] 3415 | ld [hld], a 3416 | ld a, b 3417 | or [hl] 3418 | ld [hl], a 3419 | ret 3420 | 3421 | ; ORA2r a b -- c 3422 | _ORA2r:: 3423 | ld hl, wRSTPtr 3424 | jr _ORA2.continue 3425 | 3426 | ; ORA2k a b -- a b c 3427 | _ORA2k:: 3428 | WBIT_2K_SETUP 3429 | ld a, [hli] 3430 | ld b, [hl] 3431 | inc l 3432 | or [hl] 3433 | ld c, a ; result of high byte OP in C 3434 | inc l 3435 | ld a, b 3436 | or [hl] ; result of low byte OP in A 3437 | inc l 3438 | ld [hl], c 3439 | inc l 3440 | ld [hl], a 3441 | ret 3442 | 3443 | ; ORA2kr a b -- a b c 3444 | _ORA2kr:: 3445 | RBIT_2K_SETUP 3446 | jr _ORA2k.continue 3447 | 3448 | ; EOR a b -- c 3449 | _EOR:: 3450 | ld hl, wWSTPtr 3451 | .continue 3452 | dec [hl] 3453 | ld l, [hl] 3454 | ld a, [hld] 3455 | xor [hl] 3456 | ld [hl], a 3457 | ret 3458 | 3459 | ; EORr a b -- c 3460 | _EORr:: 3461 | ld hl, wRSTPtr 3462 | jr _EOR.continue 3463 | 3464 | ; EORk a b -- a b c 3465 | _EORk:: 3466 | ld hl, wWSTPtr 3467 | .continue 3468 | ld a, [hl] 3469 | inc [hl] 3470 | ld l, a 3471 | dec l 3472 | 3473 | ld a, [hld] 3474 | xor [hl] 3475 | inc l 3476 | inc l 3477 | ld [hl], a 3478 | ret 3479 | 3480 | ; EORkr a b -- a b c 3481 | _EORkr:: 3482 | ld hl, wRSTPtr 3483 | jr _EORk.continue 3484 | 3485 | ; EOR2 a b -- c 3486 | _EOR2:: 3487 | ld hl, wWSTPtr 3488 | .continue 3489 | dec [hl] 3490 | ld a, [hl] 3491 | dec [hl] 3492 | ld l, a 3493 | ld a, [hld] 3494 | ld b, [hl] 3495 | dec l 3496 | xor [hl] 3497 | ld [hld], a 3498 | ld a, b 3499 | xor [hl] 3500 | ld [hl], a 3501 | ret 3502 | 3503 | ; EOR2r a b -- c 3504 | _EOR2r:: 3505 | ld hl, wRSTPtr 3506 | jr _EOR2.continue 3507 | 3508 | ; EOR2k a b -- a b c 3509 | _EOR2k:: 3510 | WBIT_2K_SETUP 3511 | ld a, [hli] 3512 | ld b, [hl] 3513 | inc l 3514 | xor [hl] 3515 | ld c, a ; result of high byte OP in C 3516 | inc l 3517 | ld a, b 3518 | xor [hl] ; result of low byte OP in A 3519 | inc l 3520 | ld [hl], c 3521 | inc l 3522 | ld [hl], a 3523 | ret 3524 | 3525 | ; EOR2kr a b -- a b c 3526 | _EOR2kr:: 3527 | RBIT_2K_SETUP 3528 | jr _EOR2k.continue 3529 | 3530 | ; SFT a shift8 -- c 3531 | _SFT:: 3532 | ld hl, wWSTPtr 3533 | .continue 3534 | dec [hl] 3535 | ld l, [hl] 3536 | ld b, [hl] 3537 | dec l 3538 | ld c, [hl] 3539 | ld a, b 3540 | and $0F ; get right shift count 3541 | or a 3542 | jr z, .doneRightShift 3543 | .rightShift 3544 | srl c 3545 | dec a 3546 | jr nz, .rightShift 3547 | .doneRightShift 3548 | ld a, b 3549 | and $F0 ; get left shift count 3550 | swap a 3551 | or a 3552 | jr z, .doneLeftShift 3553 | .leftShift 3554 | sla c 3555 | dec a 3556 | jr nz, .leftShift 3557 | .doneLeftShift 3558 | ld [hl], c 3559 | ret 3560 | 3561 | ; SFTr a shift8 -- c 3562 | _SFTr:: 3563 | ld hl, wRSTPtr 3564 | jr _SFT.continue 3565 | 3566 | ; SFTk a shift8 -- a shift8 c 3567 | _SFTk:: 3568 | ld hl, wWSTPtr 3569 | .continue 3570 | inc [hl] 3571 | ld l, [hl] 3572 | dec l 3573 | dec l 3574 | ld b, [hl] 3575 | dec l 3576 | ld c, [hl] 3577 | ld a, b 3578 | and $0F ; get right shift count 3579 | or a 3580 | jr z, .doneRightShift 3581 | .rightShift 3582 | srl c 3583 | dec a 3584 | jr nz, .rightShift 3585 | .doneRightShift 3586 | ld a, b 3587 | and $F0 ; get left shift count 3588 | swap a 3589 | or a 3590 | jr z, .doneLeftShift 3591 | .leftShift 3592 | sla c 3593 | dec a 3594 | jr nz, .leftShift 3595 | .doneLeftShift 3596 | inc l ; TODO: Optimize hl traversal 3597 | inc l 3598 | ld [hl], c 3599 | ret 3600 | 3601 | ; SFTkr a shift8 -- a shift8 c 3602 | _SFTkr:: 3603 | ld hl, wRSTPtr 3604 | jr _SFTk.continue 3605 | 3606 | 3607 | ; SFT2 a shift8 -- c 3608 | _SFT2:: 3609 | ld hl, wWSTPtr 3610 | .continue 3611 | dec [hl] 3612 | ld l, [hl] 3613 | 3614 | ld d, [hl] 3615 | dec l 3616 | ld c, [hl] 3617 | dec l 3618 | ld b, [hl] 3619 | ld a, d 3620 | and $0F ; get right shift count 3621 | or a 3622 | jr z, .doneRightShift 3623 | .rightShift 3624 | srl b 3625 | rr c 3626 | dec a 3627 | jr nz, .rightShift 3628 | .doneRightShift 3629 | ld a, d 3630 | and $F0 ; get left shift count 3631 | swap a 3632 | or a 3633 | jr z, .doneLeftShift 3634 | .leftShift 3635 | sla c 3636 | rl b 3637 | dec a 3638 | jr nz, .leftShift 3639 | .doneLeftShift 3640 | ld [hl], b 3641 | inc l 3642 | ld [hl], c 3643 | ret 3644 | 3645 | ; SFT2r a shift8 -- c 3646 | _SFT2r:: 3647 | ld hl, wRSTPtr 3648 | jr _SFT2.continue 3649 | 3650 | ; SFT2k a shift8 -- a shift8 c 3651 | _SFT2k:: 3652 | ld hl, wWSTPtr 3653 | inc [hl] 3654 | inc [hl] 3655 | ld a, [hl] 3656 | .continue 3657 | sub 5 3658 | ld l, a 3659 | 3660 | ld b, [hl] 3661 | inc l 3662 | ld c, [hl] 3663 | inc l 3664 | ld d, [hl] 3665 | inc l 3666 | 3667 | ld a, d 3668 | and $0F ; get right shift count 3669 | or a 3670 | jr z, .doneRightShift 3671 | .rightShift 3672 | srl b 3673 | rr c 3674 | dec a 3675 | jr nz, .rightShift 3676 | .doneRightShift 3677 | ld a, d 3678 | and $F0 ; get left shift count 3679 | swap a 3680 | or a 3681 | jr z, .doneLeftShift 3682 | .leftShift 3683 | sla c 3684 | rl b 3685 | dec a 3686 | jr nz, .leftShift 3687 | .doneLeftShift 3688 | ld [hl], b 3689 | inc l 3690 | ld [hl], c 3691 | ret 3692 | 3693 | ; SFT2kr a shift8 -- a shift8 c 3694 | _SFT2kr:: 3695 | ld h, HIGH(wRST) 3696 | ld a, [wRSTPtr] 3697 | inc a 3698 | inc a 3699 | ld [wRSTPtr], a 3700 | jr _SFT2k.continue 3701 | --------------------------------------------------------------------------------