· 7 years ago · Sep 30, 2018, 11:10 PM
1# Octojam 2017
2#
3# OctoCrawl II
4#
5# by John Deeny (jdeeny@gmail.com)
6# Copyright (c) 2017 John Deeny
7#
8
9##/ header.o8 /##
10#################
11
12##/ registers.o8 /##
13####################
14
15
16#####
17# Register definitions
18###
19# Trying to keep this sortof organized
20###
21
22:alias compare-temp vF # move compare-temp so it doesn't accidentally kill SP
23:alias SCRATCH vF
24:alias SP vE # Stack pointer
25
26# incoming parameters
27:alias PASS3 vD
28:alias PASS2 vC
29:alias PASS1 vB
30:alias PASS0 vA
31
32# Callee saved registers
33:alias NONVOL3 v9
34:alias NONVOL2 v8
35:alias NONVOL1 v7
36:alias NONVOL0 v6
37
38# Caller saved registers
39:alias VOL5 v5
40:alias VOL4 v4
41:alias VOL3 v3
42:alias VOL2 v2
43:alias VOL1 v1
44:alias VOL0 v0
45
46##/ config.o8 /##
47#################
48
49# | Start | End | Size | Contents |
50# | ------ | -------- | ------------ | ----------------------------------------------------------- |
51# | 0x0000 | 0x01FF | 512 B | Fonts, system area |
52# | 0x0200 | 0x0201 | 2 B | Trampoline to main, required as a side effect of using :org |
53# | 0x0202 | 0x0FFF | 3.5 kB | Code segment, plus any data that will be accessed with long |
54# | 0x1000 | 0x1FFF | 4 kb | Far data segment, (static) data that is > 12-bit boundary |
55# | 0x2000 | 0xFCFF | 55 kB | Heap, begins at the end of the far data segment |
56# | 0xFD00 | 0xFDFF | 256 B | Tetra CallStack |
57# | 0xFE00 | 0xFEFF | 256 B | Data Stack |
58# | 0xFF00 | 0xFFFF | 256 B | Debug space |
59
60:const SEG_CODE_START 0x202
61
62:const SEG_FAR_START 0x1000
63
64:const SEG_HEAP_START 0x4000
65:const SEG_HEAP_END 0xF000
66
67:const SEG_TETRA_CALLSTACK_START 0xFD00
68:const SEG_TETRA_CALLSTACK_END 0xFDFF
69
70:const SEG_STACK_START 0xFE00
71:const SEG_STACK_END 0xFEFF
72
73:const SEG_CODE_SIZE_START 0xFFF0
74:const SEG_CODE_SIZE_END 0xFFFF
75
76##/ far-header.o8 /##
77####################
78
79#####
80# Stuff that is in memory > 4k
81###
82
83:org SEG_FAR_START
84
85##/ build/tetra-data.o8 /##
86
87#####
88# Tetra Word Bytecode Definitions [2018-09-30 18:04:19.183767]
89###
90: TETRA_WORD_QUADRUPLE 0x10 0x10 0x0F
91 # DOUBLE DOUBLE ;
92: TETRA_WORD_DOUBLE 0x00 0x01 0x0F
93 # DUP + ;
94
95#####
96# Tetra Word Bytecode Index [2018-09-30 18:04:19.183767]
97###
98
99: tetra_bytecode_index
100
101 :calc tetra_label_hi { TETRA_WORD_QUADRUPLE >> 8 } :calc tetra_label_lo { TETRA_WORD_QUADRUPLE & 0xFF }
102 :byte tetra_label_hi :byte tetra_label_lo
103 :calc tetra_label_hi { TETRA_WORD_DOUBLE >> 8 } :calc tetra_label_lo { TETRA_WORD_DOUBLE & 0xFF }
104 :byte tetra_label_hi :byte tetra_label_lo
105
106
107#/ build/text-data.o8 /# 2018-09-30 18:04:19.483787
108
109# Glyph Table
110: glyph-table
1110x04 0x40 0xA0 0xA0 0xA0 0x40 0x00 # GLYPH_0 "0" 4px
1120x02 0x80 0x80 0x80 0x80 0x80 0x00 # GLYPH_1 "1" 2px
1130x04 0xE0 0x20 0xE0 0x80 0xE0 0x00 # GLYPH_2 "2" 4px
1140x04 0xE0 0x20 0xE0 0x20 0xE0 0x00 # GLYPH_3 "3" 4px
1150x04 0xA0 0xA0 0xE0 0x20 0x20 0x00 # GLYPH_4 "4" 4px
1160x04 0xE0 0x80 0xE0 0x20 0xE0 0x00 # GLYPH_5 "5" 4px
1170x04 0x80 0x80 0xE0 0xA0 0xE0 0x00 # GLYPH_6 "6" 4px
1180x04 0xE0 0x20 0x20 0x20 0x20 0x00 # GLYPH_7 "7" 4px
1190x04 0xE0 0xA0 0xE0 0xA0 0xE0 0x00 # GLYPH_8 "8" 4px
1200x04 0xE0 0xA0 0xE0 0x20 0x20 0x00 # GLYPH_9 "9" 4px
1210x04 0x40 0xA0 0xE0 0xA0 0xA0 0x00 # GLYPH_A "A" 4px
1220x04 0xC0 0xA0 0xC0 0xA0 0xC0 0x00 # GLYPH_B "B" 4px
1230x03 0xC0 0x80 0x80 0x80 0xC0 0x00 # GLYPH_C "C" 3px
1240x04 0xC0 0xA0 0xA0 0xA0 0xC0 0x00 # GLYPH_D "D" 4px
1250x03 0xC0 0x80 0xC0 0x80 0xC0 0x00 # GLYPH_E "E" 3px
1260x03 0xC0 0x80 0xC0 0x80 0x80 0x00 # GLYPH_F "F" 3px
1270x05 0xE0 0x80 0xA0 0x90 0xF0 0x10 # GLYPH_G "G" 5px
1280x04 0xA0 0xA0 0xE0 0xA0 0xA0 0x00 # GLYPH_H "H" 4px
1290x02 0x80 0x80 0x80 0x80 0x80 0x00 # GLYPH_I "I" 2px
1300x04 0x20 0x20 0x20 0xA0 0xE0 0x00 # GLYPH_J "J" 4px
1310x04 0xA0 0xC0 0x80 0xC0 0xA0 0x00 # GLYPH_K "K" 4px
1320x03 0x80 0x80 0x80 0x80 0xC0 0x00 # GLYPH_L "L" 3px
1330x06 0x88 0xD8 0xA8 0x88 0x88 0x00 # GLYPH_M "M" 6px
1340x05 0x90 0x90 0xD0 0xB0 0x90 0x00 # GLYPH_N "N" 5px
1350x04 0xE0 0xA0 0xA0 0xA0 0xE0 0x00 # GLYPH_O "O" 4px
1360x04 0xE0 0xA0 0xE0 0x80 0x80 0x00 # GLYPH_P "P" 4px
1370x04 0xE0 0xA0 0xA0 0xA0 0xE0 0x30 # GLYPH_Q "Q" 4px
1380x04 0xE0 0xA0 0xE0 0xC0 0xA0 0x00 # GLYPH_R "R" 4px
1390x04 0xE0 0x80 0xE0 0x20 0xE0 0x00 # GLYPH_S "S" 4px
1400x04 0xE0 0x40 0x40 0x40 0x40 0x00 # GLYPH_T "T" 4px
1410x04 0xA0 0xA0 0xA0 0xA0 0xE0 0x00 # GLYPH_U "U" 4px
1420x04 0xA0 0xA0 0xA0 0xA0 0x40 0x00 # GLYPH_V "V" 4px
1430x06 0x88 0x88 0x88 0xA8 0x50 0x00 # GLYPH_W "W" 6px
1440x04 0xA0 0xA0 0x40 0xA0 0xA0 0x00 # GLYPH_X "X" 4px
1450x04 0xA0 0xA0 0x40 0x40 0x40 0x00 # GLYPH_Y "Y" 4px
1460x04 0xE0 0x20 0x40 0x80 0xE0 0x00 # GLYPH_Z "Z" 4px
1470x04 0x00 0x00 0x60 0xA0 0x60 0x00 # GLYPH_a "a" 4px
1480x04 0x80 0x80 0xC0 0xA0 0xC0 0x00 # GLYPH_b "b" 4px
1490x04 0x00 0x60 0x80 0x80 0x60 0x00 # GLYPH_c "c" 4px
1500x04 0x20 0x20 0x60 0xA0 0x60 0x00 # GLYPH_d "d" 4px
1510x04 0x00 0x40 0xA0 0xC0 0x60 0x00 # GLYPH_e "e" 4px
1520x03 0x40 0x80 0xC0 0x80 0x80 0x00 # GLYPH_f "f" 3px
1530x04 0x00 0x40 0xA0 0x60 0x20 0xC0 # GLYPH_g "g" 4px
1540x04 0x00 0x80 0x80 0xE0 0xA0 0x00 # GLYPH_h "h" 4px
1550x02 0x00 0x80 0x00 0x80 0x80 0x00 # GLYPH_i "i" 2px
1560x03 0x00 0x40 0x00 0x40 0x40 0x80 # GLYPH_j "j" 3px
1570x04 0x80 0x80 0xA0 0xC0 0xA0 0x00 # GLYPH_k "k" 4px
1580x02 0x80 0x80 0x80 0x80 0x80 0x00 # GLYPH_l "l" 2px
1590x06 0x00 0x00 0x50 0xA8 0xA8 0x00 # GLYPH_m "m" 6px
1600x04 0x00 0x00 0x40 0xA0 0xA0 0x00 # GLYPH_n "n" 4px
1610x04 0x00 0x00 0x40 0xA0 0x40 0x00 # GLYPH_o "o" 4px
1620x04 0x00 0x00 0xC0 0xA0 0xC0 0x80 # GLYPH_p "p" 4px
1630x04 0x00 0x00 0x40 0xA0 0x60 0x20 # GLYPH_q "q" 4px
1640x03 0x00 0x00 0xC0 0x80 0x80 0x00 # GLYPH_r "r" 3px
1650x03 0x00 0x40 0x80 0x40 0x80 0x00 # GLYPH_s "s" 3px
1660x03 0x00 0x80 0xC0 0x80 0x40 0x00 # GLYPH_t "t" 3px
1670x04 0x00 0x00 0xA0 0xA0 0xE0 0x00 # GLYPH_u "u" 4px
1680x04 0x00 0x00 0xA0 0xA0 0x40 0x00 # GLYPH_v "v" 4px
1690x06 0x00 0x00 0x88 0xA8 0x50 0x00 # GLYPH_w "w" 6px
1700x04 0x00 0x00 0xA0 0x40 0xA0 0x00 # GLYPH_x "x" 4px
1710x04 0x00 0x00 0xA0 0xE0 0x20 0xC0 # GLYPH_y "y" 4px
1720x04 0x00 0xC0 0x40 0x80 0xC0 0x00 # GLYPH_z "z" 4px
1730x07 0x9C 0x84 0xE4 0xA4 0xE4 0x00 # GLYPH_67 "67" 7px
1740x06 0xD8 0x90 0x90 0x90 0xD8 0x00 # GLYPH_CC "CC" 6px
1750x08 0xEE 0x88 0xEE 0x82 0xEE 0x00 # GLYPH_ES "ES" 8px
1760x02 0x80 0x80 0x80 0x00 0x80 0x00 # GLYPH_! "!" 2px
1770x02 0x00 0x00 0x00 0x00 0x80 0x00 # GLYPH_. "." 2px
178# String table
179: STR_ABCDEF 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0xFF
180: STR_Alphabet 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0xFF
181: STR_LowerAlphabet 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x3B 0x3C 0x3D 0xFF
182: STR_Numerals 0x00 0x01 0x02 0x03 0x04 0x05 0x3E 0x08 0x09 0xFF
183: STR_RunningTests 0x1B 0x38 0x31 0x31 0x2C 0x31 0x2A 0x1D 0x28 0x36 0x37 0x36 0x42 0x42 0x42 0xFF
184: STR_Success 0x1C 0x38 0x26 0x26 0x28 0x36 0x36 0x41 0x41 0xFF
185
186##/ far.o8 /##
187##############
188
189# Define Heap Segment
190:org SEG_HEAP_START
191: mm_heap
192:org SEG_HEAP_END
193: mm_heap_end
194
195# Define the Tetra CallStack Segment
196:org SEG_STACK_START
197: mm_stack
198:org SEG_STACK_END
199: mm_stack_end
200
201
202# Define the Stack Segment
203:org SEG_TETRA_CALLSTACK_START
204: tetra_callstack
205:org SEG_TETRA_CALLSTACK_END
206: tetra_callstack_end
207
208##/ code-header.o8 /##
209######################
210
211:org SEG_CODE_START
212
213: CODE_START
214
215##/ assert.o8 /##
216#################
217
218# Stop execution
219:macro assert reason { :breakpoint reason vF := vF }
220
221# Stop execution if the condition isn't true
222# lhs must be a register, rhs can be a register or 8-bit literal
223# ``` assert-eq v0 7
224# assert-neq v5 0x55
225# ```
226:macro assert-eq lhs rhs fail-reason { if lhs != rhs then assert fail-reason }
227:macro assert-neq lhs rhs fail-reason { if lhs == rhs then assert fail-reason }
228
229:macro assert-neq0-u16 lhs-hi lhs-lo fail-reason { SCRATCH := lhs-hi SCRATCH |= lhs-lo if SCRATCH == 0 then assert fail-reason }
230
231#####
232# assert-mem
233###
234# Checks to see if a location in RAM is a series of bytes.
235# Use assert-mem to point to the correct location, and is for
236# each byte:
237# ``` assert-mem area-of-interest show-this-breakpoint-message
238# is 0x33
239# is 0
240# ```
241# This will clobber v0.
242#
243# Use `assert-mem` for constant addresses, `assert-mem-at-PASSxx` for register
244# indirect.
245:macro assert-mem-builder fail-reason {
246 :calc assert-mem-skip-loc { HERE + 4 }
247 jump assert-mem-skip-loc # Jump over the return instruction
248 :calc assert-mem-fail-loc { HERE }
249 :breakpoint fail-reason
250 ;
251}
252
253:macro assert-mem test-location fail-reason {
254 i := long test-location
255 assert-mem-builder fail-reason
256}
257
258:macro assert-mem-at-PASS01 fail-reason {
259 reg_load_i_PASS01
260 assert-mem-builder fail-reason
261}
262
263:macro assert-mem-at-PASS23 fail-reason {
264 reg_load_i_PASS23
265 assert-mem-builder
266}
267
268
269# assert-mem-fail-loc is set by the previous assert-mem invocation
270:macro is val { load v0 if v0 != val then assert-mem-fail-loc }
271
272##/ mem.o8 /##
273##############
274
275# Set I = address in registers
276:macro reg-set-i-macro reg-hi reg-lo target {
277 i := target
278 save reg-hi - reg-lo
279 0xF0 0x00 : target 0x00 0x00
280 ;
281}
282
283
284: reg_load_i_PASS01 reg-set-i-macro PASS0 PASS1 reg_load_i_PASS01_target
285:macro reg_load_i { reg_load_i_PASS01 } # Macro for deprecated version
286: reg_load_i_PASS23 reg-set-i-macro PASS2 PASS3 reg_load_i_PASS23_target
287
288
289# Swap two registers without using a third
290:macro reg_swap rega regb {
291 rega ^= regb
292 regb ^= rega
293 rega ^= regb
294}
295
296# Set lhs equal to rhs
297# lhs must be registers, but rhs can be registers or literals
298:macro reg_set_u16 lhs_hi lhs_lo rhs_hi rhs_lo {
299 lhs_hi := rhs_hi
300 lhs_lo := rhs_lo
301}
302
303# Set 16-bit lhs equal to zero-extended 8-bit rhs
304# lhs must be registers, but rhs can be registers or literals
305:macro reg_set_u16_u8 lhs_hi lhs_lo rhs {
306 lhs_hi := 0
307 lhs_lo := rhs
308}
309
310:macro reg_set_u16_u16imm lhs_hi lhs_lo rhs {
311 hilo rhs
312 reg_set_u16 lhs_hi lhs_lo hi lo
313}
314
315:macro reg_and_u16 lhs-hi lhs-lo rhs-hi rhs-lo {
316 lhs-hi &= rhs-hi
317 lhs-lo &= rhs-lo
318}
319
320:macro reg_or_u16 lhs-hi lhs-lo rhs-hi rhs-lo {
321 lhs-hi |= rhs-hi
322 lhs-lo |= rhs-lo
323}
324
325
326# Shift a register n times to the right, saving the shifted out bits in the MSBs of dest
327:macro reg_split_right src shift-countdown dest {
328 loop while shift-countdown != 0
329 dest >>= dest
330 src >>= src
331 if vF != 0 then dest += 0x80
332 shift-countdown += -1
333 again
334}
335
336# given a size in vD/vE, de
337# Loads registers some offset from current I
338# mem_load_offset <offset byte literal> <first dest register> <last dest register>
339:macro mem_load_offset offset outreg-first outreg-last {
340 SCRATCH := offset
341 i += SCRATCH
342 load outreg-first - outreg-last
343}
344
345# Saves registers some offset from current I
346# mem_save_offset <offset byte literal> <first source register> <last source register>
347:macro mem_save_offset offset inreg-first inreg-last {
348 SCRATCH := offset
349 i += SCRATCH
350 save inreg-first - inreg-last
351}
352
353
354:macro hilo value { :calc hi { value >> 8 } :calc lo { value & 0xFF } }
355
356:macro tobytes value { hilo value :byte hi :byte lo }
357
358##/ stack.o8 /##
359################
360
361#####
362# 8-bit data stack
363###
364#
365# Function `stack-init`
366# Initializes the stack data structures
367# The macro `push <firstreg lastreg bytes>` is used to push a contiguous set of registers onto the stack
368# The macro `pop <firstreg lastreg bytes>` is used to pop a contiguous set of registers off the stack
369#
370# To keep the stack fairly fast the stack pointer is stored in a register, macros are used, and the stack
371# is 8-bit and can only hold 255 items.
372#
373###
374
375
376# Versatile macros that are specified for use with a specific stack
377# TODO: Make non-debug versions / functions not macros for size
378:macro stack_decr_sp sp-reg bytes {
379 vF := bytes
380 sp-reg -= vF
381 assert-neq vF 0 ASSERT-FAIL:_Stack_Underflow
382}
383
384:macro stack_incr_sp sp-reg bytes {
385 SCRATCH := bytes
386 sp-reg += SCRATCH
387 assert-neq vF 1 ASSERT-FAIL:_Stack_Overflow
388}
389
390:macro stack_load_sp sp-reg stack-start {
391 i := long stack-start
392 i += sp-reg
393}
394
395:macro stack_push sp-reg stack-load-func firstreg lastreg bytes {
396 stack-load-func
397 save firstreg - lastreg
398 stack_incr_sp sp-reg bytes
399}
400
401:macro stack_pop sp-reg stack-load-func firstreg lastreg bytes {
402 stack_decr_sp sp-reg bytes
403 stack-load-func
404 load firstreg - lastreg
405}
406
407:macro stack_peek sp-reg stack-load-func firstreg lastreg bytes {
408 stack_decr_sp sp-reg bytes
409 stack-load-func
410 load firstreg - lastreg
411 stack_incr_sp sp-reg bytes
412}
413
414:macro stack_drop sp-reg bytes {
415 stack_decr_sp sp-reg bytes
416}
417
418:macro stack_init sp-reg {
419 sp-reg := 0
420 # TODO: Debug fill stack with canary
421}
422
423# Macros for the 256-byte data stack
424:calc MM_STACK_SIZE { ( SEG_STACK_END - SEG_STACK_START ) + 1 }
425:macro mm_decr_sp bytes { stack_decr_sp SP bytes }
426:macro mm_incr_sp bytes { stack_incr_sp SP bytes }
427: mm_load_sp stack_load_sp SP SEG_STACK_START ;
428:macro push firstreg lastreg bytes { stack_push SP mm_load_sp firstreg lastreg bytes }
429:macro pop firstreg lastreg bytes { stack_pop SP mm_load_sp firstreg lastreg bytes }
430:macro stackpeek firstreg lastreg bytes { stack_peek SP mm_load_sp firstreg lastreg bytes }
431:macro stackdrop bytes { stack_drop SP bytes }
432: stack-init stack_init SP ;
433
434##/ math.o8 /##
435###############
436
437#####
438# Math Macros
439###
440
441:macro SUB_OVERFLOW { vF == 0 }
442:macro ADD_OVERFLOW { vF != 0 }
443:macro SUB_NOT_OVERFLOW { vF != 0 }
444:macro ADD_NOT_OVERFLOW { vF == 0 }
445:macro SHIFT_WAS_BIT { vF != 0 }
446:macro SHIFT_WAS_NOT_BIT { vF == 0 }
447
448
449:macro math_add_u16_u8 outreg_hi outreg_lo inreg_lo {
450 outreg_lo += inreg_lo
451 outreg_hi += vF
452}
453
454:macro math_add_u16_u8imm outreg_hi outreg_lo in_imm {
455 SCRATCH := in_imm
456 outreg_lo += SCRATCH
457 outreg_hi += vF
458}
459
460:macro math_add_u16 outreg_hi outreg_lo inreg_hi inreg_lo {
461 outreg_lo += inreg_lo
462 outreg_hi += vF
463 outreg_hi += inreg_hi
464}
465
466:macro math_add_u16_u16imm outreg_hi outreg_lo in_imm {
467 hilo in_imm
468 math_add_u16 outreg_hi outreg_lo hi lo
469}
470
471:macro math_sub_u16 outreg_hi outreg_lo inreg_hi inreg_lo {
472 outreg_lo -= inreg_lo
473 if vF == 0 then outreg_hi += -1
474 outreg_hi -= inreg_hi
475}
476
477# reverse subtract
478:macro math_subr_u16 outreg_hi outreg_lo inreg_hi inreg_lo {
479 outreg_lo =- inreg_lo
480 if vF == 0 then outreg_hi += -1
481 outreg_hi =- inreg_hi
482}
483
484
485:macro math_sub_u16_u8 outreg_hi outreg_lo inreg_lo {
486 outreg_lo -= inreg_lo
487 if vF == 0 then outreg_hi += -1
488}
489
490:macro math_sub_u16_u8imm outreg_hi outreg_lo in_imm {
491 SCRATCH := in_imm
492 outreg_lo -= SCRATCH
493 if vF == 0 then outreg_hi += -1
494}
495
496
497:macro math_div8_u8 inreg outreg {
498 outreg >>= inreg
499 outreg >>= outreg
500 outreg >>= outreg
501}
502
503:macro math_div16_u8 inreg outreg {
504 outreg >>= inreg
505 outreg >>= outreg
506 outreg >>= outreg
507 outreg >>= outreg
508}
509
510:macro math_div8_u16 inreg_hi inreg_lo outreg_hi outreg_lo {
511 math_div8_u8 inreg_lo outreg_lo
512 outreg_hi >>= inreg_hi
513 if vF == 1 then outreg_lo += 0x20
514 outreg_hi >>= outreg_hi
515 if vF == 1 then outreg_lo += 0x40
516 outreg_hi >>= outreg_hi
517 if vF == 1 then outreg_lo += 0x80
518}
519
520:macro math_mul6_u8 inreg outreg {
521 outreg <<= inreg
522 outreg += inreg
523 outreg <<= outreg
524}
525
526:macro math_mul8_u8 inreg outreg {
527 outreg <<= inreg
528 outreg <<= outreg
529 outreg <<= outreg
530}
531
532:macro math_mul8_u16 inreg_hi inreg_lo outreg_hi outreg_lo {
533 math_mul8_u8 inreg_hi outreg_hi
534 outreg_lo <<= inreg_lo
535 if vF == 1 then outreg_hi += 0x04
536 outreg_lo <<= outreg_lo
537 if vF == 1 then outreg_hi += 0x02
538 outreg_lo <<= outreg_lo
539 if vF == 1 then outreg_hi += 0x01
540}
541
542:macro math_or_u16 outreg-hi outreg-lo inreg-hi inreg-lo {
543 outreg-hi |= inreg-hi
544 outreg-lo |= inreg-lo
545}
546
547
548:macro math_and_u8_literal outreg literal {
549 SCRATCH := literal
550 outreg &= SCRATCH
551}
552
553:macro math_shiftleft_u16_1 reg_hi reg_lo temp {
554 reg_lo <<= reg_lo
555 temp := vF
556 reg_hi <<= reg_hi
557 reg_hi |= temp
558}
559
560:macro math_shiftright_u16_1 reg_hi reg_lo temp {
561 reg_hi >>= reg_hi
562 temp := vf
563 reg_lo >>= reg_lo
564 if temp == 1 then reg_lo += 0x80
565}
566
567
568:macro math_jump_eq_u16 lhs_hi lhs_lo rhs_hi rhs_lo target fallthrough-label {
569 if lhs_hi != rhs_hi then jump fallthrough-label
570 if lhs_lo == rhs_lo then jump target
571 : fallthrough-label
572}
573
574
575:macro math_jump_eq0_u16 lhs_hi lhs_lo target {
576 SCRATCH := lhs_hi
577 SCRATCH |= lhs_lo
578 if SCRATCH == 0 then jump target
579}
580
581
582# Jump to target if lhs > rhs
583:macro math_jump_gt_u16 lhs-hi lhs-lo rhs-hi rhs-lo temp-hi temp-lo target {
584 reg_set_u16 temp-hi temp-lo rhs-hi rhs-lo
585 math_sub_u16 temp-hi temp-lo lhs-hi lhs-lo
586 if vF == 0 then jump target
587}
588
589# Jump to target if lhs < rhs
590:macro math_jump_lt_u16 lhs-hi lhs-lo rhs-hi rhs-lo temp-hi temp-lo target {
591 temp-hi := lhs-hi
592 temp-lo := lhs-lo
593 temp-lo -= rhs-lo
594 temp-lo := vf
595 #if vf == 0 then temp-hi += -1
596 temp-hi -= rhs-hi
597 if vf == 0 then jump target
598 temp-lo |= temp-hi
599 if temp-lo == 0 then jump target
600}
601
602# Jump to target is lhs >= rhs
603:macro math_jump_gte_u16 lhs-hi lhs-lo rhs-hi rhs-lo temp-hi temp-lo target {
604 reg_set_u16 temp-hi temp-lo rhs-hi rhs-lo
605 math_subr_u16 temp-hi temp-lo lhs-hi lhs-lo
606 if vF == 1 then jump target
607}
608
609# Jump to target is lhs <= rhs
610:macro math_jump_lte_u16 lhs-hi lhs-lo rhs-hi rhs-lo temp-hi temp-lo target {
611 reg_set_u16 temp-hi temp-lo lhs-hi lhs-lo
612 math_subr_u16 temp-hi temp-lo rhs-hi rhs-lo
613 if vF == 1 then jump target
614}
615# Division Implementations
616#
617# See [the Division Algorithm page on Wikipedia](https://en.wikipedia.org/wiki/Division_algorithm) for
618# more information.
619#
620
621# if D = 0 then error(DivisionByZeroException) end
622# Q := 0 -- Initialize quotient and remainder to zero
623# R := 0
624# for i := n ç«ãƒ»1 .. 0 do -- Where n is number of bits in N
625# R := R << 1 -- Left-shift R by 1 bit
626# R(0) := N(i) -- Set the least-significant bit of R equal to bit i of the numerator
627# if R ç«•ï½¥ D then
628# R := R ç«ãƒ»D
629# Q(i) := 1
630# end
631# end
632#
633# To save code space, the division functions are wrapped in macros. If division
634# is used, the macros `math_div_u8_implement` and 'math_div_u16_implement' need
635# to be called to instantiate the `math_duv_i8` and `math_div_u16 functions`.
636
637:macro math_div_u8_macro N-reg D-reg R-reg Q-reg i-reg {
638 assert-neq D-reg 0 Divide_By_Zero
639 Q-reg := 0
640 R-reg := 0
641
642 i-reg := 0x80
643 loop
644 R-reg <<= R-reg
645 N-reg <<= N-reg
646 R-reg |= vF # R(0) := N(i) # want to shift and grab bit in vf
647
648 #if R >= D then
649 vF := R-reg
650 vF -= D-reg
651 if vF == 1 then Q-reg |= i-reg
652 if vF == 1 then R-reg -= D-reg
653 #endif
654 i-reg >>= i-reg
655 while i-reg != 0 again
656}
657
658:macro math_div_u8_implement {
659: math_div_u8
660 math_div_u8_macro PASS0 PASS1 VOL0 VOL1 VOL2
661 PASS0 := VOL1
662 PASS1 := VOL0
663 ;
664}
665
666# 10 bytes vv
667:macro math_div_u16_macro N-reg-hi N-reg-lo D-reg-hi D-reg-lo R-reg-hi R-reg-lo Q-reg-hi Q-reg-lo i-reg-hi i-reg-lo temp-hi temp-lo {
668 SCRATCH := D-reg-hi
669 SCRATCH |= D-reg-lo
670 assert-neq SCRATCH 0 Divide_By_Zero
671
672 reg_set_u16 Q-reg-hi Q-reg-lo 0 0
673 reg_set_u16 R-reg-hi R-reg-lo 0 0
674
675 reg_set_u16 i-reg-hi i-reg-lo 0x80 0x00
676 loop
677 #:breakpoint div
678 math_shiftleft_u16_1 R-reg-hi R-reg-lo temp-lo
679 math_shiftleft_u16_1 N-reg-hi N-reg-lo temp-lo
680 R-reg-lo |= vF # R(0) := N(i) # want to shift and grab bit in vf
681
682 #if R >= D then
683 #:breakpoint compare
684 math_jump_lt_u16 R-reg-hi R-reg-lo D-reg-hi D-reg-lo temp-hi temp-lo math_div_u16_next
685 #:breakpoint orsub
686 math_or_u16 Q-reg-hi Q-reg-lo i-reg-hi i-reg-lo
687 math_sub_u16 R-reg-hi R-reg-lo D-reg-hi D-reg-lo
688 #endif
689 : math_div_u16_next
690 math_shiftright_u16_1 i-reg-hi i-reg-lo temp-lo
691 while SCRATCH == 0 again # Last time through vF is 1 not 0
692}
693
694:macro math_div_u16_implement {
695: math_div_u16
696 push NONVOL0 NONVOL1 2 # TODO: See about making this not require a push
697 :alias divu16-N-reg-hi PASS0
698 :alias divu16-N-reg-lo PASS1
699 :alias divu16-D-reg-hi PASS2
700 :alias divu16-D-reg-lo PASS3
701 :alias divu16-R-reg-hi VOL0
702 :alias divu16-R-reg-lo VOL1
703 :alias divu16-Q-reg-hi VOL2
704 :alias divu16-Q-reg-lo VOL3
705 :alias divu16-i-reg-hi VOL4
706 :alias divu16-i-reg-lo VOL5
707 :alias divu16-temp-hi NONVOL0
708 :alias divu16-temp-lo NONVOL1
709 #:breakpoint div16start
710 math_div_u16_macro divu16-N-reg-hi divu16-N-reg-lo divu16-D-reg-hi divu16-D-reg-lo divu16-R-reg-hi divu16-R-reg-lo divu16-Q-reg-hi divu16-Q-reg-lo divu16-i-reg-hi divu16-i-reg-lo divu16-temp-hi divu16-temp-lo
711 reg_set_u16 PASS0 PASS1 VOL2 VOL3
712 reg_set_u16 PASS2 PASS3 VOL0 VOL1
713 #:breakpoint divend
714 pop NONVOL0 NONVOL1 2
715 ;
716}
717##/ memcopy.o8 /##
718
719
720# Copy n bytes from @PASS01..@PASS01+n to @PASS23..@PASS23+n
721# n is passed in as VOL4
722# PASS0-3 are preserved
723# VOL0 - VOL4 are clobbered
724
725
726: mem_copy_PASS01_PASS23
727 if VOL4 == 0 then return
728
729 push PASS0 PASS3 4 # Push arguments so caller doesn't need recalc/save them
730
731 VOL3 := 0x03
732 VOL3 &= VOL4 # If there is an odd number of bytes, do a smaller copy first
733 VOL4 -= VOL3
734 if VOL3 == 0 then jump mem_copy_PASS01_PASS23_aligned
735 loop while VOL3 != 0 # TODO: This is probably the slowest way to do this
736 reg_load_i_PASS01
737 load v0 - v0
738 reg_load_i_PASS23
739 save v0 - v0
740 VOL3 += -1
741 math_add_u16_u8imm PASS0 PASS1 1 # Advance pointers
742 math_add_u16_u8imm PASS2 PASS3 1
743 again
744: mem_copy_PASS01_PASS23_aligned
745 loop while VOL4 != 0
746 reg_load_i_PASS01
747 load v0 - v3
748 reg_load_i_PASS23
749 save v0 - v3
750 VOL4 += -4
751 math_add_u16_u8imm PASS0 PASS1 4 # Advance pointers
752 math_add_u16_u8imm PASS2 PASS3 4
753 again
754
755 pop PASS0 PASS3 4 # Restore arguments
756 ;
757
758: mem_copy_PASS23_PASS01
759 if VOL4 == 0 then return
760
761 push PASS0 PASS3 4 # Push arguments so caller doesn't need recalc/save them
762
763 VOL3 := 0x03
764 VOL3 &= VOL4 # If there is an odd number of bytes, do a smaller copy first
765 VOL4 -= VOL3
766 if VOL3 == 0 then jump mem_copy_PASS23_PASS01_aligned
767 loop while VOL3 != 0 # TODO: This is probably the slowest way to do this
768 reg_load_i_PASS23
769 load v0 - v0
770 reg_load_i_PASS01
771 save v0 - v0
772 VOL3 += -1
773 math_add_u16_u8imm PASS0 PASS1 1 # Advance pointers
774 math_add_u16_u8imm PASS2 PASS3 1
775 again
776 : mem_copy_PASS23_PASS01_aligned
777 loop while VOL4 != 0
778 reg_load_i_PASS23
779 load v0 - v3
780 reg_load_i_PASS01
781 save v0 - v3
782 VOL4 += -4
783 math_add_u16_u8imm PASS0 PASS1 4 # Advance pointers
784 math_add_u16_u8imm PASS2 PASS3 4
785 again
786
787 pop PASS0 PASS3 4 # Restore arguments
788 ;
789
790##/ lists.o8 /##
791#################
792
793#####
794# List primatives
795###
796# Provides functions required to implement intrusive lists.
797# The list is made up of nodes with Prev and Next links,
798# followed by a body of any size.
799#
800# i := node-address
801# +------+------+-------/ /-------+
802# | Prev | Next | Body ... n bytes|
803# +------+------+-------/ /-------+
804# i+0 i+2 i+4 i+4+n
805#
806#
807# API:
808# Basic Operations
809# ListInitHead Initialize head node with sentinal
810# ListInsert Add a node by inserting between node i and next
811# ListUnlink Unlink a node from the list
812#
813# Iteration
814# ListNext Advance to next node
815# ListPrev Fall back to prev node
816#
817# TODO:
818# Higher Level
819# ListDelete Unlink node and free allocated memory
820# ListPushFront Insert node at front of list
821# ListPushBack Insert node at back of list
822# ListPopFront Unlink node from front of list
823# ListPopBack Unlink node from back of list
824# ListSwap Swap two nodes
825#
826# Advanced
827# ListRemoveIf Remove nodes matching some condition
828# ListSort Sort list
829# ListReverse Reverse list
830# ListSplice Insert one list in another
831# ListMergeSorted Merge two sorted lists
832#
833#
834# Tests:
835#
836#
837###
838
839:const LIST_HEADER_BYTES 4
840:const LIST_NEXT_OFFSET 0
841:const LIST_PREV_OFFSET 2
842:calc LIST_BODY_OFFSET { LIST_HEADER_BYTES }
843
844
845:macro list_load_next outreg-first outreg-last { load outreg-first - outreg-last } # Offset is 0 for this case
846:macro list_load_prev outreg-first outreg-last { mem_load_offset LIST_PREV_OFFSET outreg-first outreg-last }
847:macro list_save_next inreg-first inreg-last { save inreg-first - inreg-last } # Offset is 0 for this case
848:macro list_save_prev inreg-first inreg-last { mem_save_offset LIST_PREV_OFFSET inreg-first inreg-last }
849
850
851
852# ListInitHead
853# Initializes the head of a list with sentinal values
854#
855# In: PASS0/1 = location of head
856# Out: None
857: ListInitHead
858#:breakpoint ListInitHead
859 reg_load_i # Load i with PASS0/1
860 VOL0 := 0
861 VOL1 := 0
862 save VOL1 # Store 0,0 in Next and Prev pointers
863 save VOL1
864 ;
865
866# ListInsert
867# Inserts a node after the current node
868#
869# In: PASS0/1 = current node
870# PASS2/3 = new node
871# Out: None
872: ListInsert
873 reg_load_i_PASS01 # i = current node
874 list_load_next VOL2 VOL3 # Keep old next
875 list_save_next PASS2 PASS3 # curr->next = new
876
877 reg_swap PASS0 PASS2
878 reg_swap PASS1 PASS3
879 reg_load_i_PASS01
880 # new->next = saved next
881 list_save_next VOL2 VOL3
882 # new->prev = cur
883 list_save_prev PASS2 PASS3
884 # next->prev = new
885 reg_set_u16 PASS2 PASS3 VOL2 VOL3
886 reg_load_i_PASS23
887 list_save_prev PASS0 PASS1
888 ;
889
890# Does the same as insert, but the pointers are 4-bytes offset from where they are stored
891: ListInsert_4ByteOffset
892 math_add_u16_u8imm PASS0 PASS1 4
893 reg_load_i_PASS01 # i = current node
894 math_sub_u16_u8imm PASS0 PASS1 4
895 list_load_next VOL4 VOL5 # Keep old next
896
897 list_save_next PASS2 PASS3 # curr->next = new
898
899 reg_swap PASS0 PASS2
900 reg_swap PASS1 PASS3
901 math_add_u16_u8imm PASS0 PASS1 4
902
903 reg_load_i_PASS01
904 math_sub_u16_u8imm PASS0 PASS1 4
905 # new->next = saved next
906 list_save_next VOL4 VOL5
907 # new->prev = cur
908 list_save_prev PASS2 PASS3
909 # next->prev = new
910 reg_set_u16 PASS2 PASS3 VOL4 VOL5
911 #math_add_u16_u8imm PASS2 PASS3 4
912
913 reg_load_i_PASS23
914 list_save_prev PASS0 PASS1
915 ;
916
917
918
919# ListUnlink
920# Unlinks a node from the list
921#
922# In: PASS0/1 = current node
923# Out: None
924: ListUnlink
925:breakpoint ListUnlink
926 reg_load_i
927 list_load_next VOL0 VOL3 # Load 4 bytes (next & prev)
928
929 # prev->next = curr->next
930 reg_set_u16 PASS0 PASS1 VOL2 VOL3
931 reg_load_i
932 list_save_next VOL0 VOL1
933
934 # next->prev = curr->prev
935 reg_set_u16 PASS0 PASS1 VOL0 VOL1
936 reg_load_i
937 list_save_prev VOL2 VOL3
938 ;
939
940
941
942
943# ListNext
944# Advances to next node
945# In: PASS0/PASS1 = current node
946# Out: PASS0/PASS1 = next next
947: ListNext
948 reg_load_i
949 list_load_next PASS0 PASS1
950 ;
951
952# ListPrev
953# Fall back to previous node
954# In: PASS0/PASS1 = current node
955# Out: PASS0/PASS1 = next next
956: ListPrev
957 reg_load_i
958 list_load_prev PASS0 PASS1
959 ;
960
961
962:macro ListEnableDebug {
963: ListDebug
964 # i := PASS0/PASS1
965 reg_load_i
966 : list_debug_next
967 # Look at data
968 load v0 - v5
969 :breakpoint Debug-List
970 ListNext
971 if PASS0 != 0 then jump list_debug_next
972 if PASS1 != 0 then jump list_debug_next
973 ;
974}
975
976##/ heap.o8 /##
977###############
978
979#####
980# 16-bit heap
981###
982# Highly influenced by [umm_malloc](https://github.com/rhempel/umm_malloc)
983#
984###
985# API
986###
987# `heap_init` Initializes the heap data structures
988#
989# `malloc` Allocates the requested number of bytes on the heap
990# In: PASS0/1 - bytes to allocate
991# Out: PASS0/1 - Address of allocated memory, 0x0000 upon error
992#
993# `free` Frees a block of memory that was allocated with `malloc`
994# In: PASS0/1 - Pointer to allocated block
995#
996# Macro `load_from <addr-reg-lo addr-reg-hi dest-reg-lo dest-reg-hi>`
997# Loads from the address in `addr-reg-[lo,hi]`. Data is stored in the set of registers
998# `dest-reg-lo - dest-reg-hi`.
999#
1000# Macro `save_to <addr-reg-lo addr-reg-hi src-reg-lo src-reg-hi>`
1001# Saves to the address in `addr-reg-[lo,hi]`. Data is sourced from the set of registers
1002# `dest-reg-lo - dest-reg-hi`.
1003###
1004
1005
1006# A block is a minimum of 8 bytes, 4 of which are a header containing links to the BlockList. When a block is free,
1007# the next 4 bytes are links to the FreeList. When a block is allocated, the block is no longer on the free list,
1008# so those bytes can be repurposed for data storage.
1009#
1010#
1011# +-----------------+------------+-----------+--------+--------+--------+--------+----------------+----------------+
1012# | Block Type | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte6 | Byte 7 | Rest of Block |
1013# +=================+============+===========+========+========+========+========+================+================+
1014# | Free Block | NextPtr + FreeFlag = 1 | PrevPtr | NextFreePtr | PrevFreePtr | N/A |
1015# +-----------------+------------------------+-----------------+-----------------+----------------+----------------+
1016# | Allocated Block | NextPtr + FreeFlag = 0 | PrevPtr | Body |
1017# +-----------------+------------------------+-----------------+---------------------------------------------------+
1018#
1019# The first and last blocks are used as the head and tail of both the block list and free list.
1020#
1021# The Prev And Next links always move through memory monotonically, but the PrevFree and NextFree have no such constraint.
1022#
1023# After initialization:
1024# Block 0: Next = 1 Prev = Null NextFree = 1 PrevFree = Null 8 bytes
1025# Block 1: Next = n Prev = 0 NextFree = n PrevFree = 0 (SizeOfHeap - 16) Bytes
1026# Block n: Next = Null Prev = 1 NextFree = Null PrevFree = 1 8 bytes
1027#
1028# An allocation scans the free list for the best-fit block:
1029# Set best-size to largest possible
1030# Starting at head of free list,
1031# If free block is an exact size, use that block
1032# If free block is larger than requested size and smaller than best-size, record this block as best
1033# Advance to next free block
1034# If no block was found, we are out of memory
1035#
1036# If block was exact:
1037# Remove the block from the free list
1038# Mark block as not free
1039# Return address + 4 to caller (caller doesn't care or want the header)
1040# Else, block not exact:
1041# Split the block into two pieces
1042# The beginning of the block is still free, and still points to the correct next free block
1043# The last part of the block will be returned to caller
1044# Insert the new block into the block list
1045#
1046#
1047# Free returns a block to the free list. If the previous and/or next blocks are also
1048# free, those blocks are coalesced into one larger block.
1049# Adjust address to address - 4 so we are looking at the header
1050# If next is marked free:
1051# Unlink the next block from the block list (this makes the current block larger)
1052# If Prev is marked free:
1053# Unlink the current block from the block list (this makes the previous block larger)
1054# The previous block was already on the free list
1055# If Prev was not free:
1056# Add the current block to the head of the free list
1057# Mark the current block free
1058
1059
1060
1061# [ next block / FREE? | prev block | next free | prev free ]
1062# bottom bit of next block is used to mark if this block is free
1063# after a block is used, data is stored in the next/prev free area
1064
1065:const MM_HEAP_BLOCKSIZE 8
1066:const MM_HEAP_HEADERSIZE 4
1067:const MM_HEAP_BLOCKSHIFT 3
1068:calc MM_HEAP_HEADERSIZE_PLUS_ROUNDING { 4 + ( MM_HEAP_BLOCKSIZE - 1 ) }
1069
1070# Align the heap for 8-bit blocks
1071:calc MM_HEAP_ADDR { ( ( SEG_HEAP_START + ( MM_HEAP_BLOCKSIZE - 1 ) ) >> MM_HEAP_BLOCKSHIFT ) << MM_HEAP_BLOCKSHIFT }
1072:calc MM_HEAP_END_ADDR { ( ( SEG_HEAP_END + 1 ) >> MM_HEAP_BLOCKSHIFT ) << MM_HEAP_BLOCKSHIFT }
1073
1074:calc MM_HEAP_ADDR_HI { SEG_HEAP_START >> 8 }
1075:calc MM_HEAP_ADDR_LO { SEG_HEAP_START & 0xFF }
1076
1077:calc MM_HEAP_SIZE { ( MM_HEAP_END_ADDR - MM_HEAP_ADDR ) + 1 }
1078:calc MM_HEAP_SIZE_HI { MM_HEAP_SIZE >> 8 }
1079:calc MM_HEAP_SIZE_LO { MM_HEAP_SIZE & 0xFF }
1080
1081:calc MM_HEAP_BODYSIZE { MM_HEAP_BLOCKSIZE - MM_HEAP_HEADERSIZE }
1082:calc MM_HEAP_NUMBLOCKS { MM_HEAP_SIZE / MM_HEAP_BLOCKSIZE }
1083
1084:const MM_HEAP_FREEBIT 1
1085:calc MM_HEAP_FREEMASK { MM_HEAP_BLOCKSIZE - 1 }
1086:calc MM_HEAP_BLOCKMASK { ~ MM_HEAP_FREEMASK }
1087:const MM_HEAP_NEXT_OFFSET 0
1088:const MM_HEAP_PREV_OFFSET 2
1089:const MM_HEAP_NEXTFREE_OFFSET 4
1090:const MM_HEAP_PREVFREE_OFFSET 6
1091
1092:calc MM_HEAP_BLOCK0_ADDR { MM_HEAP_ADDR }
1093:calc MM_HEAP_BLOCK0_ADDR_HI { MM_HEAP_BLOCK0_ADDR >> 8 }
1094:calc MM_HEAP_BLOCK0_ADDR_LO { MM_HEAP_BLOCK0_ADDR & 0xFF }
1095
1096:calc MM_HEAP_BLOCK1_ADDR { MM_HEAP_BLOCK0_ADDR + MM_HEAP_BLOCKSIZE }
1097:calc MM_HEAP_BLOCK1_ADDR_HI { MM_HEAP_BLOCK1_ADDR >> 8 }
1098:calc MM_HEAP_BLOCK1_ADDR_LO { MM_HEAP_BLOCK1_ADDR & 0xFF }
1099
1100:calc MM_HEAP_LASTBLOCK_ADDR { MM_HEAP_END_ADDR - MM_HEAP_BLOCKSIZE }
1101:calc MM_HEAP_LASTBLOCK_ADDR_HI { MM_HEAP_LASTBLOCK_ADDR >> 8 }
1102:calc MM_HEAP_LASTBLOCK_ADDR_LO { MM_HEAP_LASTBLOCK_ADDR & 0xFF }
1103:calc MM_HEAP_LASTBLOCK_ADDR_LO_AND_FREE { MM_HEAP_LASTBLOCK_ADDR_LO | MM_HEAP_FREEBIT }
1104
1105: heap_init
1106 # Setup the 0th block, which is the special head of the list, and just points to the 1st block
1107 # [*NEXT|PREV|NF|PF] = [ 0, 1, 1, 1 ]
1108 i := long mm_heap
1109 reg_set_u16 VOL0 VOL1 MM_HEAP_BLOCK1_ADDR_HI MM_HEAP_BLOCK1_ADDR_LO
1110 reg_set_u16 VOL2 VOL3 0 0 #MM_HEAP_BLOCK0_ADDR_HI MM_HEAP_BLOCK0_ADDR_LO
1111 save VOL3
1112 #reg_set_u16 VOL2 VOL3 MM_HEAP_BLOCK1_ADDR_HI MM_HEAP_BLOCK1_ADDR_LO
1113 save VOL3
1114
1115 # I auto-advanced to the 1st block
1116 # The first block is one huge block covering all available memory
1117 # This is the last block, so the next free block is 0
1118 # [*NEXT|PREV|NF|PF] = [ FREE | last-block, 0, 0, 0 ]
1119 reg_set_u16 VOL0 VOL1 MM_HEAP_LASTBLOCK_ADDR_HI MM_HEAP_LASTBLOCK_ADDR_LO_AND_FREE
1120 reg_set_u16 VOL2 VOL3 MM_HEAP_BLOCK0_ADDR_HI MM_HEAP_BLOCK0_ADDR_LO
1121 save VOL3
1122 reg_set_u16 VOL0 VOL1 0 0 # MM_HEAP_BLOCK0_ADDR_HI MM_HEAP_BLOCK0_ADDR_LO
1123 reg_set_u16 VOL2 VOL3 0 0 # MM_HEAP_BLOCK0_ADDR_HI MM_HEAP_BLOCK0_ADDR_LO
1124 save VOL3
1125
1126 # Finally, setup the last block to indicate no more blocks
1127 # This is not a free block, so no need to touch NF/PF
1128 # [NEXT|PREV|NF|PF] = [ 0, 1, ??, ?? ]
1129 i := long MM_HEAP_LASTBLOCK_ADDR
1130 reg_set_u16 VOL0 VOL1 0 0 #MM_HEAP_BLOCK0_ADDR_HI MM_HEAP_BLOCK0_ADDR_LO
1131 reg_set_u16 VOL2 VOL3 MM_HEAP_BLOCK1_ADDR_HI MM_HEAP_BLOCK1_ADDR_LO
1132 save VOL3
1133 #:breakpoint heap-init
1134 ;
1135
1136
1137# Loads from particular heap offsets
1138:macro heap_load_next outreg-first outreg-last { load outreg-first - outreg-last } # Offset is 0 for this case
1139:macro heap_load_prev outreg-first outreg-last { mem_load_offset MM_HEAP_PREV_OFFSET outreg-first outreg-last }
1140:macro heap_load_prevfree outreg-first outreg-last { mem_load_offset MM_HEAP_PREVFREE_OFFSET outreg-first outreg-last }
1141:macro heap_load_nextfree outreg-first outreg-last { mem_load_offset MM_HEAP_NEXTFREE_OFFSET outreg-first outreg-last }
1142
1143# Saves to particular heap offsets
1144:macro heap_save_next inreg-first inreg-last { save inreg-first - inreg-last } # Offset is 0 for this case
1145:macro heap_save_prev inreg-first inreg-last { mem_save_offset MM_HEAP_PREV_OFFSET inreg-first inreg-last }
1146:macro heap_save_nextfree inreg-first inreg-last { mem_save_offset MM_HEAP_NEXTFREE_OFFSET inreg-first inreg-last }
1147:macro heap_save_prevfree inreg-first inreg-last { mem_save_offset MM_HEAP_PREVFREE_OFFSET inreg-first inreg-last }
1148
1149# round the address in addr-hi/lo to the next block boundary
1150:macro mm_heap_round_req_to_blocksize addr-hi addr-lo {
1151 SCRATCH := MM_HEAP_HEADERSIZE_PLUS_ROUNDING
1152 math_add_u16_u8 addr-hi addr-lo SCRATCH
1153 SCRATCH := MM_HEAP_BLOCKMASK
1154 addr-lo &= SCRATCH
1155}
1156
1157# disconnect the block at addr-hi/lo from the free list
1158# set the nextfree of the current prevfree to the current nextfree
1159# and set the prevfree of the current nextfree to the current prevfree
1160:macro mm_heap_disconnect_from_free_list addr-hi addr-lo {
1161 # Set I to registed address
1162 reg_set_u16 PASS0 PASS1 addr-hi addr-lo
1163 reg_load_i
1164
1165 # Clear the free flag on current block
1166 heap_load_next VOL0 VOL1
1167 SCRATCH := MM_HEAP_BLOCKMASK
1168 SCRATCH &= VOL1
1169 heap_save_next VOL0 VOL1
1170
1171 # Store current prevfree and nextfree
1172 # VOL0/VOL1 = nextfree
1173 # VOL2/VOL3 = prevfree
1174 heap_load_nextfree VOL0 VOL3 # load 4 bytes
1175 #:breakpoint disco-loadednfpf
1176
1177 # Set nextfree of current prevfree to current nextfree
1178 PASS0 := VOL2
1179 PASS1 := VOL3
1180 reg_load_i
1181 heap_save_nextfree VOL0 VOL1
1182 #:breakpoint disco-saved-prevfree-nextfree
1183
1184 # Set prevfree of current nextfree to current prevfree
1185 PASS0 := VOL0
1186 PASS1 := VOL1
1187 reg_load_i
1188 heap_save_prevfree VOL2 VOL3
1189 #:breakpoint disco-saved-nextfree-prevfree
1190}
1191
1192# Clear free bit in block address
1193:macro mm_heap_clearfree addr-hi addr-lo {
1194 SCRATCH := MM_HEAP_BLOCKMASK
1195 addr-lo &= SCRATCH
1196}
1197
1198# Set the free bit in a block address
1199:macro mm_heap_setfree addr-hi addr-lo {
1200 SCRATCH := MM_HEAP_FREEBIT
1201 addr-lo |= SCRATCH
1202}
1203
1204:macro mm_heap_jump_if_not_free next-hi next-lo target {
1205 SCRATCH := MM_HEAP_FREEBIT
1206 SCRATCH &= next-lo
1207 if SCRATCH == 0 then jump target
1208}
1209
1210# in: PASS0/1 = request size in bytes
1211# out: PASS0/1 = address of allocated block
1212: malloc
1213
1214# Live list:
1215#
1216# Phases | ReqSize CurrBlock BestBlock BestSize CurrSize NewBlock NextBlock
1217#--------------------------------------------------------------------
1218# Loop Setup | X X X
1219# Loop: |
1220# Check Block | X X X X
1221# Save Best | X X X X
1222# Advance Loop| X
1223# Found Exact | X
1224# Split | X X X
1225
1226
1227:alias MM_M_REQSIZE_HI VOL4
1228:alias MM_M_REQSIZE_LO VOL5
1229
1230:alias MM_M_CURRBLOCK_HI NONVOL2
1231:alias MM_M_CURRBLOCK_LO NONVOL3
1232
1233:alias MM_M_BESTSIZE_HI NONVOL0
1234:alias MM_M_BESTSIZE_LO NONVOL1
1235
1236# BestBlock and NextBlock share VOL2/3
1237:alias MM_M_BESTBLOCK_HI VOL2
1238:alias MM_M_BESTBLOCK_LO VOL3
1239
1240:alias MM_M_NEXTBLOCK_HI NONVOL0
1241:alias MM_M_NEXTBLOCK_LO NONVOL1
1242
1243:alias MM_M_CURRSIZE_HI VOL0
1244:alias MM_M_CURRSIZE_LO VOL1
1245
1246# Newblock is stored in PASS0/1, since it will be returned anyway
1247:alias MM_M_NEWBLOCK_HI PASS0
1248:alias MM_M_NEWBLOCK_LO PASS1
1249
1250
1251 # if size requested is 0, return 'NULL'
1252 SCRATCH := PASS0
1253 SCRATCH |= PASS1
1254 if SCRATCH == 0 then return # PASS0/PASS1 are already 0
1255
1256 #:breakpoint malloc-begins
1257
1258 push NONVOL0 NONVOL3 4 # save registers
1259 # TODO: Clean this up and hopefully remove the push
1260
1261 # Adjust requested size to account for header and pad out to a page
1262 reg_set_u16 MM_M_REQSIZE_HI MM_M_REQSIZE_LO PASS0 PASS1
1263 mm_heap_round_req_to_blocksize MM_M_REQSIZE_HI MM_M_REQSIZE_LO
1264
1265 # Clear Best Size by setting to largest possible
1266 reg_set_u16 MM_M_BESTSIZE_HI MM_M_BESTSIZE_LO 0xFF 0xFF
1267
1268 # Start with Current Block = Block0->NextFree
1269 i := long MM_HEAP_ADDR # I = Block0->NextFree
1270 heap_load_nextfree MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1271
1272 ## Traverse free list to find best size block
1273 : mm_malloc_traverse_free_list
1274 #:breakpoint malloc-free-list-loop
1275
1276 # If CurrBlock = Null then this is end of list, examine results
1277 math_jump_eq0_u16 MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO mm_malloc_free_list_end
1278
1279 # Determine size of CurrentBlock
1280 # Size = addrof(CurrentBlock->Next) - addrof(CurrentBlock)
1281 # Don't need to account for the header, the request size has been adjusted
1282 reg_set_u16 PASS0 PASS1 MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1283 reg_load_i # I = CurrentBlock
1284 heap_load_next PASS0 PASS1
1285 mm_heap_clearfree PASS0 PASS1
1286 reg_set_u16 MM_M_CURRSIZE_HI MM_M_CURRSIZE_LO PASS0 PASS1
1287 math_sub_u16 MM_M_CURRSIZE_HI MM_M_CURRSIZE_LO MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO # CURSIZE = Next - Current
1288 #:breakpoint sizecheck
1289
1290 # if block size is exactly the requested size, we can just hand out this block immediately
1291 math_jump_eq_u16 MM_M_CURRSIZE_HI MM_M_CURRSIZE_LO MM_M_REQSIZE_HI MM_M_REQSIZE_LO mm_malloc_exact_size mm_malloc_not_exact
1292
1293 #:breakpoint malloc-block-not-equal
1294 # if the block is not big enough, continue
1295 math_jump_lt_u16 MM_M_CURRSIZE_HI MM_M_CURRSIZE_LO MM_M_REQSIZE_HI MM_M_REQSIZE_LO PASS0 PASS1 mm_malloc_next_free_block
1296
1297 #:breakpoint malloc-block-big-enough
1298 # if block size larger than current best, continue
1299 math_jump_gt_u16 MM_M_CURRSIZE_HI MM_M_CURRSIZE_LO MM_M_BESTSIZE_HI MM_M_BESTSIZE_LO PASS0 PASS1 mm_malloc_next_free_block
1300
1301 #:breakpoint malloc-block-save
1302 # Save current block, it is the best size so far
1303 reg_set_u16 MM_M_BESTBLOCK_HI MM_M_BESTBLOCK_LO MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1304 reg_set_u16 MM_M_BESTSIZE_HI MM_M_BESTSIZE_LO MM_M_CURRSIZE_HI MM_M_CURRSIZE_LO
1305
1306
1307 : mm_malloc_next_free_block
1308
1309 # Set current block to next _free_ block
1310 # TODO: I already = current block ?
1311 #:breakpoint malloc-prenext
1312 reg_set_u16 PASS0 PASS1 MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1313 reg_load_i
1314 heap_load_nextfree MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1315 mm_heap_clearfree MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1316 #:breakpoint malloc-postnext
1317 jump mm_malloc_traverse_free_list
1318
1319 : mm_malloc_free_list_end
1320 #:breakpoint malloc-list-end
1321 # if best size is still 0xffff then we ran out of memory
1322 SCRATCH := MM_M_BESTSIZE_HI
1323 SCRATCH &= MM_M_BESTSIZE_LO
1324 if SCRATCH == 0xFF then jump mm_malloc_oom
1325
1326
1327 : mm_malloc_split
1328 # At this point, the best block exists and is not exact size
1329 # So we need to split the block into two pieces
1330 # One piece is allocated, the remainder is still free
1331 # We put the allocated bit at the end to reduce the number of updates
1332 # by keeping the free list the same
1333 #
1334 # BestBlock->next = newblock +
1335
1336 #:breakpoint malloc-split
1337
1338 # set i to BestBlock, the block we are going to split
1339 reg_set_u16 PASS0 PASS1 MM_M_BESTBLOCK_HI MM_M_BESTBLOCK_LO
1340 reg_load_i
1341
1342 # Save BestBlock->Next so we can put it in the newblock
1343 # Have to clear the free flag since we want to store the bare address
1344 heap_load_next MM_M_NEXTBLOCK_HI MM_M_NEXTBLOCK_LO
1345 mm_heap_clearfree MM_M_NEXTBLOCK_HI MM_M_NEXTBLOCK_LO
1346
1347 # Addr of new block = BestBlock->Next - ReqSize
1348 reg_set_u16 MM_M_NEWBLOCK_HI MM_M_NEWBLOCK_LO MM_M_REQSIZE_HI MM_M_REQSIZE_LO
1349 math_subr_u16 MM_M_NEWBLOCK_HI MM_M_NEWBLOCK_LO MM_M_NEXTBLOCK_HI MM_M_NEXTBLOCK_LO
1350
1351 # Point BestBlock->Next to the new block
1352 # The beginning of the block is free, so set free flag
1353 #:breakpoint best->next=new
1354 reg_set_u16 VOL0 VOL1 MM_M_NEWBLOCK_HI MM_M_NEWBLOCK_LO
1355 mm_heap_setfree VOL0 VOL1
1356 heap_save_next VOL0 VOL1
1357
1358 # Set i to NewBlock
1359 reg_set_u16 PASS0 PASS1 MM_M_NEWBLOCK_HI MM_M_NEWBLOCK_LO
1360 reg_load_i
1361
1362 # NewBlock->Next points to the old BestBlock-Next
1363 #:breakpoint new->next=old->next
1364 heap_save_next MM_M_NEXTBLOCK_HI MM_M_NEXTBLOCK_LO
1365
1366 # Set NewBlock->Prev to point to BestBlock
1367 #:breakpoint new->prev=best
1368 heap_save_prev MM_M_BESTBLOCK_HI MM_M_BESTBLOCK_LO
1369
1370 # return the new block
1371 #reg_set_u16 PASS0 PASS1 MM_M_NEWBLOCK_HI MM_M_NEWBLOCK_LO
1372 jump mm_malloc_pop_and_return
1373
1374 : mm_malloc_oom
1375 assert malloc-Out_Of_Memory
1376 # Return `Null` (0x0000)
1377 reg_set_u16 PASS0 PASS1 0x00 0x00
1378 jump mm_malloc_pop_and_return
1379
1380 : mm_malloc_exact_size
1381 #:breakpoint malloc-exact
1382
1383 # This is an exact fit and we don't need to split off a block
1384 # 'unhook' from free list and clear free bit
1385 mm_heap_disconnect_from_free_list MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1386 reg_set_u16 PASS0 PASS1 MM_M_CURRBLOCK_HI MM_M_CURRBLOCK_LO
1387 : mm_malloc_pop_and_return
1388
1389 # Return the address of the body, 4 bytes after the header
1390 math_add_u16_u8imm PASS0 PASS1 MM_HEAP_HEADERSIZE
1391
1392 pop NONVOL0 NONVOL3 4
1393 #:breakpoint malloc-end
1394 ;
1395
1396# in: PASS0/1 - Address to free
1397: free
1398 #:breakpoint free
1399 push NONVOL0 NONVOL1 2
1400:alias MM_F_TOFREE_HI NONVOL0
1401:alias MM_F_TOFREE_LO NONVOL1
1402
1403# NEXT and PREV must be adjacent
1404:alias MM_F_NEXT_HI VOL2
1405:alias MM_F_NEXT_LO VOL3
1406:alias MM_F_PREV_HI VOL4
1407:alias MM_F_PREV_LO VOL5
1408
1409# NEXT NEXT and 0NEXTFREE share registers
1410:alias MM_F_NEXTNEXT_HI PASS2
1411:alias MM_F_NEXTNEXT_LO PASS3
1412:alias MM_F_0NEXTFREE_HI MM_F_NEXTNEXT_HI
1413:alias MM_F_0NEXTFREE_LO MM_F_NEXTNEXT_LO
1414
1415 assert-neq0-u16 PASS0 PASS1 ASSERT_Attempt_To_Free_Null
1416 # TODO: This could also check to make sure the memory is in the heap and if the address is on a block boundary
1417
1418 # Subtract headersize from the address
1419 SCRATCH := MM_HEAP_HEADERSIZE
1420 math_sub_u16_u8 PASS0 PASS1 SCRATCH
1421
1422 # Save the block to be freed
1423 reg_set_u16 MM_F_TOFREE_HI MM_F_TOFREE_LO PASS0 PASS1
1424
1425 # i = ToFree
1426 reg_load_i
1427
1428 # Load prev/next (4 bytes)
1429 heap_load_next MM_F_NEXT_HI MM_F_PREV_LO
1430 mm_heap_clearfree MM_F_NEXT_HI MM_F_NEXT_LO
1431
1432 #:breakpoint free-next-prev-loaded
1433
1434 ## If Next is free, combine ToFree with Next
1435 # i = Next
1436 reg_set_u16 PASS0 PASS1 MM_F_NEXT_HI MM_F_NEXT_LO
1437 reg_load_i
1438 # Load Next->Next
1439 heap_load_next MM_F_NEXTNEXT_HI MM_F_NEXTNEXT_LO
1440
1441 #:breakpoint free-checking-is-next-free
1442 # Can't combine if Next isn't free
1443 mm_heap_jump_if_not_free MM_F_NEXTNEXT_HI MM_F_NEXTNEXT_LO mm_free_next_not_free
1444
1445 #:breakpoint free-next-is-free
1446
1447 # Disconnect Next from free list
1448 mm_heap_disconnect_from_free_list MM_F_NEXT_HI MM_F_NEXT_LO
1449
1450 # Assimilate ToFree and Next
1451 # I = Next->Next
1452 reg_set_u16 PASS0 PASS1 MM_F_NEXTNEXT_HI MM_F_NEXTNEXT_LO
1453 reg_load_i
1454
1455 # Set Next->Next->Prev to ToFree
1456 heap_save_prev MM_F_TOFREE_HI MM_F_TOFREE_LO
1457
1458 # Set ToFree->Next = Next->Next
1459 # I = ToFree
1460 reg_set_u16 PASS0 PASS1 MM_F_TOFREE_HI MM_F_TOFREE_LO
1461 reg_load_i
1462 heap_save_next MM_F_NEXTNEXT_HI MM_F_NEXTNEXT_LO
1463
1464 # Track change to next block when needed to assimilate prev block
1465 reg_set_u16 MM_F_NEXT_HI MM_F_NEXT_LO MM_F_NEXTNEXT_HI MM_F_NEXTNEXT_LO
1466 : mm_free_next_not_free
1467
1468 #:breakpoint free-checking-is-prev-free
1469 ## If the Prev block is free, combine ToFree with Free
1470 # i = Prev
1471 reg_set_u16 PASS0 PASS1 MM_F_PREV_HI MM_F_PREV_LO
1472 reg_load_i
1473 # Load Prev->Next to check free status, can't combine if prev isn't free
1474 heap_load_next VOL0 VOL1
1475 mm_heap_jump_if_not_free VOL0 VOL1 mm_free_prev_not_free
1476
1477 #:breakpoint free-assimilate-prev
1478 # Assimilate Prev and ToFree
1479 # Prev->Next = Next (note: next might have just changed, fixed above)
1480 heap_save_next MM_F_NEXT_HI MM_F_NEXT_LO
1481
1482 # Next->Prev = Prev
1483 # Prev is already on the free list
1484 reg_set_u16 PASS0 PASS1 MM_F_NEXT_HI MM_F_NEXT_LO
1485 reg_load_i
1486 heap_save_prev MM_F_PREV_HI MM_F_PREV_LO
1487
1488 # Finished
1489 jump mm_free_pop_and_return
1490
1491 : mm_free_prev_not_free
1492 :breakpoint free-adding-to-freelist
1493 ## If the Prev block wasn't free, then add ToFree to the head of the free list
1494 # Set 0->nextfree->prevfree = tofree
1495 i := long MM_HEAP_BLOCK0_ADDR
1496 heap_load_nextfree MM_F_0NEXTFREE_HI MM_F_0NEXTFREE_LO
1497 reg_set_u16 PASS0 PASS1 MM_F_0NEXTFREE_HI MM_F_0NEXTFREE_LO
1498 reg_load_i # I = 0->nextfree
1499 heap_save_prevfree MM_F_TOFREE_HI MM_F_TOFREE_LO
1500
1501 :breakpoint set-nextfree
1502 # set tofree->nextfree = 0->nextfree
1503 reg_set_u16 PASS0 PASS1 MM_F_TOFREE_HI MM_F_TOFREE_LO
1504 reg_load_i
1505 heap_save_nextfree MM_F_0NEXTFREE_HI MM_F_0NEXTFREE_LO
1506
1507 :breakpoint set-prevfree
1508
1509 # set tofree-prevfree = Block0
1510 VOL0 := MM_HEAP_BLOCK0_ADDR_HI
1511 VOL1 := MM_HEAP_BLOCK0_ADDR_LO
1512 save VOL0 - VOL1
1513
1514 # set 0->nextfree = tofree
1515 i := long MM_HEAP_BLOCK0_ADDR
1516 heap_save_nextfree MM_F_TOFREE_HI MM_F_TOFREE_LO
1517
1518 : mm_free_pop_and_return
1519 :breakpoint free-complete
1520 push NONVOL0 NONVOL1 2
1521 ;
1522
1523:macro malloc_u16imm u16imm {
1524 hilo u16imm
1525 reg_set_u16_u16imm PASS0 PASS1 u16imm
1526 malloc
1527}
1528##/ trampoline.l8 /##
1529#####################
1530
1531
1532# Allocates space for the trampoline
1533:macro Trampoline: label {
1534 : label jump label
1535}
1536
1537# Sets the trampoline to jump to target-reg-hi and target-reg-lo
1538:macro Trampoline-Set-Jump label target-reg-hi target-reg-lo {
1539 target-reg-hi += 0x10
1540 i := label
1541 save target-reg-hi - target-reg-lo
1542}
1543
1544# Slightly faster if using a constant target
1545:macro Trampoline-Set-Jump-Const label target reg-hi reg-lo {
1546 hilo target
1547 :calc hi { 0x10 | hi }
1548 reg-hi := hi
1549 reg-lo := lo
1550 i := label
1551 save reg-hi - reg-lo
1552}
1553
1554# Makes the trampoline a `Call`
1555:macro Trampoline-Set-Call-Const label target reg-hi reg-lo {
1556 hilo target
1557 :calc hi { 0x20 | hi }
1558 reg-hi := hi
1559 reg-lo := lo
1560 i := label
1561 save reg-hi - reg-lo
1562}
1563
1564# Makes the trampoline a no-op
1565:macro Trampoline-Set-Nop label reg-hi reg-lo {
1566 i := label
1567 reg-hi := 0x8F # Set instruction to VF := VF
1568 reg-lo := 0xF0
1569 save reg-hi - reg-lo
1570}
1571
1572
1573
1574
1575## Other Self-Modifying tools
1576
1577# Create a target to be modified by the macros below.
1578:macro DataTarget: target-label {
1579 :next target-label
1580}
1581
1582# Changes 1 byte in memory, intended to be used
1583# with self-modifying code
1584:macro Modify-Target target-label reg-in {
1585 i := target-label
1586 save reg-in - reg-in
1587}
1588
1589# Changes 1 byte in memory, intended to be used
1590# with self-modifying code
1591:macro Modify-Target-Imm target-label u8imm {
1592 i := target-label
1593 SCRATCH := u8imm
1594 save SCRATCH - SCRATCH
1595}
1596
1597##/ tetra/tetra-base.o8 /##
1598###########################
1599
1600#####
1601# Tetra Octo Base Words
1602###
1603# These are Octo implementations of the most basic Tetra Words. These are used
1604# by the rest of the Tetra code to build more complicated Words.
1605#
1606# TODO: It would be really nice to prune the unused functions automatically
1607# That could be done with macros that are instantiated as needed.
1608#
1609# See base.t4 for the built-in word definitions
1610###
1611
1612#:: LITERAL16 TETRA_LITERAL
1613:macro TETRA_PUSH_U16_MACRO {
1614 # Load 2 bytes from BYTECODE
1615 # This bit assumes PASS0/1 is a ptr to the current bytecode
1616 reg_load_i
1617 load VOL0 - VOL1
1618
1619 vF := 2
1620 math_add_u16_u8 PASS0 PASS1 vF
1621
1622 # Push 2 bytes
1623 push VOL0 VOL1 2
1624}
1625
1626:macro TETRA_RET_MACRO {
1627 # Return back to whatever called tetra_decode
1628 return
1629}
1630#:: + TETRA_ADD
1631:macro TETRA_ADD_MACRO {
1632 # Pop 4 bytes
1633 # add u16
1634 # Push 2 bytes
1635}
1636
1637#:: - TETRA_SUB
1638:macro TETRA_SUB_MACRO {
1639 # Pop 4 bytes
1640 # sub u16
1641 # push 2 bytes
1642}
1643
1644#:: * TETRA_MUL
1645:macro TETRA_MUL_MACRO {
1646 # pop 4 bytes
1647 # call math_mul_u16 that doesn't yet exist
1648 # push 2 bytes (4 bytes??)
1649}
1650#:: / TETRA_DIV
1651:macro TETRA_DIV_MACRO { }
1652#:: AND TETRA_AND
1653:macro TETRA_AND_MACRO { }
1654#:: OR TETRA_OR
1655:macro TETRA_OR_MACRO { }
1656#:: XOR TETRA_XOR
1657:macro TETRA_XOR_MACRO { }
1658#:: = TETRA_EQ
1659:macro TETRA_EQ_MACRO { }
1660#:: < TETRA_LT
1661:macro TETRA_LT_MACRO { }
1662#:: > TETRA_GT
1663:macro TETRA_GT_MACRO { }
1664#:: NEGATE TETRA_NEGATE
1665:macro TETRA_NEGATE_MACRO { }
1666#:: JUMP_IF_ZERO TETRA_JUMP_IF_ZERO
1667:macro TETRA_JUMP_IF_ZERO_MACRO { }
1668#:: JUMP TETRA_JUMP
1669:macro TETRA_JUMP_MACRO { }
1670#:: DUP TETRA_DUP
1671:macro TETRA_DUP_MACRO { }
1672#:: DROP TETRA_DROP
1673:macro TETRA_DROP_MACRO { }
1674
1675##/ build/tetra-code.o8 /##
1676#################
1677
1678:const TETRA_OCTOWORD_COUNT 17
1679:const TETRA_TETRAWORD_COUNT 2
1680#####
1681# Tetra Dictionary Jumptable [2018-09-30 18:04:19.183767]
1682###
1683: tetra_jumptable_lo
1684:const T4_DUP 0x00 jump TETRA_DUP
1685:const T4_+ 0x01 jump TETRA_ADD
1686:const T4_JUMP 0x02 jump TETRA_JUMP
1687:const T4_- 0x03 jump TETRA_SUB
1688:const T4_AND 0x04 jump TETRA_AND
1689:const T4_DROP 0x05 jump TETRA_DROP
1690:const T4_QUADRUPLE 0x06 jump tetra_call_bytecode
1691:const T4_JUMP_IF_ZERO 0x07 jump TETRA_JUMP_IF_ZERO
1692:const T4_XOR 0x08 jump TETRA_XOR
1693:const T4_NEGATE 0x09 jump TETRA_NEGATE
1694:const T4_= 0x0A jump TETRA_EQ
1695:const T4_* 0x0B jump TETRA_MUL
1696:const T4_PUSH_U16 0x0C jump TETRA_PUSH_U16
1697:const T4_OR 0x0D jump TETRA_OR
1698:const T4_> 0x0E jump TETRA_GT
1699:const T4_; 0x0F jump TETRA_RET
1700:const T4_DOUBLE 0x10 jump tetra_call_bytecode
1701:const T4_< 0x11 jump TETRA_LT
1702:const T4_/ 0x12 jump TETRA_DIV
1703
1704#####
1705# Tetra Built-in Macro Invocations [2018-09-30 18:04:19.183767]
1706###
1707: TETRA_DUP TETRA_DUP_MACRO
1708: TETRA_ADD TETRA_ADD_MACRO
1709: TETRA_JUMP TETRA_JUMP_MACRO
1710: TETRA_SUB TETRA_SUB_MACRO
1711: TETRA_AND TETRA_AND_MACRO
1712: TETRA_DROP TETRA_DROP_MACRO
1713: TETRA_JUMP_IF_ZERO TETRA_JUMP_IF_ZERO_MACRO
1714: TETRA_XOR TETRA_XOR_MACRO
1715: TETRA_NEGATE TETRA_NEGATE_MACRO
1716: TETRA_EQ TETRA_EQ_MACRO
1717: TETRA_MUL TETRA_MUL_MACRO
1718: TETRA_PUSH_U16 TETRA_PUSH_U16_MACRO
1719: TETRA_OR TETRA_OR_MACRO
1720: TETRA_GT TETRA_GT_MACRO
1721: TETRA_RET TETRA_RET_MACRO
1722: TETRA_LT TETRA_LT_MACRO
1723: TETRA_DIV TETRA_DIV_MACRO
1724#################
1725
1726
1727##/ tetra/tetra.o8 /##
1728######################
1729
1730#####
1731# Tetra Interpreter
1732###
1733# This is the core of the Tetra interpreter. The most basic primitives used by the
1734# Base Octo Words are declared here, as well as the main loop of the interpreter.
1735#
1736# Initialization is intended to be fast, so a new interpreter can be started for
1737# each entity that runs some code.
1738###
1739
1740:const TETRA_BUILTIN_MASK 0b00000111
1741:const TETRA_BUILTIN_FLAG 0b00000000
1742
1743:macro TETRA_NEXT { jump tetra_decode }
1744
1745# Tetra data is put on the same stack that is used in the rest of the program, but
1746# it employs a second stack, the Tetra Call Stack, to store call data for the
1747# interpreter. Currently, this stack is 256 bytes, but that may not be appropriate (too much?).
1748# TODO: Analyze stack usage
1749
1750:alias TETRA_SP VOL3 # Putting this in VOL is perhaps dangerous
1751:calc TETRA_CALLSTACK_SIZE { ( SEG_TETRA_CALLSTACK_END - SEG_TETRA_CALLSTACK_START ) + 1 }
1752:macro tetra_callstack_decr_sp bytes { stack_decr_sp TETRA_SP bytes }
1753:macro tetra_callstack_incr_sp bytes { stack_incr_sp TETRA_SP bytes }
1754: tetra_callstack_load_sp stack_load_sp TETRA_SP SEG_TETRA_CALLSTACK_START ;
1755:macro tetra_callstack_push firstreg lastreg bytes { stack_push TETRA_SP tetra_callstack_load_sp firstreg lastreg bytes }
1756:macro tetra_callstack_pop firstreg lastreg bytes { stack_pop TETRA_SP tetra_callstack_load_sp firstreg lastreg bytes }
1757: tetra-stack-init stack_init TETRA_SP ;
1758
1759
1760: tetra_init
1761 tetra-stack-init
1762 ;
1763
1764# Given a memory location, initialize the interpreter and execute the tetra
1765# code located there.
1766# PASS0/PASS1 = location of byte code
1767: tetra_exec
1768 # Initialize callstack
1769 tetra-stack-init
1770 # Set PASS0,1 to point at bytecode (already there right?)
1771 # Call decoder to process the bytecode
1772 tetra_decode
1773
1774#####
1775# tetra_decode
1776###
1777# This is the core of the Tetra interpreter. It 'decodes' one byte of bytecode
1778# by jumping into the jumptable. The jumptable will jump to the appropriate handler
1779# which will in turn jump back to tetra_decode.
1780#
1781# This function returns when the return (;) word is executed.
1782#
1783# In: PASS0 PASS1 = next Tetra byte-code
1784###
1785: tetra_decode
1786:alias tetra_decode_idx VOL1
1787 # Load a byte from the byte-code
1788 reg_load_i
1789 load tetra_decode_idx - tetra_decode_idx
1790
1791 # increment byte-code ptr
1792 vF := 1
1793 math_add_u16_u8 PASS0 PASS1 vF
1794
1795 # Jump into the jumptable
1796 v0 <<= tetra_decode_idx
1797 # if VF != 0 then jump0 tetra_jumptable_hi # Support 256 words
1798 jump0 tetra_jumptable_lo
1799
1800
1801: tetra_call_bytecode
1802 # Push current byte-code ptr on stack
1803 tetra_callstack_push PASS0 PASS1 2
1804
1805 # set PASS0/1 to new bytecode location
1806 # Subtract OctoWords from index
1807 :calc TETRA_BYTECODE_INDEX_OFFSET { ( 0 - ( TETRA_OCTOWORD_COUNT * 2 ) ) & 0xFF }
1808 VOL1 += TETRA_BYTECODE_INDEX_OFFSET
1809 i := long tetra_bytecode_index
1810 i += tetra_decode_idx
1811 i += tetra_decode_idx
1812 load PASS0 - PASS1
1813
1814 # execute the new bytecode
1815 tetra_decode
1816
1817 # When the bytecode returns, execution resumes here
1818 # Restore byte-code ptr
1819 tetra_callstack_pop PASS0 PASS1 2
1820
1821 # Continue decoding
1822 TETRA_NEXT
1823#/ build/text-code.o8 /# 2018-09-30 18:04:19.483787
1824
1825# Glyph Numbers
1826:const GLYPH_0 0x00
1827:const GLYPH_1 0x01
1828:const GLYPH_2 0x02
1829:const GLYPH_3 0x03
1830:const GLYPH_4 0x04
1831:const GLYPH_5 0x05
1832:const GLYPH_6 0x06
1833:const GLYPH_7 0x07
1834:const GLYPH_8 0x08
1835:const GLYPH_9 0x09
1836:const GLYPH_A 0x0A
1837:const GLYPH_B 0x0B
1838:const GLYPH_C 0x0C
1839:const GLYPH_D 0x0D
1840:const GLYPH_E 0x0E
1841:const GLYPH_F 0x0F
1842:const GLYPH_G 0x10
1843:const GLYPH_H 0x11
1844:const GLYPH_I 0x12
1845:const GLYPH_J 0x13
1846:const GLYPH_K 0x14
1847:const GLYPH_L 0x15
1848:const GLYPH_M 0x16
1849:const GLYPH_N 0x17
1850:const GLYPH_O 0x18
1851:const GLYPH_P 0x19
1852:const GLYPH_Q 0x1A
1853:const GLYPH_R 0x1B
1854:const GLYPH_S 0x1C
1855:const GLYPH_T 0x1D
1856:const GLYPH_U 0x1E
1857:const GLYPH_V 0x1F
1858:const GLYPH_W 0x20
1859:const GLYPH_X 0x21
1860:const GLYPH_Y 0x22
1861:const GLYPH_Z 0x23
1862:const GLYPH_a 0x24
1863:const GLYPH_b 0x25
1864:const GLYPH_c 0x26
1865:const GLYPH_d 0x27
1866:const GLYPH_e 0x28
1867:const GLYPH_f 0x29
1868:const GLYPH_g 0x2A
1869:const GLYPH_h 0x2B
1870:const GLYPH_i 0x2C
1871:const GLYPH_j 0x2D
1872:const GLYPH_k 0x2E
1873:const GLYPH_l 0x2F
1874:const GLYPH_m 0x30
1875:const GLYPH_n 0x31
1876:const GLYPH_o 0x32
1877:const GLYPH_p 0x33
1878:const GLYPH_q 0x34
1879:const GLYPH_r 0x35
1880:const GLYPH_s 0x36
1881:const GLYPH_t 0x37
1882:const GLYPH_u 0x38
1883:const GLYPH_v 0x39
1884:const GLYPH_w 0x3A
1885:const GLYPH_x 0x3B
1886:const GLYPH_y 0x3C
1887:const GLYPH_z 0x3D
1888:const GLYPH_67 0x3E
1889:const GLYPH_CC 0x3F
1890:const GLYPH_ES 0x40
1891:const GLYPH_! 0x41
1892:const GLYPH_. 0x42
1893
1894
1895:macro glyph-index idx-reg {
1896 i := long glyph-table
1897 i += idx-reg
1898 i += idx-reg
1899 i += idx-reg
1900 i += idx-reg
1901 i += idx-reg
1902 i += idx-reg
1903 i += idx-reg
1904}
1905
1906##/ graphics/header.o8 /##
1907##########################
1908
1909# 4B: Layer *Next/*Prev
1910# 4B: Needed *Next/*Prev
1911# 2B: *Buffer
1912# 2B: *TileBuffer
1913# 2B: X, Y
1914# 1B: Rows
1915#
1916
1917:const DisplayObject_Bytes 15
1918:const DisplayObject_NeededNext_Offset 4
1919:const DisplayObject_NeededPrev_Offset 6
1920:const DisplayObject_BufferPtr_Offset 8
1921:const DisplayObject_X_Offset 12
1922:const DisplayObject_Y_Offset 13
1923
1924:const DisplayObject_TileBufferPtr_Offset 10
1925##/ graphics/tile.o8 /##
1926########################
1927
1928#####
1929# Tiles
1930###
1931# Tiles are 16x16 images designed to be sprited to screen quickly using the
1932# SuperChip 16x16 sprite op. The Display abstraction is based on the DisplayTile
1933# which has 2 planes of image data. The TileSet abstraction is based on the MaskedTile
1934# which also includes a mask plane.
1935#
1936# TilePlane:
1937# A 1-bit 16x16 square of pixels. Tiles contain 2 or 3 planes.
1938#
1939# DisplayTile:
1940# A 4-color 16x16 image with two TilePlanes:
1941# Plane 0, 32B
1942# Plane 1, 23B
1943#
1944# MaskedTiled:
1945# A 4-color 16x16 image with mask, represent as three TilePlanes:
1946# Mask, 32B
1947# Plane 0, 32B
1948# Plane 1, 32B
1949###
1950
1951
1952# TilePlane:
1953# 32 bytes plane data, ( 2 bytes per row, 16 rows)
1954:const Tile_Height 16
1955:const Tile_Width 16
1956:calc TilePlane_Bytes { Tile_Height * ( Tile_Width / 8 ) }
1957
1958# DisplayTile:
1959# Plane0 TilePlane (32 B)
1960# Plane1 TilePlane (32 B)
1961:calc DisplayTile_Bytes { TilePlane_Bytes * 2 }
1962:calc DisplayTile_Plane0_Offset { 0 }
1963:calc DisplayTile_Plane1_Offset { TilePlane_Bytes }
1964
1965
1966# MaskedTile
1967# Mask TilePlane (32 B)
1968# Plane0 TilePlane (32 B)
1969# Plane1 TilePlane (32 B)
1970# TODO: Remove, the concept of a masked tile is going away
1971:calc MaskedTile_Bytes { TilePlane_Bytes * 3 }
1972:calc MaskedTile_Mask_Offset { 0 }
1973:calc MaskedTile_Plane0_Offset { TilePlane_Bytes }
1974:calc MaskedTile_Plane1_Offset { TilePlane_Bytes * 2 }
1975
1976
1977
1978# Given a number of pixels, returns how many tiles would be needed
1979:macro Tile-pxtotiles reg-in reg-out {
1980 reg-out := reg-in
1981 reg-out += 15
1982 reg-out >>= reg-out
1983 reg-out >>= reg-out
1984 reg-out >>= reg-out
1985 reg-out >>= reg-out
1986}
1987
1988# Given a number of tiles, returns how many pixels there are
1989:macro Tile-tiletopx reg-in reg-out {
1990 reg-out <<= reg-in
1991 reg-out <<= reg-out
1992 reg-out <<= reg-out
1993 reg-out <<= reg-out
1994}
1995##/ graphics/tilebuffer.o8 /##
1996##############################
1997
1998#####
1999# `TileBuffer`, `TileSlice`, and `TileRow`
2000###
2001# A `TileBuffer` holds a 2D pixel buffer that always has a width that is a multiple
2002# of 16 pixels. It can be any height. It has a mask plane and 2 color planes.
2003#
2004# `TileBuffer`s hold an array of `TileSlice`s, which contain an array of `TileRow`s.
2005#
2006# When a `Buffer` needs to be composited, it is automatically converted into a
2007# `TileBuffer` that is much faster to composite. The data in a `TileBuffer` is
2008# offset horizontally compared to the original `Buffer` so it does not need to be
2009# shifted during compositing.
2010#
2011### TileRow
2012# The data in a `TileRow` (called that because it is one row of a 16x16 `Tile`) is
2013# 6 bytes representing 16 pixels:
2014# 2 Mask Bytes, 2 Plane0 Bytes, and 2 Plane2 Bytes [MM0011]
2015#
2016### TileSlice
2017# Each full row of pixels in the `TileBuffer` is a `TileSlice`:
2018# Byte 0: n Tiles/TileRows wide
2019# n `TileRow`s (n*6 bytes)
2020# Contents: [nMM0011MM0011MM0011...]
2021#
2022### TileBuffer
2023# One `TileSlice` per row is held in the `TileBuffer`:
2024# Byte 0: n rows tall
2025# Byte 1: n bytes per row
2026# n `TileSlices`s
2027###
2028
2029# `TileRow`
2030:const TileRow_Plane_Bytes 2
2031:calc TileRow_Plane1_Bytes { TileRow_Plane_Bytes * 2 }
2032
2033:const TileRow_Planes 3
2034:calc TileRow_Bytes { TileRow_Plane_Bytes * TileRow_Planes }
2035
2036# `TileSlice`
2037:const TileSlice_Header_TileWidth 0
2038:const TileSlice_Header_RowBytes 1
2039:const TileSlice_Header_Bytes 2
2040
2041# `TileBuffer`
2042:const TileBuffer_Header_Bytes 7
2043
2044:const TileBuffer_CachedTileListNext_Offset 0
2045:const TileBuffer_CachedTileListPrev_Offset 2
2046:const TileBuffer_RefCount_Offset 4
2047:const TileBuffer_BufferCachePtr_Offset 5
2048:const TileBuffer_TileData_Offset 7
2049
2050:macro TileSliceBytes reg-tiles-in reg-bytes-out {
2051 math_mul6_u8 reg-tiles-in reg-bytes-out # Multiply by 6
2052 reg-bytes-out += TileSlice_Header_Bytes # Add header
2053}
2054
2055
2056# In: PASS0/1: Dimensions (x, y) in pixels
2057# Out: Ptr to allocated TileBuffer
2058# Clobbers VOL0-3
2059: TileBuffer_New
2060 #:breakpoint TileBuffer_New
2061
2062 Tile-pxtotiles PASS0 VOL0
2063 #:breakpoint vol0=tiles
2064 TileSliceBytes VOL0 VOL1
2065 #:breakpoint vol1=slicebytes
2066
2067 VOL2 := PASS1
2068 VOL3 := PASS1
2069 reg_set_u16 PASS0 PASS1 0 TileBuffer_Header_Bytes # Clear byte count to just header
2070 #:breakpoint preloop
2071 loop while VOL2 != 0
2072 math_add_u16_u8 PASS0 PASS1 VOL1 # Add the size of a slice for each row
2073 VOL2 += -1
2074 again
2075
2076 push VOL0 VOL3 4
2077 # PASS0/1 = bytes
2078 #:breakpoint premalloc
2079 malloc
2080 #:breakpoint postmalloc
2081
2082 pop VOL0 VOL3 4
2083 # PASS0/1 = addr
2084 reg_load_i_PASS01
2085 save VOL3 - VOL3
2086 vf := 1 # Save Height in px (rows)
2087 i += vf
2088 VOL2 := VOL3
2089 loop while VOL2 != 0
2090 save VOL0 - VOL1 # Save tile width and byte width
2091 i += VOL1 # inc i by row bytes
2092 VOL2 += -1
2093 again
2094 #:breakpoint done
2095 # Address is returned in PASS0/1
2096 ;
2097##/ display.o8 /##
2098##################
2099
2100######
2101# Display Abstraction Layer
2102###
2103# Maintains a display-sized buffer of 16x16 tiles, arranged in memory so that
2104# a 4-color tile can be copied to the display with a single sprite instruction.
2105#
2106# The general use is:
2107# Create a buffer
2108# Make changes to the buffer
2109# Clear the screen and sprite the entire buffer
2110# Erase a tile
2111# Change a portion or all of that tile
2112# Redisplay the tile, restoring the display
2113#
2114###
2115# API
2116###
2117# DisplayNew(color) -> *Display
2118# DisplayWipe(*Display, Pattern) -> *Display
2119# DisplayBlt(*Display, *Buffer, SrcRect, DestRect, op) -> *Display
2120# DisplayRedraw(*Display) -> *Display
2121# DisplayEraseTile(*Display, TileId) -> *Display
2122# DisplayDrawTile(*Display, TileId) -> *Display
2123#
2124#
2125
2126# The value used for empty areas of image and mask data
2127:const DISPLAY_EMPTY_MASK 0xFF
2128:const DISPLAY_EMPTY_COLOR 0x00
2129
2130
2131: DisplayInit
2132 hires
2133 plane 3 # Always stay in plane 3, any time it is changed away it needs to be changed back
2134 ;
2135
2136:const Display_Width 128
2137:const Display_Height 64
2138:calc Display_NumTiles { ( Display_Width / Tile_Width ) * ( Display_Height / Tile_Height ) }
2139
2140
2141# 8B: LayerList *Head/*Tail
2142# 8B: CachedTilesList *Head/*Tail
2143# 8B: NeededObjectList *Head/*Tail
2144# 32B: TileDirtyFlags
2145# 256B: *PerTileLayerArray[2B*4*8*4]
2146# 2kB: Tile Image Data
2147# k = 8*4*16*16/8*2 = 2048
2148:calc Display_Header_Bytes { 24 }
2149:calc Display_DirtyFlags_Bytes { 32 }
2150:calc Display_TileLayerArray_Bytes { 2 * 4 * 8 * 4 }
2151:calc Display_Tile_Bytes { DisplayTile_Bytes * Display_NumTiles }
2152:calc Display_Bytes { Display_Header_Bytes + Display_DirtyFlags_Bytes + Display_TileLayerArray_Bytes + Display_Tile_Bytes }
2153
2154:const Display_LayerListHead_Offset 0
2155:const Display_LayerListTail_Offset 4
2156:const Display_CachedTilesListHead_Offset 8
2157:const Display_CachedTilesListTail_Offset 12
2158:const Display_NeededTilesListHead_Offset 16
2159:const Display_NeededTilesListTail_Offset 20
2160:calc Display_TileData_Offset { Display_Header_Bytes + Display_DirtyFlags_Bytes + Display_Tile_Bytes }
2161
2162
2163:calc Display_TileDirtyFlags_Offset { Display_Header_Bytes }
2164
2165:calc Display_TileLayerArray_Offset { Display_Header_Bytes + Display_DirtyFlags_Bytes }
2166
2167# DisplayNew(Pattern) -> *Display
2168# In:
2169# PASS0/1 = Pattern
2170# Out:
2171# PASS0/1 = Pointer to new Display
2172: DisplayNew
2173 #push NONVOL0 NONVOL1 2
2174 #reg_set_u16 NONVOL0 NONVOL1 PASS0 PASS1 # Copy pattern
2175 hilo Display_Bytes
2176 reg_set_u16 PASS0 PASS1 hi lo
2177 malloc # Returns *Display in PASS0/1
2178 :breakpoint display_new_malloced
2179 assert-neq0-u16 PASS0 PASS1 ASSERT-FAIL:_DisplayNew_malloc
2180 reg_load_i_PASS01
2181 reg_set_u16 VOL0 VOL1 PASS0 PASS1
2182 math_add_u16_u8imm VOL0 VOL1 Display_LayerListTail_Offset
2183 VOL2 := 0
2184 VOL3 := 0
2185 save VOL3 # Save LayerList Head/Tail
2186 VOL0 := 0
2187 VOL1 := 0
2188 reg_set_u16 VOL2 VOL3 PASS0 PASS1
2189 #math_add_u16_u8imm VOL2 VOL3 Display_LayerListHead_Offset
2190 save VOL3
2191
2192 reg_load_i_PASS01
2193 reg_set_u16 VOL0 VOL1 PASS0 PASS1
2194 math_add_u16_u8imm VOL0 VOL1 Display_CachedTilesListTail_Offset
2195 VOL2 := 0
2196 VOL3 := 0
2197 SCRATCH := Display_CachedTilesListHead_Offset
2198 i += SCRATCH
2199 save VOL3
2200 VOL0 := 0
2201 VOL1 := 0
2202 reg_set_u16 VOL2 VOL3 PASS0 PASS1
2203 math_add_u16_u8imm VOL2 VOL3 Display_CachedTilesListHead_Offset
2204 save VOL3 # Save CachedTilesTile Head/Tail
2205
2206 # TODO: Setup Cached list
2207 reg_load_i_PASS01
2208 reg_set_u16 VOL0 VOL1 PASS0 PASS1
2209 math_add_u16_u8imm VOL0 VOL1 Display_NeededTilesListTail_Offset
2210 VOL2 := 0
2211 VOL3 := 0
2212 SCRATCH := Display_NeededTilesListHead_Offset
2213 i += SCRATCH
2214 save VOL3
2215 VOL0 := 0
2216 VOL1 := 0
2217 reg_set_u16 VOL2 VOL3 PASS0 PASS1
2218 math_add_u16_u8imm VOL2 VOL3 Display_NeededTilesListHead_Offset
2219 save VOL3 # Save NeededTilesTile Head/Tail
2220
2221
2222 VOL2 := 0
2223 VOL3 := 0
2224
2225 # Save 256 bytes of 0s for tile layer array
2226 # + 32 for tile dirty flags
2227 SCRATCH := 18
2228 loop while SCRATCH != 0
2229 save VOL3
2230 save VOL3
2231 save VOL3
2232 save VOL3
2233 SCRATCH += -1
2234 again
2235
2236 #reg_set_u16 PASS2 PASS3 NONVOL0 NONVOL1
2237 #DisplayWipe # PASS0/1 = *Display, PASS2/3=Pattern
2238 #pop NONVOL0 NONVOL1 2
2239 ;
2240
2241# DisplayNew(Pattern) -> *Display
2242# In:
2243# PASS0/1 = Pointer to Display
2244# PASS2/3 = Pattern
2245# Out:
2246# PASS0/1 = Pointer to Display
2247: DisplayWipe
2248 push PASS0 PASS1 2
2249 math_add_u16_u16imm PASS0 PASS1 Display_TileData_Offset
2250 reg_load_i # i = PASS0/1 = *Display
2251 SCRATCH += Display_Header_Bytes
2252 i += SCRATCH
2253 SCRATCH := 0 # We can use vF as long as we are careful to
2254 : display-wipe-again # not use any instructions that modify vF
2255 VOL0 := PASS2
2256 display-clear-halftile
2257 VOL0 := PASS3
2258 display-clear-halftile
2259 SCRATCH += 1
2260 if SCRATCH != Display_NumTiles then jump display-wipe-again
2261 pop PASS0 PASS1 2
2262
2263 ;
2264
2265# Internal function to clear one plane of a tile, copies VOL0 to 32 bytes at i, indexing i to i +32
2266: display-clear-halftile
2267 VOL1 := VOL0 VOL2 := VOL0 VOL3 := VOL0 VOL4 := VOL0 VOL5 := VOL0
2268 save VOL5 save VOL5 save VOL5 save VOL5 save VOL5 save VOL1
2269 ;
2270
2271# DisplayRedraw(*Display) -> *Display
2272# In:
2273# PASS0/1 = Pointer to Display
2274# Out:
2275# PASS0/1 = Pointer to Display
2276: DisplayRedraw
2277 reg_load_i # i = PASS0/1 = *Display
2278
2279 clear
2280 VOL0 := 0
2281 VOL1 := 0
2282
2283 VOL3 := DisplayTile_Bytes
2284 : display-redraw-again
2285 sprite VOL0 VOL1 0
2286 VOL0 += 16
2287 i += VOL3
2288 if VOL0 != Display_Width then jump display-redraw-again
2289 VOL0 := 0
2290 VOL1 += 16
2291 if VOL1 != Display_Height then jump display-redraw-again
2292 ;
2293
2294## DisplayBlt(*Display, *Buffer, SrcRect, DestRect, op) -> *Display
2295## In:
2296## PASS0/1 = Pointer to Display
2297## PASS2/3 = Pointer to Source Buffer
2298## Stack:
2299## op: 1-byte operation code, op could determine what happens if we run out of source, empty, loop, mirror, invert etc
2300## SrcCorner: Xs, Ys - Offset from the top-left corner of the src to begin read
2301## DestCorner: Xd, Yd - Offset from top-left corner of dest to begin write
2302## Dimensions: W, H - Size of the rectangle to be copied
2303## (Top of stack)
2304## Out:
2305## PASS0/1 = Pointer to Display
2306#: DisplayBltTile
2307# ;
2308
2309# This function copies the entirety of a tile-aligned buffer to a tile
2310# In:
2311# PASS0/1 = Pointer to Display Tile
2312# PASS2/3 = Pointer to Source BufferTile
2313# Stack: op <-- TODO: For now, ignores op, copies with alpha
2314: DisplayBltTileBuffer
2315:breakpoint blttilebuffer
2316 reg_load_i_PASS23
2317 load VOL5 - VOL5 # height in px
2318 math_add_u16_u8imm PASS2 PASS3 TileBuffer_Header_Bytes # Advance past header
2319 ## Loop over rows
2320 loop while VOL5 != 0
2321 push PASS0 PASS1 2
2322 DisplayBltTileSlice # PASS0/1 = Display, PASS2/3 = Source
2323 pop PASS0 PASS1 2
2324 math_add_u16_u8imm PASS0 PASS1 2 # next row of same tile
2325 VOL5 += -1
2326 again
2327;
2328
2329# In PASS0/1 = pointer to display tile
2330# PASS2/3 = pointer to source slice
2331: DisplayBltTileSlice
2332 #:breakpoint slice
2333 reg_load_i_PASS23
2334 load VOL4 - VOL4 # width in tiles
2335 #:breakpoint loadwidth
2336 math_add_u16_u8imm PASS2 PASS3 TileSlice_Header_Bytes
2337 loop while VOL4 != 0
2338 #:breakpoint pertile
2339 push PASS0 PASS1 2
2340 DisplayBltTileRow
2341 pop PASS0 PASS1 2
2342 math_add_u16_u8imm PASS0 PASS1 DisplayTile_Bytes
2343 VOL4 += -1
2344 again
2345 ;
2346
2347: DisplayBltTileRow
2348 push VOL0 NONVOL1 8
2349 reg_load_i_PASS23
2350 load VOL0 - VOL5 # load mask, p0, p1
2351 #:breakpoint loadedsrc
2352 math_add_u16_u8imm PASS2 PASS3 4
2353
2354 reg_load_i_PASS01
2355 load NONVOL0 - NONVOL1 # load dest data
2356 #:breakpoint loadeddest0
2357 reg_and_u16 NONVOL0 NONVOL1 VOL0 VOL1 # and with mask
2358 reg_or_u16 NONVOL0 NONVOL1 VOL2 VOL3 # or p0
2359 save NONVOL0 - NONVOL1
2360 #:breakpoint savedest0
2361
2362 math_add_u16_u8imm PASS0 PASS1 TilePlane_Bytes
2363
2364 reg_load_i_PASS01
2365 load NONVOL0 - NONVOL1 # load dest data
2366 #:breakpoint loadeddest1
2367 reg_and_u16 NONVOL0 NONVOL1 VOL0 VOL1 # and with mask
2368 reg_or_u16 NONVOL0 NONVOL1 VOL4 VOL5 # or p1
2369 save NONVOL0 - NONVOL1
2370 #:breakpoint savedest1
2371
2372 math_add_u16_u8imm PASS2 PASS3 2
2373
2374 pop VOL0 NONVOL1 8
2375
2376 ;
2377
2378
2379# TODO: Make a graphics header file so this isn't repeated
2380:const _DisplayObject_TileBufferPtr_Offset 10
2381
2382# Add an object to the layer list
2383# In:
2384# Pass 01: *Display
2385# Pass 23: *Object
2386# Out:
2387#
2388: Display_addObjectFront
2389 push PASS0 PASS3 4
2390 ListInsert # PASS01 = *Display[LayerList]
2391 stackpeek PASS0 PASS3 4
2392 reg_load_i_PASS23
2393 mem_load_offset _DisplayObject_TileBufferPtr_Offset VOL0 VOL1
2394 SCRATCH := VOL0
2395 SCRATCH |= VOL1
2396 if SCRATCH != 0 then jump Display_addObjectFront_hasTileBuffer
2397 # If *TileBuffer = 0, add this object to the needed list
2398 :calc _4bytes_Display_NeededTilesListHead_Offset { Display_NeededTilesListHead_Offset - 4 }
2399 math_add_u16_u8imm PASS0 PASS1 _4bytes_Display_NeededTilesListHead_Offset
2400 ListInsert_4ByteOffset
2401 # increment the dirty list by 1
2402 pop PASS0 PASS3 4
2403
2404 : Display_addObjectFront_hasTileBuffer
2405 ;
2406
2407: Display_addObjectRear
2408 reg_load_i_PASS01
2409 :calc NeededListHead_4ByteOffset { Display_NeededTilesListHead_Offset - 4 }
2410 mem_load_offset NeededListHead_4ByteOffset PASS0 PASS1
2411 ListInsert_4ByteOffset # Insert in Needed list, which is 4-bytes into the display object
2412 ;
2413
2414# TODO: Insert an object in the middle of the layer list
2415: Display_InsertObject
2416 ;
2417
2418# Scans layer list, places pointers in the per-tile list
2419# In:
2420# PASS01: Layer List Head
2421# Out:
2422#
2423: Display_calcLayers
2424 # Walk layer list,
2425 loop
2426 reg_load_i_PASS01
2427 load PASS0 - PASS1
2428 SCRATCH := PASS0
2429 SCRATCH |= PASS1
2430 while SCRATCH != 0
2431 # Determine which tiles layer is in,
2432 # Add pointer to tilebuffer & rows to per-tile list for each
2433 again
2434 ;
2435
2436# Creates a TileBuffer from the Needed Buffers List
2437# Removes the buffer from the NeededBufferslist
2438# In:
2439# PASS01: *Display
2440#
2441: Display_createNeededBuffers
2442 ## Is there an item at the tail of the list?
2443 reg_load_i_PASS01
2444 mem_load_offset Display_NeededTilesListTail_Offset PASS2 PASS3
2445 reg_load_i_PASS23
2446 reg_set_u16 VOL4 VOL5 PASS2 PASS3
2447 mem_load_offset DisplayObject_NeededPrev_Offset PASS2 PASS3
2448 SCRATCH := PASS2
2449 SCRATCH |= PASS3
2450 if SCRATCH == 0 then return # No buffers in list
2451 :breakpoint createneeded
2452 ## Unlink Buffer from tail of Needed list
2453 reg_load_i_PASS23
2454 math_add_u16_u8imm PASS0 PASS1 _4bytes_Display_NeededTilesListHead_Offset
2455 mem_save_offset DisplayObject_NeededNext_Offset PASS0 PASS1
2456 reg_load_i_PASS01
2457 mem_save_offset DisplayObject_NeededNext_Offset PASS2 PASS3
2458
2459 reg_set_u16 PASS0 PASS1 VOL4 VOL5 # PASS01 = *DisplayObject
2460
2461 ## Check buffer for a cache
2462 :breakpoint DO-acquire
2463 DisplayObject_acquireTileBufferPtr # Get a buffer if possible
2464 # PASS2/3 = *TileBuffer if it exists
2465 SCRATCH := PASS2
2466 SCRATCH |= PASS3
2467 if SCRATCH != 0 then jump Display_createNeededBuffers_cache_exists
2468 ## if no tilebuffer, create a buffer
2469 ## PASS2 = X shift
2470 PASS2 := 0 # TODO: use correct shift amount
2471 :breakpoint totilebuffer
2472 Buffer-toTileBuffer # Returns *TileBuffer
2473
2474
2475 : Display_createNeededBuffers_cache_exists
2476 ## link the buffer to display obj and cache, increment refcount
2477
2478 # ...
2479
2480
2481 ;
2482
2483# Composites a Dirty Tile (or more?)
2484# If the tile dirty flag is 0, none of the layers need updates
2485# Set dirty flag to 0x80, which means 'clean' and prevents updates
2486# In:
2487# PASS01: *Display
2488: Display_compositeDirtyTiles
2489 reg_load_i_PASS01
2490 SCRATCH := Display_TileDirtyFlags_Offset
2491 i += SCRATCH
2492 # Find first tile with dirty flag == 0
2493 VOL2 := 0
2494 loop
2495 while VOL2 != Display_NumTiles
2496 load VOL0
2497 while VOL0 != 0
2498 VOL2 += 1
2499 again
2500
2501 if VOL2 == Display_NumTiles then return # Bail if nothing to composite
2502
2503 # VOL2 = tile number of dirty tile
2504 reg_load_i_PASS01
2505 SCRATCH := Display_TileDirtyFlags_Offset
2506 i += SCRATCH
2507 i += VOL2
2508 VOL0 := 0x80
2509 save VOL0 # Set dirty flag to 0x80
2510 # Composite the tile
2511
2512 ;
2513
2514# Replace
2515# In:
2516# PASS01: *Display
2517# PASS2: Tile number
2518: Display_compositeTile
2519 ;
2520##/ graphics/buffer/buffer.o8 /##
2521#################################
2522
2523#####
2524# Buffers
2525###
2526# A buffer is a contiguous rectangle of pixels. They have 2 planes of color
2527# data and 1 plane of alpha data.
2528#
2529# To be composited onto a `Display`, it must be first converted to a `TileSet`.
2530#
2531###
2532
2533# Buffer:
2534# 2B: *BufferCache
2535# 2B: Width, Height
2536# nB: Mask Data, n=W*H/8
2537# nB: Plane0 Data, n=W*H/8
2538# nB: Plane1 Data, n=W*H/8
2539:const Buffer_Header_Bytes 4
2540:const Buffer_Height_Offset 3
2541# mask data * (ceil(width * height / 8)
2542# plane 0 data * (ceil(width * height / 8)
2543# plane 1 data * (ceil(width * height / 8)
2544#
2545#
2546#
2547#####
2548# API
2549###
2550#
2551# BufferNew(x,y)
2552###
2553
2554# i = buffer
2555:macro Buffer_loadHeight reg-out {
2556 SCRATCH := Buffer_Height_Offset
2557 i += SCRATCH
2558 load reg-out - reg-out
2559}
2560
2561# Given a buffer in PASS01, return the height and width in PASS01
2562: Buffer_getHeightWidth
2563 reg_load_i_PASS01
2564 load PASS0 - PASS1
2565 ;
2566
2567
2568 ##/ graphics/cache.o8 /##
2569 #########################
2570
2571
2572 #####
2573 # Tile Cache
2574 ###
2575 # Buffers need to be converted to TileSets before they can be composited or
2576 # displayed. Since that is slow and may need to be performed for several
2577 # alignments, we will cache the TileSets. These will be tracked and when culling
2578 # is needed due to memory pressue, the stalest entries are dropped.
2579 #
2580 # When a cache item is dropped, a reverse link is followed so the cache entry
2581 # isn't left dangling in the Buffer def.
2582
2583
2584 # Each Buffer has a pointer to an associate cached, if one exists.
2585 # When a tile is needed, check for null, if null, create cache.
2586 # Load the cache, check if shift pointer is null, if null, create tilebuffer
2587 # Load tilebuffer and use
2588
2589:calc BufferCache_TileBufferPtrArray_Offset { 3 }
2590##/ graphics/displayobject.o8 /##
2591#################################
2592
2593
2594# A `DisplayObject` is a unique image on a `Display`. It is part of the Display's
2595# Layer List, and has Next/Prev pointers for this list. It has the position of
2596# the object's topleft corner, and a pointer to the backing `TileBuffer`.
2597
2598
2599# In:
2600# PASS0/1: Backing *Buffer
2601# PASS2/3: x, y in pixels
2602# Out:
2603# PASS0/1: *DisplayObject
2604: DisplayObject_New
2605 reg_load_i_PASS01
2606 Buffer_loadHeight VOL4
2607 push PASS0 PASS3 4 # Save parameters to store in the DisplayObject header
2608 push VOL4 VOL4 1
2609 malloc_u16imm DisplayObject_Bytes
2610 :breakpoint malloc-DO
2611 reg_load_i_PASS01
2612
2613 VOL0 := 0
2614 VOL1 := 0
2615 VOL2 := 0
2616 VOL3 := 0
2617 save VOL3 save VOL3 # next, prev = null * 2
2618 pop VOL0 VOL4 5
2619 reg_load_i_PASS01
2620 SCRATCH := DisplayObject_BufferPtr_Offset
2621 i += SCRATCH
2622 save VOL1 # save *buffer
2623 VOL0 := 0
2624 VOL1 := 0
2625 save VOL1 # *TileBuffer
2626 save VOL2 - VOL4 # Save x, y, rows
2627 DisplayObject_acquireTileBufferPtr # Get the cached tilebuffer if it exists
2628 ;
2629
2630
2631
2632# If a cached tilebuffer exists, set the tilebuffer ptr to it, and increase the refcount
2633# Else, 0 the tilebufferptr
2634# In:
2635# PASS01: *DisplayObject
2636# Out:
2637# PASS01: *DisplayObject
2638# PASS23: *TileBuffer
2639: DisplayObject_acquireTileBufferPtr
2640 reg_load_i_PASS01
2641 mem_load_offset DisplayObject_X_Offset VOL0 VOL0
2642 reg_load_i_PASS01
2643 mem_load_offset DisplayObject_BufferPtr_Offset PASS2 PASS3
2644 reg_load_i_PASS23
2645 load PASS2 - PASS3 # BufferCache is first bytes of Buffer
2646 SCRATCH := PASS2
2647 SCRATCH |= PASS3
2648 if SCRATCH == 0 then jump DisplayObject_acquireTileBufferPtr-nobuffercache
2649 # There is a buffercache
2650 reg_load_i_PASS23
2651 VOL0 <<= VOL0
2652 i += VOL0
2653 mem_load_offset BufferCache_TileBufferPtrArray_Offset PASS2 PASS3
2654 SCRATCH := PASS2
2655 SCRATCH |= PASS3
2656 if SCRATCH == 0 then jump DisplayObject_acquireTileBufferPtr-nobuffercache
2657 reg_load_i_PASS23
2658 mem_load_offset TileBuffer_RefCount_Offset VOL0 VOL0
2659 VOL0 += 1
2660 save VOL0 - VOL0 # save incremented refcount
2661 reg_load_i_PASS01
2662 mem_save_offset DisplayObject_TileBufferPtr_Offset PASS2 PASS3
2663 ;
2664
2665 : DisplayObject_acquireTileBufferPtr-nobuffercache
2666 reg_load_i_PASS01
2667 PASS2 := 0
2668 PASS3 := 0
2669 mem_save_offset DisplayObject_TileBufferPtr_Offset PASS2 PASS3
2670
2671 ;
2672
2673# If *tilebuffer nonzero, decrement the refcount and 0 the ptr
2674: DisplayObject_releaseTileBufferPtr
2675 ;
2676##/ graphics/bufferaligner.o8 /##
2677##################################
2678
2679
2680# Algorithm
2681#
2682# Given address of Buffer in PASS0/1
2683#
2684#
2685## Calculate sizes
2686# required-pixels = Width + Offset
2687# tile-width = required-pixels.totiles
2688# pixel-width = tile-width.topixels
2689# last-pixels = pixel-width - required-pixels
2690#
2691## Allocate
2692# Allocate a new TileBuffer, tile-width tiles x height rows
2693#
2694## Determine copy parameters:
2695# Body:
2696# Body tiles = tile-width (FIXME: ??)
2697# Prelude:
2698# If shift = 0, no prelude
2699# Else, if shift 1 - 7, prelude is 2 shifted bytes, (body-tiles -= 1 ?)
2700# Else, if shift = 8, prelude is one 0, and one aligned byte
2701# Else, if shift 9 - 15, prelude is a 0 and one shifed byte
2702# Epilogue:
2703# If last-pixels = 0, no epilogue
2704# Else if last-pixel 1 - 7, epilogue is 1 shifted bytes and a 0
2705#
2706# Do setup of loop
2707#
2708## The Loop
2709# For each row of the buffer:
2710# output prelude
2711# output body
2712# output epilogue
2713#
2714## Return address of TileBuffer in PASS0/1
2715
2716## Macros to make things easier to read in the copy kernels
2717:macro Bufferaligner_loadi_src { reg_load_i_PASS23 }
2718:macro Bufferaligner_loadi_dest { reg_load_i_PASS01 }
2719:macro Bufferaligner_inc_src n-imm { math_add_u16_u8imm PASS2 PASS3 n-imm }
2720:macro Bufferaligner_inc_dest n-imm { math_add_u16_u8imm PASS0 PASS1 n-imm }
2721
2722:macro Bufferaligner_load_and_inc_src reg-hi reg-lo bytes { Bufferaligner_loadi_src load reg-hi - reg-lo Bufferaligner_inc_src bytes }
2723:macro Bufferaligner_save_and_inc_dest reg-hi reg-lo bytes { Bufferaligner_loadi_dest save reg-hi - reg-lo Bufferaligner_inc_dest bytes }
2724
2725:alias Bufferaligner_ROW VOL4
2726:alias Bufferaligner_COL VOL5
2727
2728
2729#####
2730# Copy Kernels
2731###
2732# These copy bytes from the source to dest
2733#
2734# In:
2735# PASS0/1 = dest ptr
2736# PASS2/3 = source ptr
2737# NONVOL0 = default value (used for empty pixels)
2738# NONVOL1 = shift amount
2739# NONVOL2 = carry byte
2740# Out:
2741# PASS0/1 = source ptr + consumed bytes
2742# PASS2/3 = dest ptr + TileRows filled
2743
2744
2745# Shifts VOL0 and VOL1 right NONVOL1 times,
2746# then ORs with NONVOL2
2747# Extra bits end up in NONVOL2
2748: Bufferaligner_copyshift
2749 VOL2 := 0
2750 VOL3 := NONVOL1
2751 loop while VOL3 != 0
2752 VOL2 >>= VOL2
2753 VOL1 >>= VOL1
2754 if vF != 0 then VOL2 += 0x08
2755 VOL0 >>= VOL0
2756 if vF != 0 then VOL1 += 0x80
2757
2758 NONVOL2 >>= NONVOL2
2759 if vf != 0 then
2760 VOL0 += 0x80
2761 VOL3 += -1
2762 again
2763 #VOL0 |= NONVOL2
2764 NONVOL2 := VOL2
2765 ;
2766
2767: Bufferaligner_copyaligned
2768 #:breakpoint Bufferaligner_copyaligned
2769 Bufferaligner_load_and_inc_src VOL0 VOL1 2 # Load and advance src
2770 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save and advance dest
2771 ;
2772
2773: Bufferaligner_copyaligned_leadingbyte
2774 #:breakpoint Bufferaligner_copyaligned_leadingbyte
2775 Bufferaligner_load_and_inc_src VOL1 VOL1 1 # Load and advance src
2776 VOL0 := NONVOL0
2777 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save 0x00 and src to dest
2778 ;
2779
2780: Bufferaligner_copyaligned_trailingbyte
2781 #:breakpoint Bufferaligner_copyaligned_trailingbyte
2782 Bufferaligner_load_and_inc_src VOL0 VOL0 1 # Load and advance src
2783 VOL1 := NONVOL0
2784 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save 0x00 and src to dest
2785 ;
2786
2787: Bufferaligner_copyshifted
2788 #:breakpoint Bufferaligner_copyshifted
2789 Bufferaligner_load_and_inc_src VOL0 VOL1 2 # Load and advance src
2790 Bufferaligner_copyshift
2791 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save and advance dest
2792 ;
2793
2794: Bufferaligner_copyshifted_leadingbits
2795 #:breakpoint Bufferaligner_copyshifted_leadingbits
2796 NONVOL2 := NONVOL0
2797 Bufferaligner_load_and_inc_src VOL0 VOL1 2 # Load and advance src
2798 Bufferaligner_copyshift
2799 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save and advance dest
2800 ;
2801
2802: Bufferaligner_copyshifted_leadingbits_and_trailing_byte
2803 #:breakpoint Bufferaligner_copyshifted_leadingbits_and_trailing_byte
2804 NONVOL2 := NONVOL0
2805 Bufferaligner_load_and_inc_src VOL0 VOL0 1 # Load and advance src
2806 VOL1 := NONVOL0
2807 Bufferaligner_copyshift
2808 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save and advance dest
2809 ;
2810
2811: Bufferaligner_copyshifted_leadingbyteandbits
2812 #:breakpoint Bufferaligner_copyshifted_leadingbyteandbits
2813 NONVOL2 := NONVOL0
2814 Bufferaligner_load_and_inc_src VOL1 VOL1 1 # Load and advance src
2815 VOL0 := NONVOL0
2816 Bufferaligner_copyshift
2817 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save and advance dest
2818 ;
2819
2820: Bufferaligner_copyshifted_trailingbits
2821 #:breakpoint Bufferaligner_copyshifted_trailingbits
2822 Bufferaligner_load_and_inc_src VOL0 VOL0 1 # Load and advance src
2823 VOL1 := NONVOL0
2824 Bufferaligner_copyshift
2825 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save and advance dest
2826 ;
2827
2828: Bufferaligner_copyshifted_trailingbyteandbits
2829 #:breakpoint Bufferaligner_copyshifted_trailingbyteandbits
2830 VOL0 := NONVOL0
2831 VOL1 := NONVOL0
2832 Bufferaligner_copyshift
2833 Bufferaligner_save_and_inc_dest VOL0 VOL1 TileRow_Bytes # Save and advance dest
2834 ;
2835
2836
2837
2838:macro Bufferaligner_choose_leading shift remainder {
2839 if shift == 0 then jump Bufferaligner_choose_leading_nop
2840 Bufferaligner_COL += -1 # If leading isn't a nop, the body is one col smaller
2841 if shift == 8 then jump Bufferaligner_choose_leading_aligned
2842 vF := 0x07
2843 vF &= shift
2844 if vF == shift then jump Bufferaligner_choose_leading_shiftedbits
2845 # else shift is > 8
2846 : Bufferaligner_choose_leading_shiftedbytesandbits
2847 vF := 7
2848 shift &= vF
2849 Trampoline-Set-Call-Const Bufferaligner-tramp-leading Bufferaligner_copyshifted_leadingbyteandbits VOL0 VOL1
2850 jump Bufferaligner_choose_leading_done
2851 : Bufferaligner_choose_leading_nop
2852 Trampoline-Set-Nop Bufferaligner-tramp-leading VOL0 VOL1
2853 jump Bufferaligner_choose_leading_done
2854 : Bufferaligner_choose_leading_aligned
2855 Trampoline-Set-Call-Const Bufferaligner-tramp-leading Bufferaligner_copyaligned_leadingbyte VOL0 VOL1
2856 jump Bufferaligner_choose_leading_done
2857 : Bufferaligner_choose_leading_shiftedbits
2858 VOL0 := shift
2859 VOL0 += remainder
2860 vF := 0x07
2861 vF &= VOL0
2862 if vF != VOL0 then jump Bufferaligner_choose_leading_shiftedbits_and_trailingbyte
2863 Trampoline-Set-Call-Const Bufferaligner-tramp-leading Bufferaligner_copyshifted_leadingbits VOL0 VOL1
2864 jump Bufferaligner_choose_leading_done
2865 : Bufferaligner_choose_leading_shiftedbits_and_trailingbyte
2866 Trampoline-Set-Call-Const Bufferaligner-tramp-leading Bufferaligner_copyshifted_leadingbits_and_trailing_byte VOL0 VOL1
2867 : Bufferaligner_choose_leading_done
2868}
2869
2870:macro Bufferaligner_choose_trailing remainder {
2871 #:breakpoint choose_trailing
2872 if Bufferaligner_COL == 0 then jump Bufferaligner_choose_trailing_nop # If we already ate the col with leading, nop
2873 if remainder == 0 then jump Bufferaligner_choose_trailing_nop
2874 Bufferaligner_COL += -1 # If trailing isn't a nop, the body is one col smaller
2875 if remainder == 8 then jump Bufferaligner_choose_trailing_aligned
2876 vF := 0x07
2877 vF &= remainder
2878 if vF == remainder then jump Bufferaligner_choose_trailing_shiftedbits
2879 # else shift is > 8
2880 : Bufferaligner_choose_trailing_shiftedbytesandbits
2881 vF := 7
2882 remainder &= vF
2883 Trampoline-Set-Call-Const Bufferaligner-tramp-trailing Bufferaligner_copyshifted_trailingbyteandbits VOL0 VOL1
2884 jump Bufferaligner_choose_trailing_done
2885 : Bufferaligner_choose_trailing_nop
2886 Trampoline-Set-Nop Bufferaligner-tramp-trailing VOL0 VOL1
2887 jump Bufferaligner_choose_trailing_done
2888 : Bufferaligner_choose_trailing_aligned
2889 Trampoline-Set-Call-Const Bufferaligner-tramp-trailing Bufferaligner_copyaligned_trailingbyte VOL0 VOL1
2890 jump Bufferaligner_choose_trailing_done
2891 : Bufferaligner_choose_trailing_shiftedbits
2892 Trampoline-Set-Call-Const Bufferaligner-tramp-trailing Bufferaligner_copyshifted_trailingbyteandbits VOL0 VOL1
2893 : Bufferaligner_choose_trailing_done
2894}
2895
2896:macro Bufferaligner_choose_body shift {
2897 if shift == 0 then jump Bufferaligner_choose_body_aligned
2898 : Bufferaligner_choose_body_shifted
2899 Trampoline-Set-Call-Const Bufferaligner-tramp-body Bufferaligner_copyshifted VOL0 VOL1
2900 jump Bufferaligner_choose_body_done
2901 : Bufferaligner_choose_body_aligned
2902 Trampoline-Set-Call-Const Bufferaligner-tramp-body Bufferaligner_copyaligned VOL0 VOL1
2903 : Bufferaligner_choose_body_done
2904}
2905
2906# Buffer-toTileBuffer
2907# In:
2908# PASS0/1 - Pointer to Source Buffer
2909# PASS2 - X offset in pixels from top left corner of tile to top left corner of Buffer
2910# Out:
2911# PASS0/1 - Pointer to Dest TileSet
2912: Buffer-toTileBuffer
2913 :breakpoint Buffer-toTileBuffer
2914
2915 push NONVOL0 NONVOL3 4
2916 NONVOL1 := PASS2 # Store Offset / Shift
2917
2918 ## Save pointer to body of Src in PASS2/3
2919 reg_set_u16 PASS2 PASS3 PASS0 PASS1
2920 math_add_u16_u8 PASS2 PASS3 Buffer_Header_Bytes
2921
2922 ## Load parameters from src buffer
2923 #:breakpoint loadsrc
2924 reg_load_i_PASS01 # i := src
2925 load VOL4 - VOL5 # Load width and rows
2926 #:breakpoint Bufferalign-load-width-rows
2927 VOL4 += NONVOL1 # RequiredWidth = Width + Offset
2928 NONVOL3 := VOL4
2929 #:breakpoint Bufferalign-requiredwidth
2930
2931
2932 ## Allocate the TileBuffer
2933 PASS0 := VOL4 # Width (px)
2934 PASS1 := VOL5 # Height (px)
2935 TileBuffer_New # Allocates a TileBuffer, PASS0 tiles wide and PASS1 rows tall
2936 Bufferaligner_loadi_dest
2937 load Bufferaligner_ROW - Bufferaligner_COL # Load TileBuffer dims from dest structure
2938 #:breakpoint loaddim
2939 # Currently: PASS0/1 = Dest, PASS2/3 = Src, NONVOL1 = shift, VOL4 = width in tiles, VOL5 = height in px
2940
2941 push PASS0 PASS1 2 # Save a pointer to dest so it can be returned after the copy
2942
2943 Bufferaligner_loadi_dest
2944 load VOL0 - VOL1 # Load VOL0 = tiles wide, VOL1 = bytes per slice(row)
2945 Bufferaligner_inc_dest TileBuffer_Header_Bytes # Advance past the dest header
2946 #:breakpoint tiletopx
2947 Tile-tiletopx VOL1 NONVOL2 # Calculate pixel width of tilebuffer
2948 #:breakpoint calcremainder
2949 NONVOL2 -= NONVOL3 # Remainder pixels = tilebuffer width - RequiredWidth
2950
2951 #:breakpoint Bufferaligner-before-leadsetup
2952 ## Setup Leading Trampoline
2953 Bufferaligner_choose_leading NONVOL1 NONVOL2 # Also, shift &= 7
2954
2955 #:breakpoint Bufferaligner_before_trailsetup
2956 ## Setup Trailing Trampoline
2957 Bufferaligner_choose_trailing NONVOL2 # Also, remainder &= 7
2958
2959 #:breakpoint Bufferaligner_before_bodysetup
2960 ## Setup Body Trampoline
2961 Bufferaligner_choose_body NONVOL1
2962
2963 ## Setup Data Targets
2964 #:breakpoint Bufferaligner-Modify
2965 Modify-Target Bufferaligner-target-row Bufferaligner_ROW
2966 Modify-Target Bufferaligner-target-col Bufferaligner_COL
2967
2968 ### Perform the copy
2969 : Bufferaligner-toTileBuffer-copy-maskplane
2970 push PASS0 PASS1 2 # Save dest+header for reset after each plane
2971 NONVOL0 := DISPLAY_EMPTY_MASK # Set the value for empty data
2972 Bufferaligner_copyplane # Copy Mask
2973 NONVOL0 := DISPLAY_EMPTY_COLOR
2974
2975 : Bufferaligner-toTileBuffer-copy-plane0
2976 stackpeek PASS0 PASS1 2 # Restore dest
2977 Bufferaligner_inc_dest TileRow_Plane_Bytes
2978 Bufferaligner_copyplane # Copy Plane0
2979
2980 : Bufferaligner-toTileBuffer-copy-plane1
2981 pop PASS0 PASS1 2
2982 Bufferaligner_inc_dest TileRow_Plane1_Bytes
2983 Bufferaligner_copyplane # Copy Plane1
2984
2985 ### Clean Up
2986 pop PASS0 PASS1 2 # Return Dest as PASS01
2987 pop NONVOL0 NONVOL3 4 # Restore saved regs
2988 ;
2989
2990# Copies one plane
2991# The `Trampoline`s in the loop are retargeted by the setup code above
2992: Bufferaligner_copyplane
2993 #:breakpoint bufferaligner-copyplane
2994 DataTarget: Bufferaligner-target-row Bufferaligner_ROW := 0
2995 loop while Bufferaligner_ROW != 0
2996 #:breakpoint bufferaligner-copyplane-row
2997
2998 Bufferaligner_ROW += -1
2999 Bufferaligner_inc_dest TileSlice_Header_Bytes # Every row we have to skip the Slice Header
3000
3001 ## Copy one row of the Buffer to the TileBuffer
3002 #:breakpoint bufferaligner-copyplane-leading
3003 Trampoline: Bufferaligner-tramp-leading
3004 DataTarget: Bufferaligner-target-col Bufferaligner_COL := 0
3005 loop while Bufferaligner_COL != 0
3006 #:breakpoint bufferaligner-copyplane-col
3007
3008 Bufferaligner_COL += -1
3009 Trampoline: Bufferaligner-tramp-body
3010 again
3011 #:breakpoint bufferaligner-copyplane-trailing
3012 Trampoline: Bufferaligner-tramp-trailing
3013 # The source should be advanced to the next row in the plane
3014 # The dest should be advanced to the next slice
3015 again
3016 #:breakpoint bufferaligner-copyplane-done
3017 ;
3018
3019
3020
3021
3022# #####
3023# # Graphviz
3024# ###
3025# # Playing around with this as a way to assist coloring registers.
3026# # This is still not quite right, surprisingly hard to get right
3027# digraph Bufferaligner {
3028# Input -> Offset
3029# Input -> Src -> BufferWidth
3030# LeadChoice -> Cols
3031# TotalTiles -> Cols
3032# TrailChoice -> Cols
3033# Shift -> BodyChoice
3034# Shift -> LeadChoice
3035# TotalTiles -> SliceWidthPx
3036# SliceWidthPx -> TrailChoice
3037# Offset -> Shift -> TrailChoice
3038# RequiredWidth -> TrailChoice
3039# NewTileBuffer -> Dest
3040# NewTileBuffer -> TotalTiles
3041# Dest -> CopyLoopParams
3042# Src -> CopyLoopParams
3043# Shift -> CopyLoopParams
3044# Src -> Rows
3045# CopyLoop -> Complete
3046# Dest -> Complete
3047# Rows -> SetupCopyTargets -> CopyLoop
3048# Cols -> SetupCopyTargets
3049# TrailChoice -> BodyChoice
3050# TrailChoice -> SetupCopyTrampolines -> CopyLoop
3051# BodyChoice -> SetupCopyTrampolines
3052# LeadChoice -> SetupCopyTrampolines
3053# CopyLoopParams [shape=box];
3054# SetupCopyTrampolines [shape=box];
3055# SetupCopyTargets [shape=box];
3056# CopyLoop [shape=box];
3057# NewTileBuffer [shape=box];
3058# BodyChoice [shape=octagon];
3059# LeadChoice [shape=octagon];
3060# TrailChoice [shape=octagon];
3061# CopyLoopParams -> CopyLoop
3062# LeadChoice -> BodyChoice
3063# Src -> CopyLoop
3064# Rows -> NewTileBuffer
3065# Shift -> RequiredWidth
3066# BufferWidth -> RequiredWidth -> NewTileBuffer
3067# }
3068##/ compositor.o8 /##
3069#####################
3070
3071#####
3072# The Compositor
3073###
3074# Responsible for combining a layered set of buffers into one display
3075#
3076# Given an array of tiles, composite them
3077
3078
3079
3080#Old:
3081#
3082# Data:
3083# - A list of buffers
3084# - A list for each tile, containing an ordered list of bufferalignedtiles
3085# - A pointer to a Display object
3086# - A list of dirty tiles
3087#
3088#
3089# API
3090#
3091# CompositorNew (*Display) -> *Compositor
3092# CompositorDestruct (*Compositor)
3093# CompositorAddBuffer (*)
3094
3095
3096
3097:macro convert-to-compositor {
3098# This function copies the entirety of a tile-aligned buffer to a tile
3099# In:
3100# PASS0/1 = Pointer to Display Tile
3101# PASS2/3 = Pointer to Source Tile
3102# Stack: op <-- TODO: For now, ignores op, copies with alpha
3103: DisplayBltTileBuffer
3104 VOL4 := 0 # Offset into src
3105 VOL5 := TilePlane_Bytes # Offset from plane to plane
3106
3107
3108 # Mask the destination with the buffer's alpha channel
3109 : display-blt-TileBuffer-loop-alpha # alpha masking loop begins
3110 reg_load_i_PASS23
3111 i += VOL4
3112 load VOL0 - VOL1 # load alpha
3113
3114 reg_load_i_PASS01
3115 i += VOL4
3116 load VOL2 - VOL3 # load dest
3117 VOL2 &= VOL0
3118 VOL3 &= VOL1
3119 save VOL2 - VOL3 # save masked dest
3120
3121 i += VOL5 # advance to plane 2 of dest
3122 load VOL2 - VOL3
3123 VOL2 &= VOL0
3124 VOL3 &= VOL1
3125 save VOL2 - VOL3 # save masked dest
3126 VOL4 += 2
3127 if VOL4 != TilePlane_Bytes then jump display-blt-TileBuffer-loop-alpha
3128
3129
3130 # Combine the buffers pixel data with the destination
3131 VOL4 := TilePlane_Bytes # Offset into src, skip alpha
3132 VOL5 := 0 # Offset into dest
3133 reg_load_i_PASS01
3134 i += VOL5 # i := dest
3135 : display-blt-TileBuffer-loop-planes
3136
3137 load VOL0 - VOL1 # load dest
3138
3139 reg_load_i_PASS23
3140 i += VOL4
3141 load VOL2 - VOL3 # load src
3142
3143 VOL0 |= VOL2
3144 VOL1 |= VOL3
3145
3146 reg_load_i_PASS01
3147 i += VOL5
3148 save VOL1 # save combined data, auto-advance i for the next dest load
3149 VOL4 += 2
3150 VOL5 += 2
3151 if VOL5 != DisplayTile_Bytes then jump display-blt-TileBuffer-loop-planes
3152
3153 ;
3154}
3155#
3156# Text stuff
3157#
3158# Start with:
3159# - a font that has bitmaps for each character
3160# - a list of a strings that need to be produced
3161# - Includes each letter if we want to do player text entry
3162# - hyphenation information for these strings
3163#
3164# Processing:
3165# - combine characters into kerned chunks up to 8 px wide
3166# - determine which chucks save the most ram
3167# - assign best chunks to 256 - 1 = 255 glyphs
3168# - the final value is for hyphenation spots
3169# - save all the required strings, encoding using the defined chunks
3170# - strings need to be built from substrings
3171# - need a system for markov chain stuff
3172
3173
3174# You <verb> the <noun>.
3175#
3176# Should be able to store something that points to a mad lib sentence and attaches
3177# the appropriate data to fill in the blanks.
3178#
3179# string = { <string-num>, <fill-string>, <fill-string> }
3180#
3181# Strings should be able to have multiple representations depending on context.
3182# Or they might not, but there should be a way to represent:
3183# string = { string1, but string2 if <condition> is true }
3184#
3185#
3186# Verbs:
3187# hit
3188# miss
3189#
3190# Nouns:
3191# rat
3192# spark bug
3193# dinosaur
3194#
3195#
3196# spark bug = spa/rk + bu/g
3197# dinosaur = di-no-sa/ur
3198#
3199# string defined recursively
3200#
3201# dinosaurs = dinosaur + s
3202#
3203# colored dinosaures = co-lo/red + dinosaurs
3204#
3205# glyph = bitmap
3206#
3207#
3208# string = { [ string | glyph | space | hyphenation mark ], ... }
3209#
3210#
3211# From code, should be able to:
3212# - point to a string and call "add string to log" or "compose bitmap from string" type functions
3213# - compose a string from substrings for later display (ie take entry from player or build up a "you got hit by the rat" message)
3214# - build up a list of strings for display (message log)
3215
3216:const glyph_HYPHENATION 0xFC
3217:const glyph_SPACE 0xFD
3218:const glyph_SUBSTRING 0xFE
3219:const GLYPH_END 0xFF
3220
3221:macro text-sprite-glyph x-reg y-reg glyph-reg height-px {
3222 glyph-index glyph-reg
3223 load VOL0
3224 sprite x-reg y-reg height-px
3225 x-reg += VOL0
3226}
3227
3228
3229# In: PASS0/1 = Address of string, PASS2 = x, PASS3 = y
3230: text-render-string
3231
3232 #:breakpoint renderstring
3233 reg_load_i
3234 SCRATCH := 1
3235 math_add_u16_u8 PASS0 PASS1 SCRATCH
3236 load VOL0
3237 # check for special glyphs
3238 if VOL0 == GLYPH_END then return
3239 if VOL0 == glyph_HYPHENATION then jump text-render-string
3240 if VOL0 == glyph_SUBSTRING then jump text-render-substring
3241 if VOL0 == glyph_SPACE then jump text-render-space
3242
3243 text-sprite-glyph PASS2 PASS3 VOL0 6
3244
3245 jump text-render-string
3246: text-render-space
3247 PASS2 += 1
3248 jump text-render-string
3249
3250: text-render-substring
3251 #:breakpoint substring
3252 push PASS0 PASS1 2
3253 reg_load_i
3254 load PASS0 - PASS1
3255 text-render-string
3256 pop PASS0 PASS1 2 # Restore string address
3257 SCRATCH := 2
3258 math_add_u16_u8 PASS0 PASS1 SCRATCH # Add 2 to skip over substring
3259 jump text-render-string
3260##/ text/numbers.o8 /##
3261#######################
3262
3263# PASS0/1 = string buffer - must be able to hold 5 bytes
3264# PASS2/3 = number
3265: text_tostr_u16_hex
3266 reg_load_i
3267 VOL0 >>= PASS2
3268 VOL0 >>= VOL0
3269 VOL0 >>= VOL0
3270 VOL0 >>= VOL0
3271 VOL1 := 0x0F
3272 VOL1 &= PASS2
3273
3274 save v1
3275
3276 VOL0 >>= PASS3
3277 VOL0 >>= VOL0
3278 VOL0 >>= VOL0
3279 VOL0 >>= VOL0
3280 VOL1 := 0x0F
3281 VOL1 &= PASS3
3282
3283 VOL2 := GLYPH_END
3284 save v2
3285 ;
3286
3287##/ main.o8 /##
3288###############
3289
3290: list-head 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0
3291:calc list-head-hi { list-head >> 8 }
3292:calc list-head-lo { list-head & 0xff }
3293
3294: list-next 0xF1 0xF1 0xF1 0xF1 0xF1 0xF1 0xF1
3295:calc list-next-hi { list-next >> 8 }
3296:calc list-next-lo { list-next & 0xff }
3297
3298: list-third 0xF2 0xF2 0xF2 0xF2 0xF2 0xF2 0xF2
3299:calc list-third-hi { list-third >> 8 }
3300:calc list-third-lo { list-third & 0xff }
3301
3302
3303ListEnableDebug
3304
3305: buffer-test
330616 16 0 0 0
33070xFF 0xFF 0xC0 0x03 0xC0 0x03 0xC0 0x03
33080xC0 0x03 0xC0 0x03 0xC0 0x03 0x80 0x01
33090x80 0x01 0x80 0x01 0x80 0x01 0xFF 0xFF
33100xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
3311
33120xFF 0x00 0x3F 0xFC 0x20 0x04 0x22 0x44
33130x20 0x04 0x28 0x14 0x27 0xE4 0x20 0x04
33140x3F 0xFC 0x3F 0xFC 0x00 0x00 0x00 0x00
33150x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
33160x00 0x00 0x00 0x00 0x1F 0xF8 0x1F 0xF8
33170x1F 0xF8 0x1F 0xF8 0x1F 0xF8 0x5F 0xFA
33180x40 0x02 0x40 0x02 0x7F 0xFE 0x00 0x00
33190x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
3320
3321
3322
3323
3324: main
3325 stack-init
3326 heap_init
3327 DisplayInit
3328
3329 DisplayNew
3330
3331 : redo
3332 push PASS0 PASS1 2
3333
3334 #reg_set_u16 PASS0 PASS1 0xFF 0xFF
3335 PASS2 := 0x1c
3336 #if PASS2 != 0 then PASS2 := 0xFF
3337 PASS3 := 0xc9
3338 #if PASS3 != 0 then PASS3 := 0xFF
3339
3340 DisplayWipe
3341
3342 DisplayRedraw
3343:breakpoint DisplayRedrawn
3344
3345
3346
3347 hilo buffer-test
3348 reg_set_u16 PASS0 PASS1 hi lo
3349
3350 :breakpoint new-displayobj
3351 DisplayObject_New
3352
3353 reg_set_u16 PASS2 PASS3 PASS0 PASS1
3354 stackpeek PASS0 PASS1 2
3355 :breakpoint add-front
3356 Display_addObjectFront
3357
3358 :breakpoint display-calc-layers
3359 Display_calcLayers
3360 :breakpoint create-needed
3361 Display_createNeededBuffers
3362 :breakpoint composite-dirty
3363 Display_compositeDirtyTiles
3364
3365
3366
3367 PASS2 := 0
3368 PASS3 := 0
3369 Buffer-toTileBuffer
3370 :breakpoint gotbuffer
3371 reg_set_u16 PASS2 PASS3 PASS0 PASS1
3372 stackpeek PASS0 PASS1 2
3373
3374 DisplayBltTileBuffer
3375 pop PASS0 PASS1 2
3376 :breakpoint redraw
3377 DisplayRedraw
3378:breakpoint DisplayRedrawn
3379 : delayloop
3380 vf := 20
3381 delay := vf
3382 loop
3383 vf := delay
3384 if vf != 0 then again
3385jump redo
3386 reg_set_u16 PASS0 PASS1 list-head-hi list-head-lo
3387 ListInitHead
3388
3389 reg_set_u16 NONVOL0 NONVOL1 0x00 0x00
3390
3391 : test-loop
3392
3393 reg_set_u16 PASS0 PASS1 0x00 0x10
3394 malloc
3395 reg_load_i
3396 VOL0 := 1
3397 math_add_u16_u8 NONVOL0 NONVOL1 VOL0
3398 mem_save_offset 4 NONVOL0 NONVOL1
3399 reg_set_u16 PASS2 PASS3 PASS0 PASS1
3400 reg_set_u16 PASS0 PASS1 list-head-hi list-head-lo
3401 ListInsert
3402 VOL0 := random 0x01
3403 if VOL0 == 0 then jump nodel
3404 #push PASS0 PASS1 2
3405 #ListUnlink
3406 #pop PASS0 PASS1 2
3407 #free
3408 : nodel
3409 reg_set_u16 PASS0 PASS1 list-head-hi list-head-lo
3410 #ListDebug
3411
3412
3413
3414 jump test-loop
3415
3416##/ code-footer.o8 /##
3417######################
3418
3419: CODE_END # marker for end of code segment
3420#####
3421# Code Size Indicator
3422###
3423# A little toy using metaprogramming to show the size of the code segment (from
3424# 0x200 to 0x3FFF) and the decimal percentage used.
3425###
3426:org SEG_CODE_SIZE_START
3427:calc CODE_END_HI { CODE_END >> 8 }
3428:calc CODE_END_LO { CODE_END & 0xFF }
3429:calc CODE_SIZE { CODE_END - CODE_START }
3430:calc CODE_SIZE_HI { CODE_SIZE >> 8 }
3431:calc CODE_SIZE_LO { CODE_SIZE & 0xFF }
3432:calc CODE_SIZE_PCT { ( CODE_SIZE * 100 ) / ( 4095 - 0x200 ) }
3433:calc CODE_SIZE_PCT_HUNDREDS { floor ( CODE_SIZE_PCT / 100 ) }
3434:calc CODE_SIZE_PCT_TENS { floor ( ( CODE_SIZE_PCT - ( CODE_SIZE_PCT_HUNDREDS * 100 ) ) / 10 ) }
3435:calc CODE_SIZE_PCT_ONES { floor ( ( CODE_SIZE_PCT - ( CODE_SIZE_PCT_TENS * 10 ) ) - ( CODE_SIZE_PCT_HUNDREDS * 100 ) ) }
3436:calc CODE_SIZE_PCT_TENSONES { ( CODE_SIZE_PCT_TENS * 0x10 ) + CODE_SIZE_PCT_ONES }
34370xC0 0xDE 0xC0 0xDE 0xC0 0xDE 0x00 0x00 :byte CODE_END_HI :byte CODE_END_LO 0x00 :byte CODE_SIZE_HI :byte CODE_SIZE_LO 0x00 :byte CODE_SIZE_PCT_HUNDREDS :byte CODE_SIZE_PCT_TENSONES