1 # Testable primitives for writing to screen.
3 # Mu mostly uses the screen for text, but it builds it out of pixel graphics
4 # and a bitmap font. There is no support for a blinking cursor, scrolling and
7 # Fake screens are primarily for testing text-mode prints. However, they do
8 # support some rudimentary pixel operations as well. Caveats:
10 # - Drawing pixels atop text or vice versa is not supported. Results in a fake
11 # screen will not mimic real screens in these situations.
12 # - Fake screens currently also assume a fixed-width 8x16 font.
13 # - Combining characters don't render like in a real screen (which itself
20 data: (handle array screen-cell)
21 cursor-x: int # [0..width)
22 cursor-y: int # [0..height)
24 pixels: (handle array byte)
34 fn initialize-screen _screen: (addr screen), width: int, height: int, pixel-graphics?: boolean {
35 var screen/esi: (addr screen) <- copy _screen
36 var tmp/eax: int <- copy 0
37 var dest/edi: (addr int) <- copy 0
38 # screen->width = width
39 dest <- get screen, width
42 # screen->height = height
43 dest <- get screen, height
46 # populate screen->data
48 var data-ah/edi: (addr handle array screen-cell) <- get screen, data
49 var capacity/eax: int <- copy width
50 capacity <- multiply height
52 populate data-ah, capacity
54 # if necessary, populate screen->pixels
56 compare pixel-graphics?, 0/false
58 var pixels-ah/edi: (addr handle array byte) <- get screen, pixels
59 var capacity/eax: int <- copy width
60 capacity <- shift-left 3/log2-font-width
61 capacity <- multiply height
62 capacity <- shift-left 4/log2-font-height
64 populate pixels-ah, capacity
66 # screen->cursor-x = 0
67 dest <- get screen, cursor-x
69 # screen->cursor-y = 0
70 dest <- get screen, cursor-y
75 fn screen-size _screen: (addr screen) -> _/eax: int, _/ecx: int {
76 var screen/esi: (addr screen) <- copy _screen
77 var width/eax: int <- copy 0
78 var height/ecx: int <- copy 0
82 return 0x80/128, 0x30/48
85 var tmp/edx: (addr int) <- get screen, width
87 tmp <- get screen, height
92 # testable screen primitive
93 # return number of 8x16 units drawn
94 fn draw-code-point _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int -> _/eax: int {
95 var screen/esi: (addr screen) <- copy _screen
99 var result/eax: int <- draw-code-point-on-real-screen c, x, y, color, background-color
103 var wide?/eax: boolean <- wide-code-point? c
104 compare wide?, 0/false
107 draw-wide-code-point-on-fake-screen screen, c, x, y, color, background-color
110 draw-narrow-code-point-on-fake-screen screen, c, x, y, color, background-color
114 fn overlay-code-point _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int -> _/eax: int {
115 var screen/esi: (addr screen) <- copy _screen
119 var result/eax: int <- overlay-code-point-on-real-screen c, x, y, color, background-color
123 # TODO: support overlays in fake screen
124 var wide?/eax: boolean <- wide-code-point? c
125 compare wide?, 0/false
128 draw-wide-code-point-on-fake-screen screen, c, x, y, color, background-color
131 draw-narrow-code-point-on-fake-screen screen, c, x, y, color, background-color
135 fn draw-narrow-code-point-on-fake-screen _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int {
136 var screen/esi: (addr screen) <- copy _screen
137 # ignore if out of bounds
144 var xmax-addr/eax: (addr int) <- get screen, width
145 var xmax/eax: int <- copy *xmax-addr
159 var ymax-addr/eax: (addr int) <- get screen, height
160 var ymax/eax: int <- copy *ymax-addr
166 var index/ecx: int <- screen-cell-index screen, x, y
167 var data-ah/eax: (addr handle array screen-cell) <- get screen, data
168 var data/eax: (addr array screen-cell) <- lookup *data-ah
169 var offset/ecx: (offset screen-cell) <- compute-offset data, index
170 var dest-cell/ecx: (addr screen-cell) <- index data, offset
171 var dest-code-point/eax: (addr code-point) <- get dest-cell, data
172 var c2/edx: code-point <- copy c
173 copy-to *dest-code-point, c2
174 var dest-color/eax: (addr int) <- get dest-cell, color
175 var src-color/edx: int <- copy color
176 copy-to *dest-color, src-color
177 dest-color <- get dest-cell, background-color
178 src-color <- copy background-color
179 copy-to *dest-color, src-color
180 var dest/eax: (addr boolean) <- get dest-cell, unused?
181 copy-to *dest, 0/false
184 fn draw-wide-code-point-on-fake-screen _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int {
185 var screen/esi: (addr screen) <- copy _screen
186 # ignore if out of bounds
193 var xmax-addr/eax: (addr int) <- get screen, width
194 var xmax/eax: int <- copy *xmax-addr
195 xmax <- decrement # wide code-points need an extra unit
206 var ymax-addr/eax: (addr int) <- get screen, height
207 var ymax/eax: int <- copy *ymax-addr
213 var index/ecx: int <- screen-cell-index screen, x, y
215 var data-ah/eax: (addr handle array screen-cell) <- get screen, data
216 var data/eax: (addr array screen-cell) <- lookup *data-ah
217 var offset/ecx: (offset screen-cell) <- compute-offset data, index
218 var dest-cell/ecx: (addr screen-cell) <- index data, offset
219 var dest-code-point/eax: (addr code-point) <- get dest-cell, data
220 var c2/edx: code-point <- copy c
221 copy-to *dest-code-point, c2
222 var dest-color/eax: (addr int) <- get dest-cell, color
223 var src-color/edx: int <- copy color
224 copy-to *dest-color, src-color
225 dest-color <- get dest-cell, background-color
226 src-color <- copy background-color
227 copy-to *dest-color, src-color
228 var dest/eax: (addr boolean) <- get dest-cell, unused?
229 copy-to *dest, 0/false
231 # set next screen-cell to unused
234 var data-ah/eax: (addr handle array screen-cell) <- get screen, data
235 var data/eax: (addr array screen-cell) <- lookup *data-ah
236 var offset/ecx: (offset screen-cell) <- compute-offset data, index
237 var dest-cell/ecx: (addr screen-cell) <- index data, offset
238 var dest/eax: (addr boolean) <- get dest-cell, unused?
239 copy-to *dest, 1/true
244 fn screen-cell-index _screen: (addr screen), x: int, y: int -> _/ecx: int {
245 var screen/esi: (addr screen) <- copy _screen
246 var width-addr/eax: (addr int) <- get screen, width
247 var result/ecx: int <- copy y
248 result <- multiply *width-addr
253 fn cursor-position _screen: (addr screen) -> _/eax: int, _/ecx: int {
254 var screen/esi: (addr screen) <- copy _screen
258 var x/eax: int <- copy 0
259 var y/ecx: int <- copy 0
260 x, y <- cursor-position-on-real-screen
264 var cursor-x-addr/eax: (addr int) <- get screen, cursor-x
265 var cursor-y-addr/ecx: (addr int) <- get screen, cursor-y
266 return *cursor-x-addr, *cursor-y-addr
269 fn set-cursor-position _screen: (addr screen), x: int, y: int {
270 var screen/esi: (addr screen) <- copy _screen
274 set-cursor-position-on-real-screen x, y
286 var width-addr/eax: (addr int) <- get screen, width
287 var width/eax: int <- copy *width-addr
300 var height-addr/eax: (addr int) <- get screen, height
301 var height/eax: int <- copy *height-addr
306 # screen->cursor-x = x
307 var dest/edi: (addr int) <- get screen, cursor-x
308 var src/eax: int <- copy x
310 # screen->cursor-y = y
311 dest <- get screen, cursor-y
316 fn draw-cursor screen: (addr screen), c: code-point {
320 draw-cursor-on-real-screen c
324 var cursor-x/eax: int <- copy 0
325 var cursor-y/ecx: int <- copy 0
326 cursor-x, cursor-y <- cursor-position screen
327 var dummy/eax: int <- draw-code-point screen, c, cursor-x, cursor-y, 0/fg, 7/bg
330 fn clear-screen _screen: (addr screen) {
331 var screen/esi: (addr screen) <- copy _screen
339 set-cursor-position screen, 0, 0
340 var y/eax: int <- copy 0
341 var height/ecx: (addr int) <- get screen, height
345 var x/edx: int <- copy 0
346 var width/ebx: (addr int) <- get screen, width
350 var dummy/eax: int <- draw-code-point screen, 0/nul, x, y, 0/fg=black, 0/bg=black
357 set-cursor-position screen, 0, 0
358 var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
359 var pixels/eax: (addr array byte) <- lookup *pixels-ah
360 var i/ecx: int <- copy 0
361 var max/edx: int <- length pixels
365 var curr/eax: (addr byte) <- index pixels, i
366 var zero/ebx: byte <- copy 0
367 copy-byte-to *curr, zero
373 fn fake-screen-empty? _screen: (addr screen) -> _/eax: boolean {
374 var screen/esi: (addr screen) <- copy _screen
375 var y/eax: int <- copy 0
376 var height/ecx: (addr int) <- get screen, height
380 var x/edx: int <- copy 0
381 var width/ebx: (addr int) <- get screen, width
385 var c/eax: code-point <- screen-code-point-at screen, x, y
389 compare c, 0x20/space
399 var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
400 var pixels/eax: (addr array byte) <- lookup *pixels-ah
401 var y/ebx: int <- copy 0
402 var height-addr/edx: (addr int) <- get screen, height
403 var height/edx: int <- copy *height-addr
404 height <- shift-left 4/log2-font-height
408 var width-addr/edx: (addr int) <- get screen, width
409 var width/edx: int <- copy *width-addr
410 width <- shift-left 3/log2-font-width
411 var x/edi: int <- copy 0
415 var index/ecx: int <- pixel-index screen, x, y
416 var color-addr/ecx: (addr byte) <- index pixels, index
417 var color/ecx: byte <- copy-byte *color-addr
432 fn clear-rect _screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
433 var screen/esi: (addr screen) <- copy _screen
437 clear-rect-on-real-screen xmin, ymin, xmax, ymax, background-color
441 set-cursor-position screen, 0, 0
442 var y/eax: int <- copy ymin
443 var ymax/ecx: int <- copy ymax
447 var x/edx: int <- copy xmin
448 var xmax/ebx: int <- copy xmax
452 var dummy/eax: int <- draw-code-point screen, 0x20/space, x, y, 0/fg, background-color
459 set-cursor-position screen, 0, 0
462 # there's no code-point-utf8 that guarantees to cover every pixel, so we'll bump down
463 # to pixels for a real screen
464 fn clear-real-screen {
465 var y/eax: int <- copy 0
467 compare y, 0x300/screen-height=768
469 var x/edx: int <- copy 0
471 compare x, 0x400/screen-width=1024
473 pixel-on-real-screen x, y, 0/color=black
482 fn clear-rect-on-real-screen xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
483 var y/eax: int <- copy ymin
484 y <- shift-left 4/log2-font-height
485 var ymax/ecx: int <- copy ymax
486 ymax <- shift-left 4/log2-font-height
490 var x/edx: int <- copy xmin
491 x <- shift-left 3/log2-font-width
492 var xmax/ebx: int <- copy xmax
493 xmax <- shift-left 3/log2-font-width
497 pixel-on-real-screen x, y, background-color
506 fn screen-cell-unused-at? _screen: (addr screen), x: int, y: int -> _/eax: boolean {
507 var screen/esi: (addr screen) <- copy _screen
508 var index/ecx: int <- screen-cell-index screen, x, y
509 var result/eax: boolean <- screen-cell-unused-at-index? screen, index
513 fn screen-cell-unused-at-index? _screen: (addr screen), _index: int -> _/eax: boolean {
514 var screen/esi: (addr screen) <- copy _screen
515 var data-ah/eax: (addr handle array screen-cell) <- get screen, data
516 var data/eax: (addr array screen-cell) <- lookup *data-ah
517 var index/ecx: int <- copy _index
518 var offset/ecx: (offset screen-cell) <- compute-offset data, index
519 var cell/eax: (addr screen-cell) <- index data, offset
520 var src/eax: (addr boolean) <- get cell, unused?
524 fn screen-code-point-at _screen: (addr screen), x: int, y: int -> _/eax: code-point {
525 var screen/esi: (addr screen) <- copy _screen
526 var index/ecx: int <- screen-cell-index screen, x, y
527 var result/eax: code-point <- screen-code-point-at-index screen, index
531 fn screen-code-point-at-index _screen: (addr screen), _index: int -> _/eax: code-point {
532 var screen/esi: (addr screen) <- copy _screen
533 var data-ah/eax: (addr handle array screen-cell) <- get screen, data
534 var data/eax: (addr array screen-cell) <- lookup *data-ah
535 var index/ecx: int <- copy _index
536 var offset/ecx: (offset screen-cell) <- compute-offset data, index
537 var cell/eax: (addr screen-cell) <- index data, offset
538 var src/eax: (addr code-point) <- get cell, data
542 fn screen-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
543 var screen/esi: (addr screen) <- copy _screen
544 var index/ecx: int <- screen-cell-index screen, x, y
545 var result/eax: int <- screen-color-at-index screen, index
549 fn screen-color-at-index _screen: (addr screen), _index: int -> _/eax: int {
550 var screen/esi: (addr screen) <- copy _screen
551 var data-ah/eax: (addr handle array screen-cell) <- get screen, data
552 var data/eax: (addr array screen-cell) <- lookup *data-ah
553 var index/ecx: int <- copy _index
554 var offset/ecx: (offset screen-cell) <- compute-offset data, index
555 var cell/eax: (addr screen-cell) <- index data, offset
556 var src/eax: (addr int) <- get cell, color
557 var result/eax: int <- copy *src
561 fn screen-background-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
562 var screen/esi: (addr screen) <- copy _screen
563 var index/ecx: int <- screen-cell-index screen, x, y
564 var result/eax: int <- screen-background-color-at-index screen, index
568 fn screen-background-color-at-index _screen: (addr screen), _index: int -> _/eax: int {
569 var screen/esi: (addr screen) <- copy _screen
570 var data-ah/eax: (addr handle array screen-cell) <- get screen, data
571 var data/eax: (addr array screen-cell) <- lookup *data-ah
572 var index/ecx: int <- copy _index
573 var offset/ecx: (offset screen-cell) <- compute-offset data, index
574 var cell/eax: (addr screen-cell) <- index data, offset
575 var src/eax: (addr int) <- get cell, background-color
576 var result/eax: int <- copy *src
580 fn pixel screen: (addr screen), x: int, y: int, color: int {
584 pixel-on-real-screen x, y, color
588 var screen/esi: (addr screen) <- copy screen
589 var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
590 var pixels/eax: (addr array byte) <- lookup *pixels-ah
594 abort "pixel graphics not enabled for this screen"
596 # ignore if out of bounds
603 var xmax-addr/eax: (addr int) <- get screen, width
604 var xmax/eax: int <- copy *xmax-addr
605 xmax <- shift-left 3/log2-font-width
616 var ymax-addr/eax: (addr int) <- get screen, height
617 var ymax/eax: int <- copy *ymax-addr
618 ymax <- shift-left 4/log2-font-height
624 var index/ecx: int <- pixel-index screen, x, y
625 var dest/ecx: (addr byte) <- index pixels, index
626 var src/eax: byte <- copy-byte color
627 copy-byte-to *dest, src
630 fn pixel-index _screen: (addr screen), x: int, y: int -> _/ecx: int {
631 var screen/esi: (addr screen) <- copy _screen
632 var width-addr/eax: (addr int) <- get screen, width
633 var result/ecx: int <- copy y
634 result <- multiply *width-addr
635 result <- shift-left 3/log2-font-width
640 # double-buffering primitive
641 # 'screen' must be a fake screen. 'target-screen' is usually real.
642 # Both screens must have the same size.
643 fn copy-pixels _screen: (addr screen), target-screen: (addr screen) {
644 var screen/esi: (addr screen) <- copy _screen
645 var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
646 var _pixels/eax: (addr array byte) <- lookup *pixels-ah
647 var pixels/edi: (addr array byte) <- copy _pixels
648 var width-a/edx: (addr int) <- get screen, width
649 var width/edx: int <- copy *width-a
650 width <- shift-left 3/log2-font-width
651 var height-a/ebx: (addr int) <- get screen, height
652 var height/ebx: int <- copy *height-a
653 height <- shift-left 4/log2-font-height
654 var i/esi: int <- copy 0
655 var y/ecx: int <- copy 0
657 # screen top left pixels x y width height
660 var x/eax: int <- copy 0
665 var color-addr/ebx: (addr byte) <- index pixels, i
666 var color/ebx: byte <- copy-byte *color-addr
667 var color2/ebx: int <- copy color
668 pixel target-screen, x, y, color2
679 # It turns out double-buffering screen-cells is useless because rendering fonts
680 # takes too long. (At least under Qemu.)
681 # So we'll instead convert screen-cells to pixels when double-buffering.
682 # 'screen' must be a fake screen.
683 fn convert-screen-cells-to-pixels _screen: (addr screen) {
684 var screen/esi: (addr screen) <- copy _screen
685 var width-a/ebx: (addr int) <- get screen, width
686 var height-a/edx: (addr int) <- get screen, height
687 var data-ah/eax: (addr handle array byte) <- get screen, pixels
688 var _data/eax: (addr array byte) <- lookup *data-ah
689 var data: (addr array byte)
691 var y/ecx: int <- copy 0
695 var x/edi: int <- copy 0
696 $convert-screen-cells-to-pixels:loop-x: {
700 var tmp/eax: code-point <- screen-code-point-at screen, x, y
701 # skip null code-points that only get created when clearing screen
702 # there may be other pixels drawn there, and we don't want to clobber them
703 # this is a situation where fake screens aren't faithful to real screens; we don't support overlap between screen-cells and raw pixels
708 var tmp/eax: int <- screen-color-at screen, x, y
711 var bg/eax: int <- screen-background-color-at screen, x, y
712 var offset/eax: int <- draw-code-point-on-screen-array data, c, x, y, fg, bg, *width-a, *height-a
714 loop $convert-screen-cells-to-pixels:loop-x