├── .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 | 
15 | 
16 | 
17 | 
18 | 
19 | 
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 |
--------------------------------------------------------------------------------