https://github.com/akkartik/mu/blob/main/500text-screen.mu
  1 # Testable primitives for writing text to screen.
  2 # (Mu doesn't yet have testable primitives for graphics.)
  3 #
  4 # Unlike the top-level, this text mode has no scrolling.
  5 
  6 # coordinates here don't match top-level
  7 # Here we're consistent with graphics mode. Top-level is consistent with
  8 # terminal emulators.
  9 type screen {
 10   width: int
 11   height: int
 12   data: (handle array screen-cell)
 13   cursor-x: int
 14   cursor-y: int
 15 }
 16 
 17 type screen-cell {
 18   data: grapheme
 19   color: int
 20   background-color: int
 21 }
 22 
 23 fn initialize-screen _screen: (addr screen), width: int, height: int {
 24   var screen/esi: (addr screen) <- copy _screen
 25   var tmp/eax: int <- copy 0
 26   var dest/edi: (addr int) <- copy 0
 27   # screen->width = width
 28   dest <- get screen, width
 29   tmp <- copy width
 30   copy-to *dest, tmp
 31   # screen->height = height
 32   dest <- get screen, height
 33   tmp <- copy height
 34   copy-to *dest, tmp
 35   # screen->data = new screen-cell[width*height]
 36   {
 37     var data-addr/edi: (addr handle array screen-cell) <- get screen, data
 38     tmp <- multiply width
 39     populate data-addr, tmp
 40   }
 41   # screen->cursor-x = 0
 42   dest <- get screen, cursor-x
 43   copy-to *dest, 0
 44   # screen->cursor-y = 0
 45   dest <- get screen, cursor-y
 46   copy-to *dest, 0
 47 }
 48 
 49 # in graphemes
 50 fn screen-size _screen: (addr screen) -> _/eax: int, _/ecx: int {
 51   var screen/esi: (addr screen) <- copy _screen
 52   var width/eax: int <- copy 0
 53   var height/ecx: int <- copy 0
 54   compare screen, 0
 55   {
 56     break-if-!=
 57     return 0x80/128, 0x30/48
 58   }
 59   # fake screen
 60   var tmp/edx: (addr int) <- get screen, width
 61   width <- copy *tmp
 62   tmp <- get screen, height
 63   height <- copy *tmp
 64   return width, height
 65 }
 66 
 67 # testable screen primitive
 68 fn draw-grapheme _screen: (addr screen), g: grapheme, x: int, y: int, color: int, background-color: int {
 69   var screen/esi: (addr screen) <- copy _screen
 70   {
 71     compare screen, 0
 72     break-if-!=
 73     draw-grapheme-on-real-screen g, x, y, color, background-color
 74     return
 75   }
 76   # fake screen
 77   var idx/ecx: int <- screen-cell-index screen, x, y
 78   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
 79   var data/eax: (addr array screen-cell) <- lookup *data-ah
 80   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
 81   var dest-cell/ecx: (addr screen-cell) <- index data, offset
 82   var dest-grapheme/eax: (addr grapheme) <- get dest-cell, data
 83   var g2/edx: grapheme <- copy g
 84   copy-to *dest-grapheme, g2
 85   var dest-color/eax: (addr int) <- get dest-cell, color
 86   var src-color/edx: int <- copy color
 87   copy-to *dest-color, src-color
 88   dest-color <- get dest-cell, background-color
 89   src-color <- copy background-color
 90   copy-to *dest-color, src-color
 91 }
 92 
 93 # we can't really render non-ASCII yet, but when we do we'll be ready
 94 fn draw-code-point screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int {
 95   var g/eax: grapheme <- copy c
 96   draw-grapheme screen, g, x, y, color, background-color
 97 }
 98 
 99 # not really needed for a real screen, though it shouldn't do any harm
100 fn screen-cell-index _screen: (addr screen), x: int, y: int -> _/ecx: int {
101   var screen/esi: (addr screen) <- copy _screen
102   # only one bounds check isn't automatically handled
103   {
104     var xmax/eax: (addr int) <- get screen, width
105     var xcurr/ecx: int <- copy x
106     compare xcurr, *xmax
107     break-if-<
108     abort "tried to print out of screen bounds"
109   }
110   var width-addr/eax: (addr int) <- get screen, width
111   var result/ecx: int <- copy y
112   result <- multiply *width-addr
113   result <- add x
114   return result
115 }
116 
117 fn cursor-position _screen: (addr screen) -> _/eax: int, _/ecx: int {
118   var screen/esi: (addr screen) <- copy _screen
119   {
120     compare screen, 0
121     break-if-!=
122     var x/eax: int <- copy 0
123     var y/ecx: int <- copy 0
124     x, y <- cursor-position-on-real-screen
125     return x, y
126   }
127   # fake screen
128   var cursor-x-addr/eax: (addr int) <- get screen, cursor-x
129   var cursor-y-addr/ecx: (addr int) <- get screen, cursor-y
130   return *cursor-x-addr, *cursor-y-addr
131 }
132 
133 fn set-cursor-position _screen: (addr screen), x: int, y: int {
134   var screen/esi: (addr screen) <- copy _screen
135   {
136     compare screen, 0
137     break-if-!=
138     set-cursor-position-on-real-screen x, y
139     return
140   }
141   # fake screen
142   # ignore x < 0
143   {
144     compare x, 0
145     break-if->=
146     return
147   }
148   # ignore x >= width
149   {
150     var width-addr/eax: (addr int) <- get screen, width
151     var width/eax: int <- copy *width-addr
152     compare x, width
153     break-if-<=
154     return
155   }
156   # ignore y < 0
157   {
158     compare y, 0
159     break-if->=
160     return
161   }
162   # ignore y >= height
163   {
164     var height-addr/eax: (addr int) <- get screen, height
165     var height/eax: int <- copy *height-addr
166     compare y, height
167     break-if-<
168     return
169   }
170   # screen->cursor-x = x
171   var dest/edi: (addr int) <- get screen, cursor-x
172   var src/eax: int <- copy x
173   copy-to *dest, src
174   # screen->cursor-y = y
175   dest <- get screen, cursor-y
176   src <- copy y
177   copy-to *dest, src
178 }
179 
180 fn draw-cursor screen: (addr screen), g: grapheme {
181   {
182     compare screen, 0
183     break-if-!=
184     draw-cursor-on-real-screen g
185     return
186   }
187   # fake screen
188   var cursor-x/eax: int <- copy 0
189   var cursor-y/ecx: int <- copy 0
190   cursor-x, cursor-y <- cursor-position screen
191   draw-grapheme screen, g, cursor-x, cursor-y, 0/fg, 7/bg
192 }
193 
194 fn clear-screen _screen: (addr screen) {
195   var screen/esi: (addr screen) <- copy _screen
196   {
197     compare screen, 0
198     break-if-!=
199     clear-real-screen
200     return
201   }
202   # fake screen
203   set-cursor-position screen, 0, 0
204   var y/eax: int <- copy 0
205   var height/ecx: (addr int) <- get screen, height
206   {
207     compare y, *height
208     break-if->=
209     var x/edx: int <- copy 0
210     var width/ebx: (addr int) <- get screen, width
211     {
212       compare x, *width
213       break-if->=
214       draw-code-point screen, 0x20/space, x, y, 0/fg=black, 0/bg=black
215       x <- increment
216       loop
217     }
218     y <- increment
219     loop
220   }
221   set-cursor-position screen, 0, 0
222 }
223 
224 fn fake-screen-empty? _screen: (addr screen) -> _/eax: boolean {
225   var screen/esi: (addr screen) <- copy _screen
226   var y/eax: int <- copy 0
227   var height/ecx: (addr int) <- get screen, height
228   {
229     compare y, *height
230     break-if->=
231     var x/edx: int <- copy 0
232     var width/ebx: (addr int) <- get screen, width
233     {
234       compare x, *width
235       break-if->=
236       var g/eax: grapheme <- screen-grapheme-at screen, x, y
237       {
238         compare g, 0
239         break-if-=
240         compare g, 0x20/space
241         break-if-=
242         return 0/false
243       }
244       x <- increment
245       loop
246     }
247     y <- increment
248     loop
249   }
250   return 1/true
251 }
252 
253 fn clear-rect _screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
254   var screen/esi: (addr screen) <- copy _screen
255   {
256     compare screen, 0
257     break-if-!=
258     clear-rect-on-real-screen xmin, ymin, xmax, ymax, background-color
259     return
260   }
261   # fake screen
262   set-cursor-position screen, 0, 0
263   var y/eax: int <- copy ymin
264   var ymax/ecx: int <- copy ymax
265   {
266     compare y, ymax
267     break-if->=
268     var x/edx: int <- copy xmin
269     var xmax/ebx: int <- copy xmax
270     {
271       compare x, xmax
272       break-if->=
273       draw-code-point screen, 0x20/space, x, y, 0/fg, background-color
274       x <- increment
275       loop
276     }
277     y <- increment
278     loop
279   }
280   set-cursor-position screen, 0, 0
281 }
282 
283 # there's no grapheme that guarantees to cover every pixel, so we'll bump down
284 # to pixels for a real screen
285 fn clear-real-screen {
286   var y/eax: int <- copy 0
287   {
288     compare y, 0x300/screen-height=768
289     break-if->=
290     var x/edx: int <- copy 0
291     {
292       compare x, 0x400/screen-width=1024
293       break-if->=
294       pixel-on-real-screen x, y, 0/color=black
295       x <- increment
296       loop
297     }
298     y <- increment
299     loop
300   }
301 }
302 
303 fn clear-rect-on-real-screen xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
304   var y/eax: int <- copy ymin
305   y <- shift-left 4/log-font-height
306   var ymax/ecx: int <- copy ymax
307   ymax <- shift-left 4/log-font-height
308   {
309     compare y, ymax
310     break-if->=
311     var x/edx: int <- copy xmin
312     x <- shift-left 3/log-font-width
313     var xmax/ebx: int <- copy xmax
314     xmax <- shift-left 3/log-font-width
315     {
316       compare x, xmax
317       break-if->=
318       pixel-on-real-screen x, y, background-color
319       x <- increment
320       loop
321     }
322     y <- increment
323     loop
324   }
325 }
326 
327 fn screen-grapheme-at _screen: (addr screen), x: int, y: int -> _/eax: grapheme {
328   var screen/esi: (addr screen) <- copy _screen
329   var idx/ecx: int <- screen-cell-index screen, x, y
330   var result/eax: grapheme <- screen-grapheme-at-idx screen, idx
331   return result
332 }
333 
334 fn screen-grapheme-at-idx _screen: (addr screen), idx-on-stack: int -> _/eax: grapheme {
335   var screen/esi: (addr screen) <- copy _screen
336   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
337   var data/eax: (addr array screen-cell) <- lookup *data-ah
338   var idx/ecx: int <- copy idx-on-stack
339   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
340   var cell/eax: (addr screen-cell) <- index data, offset
341   var src/eax: (addr grapheme) <- get cell, data
342   return *src
343 }
344 
345 fn screen-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
346   var screen/esi: (addr screen) <- copy _screen
347   var idx/ecx: int <- screen-cell-index screen, x, y
348   var result/eax: int <- screen-color-at-idx screen, idx
349   return result
350 }
351 
352 fn screen-color-at-idx _screen: (addr screen), idx-on-stack: int -> _/eax: int {
353   var screen/esi: (addr screen) <- copy _screen
354   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
355   var data/eax: (addr array screen-cell) <- lookup *data-ah
356   var idx/ecx: int <- copy idx-on-stack
357   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
358   var cell/eax: (addr screen-cell) <- index data, offset
359   var src/eax: (addr int) <- get cell, color
360   var result/eax: int <- copy *src
361   return result
362 }
363 
364 fn screen-background-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
365   var screen/esi: (addr screen) <- copy _screen
366   var idx/ecx: int <- screen-cell-index screen, x, y
367   var result/eax: int <- screen-background-color-at-idx screen, idx
368   return result
369 }
370 
371 fn screen-background-color-at-idx _screen: (addr screen), idx-on-stack: int -> _/eax: int {
372   var screen/esi: (addr screen) <- copy _screen
373   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
374   var data/eax: (addr array screen-cell) <- lookup *data-ah
375   var idx/ecx: int <- copy idx-on-stack
376   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
377   var cell/eax: (addr screen-cell) <- index data, offset
378   var src/eax: (addr int) <- get cell, background-color
379   var result/eax: int <- copy *src
380   return result
381 }