# some primitives for moving the cursor without making assumptions about # raster order fn move-cursor-left screen: (addr screen) { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen compare cursor-x, 0 { break-if-> return } cursor-x <- decrement set-cursor-position screen, cursor-x, cursor-y } fn move-cursor-right screen: (addr screen) { var _width/eax: int <- copy 0 var dummy/ecx: int <- copy 0 _width, dummy <- screen-size screen var limit/edx: int <- copy _width limit <- decrement var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen compare cursor-x, limit { break-if-< return } cursor-x <- increment set-cursor-position screen, cursor-x, cursor-y } fn move-cursor-up screen: (addr screen) { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen compare cursor-y, 0 { break-if-> return } cursor-y <- decrement set-cursor-position screen, cursor-x, cursor-y } fn move-cursor-down screen: (addr screen) { var dummy/eax: int <- copy 0 var _height/ecx: int <- copy 0 dummy, _height <- screen-size screen var limit/edx: int <- copy _height limit <- decrement var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen compare cursor-y, limit { break-if-< return } cursor-y <- increment set-cursor-position screen, cursor-x, cursor-y } fn move-cursor-to-left-margin-of-next-line screen: (addr screen) { var dummy/eax: int <- copy 0 var _height/ecx: int <- copy 0 dummy, _height <- screen-size screen var limit/edx: int <- copy _height limit <- decrement var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen compare cursor-y, limit { break-if-< return } cursor-y <- increment cursor-x <- copy 0 set-cursor-position screen, cursor-x, cursor-y } fn draw-code-point-at-cursor-over-full-screen screen: (addr screen), c: code-point, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen var _offset/eax: int <- draw-code-point screen, c, cursor-x, cursor-y, color, background-color var offset/edx: int <- copy _offset var width/eax: int <- copy 0 var dummy/ecx: int <- copy 0 width, dummy <- screen-size screen move-cursor-rightward-and-downward screen, 0 width offset <- decrement compare offset, 0 { break-if-= # should never move downward here move-cursor-rightward-and-downward screen, 0 width } } # draw a single line of text from x, y to xmax # return the next 'x' coordinate # if there isn't enough space, truncate fn draw-text-rightward screen: (addr screen), text: (addr array byte), x: int, xmax: int, y: int, color: int, background-color: int -> _/eax: int { var stream-storage: (stream byte 0x400/print-buffer-size) var stream/esi: (addr stream byte) <- address stream-storage write stream, text var xcurr/eax: int <- draw-stream-rightward screen, stream, x, xmax, y, color, background-color return xcurr } # draw a single-line stream from x, y to xmax # return the next 'x' coordinate # if there isn't enough space, truncate fn draw-stream-rightward screen: (addr screen), stream: (addr stream byte), x: int, xmax: int, y: int, color: int, background-color: int -> _/eax: int { var xcurr/ecx: int <- copy x { var g/eax: code-point-utf8 <- read-code-point-utf8 stream compare g, 0xffffffff/end-of-file break-if-= var c/eax: code-point <- to-code-point g var offset/eax: int <- draw-code-point screen, c, xcurr, y, color, background-color xcurr <- add offset loop } set-cursor-position screen, xcurr, y return xcurr } fn draw-text-rightward-over-full-screen screen: (addr screen), text: (addr array byte), x: int, y: int, color: int, background-color: int -> _/eax: int { var width/eax: int <- copy 0 var height/ecx: int <- copy 0 width, height <- screen-size screen var result/eax: int <- draw-text-rightward screen, text, x, width, y, color, background-color return result } fn draw-text-rightward-from-cursor screen: (addr screen), text: (addr array byte), xmax: int, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen cursor-x <- draw-text-rightward screen, text, cursor-x, xmax, cursor-y, color, background-color set-cursor-position screen, cursor-x, cursor-y } fn draw-text-rightward-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int { var width/eax: int <- copy 0 var height/ecx: int <- copy 0 width, height <- screen-size screen draw-text-rightward-from-cursor screen, text, width, color, background-color } fn render-code-point screen: (addr screen), c: code-point, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var x/ecx: int <- copy x compare c, 0xa/newline { break-if-!= # minimum effort to clear cursor var dummy/eax: int <- draw-code-point screen, 0x20/space, x, y, color, background-color x <- copy xmin increment y return x, y } var offset/eax: int <- draw-code-point screen, c, x, y, color, background-color x <- add offset compare x, xmax { break-if-< x <- copy xmin increment y } return x, y } # draw text in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary # return the next (x, y) coordinate in raster order where drawing stopped # that way the caller can draw more if given the same min and max bounding-box. # if there isn't enough space, truncate fn draw-text-wrapping-right-then-down screen: (addr screen), _text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var stream-storage: (stream byte 0x200/print-buffer-size) var stream/edi: (addr stream byte) <- address stream-storage var text/esi: (addr array byte) <- copy _text var len/eax: int <- length text compare len, 0x200/print-buffer-size { break-if-< write stream, "ERROR: stream too small in draw-text-wrapping-right-then-down" } compare len, 0x200/print-buffer-size { break-if->= write stream, text } var x/eax: int <- copy _x var y/ecx: int <- copy _y x, y <- draw-stream-wrapping-right-then-down screen, stream, xmin, ymin, xmax, ymax, x, y, color, background-color return x, y } # draw a stream in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary # return the next (x, y) coordinate in raster order where drawing stopped # that way the caller can draw more if given the same min and max bounding-box. # if there isn't enough space, truncate fn draw-stream-wrapping-right-then-down screen: (addr screen), stream: (addr stream byte), xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var xcurr/ecx: int <- copy x var ycurr/edx: int <- copy y var c/ebx: code-point <- copy 0 var next-c/esi: code-point <- copy 0 $draw-stream-wrapping-right-then-down:loop: { # read c from either next-c or stream $draw-stream-wrapping-right-then-down:read-base: { compare next-c, 0 { break-if-= c <- copy next-c next-c <- copy 0 break $draw-stream-wrapping-right-then-down:read-base } var g/eax: code-point-utf8 <- read-code-point-utf8 stream var _c/eax: code-point <- to-code-point g c <- copy _c } compare c, 0xffffffff/end-of-file break-if-= compare c, 0xa/newline { break-if-!= # minimum effort to clear cursor var dummy/eax: int <- draw-code-point screen, 0x20/space, xcurr, ycurr, color, background-color xcurr <- copy xmin ycurr <- increment loop $draw-stream-wrapping-right-then-down:loop } var offset/eax: int <- draw-code-point screen, c, xcurr, ycurr, color, background-color # overlay a combining character if necessary $draw-stream-wrapping-right-then-down:read-combiner: { var done?/eax: boolean <- stream-empty? stream compare done?, 0/false break-if-!= # read a character var g/eax: code-point-utf8 <- read-code-point-utf8 stream var c/eax: code-point <- to-code-point g # if not a combining character, save for next iteration and loop { var combining-code-point?/eax: boolean <- combining-code-point? c compare combining-code-point?, 0/false } { break-if-!= next-c <- copy c break $draw-stream-wrapping-right-then-down:read-combiner } # otherwise overlay it without saving its width # This means strange results if a base and its combiner have different # widths. We'll always follow the base width. var dummy/eax: int <- overlay-code-point screen, c, xcurr, ycurr, color, background-color } xcurr <- add offset compare xcurr, xmax { break-if-< xcurr <- copy xmin ycurr <- increment } loop } set-cursor-position screen, xcurr, ycurr return xcurr, ycurr } fn draw-stream-wrapping-right-then-down-from-cursor screen: (addr screen), stream: (addr stream byte), xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen var end-x/edx: int <- copy cursor-x end-x <- increment compare end-x, xmax { break-if-< cursor-x <- copy xmin cursor-y <- increment } cursor-x, cursor-y <- draw-stream-wrapping-right-then-down screen, stream, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color } fn draw-stream-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), stream: (addr stream byte), color: int, background-color: int { var width/eax: int <- copy 0 var height/ecx: int <- copy 0 width, height <- screen-size screen draw-stream-wrapping-right-then-down-from-cursor screen, stream, 0/xmin, 0/ymin, width, height, color, background-color } fn move-cursor-rightward-and-downward screen: (addr screen), xmin: int, xmax: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen cursor-x <- increment compare cursor-x, xmax { break-if-< cursor-x <- copy xmin cursor-y <- increment } set-cursor-position screen, cursor-x, cursor-y } fn draw-text-wrapping-right-then-down-over-full-screen screen: (addr screen), text: (addr array byte), x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var x2/eax: int <- copy 0 var y2/ecx: int <- copy 0 x2, y2 <- screen-size screen # width, height x2, y2 <- draw-text-wrapping-right-then-down screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color return x2, y2 # cursor-x, cursor-y } fn draw-text-wrapping-right-then-down-from-cursor screen: (addr screen), text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen var end-x/edx: int <- copy cursor-x end-x <- increment compare end-x, xmax { break-if-< cursor-x <- copy xmin cursor-y <- increment } cursor-x, cursor-y <- draw-text-wrapping-right-then-down screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color } fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int { var width/eax: int <- copy 0 var height/ecx: int <- copy 0 width, height <- screen-size screen draw-text-wrapping-right-then-down-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color } fn draw-int32-hex-wrapping-right-then-down screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var stream-storage: (stream byte 0x100) var stream/esi: (addr stream byte) <- address stream-storage write-int32-hex stream, n var xcurr/edx: int <- copy x var ycurr/ecx: int <- copy y { var g/eax: code-point-utf8 <- read-code-point-utf8 stream compare g, 0xffffffff/end-of-file break-if-= var c/eax: code-point <- to-code-point g var offset/eax: int <- draw-code-point screen, c, xcurr, ycurr, color, background-color xcurr <- add offset compare xcurr, xmax { break-if-< xcurr <- copy xmin ycurr <- increment } loop } set-cursor-position screen, xcurr, ycurr return xcurr, ycurr } fn draw-int32-hex-wrapping-right-then-down-over-full-screen screen: (addr screen), n: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var x2/eax: int <- copy 0 var y2/ecx: int <- copy 0 x2, y2 <- screen-size screen # width, height x2, y2 <- draw-int32-hex-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color return x2, y2 # cursor-x, cursor-y } fn draw-int32-hex-wrapping-right-then-down-from-cursor screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen var end-x/edx: int <- copy cursor-x end-x <- increment compare end-x, xmax { break-if-< cursor-x <- copy xmin cursor-y <- increment } cursor-x, cursor-y <- draw-int32-hex-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color } fn draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int { var width/eax: int <- copy 0 var height/ecx: int <- copy 0 width, height <- screen-size screen draw-int32-hex-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color } fn draw-int32-decimal-wrapping-right-then-down screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var stream-storage: (stream byte 0x100) var stream/esi: (addr stream byte) <- address stream-storage write-int32-decimal stream, n var xcurr/edx: int <- copy x var ycurr/ecx: int <- copy y { var g/eax: code-point-utf8 <- read-code-point-utf8 stream compare g, 0xffffffff/end-of-file break-if-= var c/eax: code-point <- to-code-point g var offset/eax: int <- draw-code-point screen, c, xcurr, ycurr, color, background-color xcurr <- add offset compare xcurr, xmax { break-if-< xcurr <- copy xmin ycurr <- increment } loop } set-cursor-position screen, xcurr, ycurr return xcurr, ycurr } fn draw-int32-decimal-wrapping-right-then-down-over-full-screen screen: (addr screen), n: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var x2/eax: int <- copy 0 var y2/ecx: int <- copy 0 x2, y2 <- screen-size screen # width, height x2, y2 <- draw-int32-decimal-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color return x2, y2 # cursor-x, cursor-y } fn draw-int32-decimal-wrapping-right-then-down-from-cursor screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen var end-x/edx: int <- copy cursor-x end-x <- increment compare end-x, xmax { break-if-< cursor-x <- copy xmin cursor-y <- increment } cursor-x, cursor-y <- draw-int32-decimal-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color } fn draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int { var width/eax: int <- copy 0 var height/ecx: int <- copy 0 width, height <- screen-size screen draw-int32-decimal-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color } ## Text direction: down then right # draw a single line of text vertically from x, y to ymax # return the next 'y' coordinate # if there isn't enough space, truncate fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int { var stream-storage: (stream byte 0x100) var stream/esi: (addr stream byte) <- address stream-storage write stream, text var ycurr/eax: int <- draw-stream-downward screen, stream, x, y, ymax, color, background-color return ycurr } # draw a single-line stream vertically from x, y to ymax # return the next 'y' coordinate # if there isn't enough space, truncate # TODO: should we track horizontal width? fn draw-stream-downward screen: (addr screen), stream: (addr stream byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int { var ycurr/ecx: int <- copy y { var g/eax: code-point-utf8 <- read-code-point-utf8 stream compare g, 0xffffffff/end-of-file break-if-= var c/eax: code-point <- to-code-point g var dummy/eax: int <- draw-code-point screen, c, x, ycurr, color, background-color ycurr <- increment loop } set-cursor-position screen, x, ycurr return ycurr } fn draw-text-downward-from-cursor screen: (addr screen), text: (addr array byte), ymax: int, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen var result/eax: int <- draw-text-downward screen, text, cursor-x, cursor-y, ymax, color, background-color } # draw text down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary # return the next (x, y) coordinate in raster order where drawing stopped # that way the caller can draw more if given the same min and max bounding-box. # if there isn't enough space, truncate fn draw-text-wrapping-down-then-right screen: (addr screen), text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var stream-storage: (stream byte 0x100) var stream/esi: (addr stream byte) <- address stream-storage write stream, text var x/eax: int <- copy _x var y/ecx: int <- copy _y x, y <- draw-stream-wrapping-down-then-right screen, stream, xmin, ymin, xmax, ymax, x, y, color, background-color return x, y } # draw a stream down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary # return the next (x, y) coordinate in raster order where drawing stopped # that way the caller can draw more if given the same min and max bounding-box. # if there isn't enough space, truncate # TODO: should we track horizontal width? just always offset by 2 for now fn draw-stream-wrapping-down-then-right screen: (addr screen), stream: (addr stream byte), xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var xcurr/edx: int <- copy x var ycurr/ecx: int <- copy y { var g/eax: code-point-utf8 <- read-code-point-utf8 stream compare g, 0xffffffff/end-of-file break-if-= var c/eax: code-point <- to-code-point g var offset/eax: int <- draw-code-point screen, c, xcurr, ycurr, color, background-color ycurr <- increment compare ycurr, ymax { break-if-< xcurr <- add 2 ycurr <- copy ymin } loop } set-cursor-position screen, xcurr, ycurr return xcurr, ycurr } fn draw-text-wrapping-down-then-right-over-full-screen screen: (addr screen), text: (addr array byte), x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int { var x2/eax: int <- copy 0 var y2/ecx: int <- copy 0 x2, y2 <- screen-size screen # width, height x2, y2 <- draw-text-wrapping-down-then-right screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color return x2, y2 # cursor-x, cursor-y } fn draw-text-wrapping-down-then-right-from-cursor screen: (addr screen), text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int { var cursor-x/eax: int <- copy 0 var cursor-y/ecx: int <- copy 0 cursor-x, cursor-y <- cursor-position screen var end-y/edx: int <- copy cursor-y end-y <- increment compare end-y, ymax { break-if-< cursor-x <- increment cursor-y <- copy ymin } cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color } fn draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int { var width/eax: int <- copy 0 var height/ecx: int <- copy 0 width, height <- screen-size screen draw-text-wrapping-down-then-right-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color }