49424b1933
There's a dependency cycle here: - draw-grapheme (Mu) uses read-grapheme (Mu) to be unicode-aware. - read-grapheme uses read-byte (SubX). Streams are a fundamental data structure in Mu. For the Mu compiler to be able to reason about the safety of stream operations, they need to be an opaque type. All stream primitives are written in SubX. To manipulate a stream's internals we force people to reach for SubX. That way if there's no SubX code there's confidence that things are safe. - read-byte and other stream operations have unit tests, like they should. The unit tests need to print data to screen when say a test fails. To do this they use various check- functions (SubX) that take a string argument. - Printing a string to screen uses draw-grapheme (Mu). Perhaps I should maintain variants of drawing primitives that operate only on ASCII.
78 lines
4.2 KiB
Plaintext
78 lines
4.2 KiB
Plaintext
# streams: data structure for operating on arrays in a stateful manner
|
|
#
|
|
# A stream looks like this:
|
|
# write: int # index at which writes go
|
|
# read: int # index that we've read until
|
|
# data: (array byte) # prefixed by size as usual
|
|
#
|
|
# some primitives for operating on streams:
|
|
# - clear-stream (clears everything but the data size)
|
|
# - rewind-stream (resets read pointer)
|
|
#
|
|
# We need to do this in machine code because streams need to be opaque types,
|
|
# and we don't yet support opaque types in Mu.
|
|
|
|
== code
|
|
# instruction effective address register displacement immediate
|
|
# . op subop mod rm32 base index scale r32
|
|
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
|
|
|
|
clear-stream: # f: (addr stream byte)
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
50/push-eax
|
|
51/push-ecx
|
|
# eax = f
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax
|
|
# var count/ecx: int = f->size
|
|
8b/copy 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 8/disp8 . # copy *(eax+8) to ecx
|
|
# var max/ecx: (addr byte) = &f->data[f->size]
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 0xc/disp8 . # copy eax+ecx+12 to ecx
|
|
# f->write = 0
|
|
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax
|
|
# f->read = 0
|
|
c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 0/imm32 # copy to *(eax+4)
|
|
# - clear all stream data
|
|
# - this isn't strictly necessary, and it can slow things down *a lot*, but better safe than sorry.
|
|
# var curr/eax: (addr byte) = f->data
|
|
81 0/subop/add 3/mod/direct 0/rm32/eax . . . . . 0xc/imm32 # add to eax
|
|
$clear-stream:loop:
|
|
# if (curr >= max) break
|
|
39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx
|
|
73/jump-if-addr>= $clear-stream:end/disp8
|
|
# *curr = 0
|
|
c6 0/subop/copy-byte 0/mod/direct 0/rm32/eax . . . . . 0/imm8 # copy byte to *eax
|
|
# ++curr
|
|
40/increment-eax
|
|
eb/jump $clear-stream:loop/disp8
|
|
$clear-stream:end:
|
|
# . restore registers
|
|
59/pop-to-ecx
|
|
58/pop-to-eax
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
rewind-stream: # f: (addr stream byte)
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
50/push-eax
|
|
# eax = f
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax
|
|
# f->read = 0
|
|
c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 0/imm32 # copy to *(eax+4)
|
|
$rewind-stream:end:
|
|
# . restore registers
|
|
58/pop-to-eax
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
# . . vim:nowrap:textwidth=0
|