From 589eba07e2b1f1f3c6dbb3087b306071a2f9809b Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Mon, 11 Jan 2021 23:59:17 -0800 Subject: [PATCH] 7500 - baremetal: bounds-check screen space before drawing --- baremetal/106stream.subx | 74 +++++++++++++++++++++++++++++++++++++++ baremetal/400.mu | 3 +- baremetal/501draw-text.mu | 29 +++++++++++++-- baremetal/ex5.mu | 6 ++-- 4 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 baremetal/106stream.subx diff --git a/baremetal/106stream.subx b/baremetal/106stream.subx new file mode 100644 index 00000000..84d0580b --- /dev/null +++ b/baremetal/106stream.subx @@ -0,0 +1,74 @@ +# 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) + +== 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 diff --git a/baremetal/400.mu b/baremetal/400.mu index f25e13a3..2adb3254 100644 --- a/baremetal/400.mu +++ b/baremetal/400.mu @@ -1,7 +1,8 @@ sig pixel screen: (addr screen), x: int, y: int, color: int sig read-key kbd: (addr keyboard) -> _/eax: byte sig draw-grapheme screen: (addr screen), g: grapheme, x: int, y: int, color: int - +sig clear-stream f: (addr stream _) +sig rewind-stream f: (addr stream _) sig write f: (addr stream byte), s: (addr array byte) sig append-byte f: (addr stream byte), n: int sig read-byte s: (addr stream byte) -> _/eax: byte diff --git a/baremetal/501draw-text.mu b/baremetal/501draw-text.mu index 61b2d9ca..f19536ca 100644 --- a/baremetal/501draw-text.mu +++ b/baremetal/501draw-text.mu @@ -1,13 +1,36 @@ -fn draw-text-rightward screen: (addr screen), text: (addr array byte), x: int, y: int, color: int { +# draw a single line of text from x, y to xmax +# return the next 'x' coordinate +# if there isn't enough space, return 0 without modifying the screen +fn draw-text-rightward screen: (addr screen), text: (addr array byte), x: int, xmax: int, y: int, color: int -> _/eax: int { var stream-storage: (stream byte 0x100) var stream/esi: (addr stream byte) <- address stream-storage write stream, text + # check if we have enough space + var xcurr/ecx: int <- copy x + { + compare xcurr, xmax + break-if-> + var g/eax: grapheme <- read-grapheme stream + compare g, 0xffffffff # end-of-file + break-if-= + xcurr <- add 8 # font-width + loop + } + compare xcurr, xmax + { + break-if-<= + return 0 + } + # we do; actually draw + rewind-stream stream + xcurr <- copy x { var g/eax: grapheme <- read-grapheme stream compare g, 0xffffffff # end-of-file break-if-= - draw-grapheme screen, g, x, y, color - add-to x, 8 # font-width + draw-grapheme screen, g, xcurr, y, color + xcurr <- add 8 # font-width loop } + return xcurr } diff --git a/baremetal/ex5.mu b/baremetal/ex5.mu index 30e5b69c..ac7a9abe 100644 --- a/baremetal/ex5.mu +++ b/baremetal/ex5.mu @@ -1,4 +1,5 @@ -# Draw an ASCII string using the built-in font (GNU unifont) +# Draw a single line of ASCII text using the built-in font (GNU unifont) +# Also demonstrates bounds-checking _before_ drawing. # # To build a disk image: # ./translate_mu_baremetal baremetal/ex5.mu # emits disk.img @@ -10,5 +11,6 @@ # Expected output: text in green near the top-left corner of screen fn main { - draw-text-rightward 0, "hello from baremetal Mu!", 0x10, 0x10, 0xa + var dummy/eax: int <- draw-text-rightward 0, "hello from baremetal Mu!", 0x10, 0x400, 0x10, 0xa # xmax = end of screen, plenty of space + dummy <- draw-text-rightward 0, "you shouldn't see this", 0x10, 0xa0, 0x30, 0x3 # xmax = 0xa0, which is too narrow }