diff options
author | Bad Diode <bd@badd10de.dev> | 2021-05-18 16:40:24 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-05-18 16:40:24 +0200 |
commit | 0c7265cf0de9d4ec95d28c5e103c00a63f4a1697 (patch) | |
tree | 4a1145e849e078395430a8d718c4bd69a06fb29f | |
download | uxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.tar.gz uxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.zip |
Proof of concept of UXN on the GBA
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 74 | ||||
-rw-r--r-- | src/bd-font.c | 130 | ||||
-rw-r--r-- | src/bios_calls.s | 337 | ||||
-rw-r--r-- | src/bitmap.h | 209 | ||||
-rw-r--r-- | src/common.h | 696 | ||||
-rw-r--r-- | src/irs.s | 89 | ||||
-rw-r--r-- | src/main.c | 155 | ||||
-rw-r--r-- | src/shorthand.h | 36 | ||||
-rw-r--r-- | src/small-font.c | 45 | ||||
-rw-r--r-- | src/sprites.h | 79 | ||||
-rw-r--r-- | src/text.h | 232 | ||||
-rw-r--r-- | src/uxn/devices/ppu.c | 180 | ||||
-rw-r--r-- | src/uxn/devices/ppu.h | 36 | ||||
-rw-r--r-- | src/uxn/roms/console.c | 5 | ||||
-rw-r--r-- | src/uxn/roms/dvd.c | 22 | ||||
-rw-r--r-- | src/uxn/uxn.c | 196 | ||||
-rw-r--r-- | src/uxn/uxn.h | 55 |
18 files changed, 2577 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1 @@ | |||
build | |||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6d7a5ec --- /dev/null +++ b/Makefile | |||
@@ -0,0 +1,74 @@ | |||
1 | .POSIX: | ||
2 | .SUFFIXES: | ||
3 | |||
4 | # Path to the development kit (devkitARM) and the GBA library. | ||
5 | DEVKITPRO := /opt/devkitpro | ||
6 | DEVKITARM := /opt/devkitpro/devkitARM | ||
7 | PATH := $(DEVKITARM)/bin:$(PATH) | ||
8 | LIBGBA_DIR := $(DEVKITPRO)/libgba | ||
9 | LIBGBA_SRC := /opt/devkitpro/libgba/include/ | ||
10 | LIBGBA := $(LIBGBA_DIR)/lib/libgba.a | ||
11 | LIBGBA += $(LIBGBA_DIR)/lib/libfat.a | ||
12 | LIBGBA += $(LIBGBA_DIR)/lib/libmm.a | ||
13 | |||
14 | # Source code location and files to watch for changes. | ||
15 | SRC_DIR := src | ||
16 | SRC_MAIN := $(SRC_DIR)/main.c | ||
17 | ASM_FILES := $(wildcard $(SRC_DIR)/*.s) | ||
18 | WATCH_SRC := $(wildcard $(SRC_DIR)/*.c) | ||
19 | WATCH_SRC += $(wildcard $(SRC_DIR)/*.h) | ||
20 | WATCH_SRC += $(wildcard $(SRC_DIR)/*.s) | ||
21 | |||
22 | # Output library names and executables. | ||
23 | TARGET := uxngba | ||
24 | BUILD_DIR := build | ||
25 | ELF := $(BUILD_DIR)/$(TARGET).elf | ||
26 | BIN := $(BUILD_DIR)/$(TARGET).gba | ||
27 | |||
28 | # Compiler and linker configuration. | ||
29 | CC := arm-none-eabi-gcc | ||
30 | OBJCOPY := arm-none-eabi-objcopy | ||
31 | ARCH := -mthumb -mthumb-interwork | ||
32 | SPECS := -specs=gba.specs | ||
33 | CFLAGS := -Wall -Wextra -pedantic -fno-strict-aliasing -Wno-incompatible-pointer-types | ||
34 | CFLAGS += -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH) | ||
35 | CFLAGS += -I$(LIBGBA_SRC) | ||
36 | LDFLAGS := $(ARCH) $(SPECS) | ||
37 | LDLIBS := $(LIBGBA) | ||
38 | RELEASE_CFLAGS := -DNDEBUG -O2 -g | ||
39 | DEBUG_CFLAGS := -DDEBUG -O2 -g | ||
40 | |||
41 | .PHONY: clean run | ||
42 | |||
43 | # Setup debug/release builds. | ||
44 | # make clean && make <target> DEBUG=0 | ||
45 | # make clean && make <target> DEBUG=1 | ||
46 | DEBUG ?= 0 | ||
47 | ifeq ($(DEBUG), 1) | ||
48 | CFLAGS += $(DEBUG_CFLAGS) | ||
49 | else | ||
50 | CFLAGS += $(RELEASE_CFLAGS) | ||
51 | endif | ||
52 | |||
53 | main: $(BUILD_DIR) $(BIN) | ||
54 | |||
55 | # Strip and fix header to create final .gba file. | ||
56 | $(BIN): $(ELF) | ||
57 | $(OBJCOPY) -v -O binary $(ELF) $(BIN) | ||
58 | gbafix $(BIN) | ||
59 | |||
60 | # Link files. | ||
61 | $(ELF): $(SRC_MAIN) $(WATCH_SRC) | ||
62 | $(CC) $(CFLAGS) $(LDFLAGS) -o $(ELF) $(SRC_MAIN) $(ASM_FILES) $(LDLIBS) | ||
63 | |||
64 | # Create build directory if needed. | ||
65 | $(BUILD_DIR): | ||
66 | mkdir -p $(BUILD_DIR) | ||
67 | |||
68 | # Test the output .gba in an emulator. | ||
69 | run: main | ||
70 | mgba-qt $(BIN) | ||
71 | |||
72 | # Remove build directory. | ||
73 | clean: | ||
74 | rm -r $(BUILD_DIR) | ||
diff --git a/src/bd-font.c b/src/bd-font.c new file mode 100644 index 0000000..e3fb6cc --- /dev/null +++ b/src/bd-font.c | |||
@@ -0,0 +1,130 @@ | |||
1 | u32 bd_font[] = { | ||
2 | 0x00000000, 0x00000000, 0x00002400, 0x423c0000, | ||
3 | 0x00002400, 0x3c420000, 0x0000363e, 0x3e1c0800, | ||
4 | 0x00081c3e, 0x3e1c0800, 0x001c1c3e, 0x363e081c, | ||
5 | 0x00081c3e, 0x3e3e081c, 0x00000018, 0x18000000, | ||
6 | 0x7e7e7e66, 0x667e7e7e, 0x00001824, 0x24180000, | ||
7 | 0x7e7e665a, 0x5a667e7e, 0x00081c3e, 0x081c221c, | ||
8 | 0x001c221c, 0x08083e08, 0x00183828, 0x08080c0c, | ||
9 | 0x003c2424, 0x24343606, 0x00082208, 0x1c082208, | ||
10 | 0x040c1c3c, 0x1c0c0400, 0x2030383c, 0x38302000, | ||
11 | 0x081c3e08, 0x083e1c08, 0x00141414, 0x14140014, | ||
12 | 0x003c2a2a, 0x2c282828, 0x0038043c, 0x423c201e, | ||
13 | 0x00000000, 0x7e000000, 0x081c3e08, 0x3e1c083e, | ||
14 | 0x081c3e08, 0x08080808, 0x08080808, 0x083e1c08, | ||
15 | 0x00001030, 0x7e301000, 0x0000080c, 0x7e0c0800, | ||
16 | 0x00000000, 0x0002023e, 0x00002466, 0xff662400, | ||
17 | 0x0008081c, 0x1c3e3e00, 0x003e3e1c, 0x1c080800, | ||
18 | 0x00000000, 0x00000000, 0x00080808, 0x08080008, | ||
19 | 0x14141400, 0x00000000, 0x0000143e, 0x143e1400, | ||
20 | 0x00081c02, 0x1c201e08, 0x00002616, 0x08343200, | ||
21 | 0x00081408, 0x34122c00, 0x08080800, 0x00000000, | ||
22 | 0x08040404, 0x04040408, 0x08101010, 0x10101008, | ||
23 | 0x00001408, 0x3e081400, 0x00000808, 0x3e080800, | ||
24 | 0x00000000, 0x000c0804, 0x00000000, 0x3e000000, | ||
25 | 0x00000000, 0x000c0c00, 0x00203018, 0x0c060200, | ||
26 | 0x00001c22, 0x2a221c00, 0x00000c0a, 0x08083e00, | ||
27 | 0x00001c20, 0x1c023e00, 0x00001c22, 0x18221c00, | ||
28 | 0x00001018, 0x143e1000, 0x00003e02, 0x1e201e00, | ||
29 | 0x00001c02, 0x1e221c00, 0x00003e20, 0x10080400, | ||
30 | 0x00001c22, 0x1c221c00, 0x00001c22, 0x3c201c00, | ||
31 | 0x00000c0c, 0x000c0c00, 0x00000c0c, 0x000c0804, | ||
32 | 0x00100804, 0x04081000, 0x0000003e, 0x003e0000, | ||
33 | 0x00040810, 0x10080400, 0x001c2220, 0x10080008, | ||
34 | 0x001c322a, 0x1a021c00, 0x001c2222, 0x3e222200, | ||
35 | 0x001e221e, 0x22221e00, 0x001c2202, 0x02221c00, | ||
36 | 0x000e1222, 0x22120e00, 0x003e021e, 0x02023e00, | ||
37 | 0x003e021e, 0x02020200, 0x001c2202, 0x32221c00, | ||
38 | 0x00222222, 0x3e222200, 0x003e0808, 0x08083e00, | ||
39 | 0x00202020, 0x22221c00, 0x0022120a, 0x0e122200, | ||
40 | 0x00020202, 0x02023e00, 0x0022362a, 0x22222200, | ||
41 | 0x0022262a, 0x32222200, 0x001c2222, 0x22221c00, | ||
42 | 0x001e2222, 0x1e020200, 0x001c2222, 0x2a122c00, | ||
43 | 0x001e2222, 0x1e122200, 0x001c021c, 0x20221c00, | ||
44 | 0x003e0808, 0x08080800, 0x00222222, 0x22221c00, | ||
45 | 0x00222222, 0x22140800, 0x0022222a, 0x2a2a1400, | ||
46 | 0x00221408, 0x08142200, 0x00222214, 0x08080800, | ||
47 | 0x003e1008, 0x04023e00, 0x1c040404, 0x0404041c, | ||
48 | 0x0002060c, 0x18302000, 0x1c101010, 0x1010101c, | ||
49 | 0x08142200, 0x00000000, 0x00000000, 0x00003e00, | ||
50 | 0x00040810, 0x00000000, 0x00001c20, 0x3c223c00, | ||
51 | 0x0002021e, 0x22221e00, 0x00001c22, 0x02221c00, | ||
52 | 0x0020203c, 0x22223c00, 0x00001c22, 0x1e023c00, | ||
53 | 0x00003c02, 0x021e0202, 0x00003c22, 0x223c201c, | ||
54 | 0x0002021e, 0x22222200, 0x0008000c, 0x08083e00, | ||
55 | 0x00200020, 0x2020221c, 0x0002120a, 0x060a3200, | ||
56 | 0x000c0808, 0x08083e00, 0x0000162a, 0x2a2a2a00, | ||
57 | 0x00000e32, 0x22222200, 0x00001c22, 0x22221c00, | ||
58 | 0x00001e22, 0x221e0202, 0x00003c22, 0x223c2070, | ||
59 | 0x00001a26, 0x02020200, 0x00001c02, 0x1c201e00, | ||
60 | 0x00043e04, 0x04041800, 0x00002222, 0x22221c00, | ||
61 | 0x00002222, 0x22140800, 0x00002222, 0x2a2a1400, | ||
62 | 0x00002214, 0x08142200, 0x00002222, 0x223c201c, | ||
63 | 0x00003e10, 0x08043e00, 0x18040402, 0x02040418, | ||
64 | 0x00080808, 0x08080808, 0x0c101020, 0x2010100c, | ||
65 | 0x00002c1a, 0x00000000, 0x00000814, 0x22223e00, | ||
66 | 0x001c2202, 0x221c080e, 0x00140022, 0x2222221c, | ||
67 | 0x1008001c, 0x221e023c, 0x0814001c, 0x203c223c, | ||
68 | 0x0014001c, 0x203c223c, 0x0408001c, 0x203c223c, | ||
69 | 0x0814081c, 0x203c223c, 0x00001c02, 0x221c080e, | ||
70 | 0x0814001c, 0x221e023c, 0x0014001c, 0x221e023c, | ||
71 | 0x0408001c, 0x221e023c, 0x0014000c, 0x0808083e, | ||
72 | 0x0014000c, 0x0808083e, 0x0408000c, 0x0808083e, | ||
73 | 0x0014001c, 0x223e2222, 0x0814081c, 0x223e2222, | ||
74 | 0x1008003e, 0x021e023e, 0x00001628, 0x1c0a3400, | ||
75 | 0x003c0a1a, 0x0e0a3a00, 0x0814001c, 0x2222221c, | ||
76 | 0x0014001c, 0x2222221c, 0x0408001c, 0x2222221c, | ||
77 | 0x08140022, 0x2222221c, 0x04080022, 0x2222221c, | ||
78 | 0x00140022, 0x223c201c, 0x14001c22, 0x2222221c, | ||
79 | 0x14002222, 0x2222221c, 0x00081c22, 0x02221c08, | ||
80 | 0x0018240e, 0x04043e00, 0x00221408, 0x1c081c08, | ||
81 | 0x0038041e, 0x041e0438, 0x0030081c, 0x08080806, | ||
82 | 0x1008001c, 0x203c223c, 0x1008000c, 0x0808083e, | ||
83 | 0x1008001c, 0x2222221c, 0x10080022, 0x2222221c, | ||
84 | 0x2c1a000e, 0x32222222, 0x2c1a0022, 0x262a3222, | ||
85 | 0x001c122c, 0x001e0000, 0x000c120c, 0x001e0000, | ||
86 | 0x08000804, 0x02221c00, 0x0000003e, 0x02020000, | ||
87 | 0x0000003e, 0x20200000, 0x06241468, 0x4422f000, | ||
88 | 0x06241468, 0x54f24000, 0x08000808, 0x08080800, | ||
89 | 0x00482412, 0x24480000, 0x00122448, 0x24120000, | ||
90 | 0x00880022, 0x00880022, 0x55885522, 0x55885522, | ||
91 | 0x55aa55aa, 0x55aa55aa, 0x08080808, 0x08080808, | ||
92 | 0x0808080f, 0x08080808, 0x08080f08, 0x0f080808, | ||
93 | 0x14141417, 0x14141414, 0x0000001f, 0x14141414, | ||
94 | 0x00000f08, 0x0f080808, 0x14141710, 0x17141414, | ||
95 | 0x14141414, 0x14141414, 0x00001f10, 0x17141414, | ||
96 | 0x14141710, 0x1f000000, 0x1414141f, 0x00000000, | ||
97 | 0x08080f08, 0x0f000000, 0x0000000f, 0x08080808, | ||
98 | 0x080808f8, 0x00000000, 0x080808ff, 0x00000000, | ||
99 | 0x000000ff, 0x08080808, 0x080808f8, 0x08080808, | ||
100 | 0x000000ff, 0x00000000, 0x080808ff, 0x08080808, | ||
101 | 0x0808f808, 0xf8080808, 0x141414f4, 0x14141414, | ||
102 | 0x1414f404, 0xfc000000, 0x0000fc04, 0xf4141414, | ||
103 | 0x1414f700, 0xff000000, 0x0000ff00, 0xf7141414, | ||
104 | 0x1414f404, 0xf4141414, 0x0000ff00, 0xff000000, | ||
105 | 0x1414f700, 0xf7141414, 0x0808ff00, 0xff000000, | ||
106 | 0x141414ff, 0x00000000, 0x0000ff00, 0xff080808, | ||
107 | 0x000000ff, 0x14141414, 0x141414fc, 0x00000000, | ||
108 | 0x0808f808, 0xf8000000, 0x0000f808, 0xf8080808, | ||
109 | 0x000000fc, 0x14141414, 0x141414ff, 0x14141414, | ||
110 | 0x0808ff08, 0xff080808, 0x0808080f, 0x00000000, | ||
111 | 0x000000f8, 0x08080808, 0xffffffff, 0xffffffff, | ||
112 | 0x00000000, 0xffffffff, 0x0f0f0f0f, 0x0f0f0f0f, | ||
113 | 0xf0f0f0f0, 0xf0f0f0f0, 0xffffffff, 0x00000000, | ||
114 | 0x00002c12, 0x12122c00, 0x000c120a, 0x12223a02, | ||
115 | 0x003e2202, 0x02020200, 0x00003e14, 0x14141400, | ||
116 | 0x3e220408, 0x04223e00, 0x00003c12, 0x12120c00, | ||
117 | 0x00002222, 0x221e0202, 0x002c1a08, 0x08281000, | ||
118 | 0x1c081c22, 0x221c081c, 0x1c22223e, 0x22221c00, | ||
119 | 0x1c222222, 0x22143600, 0x1e041824, 0x22221c00, | ||
120 | 0x0000142a, 0x2a140000, 0x0020142a, 0x2a140200, | ||
121 | 0x00003c02, 0x1c023c00, 0x001c2222, 0x22222202, | ||
122 | 0x00003e00, 0x3e003e00, 0x0000081c, 0x08001c00, | ||
123 | 0x04081008, 0x04001c00, 0x10080408, 0x10001c00, | ||
124 | 0x30480808, 0x08080808, 0x08080808, 0x08080906, | ||
125 | 0x00000800, 0x3e000800, 0x00004c32, 0x004c3200, | ||
126 | 0x00182418, 0x00000000, 0x00001c1c, 0x1c000000, | ||
127 | 0x00000018, 0x18000000, 0x00f01010, 0x10121418, | ||
128 | 0x0c342424, 0x00000000, 0x1810083c, 0x00000000, | ||
129 | 0x00003c3c, 0x3c3c0000, 0x00000000, 0x00000000, | ||
130 | }; | ||
diff --git a/src/bios_calls.s b/src/bios_calls.s new file mode 100644 index 0000000..740fa02 --- /dev/null +++ b/src/bios_calls.s | |||
@@ -0,0 +1,337 @@ | |||
1 | @ | ||
2 | @ Arithmetic functions. | ||
3 | @ | ||
4 | |||
5 | @ Division. | ||
6 | .text | ||
7 | .code 16 | ||
8 | .align 2 | ||
9 | .global bios_div | ||
10 | .thumb_func | ||
11 | bios_div: | ||
12 | swi 0x06 | ||
13 | bx lr | ||
14 | |||
15 | @ Square root. | ||
16 | .text | ||
17 | .code 16 | ||
18 | .align 2 | ||
19 | .global bios_sqrt | ||
20 | .thumb_func | ||
21 | bios_sqrt: | ||
22 | swi 0x08 | ||
23 | bx lr | ||
24 | |||
25 | @ Arc-tangent. | ||
26 | .text | ||
27 | .code 16 | ||
28 | .align 2 | ||
29 | .global bios_atan | ||
30 | .thumb_func | ||
31 | bios_atan: | ||
32 | swi 0x09 | ||
33 | bx lr | ||
34 | |||
35 | @ Arc-tangent2. | ||
36 | .text | ||
37 | .code 16 | ||
38 | .align 2 | ||
39 | .global bios_atan2 | ||
40 | .thumb_func | ||
41 | bios_atan2: | ||
42 | swi 0x0a | ||
43 | bx lr | ||
44 | |||
45 | @ | ||
46 | @ Rotation/Scaling. | ||
47 | @ | ||
48 | |||
49 | @ BG Affine Set. | ||
50 | .text | ||
51 | .code 16 | ||
52 | .align 2 | ||
53 | .global bios_bg_affine_set | ||
54 | .thumb_func | ||
55 | bios_bg_affine_set: | ||
56 | swi 0x0e | ||
57 | bx lr | ||
58 | |||
59 | @ OBJ Affine Set. | ||
60 | .text | ||
61 | .code 16 | ||
62 | .align 2 | ||
63 | .global bios_obj_affine_set | ||
64 | .thumb_func | ||
65 | bios_obj_affine_set: | ||
66 | swi 0x0f | ||
67 | bx lr | ||
68 | |||
69 | @ | ||
70 | @ (De)compression. | ||
71 | @ | ||
72 | |||
73 | @ Bit unpacking. | ||
74 | .text | ||
75 | .code 16 | ||
76 | .align 2 | ||
77 | .global bios_bit_unpack | ||
78 | .thumb_func | ||
79 | bios_bit_unpack: | ||
80 | swi 0x10 | ||
81 | bx lr | ||
82 | |||
83 | @ Huffman decompression. | ||
84 | .text | ||
85 | .code 16 | ||
86 | .align 2 | ||
87 | .global bios_huff_expand | ||
88 | .thumb_func | ||
89 | bios_huff_expand: | ||
90 | swi 0x13 | ||
91 | bx lr | ||
92 | |||
93 | @ LZ77 decompression (Fast, WRAM, 8 bit writes). | ||
94 | .text | ||
95 | .code 16 | ||
96 | .align 2 | ||
97 | .global bios_lz77_expand_wram | ||
98 | .thumb_func | ||
99 | bios_lz77_expand_wram: | ||
100 | swi 0x11 | ||
101 | bx lr | ||
102 | |||
103 | @ LZ77 decompression (Slow, VRAM, 16 bit writes). | ||
104 | .text | ||
105 | .code 16 | ||
106 | .align 2 | ||
107 | .global bios_lz77_expand_vram | ||
108 | .thumb_func | ||
109 | bios_lz77_expand_vram: | ||
110 | swi 0x12 | ||
111 | bx lr | ||
112 | |||
113 | @ Run length encoding decompression (Fast, WRAM, 8 bit writes). | ||
114 | .text | ||
115 | .code 16 | ||
116 | .align 2 | ||
117 | .global bios_rle_expand_wram | ||
118 | .thumb_func | ||
119 | bios_rle_expand_wram: | ||
120 | swi 0x14 | ||
121 | bx lr | ||
122 | |||
123 | @ Run length encoding decompression (Slow, VRAM, 16 bit writes). | ||
124 | .text | ||
125 | .code 16 | ||
126 | .align 2 | ||
127 | .global bios_rle_expand_vram | ||
128 | .thumb_func | ||
129 | bios_rle_expand_vram: | ||
130 | swi 0x15 | ||
131 | bx lr | ||
132 | |||
133 | @ | ||
134 | @ Memory copy. | ||
135 | @ | ||
136 | |||
137 | @ Memcopy/memfill in 32 byte units (Fast). | ||
138 | .text | ||
139 | .code 16 | ||
140 | .align 2 | ||
141 | .global bios_memcopy_32 | ||
142 | .thumb_func | ||
143 | bios_memcopy_32: | ||
144 | swi 0x0C | ||
145 | bx lr | ||
146 | |||
147 | @ Memcopy/memfill in 4 / 2 byte units (Slow). | ||
148 | .text | ||
149 | .code 16 | ||
150 | .align 2 | ||
151 | .global bios_memcopy_8 | ||
152 | .thumb_func | ||
153 | bios_memcopy_8: | ||
154 | swi 0x0B | ||
155 | bx lr | ||
156 | |||
157 | @ | ||
158 | @ Sound functions. | ||
159 | @ | ||
160 | |||
161 | @ MIDI key to frequency. | ||
162 | .text | ||
163 | .code 16 | ||
164 | .align 2 | ||
165 | .global bios_midi2freq | ||
166 | .thumb_func | ||
167 | bios_midi2freq: | ||
168 | swi 0x1f | ||
169 | bx lr | ||
170 | |||
171 | @ Sound bias. | ||
172 | .text | ||
173 | .code 16 | ||
174 | .align 2 | ||
175 | .global bios_sound_bias | ||
176 | .thumb_func | ||
177 | bios_sound_bias: | ||
178 | swi 0x19 | ||
179 | bx lr | ||
180 | |||
181 | @ Sound channels clear. | ||
182 | .text | ||
183 | .code 16 | ||
184 | .align 2 | ||
185 | .global bios_sound_clear | ||
186 | .thumb_func | ||
187 | bios_sound_clear: | ||
188 | swi 0x1e | ||
189 | bx lr | ||
190 | |||
191 | @ Sound driver initialization. | ||
192 | .text | ||
193 | .code 16 | ||
194 | .align 2 | ||
195 | .global bios_sound_init | ||
196 | .thumb_func | ||
197 | bios_sound_init: | ||
198 | swi 0x1a | ||
199 | bx lr | ||
200 | |||
201 | @ Sound main function. To be called each 1/60 of a second after the sound VSync. | ||
202 | .text | ||
203 | .code 16 | ||
204 | .align 2 | ||
205 | .global bios_sound_main | ||
206 | .thumb_func | ||
207 | bios_sound_main: | ||
208 | swi 0x1c | ||
209 | bx lr | ||
210 | |||
211 | @ Sound driver operation mode. | ||
212 | .text | ||
213 | .code 16 | ||
214 | .align 2 | ||
215 | .global bios_sound_mode | ||
216 | .thumb_func | ||
217 | bios_sound_mode: | ||
218 | swi 0x1b | ||
219 | bx lr | ||
220 | |||
221 | @ Sound VSync. Called just after the VBlank interrupt each 1/60 of a second. | ||
222 | .text | ||
223 | .code 16 | ||
224 | .align 2 | ||
225 | .global bios_sound_vsync | ||
226 | .thumb_func | ||
227 | bios_sound_vsync: | ||
228 | swi 0x1d | ||
229 | bx lr | ||
230 | |||
231 | @ Sound VSync off In case of issues manually call this to stop the sound. | ||
232 | .text | ||
233 | .code 16 | ||
234 | .align 2 | ||
235 | .global bios_sound_vsync_off | ||
236 | .thumb_func | ||
237 | bios_sound_vsync_off: | ||
238 | swi 0x28 | ||
239 | bx lr | ||
240 | |||
241 | @ | ||
242 | @ Halt/Reset functions. | ||
243 | @ | ||
244 | |||
245 | @ Halt until interrupt request. | ||
246 | .text | ||
247 | .code 16 | ||
248 | .align 2 | ||
249 | .global bios_halt | ||
250 | .thumb_func | ||
251 | bios_halt: | ||
252 | swi 0x02 | ||
253 | bx lr | ||
254 | |||
255 | @ Halt until one of the specified interrupts occur. | ||
256 | .text | ||
257 | .code 16 | ||
258 | .align 2 | ||
259 | .global bios_interrupt_wait | ||
260 | .thumb_func | ||
261 | bios_interrupt_wait: | ||
262 | swi 0x04 | ||
263 | bx lr | ||
264 | |||
265 | @ Halt until the VBlank interrupt occurs. | ||
266 | .text | ||
267 | .code 16 | ||
268 | .align 2 | ||
269 | .global bios_vblank_wait | ||
270 | .thumb_func | ||
271 | bios_vblank_wait: | ||
272 | swi 0x05 | ||
273 | bx lr | ||
274 | |||
275 | @ Stop. Switches GBA to low power mode. | ||
276 | .text | ||
277 | .code 16 | ||
278 | .align 2 | ||
279 | .global bios_stop | ||
280 | .thumb_func | ||
281 | bios_stop: | ||
282 | swi 0x03 | ||
283 | bx lr | ||
284 | |||
285 | @ Soft reset. | ||
286 | .text | ||
287 | .code 16 | ||
288 | .align 2 | ||
289 | .global bios_soft_reset | ||
290 | .thumb_func | ||
291 | bios_soft_reset: | ||
292 | swi 0x00 | ||
293 | bx lr | ||
294 | |||
295 | @ Register RAM reset. | ||
296 | .text | ||
297 | .code 16 | ||
298 | .align 2 | ||
299 | .global bios_regram_reset | ||
300 | .thumb_func | ||
301 | bios_regram_reset: | ||
302 | swi 0x01 | ||
303 | bx lr | ||
304 | |||
305 | @ Hard reset. | ||
306 | .text | ||
307 | .code 16 | ||
308 | .align 2 | ||
309 | .global bios_hard_reset | ||
310 | .thumb_func | ||
311 | bios_hard_reset: | ||
312 | swi 0x26 | ||
313 | bx lr | ||
314 | |||
315 | @ | ||
316 | @ Misc functions. | ||
317 | @ | ||
318 | |||
319 | @ BIOS checksum. | ||
320 | .text | ||
321 | .code 16 | ||
322 | .align 2 | ||
323 | .global bios_bios_checksum | ||
324 | .thumb_func | ||
325 | bios_bios_checksum: | ||
326 | swi 0x0d | ||
327 | bx lr | ||
328 | |||
329 | @ MultiBoot. | ||
330 | .text | ||
331 | .code 16 | ||
332 | .align 2 | ||
333 | .global bios_multiboot | ||
334 | .thumb_func | ||
335 | bios_multiboot: | ||
336 | swi 0x25 | ||
337 | bx lr | ||
diff --git a/src/bitmap.h b/src/bitmap.h new file mode 100644 index 0000000..2e61f0e --- /dev/null +++ b/src/bitmap.h | |||
@@ -0,0 +1,209 @@ | |||
1 | #ifndef GBAEXP_BITMAP_H | ||
2 | #define GBAEXP_BITMAP_H | ||
3 | |||
4 | #include "common.h" | ||
5 | |||
6 | // Draws a line with the given color between (x0,y0) and (x1,y1) using the | ||
7 | // Bresenham's line drawing algorithm using exclusively integer arithmetic. | ||
8 | static void | ||
9 | draw_line(int x0, int y0, int x1, int y1, Color clr) { | ||
10 | // Pointer to the initial position of the screen buffer where we will start | ||
11 | // writing our data. | ||
12 | vu16 *destination = (u16*)(SCREEN_BUFFER + y0 * SCREEN_WIDTH + x0); | ||
13 | |||
14 | // Adjust the step direction and calculate deltas. | ||
15 | int x_step; | ||
16 | int y_step; | ||
17 | int dx; | ||
18 | int dy; | ||
19 | if (x0 > x1) { | ||
20 | x_step = -1; | ||
21 | dx = x0 - x1; | ||
22 | } else { | ||
23 | x_step = 1; | ||
24 | dx = x1 - x0; | ||
25 | } | ||
26 | if (y0 > y1) { | ||
27 | y_step = -SCREEN_WIDTH; | ||
28 | dy = y0 - y1; | ||
29 | } else { | ||
30 | y_step = +SCREEN_WIDTH; | ||
31 | dy = y1 - y0; | ||
32 | } | ||
33 | |||
34 | if(dy == 0) { | ||
35 | // Horizontal line. | ||
36 | for(int i = 0; i <= dx; i++) { | ||
37 | destination[i * x_step] = clr; | ||
38 | } | ||
39 | } else if(dx == 0) { | ||
40 | // Vertical line. | ||
41 | for(int i = 0; i <= dy; i++) { | ||
42 | destination[i * y_step] = clr; | ||
43 | } | ||
44 | } else if (dx >= dy){ | ||
45 | // Positive slope. | ||
46 | int diff = 2 * dy - dx; | ||
47 | for (int i = 0; i <= dx; ++i) { | ||
48 | *destination = clr; | ||
49 | if (diff >= 0) { | ||
50 | destination += y_step; | ||
51 | diff -= 2 * dx; | ||
52 | } | ||
53 | destination += x_step; | ||
54 | diff += 2 * dy; | ||
55 | } | ||
56 | } else { | ||
57 | // Negative slope. | ||
58 | int diff = 2 * dx - dy; | ||
59 | for (int i = 0; i <= dy; ++i) { | ||
60 | *destination = clr; | ||
61 | if (diff >= 0) { | ||
62 | destination += x_step; | ||
63 | diff -= 2 * dy; | ||
64 | } | ||
65 | destination += y_step; | ||
66 | diff += 2 * dx; | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static inline void | ||
72 | draw_rect(int x0, int y0, int x1, int y1, Color clr) { | ||
73 | if (x0 > x1) { | ||
74 | int tmp = x0; | ||
75 | x0 = x1; | ||
76 | x1 = tmp; | ||
77 | } | ||
78 | if (y0 > y1) { | ||
79 | int tmp = y0; | ||
80 | y0 = y1; | ||
81 | y1 = tmp; | ||
82 | } | ||
83 | int dx = x1 - x0; | ||
84 | int dy = y1 - y0; | ||
85 | for (int i = 0; i <= dx; ++i) { | ||
86 | int x = x0 + i; | ||
87 | FRAMEBUFFER[y0][x] = clr; | ||
88 | FRAMEBUFFER[y1][x] = clr; | ||
89 | } | ||
90 | for (int j = 0; j <= dy; ++j) { | ||
91 | int y = y0 + j; | ||
92 | FRAMEBUFFER[y][x0] = clr; | ||
93 | FRAMEBUFFER[y][x1] = clr; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | static inline void | ||
98 | draw_fill_rect(int x0, int y0, int x1, int y1, Color clr) { | ||
99 | if (x0 > x1) { | ||
100 | int tmp = x0; | ||
101 | x0 = x1; | ||
102 | x1 = tmp; | ||
103 | } | ||
104 | if (y0 > y1) { | ||
105 | int tmp = y0; | ||
106 | y0 = y1; | ||
107 | y1 = tmp; | ||
108 | } | ||
109 | int dx = x1 - x0; | ||
110 | int dy = y1 - y0; | ||
111 | for (int i = 0; i <= dx; ++i) { | ||
112 | for (int j = 0; j <= dy; ++j) { | ||
113 | int x = x0 + i; | ||
114 | int y = y0 + j; | ||
115 | FRAMEBUFFER[y][x] = clr; | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | // In Mode4 the buffer is of 8 bytes per pixel instead of 16. We can't write the | ||
121 | // color directly, instead the color is stored in the palette memory at | ||
122 | // `MEM_PAL`. Note that in this mode MEM_PAL[0] is the background color. This | ||
123 | // plotter takes an index to a color stored in MEM_PAL[col_index]. Because the | ||
124 | // GBA needs to meet memory alignment requirements, we can't write a u8 into | ||
125 | // memory, instead we need to read a u16 word, mask and or the corresponding | ||
126 | // bits and wave the updated u16. | ||
127 | static inline void | ||
128 | put_pixel_m4(int x, int y, u8 clr_idx, vu16 *buffer) { | ||
129 | int buffer_index = (y * SCREEN_WIDTH + x) / 2; | ||
130 | vu16 *destination = &buffer[buffer_index]; | ||
131 | // Odd pixels will go to the top 8 bits of the destination. Even pixels to | ||
132 | // the lower 8 bits. | ||
133 | int odd = x & 0x1; | ||
134 | if(odd) { | ||
135 | *destination= (*destination & 0xFF) | (clr_idx << 8); | ||
136 | } else { | ||
137 | *destination= (*destination & ~0xFF) | clr_idx; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static inline void | ||
142 | put_pixel_m3(int x, int y, u16 color, Scanline *buffer) { | ||
143 | buffer[y][x] = color; | ||
144 | } | ||
145 | |||
146 | static inline void | ||
147 | clear_screen_m4() { | ||
148 | size_t size = SCREEN_WIDTH * SCREEN_HEIGHT / 4; | ||
149 | u32 *buf = backbuffer; | ||
150 | for (size_t i = 0; i < size; ++i) { | ||
151 | buf[i] = 0; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | static inline void | ||
156 | clear_screen_m3() { | ||
157 | size_t size = SCREEN_WIDTH * SCREEN_HEIGHT / 4; | ||
158 | u32 *buf = FRAMEBUFFER; | ||
159 | for (size_t i = 0; i < size; ++i) { | ||
160 | buf[i] = 0; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | static inline void | ||
165 | draw_fill_rect_m4(int x0, int y0, int x1, int y1, u8 col_index, vu16 *buffer) { | ||
166 | int ix, iy; | ||
167 | for(iy = y0; iy < y1; iy++) { | ||
168 | for(ix = x0; ix < x1; ix++) { | ||
169 | put_pixel_m4(ix, iy, col_index, buffer); | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | void | ||
175 | draw_logo(void) { | ||
176 | int side = 60; | ||
177 | int line = 35; | ||
178 | int height = side * 0.5; | ||
179 | int x = SCREEN_WIDTH / 2 - height / 2; | ||
180 | int y = SCREEN_HEIGHT / 2; | ||
181 | |||
182 | // Draw red triangle. | ||
183 | draw_line(x + height - 1, y - side / 2, x, y - 1, COLOR_RED); | ||
184 | draw_line(x + height - 1, y + side / 2, x, y + 1, COLOR_RED); | ||
185 | draw_line(x + height - 1, y - side / 2 + 1, x, y, COLOR_RED); | ||
186 | draw_line(x + height - 1, y + side / 2 - 1, x, y, COLOR_RED); | ||
187 | |||
188 | // Draw white triangle. | ||
189 | draw_line(x, y - side / 2, x, y + side / 2, COLOR_WHITE); | ||
190 | draw_line(x + 1, y - side / 2, x + height, y - 1, COLOR_WHITE); | ||
191 | draw_line(x + 1, y + side / 2, x + height, y + 1, COLOR_WHITE); | ||
192 | |||
193 | // Draw white line at triangle tip. | ||
194 | draw_line(x + height, y - side / 2, x + height, y + side / 2, COLOR_WHITE); | ||
195 | draw_line(x + height + 1, y - side / 2, x + height + 1, y + side / 2, COLOR_WHITE); | ||
196 | |||
197 | // Double triangle line. | ||
198 | draw_line(x - 1, y - side / 2, x - 1, y + side / 2, COLOR_WHITE); | ||
199 | draw_line(x + 1, y - side / 2 + 1, x + height, y, COLOR_WHITE); | ||
200 | draw_line(x + 1, y + side / 2 - 1, x + height, y, COLOR_WHITE); | ||
201 | |||
202 | // Draw white lines. | ||
203 | draw_line(x - line, y, x, y, COLOR_WHITE); | ||
204 | draw_line(x + height, y, x + height + line, y, COLOR_WHITE); | ||
205 | draw_line(x - line, y + 1, x, y + 1, COLOR_WHITE); | ||
206 | draw_line(x + height, y + 1, x + height + line, y + 1, COLOR_WHITE); | ||
207 | } | ||
208 | |||
209 | #endif // GBAEXP_BITMAP_H | ||
diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..aeea1a6 --- /dev/null +++ b/src/common.h | |||
@@ -0,0 +1,696 @@ | |||
1 | #ifndef GBAEXP_COMMON_H | ||
2 | #define GBAEXP_COMMON_H | ||
3 | |||
4 | #include "shorthand.h" | ||
5 | |||
6 | // | ||
7 | // Memory sections. | ||
8 | // | ||
9 | |||
10 | // Defines for the different memory sections in the GBA. | ||
11 | #define MEM_SROM 0x00000000 | ||
12 | #define MEM_EW 0x02000000 | ||
13 | #define MEM_IW 0x03000000 | ||
14 | #define MEM_IO 0x04000000 | ||
15 | #define MEM_PAL 0x05000000 | ||
16 | #define MEM_VRAM 0x06000000 | ||
17 | #define MEM_OAM 0x07000000 | ||
18 | #define MEM_PAK 0x08000000 | ||
19 | #define MEM_CART 0x0E000000 | ||
20 | |||
21 | // | ||
22 | // Display modes. | ||
23 | // | ||
24 | |||
25 | // Display registers. | ||
26 | #define DISP_CTRL *((vu32*)(MEM_IO + 0x0000)) | ||
27 | #define DISP_STATUS *((vu16*)(MEM_IO + 0x0004)) | ||
28 | #define DISP_VCOUNT *((vu16*)(MEM_IO + 0x0006)) | ||
29 | |||
30 | // The first three bits in the DISP_CTRL are used to control the video mode. | ||
31 | #define DISP_MODE_0 0x0000 | ||
32 | #define DISP_MODE_1 0x0001 | ||
33 | #define DISP_MODE_2 0x0002 | ||
34 | #define DISP_MODE_3 0x0003 | ||
35 | #define DISP_MODE_4 0x0004 | ||
36 | #define DISP_MODE_5 0x0005 | ||
37 | #define DISP_GB (1 << 3) | ||
38 | #define DISP_PAGE (1 << 4) | ||
39 | #define DISP_OAM_HBLANK (1 << 5) | ||
40 | #define DISP_OBJ_1D (1 << 6) | ||
41 | #define DISP_BLANK (1 << 7) | ||
42 | #define DISP_BG_0 (1 << 8) | ||
43 | #define DISP_BG_1 (1 << 9) | ||
44 | #define DISP_BG_2 (1 << 10) | ||
45 | #define DISP_BG_3 (1 << 11) | ||
46 | #define DISP_OBJ (1 << 12) | ||
47 | #define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D | ||
48 | |||
49 | // These bits are used to control the DISP_STATUS register. | ||
50 | #define DISP_VBLANK_STATUS (1 << 0x0) | ||
51 | #define DISP_HBLANK_STATUS (1 << 0x1) | ||
52 | #define DISP_VCOUNT_STATUS (1 << 0x2) | ||
53 | #define DISP_VBLANK_IRQ (1 << 0x3) | ||
54 | #define DISP_HBLANK_IRQ (1 << 0x4) | ||
55 | #define DISP_VCOUNT_IRQ (1 << 0x5) | ||
56 | #define DISP_VCOUNT_TRIGGER(N) ((N) << 0x8) | ||
57 | |||
58 | // Registers to control of BG layers. | ||
59 | #define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) | ||
60 | |||
61 | // Bits to control the background. | ||
62 | #define BG_PRIORITY(N) ((N) & 0x3) | ||
63 | #define BG_CHARBLOCK(N) ((N) << 2) | ||
64 | #define BG_MOSAIC (1 << 6) | ||
65 | #define BG_HIGH_COLOR (1 << 7) | ||
66 | #define BG_SCREENBLOCK(N) ((N) << 8) | ||
67 | #define BG_AFFINE (1 << 0xD) | ||
68 | #define BG_SIZE(N) ((N) << 0xE) | ||
69 | |||
70 | // BG registers for horizontal displacement. | ||
71 | #define BG_H_SCROLL_0 *((vu16*)(0x04000010 + 0x0004 * 0)) | ||
72 | #define BG_H_SCROLL_1 *((vu16*)(0x04000010 + 0x0004 * 1)) | ||
73 | #define BG_H_SCROLL_2 *((vu16*)(0x04000010 + 0x0004 * 2)) | ||
74 | #define BG_H_SCROLL_3 *((vu16*)(0x04000010 + 0x0004 * 3)) | ||
75 | |||
76 | // BG registers for vertical displacement. | ||
77 | #define BG_V_SCROLL_0 *((vu16*)(0x04000012 + 0x0004 * 0)) | ||
78 | #define BG_V_SCROLL_1 *((vu16*)(0x04000012 + 0x0004 * 1)) | ||
79 | #define BG_V_SCROLL_2 *((vu16*)(0x04000012 + 0x0004 * 2)) | ||
80 | #define BG_V_SCROLL_3 *((vu16*)(0x04000012 + 0x0004 * 3)) | ||
81 | |||
82 | // Screen settings. | ||
83 | #define SCREEN_WIDTH 240 | ||
84 | #define SCREEN_HEIGHT 160 | ||
85 | |||
86 | // | ||
87 | // Colors. | ||
88 | // | ||
89 | |||
90 | // The GBA in mode 3 expects rbg15 colors in the VRAM, where each component | ||
91 | // (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0). | ||
92 | typedef u16 Color; | ||
93 | |||
94 | // A palette is composed of 16 colors, with color at index 0 being transparent | ||
95 | // for sprites. | ||
96 | typedef Color Palette[16]; | ||
97 | |||
98 | // Inline function to calculate the 15 bit color value. | ||
99 | static inline Color | ||
100 | rgb15(u32 red, u32 green, u32 blue ) { | ||
101 | return (blue << 10) | (green << 5) | red; | ||
102 | } | ||
103 | |||
104 | // Some nice default colors. | ||
105 | #define COLOR_RED rgb15(31, 0, 12) | ||
106 | #define COLOR_BLUE rgb15(2, 15, 30) | ||
107 | #define COLOR_CYAN rgb15(0, 30, 30) | ||
108 | #define COLOR_GREY rgb15(12, 12, 12) | ||
109 | #define COLOR_BLACK rgb15(0, 0, 0) | ||
110 | #define COLOR_WHITE rgb15(28, 28, 28) | ||
111 | |||
112 | // | ||
113 | // Tile memory access. | ||
114 | // | ||
115 | |||
116 | // NOTE: Only defining 4bpp tiles for now. | ||
117 | typedef struct Tile { | ||
118 | u32 row[8]; | ||
119 | } Tile; | ||
120 | |||
121 | // Screenblocks and charblocks (tile blocks). | ||
122 | typedef Tile TileBlock[512]; | ||
123 | #define TILE_MEM ((TileBlock*) MEM_VRAM) | ||
124 | typedef u16 ScreenBlock[1024]; | ||
125 | #define SCREENBLOCK_MEM ((ScreenBlock*)MEM_VRAM) | ||
126 | |||
127 | // Screenblock entry bits. | ||
128 | #define SCREENBLOCK_ENTRY_H_FLIP (1 << 0xA) | ||
129 | #define SCREENBLOCK_ENTRY_V_FLIP (1 << 0xB) | ||
130 | #define SCREENBLOCK_ENTRY_PAL(N) ((N) << 0xC) | ||
131 | |||
132 | size_t | ||
133 | se_index(size_t tile_x, size_t tile_y, size_t map_width) { | ||
134 | size_t sbb = ((tile_x >> 5) + (tile_y >> 5) * (map_width >> 5)); | ||
135 | return sbb * 1024 + ((tile_x & 31) + (tile_y & 31) * 32); | ||
136 | } | ||
137 | |||
138 | // We can treat the screen as a HxW matrix. With the following macro we can | ||
139 | // write a pixel to the screen at the (x, y) position using: | ||
140 | // | ||
141 | // FRAMEBUFFER[y][x] = color; | ||
142 | // | ||
143 | typedef Color Scanline[SCREEN_WIDTH]; | ||
144 | #define FRAMEBUFFER ((Scanline*) MEM_VRAM) | ||
145 | #define SCREEN_BUFFER ((u16*) MEM_VRAM) | ||
146 | #define PAL_BUFFER_BG ((u16*) MEM_PAL) | ||
147 | #define PAL_BUFFER_SPRITES ((u16*)(MEM_PAL + 0x200)) | ||
148 | #define PAL_BANK_BG ((Palette*) MEM_PAL) | ||
149 | #define PAL_BANK_SPRITES ((Palette*)(MEM_PAL + 0x200)) | ||
150 | static vu16 *backbuffer = ((vu16*)(MEM_VRAM + 0x0A000)); | ||
151 | |||
152 | // | ||
153 | // Sprites. | ||
154 | // | ||
155 | |||
156 | // Using macros instead of aligned structs for setting up OBJ attributes and | ||
157 | // affine parameters. | ||
158 | // TODO: Benchmark if this would be slower or the same that TONC's | ||
159 | // implementation. | ||
160 | // TODO: Cleanup OBJ/OAM memory copying and access. | ||
161 | #define OBJ_ATTR_0(N) *((vu16*)(MEM_OAM + 0 + 8 * (N))) | ||
162 | #define OBJ_ATTR_1(N) *((vu16*)(MEM_OAM + 2 + 8 * (N))) | ||
163 | #define OBJ_ATTR_2(N) *((vu16*)(MEM_OAM + 4 + 8 * (N))) | ||
164 | #define OBJ_AFFINE_PA(N) *((vs16*)(MEM_OAM + 6 + 8 * 0 + 8 * 4 * (N))) | ||
165 | #define OBJ_AFFINE_PB(N) *((vs16*)(MEM_OAM + 6 + 8 * 1 + 8 * 4 * (N))) | ||
166 | #define OBJ_AFFINE_PC(N) *((vs16*)(MEM_OAM + 6 + 8 * 2 + 8 * 4 * (N))) | ||
167 | #define OBJ_AFFINE_PD(N) *((vs16*)(MEM_OAM + 6 + 8 * 3 + 8 * 4 * (N))) | ||
168 | |||
169 | // OBJ_ATTR_0 parameters | ||
170 | #define OBJ_Y_COORD(N) ((N) & 0xFF) | ||
171 | #define OBJ_NORMAL (0x00 << 0x8) | ||
172 | #define OBJ_AFFINE (0x01 << 0x8) | ||
173 | #define OBJ_HIDDEN (0x02 << 0x8) | ||
174 | #define OBJ_AFFINE_2X (0x03 << 0x8) | ||
175 | #define OBJ_ALPHA_BLEND (0x01 << 0xA) | ||
176 | #define OBJ_WINDOW (0x02 << 0xA) | ||
177 | #define OBJ_SHAPE_SQUARE (0x00 << 0xE) | ||
178 | #define OBJ_SHAPE_WIDE (0x01 << 0xE) | ||
179 | #define OBJ_SHAPE_TALL (0x02 << 0xE) | ||
180 | |||
181 | // OBJ_ATTR_1 parameters | ||
182 | #define OBJ_X_COORD(N) ((N) & 0x1FF) | ||
183 | #define OBJ_AFFINE_IDX(N) ((N) << 0x9) | ||
184 | #define OBJ_H_FLIP (0x01 << 0xC) | ||
185 | #define OBJ_V_FLIP (0x01 << 0xD) | ||
186 | #define OBJ_SIZE_SMALL (0x00 << 0xE) | ||
187 | #define OBJ_SIZE_MID (0x01 << 0xE) | ||
188 | #define OBJ_SIZE_BIG (0x02 << 0xE) | ||
189 | #define OBJ_SIZE_HUGE (0x03 << 0xE) | ||
190 | |||
191 | // OBJ_ATTR_2 parameters | ||
192 | #define OBJ_TILE_INDEX(N) ((N) & 0x3FF) | ||
193 | #define OBJ_PRIORITY(N) ((N) << 0xA) | ||
194 | #define OBJ_PAL_BANK(N) ((N) << 0xC) | ||
195 | |||
196 | // | ||
197 | // Mode 4 page flipping | ||
198 | // | ||
199 | |||
200 | static inline void | ||
201 | flip_page(void) { | ||
202 | backbuffer = (u16*)((u32)backbuffer ^ 0x0A000); | ||
203 | DISP_CTRL ^= DISP_PAGE; | ||
204 | } | ||
205 | |||
206 | #define SCREEN_PAGE_1 ((vu16*) MEM_VRAM) | ||
207 | #define SCREEN_PAGE_2 ((vu16*) (MEM_VRAM + 0xa000)) | ||
208 | |||
209 | // | ||
210 | // Profiling. | ||
211 | // | ||
212 | |||
213 | #define TIMER_DATA_0 *((vu16*) (0x04000100 + 0x04 * 0)) | ||
214 | #define TIMER_DATA_1 *((vu16*) (0x04000100 + 0x04 * 1)) | ||
215 | #define TIMER_DATA_2 *((vu16*) (0x04000100 + 0x04 * 2)) | ||
216 | #define TIMER_DATA_3 *((vu16*) (0x04000100 + 0x04 * 3)) | ||
217 | #define TIMER_CTRL_0 *((vu16*) (0x04000102 + 0x04 * 0)) | ||
218 | #define TIMER_CTRL_1 *((vu16*) (0x04000102 + 0x04 * 1)) | ||
219 | #define TIMER_CTRL_2 *((vu16*) (0x04000102 + 0x04 * 2)) | ||
220 | #define TIMER_CTRL_3 *((vu16*) (0x04000102 + 0x04 * 3)) | ||
221 | |||
222 | // Timer control bits. | ||
223 | #define TIMER_CTRL_FREQ_0 0 | ||
224 | #define TIMER_CTRL_FREQ_1 1 | ||
225 | #define TIMER_CTRL_FREQ_2 2 | ||
226 | #define TIMER_CTRL_FREQ_3 3 | ||
227 | #define TIMER_CTRL_CASCADE (1 << 2) | ||
228 | #define TIMER_CTRL_IRQ (1 << 6) | ||
229 | #define TIMER_CTRL_ENABLE (1 << 7) | ||
230 | #define TIMER_CTRL_DISABLE (0 << 7) | ||
231 | |||
232 | // We use timers 2 and 3 to count the number of cycles since the profile_start | ||
233 | // functions is called. Don't use if the code we are trying to profile make use | ||
234 | // of these timers. | ||
235 | static inline | ||
236 | void profile_start(void) { | ||
237 | TIMER_DATA_2 = 0; | ||
238 | TIMER_DATA_3 = 0; | ||
239 | TIMER_CTRL_2 = 0; | ||
240 | TIMER_CTRL_3 = 0; | ||
241 | TIMER_CTRL_3 = TIMER_CTRL_ENABLE | TIMER_CTRL_CASCADE; | ||
242 | TIMER_CTRL_2 = TIMER_CTRL_ENABLE; | ||
243 | } | ||
244 | |||
245 | static inline | ||
246 | u32 profile_stop(void) { | ||
247 | TIMER_CTRL_2 = 0; | ||
248 | return (TIMER_DATA_3 << 16) | TIMER_DATA_2; | ||
249 | } | ||
250 | |||
251 | // | ||
252 | // Input handling. | ||
253 | // | ||
254 | |||
255 | // Memory address for key input and control register | ||
256 | #define KEY_INPUTS *((vu16*) 0x04000130) | ||
257 | #define KEY_CTRL *((vu16*) 0x04000132) | ||
258 | |||
259 | // Key control register bits. | ||
260 | #define KEY_IRQ_KEY(N) (N) | ||
261 | #define KEY_IRQ (1 << 0xE) | ||
262 | #define KEY_IRQ_IF_SET (1 << 0xF) | ||
263 | |||
264 | // Alias for key pressing bits. | ||
265 | #define KEY_A (1 << 0) | ||
266 | #define KEY_B (1 << 1) | ||
267 | #define KEY_SELECT (1 << 2) | ||
268 | #define KEY_START (1 << 3) | ||
269 | #define KEY_RIGHT (1 << 4) | ||
270 | #define KEY_LEFT (1 << 5) | ||
271 | #define KEY_UP (1 << 6) | ||
272 | #define KEY_DOWN (1 << 7) | ||
273 | #define KEY_R (1 << 8) | ||
274 | #define KEY_L (1 << 9) | ||
275 | |||
276 | #define KEY_MASK 0x03FF | ||
277 | |||
278 | // Saving the previous and current key states as globals for now. | ||
279 | static u16 key_curr = 0; | ||
280 | static u16 key_prev = 0; | ||
281 | |||
282 | static inline void | ||
283 | poll_keys(void) { | ||
284 | key_prev = key_curr; | ||
285 | key_curr = ~KEY_INPUTS & KEY_MASK; | ||
286 | } | ||
287 | |||
288 | // Returns true if the given key has been pressed at time of calling and was not | ||
289 | // pressed since the previous call. For example, if a key is being held, this | ||
290 | // function will return `true` only on the frame where the key initially | ||
291 | // activated. | ||
292 | static inline u32 | ||
293 | key_pressed(u32 key) { | ||
294 | return (key_curr & key) & ~(key_prev & key); | ||
295 | } | ||
296 | |||
297 | // Check if the given key is pressed and has been since at least one frame. | ||
298 | static inline u32 | ||
299 | key_hold(u32 key) { | ||
300 | return (key_curr & key) & key_prev & key; | ||
301 | } | ||
302 | |||
303 | // Check if the given key/button is currently pressed. | ||
304 | #define KEY_PRESSED(key) (~(KEY_INPUTS) & key) | ||
305 | |||
306 | // Back/unpack bits. | ||
307 | static inline u32 | ||
308 | unpack_1bb(u8 hex) { | ||
309 | const u32 conversion_u32[16] = { | ||
310 | 0x00000000, 0x00000001, 0x00000010, 0x00000011, | ||
311 | 0x00000100, 0x00000101, 0x00000110, 0x00000111, | ||
312 | 0x00001000, 0x00001001, 0x00001010, 0x00001011, | ||
313 | 0x00001100, 0x00001101, 0x00001110, 0x00001111, | ||
314 | }; | ||
315 | u8 low = hex & 0xF; | ||
316 | u8 high = (hex >> 4) & 0xF; | ||
317 | return (conversion_u32[high] << 16) | conversion_u32[low]; | ||
318 | } | ||
319 | |||
320 | // Unpack N tiles packed at 1bpp. | ||
321 | static inline void | ||
322 | unpack_tiles(u32 *src, u32 *dst, size_t n_tiles) { | ||
323 | u32 *target_src = src + n_tiles * 2; | ||
324 | while (src != target_src) { | ||
325 | *dst++ = unpack_1bb((*src >> 24) & 0xFF); | ||
326 | *dst++ = unpack_1bb((*src >> 16) & 0xFF); | ||
327 | *dst++ = unpack_1bb((*src >> 8) & 0xFF); | ||
328 | *dst++ = unpack_1bb(*src & 0xFF); | ||
329 | src++; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | // | ||
334 | // Direct Memory Access (DMA) | ||
335 | // | ||
336 | |||
337 | |||
338 | // Source, destination, and control registers. | ||
339 | #define DMA_SRC(N) *((vu32*) 0x040000B0 + (N) * 12) | ||
340 | #define DMA_DST(N) *((vu32*) 0x040000B4 + (N) * 12) | ||
341 | #define DMA_CTRL(N) *((vu32*) 0x040000B8 + (N) * 12) | ||
342 | |||
343 | // DMA control bits. | ||
344 | #define DMA_DST_INC (0 << 0x15) | ||
345 | #define DMA_DST_DEC (1 << 0x15) | ||
346 | #define DMA_DST_FIXED (2 << 0x15) | ||
347 | #define DMA_DST_RELOAD (3 << 0x15) | ||
348 | #define DMA_SRC_INC (0 << 0x17) | ||
349 | #define DMA_SRC_DEC (1 << 0x17) | ||
350 | #define DMA_SRC_FIXED (2 << 0x17) | ||
351 | #define DMA_REPEAT (1 << 0x19) | ||
352 | #define DMA_CHUNK_16 (0 << 0x1A) | ||
353 | #define DMA_CHUNK_32 (1 << 0x1A) | ||
354 | #define DMA_NOW (0 << 0x1C) | ||
355 | #define DMA_VBLANK (1 << 0x1C) | ||
356 | #define DMA_HBLANK (2 << 0x1C) | ||
357 | #define DMA_REFRESH (3 << 0x1C) | ||
358 | #define DMA_IRQ (1 << 0x1E) | ||
359 | #define DMA_ENABLE (1 << 0x1F) | ||
360 | |||
361 | // Custom struct for cleaner DMA transfer functions. | ||
362 | typedef struct DmaStr { | ||
363 | const void *src; | ||
364 | void *dst; | ||
365 | u32 ctrl; | ||
366 | } DmaStr; | ||
367 | |||
368 | #define DMA_TRANSFER ((volatile DmaStr*) 0x040000B0) | ||
369 | |||
370 | // Transfer `count` number of chunks from src to dst using a DMA channel. Note | ||
371 | // that chunks are not bytes, but instead configured based on bits set by | ||
372 | // DMA_CTRL. | ||
373 | inline void | ||
374 | dma_transfer_copy(void *dst, const void *src, u32 count, int channel, u32 options) { | ||
375 | DMA_TRANSFER[channel].ctrl = 0; | ||
376 | DMA_TRANSFER[channel].src = src; | ||
377 | DMA_TRANSFER[channel].dst = dst; | ||
378 | DMA_TRANSFER[channel].ctrl = count | options; | ||
379 | } | ||
380 | |||
381 | inline void | ||
382 | dma_transfer_fill(void *dst, volatile u32 src, u32 count, int channel, u32 options) { | ||
383 | DMA_TRANSFER[channel].ctrl = 0; | ||
384 | DMA_TRANSFER[channel].src = (const void *)&src; | ||
385 | DMA_TRANSFER[channel].dst = dst; | ||
386 | DMA_TRANSFER[channel].ctrl = count | options | DMA_SRC_FIXED; | ||
387 | } | ||
388 | |||
389 | // Copy N number of bytes using a DMA channel. | ||
390 | inline void | ||
391 | dma_copy(void *dst, const void *src, u32 size, int channel) { | ||
392 | dma_transfer_copy(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); | ||
393 | } | ||
394 | |||
395 | // Fill the dst location with the word set at src. | ||
396 | inline void | ||
397 | dma_fill(void *dst, const void *src, u32 size, int channel) { | ||
398 | dma_transfer_fill(dst, (volatile u32)src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); | ||
399 | } | ||
400 | |||
401 | // | ||
402 | // Interrupts. | ||
403 | // | ||
404 | |||
405 | #define IRQ_ENABLE *((vu16*) 0x04000200) | ||
406 | #define IRQ_ACK *((vu16*) 0x04000202) | ||
407 | #define IRQ_CTRL *((vu16*) 0x04000208) | ||
408 | #define IRQ_ACK_BIOS *((vu16*) 0x03007FF8) | ||
409 | |||
410 | typedef enum { | ||
411 | IRQ_VBLANK, | ||
412 | IRQ_HBLANK, | ||
413 | IRQ_VCOUNT, | ||
414 | IRQ_TIMER_0, | ||
415 | IRQ_TIMER_1, | ||
416 | IRQ_TIMER_2, | ||
417 | IRQ_TIMER_3, | ||
418 | IRQ_SERIAL, | ||
419 | IRQ_DMA_0, | ||
420 | IRQ_DMA_1, | ||
421 | IRQ_DMA_2, | ||
422 | IRQ_DMA_3, | ||
423 | IRQ_KEYPAD, | ||
424 | IRQ_GAMEPAK, | ||
425 | } IrqIndex; | ||
426 | |||
427 | typedef void (*IrsFunc)(void); | ||
428 | |||
429 | IrsFunc irs_table[] = { | ||
430 | [IRQ_VBLANK ] = NULL, | ||
431 | [IRQ_HBLANK ] = NULL, | ||
432 | [IRQ_VCOUNT ] = NULL, | ||
433 | [IRQ_TIMER_0] = NULL, | ||
434 | [IRQ_TIMER_1] = NULL, | ||
435 | [IRQ_TIMER_2] = NULL, | ||
436 | [IRQ_TIMER_3] = NULL, | ||
437 | [IRQ_SERIAL ] = NULL, | ||
438 | [IRQ_DMA_0 ] = NULL, | ||
439 | [IRQ_DMA_1 ] = NULL, | ||
440 | [IRQ_DMA_2 ] = NULL, | ||
441 | [IRQ_DMA_3 ] = NULL, | ||
442 | [IRQ_KEYPAD ] = NULL, | ||
443 | [IRQ_GAMEPAK] = NULL, | ||
444 | }; | ||
445 | |||
446 | void | ||
447 | irq_enable(IrqIndex idx) { | ||
448 | switch (idx) { | ||
449 | case IRQ_VBLANK: { DISP_STATUS |= DISP_VBLANK_IRQ; } break; | ||
450 | case IRQ_HBLANK: { DISP_STATUS |= DISP_HBLANK_IRQ; } break; | ||
451 | case IRQ_VCOUNT: { DISP_STATUS |= DISP_VCOUNT_IRQ; } break; | ||
452 | case IRQ_TIMER_0: { TIMER_CTRL_0 |= TIMER_CTRL_IRQ; } break; | ||
453 | case IRQ_TIMER_1: { TIMER_CTRL_1 |= TIMER_CTRL_IRQ; } break; | ||
454 | case IRQ_TIMER_2: { TIMER_CTRL_2 |= TIMER_CTRL_IRQ; } break; | ||
455 | case IRQ_TIMER_3: { TIMER_CTRL_3 |= TIMER_CTRL_IRQ; } break; | ||
456 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
457 | case IRQ_DMA_0: { DMA_CTRL(0) |= DMA_IRQ; } break; | ||
458 | case IRQ_DMA_1: { DMA_CTRL(1) |= DMA_IRQ; } break; | ||
459 | case IRQ_DMA_2: { DMA_CTRL(2) |= DMA_IRQ; } break; | ||
460 | case IRQ_DMA_3: { DMA_CTRL(3) |= DMA_IRQ; } break; | ||
461 | case IRQ_KEYPAD: { KEY_CTRL |= KEY_IRQ; } break; | ||
462 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
463 | } | ||
464 | IRQ_ENABLE |= (1 << idx); | ||
465 | } | ||
466 | |||
467 | void | ||
468 | irq_disable(IrqIndex idx) { | ||
469 | switch (idx) { | ||
470 | case IRQ_VBLANK: { DISP_STATUS &= ~DISP_VBLANK_IRQ; } break; | ||
471 | case IRQ_HBLANK: { DISP_STATUS &= ~DISP_HBLANK_IRQ; } break; | ||
472 | case IRQ_VCOUNT: { DISP_STATUS &= ~DISP_VCOUNT_IRQ; } break; | ||
473 | case IRQ_TIMER_0: { TIMER_CTRL_0 &= ~TIMER_CTRL_IRQ; } break; | ||
474 | case IRQ_TIMER_1: { TIMER_CTRL_1 &= ~TIMER_CTRL_IRQ; } break; | ||
475 | case IRQ_TIMER_2: { TIMER_CTRL_2 &= ~TIMER_CTRL_IRQ; } break; | ||
476 | case IRQ_TIMER_3: { TIMER_CTRL_3 &= ~TIMER_CTRL_IRQ; } break; | ||
477 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
478 | case IRQ_DMA_0: { DMA_CTRL(0) &= ~DMA_IRQ; } break; | ||
479 | case IRQ_DMA_1: { DMA_CTRL(1) &= ~DMA_IRQ; } break; | ||
480 | case IRQ_DMA_2: { DMA_CTRL(2) &= ~DMA_IRQ; } break; | ||
481 | case IRQ_DMA_3: { DMA_CTRL(3) &= ~DMA_IRQ; } break; | ||
482 | case IRQ_KEYPAD: { KEY_CTRL &= ~KEY_IRQ; } break; | ||
483 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
484 | } | ||
485 | IRQ_ENABLE &= ~(1 << idx); | ||
486 | } | ||
487 | |||
488 | void | ||
489 | irs_set(IrqIndex idx, IrsFunc func) { | ||
490 | // Store IRQ_CTRL status and disable interrupts for now. | ||
491 | u16 irq_ctrl = IRQ_CTRL; | ||
492 | IRQ_CTRL = 0; | ||
493 | |||
494 | // Update the IRS table and enable/disable the given IRQ. | ||
495 | irs_table[idx] = func; | ||
496 | if (func == NULL) { | ||
497 | irq_disable(idx); | ||
498 | } else { | ||
499 | irq_enable(idx); | ||
500 | } | ||
501 | |||
502 | // Restore previous irq_ctrl. | ||
503 | IRQ_CTRL = irq_ctrl; | ||
504 | } | ||
505 | |||
506 | // External irs_main function, has to be written in ARM assembly. | ||
507 | void irs_main(void); | ||
508 | |||
509 | // Initialize the function pointer for the main IRS routine written in ARM | ||
510 | // assembly and enable interrupts. | ||
511 | #define IRS_MAIN *(IrsFunc*)(0x03007FFC) | ||
512 | void | ||
513 | irq_init() { | ||
514 | IRS_MAIN = irs_main; | ||
515 | IRQ_CTRL = 1; | ||
516 | } | ||
517 | |||
518 | // Stub function pointer needed for when we want to enable interrupts that don't | ||
519 | // require a custom function, such as for the BIOS VSync. | ||
520 | void | ||
521 | irs_stub() {} | ||
522 | |||
523 | // | ||
524 | // BIOS function declarations. | ||
525 | // | ||
526 | |||
527 | // These functions declarations can be used to call the BIOS functions from the | ||
528 | // asm code. | ||
529 | int bios_vblank_wait(); | ||
530 | int bios_div(int num, int denom); | ||
531 | |||
532 | // | ||
533 | // Sound. | ||
534 | // | ||
535 | |||
536 | // Sound registers. | ||
537 | #define SOUND_SQUARE1_SWEEP *((vu16*)(MEM_IO + 0x60)) | ||
538 | #define SOUND_SQUARE1_CTRL *((vu16*)(MEM_IO + 0x62)) | ||
539 | #define SOUND_SQUARE1_FREQ *((vu16*)(MEM_IO + 0x64)) | ||
540 | #define SOUND_SQUARE2_CTRL *((vu16*)(MEM_IO + 0x68)) | ||
541 | #define SOUND_SQUARE2_FREQ *((vu16*)(MEM_IO + 0x6C)) | ||
542 | #define SOUND_WAVE_MODE *((vu16*)(MEM_IO + 0x70)) | ||
543 | #define SOUND_WAVE_CTRL *((vu16*)(MEM_IO + 0x72)) | ||
544 | #define SOUND_WAVE_FREQ *((vu16*)(MEM_IO + 0x74)) | ||
545 | #define SOUND_NOISE_CTRL *((vu16*)(MEM_IO + 0x78)) | ||
546 | #define SOUND_NOISE_FREQ *((vu16*)(MEM_IO + 0x7C)) | ||
547 | #define SOUND_DMG_MASTER *((vu16*)(MEM_IO + 0x80)) | ||
548 | #define SOUND_DSOUND_MASTER *((vu16*)(MEM_IO + 0x82)) | ||
549 | #define SOUND_STATUS *((vu16*)(MEM_IO + 0x84)) | ||
550 | #define SOUND_BIAS *((vu16*)(MEM_IO + 0x88)) | ||
551 | |||
552 | // Sound DMG master bits. | ||
553 | #define SOUND_VOLUME_LEFT(N) (N) | ||
554 | #define SOUND_VOLUME_RIGHT(N) ((N) << 4) | ||
555 | #define SOUND_ENABLE_SQUARE1_LEFT (1 << 0x8) | ||
556 | #define SOUND_ENABLE_SQUARE2_LEFT (1 << 0x9) | ||
557 | #define SOUND_ENABLE_WAVE_LEFT (1 << 0xA) | ||
558 | #define SOUND_ENABLE_NOISE_LEFT (1 << 0xB) | ||
559 | #define SOUND_ENABLE_SQUARE1_RIGHT (1 << 0xC) | ||
560 | #define SOUND_ENABLE_SQUARE2_RIGHT (1 << 0xD) | ||
561 | #define SOUND_ENABLE_WAVE_RIGHT (1 << 0xE) | ||
562 | #define SOUND_ENABLE_NOISE_RIGHT (1 << 0xF) | ||
563 | |||
564 | typedef enum { | ||
565 | SOUND_SQUARE1 = (0x1 << 0), | ||
566 | SOUND_SQUARE2 = (0x1 << 1), | ||
567 | SOUND_WAVE = (0x1 << 2), | ||
568 | SOUND_NOISE = (0x1 << 3), | ||
569 | } SoundChannel; | ||
570 | |||
571 | u16 | ||
572 | sound_volume(SoundChannel channels, u8 volume) { | ||
573 | volume = volume & 0x7; | ||
574 | channels = channels & 0xF; | ||
575 | return volume | (volume << 0x4) | (channels << 0x8) | (channels << 0xC); | ||
576 | } | ||
577 | |||
578 | // Sound Direct Sound master bits. | ||
579 | #define SOUND_DMG25 0x0 | ||
580 | #define SOUND_DMG50 0x1 | ||
581 | #define SOUND_DMG100 0x2 | ||
582 | #define SOUND_DSOUND_RATIO_A (1 << 0x2) | ||
583 | #define SOUND_DSOUND_RATIO_B (1 << 0x3) | ||
584 | #define SOUND_DSOUND_LEFT_A (1 << 0x8) | ||
585 | #define SOUND_DSOUND_RIGHT_A (1 << 0x9) | ||
586 | #define SOUND_DSOUND_TIMER_A (1 << 0xA) | ||
587 | #define SOUND_DSOUND_RESET_A (1 << 0xB) | ||
588 | #define SOUND_DSOUND_LEFT_B (1 << 0xC) | ||
589 | #define SOUND_DSOUND_RIGHT_B (1 << 0xD) | ||
590 | #define SOUND_DSOUND_TIMER_B (1 << 0xE) | ||
591 | #define SOUND_DSOUND_RESET_B (1 << 0xF) | ||
592 | |||
593 | // Sound status bits. | ||
594 | #define SOUND_ENABLE (1 << 0x7) | ||
595 | |||
596 | // DMG square control bits. | ||
597 | #define SOUND_SQUARE_LEN(N) (N) | ||
598 | #define SOUND_SQUARE_DUTY(N) ((N) << 0x6) | ||
599 | #define SOUND_SQUARE_ENV_TIME(N) ((N) << 0x8) | ||
600 | #define SOUND_SQUARE_ENV_DIR(N) ((N) << 0xB) | ||
601 | #define SOUND_SQUARE_ENV_VOL(N) ((N) << 0xC) | ||
602 | |||
603 | // DMG square 1 sweep control bits. | ||
604 | #define SOUND_SWEEP_NUMBER(N) (N) | ||
605 | #define SOUND_SWEEP_DIR(N) ((N) << 0x3) | ||
606 | #define SOUND_SWEEP_TIME(N) ((N) << 0x4) | ||
607 | |||
608 | // DMG frequency bits (Square/Wave). | ||
609 | #define SOUND_FREQ_TIMED (1 << 0xE) | ||
610 | #define SOUND_FREQ_RESET (1 << 0xF) | ||
611 | |||
612 | // DMG wave ram. | ||
613 | #define SOUND_WAVE_RAM_0 *((vu32*)(MEM_IO + 0x90)) | ||
614 | #define SOUND_WAVE_RAM_1 *((vu32*)(MEM_IO + 0x94)) | ||
615 | #define SOUND_WAVE_RAM_2 *((vu32*)(MEM_IO + 0x98)) | ||
616 | #define SOUND_WAVE_RAM_3 *((vu32*)(MEM_IO + 0x9C)) | ||
617 | |||
618 | // DMG wave control bits. | ||
619 | #define SOUND_WAVE_LENGTH(N) (N) | ||
620 | #define SOUND_WAVE_MUTE 0x0 | ||
621 | #define SOUND_WAVE_VOL_100 (0x1 << 0xD) | ||
622 | #define SOUND_WAVE_VOL_75 (0x4 << 0xD) | ||
623 | #define SOUND_WAVE_VOL_50 (0x2 << 0xD) | ||
624 | #define SOUND_WAVE_VOL_25 (0x3 << 0xD) | ||
625 | |||
626 | // DMG wave mode bits. | ||
627 | #define SOUND_WAVE_BANK_MODE(N) ((N) << 0x5) | ||
628 | #define SOUND_WAVE_BANK_SELECT(N) ((N) << 0x6) | ||
629 | #define SOUND_WAVE_ENABLE (1 << 0x7) | ||
630 | |||
631 | typedef u8 WaveBank[32]; | ||
632 | |||
633 | // typedef u32 WaveBank[4]; | ||
634 | #define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90)) | ||
635 | |||
636 | typedef enum { | ||
637 | NOTE_C_2 , NOTE_C_SHARP_2 , NOTE_D_2 , NOTE_D_SHARP_2 , | ||
638 | NOTE_E_2 , NOTE_F_2 , NOTE_F_SHARP_2 , NOTE_G_2 , | ||
639 | NOTE_G_SHARP_2 , NOTE_A_2 , NOTE_A_SHARP_2 , NOTE_B_2 , | ||
640 | NOTE_C_3 , NOTE_C_SHARP_3 , NOTE_D_3 , NOTE_D_SHARP_3 , | ||
641 | NOTE_E_3 , NOTE_F_3 , NOTE_F_SHARP_3 , NOTE_G_3 , | ||
642 | NOTE_G_SHARP_3 , NOTE_A_3 , NOTE_A_SHARP_3 , NOTE_B_3 , | ||
643 | NOTE_C_4 , NOTE_C_SHARP_4 , NOTE_D_4 , NOTE_D_SHARP_4 , | ||
644 | NOTE_E_4 , NOTE_F_4 , NOTE_F_SHARP_4 , NOTE_G_4 , | ||
645 | NOTE_G_SHARP_4 , NOTE_A_4 , NOTE_A_SHARP_4 , NOTE_B_4 , | ||
646 | NOTE_C_5 , NOTE_C_SHARP_5 , NOTE_D_5 , NOTE_D_SHARP_5 , | ||
647 | NOTE_E_5 , NOTE_F_5 , NOTE_F_SHARP_5 , NOTE_G_5 , | ||
648 | NOTE_G_SHARP_5 , NOTE_A_5 , NOTE_A_SHARP_5 , NOTE_B_5 , | ||
649 | NOTE_C_6 , NOTE_C_SHARP_6 , NOTE_D_6 , NOTE_D_SHARP_6 , | ||
650 | NOTE_E_6 , NOTE_F_6 , NOTE_F_SHARP_6 , NOTE_G_6 , | ||
651 | NOTE_G_SHARP_6 , NOTE_A_6 , NOTE_A_SHARP_6 , NOTE_B_6 , | ||
652 | NOTE_C_7 , NOTE_C_SHARP_7 , NOTE_D_7 , NOTE_D_SHARP_7 , | ||
653 | NOTE_E_7 , NOTE_F_7 , NOTE_F_SHARP_7 , NOTE_G_7 , | ||
654 | NOTE_G_SHARP_7 , NOTE_A_7 , NOTE_A_SHARP_7 , NOTE_B_7 , | ||
655 | NOTE_C_8 | ||
656 | } Note; | ||
657 | |||
658 | const char * note_names[] = { | ||
659 | "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", | ||
660 | "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3", | ||
661 | "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", | ||
662 | "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", | ||
663 | "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6", | ||
664 | "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7", | ||
665 | "C8" | ||
666 | }; | ||
667 | |||
668 | const u32 sound_rates[] = { | ||
669 | 44 , 156 , 262 , 363 , 457 , 547 , 631 , 710 , 785 , 856 , 923 , 986 , | ||
670 | 1046, 1102, 1155, 1205, 1252, 1297, 1339, 1379, 1416, 1452, 1485, 1517, | ||
671 | 1547, 1575, 1601, 1626, 1650, 1672, 1693, 1713, 1732, 1750, 1766, 1782, | ||
672 | 1797, 1811, 1824, 1837, 1849, 1860, 1870, 1880, 1890, 1899, 1907, 1915, | ||
673 | 1922, 1929, 1936, 1942, 1948, 1954, 1959, 1964, 1969, 1973, 1977, 1981, | ||
674 | 1985, 1988, 1992, 1995, 1998, 2001, 2003, 2006, 2008, 2010, 2012, 2014, | ||
675 | 2016, | ||
676 | }; | ||
677 | |||
678 | // | ||
679 | // Misc. | ||
680 | // | ||
681 | |||
682 | // Custom VSync option. This will waste a lot of battery power, since the | ||
683 | // machine is not clocked down. Prefer using `bios_vblank_wait()` if interrupts | ||
684 | // are enabled. | ||
685 | static inline void | ||
686 | wait_vsync(void) { | ||
687 | while(DISP_VCOUNT >= 160); | ||
688 | while(DISP_VCOUNT < 160); | ||
689 | } | ||
690 | |||
691 | // General utility macros. | ||
692 | #define MIN(A, B) ((A) <= (B) ? (A) : (B)) | ||
693 | #define MAX(A, B) ((A) >= (B) ? (A) : (B)) | ||
694 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) | ||
695 | |||
696 | #endif // GBAEXP_COMMON_H | ||
diff --git a/src/irs.s b/src/irs.s new file mode 100644 index 0000000..5cccb6e --- /dev/null +++ b/src/irs.s | |||
@@ -0,0 +1,89 @@ | |||
1 | .file "irs.s" | ||
2 | .extern irs_table; | ||
3 | .section .iwram, "ax", %progbits | ||
4 | .arm | ||
5 | .align | ||
6 | .global irs_main | ||
7 | |||
8 | irs_main: | ||
9 | @ Get the contents of IRQ_ENABLE, IRQ_ACK, and IRQ_CTRL | ||
10 | ldr ip, mem_irq_base_reg @ ip = (IRQ_ENABLE << 16) | IRQ_ACK | ||
11 | ldr r0, [ip] @ r0 = irq_enable | ||
12 | and r1, r0, r0, lsr #16 @ r1 = irq_enable & irq_ack | ||
13 | |||
14 | @ Disable IRQ_CTRL for now. | ||
15 | mov r3, #0 @ r3 = 0 | ||
16 | strh r3, [ip, #8] @ *(ip + 0x8) = r3 | ||
17 | |||
18 | @ r0 = irs_table address pointer | ||
19 | @ r2 = tmp | ||
20 | @ r3 = 0 (for r3; r3 < 14; r3++) | ||
21 | ldr r0, = irs_table | ||
22 | irs_main_fp_search: | ||
23 | @ Check that the current index is an active IRQ. | ||
24 | mov r2, #1 | ||
25 | and r2, r1, r2, lsl r3 | ||
26 | cmp r2, #0 | ||
27 | beq irs_main_fp_search_not_enabled | ||
28 | |||
29 | @ Extract the function pointer for this IRS if available. | ||
30 | ldr r2, [r0] | ||
31 | cmp r2, #0 | ||
32 | bne irs_main_handle_irs | ||
33 | |||
34 | irs_main_fp_search_not_enabled: | ||
35 | add r0, r0, #4 | ||
36 | add r3, #1 | ||
37 | cmp r3, #14 | ||
38 | bne irs_main_fp_search | ||
39 | b irs_main_exit | ||
40 | |||
41 | irs_main_handle_irs: | ||
42 | @ r2: Contains IRQ function pointer. | ||
43 | @ r3: Stores the IRQ index. | ||
44 | |||
45 | @ Acknowledge that we are handling this interrupt writing to IRQ_ACK and | ||
46 | @ IRQ_ACK_BIOS. | ||
47 | mov r0, #1 | ||
48 | lsl r0, r0, r3 | ||
49 | strh r0, [ip, #2] | ||
50 | ldr r1, mem_irq_ack_bios @ r1 = IRQ_ACK_BIOS | ||
51 | str r0, [r1] | ||
52 | |||
53 | @ Store the SPSR in one of the free registers and save it along with the | ||
54 | @ return pointer. | ||
55 | mrs r3, spsr | ||
56 | stmfd sp!, {r3, lr} | ||
57 | |||
58 | @ Set CPU to system mode | ||
59 | mrs r3, cpsr | ||
60 | bic r3, r3, #0xDF | ||
61 | orr r3, r3, #0x1F | ||
62 | msr cpsr, r3 | ||
63 | |||
64 | @ Call isr function pointer | ||
65 | stmfd sp!, {lr} | ||
66 | mov lr, pc | ||
67 | bx r2 | ||
68 | ldmfd sp!, {lr} | ||
69 | |||
70 | @ Set CPU to irq mode | ||
71 | mrs r3, cpsr | ||
72 | bic r3, r3, #0xDF | ||
73 | orr r3, r3, #0x92 | ||
74 | msr cpsr, r3 | ||
75 | |||
76 | @ Restore the SPSR and the registers. | ||
77 | ldmfd sp!, {r3, lr} | ||
78 | msr spsr, r3 | ||
79 | |||
80 | irs_main_exit: | ||
81 | @ Restore IRQ_CTRL. | ||
82 | mov r3, #1 @ r3 = 0 | ||
83 | strh r3, [ip, #8] @ *(ip + 0x8) = r3 | ||
84 | bx lr | ||
85 | |||
86 | mem_irq_base_reg: | ||
87 | .word 0x04000200 @ IRQ_ENABLE | ||
88 | mem_irq_ack_bios: | ||
89 | .word 0x03007FF8 @ IRQ_ACK_BIOS | ||
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..77cabcc --- /dev/null +++ b/src/main.c | |||
@@ -0,0 +1,155 @@ | |||
1 | #include <string.h> | ||
2 | |||
3 | #include "common.h" | ||
4 | #include "bitmap.h" | ||
5 | #include "text.h" | ||
6 | #include "small-font.c" | ||
7 | |||
8 | #include "uxn/uxn.h" | ||
9 | #include "uxn/uxn.c" | ||
10 | #include "uxn/devices/ppu.h" | ||
11 | #include "uxn/devices/ppu.c" | ||
12 | // #include "uxn/roms/console.c" | ||
13 | #include "uxn/roms/dvd.c" | ||
14 | // #include "uxn/roms/proportional_fonts.c" | ||
15 | // #include "uxn/roms/automata.c" | ||
16 | // #include "uxn/roms/life.c" | ||
17 | |||
18 | static Ppu ppu; | ||
19 | u8 reqdraw = 0; | ||
20 | static Device *devscreen; | ||
21 | |||
22 | void | ||
23 | nil_talk(Device *d, Uint8 b0, Uint8 w) { | ||
24 | (void)d; | ||
25 | (void)b0; | ||
26 | (void)w; | ||
27 | } | ||
28 | |||
29 | void | ||
30 | console_talk(Device *d, u8 b0, u8 w) { | ||
31 | if(!w) { | ||
32 | return; | ||
33 | } | ||
34 | switch(b0) { | ||
35 | case 0x8: txt_printf("%c", d->dat[0x8]); break; | ||
36 | case 0x9: txt_printf("0x%02x", d->dat[0x9]); break; | ||
37 | case 0xb: txt_printf("0x%04x", mempeek16(d->dat, 0xa)); break; | ||
38 | case 0xd: txt_printf("%s", &d->mem[mempeek16(d->dat, 0xc)]); break; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | void | ||
43 | system_talk(Device *d, Uint8 b0, Uint8 w) | ||
44 | { | ||
45 | if(!w) { | ||
46 | d->dat[0x2] = d->u->wst.ptr; | ||
47 | d->dat[0x3] = d->u->rst.ptr; | ||
48 | } else { | ||
49 | putcolors(&ppu, &d->dat[0x8]); | ||
50 | reqdraw = 1; | ||
51 | } | ||
52 | (void)b0; | ||
53 | } | ||
54 | |||
55 | void | ||
56 | screen_talk(Device *d, Uint8 b0, Uint8 w) { | ||
57 | if(w && b0 == 0xe) { | ||
58 | Uint16 x = mempeek16(d->dat, 0x8); | ||
59 | Uint16 y = mempeek16(d->dat, 0xa); | ||
60 | Uint8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; | ||
61 | Uint8 *layer = d->dat[0xe] >> 4 & 0x1 ? ppu.fg : ppu.bg; | ||
62 | Uint8 mode = d->dat[0xe] >> 5; | ||
63 | if(!mode) { | ||
64 | putpixel(&ppu, layer, x, y, d->dat[0xe] & 0x3); | ||
65 | } else if(mode-- & 0x1) { | ||
66 | puticn(&ppu, layer, x, y, addr, d->dat[0xe] & 0xf, mode & 0x2, mode & 0x4); | ||
67 | } else { | ||
68 | putchr(&ppu, layer, x, y, addr, d->dat[0xe] & 0xf, mode & 0x2, mode & 0x4); | ||
69 | } | ||
70 | reqdraw = 1; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | void | ||
75 | redraw(Uint16 *dst, Uxn *u) { | ||
76 | // TODO: The screen will flicker but using Mode4 for double buffering would | ||
77 | // be way too slow. | ||
78 | drawppu(&ppu); | ||
79 | clear_screen_m3(); | ||
80 | // Copy ppu data to framebuffer. | ||
81 | for (size_t j = 0; j < ppu.height; ++j) { | ||
82 | for (size_t i = 0; i < ppu.width; ++i) { | ||
83 | FRAMEBUFFER[j][i] = ppu.output[i + j * ppu.width]; | ||
84 | } | ||
85 | } | ||
86 | reqdraw = 0; | ||
87 | } | ||
88 | |||
89 | void | ||
90 | init_uxn(Uxn *u) { | ||
91 | // Initialize PPU. | ||
92 | initppu(&ppu, 30, 20, 0); | ||
93 | |||
94 | // Copy rom to VM. | ||
95 | memcpy(u->ram.dat + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); | ||
96 | |||
97 | // Prepare devices. | ||
98 | portuxn(u, 0x0, "system", system_talk); | ||
99 | portuxn(u, 0x1, "console", console_talk); | ||
100 | devscreen = portuxn(u, 0x2, "screen", screen_talk); | ||
101 | portuxn(u, 0x3, "---", nil_talk); | ||
102 | portuxn(u, 0x4, "---", nil_talk); | ||
103 | portuxn(u, 0x5, "---", nil_talk); | ||
104 | portuxn(u, 0x6, "---", nil_talk); | ||
105 | portuxn(u, 0x7, "---", nil_talk); | ||
106 | portuxn(u, 0x8, "---", nil_talk); | ||
107 | portuxn(u, 0x9, "---", nil_talk); | ||
108 | portuxn(u, 0xa, "---", nil_talk); | ||
109 | portuxn(u, 0xb, "---", nil_talk); | ||
110 | portuxn(u, 0xc, "---", nil_talk); | ||
111 | portuxn(u, 0xd, "---", nil_talk); | ||
112 | portuxn(u, 0xe, "---", nil_talk); | ||
113 | portuxn(u, 0xf, "---", nil_talk); | ||
114 | mempoke16(devscreen->dat, 2, ppu.hor * 8); | ||
115 | mempoke16(devscreen->dat, 4, ppu.ver * 8); | ||
116 | } | ||
117 | |||
118 | int main(void) { | ||
119 | // Initialize display mode and bg palette. | ||
120 | DISP_CTRL = DISP_MODE_3 | DISP_BG_2; | ||
121 | |||
122 | // Initialize text engine. | ||
123 | txt_init_bitmap( | ||
124 | TXT_MODE_MODE3, | ||
125 | (Font){ | ||
126 | .data = small_font, | ||
127 | .char_width = 4, | ||
128 | .char_height = 8, | ||
129 | .color = COLOR_BLUE, | ||
130 | .char_map = small_font_map, | ||
131 | }); | ||
132 | |||
133 | // Register interrupts. | ||
134 | irq_init(); | ||
135 | irs_set(IRQ_VBLANK, irs_stub); | ||
136 | |||
137 | // Initialize VM. | ||
138 | Uxn u = {0}; | ||
139 | init_uxn(&u); | ||
140 | evaluxn(&u, 0x0100); | ||
141 | |||
142 | // Main loop. | ||
143 | int frame_counter = 0; | ||
144 | while(true) { | ||
145 | bios_vblank_wait(); | ||
146 | poll_keys(); | ||
147 | evaluxn(&u, mempeek16(devscreen->dat, 0)); | ||
148 | if(reqdraw) { | ||
149 | redraw(ppu.output, &u); | ||
150 | } | ||
151 | frame_counter++; | ||
152 | }; | ||
153 | |||
154 | return 0; | ||
155 | } | ||
diff --git a/src/shorthand.h b/src/shorthand.h new file mode 100644 index 0000000..42eb935 --- /dev/null +++ b/src/shorthand.h | |||
@@ -0,0 +1,36 @@ | |||
1 | #ifndef UTILS_SHORTHAND_H | ||
2 | #define UTILS_SHORTHAND_H | ||
3 | |||
4 | #include <assert.h> | ||
5 | #include <stdbool.h> | ||
6 | #include <stddef.h> | ||
7 | #include <stdint.h> | ||
8 | |||
9 | // | ||
10 | // This simple header just typedefs the basic C define types to a shorter name, | ||
11 | // loads the quality of life bool macro for _Bool and defines shorthand macros | ||
12 | // for byte sizes. | ||
13 | |||
14 | typedef uint8_t u8; | ||
15 | typedef uint16_t u16; | ||
16 | typedef uint32_t u32; | ||
17 | typedef uint64_t u64; | ||
18 | typedef int8_t s8; | ||
19 | typedef int16_t s16; | ||
20 | typedef int32_t s32; | ||
21 | typedef int64_t s64; | ||
22 | typedef volatile u8 vu8; | ||
23 | typedef volatile u16 vu16; | ||
24 | typedef volatile u32 vu32; | ||
25 | typedef volatile u64 vu64; | ||
26 | typedef volatile s8 vs8; | ||
27 | typedef volatile s16 vs16; | ||
28 | typedef volatile s32 vs32; | ||
29 | typedef volatile s64 vs64; | ||
30 | |||
31 | #define KB(N) ((u64)(N) * 1024) | ||
32 | #define MB(N) ((u64)KB(N) * 1024) | ||
33 | #define GB(N) ((u64)MB(N) * 1024) | ||
34 | #define TB(N) ((u64)GB(N) * 1024) | ||
35 | |||
36 | #endif // UTILS_SHORTHAND_H | ||
diff --git a/src/small-font.c b/src/small-font.c new file mode 100644 index 0000000..2f232dd --- /dev/null +++ b/src/small-font.c | |||
@@ -0,0 +1,45 @@ | |||
1 | u32 small_font[] = { | ||
2 | 0x00000000, 0x00000000, 0x00020202, 0x00020000, | ||
3 | 0x000a0a00, 0x00000000, 0x000a1f0a, 0x1f0a0000, | ||
4 | 0x02070107, 0x04070200, 0x00010402, 0x01040000, | ||
5 | 0x0007050f, 0x050d0000, 0x00020200, 0x00000000, | ||
6 | 0x00020101, 0x01020000, 0x00020404, 0x04020000, | ||
7 | 0x00050207, 0x02050000, 0x00000207, 0x02000000, | ||
8 | 0x00000000, 0x00020100, 0x00000007, 0x00000000, | ||
9 | 0x00000000, 0x00020000, 0x00040602, 0x03010000, | ||
10 | 0x00070505, 0x05070000, 0x00020302, 0x02070000, | ||
11 | 0x00070407, 0x01070000, 0x00070406, 0x04070000, | ||
12 | 0x00040605, 0x07040000, 0x00070107, 0x04070000, | ||
13 | 0x00070107, 0x05070000, 0x00070402, 0x01010000, | ||
14 | 0x00070507, 0x05070000, 0x00070507, 0x04040000, | ||
15 | 0x00000200, 0x00020000, 0x00000200, 0x00020100, | ||
16 | 0x00040201, 0x02040000, 0x00000700, 0x07000000, | ||
17 | 0x00010204, 0x02010000, 0x00070402, 0x00020000, | ||
18 | 0x00070505, 0x01070000, 0x00070505, 0x07050000, | ||
19 | 0x00070503, 0x05070000, 0x00070101, 0x01070000, | ||
20 | 0x00030505, 0x05030000, 0x00070103, 0x01070000, | ||
21 | 0x00070103, 0x01010000, 0x00070105, 0x05070000, | ||
22 | 0x00050507, 0x05050000, 0x00070202, 0x02070000, | ||
23 | 0x00040404, 0x05070000, 0x00050503, 0x05050000, | ||
24 | 0x00010101, 0x01070000, 0x00050705, 0x05050000, | ||
25 | 0x00030505, 0x05050000, 0x00030505, 0x05070000, | ||
26 | 0x00070507, 0x01010000, 0x00070505, 0x07040000, | ||
27 | 0x00070503, 0x05050000, 0x00060107, 0x04030000, | ||
28 | 0x00070202, 0x02020000, 0x00050505, 0x05070000, | ||
29 | 0x00050505, 0x05060000, 0x00050505, 0x07050000, | ||
30 | 0x00050502, 0x05050000, 0x00050507, 0x02020000, | ||
31 | 0x00070402, 0x01070000, 0x00070101, 0x01070000, | ||
32 | 0x00010302, 0x06040000, 0x00070404, 0x04070000, | ||
33 | 0x00020500, 0x00000000, 0x00000000, 0x00070000, | ||
34 | }; | ||
35 | |||
36 | u8 small_font_map[256] = { | ||
37 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , | ||
38 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , | ||
39 | 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10, 11, 12, 13, 14, 15, | ||
40 | 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, | ||
41 | 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, | ||
42 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, | ||
43 | 0 , 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, | ||
44 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 0 , 0 , 0 , 0 , 0 , | ||
45 | }; | ||
diff --git a/src/sprites.h b/src/sprites.h new file mode 100644 index 0000000..a64b784 --- /dev/null +++ b/src/sprites.h | |||
@@ -0,0 +1,79 @@ | |||
1 | #ifndef GBAEXP_SPRITES_H | ||
2 | #define GBAEXP_SPRITES_H | ||
3 | |||
4 | #include "common.h" | ||
5 | |||
6 | typedef struct Sprite { | ||
7 | // A unique sprite identifier. | ||
8 | size_t id; | ||
9 | // The number of tiles for a single sprite frame. | ||
10 | size_t n_tiles; | ||
11 | // The starting tile of this sprite. | ||
12 | size_t tile_start; | ||
13 | // The associated palette bank for this sprite. | ||
14 | size_t pal_bank; | ||
15 | } Sprite; | ||
16 | |||
17 | |||
18 | #define NUM_SPRITES 128 | ||
19 | Sprite sprites[NUM_SPRITES]; | ||
20 | |||
21 | // Keeping track of unique sprites and current sprite memory pointer using | ||
22 | // global singletons. | ||
23 | size_t sprite_counter = 0; | ||
24 | size_t sprite_tile_counter = 0; | ||
25 | u32 *sprite_memory = NULL; | ||
26 | |||
27 | // Loads the sprite data into video memory and initialize the Sprite structure. | ||
28 | size_t | ||
29 | load_sprite_data(u32 *sprite_data, size_t n_tiles, size_t n_frames) { | ||
30 | memcpy(sprite_memory, sprite_data, 8 * n_tiles * n_frames * sizeof(u32)); | ||
31 | sprite_memory += 8 * n_tiles * n_frames; | ||
32 | Sprite sprite = { | ||
33 | .id = sprite_counter, | ||
34 | .n_tiles = n_tiles, | ||
35 | .tile_start = sprite_tile_counter, | ||
36 | }; | ||
37 | sprite_tile_counter += n_tiles * n_frames; | ||
38 | sprites[sprite_counter] = sprite; | ||
39 | return sprite_counter++; | ||
40 | } | ||
41 | |||
42 | size_t | ||
43 | load_packed_sprite_data(u32 *sprite_data, size_t n_tiles, size_t n_frames) { | ||
44 | unpack_tiles(sprite_data, sprite_memory, n_tiles * n_frames); | ||
45 | sprite_memory += 8 * n_tiles * n_frames; | ||
46 | Sprite sprite = { | ||
47 | .id = sprite_counter, | ||
48 | .n_tiles = n_tiles, | ||
49 | .tile_start = sprite_tile_counter, | ||
50 | }; | ||
51 | sprite_tile_counter += n_tiles * n_frames; | ||
52 | sprites[sprite_counter] = sprite; | ||
53 | return sprite_counter++; | ||
54 | } | ||
55 | |||
56 | void | ||
57 | init_sprites(size_t starting_tile) { | ||
58 | // Initialize all attributes by disabling rendering. If we don't do this, | ||
59 | // glitches may appear. | ||
60 | for (size_t i = 0; i < 128; ++i) { | ||
61 | OBJ_ATTR_0(i) = (1 << 9); | ||
62 | } | ||
63 | |||
64 | sprite_counter = 0; | ||
65 | // Prepare global sprite_memory address. | ||
66 | sprite_memory = &TILE_MEM[4][starting_tile]; | ||
67 | sprite_tile_counter = starting_tile; | ||
68 | } | ||
69 | |||
70 | void | ||
71 | init_sprite_pal(size_t starting_index, Color col) { | ||
72 | // Add colors to the sprite color palette. Tiles with color number 0 are | ||
73 | // treated as transparent. | ||
74 | for (size_t i = 0; i < 16; ++i) { | ||
75 | PAL_BUFFER_SPRITES[i + starting_index] = col; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | #endif // GBAEXP_SPRITES_H | ||
diff --git a/src/text.h b/src/text.h new file mode 100644 index 0000000..bfac4e4 --- /dev/null +++ b/src/text.h | |||
@@ -0,0 +1,232 @@ | |||
1 | #ifndef GBAEXP_TILES_H | ||
2 | #define GBAEXP_TILES_H | ||
3 | |||
4 | #include <stdarg.h> | ||
5 | #include <stdio.h> | ||
6 | |||
7 | #include "common.h" | ||
8 | #include "bitmap.h" | ||
9 | |||
10 | typedef enum { | ||
11 | TXT_MODE_TILED_BG, | ||
12 | TXT_MODE_MODE3, | ||
13 | } TextMode; | ||
14 | |||
15 | typedef struct Font { | ||
16 | // A pointer to an area of memory containing font data. | ||
17 | // TODO: Should we unpack each char everytime or unpack everything into RAM? | ||
18 | // Maybe this should be optional? | ||
19 | u16 *data; | ||
20 | // The char_map stores the index to the character position within the font | ||
21 | // array depending on the ascii number we want to render. This allows | ||
22 | // the usage reduced font sets, for example just uppercase letters and | ||
23 | // numbers. | ||
24 | u8 *char_map; | ||
25 | // Width and height of each font character. Only monospaced fonts are | ||
26 | // currently supported. | ||
27 | u8 char_width; | ||
28 | u8 char_height; | ||
29 | // The color of this font. | ||
30 | Color color; | ||
31 | } Font; | ||
32 | |||
33 | typedef struct TextEngine { | ||
34 | // Currently working on tiled backgrounds only. The X and Y positions | ||
35 | // correspond to the tile X and Y starting from the top left of the screen. | ||
36 | // For a 240x160 screen, we have 30x20 tiles available. | ||
37 | size_t cursor_x; | ||
38 | size_t cursor_y; | ||
39 | // Pointer to the memory being used for writing to the screen. | ||
40 | u16 *memory; | ||
41 | // TODO: Support other modes and monospaced fonts should be simple but we | ||
42 | // need to keep track of a couple of other variables, which may not be | ||
43 | // available for all modes. For example, tile modes can't have a font width | ||
44 | // smaller than the tile size, although bigger fonts (For example fonts | ||
45 | // composed of multiple tiles) should still be possible. This may not be | ||
46 | // a good time investment, but can be done in the future if needed. | ||
47 | // The mode controls how the text is rendered and how the memory is managed. | ||
48 | TextMode mode; | ||
49 | // The font used to render the text. | ||
50 | Font font; | ||
51 | } TextEngine; | ||
52 | |||
53 | static TextEngine text_engine = {0}; | ||
54 | |||
55 | static u8 default_char_map[256] = {0}; | ||
56 | |||
57 | void | ||
58 | txt_putc_tile(char c) { | ||
59 | if (c == '\0') { | ||
60 | return; | ||
61 | } | ||
62 | if (c == '\n') { | ||
63 | text_engine.cursor_x = 0; | ||
64 | text_engine.cursor_y++; | ||
65 | } else { | ||
66 | text_engine.memory[text_engine.cursor_x + 32 * text_engine.cursor_y] = c; | ||
67 | text_engine.cursor_x++; | ||
68 | if (text_engine.cursor_x >= 30) { | ||
69 | text_engine.cursor_x = 0; | ||
70 | text_engine.cursor_y++; | ||
71 | } | ||
72 | } | ||
73 | if (text_engine.cursor_y >= 20) { | ||
74 | text_engine.cursor_y = 0; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | void | ||
79 | txt_putc_m3(char c) { | ||
80 | if (c == '\0') { | ||
81 | return; | ||
82 | } | ||
83 | if (c == '\n') { | ||
84 | text_engine.cursor_x = 0; | ||
85 | text_engine.cursor_y += text_engine.font.char_height; | ||
86 | } else { | ||
87 | u8 idx = text_engine.font.char_map[(int)c] * 2; | ||
88 | u32 *packed_char = text_engine.font.data; | ||
89 | packed_char += idx; | ||
90 | Tile tile = {0}; | ||
91 | unpack_tiles(packed_char, &tile, 1); | ||
92 | int x = text_engine.cursor_x; | ||
93 | int y = text_engine.cursor_y; | ||
94 | for (size_t i = 0; i < text_engine.font.char_height; ++i) { | ||
95 | for (size_t j = 0; j < text_engine.font.char_width; ++j) { | ||
96 | if ((tile.row[i] >> 4 * j) & 0x1) { | ||
97 | // put_pixel_m4(x + j, y + i, 1, backbuffer); | ||
98 | // TODO: Clean this up please. | ||
99 | put_pixel_m3(x + j, | ||
100 | y + i, | ||
101 | text_engine.font.color, | ||
102 | FRAMEBUFFER); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | text_engine.cursor_x += text_engine.font.char_width; | ||
107 | if (text_engine.cursor_x >= SCREEN_WIDTH) { | ||
108 | text_engine.cursor_x = 0; | ||
109 | text_engine.cursor_y += text_engine.font.char_height; | ||
110 | } | ||
111 | } | ||
112 | if (text_engine.cursor_y >= SCREEN_HEIGHT) { | ||
113 | text_engine.cursor_y = 0; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | void | ||
118 | txt_putc(char c) { | ||
119 | switch (text_engine.mode) { | ||
120 | case TXT_MODE_TILED_BG: { | ||
121 | txt_putc_tile(c); | ||
122 | } break; | ||
123 | case TXT_MODE_MODE3: { | ||
124 | txt_putc_m3(c); | ||
125 | } break; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | static inline void | ||
130 | txt_puts(char *msg) { | ||
131 | while (*msg) { | ||
132 | txt_putc(*msg++); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | void | ||
137 | txt_init_tile(size_t bg, Font font, size_t cb_idx) { | ||
138 | // The screenblock for the tile map should start after the tile memory | ||
139 | // (MEM_VRAM + 0x2000 for 256 characters). Since each screenblock is | ||
140 | // composed of 1024 screenblock entries of u16 (2 bytes), we need an | ||
141 | // screenblock index offset of 8192 / (1024 * 2) = 4 screen blocks. | ||
142 | size_t sb_idx = cb_idx * 8 + 4; | ||
143 | |||
144 | // Set the background parameters for the text layer. | ||
145 | BG_CTRL(bg) = BG_CHARBLOCK(cb_idx) | BG_SCREENBLOCK(sb_idx) | BG_PRIORITY(3); | ||
146 | |||
147 | // Load font data in video memory. Each character is unpacked into a tile of | ||
148 | // 8 32bit values (4bpp), meaning that for the full ASCII set of 256 | ||
149 | // characters, we need 8192 bytes of VRAM (8 * 4 * 256). | ||
150 | text_engine.font = font; | ||
151 | unpack_tiles(font.data, &TILE_MEM[cb_idx], 256); | ||
152 | |||
153 | // Initialize default values. | ||
154 | if (font.color == 0) { | ||
155 | font.color = COLOR_WHITE; | ||
156 | } | ||
157 | |||
158 | // Load palette color. | ||
159 | PAL_BUFFER_BG[1] = font.color; | ||
160 | |||
161 | // Update text_engine variables. | ||
162 | text_engine.memory = SCREENBLOCK_MEM[sb_idx]; | ||
163 | } | ||
164 | |||
165 | void | ||
166 | txt_init_bitmap(TextMode mode, Font font) { | ||
167 | // NOTE: Only mode 3 is currently supported | ||
168 | assert(mode == TXT_MODE_MODE3); | ||
169 | |||
170 | // If font_map is NULL, initialize the standard 0-255 character map. | ||
171 | if (font.char_map == NULL) { | ||
172 | for (size_t i = 0; i < 256; ++i) { | ||
173 | default_char_map[i] = i; | ||
174 | } | ||
175 | font.char_map = &default_char_map; | ||
176 | } | ||
177 | |||
178 | // Initialize default values if set to zero. | ||
179 | if (font.char_width == 0) { | ||
180 | font.char_width = 8; | ||
181 | } | ||
182 | if (font.char_height == 0) { | ||
183 | font.char_height = 8; | ||
184 | } | ||
185 | if (font.color == 0) { | ||
186 | font.color = COLOR_WHITE; | ||
187 | } | ||
188 | |||
189 | // Prepare text engine. | ||
190 | text_engine.font = font; | ||
191 | text_engine.mode = mode; | ||
192 | } | ||
193 | |||
194 | // Print text to the screen with formatting. | ||
195 | void | ||
196 | txt_printf(char *msg, ...) { | ||
197 | va_list arg_list; | ||
198 | va_start(arg_list, msg); | ||
199 | char buf[512] = {0}; | ||
200 | vsprintf(buf, msg, arg_list); | ||
201 | txt_puts(buf); | ||
202 | } | ||
203 | |||
204 | // TODO: Update for working on bitmap modes. | ||
205 | void | ||
206 | txt_clear_line(void) { | ||
207 | for (size_t i = 0; i < 30; ++i) { | ||
208 | text_engine.memory[i + 32 * text_engine.cursor_y] = ' '; | ||
209 | } | ||
210 | text_engine.cursor_x = 0; | ||
211 | } | ||
212 | |||
213 | // TODO: Update for working on bitmap modes. | ||
214 | void | ||
215 | txt_clear_screen(void) { | ||
216 | for (size_t j = 0; j < 20; ++j) { | ||
217 | for (size_t i = 0; i < 30; ++i) { | ||
218 | text_engine.memory[i + 32 * j] = ' '; | ||
219 | } | ||
220 | } | ||
221 | text_engine.cursor_x = 0; | ||
222 | text_engine.cursor_y = 0; | ||
223 | } | ||
224 | |||
225 | void | ||
226 | txt_position(size_t tile_x, size_t tile_y) { | ||
227 | text_engine.cursor_x = tile_x; | ||
228 | text_engine.cursor_y = tile_y; | ||
229 | } | ||
230 | |||
231 | #endif // GBAEXP_TILES_H | ||
232 | |||
diff --git a/src/uxn/devices/ppu.c b/src/uxn/devices/ppu.c new file mode 100644 index 0000000..b977f97 --- /dev/null +++ b/src/uxn/devices/ppu.c | |||
@@ -0,0 +1,180 @@ | |||
1 | #include "ppu.h" | ||
2 | |||
3 | /* | ||
4 | Copyright (c) 2021 Devine Lu Linvega | ||
5 | Copyright (c) 2021 Andrew Alderwick | ||
6 | |||
7 | Permission to use, copy, modify, and distribute this software for any | ||
8 | purpose with or without fee is hereby granted, provided that the above | ||
9 | copyright notice and this permission notice appear in all copies. | ||
10 | |||
11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
12 | WITH REGARD TO THIS SOFTWARE. | ||
13 | */ | ||
14 | |||
15 | static Uint8 font[][8] = { | ||
16 | {0x00, 0x7c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7c}, | ||
17 | {0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, | ||
18 | {0x00, 0x7c, 0x82, 0x02, 0x7c, 0x80, 0x80, 0xfe}, | ||
19 | {0x00, 0x7c, 0x82, 0x02, 0x1c, 0x02, 0x82, 0x7c}, | ||
20 | {0x00, 0x0c, 0x14, 0x24, 0x44, 0x84, 0xfe, 0x04}, | ||
21 | {0x00, 0xfe, 0x80, 0x80, 0x7c, 0x02, 0x82, 0x7c}, | ||
22 | {0x00, 0x7c, 0x82, 0x80, 0xfc, 0x82, 0x82, 0x7c}, | ||
23 | {0x00, 0x7c, 0x82, 0x02, 0x1e, 0x02, 0x02, 0x02}, | ||
24 | {0x00, 0x7c, 0x82, 0x82, 0x7c, 0x82, 0x82, 0x7c}, | ||
25 | {0x00, 0x7c, 0x82, 0x82, 0x7e, 0x02, 0x82, 0x7c}, | ||
26 | {0x00, 0x7c, 0x82, 0x02, 0x7e, 0x82, 0x82, 0x7e}, | ||
27 | {0x00, 0xfc, 0x82, 0x82, 0xfc, 0x82, 0x82, 0xfc}, | ||
28 | {0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0x82, 0x7c}, | ||
29 | {0x00, 0xfc, 0x82, 0x82, 0x82, 0x82, 0x82, 0xfc}, | ||
30 | {0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x82, 0x7c}, | ||
31 | {0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x80, 0x80}}; | ||
32 | |||
33 | Uint8 | ||
34 | readpixel(Uint8 *sprite, Uint8 h, Uint8 v) | ||
35 | { | ||
36 | Uint8 ch1 = ((sprite[v] >> h) & 0x1); | ||
37 | Uint8 ch2 = (((sprite[v + 8] >> h) & 0x1) << 1); | ||
38 | return ch1 + ch2; | ||
39 | } | ||
40 | |||
41 | void | ||
42 | clear(Ppu *p) | ||
43 | { | ||
44 | int i, sz = p->height * p->width, rows = sz / 4; | ||
45 | for(i = 0; i < sz; ++i) | ||
46 | p->output[i] = p->colors[0]; | ||
47 | for(i = 0; i < rows; i++) { | ||
48 | p->fg[i] = 0; | ||
49 | p->bg[i] = 0; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | void | ||
54 | putcolors(Ppu *p, Uint8 *addr) | ||
55 | { | ||
56 | int i; | ||
57 | for(i = 0; i < 4; ++i) { | ||
58 | Uint8 | ||
59 | r = (*(addr + i / 2) >> (!(i % 2) << 2)) & 0x0f, | ||
60 | g = (*(addr + 2 + i / 2) >> (!(i % 2) << 2)) & 0x0f, | ||
61 | b = (*(addr + 4 + i / 2) >> (!(i % 2) << 2)) & 0x0f; | ||
62 | p->colors[i] = rgb15(r,g,b); | ||
63 | } | ||
64 | // p->colors[0] = COLOR_BLUE; | ||
65 | // p->colors[1] = COLOR_WHITE; | ||
66 | // p->colors[2] = COLOR_RED; | ||
67 | // p->colors[3] = COLOR_CYAN; | ||
68 | } | ||
69 | |||
70 | void | ||
71 | putpixel(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 color) | ||
72 | { | ||
73 | Uint16 row = (y % 8) + ((x / 8 + y / 8 * p->hor) * 16), col = 7 - (x % 8); | ||
74 | if(x >= p->hor * 8 || y >= p->ver * 8 || row > (p->hor * p->ver * 16) - 8) | ||
75 | return; | ||
76 | if(color == 0 || color == 2) | ||
77 | layer[row] &= ~(1UL << col); | ||
78 | else | ||
79 | layer[row] |= 1UL << col; | ||
80 | if(color == 0 || color == 1) | ||
81 | layer[row + 8] &= ~(1UL << col); | ||
82 | else | ||
83 | layer[row + 8] |= 1UL << col; | ||
84 | } | ||
85 | |||
86 | void | ||
87 | puticn(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy) | ||
88 | { | ||
89 | Uint16 v, h; | ||
90 | for(v = 0; v < 8; v++) | ||
91 | for(h = 0; h < 8; h++) { | ||
92 | Uint8 ch1 = ((sprite[v] >> (7 - h)) & 0x1); | ||
93 | if(ch1 == 1 || (color != 0x05 && color != 0x0a && color != 0x0f)) | ||
94 | putpixel(p, | ||
95 | layer, | ||
96 | x + (flipx ? 7 - h : h), | ||
97 | y + (flipy ? 7 - v : v), | ||
98 | ch1 ? color % 4 : color / 4); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | void | ||
103 | putchr(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy) | ||
104 | { | ||
105 | Uint16 v, h; | ||
106 | for(v = 0; v < 8; v++) | ||
107 | for(h = 0; h < 8; h++) { | ||
108 | Uint8 ch1 = ((sprite[v] >> (7 - h)) & 0x1) * color; | ||
109 | Uint8 ch2 = ((sprite[v + 8] >> (7 - h)) & 0x1) * color; | ||
110 | putpixel(p, | ||
111 | layer, | ||
112 | x + (flipx ? 7 - h : h), | ||
113 | y + (flipy ? 7 - v : v), | ||
114 | (((ch1 + ch2 * 2) + color / 4) & 0x3)); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* output */ | ||
119 | |||
120 | void | ||
121 | drawpixel(Ppu *p, Uint16 x, Uint16 y, Uint8 color) | ||
122 | { | ||
123 | if(x >= p->pad && x <= p->width - p->pad - 1 && y >= p->pad && y <= p->height - p->pad - 1) | ||
124 | p->output[y * p->width + x] = p->colors[color]; | ||
125 | } | ||
126 | |||
127 | void | ||
128 | drawdebugger(Ppu *p, Uint8 *stack, Uint8 ptr) | ||
129 | { | ||
130 | Uint8 i, x, y, b; | ||
131 | for(i = 0; i < 0x20; ++i) { /* memory */ | ||
132 | x = ((i % 8) * 3 + 1) * 8, y = (i / 8 + 1) * 8, b = stack[i]; | ||
133 | puticn(p, p->bg, x, y, font[(b >> 4) & 0xf], 1 + (ptr == i) * 0x7, 0, 0); | ||
134 | puticn(p, p->bg, x + 8, y, font[b & 0xf], 1 + (ptr == i) * 0x7, 0, 0); | ||
135 | } | ||
136 | for(x = 0; x < 0x20; ++x) { | ||
137 | drawpixel(p, x, p->height / 2, 2); | ||
138 | drawpixel(p, p->width - x, p->height / 2, 2); | ||
139 | drawpixel(p, p->width / 2, p->height - x, 2); | ||
140 | drawpixel(p, p->width / 2, x, 2); | ||
141 | drawpixel(p, p->width / 2 - 16 + x, p->height / 2, 2); | ||
142 | drawpixel(p, p->width / 2, p->height / 2 - 16 + x, 2); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | void | ||
147 | drawppu(Ppu *p) | ||
148 | { | ||
149 | Uint16 x, y; | ||
150 | for(y = 0; y < p->ver; ++y) | ||
151 | for(x = 0; x < p->hor; ++x) { | ||
152 | Uint8 v, h; | ||
153 | Uint16 key = (y * p->hor + x) * 16; | ||
154 | for(v = 0; v < 8; v++) | ||
155 | for(h = 0; h < 8; h++) { | ||
156 | Uint8 color = readpixel(&p->fg[key], h, v); | ||
157 | if(color == 0) | ||
158 | color = readpixel(&p->bg[key], h, v); | ||
159 | drawpixel(p, x * 8 + p->pad + 7 - h, y * 8 + p->pad + v, color); | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | int | ||
165 | initppu(Ppu *p, Uint8 hor, Uint8 ver, Uint8 pad) | ||
166 | { | ||
167 | p->hor = hor; | ||
168 | p->ver = ver; | ||
169 | p->pad = pad; | ||
170 | p->width = (8 * p->hor + p->pad * 2); | ||
171 | p->height = (8 * p->ver + p->pad * 2); | ||
172 | if(!(p->output = malloc(p->width * p->height * sizeof(Uint16)))) | ||
173 | return 0; | ||
174 | if(!(p->bg = malloc(p->width * p->height * sizeof(Uint8) / 4))) | ||
175 | return 0; | ||
176 | if(!(p->fg = malloc(p->width * p->height * sizeof(Uint8) / 4))) | ||
177 | return 0; | ||
178 | clear(p); | ||
179 | return 1; | ||
180 | } | ||
diff --git a/src/uxn/devices/ppu.h b/src/uxn/devices/ppu.h new file mode 100644 index 0000000..187d364 --- /dev/null +++ b/src/uxn/devices/ppu.h | |||
@@ -0,0 +1,36 @@ | |||
1 | #ifndef UXNGBA_PPU_H | ||
2 | #define UXNGBA_PPU_H | ||
3 | |||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | |||
7 | /* | ||
8 | Copyright (c) 2021 Devine Lu Linvega | ||
9 | Copyright (c) 2021 Andrew Alderwick | ||
10 | |||
11 | Permission to use, copy, modify, and distribute this software for any | ||
12 | purpose with or without fee is hereby granted, provided that the above | ||
13 | copyright notice and this permission notice appear in all copies. | ||
14 | |||
15 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
16 | WITH REGARD TO THIS SOFTWARE. | ||
17 | */ | ||
18 | |||
19 | typedef unsigned char Uint8; | ||
20 | typedef unsigned short Uint16; | ||
21 | typedef unsigned int Uint32; | ||
22 | |||
23 | typedef struct Ppu { | ||
24 | Uint8 *bg, *fg; | ||
25 | Uint16 hor, ver, pad, width, height; | ||
26 | Uint16 *output, colors[4]; | ||
27 | } Ppu; | ||
28 | |||
29 | int initppu(Ppu *p, Uint8 hor, Uint8 ver, Uint8 pad); | ||
30 | void putcolors(Ppu *p, Uint8 *addr); | ||
31 | void putpixel(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 color); | ||
32 | void puticn(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy); | ||
33 | void putchr(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy); | ||
34 | void drawppu(Ppu *p); | ||
35 | void drawdebugger(Ppu *p, Uint8 *stack, Uint8 ptr); | ||
36 | #endif // UXNGBA_PPU_H | ||
diff --git a/src/uxn/roms/console.c b/src/uxn/roms/console.c new file mode 100644 index 0000000..13d669a --- /dev/null +++ b/src/uxn/roms/console.c | |||
@@ -0,0 +1,5 @@ | |||
1 | u16 uxn_rom[] = { | ||
2 | 0x0121, 0x9411, 0x1801, 0x2117, 0x0100, 0x9438, 0xf401, 0x230d, | ||
3 | 0x4800, 0x6c65, 0x6f6c, 0x5720, 0x726f, 0x646c, 0x6620, 0x6f72, | ||
4 | 0x206d, 0x5855, 0x214e, 0x000d | ||
5 | }; | ||
diff --git a/src/uxn/roms/dvd.c b/src/uxn/roms/dvd.c new file mode 100644 index 0000000..2cccc84 --- /dev/null +++ b/src/uxn/roms/dvd.c | |||
@@ -0,0 +1,22 @@ | |||
1 | u16 uxn_rom[] = { | ||
2 | 0x4c21, 0x01fd, 0x3708, 0x4c21, 0x01f3, 0x370a, 0xdc21, 0x01f2, | ||
3 | 0x370c, 0x0121, 0x0133, 0x3720, 0x2201, 0x2136, 0x0200, 0x013b, | ||
4 | 0x3100, 0x2401, 0x2136, 0x0200, 0x013b, 0x3102, 0x2101, 0x0121, | ||
5 | 0x2eb2, 0x0100, 0x2120, 0xb201, 0x012e, 0x3000, 0x2201, 0x2136, | ||
6 | 0x2000, 0x2839, 0x0001, 0x2130, 0x0000, 0x2128, 0x0000, 0x0128, | ||
7 | 0x0d09, 0x0401, 0x0110, 0x0800, 0x0401, 0x0111, 0x3002, 0x2401, | ||
8 | 0x2136, 0x1000, 0x2839, 0x0201, 0x2130, 0x0000, 0x2128, 0x0000, | ||
9 | 0x0128, 0x0d09, 0x0501, 0x0110, 0x0800, 0x0501, 0x0111, 0x3000, | ||
10 | 0x0021, 0x0101, 0x0100, 0x1004, 0x0001, 0x2108, 0xfeff, 0x383a, | ||
11 | 0x0138, 0x3100, 0x0201, 0x2130, 0x0100, 0x0001, 0x0501, 0x0110, | ||
12 | 0x0800, 0xff21, 0x3afe, 0x3838, 0x0201, 0x0131, 0x2121, 0xb201, | ||
13 | 0x002e, 0x210f, 0xfd01, 0x2c01, 0x0137, 0x3002, 0x0201, 0x2130, | ||
14 | 0x1000, 0x2638, 0x2a01, 0x0137, 0x3000, 0x0001, 0x2130, 0x2000, | ||
15 | 0x2638, 0x2801, 0xcf37, 0x2e01, 0x0117, 0x362c, 0x0021, 0x3808, | ||
16 | 0x2c01, 0x2537, 0x0021, 0x3808, 0xab25, 0xe401, 0x230d, 0x2523, | ||
17 | 0x0021, 0x3808, 0xab25, 0xca01, 0x230d, 0x4323, 0x006c, 0x3f1f, | ||
18 | 0x3838, 0x7838, 0x007f, 0xfefe, 0x777e, 0xe377, 0x00c3, 0x1f0f, | ||
19 | 0x7b3b, 0xe777, 0x00c7, 0xfefc, 0x878f, 0x0e07, 0x7ffc, 0x0000, | ||
20 | 0xff0f, 0x077f, 0x0300, 0x0001, 0xf0ff, 0xfff8, 0x8700, 0x0000, | ||
21 | 0x7fff, 0xff7f, 0xf000, 0x0000, 0xfce0, 0x80fc, 0x0a00, | ||
22 | }; | ||
diff --git a/src/uxn/uxn.c b/src/uxn/uxn.c new file mode 100644 index 0000000..796a980 --- /dev/null +++ b/src/uxn/uxn.c | |||
@@ -0,0 +1,196 @@ | |||
1 | #include <stdio.h> | ||
2 | #include "uxn.h" | ||
3 | |||
4 | /* | ||
5 | Copyright (u) 2021 Devine Lu Linvega | ||
6 | |||
7 | Permission to use, copy, modify, and distribute this software for any | ||
8 | purpose with or without fee is hereby granted, provided that the above | ||
9 | copyright notice and this permission notice appear in all copies. | ||
10 | |||
11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
12 | WITH REGARD TO THIS SOFTWARE. | ||
13 | */ | ||
14 | |||
15 | #pragma mark - Operations | ||
16 | |||
17 | /* clang-format off */ | ||
18 | void push8(Stack *s, Uint8 a) { if (s->ptr == 0xff) { s->error = 2; return; } s->dat[s->ptr++] = a; } | ||
19 | Uint8 pop8_keep(Stack *s) { if (s->kptr == 0) { s->error = 1; return 0; } return s->dat[--s->kptr]; } | ||
20 | Uint8 pop8_nokeep(Stack *s) { if (s->ptr == 0) { s->error = 1; return 0; } return s->dat[--s->ptr]; } | ||
21 | static Uint8 (*pop8)(Stack *s); | ||
22 | void mempoke8(Uint8 *m, Uint16 a, Uint8 b) { m[a] = b; } | ||
23 | Uint8 mempeek8(Uint8 *m, Uint16 a) { return m[a]; } | ||
24 | void devpoke8(Device *d, Uint8 a, Uint8 b) { d->dat[a & 0xf] = b; d->talk(d, a & 0x0f, 1); } | ||
25 | Uint8 devpeek8(Device *d, Uint8 a) { d->talk(d, a & 0x0f, 0); return d->dat[a & 0xf]; } | ||
26 | void push16(Stack *s, Uint16 a) { push8(s, a >> 8); push8(s, a); } | ||
27 | Uint16 pop16(Stack *s) { return pop8(s) + (pop8(s) << 8); } | ||
28 | void mempoke16(Uint8 *m, Uint16 a, Uint16 b) { mempoke8(m, a, b >> 8); mempoke8(m, a + 1, b); } | ||
29 | Uint16 mempeek16(Uint8 *m, Uint16 a) { return (mempeek8(m, a) << 8) + mempeek8(m, a + 1); } | ||
30 | void devpoke16(Device *d, Uint8 a, Uint16 b) { devpoke8(d, a, b >> 8); devpoke8(d, a + 1, b); } | ||
31 | Uint16 devpeek16(Device *d, Uint16 a) { return (devpeek8(d, a) << 8) + devpeek8(d, a + 1); } | ||
32 | /* Stack */ | ||
33 | void op_brk(Uxn *u) { u->ram.ptr = 0; } | ||
34 | void op_nop(Uxn *u) { (void)u; } | ||
35 | void op_lit(Uxn *u) { push8(u->src, mempeek8(u->ram.dat, u->ram.ptr++)); } | ||
36 | void op_pop(Uxn *u) { pop8(u->src); } | ||
37 | void op_dup(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, a); push8(u->src, a); } | ||
38 | void op_swp(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, a); push8(u->src, b); } | ||
39 | void op_ovr(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, b); } | ||
40 | void op_rot(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, c); } | ||
41 | /* Logic */ | ||
42 | void op_equ(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b == a); } | ||
43 | void op_neq(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b != a); } | ||
44 | void op_gth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b > a); } | ||
45 | void op_lth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b < a); } | ||
46 | void op_jmp(Uxn *u) { Uint8 a = pop8(u->src); u->ram.ptr += (Sint8)a; } | ||
47 | void op_jnz(Uxn *u) { Uint8 a = pop8(u->src); if (pop8(u->src)) u->ram.ptr += (Sint8)a; } | ||
48 | void op_jsr(Uxn *u) { Uint8 a = pop8(u->src); push16(u->dst, u->ram.ptr); u->ram.ptr += (Sint8)a; } | ||
49 | void op_sth(Uxn *u) { Uint8 a = pop8(u->src); push8(u->dst, a); } | ||
50 | /* Memory */ | ||
51 | void op_pek(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, mempeek8(u->ram.dat, a)); } | ||
52 | void op_pok(Uxn *u) { Uint8 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, a, b); } | ||
53 | void op_ldr(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, mempeek8(u->ram.dat, u->ram.ptr + (Sint8)a)); } | ||
54 | void op_str(Uxn *u) { Uint8 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, u->ram.ptr + (Sint8)a, b); } | ||
55 | void op_lda(Uxn *u) { Uint16 a = pop16(u->src); push8(u->src, mempeek8(u->ram.dat, a)); } | ||
56 | void op_sta(Uxn *u) { Uint16 a = pop16(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, a, b); } | ||
57 | void op_dei(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, devpeek8(&u->dev[a >> 4], a)); } | ||
58 | void op_deo(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); devpoke8(&u->dev[a >> 4], a, b); } | ||
59 | /* Arithmetic */ | ||
60 | void op_add(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b + a); } | ||
61 | void op_sub(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b - a); } | ||
62 | void op_mul(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b * a); } | ||
63 | void op_div(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b / a); } | ||
64 | void op_and(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b & a); } | ||
65 | void op_ora(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b | a); } | ||
66 | void op_eor(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b ^ a); } | ||
67 | void op_sft(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b >> (a & 0x07) << ((a & 0x70) >> 4)); } | ||
68 | /* Stack */ | ||
69 | void op_lit16(Uxn *u) { push16(u->src, mempeek16(u->ram.dat, u->ram.ptr++)); u->ram.ptr++; } | ||
70 | void op_pop16(Uxn *u) { pop16(u->src); } | ||
71 | void op_dup16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, a); push16(u->src, a); } | ||
72 | void op_swp16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, a); push16(u->src, b); } | ||
73 | void op_ovr16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b); push16(u->src, a); push16(u->src, b); } | ||
74 | void op_rot16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src), c = pop16(u->src); push16(u->src, b); push16(u->src, a); push16(u->src, c); } | ||
75 | /* Logic(16-bits) */ | ||
76 | void op_equ16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b == a); } | ||
77 | void op_neq16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b != a); } | ||
78 | void op_gth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b > a); } | ||
79 | void op_lth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b < a); } | ||
80 | void op_jmp16(Uxn *u) { u->ram.ptr = pop16(u->src); } | ||
81 | void op_jnz16(Uxn *u) { Uint16 a = pop16(u->src); if (pop8(u->src)) u->ram.ptr = a; } | ||
82 | void op_jsr16(Uxn *u) { push16(u->dst, u->ram.ptr); u->ram.ptr = pop16(u->src); } | ||
83 | void op_sth16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->dst, a); } | ||
84 | /* Memory(16-bits) */ | ||
85 | void op_pek16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, mempeek16(u->ram.dat, a)); } | ||
86 | void op_pok16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, a, b); } | ||
87 | void op_ldr16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, mempeek16(u->ram.dat, u->ram.ptr + (Sint8)a)); } | ||
88 | void op_str16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, u->ram.ptr + (Sint8)a, b); } | ||
89 | void op_lda16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, mempeek16(u->ram.dat, a)); } | ||
90 | void op_sta16(Uxn *u) { Uint16 a = pop16(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, a, b); } | ||
91 | void op_dei16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, devpeek16(&u->dev[a >> 4], a)); } | ||
92 | void op_deo16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); devpoke16(&u->dev[a >> 4], a, b); } | ||
93 | /* Arithmetic(16-bits) */ | ||
94 | void op_add16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b + a); } | ||
95 | void op_sub16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b - a); } | ||
96 | void op_mul16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b * a); } | ||
97 | void op_div16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b / a); } | ||
98 | void op_and16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b & a); } | ||
99 | void op_ora16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b | a); } | ||
100 | void op_eor16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b ^ a); } | ||
101 | void op_sft16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b >> (a & 0x000f) << ((a & 0x00f0) >> 4)); } | ||
102 | |||
103 | void (*ops[])(Uxn *u) = { | ||
104 | op_brk, op_lit, op_nop, op_pop, op_dup, op_swp, op_ovr, op_rot, | ||
105 | op_equ, op_neq, op_gth, op_lth, op_jmp, op_jnz, op_jsr, op_sth, | ||
106 | op_pek, op_pok, op_ldr, op_str, op_lda, op_sta, op_dei, op_deo, | ||
107 | op_add, op_sub, op_mul, op_div, op_and, op_ora, op_eor, op_sft, | ||
108 | /* 16-bit */ | ||
109 | op_brk, op_lit16, op_nop, op_pop16, op_dup16, op_swp16, op_ovr16, op_rot16, | ||
110 | op_equ16, op_neq16, op_gth16, op_lth16, op_jmp16, op_jnz16, op_jsr16, op_sth16, | ||
111 | op_pek16, op_pok16, op_ldr16, op_str16, op_lda16, op_sta16, op_dei16, op_deo16, | ||
112 | op_add16, op_sub16, op_mul16, op_div16, op_and16, op_ora16, op_eor16, op_sft16 | ||
113 | }; | ||
114 | |||
115 | /* clang-format on */ | ||
116 | |||
117 | #pragma mark - Core | ||
118 | |||
119 | int | ||
120 | haltuxn(Uxn *u, char *name, int id) | ||
121 | { | ||
122 | txt_printf("Halted: %s#%04x, at 0x%04x\n", name, id, u->ram.ptr); | ||
123 | u->ram.ptr = 0; | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | void | ||
128 | opcuxn(Uxn *u, Uint8 instr) | ||
129 | { | ||
130 | Uint8 op = instr & 0x3f, freturn = instr & 0x40, fkeep = instr & 0x80; | ||
131 | u->src = freturn ? &u->rst : &u->wst; | ||
132 | u->dst = freturn ? &u->wst : &u->rst; | ||
133 | if(fkeep) { | ||
134 | pop8 = pop8_keep; | ||
135 | u->src->kptr = u->src->ptr; | ||
136 | } else { | ||
137 | pop8 = pop8_nokeep; | ||
138 | } | ||
139 | (*ops[op])(u); | ||
140 | } | ||
141 | |||
142 | int | ||
143 | stepuxn(Uxn *u, Uint8 instr) | ||
144 | { | ||
145 | opcuxn(u, instr); | ||
146 | if(u->wst.error) | ||
147 | return haltuxn(u, u->wst.error == 1 ? "Working-stack underflow" : "Working-stack overflow", instr); | ||
148 | if(u->rst.error) | ||
149 | return haltuxn(u, u->rst.error == 1 ? "Return-stack underflow" : "Return-stack overflow", instr); | ||
150 | return 1; | ||
151 | } | ||
152 | |||
153 | int | ||
154 | evaluxn(Uxn *u, Uint16 vec) | ||
155 | { | ||
156 | u->ram.ptr = vec; | ||
157 | u->wst.error = 0; | ||
158 | u->rst.error = 0; | ||
159 | while(u->ram.ptr) | ||
160 | if(!stepuxn(u, u->ram.dat[u->ram.ptr++])) | ||
161 | return 0; | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | int | ||
166 | bootuxn(Uxn *u) | ||
167 | { | ||
168 | size_t i; | ||
169 | char *cptr = (char *)u; | ||
170 | for(i = 0; i < sizeof(*u); i++) | ||
171 | cptr[i] = 0; | ||
172 | return 1; | ||
173 | } | ||
174 | |||
175 | int | ||
176 | loaduxn(Uxn *u, char *filepath) | ||
177 | { | ||
178 | FILE *f; | ||
179 | if(!(f = fopen(filepath, "rb"))) | ||
180 | return haltuxn(u, "Missing input rom.", 0); | ||
181 | fread(u->ram.dat + PAGE_PROGRAM, sizeof(u->ram.dat) - PAGE_PROGRAM, 1, f); | ||
182 | txt_printf("Uxn loaded[%s].\n", filepath); | ||
183 | return 1; | ||
184 | } | ||
185 | |||
186 | Device * | ||
187 | portuxn(Uxn *u, Uint8 id, char *name, void (*talkfn)(Device *d, Uint8 b0, Uint8 w)) | ||
188 | { | ||
189 | Device *d = &u->dev[id]; | ||
190 | d->addr = id * 0x10; | ||
191 | d->u = u; | ||
192 | d->mem = u->ram.dat; | ||
193 | d->talk = talkfn; | ||
194 | txt_printf("Device added #%02x: %s, at 0x%04x \n", id, name, d->addr); | ||
195 | return d; | ||
196 | } | ||
diff --git a/src/uxn/uxn.h b/src/uxn/uxn.h new file mode 100644 index 0000000..b2c90ac --- /dev/null +++ b/src/uxn/uxn.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #ifndef UXNGBA_UXN_H | ||
2 | #define UXNGBA_UXN_H | ||
3 | |||
4 | #include <stdio.h> | ||
5 | |||
6 | /* | ||
7 | Copyright (c) 2021 Devine Lu Linvega | ||
8 | |||
9 | Permission to use, copy, modify, and distribute this software for any | ||
10 | purpose with or without fee is hereby granted, provided that the above | ||
11 | copyright notice and this permission notice appear in all copies. | ||
12 | |||
13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
14 | WITH REGARD TO THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | typedef unsigned char Uint8; | ||
18 | typedef signed char Sint8; | ||
19 | typedef unsigned short Uint16; | ||
20 | typedef signed short Sint16; | ||
21 | |||
22 | #define PAGE_PROGRAM 0x0100 | ||
23 | |||
24 | typedef struct { | ||
25 | Uint8 ptr, kptr, error; | ||
26 | Uint8 dat[256]; | ||
27 | } Stack; | ||
28 | |||
29 | typedef struct { | ||
30 | Uint16 ptr; | ||
31 | Uint8 dat[KB(16)]; | ||
32 | } Memory; | ||
33 | |||
34 | typedef struct Device { | ||
35 | struct Uxn *u; | ||
36 | Uint8 addr, dat[16], *mem; | ||
37 | void (*talk)(struct Device *d, Uint8, Uint8); | ||
38 | } Device; | ||
39 | |||
40 | typedef struct Uxn { | ||
41 | Stack wst, rst, *src, *dst; | ||
42 | Memory ram; | ||
43 | Device dev[16]; | ||
44 | } Uxn; | ||
45 | |||
46 | struct Uxn; | ||
47 | |||
48 | void mempoke16(Uint8 *m, Uint16 a, Uint16 b); | ||
49 | Uint16 mempeek16(Uint8 *m, Uint16 a); | ||
50 | |||
51 | int loaduxn(Uxn *c, char *filepath); | ||
52 | int bootuxn(Uxn *c); | ||
53 | int evaluxn(Uxn *u, Uint16 vec); | ||
54 | Device *portuxn(Uxn *u, Uint8 id, char *name, void (*talkfn)(Device *, Uint8, Uint8)); | ||
55 | #endif // UXNGBA_UXN_H | ||