Implement text rendering
This commit is contained in:
parent
ddbf083d1b
commit
2c507b6ecb
|
@ -2,22 +2,23 @@ import json
|
|||
import struct
|
||||
|
||||
# generates:
|
||||
# font_data.dat (binary file, expected to load at location 0xf000_0100)
|
||||
# font_data.inc (fox32 assembly)
|
||||
# font_data.inc defines constants
|
||||
# N_CHARS: number of defined characters (u32)
|
||||
# CHAR_TABLE: pointer to main char table (&Char)
|
||||
# UNKNOWN_CHAR: pointer to unknown character (&Char)
|
||||
# font_data.bin
|
||||
# font_table.bin
|
||||
# font_data.inc
|
||||
#
|
||||
# struct Char {
|
||||
# width: u8, height: u8, baseline: u8,
|
||||
# data_ptr: &[u8],
|
||||
# data_offset: offset into font_data
|
||||
# } (7 bytes)
|
||||
|
||||
DATA_FILE_ORIGIN = 0xf000_0100
|
||||
CODEPAGE = {
|
||||
0: None,
|
||||
128: 'Ξ',
|
||||
}
|
||||
for x in range(32, 127):
|
||||
CODEPAGE[x] = chr(x)
|
||||
|
||||
BASELINE = 7
|
||||
N_CHARS = 127
|
||||
|
||||
codepoints = json.load(open("ultlf/codepoints.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:
|
||||
table = b''
|
||||
char_data = b''
|
||||
SPACE = [" "]
|
||||
|
||||
for ch in [None] + list(range(N_CHARS)):
|
||||
if ch in codepoints:
|
||||
i = codepoints.index(ch)
|
||||
glyph = font_data[i]
|
||||
baseline = baselines[i]
|
||||
if '?' in "".join(glyph):
|
||||
print(f"Font doesn't have char {ch}")
|
||||
glyph = UNKNOWN
|
||||
baseline = 6
|
||||
else:
|
||||
SCALE = 2
|
||||
def scale(glyph):
|
||||
out = []
|
||||
for line in glyph:
|
||||
line_scaled = ""
|
||||
for ch in line:
|
||||
line_scaled += ch * SCALE
|
||||
out.extend([line_scaled] * SCALE)
|
||||
return out
|
||||
|
||||
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
|
||||
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 ch in glyph:
|
||||
char_data += b'\x00' if ch == ' ' else b'\x01'
|
||||
for ch in line:
|
||||
data += b'\x00' if ch == ' ' else b'\x01'
|
||||
|
||||
dat.write(char_data + table)
|
||||
table_file.write(table)
|
||||
data_file.write(data)
|
||||
|
||||
inc.write(f'''
|
||||
const N_CHARS: {N_CHARS}
|
||||
const CHAR_TABLE: {hex(DATA_FILE_ORIGIN + len(char_data))}
|
||||
const UNKNOWN_CHAR: {hex(DATA_FILE_ORIGIN)}
|
||||
''')
|
||||
inc_file.write(f"const FONT_SIZE: {SCALE}\n")
|
||||
inc_file.write(f"const FONT_LINE_HEIGHT: {8 * SCALE}\n")
|
||||
|
|
|
@ -6,6 +6,9 @@ const BG_FRAMEBUFFER: 0x2000000
|
|||
const SCREEN_WIDTH: 640
|
||||
const SCREEN_HEIGHT: 480
|
||||
|
||||
const COLOR_WHITE: 0xffffffc0
|
||||
const COLOR_BLUE: 0xffffff40
|
||||
const COLOR_ORANGE: 0xff4080ff
|
||||
|
||||
; Globals
|
||||
const FRAMENR: 0x00000000
|
||||
|
@ -17,8 +20,6 @@ entry:
|
|||
mcl ; disable MMU
|
||||
mov rsp, SYS_STACK_START ; set stack pointer
|
||||
|
||||
mov r0, entry_str
|
||||
call serial_print
|
||||
mov r0, version_str_1
|
||||
call serial_print
|
||||
mov r0, version_str
|
||||
|
@ -32,18 +33,64 @@ entry:
|
|||
|
||||
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
|
||||
call serial_print
|
||||
|
||||
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
|
||||
#include "data/version.inc"
|
||||
|
||||
version_str_1: data.str " - version " data.8 0
|
||||
version_str_2: data.str " (" data.8 0
|
||||
version_str_3: data.str ") ==" data.8 10 data.8 0
|
||||
version_str_1: data.str "version " data.8 0
|
||||
version_str_2: data.str " (commit " 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
|
||||
|
||||
|
@ -96,4 +143,5 @@ serial_print_nl:
|
|||
ret
|
||||
|
||||
#include "logo.asm"
|
||||
#include "text.asm"
|
||||
|
||||
|
|
202
xenrom/text.asm
202
xenrom/text.asm
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user