aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-18 16:40:24 +0200
committerBad Diode <bd@badd10de.dev>2021-05-18 16:40:24 +0200
commit0c7265cf0de9d4ec95d28c5e103c00a63f4a1697 (patch)
tree4a1145e849e078395430a8d718c4bd69a06fb29f
downloaduxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.tar.gz
uxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.zip
Proof of concept of UXN on the GBA
-rw-r--r--.gitignore1
-rw-r--r--Makefile74
-rw-r--r--src/bd-font.c130
-rw-r--r--src/bios_calls.s337
-rw-r--r--src/bitmap.h209
-rw-r--r--src/common.h696
-rw-r--r--src/irs.s89
-rw-r--r--src/main.c155
-rw-r--r--src/shorthand.h36
-rw-r--r--src/small-font.c45
-rw-r--r--src/sprites.h79
-rw-r--r--src/text.h232
-rw-r--r--src/uxn/devices/ppu.c180
-rw-r--r--src/uxn/devices/ppu.h36
-rw-r--r--src/uxn/roms/console.c5
-rw-r--r--src/uxn/roms/dvd.c22
-rw-r--r--src/uxn/uxn.c196
-rw-r--r--src/uxn/uxn.h55
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.
5DEVKITPRO := /opt/devkitpro
6DEVKITARM := /opt/devkitpro/devkitARM
7PATH := $(DEVKITARM)/bin:$(PATH)
8LIBGBA_DIR := $(DEVKITPRO)/libgba
9LIBGBA_SRC := /opt/devkitpro/libgba/include/
10LIBGBA := $(LIBGBA_DIR)/lib/libgba.a
11LIBGBA += $(LIBGBA_DIR)/lib/libfat.a
12LIBGBA += $(LIBGBA_DIR)/lib/libmm.a
13
14# Source code location and files to watch for changes.
15SRC_DIR := src
16SRC_MAIN := $(SRC_DIR)/main.c
17ASM_FILES := $(wildcard $(SRC_DIR)/*.s)
18WATCH_SRC := $(wildcard $(SRC_DIR)/*.c)
19WATCH_SRC += $(wildcard $(SRC_DIR)/*.h)
20WATCH_SRC += $(wildcard $(SRC_DIR)/*.s)
21
22# Output library names and executables.
23TARGET := uxngba
24BUILD_DIR := build
25ELF := $(BUILD_DIR)/$(TARGET).elf
26BIN := $(BUILD_DIR)/$(TARGET).gba
27
28# Compiler and linker configuration.
29CC := arm-none-eabi-gcc
30OBJCOPY := arm-none-eabi-objcopy
31ARCH := -mthumb -mthumb-interwork
32SPECS := -specs=gba.specs
33CFLAGS := -Wall -Wextra -pedantic -fno-strict-aliasing -Wno-incompatible-pointer-types
34CFLAGS += -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH)
35CFLAGS += -I$(LIBGBA_SRC)
36LDFLAGS := $(ARCH) $(SPECS)
37LDLIBS := $(LIBGBA)
38RELEASE_CFLAGS := -DNDEBUG -O2 -g
39DEBUG_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
46DEBUG ?= 0
47ifeq ($(DEBUG), 1)
48 CFLAGS += $(DEBUG_CFLAGS)
49else
50 CFLAGS += $(RELEASE_CFLAGS)
51endif
52
53main: $(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.
69run: main
70 mgba-qt $(BIN)
71
72# Remove build directory.
73clean:
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 @@
1u32 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
11bios_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
21bios_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
31bios_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
41bios_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
55bios_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
65bios_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
79bios_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
89bios_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
99bios_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
109bios_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
119bios_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
129bios_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
143bios_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
153bios_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
167bios_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
177bios_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
187bios_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
197bios_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
207bios_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
217bios_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
227bios_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
237bios_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
251bios_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
261bios_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
271bios_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
281bios_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
291bios_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
301bios_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
311bios_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
325bios_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
335bios_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.
8static void
9draw_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
71static inline void
72draw_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
97static inline void
98draw_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.
127static inline void
128put_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
141static inline void
142put_pixel_m3(int x, int y, u16 color, Scanline *buffer) {
143 buffer[y][x] = color;
144}
145
146static inline void
147clear_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
155static inline void
156clear_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
164static inline void
165draw_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
174void
175draw_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).
92typedef u16 Color;
93
94// A palette is composed of 16 colors, with color at index 0 being transparent
95// for sprites.
96typedef Color Palette[16];
97
98// Inline function to calculate the 15 bit color value.
99static inline Color
100rgb15(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.
117typedef struct Tile {
118 u32 row[8];
119} Tile;
120
121// Screenblocks and charblocks (tile blocks).
122typedef Tile TileBlock[512];
123#define TILE_MEM ((TileBlock*) MEM_VRAM)
124typedef 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
132size_t
133se_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//
143typedef 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))
150static 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
200static inline void
201flip_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.
235static inline
236void 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
245static inline
246u32 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.
279static u16 key_curr = 0;
280static u16 key_prev = 0;
281
282static inline void
283poll_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.
292static inline u32
293key_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.
298static inline u32
299key_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.
307static inline u32
308unpack_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.
321static inline void
322unpack_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.
362typedef 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.
373inline void
374dma_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
381inline void
382dma_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.
390inline void
391dma_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.
396inline void
397dma_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
410typedef 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
427typedef void (*IrsFunc)(void);
428
429IrsFunc 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
446void
447irq_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
467void
468irq_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
488void
489irs_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.
507void 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)
512void
513irq_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.
520void
521irs_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.
529int bios_vblank_wait();
530int 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
564typedef enum {
565 SOUND_SQUARE1 = (0x1 << 0),
566 SOUND_SQUARE2 = (0x1 << 1),
567 SOUND_WAVE = (0x1 << 2),
568 SOUND_NOISE = (0x1 << 3),
569} SoundChannel;
570
571u16
572sound_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
631typedef u8 WaveBank[32];
632
633// typedef u32 WaveBank[4];
634#define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90))
635
636typedef 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
658const 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
668const 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.
685static inline void
686wait_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
8irs_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
22irs_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
34irs_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
41irs_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
80irs_main_exit:
81 @ Restore IRQ_CTRL.
82 mov r3, #1 @ r3 = 0
83 strh r3, [ip, #8] @ *(ip + 0x8) = r3
84 bx lr
85
86mem_irq_base_reg:
87 .word 0x04000200 @ IRQ_ENABLE
88mem_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
18static Ppu ppu;
19u8 reqdraw = 0;
20static Device *devscreen;
21
22void
23nil_talk(Device *d, Uint8 b0, Uint8 w) {
24 (void)d;
25 (void)b0;
26 (void)w;
27}
28
29void
30console_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
42void
43system_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
55void
56screen_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
74void
75redraw(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
89void
90init_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
118int 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
14typedef uint8_t u8;
15typedef uint16_t u16;
16typedef uint32_t u32;
17typedef uint64_t u64;
18typedef int8_t s8;
19typedef int16_t s16;
20typedef int32_t s32;
21typedef int64_t s64;
22typedef volatile u8 vu8;
23typedef volatile u16 vu16;
24typedef volatile u32 vu32;
25typedef volatile u64 vu64;
26typedef volatile s8 vs8;
27typedef volatile s16 vs16;
28typedef volatile s32 vs32;
29typedef 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 @@
1u32 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
36u8 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
6typedef 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
19Sprite sprites[NUM_SPRITES];
20
21// Keeping track of unique sprites and current sprite memory pointer using
22// global singletons.
23size_t sprite_counter = 0;
24size_t sprite_tile_counter = 0;
25u32 *sprite_memory = NULL;
26
27// Loads the sprite data into video memory and initialize the Sprite structure.
28size_t
29load_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
42size_t
43load_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
56void
57init_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
70void
71init_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
10typedef enum {
11 TXT_MODE_TILED_BG,
12 TXT_MODE_MODE3,
13} TextMode;
14
15typedef 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
33typedef 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
53static TextEngine text_engine = {0};
54
55static u8 default_char_map[256] = {0};
56
57void
58txt_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
78void
79txt_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
117void
118txt_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
129static inline void
130txt_puts(char *msg) {
131 while (*msg) {
132 txt_putc(*msg++);
133 }
134}
135
136void
137txt_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
165void
166txt_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.
195void
196txt_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.
205void
206txt_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.
214void
215txt_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
225void
226txt_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/*
4Copyright (c) 2021 Devine Lu Linvega
5Copyright (c) 2021 Andrew Alderwick
6
7Permission to use, copy, modify, and distribute this software for any
8purpose with or without fee is hereby granted, provided that the above
9copyright notice and this permission notice appear in all copies.
10
11THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12WITH REGARD TO THIS SOFTWARE.
13*/
14
15static 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
33Uint8
34readpixel(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
41void
42clear(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
53void
54putcolors(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
70void
71putpixel(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
86void
87puticn(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
102void
103putchr(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
120void
121drawpixel(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
127void
128drawdebugger(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
146void
147drawppu(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
164int
165initppu(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/*
8Copyright (c) 2021 Devine Lu Linvega
9Copyright (c) 2021 Andrew Alderwick
10
11Permission to use, copy, modify, and distribute this software for any
12purpose with or without fee is hereby granted, provided that the above
13copyright notice and this permission notice appear in all copies.
14
15THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16WITH REGARD TO THIS SOFTWARE.
17*/
18
19typedef unsigned char Uint8;
20typedef unsigned short Uint16;
21typedef unsigned int Uint32;
22
23typedef struct Ppu {
24 Uint8 *bg, *fg;
25 Uint16 hor, ver, pad, width, height;
26 Uint16 *output, colors[4];
27} Ppu;
28
29int initppu(Ppu *p, Uint8 hor, Uint8 ver, Uint8 pad);
30void putcolors(Ppu *p, Uint8 *addr);
31void putpixel(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 color);
32void puticn(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy);
33void putchr(Ppu *p, Uint8 *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy);
34void drawppu(Ppu *p);
35void 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 @@
1u16 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 @@
1u16 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/*
5Copyright (u) 2021 Devine Lu Linvega
6
7Permission to use, copy, modify, and distribute this software for any
8purpose with or without fee is hereby granted, provided that the above
9copyright notice and this permission notice appear in all copies.
10
11THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12WITH REGARD TO THIS SOFTWARE.
13*/
14
15#pragma mark - Operations
16
17/* clang-format off */
18void push8(Stack *s, Uint8 a) { if (s->ptr == 0xff) { s->error = 2; return; } s->dat[s->ptr++] = a; }
19Uint8 pop8_keep(Stack *s) { if (s->kptr == 0) { s->error = 1; return 0; } return s->dat[--s->kptr]; }
20Uint8 pop8_nokeep(Stack *s) { if (s->ptr == 0) { s->error = 1; return 0; } return s->dat[--s->ptr]; }
21static Uint8 (*pop8)(Stack *s);
22void mempoke8(Uint8 *m, Uint16 a, Uint8 b) { m[a] = b; }
23Uint8 mempeek8(Uint8 *m, Uint16 a) { return m[a]; }
24void devpoke8(Device *d, Uint8 a, Uint8 b) { d->dat[a & 0xf] = b; d->talk(d, a & 0x0f, 1); }
25Uint8 devpeek8(Device *d, Uint8 a) { d->talk(d, a & 0x0f, 0); return d->dat[a & 0xf]; }
26void push16(Stack *s, Uint16 a) { push8(s, a >> 8); push8(s, a); }
27Uint16 pop16(Stack *s) { return pop8(s) + (pop8(s) << 8); }
28void mempoke16(Uint8 *m, Uint16 a, Uint16 b) { mempoke8(m, a, b >> 8); mempoke8(m, a + 1, b); }
29Uint16 mempeek16(Uint8 *m, Uint16 a) { return (mempeek8(m, a) << 8) + mempeek8(m, a + 1); }
30void devpoke16(Device *d, Uint8 a, Uint16 b) { devpoke8(d, a, b >> 8); devpoke8(d, a + 1, b); }
31Uint16 devpeek16(Device *d, Uint16 a) { return (devpeek8(d, a) << 8) + devpeek8(d, a + 1); }
32/* Stack */
33void op_brk(Uxn *u) { u->ram.ptr = 0; }
34void op_nop(Uxn *u) { (void)u; }
35void op_lit(Uxn *u) { push8(u->src, mempeek8(u->ram.dat, u->ram.ptr++)); }
36void op_pop(Uxn *u) { pop8(u->src); }
37void op_dup(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, a); push8(u->src, a); }
38void op_swp(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, a); push8(u->src, b); }
39void 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); }
40void 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 */
42void op_equ(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b == a); }
43void op_neq(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b != a); }
44void op_gth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b > a); }
45void op_lth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b < a); }
46void op_jmp(Uxn *u) { Uint8 a = pop8(u->src); u->ram.ptr += (Sint8)a; }
47void op_jnz(Uxn *u) { Uint8 a = pop8(u->src); if (pop8(u->src)) u->ram.ptr += (Sint8)a; }
48void op_jsr(Uxn *u) { Uint8 a = pop8(u->src); push16(u->dst, u->ram.ptr); u->ram.ptr += (Sint8)a; }
49void op_sth(Uxn *u) { Uint8 a = pop8(u->src); push8(u->dst, a); }
50/* Memory */
51void op_pek(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, mempeek8(u->ram.dat, a)); }
52void op_pok(Uxn *u) { Uint8 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, a, b); }
53void op_ldr(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, mempeek8(u->ram.dat, u->ram.ptr + (Sint8)a)); }
54void op_str(Uxn *u) { Uint8 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, u->ram.ptr + (Sint8)a, b); }
55void op_lda(Uxn *u) { Uint16 a = pop16(u->src); push8(u->src, mempeek8(u->ram.dat, a)); }
56void op_sta(Uxn *u) { Uint16 a = pop16(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, a, b); }
57void op_dei(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, devpeek8(&u->dev[a >> 4], a)); }
58void op_deo(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); devpoke8(&u->dev[a >> 4], a, b); }
59/* Arithmetic */
60void op_add(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b + a); }
61void op_sub(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b - a); }
62void op_mul(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b * a); }
63void op_div(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b / a); }
64void op_and(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b & a); }
65void op_ora(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b | a); }
66void op_eor(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b ^ a); }
67void op_sft(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b >> (a & 0x07) << ((a & 0x70) >> 4)); }
68/* Stack */
69void op_lit16(Uxn *u) { push16(u->src, mempeek16(u->ram.dat, u->ram.ptr++)); u->ram.ptr++; }
70void op_pop16(Uxn *u) { pop16(u->src); }
71void op_dup16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, a); push16(u->src, a); }
72void op_swp16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, a); push16(u->src, b); }
73void 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); }
74void 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) */
76void op_equ16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b == a); }
77void op_neq16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b != a); }
78void op_gth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b > a); }
79void op_lth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b < a); }
80void op_jmp16(Uxn *u) { u->ram.ptr = pop16(u->src); }
81void op_jnz16(Uxn *u) { Uint16 a = pop16(u->src); if (pop8(u->src)) u->ram.ptr = a; }
82void op_jsr16(Uxn *u) { push16(u->dst, u->ram.ptr); u->ram.ptr = pop16(u->src); }
83void op_sth16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->dst, a); }
84/* Memory(16-bits) */
85void op_pek16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, mempeek16(u->ram.dat, a)); }
86void op_pok16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, a, b); }
87void op_ldr16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, mempeek16(u->ram.dat, u->ram.ptr + (Sint8)a)); }
88void op_str16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, u->ram.ptr + (Sint8)a, b); }
89void op_lda16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, mempeek16(u->ram.dat, a)); }
90void op_sta16(Uxn *u) { Uint16 a = pop16(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, a, b); }
91void op_dei16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, devpeek16(&u->dev[a >> 4], a)); }
92void 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) */
94void op_add16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b + a); }
95void op_sub16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b - a); }
96void op_mul16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b * a); }
97void op_div16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b / a); }
98void op_and16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b & a); }
99void op_ora16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b | a); }
100void op_eor16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b ^ a); }
101void op_sft16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b >> (a & 0x000f) << ((a & 0x00f0) >> 4)); }
102
103void (*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
119int
120haltuxn(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
127void
128opcuxn(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
142int
143stepuxn(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
153int
154evaluxn(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
165int
166bootuxn(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
175int
176loaduxn(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
186Device *
187portuxn(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/*
7Copyright (c) 2021 Devine Lu Linvega
8
9Permission to use, copy, modify, and distribute this software for any
10purpose with or without fee is hereby granted, provided that the above
11copyright notice and this permission notice appear in all copies.
12
13THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14WITH REGARD TO THIS SOFTWARE.
15*/
16
17typedef unsigned char Uint8;
18typedef signed char Sint8;
19typedef unsigned short Uint16;
20typedef signed short Sint16;
21
22#define PAGE_PROGRAM 0x0100
23
24typedef struct {
25 Uint8 ptr, kptr, error;
26 Uint8 dat[256];
27} Stack;
28
29typedef struct {
30 Uint16 ptr;
31 Uint8 dat[KB(16)];
32} Memory;
33
34typedef struct Device {
35 struct Uxn *u;
36 Uint8 addr, dat[16], *mem;
37 void (*talk)(struct Device *d, Uint8, Uint8);
38} Device;
39
40typedef struct Uxn {
41 Stack wst, rst, *src, *dst;
42 Memory ram;
43 Device dev[16];
44} Uxn;
45
46struct Uxn;
47
48void mempoke16(Uint8 *m, Uint16 a, Uint16 b);
49Uint16 mempeek16(Uint8 *m, Uint16 a);
50
51int loaduxn(Uxn *c, char *filepath);
52int bootuxn(Uxn *c);
53int evaluxn(Uxn *u, Uint16 vec);
54Device *portuxn(Uxn *u, Uint8 id, char *name, void (*talkfn)(Device *, Uint8, Uint8));
55#endif // UXNGBA_UXN_H