Phase 8a: finish typed indexing — fixeds/ptrs buffers (retire peekf/pokef/peekp/pokep/str_len)
The last peek/poke pairs were the same "typed buffer access wearing a C name" as peek8/peek32, so they become indexing too: peekf(sc_x, i) -> sc_x[i] (a `fixeds` buffer -> a fixed) pokep(gc_bmp, s,b) -> gc_bmp[s] = b (a `ptrs` buffer -> a pointer) str_len(s) -> len(s) (len is polymorphic since 7f; str_len was dead) Two new element-typed buffers join words: `fixeds` (32-bit fixed) and `ptrs` (pointer, 8-byte stride). emit_index_addr dispatches on the base type; IR is byte-identical to the old intrinsics. The peekf/peekp buffers (ed_x0/ed_x1/ol_x/sc_x fixed coords; gc_bmp/img_px/ tt_data/ui_text pointer arrays) were retyped scope-aware, then the 33 sites migrated to indexing. Two bugs found via a menu golden diff / a link-time type error and fixed: the buffer-name regex truncated digit suffixes (ed_x0 -> ed_x), and the char-literal brace-miscount skipped a few module declarations (same class as 7j). Reseeded (22371 lines); C-free fixpoint holds; goldens byte-identical; 18/18; vocab (fixeds/ptrs types in, 5 intrinsics out) + doc-fences clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ae65acde33
commit
5a9e8cb2da
13 changed files with 2992 additions and 3206 deletions
|
|
@ -147,9 +147,12 @@ See `examples/menu.ludic` for a complete title screen.
|
||||||
| `ptr` | raw address (runtime/FFI) | `ptr` |
|
| `ptr` | raw address (runtime/FFI) | `ptr` |
|
||||||
| `byte` | pointer to bytes — `p[i]` reads/writes one byte | `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` |
|
| `words` | pointer to 32-bit words — `w[i]` reads/writes an `int` | `ptr` |
|
||||||
|
| `fixeds` | buffer of `fixed` values — `f[i]` reads/writes a `fixed` | `ptr` |
|
||||||
|
| `ptrs` | buffer of pointers — `p[i]` reads/writes a `ptr` | `ptr` |
|
||||||
|
|
||||||
Allocate raw buffers with `bytes(n)` (n bytes) or `words(n)` (n 32-bit words);
|
Allocate raw buffers with `bytes(n)` (n bytes) or `words(n)` (n 32-bit words);
|
||||||
both return a pointer you index with `buf[i]`.
|
both return a pointer you index with `buf[i]` — retype the binding (`words` /
|
||||||
|
`fixeds` / `ptrs`) to pick the element size.
|
||||||
|
|
||||||
Numeric literals: `42` and `0x1affff` are `int`; a literal with a decimal
|
Numeric literals: `42` and `0x1affff` are `int`; a literal with a decimal
|
||||||
point (`1.5`) is `fixed`. Arithmetic on two `fixed` values lowers to
|
point (`1.5`) is `fixed`. Arithmetic on two `fixed` values lowers to
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const IMG_MAX: int = 32
|
||||||
const SPR_MAX: int = 160
|
const SPR_MAX: int = 160
|
||||||
const SPR_SZ: int = 16
|
const SPR_SZ: int = 16
|
||||||
|
|
||||||
var img_px: ptr = null # IMG_MAX pointers to RGBA buffers
|
var img_px: ptrs = null # IMG_MAX pointers to RGBA buffers
|
||||||
var img_w: words = null
|
var img_w: words = null
|
||||||
var img_h: words = null
|
var img_h: words = null
|
||||||
var img_n: int = 0
|
var img_n: int = 0
|
||||||
|
|
@ -232,7 +232,7 @@ fn rt_image_load(path: str) -> int {
|
||||||
if rt_decode_png(path) == false { return 0 - 1 }
|
if rt_decode_png(path) == false { return 0 - 1 }
|
||||||
let id = img_n
|
let id = img_n
|
||||||
img_n = img_n + 1
|
img_n = img_n + 1
|
||||||
pokep(img_px, id, png_px)
|
img_px[id] = png_px
|
||||||
img_w[id] = png_w
|
img_w[id] = png_w
|
||||||
img_h[id] = png_h
|
img_h[id] = png_h
|
||||||
return id
|
return id
|
||||||
|
|
@ -265,7 +265,7 @@ fn rt_draw_image(id: int, dx: int, dy: int) -> void {
|
||||||
if id >= img_n { return }
|
if id >= img_n { return }
|
||||||
let w = img_w[id]
|
let w = img_w[id]
|
||||||
let h = img_h[id]
|
let h = img_h[id]
|
||||||
let s: words = peekp(img_px, id)
|
let s: words = img_px[id]
|
||||||
for y in 0 .. h {
|
for y in 0 .. h {
|
||||||
for x in 0 .. w {
|
for x in 0 .. w {
|
||||||
rt_blend_px(dx + x, dy + y, s[y * w + x])
|
rt_blend_px(dx + x, dy + y, s[y * w + x])
|
||||||
|
|
@ -280,7 +280,7 @@ fn rt_draw_image_scaled(id: int, dx: int, dy: int, dw: int, dh: int) -> void {
|
||||||
if dh <= 0 { return }
|
if dh <= 0 { return }
|
||||||
let w = img_w[id]
|
let w = img_w[id]
|
||||||
let h = img_h[id]
|
let h = img_h[id]
|
||||||
let s: words = peekp(img_px, id)
|
let s: words = img_px[id]
|
||||||
for y in 0 .. dh {
|
for y in 0 .. dh {
|
||||||
for x in 0 .. dw {
|
for x in 0 .. dw {
|
||||||
rt_blend_px(dx + x, dy + y, s[(y * h / dh) * w + x * w / dw])
|
rt_blend_px(dx + x, dy + y, s[(y * h / dh) * w + x * w / dw])
|
||||||
|
|
@ -305,7 +305,7 @@ fn rt_draw_9slice(id: int, dx: int, dy: int, dw: int, dh: int, inset: int) -> vo
|
||||||
if dh <= 0 { return }
|
if dh <= 0 { return }
|
||||||
let w = img_w[id]
|
let w = img_w[id]
|
||||||
let h = img_h[id]
|
let h = img_h[id]
|
||||||
let s: words = peekp(img_px, id)
|
let s: words = img_px[id]
|
||||||
for y in 0 .. dh {
|
for y in 0 .. dh {
|
||||||
let sy = rt_9map(y, dh, h, inset)
|
let sy = rt_9map(y, dh, h, inset)
|
||||||
for x in 0 .. dw {
|
for x in 0 .. dw {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const TT_EDGES: int = 16384 # line segments in one glyph
|
||||||
const TT_GC: int = 512 # glyph cache entries
|
const TT_GC: int = 512 # glyph cache entries
|
||||||
|
|
||||||
# ---- per-font tables (parallel arrays; index = font id) -------------------
|
# ---- per-font tables (parallel arrays; index = font id) -------------------
|
||||||
var tt_data: ptr = null
|
var tt_data: ptrs = null
|
||||||
var tt_size: words = null
|
var tt_size: words = null
|
||||||
var tt_upem: words = null
|
var tt_upem: words = null
|
||||||
var tt_nglyf: words = null
|
var tt_nglyf: words = null
|
||||||
|
|
@ -35,20 +35,20 @@ var tt_cfmt: words = null
|
||||||
var tt_n: int = 0
|
var tt_n: int = 0
|
||||||
|
|
||||||
# ---- outline + edge scratch (reused for every glyph) ----------------------
|
# ---- outline + edge scratch (reused for every glyph) ----------------------
|
||||||
var ol_x: ptr = null
|
var ol_x: fixeds = null
|
||||||
var ol_y: ptr = null
|
var ol_y: fixeds = null
|
||||||
var ol_on: words = null
|
var ol_on: words = null
|
||||||
var ol_ends: words = null
|
var ol_ends: words = null
|
||||||
var ol_n: int = 0
|
var ol_n: int = 0
|
||||||
var ol_ne: int = 0
|
var ol_ne: int = 0
|
||||||
|
|
||||||
var ed_x0: ptr = null
|
var ed_x0: fixeds = null
|
||||||
var ed_y0: ptr = null
|
var ed_y0: fixeds = null
|
||||||
var ed_x1: ptr = null
|
var ed_x1: fixeds = null
|
||||||
var ed_y1: ptr = null
|
var ed_y1: fixeds = null
|
||||||
var ed_n: int = 0
|
var ed_n: int = 0
|
||||||
|
|
||||||
var sc_x: ptr = null # scanline crossings
|
var sc_x: fixeds = null # scanline crossings
|
||||||
var sc_d: words = null
|
var sc_d: words = null
|
||||||
|
|
||||||
# ---- glyph cache ----------------------------------------------------------
|
# ---- glyph cache ----------------------------------------------------------
|
||||||
|
|
@ -61,7 +61,7 @@ var gc_h: words = null
|
||||||
var gc_ox: words = null
|
var gc_ox: words = null
|
||||||
var gc_oy: words = null
|
var gc_oy: words = null
|
||||||
var gc_adv: words = null
|
var gc_adv: words = null
|
||||||
var gc_bmp: ptr = null
|
var gc_bmp: ptrs = null
|
||||||
|
|
||||||
fn rt_tt_init() -> void {
|
fn rt_tt_init() -> void {
|
||||||
tt_data = bytes(TT_MAX * 8)
|
tt_data = bytes(TT_MAX * 8)
|
||||||
|
|
@ -193,7 +193,7 @@ fn rt_font_load(path: str) -> int {
|
||||||
|
|
||||||
let id = tt_n
|
let id = tt_n
|
||||||
tt_n = tt_n + 1
|
tt_n = tt_n + 1
|
||||||
pokep(tt_data, id, d)
|
tt_data[id] = d
|
||||||
tt_size[id] = size
|
tt_size[id] = size
|
||||||
var upem = tt_u16(d, head + 18)
|
var upem = tt_u16(d, head + 18)
|
||||||
if upem <= 0 { upem = 1000 }
|
if upem <= 0 { upem = 1000 }
|
||||||
|
|
@ -213,7 +213,7 @@ fn rt_font_load(path: str) -> int {
|
||||||
|
|
||||||
# ---- codepoint -> glyph id ------------------------------------------------
|
# ---- codepoint -> glyph id ------------------------------------------------
|
||||||
fn tt_glyph_index(id: int, cp: int) -> int {
|
fn tt_glyph_index(id: int, cp: int) -> int {
|
||||||
let d = peekp(tt_data, id)
|
let d = tt_data[id]
|
||||||
let s = tt_cmap[id]
|
let s = tt_cmap[id]
|
||||||
if s < 0 { return 0 }
|
if s < 0 { return 0 }
|
||||||
let fmt = tt_cfmt[id]
|
let fmt = tt_cfmt[id]
|
||||||
|
|
@ -262,7 +262,7 @@ fn tt_glyph_index(id: int, cp: int) -> int {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tt_advance(id: int, gid: int) -> int {
|
fn tt_advance(id: int, gid: int) -> int {
|
||||||
let d = peekp(tt_data, id)
|
let d = tt_data[id]
|
||||||
let hmtx = tt_hmtx[id]
|
let hmtx = tt_hmtx[id]
|
||||||
let n = tt_nhm[id]
|
let n = tt_nhm[id]
|
||||||
if gid < n { return tt_u16(d, hmtx + gid * 4) }
|
if gid < n { return tt_u16(d, hmtx + gid * 4) }
|
||||||
|
|
@ -272,20 +272,20 @@ fn tt_advance(id: int, gid: int) -> int {
|
||||||
# ---- outline extraction ---------------------------------------------------
|
# ---- outline extraction ---------------------------------------------------
|
||||||
fn ol_pt(x: fixed, y: fixed, on: int) -> void {
|
fn ol_pt(x: fixed, y: fixed, on: int) -> void {
|
||||||
if ol_n >= TT_PTS { return }
|
if ol_n >= TT_PTS { return }
|
||||||
pokef(ol_x, ol_n, x)
|
ol_x[ol_n] = x
|
||||||
pokef(ol_y, ol_n, y)
|
ol_y[ol_n] = y
|
||||||
ol_on[ol_n] = on
|
ol_on[ol_n] = on
|
||||||
ol_n = ol_n + 1
|
ol_n = ol_n + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tt_glyph_start(id: int, gid: int) -> int {
|
fn tt_glyph_start(id: int, gid: int) -> int {
|
||||||
let d = peekp(tt_data, id)
|
let d = tt_data[id]
|
||||||
let loca = tt_loca[id]
|
let loca = tt_loca[id]
|
||||||
if tt_locfm[id] == 0 { return tt_u16(d, loca + gid * 2) * 2 }
|
if tt_locfm[id] == 0 { return tt_u16(d, loca + gid * 2) * 2 }
|
||||||
return tt_u32(d, loca + gid * 4)
|
return tt_u32(d, loca + gid * 4)
|
||||||
}
|
}
|
||||||
fn tt_glyph_end(id: int, gid: int) -> int {
|
fn tt_glyph_end(id: int, gid: int) -> int {
|
||||||
let d = peekp(tt_data, id)
|
let d = tt_data[id]
|
||||||
let loca = tt_loca[id]
|
let loca = tt_loca[id]
|
||||||
if tt_locfm[id] == 0 { return tt_u16(d, loca + (gid + 1) * 2) * 2 }
|
if tt_locfm[id] == 0 { return tt_u16(d, loca + (gid + 1) * 2) * 2 }
|
||||||
return tt_u32(d, loca + (gid + 1) * 4)
|
return tt_u32(d, loca + (gid + 1) * 4)
|
||||||
|
|
@ -299,7 +299,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
|
||||||
let goff = tt_glyph_start(id, gid)
|
let goff = tt_glyph_start(id, gid)
|
||||||
let gend = tt_glyph_end(id, gid)
|
let gend = tt_glyph_end(id, gid)
|
||||||
if goff >= gend { return }
|
if goff >= gend { return }
|
||||||
let d = peekp(tt_data, id)
|
let d = tt_data[id]
|
||||||
let g = tt_glyf[id] + goff
|
let g = tt_glyf[id] + goff
|
||||||
let nc = tt_i16(d, g)
|
let nc = tt_i16(d, g)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
# ---- rasterization --------------------------------------------------------
|
# ---- rasterization --------------------------------------------------------
|
||||||
fn ed_add(x0: fixed, y0: fixed, x1: fixed, y1: fixed) -> void {
|
fn ed_add(x0: fixed, y0: fixed, x1: fixed, y1: fixed) -> void {
|
||||||
if ed_n >= TT_EDGES { return }
|
if ed_n >= TT_EDGES { return }
|
||||||
pokef(ed_x0, ed_n, x0)
|
ed_x0[ed_n] = x0
|
||||||
pokef(ed_y0, ed_n, y0)
|
ed_y0[ed_n] = y0
|
||||||
pokef(ed_x1, ed_n, x1)
|
ed_x1[ed_n] = x1
|
||||||
pokef(ed_y1, ed_n, y1)
|
ed_y1[ed_n] = y1
|
||||||
ed_n = ed_n + 1
|
ed_n = ed_n + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,8 +68,8 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
|
||||||
var maxx = 0.0 - 999999.0
|
var maxx = 0.0 - 999999.0
|
||||||
var maxy = 0.0 - 999999.0
|
var maxy = 0.0 - 999999.0
|
||||||
for i in 0 .. ol_n {
|
for i in 0 .. ol_n {
|
||||||
let X = peekf(ol_x, i) * scale
|
let X = ol_x[i] * scale
|
||||||
let Y = peekf(ol_y, i) * scale
|
let Y = ol_y[i] * scale
|
||||||
if X < minx { minx = X }
|
if X < minx { minx = X }
|
||||||
if X > maxx { maxx = X }
|
if X > maxx { maxx = X }
|
||||||
if Y < miny { miny = Y }
|
if Y < miny { miny = Y }
|
||||||
|
|
@ -106,30 +106,30 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
|
||||||
var sy = 0.0
|
var sy = 0.0
|
||||||
if first_on < 0 {
|
if first_on < 0 {
|
||||||
# all off-curve: start at the midpoint of the first and last point
|
# all off-curve: start at the midpoint of the first and last point
|
||||||
let mx = (peekf(ol_x, start) + peekf(ol_x, start + cnt - 1)) / 2
|
let mx = (ol_x[start] + ol_x[start + cnt - 1]) / 2
|
||||||
let my = (peekf(ol_y, start) + peekf(ol_y, start + cnt - 1)) / 2
|
let my = (ol_y[start] + ol_y[start + cnt - 1]) / 2
|
||||||
sx = (mx * scale - fx(x0)) * TT_SS
|
sx = (mx * scale - fx(x0)) * TT_SS
|
||||||
sy = (fx(y1) - my * scale) * TT_SS
|
sy = (fx(y1) - my * scale) * TT_SS
|
||||||
first_on = 0
|
first_on = 0
|
||||||
} else {
|
} else {
|
||||||
sx = (peekf(ol_x, start + first_on) * scale - fx(x0)) * TT_SS
|
sx = (ol_x[start + first_on] * scale - fx(x0)) * TT_SS
|
||||||
sy = (fx(y1) - peekf(ol_y, start + first_on) * scale) * TT_SS
|
sy = (fx(y1) - ol_y[start + first_on] * scale) * TT_SS
|
||||||
}
|
}
|
||||||
var curx = sx
|
var curx = sx
|
||||||
var cury = sy
|
var cury = sy
|
||||||
var step = 0
|
var step = 0
|
||||||
while step < cnt {
|
while step < cnt {
|
||||||
let i = (first_on + 1 + step) % cnt
|
let i = (first_on + 1 + step) % cnt
|
||||||
let ix = (peekf(ol_x, start + i) * scale - fx(x0)) * TT_SS
|
let ix = (ol_x[start + i] * scale - fx(x0)) * TT_SS
|
||||||
let iy = (fx(y1) - peekf(ol_y, start + i) * scale) * TT_SS
|
let iy = (fx(y1) - ol_y[start + i] * scale) * TT_SS
|
||||||
if ol_on[start + i] == 1 {
|
if ol_on[start + i] == 1 {
|
||||||
ed_add(curx, cury, ix, iy)
|
ed_add(curx, cury, ix, iy)
|
||||||
curx = ix
|
curx = ix
|
||||||
cury = iy
|
cury = iy
|
||||||
} else {
|
} else {
|
||||||
var j = (i + 1) % cnt
|
var j = (i + 1) % cnt
|
||||||
let jx = (peekf(ol_x, start + j) * scale - fx(x0)) * TT_SS
|
let jx = (ol_x[start + j] * scale - fx(x0)) * TT_SS
|
||||||
let jy = (fx(y1) - peekf(ol_y, start + j) * scale) * TT_SS
|
let jy = (fx(y1) - ol_y[start + j] * scale) * TT_SS
|
||||||
var ex = jx
|
var ex = jx
|
||||||
var ey = jy
|
var ey = jy
|
||||||
if ol_on[start + j] == 1 {
|
if ol_on[start + j] == 1 {
|
||||||
|
|
@ -159,38 +159,38 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
|
||||||
let yc = fx(sy) + 0.5
|
let yc = fx(sy) + 0.5
|
||||||
var m = 0
|
var m = 0
|
||||||
for i in 0 .. ed_n {
|
for i in 0 .. ed_n {
|
||||||
let ya = peekf(ed_y0, i)
|
let ya = ed_y0[i]
|
||||||
let yb = peekf(ed_y1, i)
|
let yb = ed_y1[i]
|
||||||
var hit = 0
|
var hit = 0
|
||||||
if ya <= yc { if yb > yc { hit = 1 } }
|
if ya <= yc { if yb > yc { hit = 1 } }
|
||||||
if yb <= yc { if ya > yc { hit = 1 } }
|
if yb <= yc { if ya > yc { hit = 1 } }
|
||||||
if hit == 1 {
|
if hit == 1 {
|
||||||
let t = (yc - ya) / (yb - ya)
|
let t = (yc - ya) / (yb - ya)
|
||||||
pokef(sc_x, m, peekf(ed_x0, i) + t * (peekf(ed_x1, i) - peekf(ed_x0, i)))
|
sc_x[m] = ed_x0[i] + t * (ed_x1[i] - ed_x0[i])
|
||||||
if yb > ya { sc_d[m] = 1 } else { sc_d[m] = 0 - 1 }
|
if yb > ya { sc_d[m] = 1 } else { sc_d[m] = 0 - 1 }
|
||||||
m = m + 1
|
m = m + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# insertion sort the crossings by x
|
# insertion sort the crossings by x
|
||||||
for i in 1 .. m {
|
for i in 1 .. m {
|
||||||
let kx = peekf(sc_x, i)
|
let kx = sc_x[i]
|
||||||
let kd = sc_d[i]
|
let kd = sc_d[i]
|
||||||
var j = i - 1
|
var j = i - 1
|
||||||
var placed = 0
|
var placed = 0
|
||||||
while j >= 0 {
|
while j >= 0 {
|
||||||
if peekf(sc_x, j) > kx {
|
if sc_x[j] > kx {
|
||||||
pokef(sc_x, j + 1, peekf(sc_x, j))
|
sc_x[j + 1] = sc_x[j]
|
||||||
sc_d[j + 1] = sc_d[j]
|
sc_d[j + 1] = sc_d[j]
|
||||||
j = j - 1
|
j = j - 1
|
||||||
} else {
|
} else {
|
||||||
pokef(sc_x, j + 1, kx)
|
sc_x[j + 1] = kx
|
||||||
sc_d[j + 1] = kd
|
sc_d[j + 1] = kd
|
||||||
placed = 1
|
placed = 1
|
||||||
j = 0 - 1
|
j = 0 - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if placed == 0 {
|
if placed == 0 {
|
||||||
pokef(sc_x, 0, kx)
|
sc_x[0] = kx
|
||||||
sc_d[0] = kd
|
sc_d[0] = kd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -199,8 +199,8 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
|
||||||
for i in 0 .. m - 1 {
|
for i in 0 .. m - 1 {
|
||||||
wind = wind + sc_d[i]
|
wind = wind + sc_d[i]
|
||||||
if wind != 0 {
|
if wind != 0 {
|
||||||
var xa = peekf(sc_x, i)
|
var xa = sc_x[i]
|
||||||
var xb = peekf(sc_x, i + 1)
|
var xb = sc_x[i + 1]
|
||||||
if xa < 0.0 { xa = 0.0 }
|
if xa < 0.0 { xa = 0.0 }
|
||||||
if xb > fx(SW) { xb = fx(SW) }
|
if xb > fx(SW) { xb = fx(SW) }
|
||||||
if xb > xa {
|
if xb > xa {
|
||||||
|
|
@ -249,11 +249,11 @@ fn tt_glyph_get(font: int, cp: int, px: int) -> int {
|
||||||
}
|
}
|
||||||
let s = h
|
let s = h
|
||||||
if gc_used[s] == 1 {
|
if gc_used[s] == 1 {
|
||||||
let old = peekp(gc_bmp, s)
|
let old = gc_bmp[s]
|
||||||
if (old == null) == false { mem_free(old) }
|
if (old == null) == false { mem_free(old) }
|
||||||
}
|
}
|
||||||
let bmp = tt_raster(font, tt_glyph_index(font, cp), px)
|
let bmp = tt_raster(font, tt_glyph_index(font, cp), px)
|
||||||
pokep(gc_bmp, s, bmp)
|
gc_bmp[s] = bmp
|
||||||
gc_used[s] = 1
|
gc_used[s] = 1
|
||||||
gc_font[s] = font
|
gc_font[s] = font
|
||||||
gc_cp[s] = cp
|
gc_cp[s] = cp
|
||||||
|
|
@ -345,7 +345,7 @@ fn rt_text_ttf(font: int, x: int, y: int, s: str, colour: int, px: int) -> void
|
||||||
baseline = baseline + lineh
|
baseline = baseline + lineh
|
||||||
} else {
|
} else {
|
||||||
let g = tt_glyph_get(font, cp, px)
|
let g = tt_glyph_get(font, cp, px)
|
||||||
tt_blit(peekp(gc_bmp, g), gc_w[g], gc_h[g], penx + gc_ox[g], baseline - gc_oy[g], colour)
|
tt_blit(gc_bmp[g], gc_w[g], gc_h[g], penx + gc_ox[g], baseline - gc_oy[g], colour)
|
||||||
penx = penx + gc_adv[g]
|
penx = penx + gc_adv[g]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ var ui_visible: words = null
|
||||||
var ui_fired: words = null
|
var ui_fired: words = null
|
||||||
var ui_hasdyn: words = null
|
var ui_hasdyn: words = null
|
||||||
var ui_dyn: ptr = null # UI_MAX * 96 bytes
|
var ui_dyn: ptr = null # UI_MAX * 96 bytes
|
||||||
var ui_text: ptr = null # UI_MAX static string pointers
|
var ui_text: ptrs = null # UI_MAX static string pointers
|
||||||
|
|
||||||
var ui_n: int = 0
|
var ui_n: int = 0
|
||||||
var ui_active: int = -1
|
var ui_active: int = -1
|
||||||
|
|
@ -138,7 +138,7 @@ fn rt_ui_reset(n: int) -> void {
|
||||||
ui_visible[i] = 1
|
ui_visible[i] = 1
|
||||||
ui_fired[i] = 0
|
ui_fired[i] = 0
|
||||||
ui_hasdyn[i] = 0
|
ui_hasdyn[i] = 0
|
||||||
pokep(ui_text, i, null)
|
ui_text[i] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,13 +171,13 @@ fn rt_ui_set(i: int, k: int, v: int) -> void {
|
||||||
fn rt_ui_static_text(i: int, s: str) -> void {
|
fn rt_ui_static_text(i: int, s: str) -> void {
|
||||||
if i < 0 { return }
|
if i < 0 { return }
|
||||||
if i >= UI_MAX { return }
|
if i >= UI_MAX { return }
|
||||||
pokep(ui_text, i, s)
|
ui_text[i] = s
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- text helpers: TrueType when a font is loaded, bitmap otherwise -------
|
# ---- text helpers: TrueType when a font is loaded, bitmap otherwise -------
|
||||||
fn ui_str(i: int) -> ptr {
|
fn ui_str(i: int) -> ptr {
|
||||||
if 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)
|
let t = ui_text[i]
|
||||||
if (t == null) { return ptr_add(ui_dyn, i * 96) }
|
if (t == null) { return ptr_add(ui_dyn, i * 96) }
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,21 @@ fn emit_index_addr(e: Node) -> ptr {
|
||||||
let base = emit_expr(e.a)
|
let base = emit_expr(e.a)
|
||||||
if not is_slice_ty(base.ty) { # a raw pointer: address of element 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 bi = emit_expr(e.b) # (evaluate index first — it may set g_addr_ty)
|
||||||
if (base.ty == "words") { # a `words` buffer: 32-bit elements
|
if (base.ty == "words") { # a `words` buffer: 32-bit int elements
|
||||||
let rw = emit_bind(`getelementptr inbounds i32, ptr {base.code}, i32 {bi.code}`)
|
let rw = emit_bind(`getelementptr inbounds i32, ptr {base.code}, i32 {bi.code}`)
|
||||||
g_addr_ty = "int"
|
g_addr_ty = "int"
|
||||||
return rw
|
return rw
|
||||||
}
|
}
|
||||||
|
if (base.ty == "fixeds") { # a `fixeds` buffer: 32-bit fixed elements
|
||||||
|
let rf = emit_bind(`getelementptr inbounds i32, ptr {base.code}, i32 {bi.code}`)
|
||||||
|
g_addr_ty = "fixed"
|
||||||
|
return rf
|
||||||
|
}
|
||||||
|
if (base.ty == "ptrs") { # a `ptrs` buffer: pointer elements
|
||||||
|
let rp = emit_bind(`getelementptr inbounds ptr, ptr {base.code}, i32 {bi.code}`)
|
||||||
|
g_addr_ty = "ptr"
|
||||||
|
return rp
|
||||||
|
}
|
||||||
let r = emit_bind(`getelementptr inbounds i8, ptr {base.code}, i32 {bi.code}`) # a ptr/str: bytes
|
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
|
g_addr_ty = "byte" # set last so the caller sees it
|
||||||
return r
|
return r
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ fn lbl(pfx: ptr) -> ptr { let r = (pfx + itoa(ll_lbl)); ll_lbl = ll_lbl + 1; r
|
||||||
fn llty(t: ptr) -> ptr {
|
fn llty(t: ptr) -> ptr {
|
||||||
if (t == "int") or (t == "bool") or (t == "fixed") { return "i32" }
|
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 == "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 == "words") or (t == "fixeds") or (t == "ptrs") { return "ptr" } # typed buffers
|
||||||
if (t == "void") { return "void" }
|
if (t == "void") { return "void" }
|
||||||
return "ptr"
|
return "ptr"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,8 @@ var g_windowed: bool = false # headless by default (games read stdin / dump
|
||||||
fn is_intrinsic2(name: ptr) -> bool {
|
fn is_intrinsic2(name: ptr) -> bool {
|
||||||
if (name == "mem_free") or (name == "mem_copy") or (name == "mem_set") { return true }
|
if (name == "mem_free") or (name == "mem_copy") or (name == "mem_set") { return true }
|
||||||
if (name == "ptr_add") or (name == "read_byte") or (name == "write_byte") { return true }
|
if (name == "ptr_add") or (name == "read_byte") or (name == "write_byte") { return true }
|
||||||
if (name == "str_len") or (name == "os_time") { return true }
|
if (name == "os_time") { return true }
|
||||||
if (name == "peekf") or (name == "pokef") or (name == "as_fixed") or (name == "as_int") { return true }
|
if (name == "as_fixed") or (name == "as_int") { return true }
|
||||||
if (name == "peekp") or (name == "pokep") { return true }
|
|
||||||
if (name == "is_windowed") or (name == "game_title") { return true }
|
if (name == "is_windowed") or (name == "game_title") { return true }
|
||||||
if (name == "win_open") or (name == "win_poll") or (name == "win_present") { return true }
|
if (name == "win_open") or (name == "win_poll") or (name == "win_present") { return true }
|
||||||
if (name == "win_running") or (name == "win_close") { return true }
|
if (name == "win_running") or (name == "win_close") { return true }
|
||||||
|
|
@ -42,37 +41,10 @@ fn emit_intrinsic2(name: ptr, e: Node) -> Val {
|
||||||
if (name == "write_byte") {
|
if (name == "write_byte") {
|
||||||
let v = arg_code(e, 0); emit(" call i32 @putchar(i32 "); emit(v); emit(")\n"); return val("0", "void")
|
let v = arg_code(e, 0); emit(" call i32 @putchar(i32 "); emit(v); emit(")\n"); return val("0", "void")
|
||||||
}
|
}
|
||||||
if (name == "str_len") {
|
|
||||||
let s = arg_code(e, 0)
|
|
||||||
let r = emit_bind(`call i64 @strlen(ptr {s})`)
|
|
||||||
return val(emit_bind(`trunc i64 {r} to i32`), "int")
|
|
||||||
}
|
|
||||||
if (name == "os_time") {
|
if (name == "os_time") {
|
||||||
let r = emit_bind("call i64 @time(ptr null)")
|
let r = emit_bind("call i64 @time(ptr null)")
|
||||||
return val(emit_bind(`trunc i64 {r} to i32`), "int")
|
return val(emit_bind(`trunc i64 {r} to i32`), "int")
|
||||||
}
|
}
|
||||||
if (name == "peekf") {
|
|
||||||
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}`), "fixed")
|
|
||||||
}
|
|
||||||
if (name == "pokef") {
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
if (name == "peekp") {
|
|
||||||
let p = arg_code(e, 0); let i = arg_code(e, 1)
|
|
||||||
let g = nreg(); emit(" "); emit(g); emit(" = getelementptr inbounds ptr, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
|
||||||
return val(emit_bind(`load ptr, ptr {g}`), "ptr")
|
|
||||||
}
|
|
||||||
if (name == "pokep") {
|
|
||||||
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 ptr, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
|
||||||
emit(" store ptr "); emit(v); emit(", ptr "); emit(g); emit("\n")
|
|
||||||
return val("0", "void")
|
|
||||||
}
|
|
||||||
if (name == "as_fixed") { let a = emit_expr(e.kids[0]); return val(a.code, "fixed") }
|
if (name == "as_fixed") { let a = emit_expr(e.kids[0]); return val(a.code, "fixed") }
|
||||||
if (name == "as_int") { let a = emit_expr(e.kids[0]); return val(a.code, "int") }
|
if (name == "as_int") { let a = emit_expr(e.kids[0]); return val(a.code, "int") }
|
||||||
if (name == "is_windowed") { if g_windowed { return val("1", "bool") }; return val("0", "bool") }
|
if (name == "is_windowed") { if g_windowed { return val("1", "bool") }; return val("0", "bool") }
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -52,7 +52,7 @@ object LudicVocabulary {
|
||||||
"enable", "disable", "match", "machine", "state", "become", "where",
|
"enable", "disable", "match", "machine", "state", "become", "where",
|
||||||
"and", "or", "not", "break", "continue", "new"
|
"and", "or", "not", "break", "continue", "new"
|
||||||
)
|
)
|
||||||
val PRIMITIVES = setOf("int", "fixed", "bool", "entity", "str", "ptr", "byte", "words", "void")
|
val PRIMITIVES = setOf("int", "fixed", "bool", "entity", "str", "ptr", "byte", "words", "fixeds", "ptrs", "void")
|
||||||
val PHASES = setOf("Start", "Input", "FixedUpdate", "Update", "LateUpdate", "Render")
|
val PHASES = setOf("Start", "Input", "FixedUpdate", "Update", "LateUpdate", "Render")
|
||||||
val WIDGETS = setOf("panel", "col", "row", "label", "button", "image", "spacer")
|
val WIDGETS = setOf("panel", "col", "row", "label", "button", "image", "spacer")
|
||||||
|
|
||||||
|
|
@ -67,10 +67,10 @@ object LudicVocabulary {
|
||||||
"status", "print", "str", "quit",
|
"status", "print", "str", "quit",
|
||||||
// compiler intrinsics: the floor the Ludic-written runtime stands on
|
// compiler intrinsics: the floor the Ludic-written runtime stands on
|
||||||
"bytes", "words", "mem_realloc", "mem_free", "mem_copy", "mem_set", "os_argc", "os_arg", "file_stderr", "file_stdout",
|
"bytes", "words", "mem_realloc", "mem_free", "mem_copy", "mem_set", "os_argc", "os_arg", "file_stderr", "file_stdout",
|
||||||
"peekp", "pokep", "peekf", "pokef", "ptr_add",
|
"ptr_add",
|
||||||
"as_fixed", "as_int", "file_open", "file_read", "file_write",
|
"as_fixed", "as_int", "file_open", "file_read", "file_write",
|
||||||
"file_seek", "file_tell", "file_close", "read_byte", "write_byte",
|
"file_seek", "file_tell", "file_close", "read_byte", "write_byte",
|
||||||
"str_len", "os_exit", "os_time",
|
"os_exit", "os_time",
|
||||||
"is_windowed", "game_title", "win_open", "win_poll", "win_present",
|
"is_windowed", "game_title", "win_open", "win_poll", "win_present",
|
||||||
"win_running", "win_close"
|
"win_running", "win_close"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@
|
||||||
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
|
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
|
||||||
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\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": "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|byte|words|void)\\b" },
|
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|byte|words|fixeds|ptrs|void)\\b" },
|
||||||
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\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" }
|
{ "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",
|
"comment": "compiler intrinsics — these lower straight to libc or the OS",
|
||||||
"name": "support.function.intrinsic.ludic",
|
"name": "support.function.intrinsic.ludic",
|
||||||
"match": "\\b(bytes|words|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*\\()"
|
"match": "\\b(bytes|words|mem_realloc|mem_free|mem_copy|mem_set|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|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*\\()"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@
|
||||||
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
|
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
|
||||||
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\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": "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|byte|words|void)\\b" },
|
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|byte|words|fixeds|ptrs|void)\\b" },
|
||||||
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\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" }
|
{ "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",
|
"comment": "compiler intrinsics — these lower straight to libc or the OS",
|
||||||
"name": "support.function.intrinsic.ludic",
|
"name": "support.function.intrinsic.ludic",
|
||||||
"match": "\\b(bytes|words|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*\\()"
|
"match": "\\b(bytes|words|mem_realloc|mem_free|mem_copy|mem_set|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|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*\\()"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ static const char* LUDIC_KW_STMT[] = {
|
||||||
"and","or","not","break","continue","new", 0
|
"and","or","not","break","continue","new", 0
|
||||||
};
|
};
|
||||||
static const char* LUDIC_TYPES[] = {
|
static const char* LUDIC_TYPES[] = {
|
||||||
"int","fixed","bool","entity","str","ptr","byte","words","void", 0
|
"int","fixed","bool","entity","str","ptr","byte","words","fixeds","ptrs","void", 0
|
||||||
};
|
};
|
||||||
static const char* LUDIC_PHASES[] = {
|
static const char* LUDIC_PHASES[] = {
|
||||||
"Start","Input","FixedUpdate","Update","LateUpdate","Render", 0
|
"Start","Input","FixedUpdate","Update","LateUpdate","Render", 0
|
||||||
|
|
@ -155,10 +155,6 @@ static const LBuiltin LUDIC_INTRINSICS[] = {
|
||||||
{"mem_free","mem_free(p: ptr)","Release an allocation."},
|
{"mem_free","mem_free(p: ptr)","Release an allocation."},
|
||||||
{"mem_copy","mem_copy(dst: ptr, src: ptr, n: int)","memcpy."},
|
{"mem_copy","mem_copy(dst: ptr, src: ptr, n: int)","memcpy."},
|
||||||
{"mem_set","mem_set(p: ptr, byte: int, n: int)","memset."},
|
{"mem_set","mem_set(p: ptr, byte: int, n: int)","memset."},
|
||||||
{"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."},
|
|
||||||
{"pokef","pokef(p: ptr, off: int, v: fixed)","Write a fixed-point word."},
|
|
||||||
{"ptr_add","ptr_add(p: ptr, off: int) -> ptr","Offset a pointer by bytes."},
|
{"ptr_add","ptr_add(p: ptr, off: int) -> ptr","Offset a pointer by bytes."},
|
||||||
{"as_fixed","as_fixed(i: int) -> fixed","Reinterpret an int as fixed (no conversion)."},
|
{"as_fixed","as_fixed(i: int) -> fixed","Reinterpret an int as fixed (no conversion)."},
|
||||||
{"as_int","as_int(f: fixed) -> int","Reinterpret a fixed as int (no conversion)."},
|
{"as_int","as_int(f: fixed) -> int","Reinterpret a fixed as int (no conversion)."},
|
||||||
|
|
@ -170,7 +166,6 @@ static const LBuiltin LUDIC_INTRINSICS[] = {
|
||||||
{"file_close","file_close(f: ptr)","fclose."},
|
{"file_close","file_close(f: ptr)","fclose."},
|
||||||
{"read_byte","read_byte() -> int","Read one byte from stdin, -1 at EOF."},
|
{"read_byte","read_byte() -> int","Read one byte from stdin, -1 at EOF."},
|
||||||
{"write_byte","write_byte(b: int)","Write one byte to stdout."},
|
{"write_byte","write_byte(b: int)","Write one byte to stdout."},
|
||||||
{"str_len","str_len(s: str) -> int","Length of a string in bytes."},
|
|
||||||
{"os_exit","os_exit(code: int)","Terminate the process."},
|
{"os_exit","os_exit(code: int)","Terminate the process."},
|
||||||
{"os_time","os_time() -> int","Seconds since the epoch."},
|
{"os_time","os_time() -> int","Seconds since the epoch."},
|
||||||
{"is_windowed","is_windowed() -> bool","True when the build has a window."},
|
{"is_windowed","is_windowed() -> bool","True when the build has a window."},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue