Phase 7j: words buffers + w[i] word indexing (retire peek32/poke32)

32-bit word access is indexing now, not peek32/poke32:

  peek32(ui_rx, i)       -> ui_rx[i]        (reads an int)
  poke32(rt_fb, i, c)    -> rt_fb[i] = c    (writes an int)

A buffer typed `words` (a pointer whose elements are i32) indexes with `w[i]`
as a full int; a plain `ptr`/`str` keeps byte indexing. emit_index_addr picks the
element type from the base's type — byte-identical IR to the old intrinsics, so
the migration reproduces the compiler and every golden render exactly.

The ~60 word buffers (rt_fb, tt_*/gc_* font tables, png_px/spr_px pixels, ui_*
layout arrays) were retyped from `ptr` to `words` scope-aware (per-function, so
the s/out/p byte-vs-word name collisions across functions stay correct), then the
254 peek32/poke32 sites migrated to indexing. A scope-analysis miss left 12
buffers (gc_*, sc_d, ui_rx/ui_ry) un-retyped — caught as a menu golden diff and
fixed. No true mixed byte+word access exists on any one variable, so a per-buffer
element type is sound.

Reseeded (22527 lines); C-free fixpoint holds; goldens byte-identical; 18/18;
vocab (byte/words types in, peek32/poke32 out) + doc-fences clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-28 02:20:50 +03:00
parent 454427bce8
commit ae0bae0457
17 changed files with 3032 additions and 3118 deletions

View file

@ -145,6 +145,8 @@ See `examples/menu.ludic` for a complete title screen.
| `entity` | entity handle | `i32` |
| `str` | string literal | `ptr` |
| `ptr` | raw address (runtime/FFI) | `ptr` |
| `byte` | pointer to bytes — `p[i]` reads/writes one byte | `ptr` |
| `words` | pointer to 32-bit words — `w[i]` reads/writes an `int` | `ptr` |
Numeric literals: `42` and `0x1affff` are `int`; a literal with a decimal
point (`1.5`) is `fixed`. Arithmetic on two `fixed` values lowers to

View file

@ -20,10 +20,10 @@
# ============================================================================
# ---- state ----------------------------------------------------------------
var rt_fb: ptr = null # framebuffer, one i32 (0x00RRGGBB) per pixel
var rt_fb: words = null # framebuffer, one i32 (0x00RRGGBB) per pixel
var rt_fbw: int = 320
var rt_fbh: int = 240
var rt_regs: ptr = null # the 64 general-purpose game registers
var rt_regs: words = null # the 64 general-purpose game registers
var rt_rng: int = 305419896 # xorshift32 state
var rt_alive: int = 1 # platform still running?
@ -62,7 +62,7 @@ fn rt_shutdown() -> void {
fn rt_clear(c: int) -> void {
let n = rt_fbw * rt_fbh
for i in 0 .. n {
poke32(rt_fb, i, c)
rt_fb[i] = c
}
}
@ -71,7 +71,7 @@ fn rt_put_px(x: int, y: int, c: int) -> void {
if y < 0 { return }
if x >= rt_fbw { return }
if y >= rt_fbh { return }
poke32(rt_fb, y * rt_fbw + x, c)
rt_fb[y * rt_fbw + x] = c
}
fn rt_fill_rect(x: int, y: int, w: int, h: int, c: int) -> void {
@ -84,7 +84,7 @@ fn rt_fill_rect(x: int, y: int, w: int, h: int, c: int) -> void {
let row = j * rt_fbw
var i = x0
while i < x1 {
poke32(rt_fb, row + i, c)
rt_fb[row + i] = c
i = i + 1
}
j = j + 1
@ -175,13 +175,13 @@ fn rt_text_int(x: int, y: int, n: int, colour: int, sc: int) -> void {
fn rt_reg(i: int) -> int {
if i < 0 { return 0 }
if i >= 64 { return 0 }
return peek32(rt_regs, i)
return rt_regs[i]
}
fn rt_set_reg(i: int, v: int) -> void {
if i < 0 { return }
if i >= 64 { return }
poke32(rt_regs, i, v)
rt_regs[i] = v
}
# ---- rng (xorshift32) -----------------------------------------------------
@ -290,7 +290,7 @@ fn rt_dump_ppm(path: str) -> void {
let px = rt_fbw * rt_fbh
let buf = mem_alloc(px * 3)
for i in 0 .. px {
let c = peek32(rt_fb, i)
let c = rt_fb[i]
buf[i * 3] = (c / 65536) % 256
buf[i * 3 + 1] = (c / 256) % 256
buf[i * 3 + 2] = c % 256
@ -369,11 +369,11 @@ fn rt_status_text() -> ptr {
# only it knows their shape. Everything below belongs to the runtime, so the
# runtime writes it — same order both ways.
fn rt_save_state(f: ptr) -> void {
let w = mem_alloc(16)
poke32(w, 0, rt_rng)
poke32(w, 1, rt_mapw)
poke32(w, 2, rt_maph)
poke32(w, 3, rt_alive)
let w: words = mem_alloc(16)
w[0] = rt_rng
w[1] = rt_mapw
w[2] = rt_maph
w[3] = rt_alive
file_write(f, w, 16)
file_write(f, rt_regs, 64 * 4)
file_write(f, rt_map, 96 * 64)
@ -382,11 +382,11 @@ fn rt_save_state(f: ptr) -> void {
}
fn rt_load_state(f: ptr) -> void {
let w = mem_alloc(16)
let w: words = mem_alloc(16)
file_read(f, w, 16)
rt_rng = peek32(w, 0)
rt_mapw = peek32(w, 1)
rt_maph = peek32(w, 2)
rt_rng = w[0]
rt_mapw = w[1]
rt_maph = w[2]
file_read(f, rt_regs, 64 * 4)
file_read(f, rt_map, 96 * 64)
file_read(f, rt_statusbuf, 96)

View file

@ -14,11 +14,11 @@ const SPR_MAX: int = 160
const SPR_SZ: int = 16
var img_px: ptr = null # IMG_MAX pointers to RGBA buffers
var img_w: ptr = null
var img_h: ptr = null
var img_w: words = null
var img_h: words = null
var img_n: int = 0
var spr_px: ptr = null # SPR_MAX * 16 * 16 RGBA pixels, one block
var spr_px: words = null # SPR_MAX * 16 * 16 RGBA pixels, one block
var spr_n: int = 0
fn rt_image_init() -> void {
@ -60,7 +60,7 @@ var rt_file_len: int = 0
# decoded image is left in these; -1 width means failure
var png_w: int = 0
var png_h: int = 0
var png_px: ptr = null
var png_px: words = null
fn rt_decode_png(path: str) -> bool {
png_w = 0
@ -168,7 +168,7 @@ fn rt_decode_png(path: str) -> bool {
}
# expand to 0xAARRGGBB
let out = mem_alloc(w * h * 4)
let out: words = mem_alloc(w * h * 4)
let maxv = (1 << bd) - 1
for y in 0 .. h {
let row = y * (stride + 1) + 1
@ -211,7 +211,7 @@ fn rt_decode_png(path: str) -> bool {
G = g
B = g
}
poke32(out, y * w + x, (((A << 24) | (R << 16)) | ((G << 8) | B)))
out[y * w + x] = (((A << 24) | (R << 16)) | ((G << 8) | B))
}
}
@ -233,8 +233,8 @@ fn rt_image_load(path: str) -> int {
let id = img_n
img_n = img_n + 1
pokep(img_px, id, png_px)
poke32(img_w, id, png_w)
poke32(img_h, id, png_h)
img_w[id] = png_w
img_h[id] = png_h
return id
}
@ -249,7 +249,7 @@ fn rt_blend_px(x: int, y: int, argb: int) -> void {
var g = ((argb >> 8) & 255)
var b = (argb & 255)
if a != 255 {
let dst = peek32(rt_fb, y * rt_fbw + x)
let dst = rt_fb[y * rt_fbw + x]
let dr = ((dst >> 16) & 255)
let dg = ((dst >> 8) & 255)
let db = (dst & 255)
@ -257,18 +257,18 @@ fn rt_blend_px(x: int, y: int, argb: int) -> void {
g = (g * a + dg * (255 - a)) / 255
b = (b * a + db * (255 - a)) / 255
}
poke32(rt_fb, y * rt_fbw + x, (((r << 16) | (g << 8)) | b))
rt_fb[y * rt_fbw + x] = (((r << 16) | (g << 8)) | b)
}
fn rt_draw_image(id: int, dx: int, dy: int) -> void {
if id < 0 { return }
if id >= img_n { return }
let w = peek32(img_w, id)
let h = peek32(img_h, id)
let s = peekp(img_px, id)
let w = img_w[id]
let h = img_h[id]
let s: words = peekp(img_px, id)
for y in 0 .. h {
for x in 0 .. w {
rt_blend_px(dx + x, dy + y, peek32(s, y * w + x))
rt_blend_px(dx + x, dy + y, s[y * w + x])
}
}
}
@ -278,12 +278,12 @@ fn rt_draw_image_scaled(id: int, dx: int, dy: int, dw: int, dh: int) -> void {
if id >= img_n { return }
if dw <= 0 { return }
if dh <= 0 { return }
let w = peek32(img_w, id)
let h = peek32(img_h, id)
let s = peekp(img_px, id)
let w = img_w[id]
let h = img_h[id]
let s: words = peekp(img_px, id)
for y in 0 .. dh {
for x in 0 .. dw {
rt_blend_px(dx + x, dy + y, peek32(s, (y * h / dh) * w + x * w / dw))
rt_blend_px(dx + x, dy + y, s[(y * h / dh) * w + x * w / dw])
}
}
}
@ -303,13 +303,13 @@ fn rt_draw_9slice(id: int, dx: int, dy: int, dw: int, dh: int, inset: int) -> vo
if id >= img_n { return }
if dw <= 0 { return }
if dh <= 0 { return }
let w = peek32(img_w, id)
let h = peek32(img_h, id)
let s = peekp(img_px, id)
let w = img_w[id]
let h = img_h[id]
let s: words = peekp(img_px, id)
for y in 0 .. dh {
let sy = rt_9map(y, dh, h, inset)
for x in 0 .. dw {
rt_blend_px(dx + x, dy + y, peek32(s, sy * w + rt_9map(x, dw, w, inset)))
rt_blend_px(dx + x, dy + y, s[sy * w + rt_9map(x, dw, w, inset)])
}
}
}
@ -326,11 +326,11 @@ fn rt_png_load(path: str) -> int {
var px = 0
if x < png_w {
if y < png_h {
let c = peek32(png_px, y * png_w + x)
let c = png_px[y * png_w + x]
if ((c >> 24) & 255) >= 128 { px = ((255 << 24) | (c & 16777215)) }
}
}
poke32(spr_px, base + y * SPR_SZ + x, px)
spr_px[base + y * SPR_SZ + x] = px
}
}
mem_free(png_px)
@ -344,7 +344,7 @@ fn rt_draw_sprite(id: int, px: int, py: int) -> void {
let base = id * SPR_SZ * SPR_SZ
for y in 0 .. SPR_SZ {
for x in 0 .. SPR_SZ {
let p = peek32(spr_px, base + y * SPR_SZ + x)
let p = spr_px[base + y * SPR_SZ + x]
if ((p >> 24) & 255) != 0 {
rt_put_px(px + x, py + y, (p & 16777215))
}
@ -359,7 +359,7 @@ fn rt_draw_sprite_scaled(id: int, px: int, py: int, sc: int) -> void {
let base = id * SPR_SZ * SPR_SZ
for y in 0 .. SPR_SZ {
for x in 0 .. SPR_SZ {
let p = peek32(spr_px, base + y * SPR_SZ + x)
let p = spr_px[base + y * SPR_SZ + x]
if ((p >> 24) & 255) != 0 {
rt_fill_rect(px + x * sc, py + y * sc, sc, sc, (p & 16777215))
}
@ -379,7 +379,7 @@ fn rt_sprites_load(path: str) -> void {
let d = rt_read_file(path)
if (d == null) { spr_n = 0; return }
let size = rt_file_len
let pal = mem_alloc(128 * 4)
let pal: words = mem_alloc(128 * 4)
mem_set(pal, 0, 128 * 4)
var cur = 0 - 1
var row = 0
@ -400,7 +400,7 @@ fn rt_sprites_load(path: str) -> void {
let hv = rt_hexval(d[start + 6 + k])
if hv >= 0 { v = v * 16 + hv }
}
if ch < 128 { poke32(pal, ch, ((255 << 24) | v)) }
if ch < 128 { pal[ch] = ((255 << 24) | v) }
}
if c0 == 115 { # 's' — "spr" starts a new sprite
cur = 0 - 1
@ -418,8 +418,7 @@ fn rt_sprites_load(path: str) -> void {
if row < SPR_SZ {
for x in 0 .. SPR_SZ {
if x < len {
poke32(spr_px, cur * SPR_SZ * SPR_SZ + row * SPR_SZ + x,
peek32(pal, (d[start + x] & 127)))
spr_px[cur * SPR_SZ * SPR_SZ + row * SPR_SZ + x] = pal[(d[start + x] & 127)]
}
}
row = row + 1

View file

@ -53,40 +53,40 @@ fn z_table_new(nsym: int) -> ptr {
}
# lengths[i] = code length of symbol i (0 = symbol unused)
fn z_table_build(table: ptr, lengths: ptr, n: int) -> void {
fn z_table_build(table: words, lengths: words, n: int) -> void {
for i in 0 .. 16 {
poke32(table, i, 0)
table[i] = 0
}
for s in 0 .. n {
let l = peek32(lengths, s)
poke32(table, l, peek32(table, l) + 1)
let l = lengths[s]
table[l] = table[l] + 1
}
poke32(table, 0, 0) # length 0 means "not present"
table[0] = 0 # length 0 means "not present"
# offset of each length's first symbol
let offs = mem_alloc(16 * 4)
poke32(offs, 1, 0)
let offs: words = mem_alloc(16 * 4)
offs[1] = 0
for l in 1 .. 15 {
poke32(offs, l + 1, peek32(offs, l) + peek32(table, l))
offs[l + 1] = offs[l] + table[l]
}
for s in 0 .. n {
let l = peek32(lengths, s)
let l = lengths[s]
if l != 0 {
poke32(table, 16 + peek32(offs, l), s)
poke32(offs, l, peek32(offs, l) + 1)
table[16 + offs[l]] = s
offs[l] = offs[l] + 1
}
}
mem_free(offs)
}
fn z_decode(table: ptr) -> int {
fn z_decode(table: words) -> int {
var code = 0
var first = 0
var index = 0
for len in 1 .. 16 {
code = (code | z_bits(1))
let count = peek32(table, len)
let count = table[len]
if code - first < count {
return peek32(table, 16 + index + (code - first))
return table[16 + index + (code - first)]
}
index = index + count
first = ((first + count) << 1)
@ -173,13 +173,13 @@ fn z_codes(out: ptr, at: int, cap: int, lit: ptr, dist: ptr) -> int {
}
fn z_fixed_tables(lit: ptr, dist: ptr) -> void {
let lengths = mem_alloc(288 * 4)
for i in 0 .. 144 { poke32(lengths, i, 8) }
for i in 144 .. 256 { poke32(lengths, i, 9) }
for i in 256 .. 280 { poke32(lengths, i, 7) }
for i in 280 .. 288 { poke32(lengths, i, 8) }
let lengths: words = mem_alloc(288 * 4)
for i in 0 .. 144 { lengths[i] = 8 }
for i in 144 .. 256 { lengths[i] = 9 }
for i in 256 .. 280 { lengths[i] = 7 }
for i in 280 .. 288 { lengths[i] = 8 }
z_table_build(lit, lengths, 288)
for i in 0 .. 30 { poke32(lengths, i, 5) }
for i in 0 .. 30 { lengths[i] = 5 }
z_table_build(dist, lengths, 30)
mem_free(lengths)
}
@ -191,13 +191,13 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
if nlen > 286 { return 0 }
if ndist > 30 { return 0 }
let lengths = mem_alloc(320 * 4)
for i in 0 .. 19 { poke32(lengths, i, 0) }
let lengths: words = mem_alloc(320 * 4)
for i in 0 .. 19 { lengths[i] = 0 }
# the code-length alphabet is transmitted in this fixed permutation
# 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 — biased by '0' so it is one literal
let order = "@AB08796:5;4<3=2>1?"
for i in 0 .. ncode {
poke32(lengths, order[i] - 48, z_bits(3))
lengths[order[i] - 48] = z_bits(3)
}
let clen = z_table_new(19)
z_table_build(clen, lengths, 19)
@ -207,7 +207,7 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
let sym = z_decode(clen)
if sym < 0 { return 0 }
if sym < 16 {
poke32(lengths, n, sym)
lengths[n] = sym
n = n + 1
}
if sym >= 16 {
@ -215,14 +215,14 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
var rep = 0
if sym == 16 {
if n == 0 { return 0 }
prev = peek32(lengths, n - 1)
prev = lengths[n - 1]
rep = 3 + z_bits(2)
}
if sym == 17 { rep = 3 + z_bits(3) }
if sym == 18 { rep = 11 + z_bits(7) }
for k in 0 .. rep {
if n < 320 {
poke32(lengths, n, prev)
lengths[n] = prev
n = n + 1
}
}
@ -230,8 +230,8 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
}
z_table_build(lit, lengths, nlen)
# the distance lengths follow the literal ones in the same buffer
let dl = mem_alloc(32 * 4)
for i in 0 .. ndist { poke32(dl, i, peek32(lengths, nlen + i)) }
let dl: words = mem_alloc(32 * 4)
for i in 0 .. ndist { dl[i] = lengths[nlen + i] }
z_table_build(dist, dl, ndist)
mem_free(dl)
mem_free(lengths)

View file

@ -19,26 +19,26 @@ const TT_GC: int = 512 # glyph cache entries
# ---- per-font tables (parallel arrays; index = font id) -------------------
var tt_data: ptr = null
var tt_size: ptr = null
var tt_upem: ptr = null
var tt_nglyf: ptr = null
var tt_locfm: ptr = null
var tt_nhm: ptr = null
var tt_asc: ptr = null
var tt_desc: ptr = null
var tt_gap: ptr = null
var tt_glyf: ptr = null
var tt_loca: ptr = null
var tt_hmtx: ptr = null
var tt_cmap: ptr = null
var tt_cfmt: ptr = null
var tt_size: words = null
var tt_upem: words = null
var tt_nglyf: words = null
var tt_locfm: words = null
var tt_nhm: words = null
var tt_asc: words = null
var tt_desc: words = null
var tt_gap: words = null
var tt_glyf: words = null
var tt_loca: words = null
var tt_hmtx: words = null
var tt_cmap: words = null
var tt_cfmt: words = null
var tt_n: int = 0
# ---- outline + edge scratch (reused for every glyph) ----------------------
var ol_x: ptr = null
var ol_y: ptr = null
var ol_on: ptr = null
var ol_ends: ptr = null
var ol_on: words = null
var ol_ends: words = null
var ol_n: int = 0
var ol_ne: int = 0
@ -49,18 +49,18 @@ var ed_y1: ptr = null
var ed_n: int = 0
var sc_x: ptr = null # scanline crossings
var sc_d: ptr = null
var sc_d: words = null
# ---- glyph cache ----------------------------------------------------------
var gc_used: ptr = null
var gc_font: ptr = null
var gc_cp: ptr = null
var gc_px: ptr = null
var gc_w: ptr = null
var gc_h: ptr = null
var gc_ox: ptr = null
var gc_oy: ptr = null
var gc_adv: ptr = null
var gc_used: words = null
var gc_font: words = null
var gc_cp: words = null
var gc_px: words = null
var gc_w: words = null
var gc_h: words = null
var gc_ox: words = null
var gc_oy: words = null
var gc_adv: words = null
var gc_bmp: ptr = null
fn rt_tt_init() -> void {
@ -139,8 +139,8 @@ fn tt_find_table(d: ptr, base: int, a: int, b: int, c: int, e: int) -> int {
# pick the most capable Unicode subtable, as the C loader did
fn tt_pick_cmap(id: int, d: ptr, co: int) -> void {
poke32(tt_cmap, id, 0 - 1)
poke32(tt_cfmt, id, 0)
tt_cmap[id] = 0 - 1
tt_cfmt[id] = 0
if co < 0 { return }
let n = tt_u16(d, co + 2)
var best = 0 - 1
@ -162,8 +162,8 @@ fn tt_pick_cmap(id: int, d: ptr, co: int) -> void {
}
}
if best < 0 { return }
poke32(tt_cmap, id, best)
poke32(tt_cfmt, id, tt_u16(d, best))
tt_cmap[id] = best
tt_cfmt[id] = tt_u16(d, best)
}
fn rt_font_load(path: str) -> int {
@ -194,19 +194,19 @@ fn rt_font_load(path: str) -> int {
let id = tt_n
tt_n = tt_n + 1
pokep(tt_data, id, d)
poke32(tt_size, id, size)
tt_size[id] = size
var upem = tt_u16(d, head + 18)
if upem <= 0 { upem = 1000 }
poke32(tt_upem, id, upem)
poke32(tt_locfm, id, tt_i16(d, head + 50))
poke32(tt_nglyf, id, tt_u16(d, maxp + 4))
poke32(tt_asc, id, tt_i16(d, hhea + 4))
poke32(tt_desc, id, tt_i16(d, hhea + 6))
poke32(tt_gap, id, tt_i16(d, hhea + 8))
poke32(tt_nhm, id, tt_u16(d, hhea + 34))
poke32(tt_glyf, id, glyf)
poke32(tt_loca, id, loca)
poke32(tt_hmtx, id, hmtx)
tt_upem[id] = upem
tt_locfm[id] = tt_i16(d, head + 50)
tt_nglyf[id] = tt_u16(d, maxp + 4)
tt_asc[id] = tt_i16(d, hhea + 4)
tt_desc[id] = tt_i16(d, hhea + 6)
tt_gap[id] = tt_i16(d, hhea + 8)
tt_nhm[id] = tt_u16(d, hhea + 34)
tt_glyf[id] = glyf
tt_loca[id] = loca
tt_hmtx[id] = hmtx
tt_pick_cmap(id, d, tt_find_table(d, base, 99, 109, 97, 112))
return id
}
@ -214,9 +214,9 @@ fn rt_font_load(path: str) -> int {
# ---- codepoint -> glyph id ------------------------------------------------
fn tt_glyph_index(id: int, cp: int) -> int {
let d = peekp(tt_data, id)
let s = peek32(tt_cmap, id)
let s = tt_cmap[id]
if s < 0 { return 0 }
let fmt = peek32(tt_cfmt, id)
let fmt = tt_cfmt[id]
if fmt == 4 {
if cp > 65535 { return 0 }
@ -263,8 +263,8 @@ fn tt_glyph_index(id: int, cp: int) -> int {
fn tt_advance(id: int, gid: int) -> int {
let d = peekp(tt_data, id)
let hmtx = peek32(tt_hmtx, id)
let n = peek32(tt_nhm, id)
let hmtx = tt_hmtx[id]
let n = tt_nhm[id]
if gid < n { return tt_u16(d, hmtx + gid * 4) }
return tt_u16(d, hmtx + (n - 1) * 4)
}
@ -274,33 +274,33 @@ fn ol_pt(x: fixed, y: fixed, on: int) -> void {
if ol_n >= TT_PTS { return }
pokef(ol_x, ol_n, x)
pokef(ol_y, ol_n, y)
poke32(ol_on, ol_n, on)
ol_on[ol_n] = on
ol_n = ol_n + 1
}
fn tt_glyph_start(id: int, gid: int) -> int {
let d = peekp(tt_data, id)
let loca = peek32(tt_loca, id)
if peek32(tt_locfm, id) == 0 { return tt_u16(d, loca + gid * 2) * 2 }
let loca = tt_loca[id]
if tt_locfm[id] == 0 { return tt_u16(d, loca + gid * 2) * 2 }
return tt_u32(d, loca + gid * 4)
}
fn tt_glyph_end(id: int, gid: int) -> int {
let d = peekp(tt_data, id)
let loca = peek32(tt_loca, id)
if peek32(tt_locfm, id) == 0 { return tt_u16(d, loca + (gid + 1) * 2) * 2 }
let loca = tt_loca[id]
if tt_locfm[id] == 0 { return tt_u16(d, loca + (gid + 1) * 2) * 2 }
return tt_u32(d, loca + (gid + 1) * 4)
}
# Append glyph `gid`, transformed by [a b c e] + (dx,dy), to the outline.
fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx: fixed, dy: fixed, depth: int) -> void {
if gid < 0 { return }
if gid >= peek32(tt_nglyf, id) { return }
if gid >= tt_nglyf[id] { return }
if depth > 5 { return }
let goff = tt_glyph_start(id, gid)
let gend = tt_glyph_end(id, gid)
if goff >= gend { return }
let d = peekp(tt_data, id)
let g = peek32(tt_glyf, id) + goff
let g = tt_glyf[id] + goff
let nc = tt_i16(d, g)
if nc >= 0 {
@ -327,8 +327,8 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
}
}
let xs = mem_alloc((npts + 1) * 4)
let ys = mem_alloc((npts + 1) * 4)
let xs: words = mem_alloc((npts + 1) * 4)
let ys: words = mem_alloc((npts + 1) * 4)
var xv = 0
for k in 0 .. npts {
let fl = flags[k]
@ -339,7 +339,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
} else {
if (fl & 16) == 0 { xv = xv + tt_i16(d, p); p = p + 2 }
}
poke32(xs, k, xv)
xs[k] = xv
}
var yv = 0
for k in 0 .. npts {
@ -351,19 +351,19 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
} else {
if (fl & 32) == 0 { yv = yv + tt_i16(d, p); p = p + 2 }
}
poke32(ys, k, yv)
ys[k] = yv
}
var start = 0
for ci in 0 .. nc {
let last = tt_u16(d, endpts + ci * 2)
for k in start .. last + 1 {
let X = fx(peek32(xs, k))
let Y = fx(peek32(ys, k))
let X = fx(xs[k])
let Y = fx(ys[k])
ol_pt(a * X + c * Y + dx, b * X + e * Y + dy, (flags[k] & 1))
}
if ol_ne < 256 {
poke32(ol_ends, ol_ne, ol_n)
ol_ends[ol_ne] = ol_n
ol_ne = ol_ne + 1
}
start = last + 1

View file

@ -52,7 +52,7 @@ var gr_oy: int = 0
var gr_adv: int = 0
fn tt_raster(id: int, gid: int, px: int) -> ptr {
let upem = peek32(tt_upem, id)
let upem = tt_upem[id]
let scale = fx(px) / upem
gr_adv = flr(fx(tt_advance(id, gid)) * scale + 0.5)
gr_w = 0
@ -95,12 +95,12 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
# build edges in supersample space (y grows downward)
var start = 0
for ci in 0 .. ol_ne {
let end = peek32(ol_ends, ci)
let end = ol_ends[ci]
let cnt = end - start
if cnt >= 2 {
var first_on = 0 - 1
for i in 0 .. cnt {
if first_on < 0 { if peek32(ol_on, start + i) == 1 { first_on = i } }
if first_on < 0 { if ol_on[start + i] == 1 { first_on = i } }
}
var sx = 0.0
var sy = 0.0
@ -122,7 +122,7 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
let i = (first_on + 1 + step) % cnt
let ix = (peekf(ol_x, start + i) * scale - fx(x0)) * TT_SS
let iy = (fx(y1) - peekf(ol_y, start + i) * scale) * TT_SS
if peek32(ol_on, start + i) == 1 {
if ol_on[start + i] == 1 {
ed_add(curx, cury, ix, iy)
curx = ix
cury = iy
@ -132,7 +132,7 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
let jy = (fx(y1) - peekf(ol_y, start + j) * scale) * TT_SS
var ex = jx
var ey = jy
if peek32(ol_on, start + j) == 1 {
if ol_on[start + j] == 1 {
step = step + 1
} else {
ex = (ix + jx) / 2 # implied on-curve midpoint
@ -167,37 +167,37 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
if hit == 1 {
let t = (yc - ya) / (yb - ya)
pokef(sc_x, m, peekf(ed_x0, i) + t * (peekf(ed_x1, i) - peekf(ed_x0, i)))
if yb > ya { poke32(sc_d, m, 1) } else { poke32(sc_d, m, 0 - 1) }
if yb > ya { sc_d[m] = 1 } else { sc_d[m] = 0 - 1 }
m = m + 1
}
}
# insertion sort the crossings by x
for i in 1 .. m {
let kx = peekf(sc_x, i)
let kd = peek32(sc_d, i)
let kd = sc_d[i]
var j = i - 1
var placed = 0
while j >= 0 {
if peekf(sc_x, j) > kx {
pokef(sc_x, j + 1, peekf(sc_x, j))
poke32(sc_d, j + 1, peek32(sc_d, j))
sc_d[j + 1] = sc_d[j]
j = j - 1
} else {
pokef(sc_x, j + 1, kx)
poke32(sc_d, j + 1, kd)
sc_d[j + 1] = kd
placed = 1
j = 0 - 1
}
}
if placed == 0 {
pokef(sc_x, 0, kx)
poke32(sc_d, 0, kd)
sc_d[0] = kd
}
}
var wind = 0
let oy = sy / TT_SS
for i in 0 .. m - 1 {
wind = wind + peek32(sc_d, i)
wind = wind + sc_d[i]
if wind != 0 {
var xa = peekf(sc_x, i)
var xb = peekf(sc_x, i + 1)
@ -239,30 +239,30 @@ fn tt_glyph_get(font: int, cp: int, px: int) -> int {
let h = gc_hash(font, cp, px)
for k in 0 .. 8 {
let s = ((h + k) & (TT_GC - 1))
if peek32(gc_used, s) == 1 {
if peek32(gc_font, s) == font {
if peek32(gc_cp, s) == cp {
if peek32(gc_px, s) == px { return s }
if gc_used[s] == 1 {
if gc_font[s] == font {
if gc_cp[s] == cp {
if gc_px[s] == px { return s }
}
}
}
}
let s = h
if peek32(gc_used, s) == 1 {
if gc_used[s] == 1 {
let old = peekp(gc_bmp, s)
if (old == null) == false { mem_free(old) }
}
let bmp = tt_raster(font, tt_glyph_index(font, cp), px)
pokep(gc_bmp, s, bmp)
poke32(gc_used, s, 1)
poke32(gc_font, s, font)
poke32(gc_cp, s, cp)
poke32(gc_px, s, px)
poke32(gc_w, s, gr_w)
poke32(gc_h, s, gr_h)
poke32(gc_ox, s, gr_ox)
poke32(gc_oy, s, gr_oy)
poke32(gc_adv, s, gr_adv)
gc_used[s] = 1
gc_font[s] = font
gc_cp[s] = cp
gc_px[s] = px
gc_w[s] = gr_w
gc_h[s] = gr_h
gc_ox[s] = gr_ox
gc_oy[s] = gr_oy
gc_adv[s] = gr_adv
return s
}
@ -313,11 +313,11 @@ fn tt_blit(bmp: ptr, w: int, h: int, dx: int, dy: int, colour: int) -> void {
let pxx = dx + x
if pxx >= 0 {
if pxx < rt_fbw {
let d = peek32(rt_fb, py * rt_fbw + pxx)
let d = rt_fb[py * rt_fbw + pxx]
let rr = (cr * a + ((d >> 16) & 255) * (255 - a)) / 255
let rg = (cg * a + ((d >> 8) & 255) * (255 - a)) / 255
let rb = (cb * a + (d & 255) * (255 - a)) / 255
poke32(rt_fb, py * rt_fbw + pxx, (((rr << 16) | (rg << 8)) | rb))
rt_fb[py * rt_fbw + pxx] = (((rr << 16) | (rg << 8)) | rb)
}
}
}
@ -331,10 +331,10 @@ fn rt_text_ttf(font: int, x: int, y: int, s: str, colour: int, px: int) -> void
if font < 0 { return }
if font >= tt_n { return }
if px <= 0 { return }
let upem = peek32(tt_upem, font)
let upem = tt_upem[font]
let scale = fx(px) / upem
var baseline = y + flr(fx(peek32(tt_asc, font)) * scale + 0.5)
let lineh = flr(fx(peek32(tt_asc, font) - peek32(tt_desc, font) + peek32(tt_gap, font)) * scale + 0.5)
var baseline = y + flr(fx(tt_asc[font]) * scale + 0.5)
let lineh = flr(fx(tt_asc[font] - tt_desc[font] + tt_gap[font]) * scale + 0.5)
var penx = x
var i = 0
while s[i] != 0 {
@ -345,8 +345,8 @@ fn rt_text_ttf(font: int, x: int, y: int, s: str, colour: int, px: int) -> void
baseline = baseline + lineh
} else {
let g = tt_glyph_get(font, cp, px)
tt_blit(peekp(gc_bmp, g), peek32(gc_w, g), peek32(gc_h, g), penx + peek32(gc_ox, g), baseline - peek32(gc_oy, g), colour)
penx = penx + peek32(gc_adv, g)
tt_blit(peekp(gc_bmp, g), gc_w[g], gc_h[g], penx + gc_ox[g], baseline - gc_oy[g], colour)
penx = penx + gc_adv[g]
}
}
}
@ -365,7 +365,7 @@ fn rt_text_w(font: int, s: str, px: int) -> int {
if w > best { best = w }
w = 0
} else {
w = w + peek32(gc_adv, tt_glyph_get(font, cp, px))
w = w + gc_adv[tt_glyph_get(font, cp, px)]
}
}
if w > best { best = w }
@ -375,6 +375,6 @@ fn rt_text_w(font: int, s: str, px: int) -> int {
fn rt_text_h(font: int, px: int) -> int {
if font < 0 { return 0 }
if font >= tt_n { return 0 }
let scale = fx(px) / peek32(tt_upem, font)
return flr(fx(peek32(tt_asc, font) - peek32(tt_desc, font) + peek32(tt_gap, font)) * scale + 0.5)
let scale = fx(px) / tt_upem[font]
return flr(fx(tt_asc[font] - tt_desc[font] + tt_gap[font]) * scale + 0.5)
}

View file

@ -45,33 +45,33 @@ const K_VISIBLE: int = 18
const K_IMG: int = 19
const K_SKIN: int = 20
var ui_type: ptr = null
var ui_parent: ptr = null
var ui_w: ptr = null
var ui_h: ptr = null
var ui_x: ptr = null
var ui_y: ptr = null
var ui_haspos: ptr = null
var ui_pad: ptr = null
var ui_gap: ptr = null
var ui_bg: ptr = null
var ui_fg: ptr = null
var ui_border: ptr = null
var ui_align: ptr = null
var ui_grow: ptr = null
var ui_font: ptr = null
var ui_size: ptr = null
var ui_skin: ptr = null
var ui_inset: ptr = null
var ui_img: ptr = null
var ui_focusable: ptr = null
var ui_rx: ptr = null
var ui_ry: ptr = null
var ui_rw: ptr = null
var ui_rh: ptr = null
var ui_visible: ptr = null
var ui_fired: ptr = null
var ui_hasdyn: ptr = null
var ui_type: words = null
var ui_parent: words = null
var ui_w: words = null
var ui_h: words = null
var ui_x: words = null
var ui_y: words = null
var ui_haspos: words = null
var ui_pad: words = null
var ui_gap: words = null
var ui_bg: words = null
var ui_fg: words = null
var ui_border: words = null
var ui_align: words = null
var ui_grow: words = null
var ui_font: words = null
var ui_size: words = null
var ui_skin: words = null
var ui_inset: words = null
var ui_img: words = null
var ui_focusable: words = null
var ui_rx: words = null
var ui_ry: words = null
var ui_rw: words = null
var ui_rh: words = null
var ui_visible: words = null
var ui_fired: words = null
var ui_hasdyn: words = null
var ui_dyn: ptr = null # UI_MAX * 96 bytes
var ui_text: ptr = null # UI_MAX static string pointers
@ -115,29 +115,29 @@ fn rt_ui_init() -> void {
fn rt_ui_reset(n: int) -> void {
ui_n = n
for i in 0 .. n {
poke32(ui_type, i, 0)
poke32(ui_parent, i, 0 - 1)
poke32(ui_w, i, 0)
poke32(ui_h, i, 0)
poke32(ui_x, i, 0)
poke32(ui_y, i, 0)
poke32(ui_haspos, i, 0)
poke32(ui_pad, i, 0)
poke32(ui_gap, i, 0)
poke32(ui_bg, i, 0 - 1)
poke32(ui_fg, i, 0 - 1)
poke32(ui_border, i, 0 - 1)
poke32(ui_align, i, 0 - 1)
poke32(ui_grow, i, 0)
poke32(ui_font, i, 0 - 1)
poke32(ui_size, i, 8)
poke32(ui_skin, i, 0 - 1)
poke32(ui_inset, i, 6)
poke32(ui_img, i, 0 - 1)
poke32(ui_focusable, i, 0)
poke32(ui_visible, i, 1)
poke32(ui_fired, i, 0)
poke32(ui_hasdyn, i, 0)
ui_type[i] = 0
ui_parent[i] = 0 - 1
ui_w[i] = 0
ui_h[i] = 0
ui_x[i] = 0
ui_y[i] = 0
ui_haspos[i] = 0
ui_pad[i] = 0
ui_gap[i] = 0
ui_bg[i] = 0 - 1
ui_fg[i] = 0 - 1
ui_border[i] = 0 - 1
ui_align[i] = 0 - 1
ui_grow[i] = 0
ui_font[i] = 0 - 1
ui_size[i] = 8
ui_skin[i] = 0 - 1
ui_inset[i] = 6
ui_img[i] = 0 - 1
ui_focusable[i] = 0
ui_visible[i] = 1
ui_fired[i] = 0
ui_hasdyn[i] = 0
pokep(ui_text, i, null)
}
}
@ -145,27 +145,27 @@ fn rt_ui_reset(n: int) -> void {
fn rt_ui_set(i: int, k: int, v: int) -> void {
if i < 0 { return }
if i >= UI_MAX { return }
if k == K_TYPE { poke32(ui_type, i, v) }
if k == K_PARENT { poke32(ui_parent, i, v) }
if k == K_W { poke32(ui_w, i, v) }
if k == K_H { poke32(ui_h, i, v) }
if k == K_X { poke32(ui_x, i, v) }
if k == K_Y { poke32(ui_y, i, v) }
if k == K_HASPOS { poke32(ui_haspos, i, v) }
if k == K_PAD { poke32(ui_pad, i, v) }
if k == K_GAP { poke32(ui_gap, i, v) }
if k == K_BG { poke32(ui_bg, i, v) }
if k == K_FG { poke32(ui_fg, i, v) }
if k == K_BORDER { poke32(ui_border, i, v) }
if k == K_ALIGN { poke32(ui_align, i, v) }
if k == K_GROW { poke32(ui_grow, i, v) }
if k == K_FONT { poke32(ui_font, i, v) }
if k == K_SIZE { poke32(ui_size, i, v) }
if k == K_SKININSET { poke32(ui_inset, i, v) }
if k == K_FOCUSABLE { poke32(ui_focusable, i, v) }
if k == K_VISIBLE { poke32(ui_visible, i, v) }
if k == K_IMG { poke32(ui_img, i, v) }
if k == K_SKIN { poke32(ui_skin, i, v) }
if k == K_TYPE { ui_type[i] = v }
if k == K_PARENT { ui_parent[i] = v }
if k == K_W { ui_w[i] = v }
if k == K_H { ui_h[i] = v }
if k == K_X { ui_x[i] = v }
if k == K_Y { ui_y[i] = v }
if k == K_HASPOS { ui_haspos[i] = v }
if k == K_PAD { ui_pad[i] = v }
if k == K_GAP { ui_gap[i] = v }
if k == K_BG { ui_bg[i] = v }
if k == K_FG { ui_fg[i] = v }
if k == K_BORDER { ui_border[i] = v }
if k == K_ALIGN { ui_align[i] = v }
if k == K_GROW { ui_grow[i] = v }
if k == K_FONT { ui_font[i] = v }
if k == K_SIZE { ui_size[i] = v }
if k == K_SKININSET { ui_inset[i] = v }
if k == K_FOCUSABLE { ui_focusable[i] = v }
if k == K_VISIBLE { ui_visible[i] = v }
if k == K_IMG { ui_img[i] = v }
if k == K_SKIN { ui_skin[i] = v }
}
fn rt_ui_static_text(i: int, s: str) -> void {
@ -176,7 +176,7 @@ fn rt_ui_static_text(i: int, s: str) -> void {
# ---- text helpers: TrueType when a font is loaded, bitmap otherwise -------
fn ui_str(i: int) -> ptr {
if peek32(ui_hasdyn, i) == 1 { return ptr_add(ui_dyn, i * 96) }
if ui_hasdyn[i] == 1 { return ptr_add(ui_dyn, i * 96) }
let t = peekp(ui_text, i)
if (t == null) { return ptr_add(ui_dyn, i * 96) }
return t
@ -222,7 +222,7 @@ fn ui_under(i: int, root: int) -> bool {
var k = i
while k >= 0 {
if k == root { return true }
k = peek32(ui_parent, k)
k = ui_parent[k]
}
return false
}
@ -231,55 +231,55 @@ fn ui_under(i: int, root: int) -> bool {
fn ui_measure() -> void {
var i = ui_n - 1
while i >= 0 {
if peek32(ui_visible, i) == 0 {
poke32(ui_rw, i, 0)
poke32(ui_rh, i, 0)
if ui_visible[i] == 0 {
ui_rw[i] = 0
ui_rh[i] = 0
} else {
let t = peek32(ui_type, i)
let pad = peek32(ui_pad, i)
let t = ui_type[i]
let pad = ui_pad[i]
var cw = 0
var ch = 0
if t == WT_LABEL { cw = 0 - 1 }
if t == WT_BUTTON { cw = 0 - 1 }
if cw == 0 - 1 {
let s = ui_str(i)
cw = ui_tw(peek32(ui_font, i), s, peek32(ui_size, i)) + 2 * pad
ch = ui_th(peek32(ui_font, i), peek32(ui_size, i)) + 2 * pad
cw = ui_tw(ui_font[i], s, ui_size[i]) + 2 * pad
ch = ui_th(ui_font[i], ui_size[i]) + 2 * pad
if t == WT_BUTTON {
cw = cw + 10
ch = ch + 6
}
} else {
if t == WT_IMAGE {
let im = peek32(ui_img, i)
let im = ui_img[i]
if im >= 0 {
cw = peek32(img_w, im)
ch = peek32(img_h, im)
cw = img_w[im]
ch = img_h[im]
}
} else {
if t == WT_SPACER {
cw = peek32(ui_w, i)
ch = peek32(ui_h, i)
cw = ui_w[i]
ch = ui_h[i]
} else {
let dir = ui_dir(t)
var mainsz = 0
var cross = 0
var nc = 0
for c in 0 .. ui_n {
if peek32(ui_parent, c) == i {
if peek32(ui_visible, c) == 1 {
if ui_parent[c] == i {
if ui_visible[c] == 1 {
if dir == 1 {
mainsz = mainsz + peek32(ui_rh, c)
cross = max(cross, peek32(ui_rw, c))
mainsz = mainsz + ui_rh[c]
cross = max(cross, ui_rw[c])
} else {
mainsz = mainsz + peek32(ui_rw, c)
cross = max(cross, peek32(ui_rh, c))
mainsz = mainsz + ui_rw[c]
cross = max(cross, ui_rh[c])
}
nc = nc + 1
}
}
}
if nc > 1 { mainsz = mainsz + peek32(ui_gap, i) * (nc - 1) }
if nc > 1 { mainsz = mainsz + ui_gap[i] * (nc - 1) }
mainsz = mainsz + 2 * pad
cross = cross + 2 * pad
if dir == 1 {
@ -292,20 +292,20 @@ fn ui_measure() -> void {
}
}
}
var rw = peek32(ui_w, i)
var rh = peek32(ui_h, i)
var rw = ui_w[i]
var rh = ui_h[i]
if rw == 0 { rw = cw }
if rh == 0 { rh = ch }
poke32(ui_rw, i, rw)
poke32(ui_rh, i, rh)
ui_rw[i] = rw
ui_rh[i] = rh
}
i = i - 1
}
}
fn ui_eff_align(child: int, parent: int) -> int {
if peek32(ui_align, child) >= 0 { return peek32(ui_align, child) }
if peek32(ui_align, parent) >= 0 { return peek32(ui_align, parent) }
if ui_align[child] >= 0 { return ui_align[child] }
if ui_align[parent] >= 0 { return ui_align[parent] }
return 0
}

View file

@ -3,24 +3,24 @@
# ---- arrange: rects, parents before children ------------------------------
fn ui_arrange(i: int, x: int, y: int) -> void {
poke32(ui_rx, i, x)
poke32(ui_ry, i, y)
let t = peek32(ui_type, i)
ui_rx[i] = x
ui_ry[i] = y
let t = ui_type[i]
if ui_is_container(t) == false { return }
let dir = ui_dir(t)
let pad = peek32(ui_pad, i)
let gap = peek32(ui_gap, i)
let innerw = peek32(ui_rw, i) - 2 * pad
let innerh = peek32(ui_rh, i) - 2 * pad
let pad = ui_pad[i]
let gap = ui_gap[i]
let innerw = ui_rw[i] - 2 * pad
let innerh = ui_rh[i] - 2 * pad
var total = 0
var ng = 0
var grows = 0
for c in 0 .. ui_n {
if peek32(ui_parent, c) == i {
if peek32(ui_visible, c) == 1 {
if dir == 1 { total = total + peek32(ui_rh, c) } else { total = total + peek32(ui_rw, c) }
if ui_parent[c] == i {
if ui_visible[c] == 1 {
if dir == 1 { total = total + ui_rh[c] } else { total = total + ui_rw[c] }
ng = ng + 1
grows = grows + peek32(ui_grow, c)
grows = grows + ui_grow[c]
}
}
}
@ -31,18 +31,18 @@ fn ui_arrange(i: int, x: int, y: int) -> void {
var cx = x + pad
var cy = y + pad
for c in 0 .. ui_n {
if peek32(ui_parent, c) == i {
if peek32(ui_visible, c) == 1 {
var kw = peek32(ui_rw, c)
var kh = peek32(ui_rh, c)
if peek32(ui_grow, c) != 0 {
if ui_parent[c] == i {
if ui_visible[c] == 1 {
var kw = ui_rw[c]
var kh = ui_rh[c]
if ui_grow[c] != 0 {
if grows > 0 {
if dir == 1 { kh = kh + extra / grows } else { kw = kw + extra / grows }
}
}
let al = ui_eff_align(c, i)
poke32(ui_rw, c, kw)
poke32(ui_rh, c, kh)
ui_rw[c] = kw
ui_rh[c] = kh
if dir == 1 {
var ax = x + pad
if al == 1 { ax = x + pad + (innerw - kw) / 2 }
@ -64,11 +64,11 @@ fn ui_arrange(i: int, x: int, y: int) -> void {
fn ui_layout() -> void {
if ui_active < 0 { return }
ui_measure()
var px = (rt_fbw - peek32(ui_rw, ui_active)) / 2
var py = (rt_fbh - peek32(ui_rh, ui_active)) / 2
if peek32(ui_haspos, ui_active) == 1 {
px = peek32(ui_x, ui_active)
py = peek32(ui_y, ui_active)
var px = (rt_fbw - ui_rw[ui_active]) / 2
var py = (rt_fbh - ui_rh[ui_active]) / 2
if ui_haspos[ui_active] == 1 {
px = ui_x[ui_active]
py = ui_y[ui_active]
}
ui_arrange(ui_active, px, py)
}
@ -85,77 +85,77 @@ fn ui_lighten(c: int) -> int {
}
fn ui_draw_node(i: int) -> void {
if peek32(ui_visible, i) == 0 { return }
let t = peek32(ui_type, i)
let rx = peek32(ui_rx, i)
let ry = peek32(ui_ry, i)
let rw = peek32(ui_rw, i)
let rh = peek32(ui_rh, i)
let skin = peek32(ui_skin, i)
if ui_visible[i] == 0 { return }
let t = ui_type[i]
let rx = ui_rx[i]
let ry = ui_ry[i]
let rw = ui_rw[i]
let rh = ui_rh[i]
let skin = ui_skin[i]
var focused = 0
if i == ui_focus { focused = 1 }
if t == WT_PANEL {
if skin >= 0 {
rt_draw_9slice(skin, rx, ry, rw, rh, peek32(ui_inset, i))
rt_draw_9slice(skin, rx, ry, rw, rh, ui_inset[i])
} else {
if peek32(ui_bg, i) >= 0 { rt_fill_rect(rx, ry, rw, rh, peek32(ui_bg, i)) }
if peek32(ui_border, i) >= 0 { rt_frame_rect(rx, ry, rw, rh, peek32(ui_border, i)) }
if ui_bg[i] >= 0 { rt_fill_rect(rx, ry, rw, rh, ui_bg[i]) }
if ui_border[i] >= 0 { rt_frame_rect(rx, ry, rw, rh, ui_border[i]) }
}
}
if t == WT_BUTTON {
var bg = peek32(ui_bg, i)
var bg = ui_bg[i]
if bg < 0 { bg = 0x2a2a3a }
if focused == 1 { bg = ui_lighten(bg) }
if skin >= 0 {
rt_draw_9slice(skin, rx, ry, rw, rh, peek32(ui_inset, i))
rt_draw_9slice(skin, rx, ry, rw, rh, ui_inset[i])
} else {
rt_fill_rect(rx, ry, rw, rh, bg)
}
var bc = peek32(ui_border, i)
var bc = ui_border[i]
if bc < 0 { bc = 0x555577 }
if focused == 1 { bc = 0xffff00 }
rt_frame_rect(rx, ry, rw, rh, bc)
let s = ui_str(i)
var fg = peek32(ui_fg, i)
var fg = ui_fg[i]
if fg < 0 { fg = 0xffffff }
let font = peek32(ui_font, i)
let size = peek32(ui_size, i)
let font = ui_font[i]
let size = ui_size[i]
ui_draw_text(font, rx + (rw - ui_tw(font, s, size)) / 2, ry + (rh - ui_th(font, size)) / 2, s, fg, size)
}
if t == WT_LABEL {
let s = ui_str(i)
var fg = peek32(ui_fg, i)
var fg = ui_fg[i]
if fg < 0 { fg = 0xffffff }
let font = peek32(ui_font, i)
let size = peek32(ui_size, i)
let pad = peek32(ui_pad, i)
let font = ui_font[i]
let size = ui_size[i]
let pad = ui_pad[i]
let tw = ui_tw(font, s, size)
var tx = rx + pad
let al = peek32(ui_align, i)
let al = ui_align[i]
if al == 1 { tx = rx + (rw - tw) / 2 }
if al == 2 { tx = rx + rw - pad - tw }
ui_draw_text(font, tx, ry + pad, s, fg, size)
}
if t == WT_IMAGE {
if peek32(ui_img, i) >= 0 { rt_draw_image_scaled(peek32(ui_img, i), rx, ry, rw, rh) }
if ui_img[i] >= 0 { rt_draw_image_scaled(ui_img[i], rx, ry, rw, rh) }
}
for c in 0 .. ui_n {
if peek32(ui_parent, c) == i { ui_draw_node(c) }
if ui_parent[c] == i { ui_draw_node(c) }
}
}
# ---- focus and activation -------------------------------------------------
var ui_fl: ptr = null
var ui_fl: words = null
fn ui_focusables() -> int {
if (ui_fl == null) { ui_fl = mem_alloc(UI_MAX * 4) }
var n = 0
for i in 0 .. ui_n {
if peek32(ui_focusable, i) == 1 {
if peek32(ui_visible, i) == 1 {
if ui_focusable[i] == 1 {
if ui_visible[i] == 1 {
if ui_under(i, ui_active) {
poke32(ui_fl, n, i)
ui_fl[n] = i
n = n + 1
}
}
@ -168,12 +168,12 @@ fn rt_ui_open(root: int) -> void {
ui_active = root
let n = ui_focusables()
ui_focus = 0 - 1
if n > 0 { ui_focus = peek32(ui_fl, 0) }
if n > 0 { ui_focus = ui_fl[0] }
}
fn rt_ui_tick(k: int) -> void {
if ui_active < 0 { return }
for i in 0 .. ui_n { poke32(ui_fired, i, 0) }
for i in 0 .. ui_n { ui_fired[i] = 0 }
ui_layout()
let n = ui_focusables()
if n == 0 {
@ -182,23 +182,23 @@ fn rt_ui_tick(k: int) -> void {
}
var cur = 0 - 1
for i in 0 .. n {
if peek32(ui_fl, i) == ui_focus { cur = i }
if ui_fl[i] == ui_focus { cur = i }
}
if cur < 0 {
cur = 0
ui_focus = peek32(ui_fl, 0)
ui_focus = ui_fl[0]
}
if k == 119 { # 'w'
cur = (cur - 1 + n) % n
ui_focus = peek32(ui_fl, cur)
ui_focus = ui_fl[cur]
}
if k == 115 { # 's'
cur = (cur + 1) % n
ui_focus = peek32(ui_fl, cur)
ui_focus = ui_fl[cur]
}
if k == 32 { poke32(ui_fired, ui_focus, 1) }
if k == 10 { poke32(ui_fired, ui_focus, 1) }
if k == 13 { poke32(ui_fired, ui_focus, 1) }
if k == 32 { ui_fired[ui_focus] = 1 }
if k == 10 { ui_fired[ui_focus] = 1 }
if k == 13 { ui_fired[ui_focus] = 1 }
}
fn rt_ui_render() -> void {
@ -211,7 +211,7 @@ fn rt_ui_render() -> void {
fn rt_ui_clicked(id: int) -> bool {
if id < 0 { return false }
if id >= ui_n { return false }
return peek32(ui_fired, id) == 1
return ui_fired[id] == 1
}
fn rt_ui_set_text(id: int, s: str) -> void {
@ -229,7 +229,7 @@ fn rt_ui_set_text(id: int, s: str) -> void {
}
}
ui_dyn[base + i] = 0
poke32(ui_hasdyn, id, 1)
ui_hasdyn[id] = 1
}
fn rt_ui_set_int(id: int, n: int) -> void {
@ -263,7 +263,7 @@ fn rt_ui_set_int(id: int, n: int) -> void {
}
}
ui_dyn[base + at] = 0
poke32(ui_hasdyn, id, 1)
ui_hasdyn[id] = 1
}
fn rt_ui_focus(id: int) -> void {
@ -279,5 +279,5 @@ fn rt_ui_focused() -> int {
fn rt_ui_visible(id: int, v: int) -> void {
if id < 0 { return }
if id >= ui_n { return }
poke32(ui_visible, id, v)
ui_visible[id] = v
}

View file

@ -21,9 +21,14 @@ fn emit_member_addr(e: Node) -> ptr {
# address of `base[index]` (slices only in this subset)
fn emit_index_addr(e: Node) -> ptr {
let base = emit_expr(e.a)
if not is_slice_ty(base.ty) { # a raw ptr/str: address of byte i.
if not is_slice_ty(base.ty) { # a raw pointer: address of element i
let bi = emit_expr(e.b) # (evaluate index first — it may set g_addr_ty)
let r = emit_bind(`getelementptr inbounds i8, ptr {base.code}, i32 {bi.code}`)
if (base.ty == "words") { # a `words` buffer: 32-bit elements
let rw = emit_bind(`getelementptr inbounds i32, ptr {base.code}, i32 {bi.code}`)
g_addr_ty = "int"
return rw
}
let r = emit_bind(`getelementptr inbounds i8, ptr {base.code}, i32 {bi.code}`) # a ptr/str: bytes
g_addr_ty = "byte" # set last so the caller sees it
return r
}

View file

@ -54,6 +54,7 @@ fn lbl(pfx: ptr) -> ptr { let r = (pfx + itoa(ll_lbl)); ll_lbl = ll_lbl + 1; r
fn llty(t: ptr) -> ptr {
if (t == "int") or (t == "bool") or (t == "fixed") { return "i32" }
if (t == "byte") { return "i8" } # a single byte (p[i] on a raw ptr)
if (t == "words") { return "ptr" } # a 32-bit-word buffer (w[i] is i32)
if (t == "void") { return "void" }
return "ptr"
}

View file

@ -13,7 +13,6 @@ fn is_intrinsic(name: ptr) -> bool {
if (name == "os_argc") or (name == "os_arg") or (name == "os_exit") or (name == "file_stderr") { return true }
if (name == "file_stdout") { return true }
if (name == "os_system") or (name == "os_getenv") { return true }
if (name == "peek32") or (name == "poke32") { return true }
return false
}
# emit " <r> = <rest>\n" and return r
@ -87,19 +86,7 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
return val(emit_bind(`call ptr @getenv(ptr {n})`), "str")
}
# bitwise ops are the operators & | ^ << >> ~ now (see emit_bin / p_mul).
if (name == "peek32") {
let p = arg_code(e, 0); let i = arg_code(e, 1)
let g = nreg()
emit(" "); emit(g); emit(" = getelementptr inbounds i32, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
return val(emit_bind(`load i32, ptr {g}`), "int")
}
if (name == "poke32") {
let p = arg_code(e, 0); let i = arg_code(e, 1); let v = arg_code(e, 2)
let g = nreg()
emit(" "); emit(g); emit(" = getelementptr inbounds i32, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
emit(" store i32 "); emit(v); emit(", ptr "); emit(g); emit("\n")
return val("0", "void")
}
# peek32 / poke32 are `words` indexing now: w[i] and w[i] = v (see emit_index_addr).
g_intrin_ok = false
return val("0", "void")
}

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@ program T {
print((1 << 4)) # 16
print(((4 | 1) & 6)) # 4
let p = mem_alloc(16)
poke32(p, 1, 9999)
print(peek32(p, 1)) # 9999
p[1] = 9999
print(p[1]) # 9999
}
}

View file

@ -52,7 +52,7 @@ object LudicVocabulary {
"enable", "disable", "match", "machine", "state", "become", "where",
"and", "or", "not", "break", "continue", "new"
)
val PRIMITIVES = setOf("int", "fixed", "bool", "entity", "str", "ptr", "void")
val PRIMITIVES = setOf("int", "fixed", "bool", "entity", "str", "ptr", "byte", "words", "void")
val PHASES = setOf("Start", "Input", "FixedUpdate", "Update", "LateUpdate", "Render")
val WIDGETS = setOf("panel", "col", "row", "label", "button", "image", "spacer")
@ -66,9 +66,8 @@ object LudicVocabulary {
"ui_focused", "ui_visible", "key", "reg", "set_reg", "self", "save", "load",
"status", "print", "str", "quit",
// compiler intrinsics: the floor the Ludic-written runtime stands on
"mem_alloc", "mem_realloc", "mem_free", "mem_copy", "mem_set", "peek32",
"os_argc", "os_arg", "file_stderr", "file_stdout",
"poke32", "peekp", "pokep", "peekf", "pokef", "ptr_add",
"mem_alloc", "mem_realloc", "mem_free", "mem_copy", "mem_set", "os_argc", "os_arg", "file_stderr", "file_stdout",
"peekp", "pokep", "peekf", "pokef", "ptr_add",
"as_fixed", "as_int", "file_open", "file_read", "file_write",
"file_seek", "file_tell", "file_close", "read_byte", "write_byte",
"str_len", "os_exit", "os_time",

View file

@ -177,7 +177,7 @@
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\b" },
{ "name": "storage.type.ludic", "match": "\\b(program|property|model|enum|ui|const|var|let|fn|handler|entry|state)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|byte|words|void)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\b" },
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
]
@ -209,7 +209,7 @@
{
"comment": "compiler intrinsics — these lower straight to libc or the OS",
"name": "support.function.intrinsic.ludic",
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peek32|poke32|peekp|pokep|peekf|pokef|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peekp|pokep|peekf|pokef|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
}
]
},

View file

@ -177,7 +177,7 @@
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\b" },
{ "name": "storage.type.ludic", "match": "\\b(program|property|model|enum|ui|const|var|let|fn|handler|entry|state)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|byte|words|void)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\b" },
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
]
@ -209,7 +209,7 @@
{
"comment": "compiler intrinsics — these lower straight to libc or the OS",
"name": "support.function.intrinsic.ludic",
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peek32|poke32|peekp|pokep|peekf|pokef|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peekp|pokep|peekf|pokef|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
}
]
},

View file

@ -71,7 +71,7 @@ static const char* LUDIC_KW_STMT[] = {
"and","or","not","break","continue","new", 0
};
static const char* LUDIC_TYPES[] = {
"int","fixed","bool","entity","str","ptr","void", 0
"int","fixed","bool","entity","str","ptr","byte","words","void", 0
};
static const char* LUDIC_PHASES[] = {
"Start","Input","FixedUpdate","Update","LateUpdate","Render", 0
@ -154,8 +154,6 @@ static const LBuiltin LUDIC_INTRINSICS[] = {
{"mem_free","mem_free(p: ptr)","Release an allocation."},
{"mem_copy","mem_copy(dst: ptr, src: ptr, n: int)","memcpy."},
{"mem_set","mem_set(p: ptr, byte: int, n: int)","memset."},
{"peek32","peek32(p: ptr, off: int) -> int","Read a 32-bit word."},
{"poke32","poke32(p: ptr, off: int, v: int)","Write a 32-bit word."},
{"peekp","peekp(p: ptr, off: int) -> ptr","Read a pointer-sized word."},
{"pokep","pokep(p: ptr, off: int, v: ptr)","Write a pointer-sized word."},
{"peekf","peekf(p: ptr, off: int) -> fixed","Read a fixed-point word."},