summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-06-03 11:24:31 +0200
committerBad Diode <bd@badd10de.dev>2021-06-03 11:24:31 +0200
commit0eda19bd2bb551bf9186b0fbe1a806a28d5a3597 (patch)
tree859aad06f65f2502a13d26c372c4978147fa1864
parent6b68cbd8a25dc2e554cf6741ff61ef87c030d841 (diff)
downloadgba-sequencer-0eda19bd2bb551bf9186b0fbe1a806a28d5a3597.tar.gz
gba-sequencer-0eda19bd2bb551bf9186b0fbe1a806a28d5a3597.zip
Add initial text background drawing functions
-rw-r--r--Makefile5
-rw-r--r--src/main.c18
-rw-r--r--src/renderer.c22
-rw-r--r--src/text/posprintf.h1
-rw-r--r--src/text/posprintf.s419
-rw-r--r--src/text/text.h114
6 files changed, 556 insertions, 23 deletions
diff --git a/Makefile b/Makefile
index 2c58d00..369ad26 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,9 @@ BUILD_DIR := build
17SRC_MAIN := $(SRC_DIR)/main.c 17SRC_MAIN := $(SRC_DIR)/main.c
18SRC_BDGBA := $(wildcard $(SRC_DIR)/gba/*.s) 18SRC_BDGBA := $(wildcard $(SRC_DIR)/gba/*.s)
19SRC_BDGBA += $(wildcard $(SRC_DIR)/gba/*.c) 19SRC_BDGBA += $(wildcard $(SRC_DIR)/gba/*.c)
20SRC_BDTEXT := $(wildcard $(SRC_DIR)/text/*.s)
21SRC_BDTEXT += $(wildcard $(SRC_DIR)/text/*.c)
22SRC := $(SRC_MAIN) $(SRC_BDGBA) $(SRC_BDTEXT)
20WATCH_SRC := $(shell find $(SRC_DIR) -name *.c -or -name *.s -or -name *.h) 23WATCH_SRC := $(shell find $(SRC_DIR) -name *.c -or -name *.s -or -name *.h)
21INC_DIRS := $(shell find $(SRC_DIR) -type d) 24INC_DIRS := $(shell find $(SRC_DIR) -type d)
22INC_DIRS += $(BUILD_DIR) 25INC_DIRS += $(BUILD_DIR)
@@ -65,7 +68,7 @@ $(BIN): $(ELF)
65 68
66# Link files. 69# Link files.
67$(ELF): $(SRC_MAIN) $(WATCH_SRC) 70$(ELF): $(SRC_MAIN) $(WATCH_SRC)
68 $(CC) $(CFLAGS) $(LDFLAGS) -o $(ELF) $(SRC_MAIN) $(SRC_BDGBA) $(LDLIBS) 71 $(CC) $(CFLAGS) $(LDFLAGS) -o $(ELF) $(SRC) $(LDLIBS)
69 72
70# Create build directory if needed. 73# Create build directory if needed.
71$(BUILD_DIR): 74$(BUILD_DIR):
diff --git a/src/main.c b/src/main.c
index 1e1ef11..6226bcd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -35,10 +35,14 @@ WITH REGARD TO THIS SOFTWARE.
35 do { \ 35 do { \
36 txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\ 36 txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\
37 txt_printf("EVAL: %lu ", eval_cycles);\ 37 txt_printf("EVAL: %lu ", eval_cycles);\
38 txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\ 38 txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+1);\
39 txt_printf("FLIP: %lu ", flip_cycles);\ 39 txt_printf("FLIP: %lu ", flip_cycles);\
40 txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+2);\
41 txt_printf("FRAME: %lu ", frame_counter);\
42 frame_counter++;\
40 } while (0) 43 } while (0)
41#define PROF_INIT() \ 44#define PROF_INIT() \
45 u32 frame_counter = 0;\
42 u32 eval_cycles = 0;\ 46 u32 eval_cycles = 0;\
43 u32 flip_cycles = 0; 47 u32 flip_cycles = 0;
44#else 48#else
@@ -65,19 +69,19 @@ int main(void) {
65 draw_pixel(10, 1, 2); 69 draw_pixel(10, 1, 2);
66 draw_pixel(10, 2, 3); 70 draw_pixel(10, 2, 3);
67 draw_pixel(10, 3, 4); 71 draw_pixel(10, 3, 4);
68 draw_pixel(0, 0, 1); 72 draw_pixel( 0, 0, 1);
69 draw_pixel(0, 1, 2); 73 draw_pixel( 0, 1, 2);
70 draw_pixel(0, 2, 3); 74 draw_pixel( 0, 2, 3);
71 draw_pixel(0, 3, 4); 75 draw_pixel( 0, 3, 4);
76 txt_position(8, 8);
77 txt_printf("Hello world!");
72 78
73 // Main loop. 79 // Main loop.
74 PROF_INIT(); 80 PROF_INIT();
75 size_t frame_counter = 0;
76 while(true) { 81 while(true) {
77 bios_vblank_wait(); 82 bios_vblank_wait();
78 PROF_SHOW(); 83 PROF_SHOW();
79 PROF(flip_buffer(), flip_cycles); 84 PROF(flip_buffer(), flip_cycles);
80 frame_counter++;
81 } 85 }
82 86
83 return 0; 87 return 0;
diff --git a/src/renderer.c b/src/renderer.c
index bddaacd..bd2c023 100644
--- a/src/renderer.c
+++ b/src/renderer.c
@@ -12,10 +12,10 @@
12// the remaining two available for other background layers. There are 14KB of 12// the remaining two available for other background layers. There are 14KB of
13// sprite memory available, since the backbuffer is located at the end of the 13// sprite memory available, since the backbuffer is located at the end of the
14// VRAM, but if more space is needed it can be moved to the end of the BG 14// VRAM, but if more space is needed it can be moved to the end of the BG
15// charblocks instead. 15// charblocks instead as described below.
16// 16//
17 17
18#include "bd-font.c" 18#include "text.h"
19 19
20// The frontbuffer is located at the beginning of the VRAM, and requires 20KB of 20// The frontbuffer is located at the beginning of the VRAM, and requires 20KB of
21// video memory for 32 * 20 tiles at 4bpp. 21// video memory for 32 * 20 tiles at 4bpp.
@@ -40,6 +40,7 @@
40#define FONT_DATA ((u32*)(MEM_VRAM + KB(22))) 40#define FONT_DATA ((u32*)(MEM_VRAM + KB(22)))
41#define FONT_TILEMAP ((u16*)(MEM_VRAM + KB(30))) 41#define FONT_TILEMAP ((u16*)(MEM_VRAM + KB(30)))
42#define FONT_SB 15 42#define FONT_SB 15
43#define FONT_OFFSET 192
43 44
44// Keep track of which tiles need to be copied to the frontbuffer. 45// Keep track of which tiles need to be copied to the frontbuffer.
45static u32 dirty_tiles[21] = {0}; 46static u32 dirty_tiles[21] = {0};
@@ -86,8 +87,6 @@ flip_buffer(void) {
86 } 87 }
87} 88}
88 89
89static u16 font_map[256] = {0};
90
91void 90void
92renderer_init(void) { 91renderer_init(void) {
93 // Initialize display mode and bg palette. 92 // Initialize display mode and bg palette.
@@ -100,9 +99,9 @@ renderer_init(void) {
100 // Use DMA to clear front and back buffers as well as the font memory map. 99 // Use DMA to clear front and back buffers as well as the font memory map.
101 dma_fill(FRONTBUF, 0, KB(20), 3); 100 dma_fill(FRONTBUF, 0, KB(20), 3);
102 dma_fill(FRONTBUF_TILEMAP, 0, KB(2), 3); 101 dma_fill(FRONTBUF_TILEMAP, 0, KB(2), 3);
103 dma_fill(FONT_DATA, 0, KB(8), 3);
104 dma_fill(FONT_TILEMAP, 0, KB(2), 3);
105 dma_fill(BACKBUF, 0, KB(20), 3); 102 dma_fill(BACKBUF, 0, KB(20), 3);
103 dma_fill(FONT_DATA, 0, KB(8), 3);
104 dma_fill(FONT_TILEMAP, FONT_OFFSET, KB(2), 3);
106 105
107 // Initialize default palette. 106 // Initialize default palette.
108 PAL_BUFFER_BG[0] = COLOR_BLACK; 107 PAL_BUFFER_BG[0] = COLOR_BLACK;
@@ -117,13 +116,6 @@ renderer_init(void) {
117 FRONTBUF_TILEMAP[i] = i; 116 FRONTBUF_TILEMAP[i] = i;
118 } 117 }
119 118
120 // Load font data into VRAM. 119 // Initialize text engine.
121 unpack_tiles(&bd_font, FONT_DATA, 256); 120 txt_init(FONT_DATA, FONT_TILEMAP, FONT_OFFSET);
122
123 // Initialize the font map translation table. That way we can write
124 // a character on the text background layer with:
125 // FONT_TILEMAP[tile_x + 32 * tile_y] = font_map['A'];
126 for (size_t i = 0; i < 256; ++i) {
127 font_map[i] = 192 + i;
128 }
129} 121}
diff --git a/src/text/posprintf.h b/src/text/posprintf.h
new file mode 100644
index 0000000..a560db5
--- /dev/null
+++ b/src/text/posprintf.h
@@ -0,0 +1 @@
/* posprintf - a condensed version of sprintf for Thumb, esp. GBA Copyright (C) 2003 Dan Posluns The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below. A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain. Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work. Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived. Author contact e-mail: dan at danposluns dot com INSTRUCTIONS: To call: posprintf(char *dest, const char *src[, param1[, param2[, ... paramN]]]); - src must be a valid zero-terminated C string. - dest must point to a sufficiently large block of memory to contain the result string. The following format specifiers are valid: %% - print a '%' symbol %s - print a string; parameter must point to a valid zero-terminated C string %d - print a 16-bit (short) integer; must be within [-65,535 .. 65,535] %l - print a 29-bit integer; approximate range [-500,000,000 .. 500,000,000] %x - print a hexadecimal number (lowercase digits) %X - print a hexadecimal number (uppercase digits) The specifiers %d, %l, %x and %X may be modified as follows: - Digits 1 through 9 indicate number of leading spaces to print, eg. %5d would print the number 123 as " 123" %5d would print the number 123456 as "123456" (no leading spaces) - When above digit is prefixed with 0, leading zeros are printed instead of spaces %05d would print the number 123 as "00123" %04d would print the number 12345 as "12345" (no leading zeros) - Negative sign consumes a leading space, eg. %05d would print the number -123 as "-0123" (Hexadecimal numbers are considered unsigned) IF YOU WANT MORE FUNCTIONALITY THAN THIS, YOU CAN FEEL FREE TO MODIFY THE CODE, BUT THEN I WOULD SUGGEST MOVING TO A MORE SOPHISTICATED LIBRARY ANYWAY. *** CAUTION IF NOT USED ON GAMEBOY ADVANCE *** Although this is mostly written as general Thumb code, the %l (29-bit print) specifier code currently uses a software interrupt (SWI) specific to the Gameboy Advance to perform a division. If you wish to port this to other ARM machines, you may need to alter this code. I believe that most ARM machines support SWI 7 as an alternative software divide, although you will have to swap the numerator/denominator registers (r0 and r1). *** END CAUTION *** My contact e-mail is: dan at danposluns dot com */ extern void posprintf(char *, const char *, ...); \ No newline at end of file
diff --git a/src/text/posprintf.s b/src/text/posprintf.s
new file mode 100644
index 0000000..f3ef1e6
--- /dev/null
+++ b/src/text/posprintf.s
@@ -0,0 +1,419 @@
1/*
2
3posprintf - a condensed version of sprintf for Thumb, esp. GBA
4Copyright (C) 2003 Dan Posluns
5
6The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below.
7
8A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain.
9
10Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work.
11
12Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived.
13
14Author contact e-mail: dan at danposluns dot com
15
16
17register map:
18
19MAIN LOOP: PROCESS16:
20r0 <- dest string address r0 <- d0
21r1 <- source string address r1 <- d1
22r2 <- integer to print r2 <- d2
23r3 <- r3 <- d3
24r4 <- current char r4 <- d4
25r5 <- r5 <- work register
26r6 <- r6 <- work register
27r7 <- r7 <- dest string address
28r8 <- number of digits to print r8 <- number of digits to print
29r9 <- leading char (' ' or '0') r9 <- leading char (' ' or '0')
30r10 <- current parameter pointer r10 <- current parameter ptr
31r11 <- r11 <-
32r12 <- r12 <- source string address
33r14 <- r14 <- lr
34
35Function parameters:
36
37r0 <- destination string address
38r1 <- source string address
39r2 <- param1
40r3 <- param2
41
42*/
43 .thumb
44 .thumb_func
45 .align
46 .global posprintf
47 .type posprintf,function
48posprintf:
49
50 push {r3} @ push our second and third parameters
51 push {r2} @ onto the stack in reverse order
52 mov r12, sp @ r12 <- first parameter pointer
53
54 push {r4-r7} @ save clobbered registers
55 mov r4, r8
56 mov r5, r9
57 mov r6, r10
58 push {r4-r6, lr}
59 mov r10, r12 @ r10 <- first parameter pointer
60
61.L_STRINGLOOP:
62 ldrb r4, [r1] @ load a char from r1
63 add r1, #1 @ advance pointer to next char
64 cmp r4, #'%' @ if char == '%' then
65 beq .L_FORMATENTRY @ handle the format specifier
66 strb r4, [r0] @ store the char back to memory
67 add r0, #1 @ advance pointer to next char
68 cmp r4, #0 @ if char != 0 then
69 bne .L_STRINGLOOP @ repeat for next char
70 @ cleanup and exit
71 pop {r4-r7} @ restore clobbered registers
72 mov r8, r4
73 mov r9, r5
74 mov r10, r6
75 mov lr, r7
76 pop {r4-r7}
77 add sp, #8 @ remove parameters from stack
78 bx lr @ return from subroutine
79
80.L_FORMATENTRY:
81 mov r5, #0 @ assume no leading character for numbers
82 mov r6, #' ' @ assume print spaces if we do print leads
83 mov r8, r5
84 mov r9, r6
85.L_FORMATSPEC:
86 ldrb r4, [r1] @ load the next char from r1
87 add r1, #1 @ advance pointer to next char
88 cmp r4, #'d' @ if char == 'd'
89 beq .L_PRINT16 @ print 16-bit number
90 cmp r4, #'s' @ if char == 's'
91 beq .L_PRINTSTR @ print string
92 cmp r4, #'0' @ if char == '0'
93 beq .L_SETLEAD @ print with leading zeros
94 cmp r4, #'%' @ if char == '%'
95 beq .L_PRINTSYMBOL @ print '%' character
96 cmp r4, #'l' @ if char == 'l'
97 beq .L_PRINT29 @ print 29-bit number
98 cmp r4, #'X' @ if char == 'X'
99 beq .L_PRINTHEXUC @ print hexadecimal uppercase
100 cmp r4, #'x' @ if char == 'x'
101 beq .L_PRINTHEXLC @ print hexadecimal lowercase
102 @ we now assume that we are choosing a number of leading digits to display
103 sub r4, #'0'
104 mov r8, r4 @ r8 <- char - '0'
105 b .L_FORMATSPEC
106
107.L_SETLEAD:
108 mov r6, #'0'
109 mov r9, r6 @ print leading zeros instead of spaces
110 b .L_FORMATSPEC
111
112.L_PRINTSYMBOL:
113 strb r4, [r0] @ store '%' symbol to memory
114 add r0, #1 @ advance pointer to next char
115 b .L_STRINGLOOP
116
117.L_PRINTSTR:
118 mov r4, r10 @ r4 <- current parameter pointer
119 ldr r2, [r4] @ r2 <- address of string to print
120 add r4, #4
121 mov r10, r4 @ increase parameter pointer
122.L_PRINTSTRLOOP:
123 ldrb r4, [r2] @ load a char from r2
124 add r2, #1 @ advance pointer to next char
125 cmp r4, #0 @ if char == 0
126 beq .L_STRINGLOOP @ then we are done
127 strb r4, [r0] @ store the char back to memory
128 add r0, #1 @ advance pointer to next char
129 b .L_PRINTSTRLOOP
130
131.L_PRINT16:
132 mov r7, r0 @ r7 <- dest string address
133 mov r4, r10 @ r4 <- current parameter pointer
134 ldr r0, [r4] @ r0 <- 16-bit integer to print
135 add r4, #4
136 mov r10, r4 @ increase parameter pointer
137 mov r3, #0 @ temp marker for L_PRINTSIGN
138 cmp r0, #0 @ if integer to print is negative
139 blt .L_PRINTSIGN @ print the sign and adjust
140.L_SIGNDONE:
141 mov lr, pc @ save this location
142 bl .L_PROCESS16 @ process a 16-bit number
143 b .L_STRINGLOOP @ return when done
144
145.L_PRINTSIGN:
146 mov r4, #'-'
147 strb r4, [r7] @ print '-' character
148 add r7, #1 @ advance pointer to next char
149 neg r0, r0 @ r2 is now positive
150 mov r4, r8
151 sub r4, #1 @ print one fewer character
152 mov r8, r4 @ r8 <- new value
153 cmp r3, #0 @ check to see who called us
154 beq .L_SIGNDONE
155 b .L_SIGN29DONE
156
157.L_PRINT29:
158 mov r7, r0 @ r7 <- dest string address
159 mov r4, r10 @ r4 <- current parameter pointer
160 ldr r0, [r4] @ r0 <- 16-bit integer to print
161 add r4, #4
162 mov r10, r4 @ increase parameter pointer
163 mov r3, #1 @ temp marker for L_PRINTSIGN
164 cmp r0, #0 @ if integer to print is negative
165 blt .L_PRINTSIGN @ print the sign and adjust
166.L_SIGN29DONE:
167 mov r12, r1
168 mov r1, #0x27
169 lsl r1, r1, #8
170 add r1, #0x10 @ r1 <- 0x2710 == 10000
171 swi 6 @ split number by dividing by 10000
172 mov r3, #0
173 sub r3, #4
174 add r8, r3 @ subtract 4 from digits to display
175 cmp r0, #0 @ if the first chunk is empty
176 beq .L_P29SKIP @ then skip it
177 push {r1} @ save the second number to display
178 mov r1, r12
179 mov lr, pc @ save this location
180 bl .L_PROCESS16 @ process a 16-bit number
181 mov r12, r1
182 pop {r1} @ load in the second number
183 mov r3, #0
184 mov r8, r3 @ print leading symbols now!
185 mov r3, #'0'
186 mov r9, r3 @ make sure they are zeros!
187.L_P29SKIP:
188 mov r0, r1 @ get ready to print second number
189 mov r1, #4
190 add r8, r1 @ add 4 back on to digits
191 mov r1, r12
192 mov lr, pc @ save this location
193 bl .L_PROCESS16 @ process a 16-bit number
194 b .L_STRINGLOOP
195
196.L_PRINTHEXLC:
197 mov r7, #39
198 mov r12, r7 @ lowercase offset
199 b .L_PRINTHEX
200.L_PRINTHEXUC:
201 mov r7, #7
202 mov r12, r7 @ uppercase offset
203.L_PRINTHEX:
204 mov r4, r10 @ r4 <- current parameter pointer
205 ldr r2, [r4] @ r2 <- integer to print
206 add r4, #4
207 mov r10, r4 @ increase parameter pointer
208 mov r4, #28 @ r4 <- 8 digits to cycle through
209 mov r6, #0xF @ r6 <- nibble mask
210 mov r7, #0 @ r7 <- print flag
211.L_PRINTHEXLOOP:
212 mov r3, r2
213 lsr r3, r4
214 and r3, r6 @ r3 <- (n >> (cycle * 4)) & 0xF
215 orr r7, r3 @ if we have not encountered a digit
216 beq .L_PH_LEADZERO @ then it is a leading zero
217 add r3, #'0'
218 mov r5, r12 @ get ready to print a letter
219 cmp r3, #'9' @ if the digit is in the alpha range
220 bgt .L_PH_ALPHA @ then print a letter
221 mov r5, #0 @ else do nothing
222.L_PH_ALPHA:
223 add r3, r5 @ add offset to correct letter
224 strb r3, [r0] @ store the char in memory
225 add r0, #1 @ advance pointer to next char
226 sub r4, #4 @ advance to next digit
227 bge .L_PRINTHEXLOOP @ loop until done
228 b .L_STRINGLOOP
229
230.L_PH_LEADZERO:
231 lsr r5, r4, #2 @ r5 <- which digit we are on
232 sub r4, #4 @ if this is our last digit
233 blt .L_PH_FINAL @ then print a zero for sure
234 cmp r8, r5 @ if r8 < current digit
235 ble .L_PRINTHEXLOOP @ then keep looping
236 mov r5, r9 @ r5 <- leading symbol to print
237 strb r5, [r0] @ store the char in memory
238 add r0, #1 @ advance pointer to next char
239 b .L_PRINTHEXLOOP
240.L_PH_FINAL:
241 mov r3, #'0' @ if n == 0, print at least one 0
242 strb r3, [r0]
243 add r0, #1
244 b .L_STRINGLOOP
245
246.L_PROCESS16:
247 mov r12, r1 @ free up registers
248 mov r5, #0xF
249 lsr r1, r0, #4
250 lsr r2, r0, #8
251 lsr r3, r0, #12
252 and r0, r5 @ r0 <- n & 0xF
253 and r1, r5 @ r1 <- (n >> 4) & 0xF
254 and r2, r5 @ r2 <- (n >> 8) & 0xF
255 and r3, r5 @ r3 <- (n >> 12) & 0xF
256 mov r6, r3
257 add r6, r2
258 add r6, r1
259 lsl r5, r6, #2
260 lsl r6, r6, #1
261 add r0, r6
262 add r0, r5 @ r0 <- 6 * (d3 + d2 + d1) + d0
263 @ divide by ten: multiply by 0x19A shifted right by 12
264 lsr r5, r0, #2
265 add r5, r0
266 lsr r5, r5, #1 @ r5 <- ((d0 >> 2) + i) >> 1
267 add r5, r0
268 lsr r5, r5, #3 @ r5 = (r5 + d0) >> 3
269 add r5, r0
270 lsr r5, r5, #1 @ r5 = (r5 + d0) >> 1
271 add r5, r0
272 lsr r5, r5, #4 @ r5 <- d0 / 10
273 @ calculate remainder as d0
274 lsl r6, r5, #2
275 add r6, r5
276 lsl r6, r6, #1 @ r6 <- q * 10
277 sub r0, r6 @ r0 <- d0 - (q * 10)
278 @ finished with d0, now calculate d1
279 lsl r6, r3, #3
280 add r5, r6
281 add r5, r3 @ r5 <- q + 9 * d3
282 lsl r6, r2, #2
283 add r5, r6
284 add r5, r2 @ r5 <- q + 9 * d3 + 5 * d2
285 add r1, r5 @ r1 <- d1 + r5
286 beq .L_LEAD_D1
287 @ divide d1 by ten: multiply by 0x19A shifted right by 12
288 lsr r5, r1, #2
289 add r5, r1
290 lsr r5, r5, #1
291 add r5, r1
292 lsr r5, r5, #3
293 add r5, r1
294 lsr r5, r5, #1
295 add r5, r1
296 lsr r5, r5, #4 @ r5 <- d1 / 10
297 @ calculate remainder as d1
298 lsl r6, r5, #2
299 add r6, r5
300 lsl r6, r6, #1
301 sub r1, r6 @ r1 <- d1 - (q * 10)
302 @ finished with d1, now calculate d2
303 lsl r2, r2, #1
304 add r2, r5 @ r2 <- 2 * d2 + q
305 mov r5, r2
306 orr r5, r3 @ if (!d2) && (!d3)
307 beq .L_LEAD_D2 @ then skip
308 @ divide d2 by ten: multiply by 0x1A >> 8 is sufficient
309 lsr r5, r2, #2
310 add r5, r2
311 lsr r5, r5, #1
312 add r5, r2
313 lsr r5, r5, #4 @ r5 <- d2 / 10
314 @ calculate remainder as d2
315 lsl r6, r5, #2
316 add r6, r5
317 lsl r6, r6, #1
318 sub r2, r6 @ r2 <- d2 - (q * 10)
319 @ finished with d2, now calculate d3
320 lsl r3, r3, #2
321 add r3, r5
322 beq .L_LEAD_D3
323 @ divide d3 by ten: multiply by 0x1A >> 8 is sufficient
324 lsr r5, r3, #2
325 add r5, r3
326 lsr r5, r5, #1
327 add r5, r3
328 lsr r5, r5, #4 @ r5 <- d3 / 10
329 @ calculate remainder as d3
330 lsl r6, r5, #2
331 add r6, r5
332 lsl r6, r6, #1
333 sub r3, r6 @ r3 <- d3 - (q * 10)
334 @ finished with d3, d4 will automatically be quotient
335 mov r4, r5
336 beq .L_LEAD_D4
337 @ now print any leading digits if we are using all five
338 mov r5, r8
339 mov r6, r9
340 sub r5, #4 @ already printed five digits
341.L_EXTRA_LEAD_LOOP:
342 sub r5, #1
343 ble .L_DONE_EXTRA_LEAD
344 strb r6, [r7] @ print a leading character
345 add r7, #1
346 b .L_EXTRA_LEAD_LOOP
347.L_DONE_EXTRA_LEAD:
348 @ now print the fifth digit (d4)
349 add r4, #'0' @ r4 <- d4 + '0'
350 strb r4, [r7] @ store a character
351 add r7, #1 @ advance string pointer
352.L_DONE_D4:
353 add r3, #'0'
354 strb r3, [r7]
355 add r7, #1
356.L_DONE_D3:
357 add r2, #'0'
358 strb r2, [r7]
359 add r7, #1
360.L_DONE_D2:
361 add r1, #'0'
362 strb r1, [r7]
363 add r7, #1
364.L_DONE_D1:
365 add r0, #'0'
366 strb r0, [r7]
367 add r7, #1
368 @ Done at last! Clean up and return to calling routine
369 mov r0, r7 @ restore r0 <- dest string address
370 mov r1, r12 @ restore r1 <- source string address
371 mov pc, lr @ return from subroutine
372
373.L_LEAD_D4:
374 mov r5, r9 @ r5 <- leading character
375 mov r6, r8
376 sub r6, #4 @ r6 <- # of chars to print
377 ble .L_DONE_D4
378.L_IN_D4:
379 strb r5, [r7] @ store a character
380 add r7, #1 @ advance string pointer
381 sub r6, #1 @ if chars to print > 0
382 bgt .L_IN_D4 @ then loop
383 b .L_DONE_D4
384
385.L_LEAD_D3:
386 mov r5, r9 @ r5 <- leading character
387 mov r6, r8
388 sub r6, #3 @ r6 <- # of chars to print
389 ble .L_DONE_D3
390.L_IN_D3:
391 strb r5, [r7] @ store a character
392 add r7, #1 @ advance string pointer
393 sub r6, #1 @ if chars to print > 0
394 bgt .L_IN_D3 @ then loop
395 b .L_DONE_D3
396
397.L_LEAD_D2:
398 mov r5, r9 @ r5 <- leading character
399 mov r6, r8
400 sub r6, #2 @ r6 <- # of chars to print
401 ble .L_DONE_D2
402.L_IN_D2:
403 strb r5, [r7] @ store a character
404 add r7, #1 @ advance string pointer
405 sub r6, #1 @ if chars to print > 0
406 bgt .L_IN_D2 @ then loop
407 b .L_DONE_D2
408
409.L_LEAD_D1:
410 mov r5, r9 @ r5 <- leading character
411 mov r6, r8
412 sub r6, #1 @ r6 <- # of chars to print
413 ble .L_DONE_D1
414.L_IN_D1:
415 strb r5, [r7] @ store a character
416 add r7, #1 @ advance string pointer
417 sub r6, #1 @ if chars to print > 0
418 bgt .L_IN_D1 @ then loop
419 b .L_DONE_D1
diff --git a/src/text/text.h b/src/text/text.h
new file mode 100644
index 0000000..65003c5
--- /dev/null
+++ b/src/text/text.h
@@ -0,0 +1,114 @@
1#ifndef TEXT_H
2#define TEXT_H
3
4#include "bd-font.c"
5#include "shorthand.h"
6#include "posprintf.h"
7
8typedef struct TextEngine {
9 // Cursor for tiled text mode The X and Y positions correspond to the tile
10 // X and Y starting from the top left of the screen. For a 240x160 screen,
11 // we have 30x20 tiles available.
12 size_t cursor_x;
13 size_t cursor_y;
14
15 // Memory location of font tile data and tilemap. Likely located on the
16 // VRAM.
17 u32 *font_data;
18 u16 *font_tilemap;
19
20 // The font map for tiled text. Writing the character stored in this
21 // position on the tilemap will show a character on the screen.
22 u16 font_map[256];
23} TextEngine;
24
25static TextEngine text_engine = {0};
26
27static inline
28void
29txt_init(u32 *font_data, u16 *font_tilemap, u16 font_offset) {
30 // Load font data into VRAM.
31 unpack_tiles(&bd_font, font_data, 256);
32
33 // Initialize the font map translation table. That way we can write
34 // a character on the text background layer with:
35 // FONT_TILEMAP[tile_x + 32 * tile_y] = font_map['A'];
36 for (size_t i = 0; i < 256; ++i) {
37 text_engine.font_map[i] = font_offset + i;
38 }
39
40 // Initialize remaining variables.
41 text_engine.font_data = font_data;
42 text_engine.font_tilemap = font_tilemap;
43}
44
45static inline
46void
47txt_putc(char c) {
48 if (c == '\0') {
49 return;
50 }
51 if (c == '\n') {
52 text_engine.cursor_x = 0;
53 text_engine.cursor_y++;
54 } else {
55 int x = text_engine.cursor_x;
56 int y = text_engine.cursor_y;
57 text_engine.font_tilemap[x + 32 * y] = text_engine.font_map[(u16)c];
58 text_engine.cursor_x += 1;
59 if (text_engine.cursor_x >= 30) {
60 text_engine.cursor_x = 0;
61 text_engine.cursor_y++;
62 }
63 }
64 if (text_engine.cursor_y >= 20) {
65 text_engine.cursor_y = 0;
66 }
67}
68
69static inline
70void
71txt_puts(char *msg) {
72 while (*msg) {
73 txt_putc(*msg++);
74 }
75}
76
77static inline
78void
79txt_clear_line(void) {
80 for (size_t i = 0; i < 30; ++i) {
81 int x = text_engine.cursor_x;
82 int y = text_engine.cursor_y;
83 text_engine.font_tilemap[x + 32 * y] = text_engine.font_map[0];
84 }
85 text_engine.cursor_x = 0;
86}
87
88static inline
89void
90txt_clear_screen(void) {
91 for (size_t j = 0; j < 20; ++j) {
92 text_engine.cursor_y = j;
93 txt_clear_line();
94 }
95 text_engine.cursor_x = 0;
96 text_engine.cursor_y = 0;
97}
98
99static inline
100void
101txt_position(size_t tile_x, size_t tile_y) {
102 text_engine.cursor_x = tile_x;
103 text_engine.cursor_y = tile_y;
104}
105
106// Print text to the screen with formatting.
107#define txt_printf(msg, ...) \
108 { \
109 char buf[256] = {0}; \
110 posprintf(buf, msg, ##__VA_ARGS__); \
111 txt_puts(buf); \
112 }
113
114#endif // TEXT_H