Implement text rendering

This commit is contained in:
xenia 2024-02-07 22:52:21 +01:00
parent ddbf083d1b
commit 2c507b6ecb
3 changed files with 306 additions and 38 deletions

View File

@ -2,22 +2,23 @@ import json
import struct import struct
# generates: # generates:
# font_data.dat (binary file, expected to load at location 0xf000_0100) # font_data.bin
# font_data.inc (fox32 assembly) # font_table.bin
# font_data.inc defines constants # font_data.inc
# N_CHARS: number of defined characters (u32)
# CHAR_TABLE: pointer to main char table (&Char)
# UNKNOWN_CHAR: pointer to unknown character (&Char)
# #
# struct Char { # struct Char {
# width: u8, height: u8, baseline: u8, # width: u8, height: u8, baseline: u8,
# data_ptr: &[u8], # data_offset: offset into font_data
# } (7 bytes) # } (7 bytes)
DATA_FILE_ORIGIN = 0xf000_0100 CODEPAGE = {
0: None,
128: 'Ξ',
}
for x in range(32, 127):
CODEPAGE[x] = chr(x)
BASELINE = 7 BASELINE = 7
N_CHARS = 127
codepoints = json.load(open("ultlf/codepoints.json")) codepoints = json.load(open("ultlf/codepoints.json"))
font_data = json.load(open("ultlf/data.json")) font_data = json.load(open("ultlf/data.json"))
@ -33,34 +34,51 @@ UNKNOWN = [
"# # #", "# # #",
] ]
with open("xenrom/data/font_data.inc", "w") as inc, open("xenrom/data/font_data.dat", "wb") as dat: SPACE = [" "]
table = b''
char_data = b''
for ch in [None] + list(range(N_CHARS)): SCALE = 2
if ch in codepoints: def scale(glyph):
i = codepoints.index(ch) out = []
glyph = font_data[i] for line in glyph:
baseline = baselines[i] line_scaled = ""
if '?' in "".join(glyph): for ch in line:
print(f"Font doesn't have char {ch}") line_scaled += ch * SCALE
glyph = UNKNOWN out.extend([line_scaled] * SCALE)
baseline = 6 return out
else:
with open("xenrom/data/font_table.dat", "wb") as table_file, open("xenrom/data/font_data.dat", "wb") as data_file, open("xenrom/data/font_data.inc", "w") as inc_file:
table = b''
data = b''
for i in range(256):
ch = CODEPAGE.get(i, None)
unicode_codepoint = ord(ch) if ch is not None else None
if unicode_codepoint is None:
glyph = UNKNOWN glyph = UNKNOWN
baseline = 6 baseline = 6
print(f"Couldn't find char {ch}") elif unicode_codepoint == 32:
glyph = SPACE
baseline = 1
else:
ultlf_idx = codepoints.index(unicode_codepoint)
glyph = font_data[ultlf_idx]
baseline = baselines[ultlf_idx]
table += struct.pack("BBBI", len(glyph[0]), len(glyph), baseline, DATA_FILE_ORIGIN + len(char_data)) if "X" in "".join(glyph):
glyph = UNKNOWN
baseline = 6
glyph = scale(glyph)
baseline *= SCALE
table += struct.pack("<BBBI", len(glyph[0]), len(glyph), baseline, len(data))
for line in glyph: for line in glyph:
for ch in glyph: for ch in line:
char_data += b'\x00' if ch == ' ' else b'\x01' data += b'\x00' if ch == ' ' else b'\x01'
dat.write(char_data + table) table_file.write(table)
data_file.write(data)
inc.write(f''' inc_file.write(f"const FONT_SIZE: {SCALE}\n")
const N_CHARS: {N_CHARS} inc_file.write(f"const FONT_LINE_HEIGHT: {8 * SCALE}\n")
const CHAR_TABLE: {hex(DATA_FILE_ORIGIN + len(char_data))}
const UNKNOWN_CHAR: {hex(DATA_FILE_ORIGIN)}
''')

View File

@ -6,6 +6,9 @@ const BG_FRAMEBUFFER: 0x2000000
const SCREEN_WIDTH: 640 const SCREEN_WIDTH: 640
const SCREEN_HEIGHT: 480 const SCREEN_HEIGHT: 480
const COLOR_WHITE: 0xffffffc0
const COLOR_BLUE: 0xffffff40
const COLOR_ORANGE: 0xff4080ff
; Globals ; Globals
const FRAMENR: 0x00000000 const FRAMENR: 0x00000000
@ -17,8 +20,6 @@ entry:
mcl ; disable MMU mcl ; disable MMU
mov rsp, SYS_STACK_START ; set stack pointer mov rsp, SYS_STACK_START ; set stack pointer
mov r0, entry_str
call serial_print
mov r0, version_str_1 mov r0, version_str_1
call serial_print call serial_print
mov r0, version_str mov r0, version_str
@ -32,18 +33,64 @@ entry:
call show_logo call show_logo
mov r0, BG_FRAMEBUFFER
mov r1, 640 ; width
mov r2, entry_str
mov r3, 20 ; x
mov r4, 20 ; y
mov r5, 20 ; l
mov r6, 620 ; r
mov r7, 100000 ; bot
mov r30, 2
mov r31, COLOR_WHITE
call draw_str_at
mov r2, version_str_1
call draw_str_at
mov r2, version_str
mov r31, COLOR_BLUE
call draw_str_at
mov r2, version_str_2
mov r31, COLOR_WHITE
call draw_str_at
mov r2, sha_str
mov r31, COLOR_ORANGE
call draw_str_at
mov r31, COLOR_WHITE
mov r2, version_str_3
call draw_str_at
mov r31, COLOR_WHITE
mov r2, credit_str_1
call draw_str_at
mov r31, COLOR_BLUE
mov r2, credit_str_2
call draw_str_at
mov r31, COLOR_WHITE
mov r2, credit_str_3
call draw_str_at
mov r31, COLOR_ORANGE
mov r2, credit_str_4
call draw_str_at
mov r0, booting_str mov r0, booting_str
call serial_print call serial_print
halt halt
entry_str: data.str "== Welcome to Xen32OS" data.8 0 entry_str: data.str "= " data.8 128 data.str "EN32OS =" data.8 10 data.8 0
; defines version_str, sha_str ; defines version_str, sha_str
#include "data/version.inc" #include "data/version.inc"
version_str_1: data.str " - version " data.8 0 version_str_1: data.str "version " data.8 0
version_str_2: data.str " (" data.8 0 version_str_2: data.str " (commit " data.8 0
version_str_3: data.str ") ==" data.8 10 data.8 0 version_str_3: data.str ")" data.8 10 data.8 0
credit_str_1: data.str "font by " data.8 0
credit_str_2: data.str "ultlang" data.8 0
credit_str_3: data.str ", os by " data.8 0
credit_str_4: data.str "xenia" data.8 0
booting_str: data.str "booting..." data.8 10 data.8 0 booting_str: data.str "booting..." data.8 10 data.8 0
@ -96,4 +143,5 @@ serial_print_nl:
ret ret
#include "logo.asm" #include "logo.asm"
#include "text.asm"

View File

@ -1 +1,203 @@
font_table: #include_bin "data/font_table.dat"
font_data: #include_bin "data/font_data.dat"
; defines FONT_SIZE and FONT_LINE_HEIGHT
#include "data/font_data.inc"
; input:
; r0: pointer to frame buffer
; r1: frame buffer width
; r2: pointer to string to draw
; r3: x coordinate to draw at
; r4: y coordinate to draw at
; r5: text box left
; r6: text box right
; r7: text box bottom
; r30: text delay (ms, blocking)
; r31: text color
; returns:
; r0, r1, r5, r6, r7, r30, r31 kept
; r2: pointer to last char drawn
; r3: x coordinate to next char to draw
; r4: y coordinate to next char to draw
draw_str_at:
; store char pointer in r8
mov r8, r2
draw_str_at_loop:
cmp r30, 0
ifnz call font_sleep
; r2: current char
movz.8 r2, [r8]
cmp r2, 0 ; check for null terminator
ifz jmp draw_str_at_loop_end
; check for newline
cmp r2, 10
ifz jmp draw_str_at_newline
; r9: char entry
mov r9, r2
mul r9, 7
add r9, font_table
; r10: width, r11: end x
movz.8 r10, [r9]
mov r11, r3
add r11, r10
; are we after the edge?
cmp r11, r6
iflt jmp draw_str_at_loop_draw
; if we have a space, don't reset
cmp r2, 32
ifz jmp draw_str_at_loop_draw
draw_str_at_linebreak:
mov r3, r5 ; reset x
; update end x
mov r11, r5
add r11, r10
; step to next line, check if we've overrun the box
add r4, FONT_LINE_HEIGHT
cmp r4, r7
ifgteq jmp draw_str_at_loop_end
draw_str_at_loop_draw:
push r0
push r1
push r2
push r3
push r4
call draw_char_at
pop r4
pop r3
pop r2
pop r1
pop r0
draw_str_at_spaceskip:
inc r8 ; update string index
mov r3, r11 ; update x coordinate
add r3, FONT_SIZE ; padding
jmp draw_str_at_loop
draw_str_at_newline:
inc r8 ; update string index
mov r3, r5 ; reset x
; step to next line, check if we've overrur the box
add r4, FONT_LINE_HEIGHT
cmp r4, r7
ifgteq jmp draw_str_at_loop_end
jmp draw_str_at_loop
draw_str_at_loop_end:
; restore char pointer into r2
mov r2, r8
ret
; input:
; r0: pointer to frame buffer
; r1: frame buffer width
; r2: char to draw
; r3: x coordinate to draw at
; r4: y coordinate to draw at
; r31: color
; returns:
; nothing
draw_char_at:
push r5
push r6
push r7
push r8
push r9
; r5: width
; r7: baseline
; r8: char data pointer
; r9: number of pixels to draw
mul r2, 7 ; size of char
add r2, font_table
movz.8 r5, [r2]
inc r2
movz.8 r9, [r2]
mul r9, r5
inc r2
movz.8 r7, [r2]
inc r2
mov r8, [r2]
add r8, font_data
; adjust y for baseline
sub r4, r7
; calculate init framebuffer address into r4
mul r4, r1
add r4, r3
mul r4, 4
add r4, r0
; r6: x counter in glyph
; r9: countdown
mov r6, 0
draw_char_at_loop:
cmp r9, 0 ; zero terminator
ifz jmp draw_char_at_loop_done
cmp.8 [r8], 0
ifnz mov [r4], r31
inc r8 ; advance glyph data pointer
dec r9 ; lower count
add r4, 4 ; advance screen pointer
inc r6 ; advance x counter
cmp r6, r5 ; are we at the end of the glyph line?
ifnz jmp draw_char_at_loop
; move screen pointer down one (x4 for 4 bytes per pixel)
add r4, r1
add r4, r1
add r4, r1
add r4, r1
sub r4, r5
sub r4, r5
sub r4, r5
sub r4, r5
mov r6, 0
jmp draw_char_at_loop
draw_char_at_loop_done:
pop r9
pop r8
pop r7
pop r6
pop r5
ret
; input
; r30: time to sleep (ms)
font_sleep:
push r0
push r1
; r0: end time
; r1: current time
in r0, 0x8000_0706 ; RTC uptime (ms)
add r0, 30
font_sleep_busyloop:
in r1, 0x8000_0706
cmp r1, r0
iflt jmp font_sleep_busyloop
pop r1
pop r0
ret