https://github.com/akkartik/mu/blob/main/baremetal/501draw-text.mu
  1 # some primitives for moving the cursor without making assumptions about
  2 # raster order
  3 fn move-cursor-left screen: (addr screen) {
  4   var cursor-x/eax: int <- copy 0
  5   var cursor-y/ecx: int <- copy 0
  6   cursor-x, cursor-y <- cursor-position screen
  7   compare cursor-x, 0
  8   {
  9     break-if->
 10     return
 11   }
 12   cursor-x <- decrement
 13   set-cursor-position screen, cursor-x, cursor-y
 14 }
 15 
 16 fn move-cursor-right screen: (addr screen) {
 17   var _width/eax: int <- copy 0
 18   var dummy/ecx: int <- copy 0
 19   _width, dummy <- screen-size screen
 20   var limit/edx: int <- copy _width
 21   limit <- decrement
 22   var cursor-x/eax: int <- copy 0
 23   var cursor-y/ecx: int <- copy 0
 24   cursor-x, cursor-y <- cursor-position screen
 25   compare cursor-x, limit
 26   {
 27     break-if-<
 28     return
 29   }
 30   cursor-x <- increment
 31   set-cursor-position screen, cursor-x, cursor-y
 32 }
 33 
 34 fn move-cursor-up screen: (addr screen) {
 35   var cursor-x/eax: int <- copy 0
 36   var cursor-y/ecx: int <- copy 0
 37   cursor-x, cursor-y <- cursor-position screen
 38   compare cursor-y, 0
 39   {
 40     break-if->
 41     return
 42   }
 43   cursor-y <- decrement
 44   set-cursor-position screen, cursor-x, cursor-y
 45 }
 46 
 47 fn move-cursor-down screen: (addr screen) {
 48   var dummy/eax: int <- copy 0
 49   var _height/ecx: int <- copy 0
 50   dummy, _height <- screen-size screen
 51   var limit/edx: int <- copy _height
 52   limit <- decrement
 53   var cursor-x/eax: int <- copy 0
 54   var cursor-y/ecx: int <- copy 0
 55   cursor-x, cursor-y <- cursor-position screen
 56   compare cursor-y, limit
 57   {
 58     break-if-<
 59     return
 60   }
 61   cursor-y <- increment
 62   set-cursor-position screen, cursor-x, cursor-y
 63 }
 64 
 65 fn draw-grapheme-at-cursor screen: (addr screen), g: grapheme, color: int, background-color: int {
 66   var cursor-x/eax: int <- copy 0
 67   var cursor-y/ecx: int <- copy 0
 68   cursor-x, cursor-y <- cursor-position screen
 69   draw-grapheme screen, g, cursor-x, cursor-y, color, background-color
 70 }
 71 
 72 # we can't really render non-ASCII yet, but when we do we'll be ready
 73 fn draw-code-point-at-cursor screen: (addr screen), c: code-point, color: int, background-color: int {
 74   var g/eax: grapheme <- copy c
 75   draw-grapheme-at-cursor screen, g, color, background-color
 76 }
 77 
 78 # draw a single line of text from x, y to xmax
 79 # return the next 'x' coordinate
 80 # if there isn't enough space, return 0 without modifying the screen
 81 fn draw-text-rightward screen: (addr screen), text: (addr array byte), x: int, xmax: int, y: int, color: int, background-color: int -> _/eax: int {
 82   var stream-storage: (stream byte 0x100)
 83   var stream/esi: (addr stream byte) <- address stream-storage
 84   write stream, text
 85   # check if we have enough space
 86   var xcurr/ecx: int <- copy x
 87   {
 88     compare xcurr, xmax
 89     break-if->
 90     var g/eax: grapheme <- read-grapheme stream
 91     compare g, 0xffffffff/end-of-file
 92     break-if-=
 93     xcurr <- increment
 94     loop
 95   }
 96   compare xcurr, xmax
 97   {
 98     break-if-<=
 99     return 0
100   }
101   # we do; actually draw
102   rewind-stream stream
103   xcurr <- copy x
104   {
105     var g/eax: grapheme <- read-grapheme stream
106     compare g, 0xffffffff/end-of-file
107     break-if-=
108     draw-grapheme screen, g, xcurr, y, color, background-color
109     xcurr <- increment
110     loop
111   }
112   set-cursor-position screen, xcurr, y
113   return xcurr
114 }
115 
116 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 {
117   var width/eax: int <- copy 0
118   var height/ecx: int <- copy 0
119   width, height <- screen-size screen
120   var result/eax: int <- draw-text-rightward screen, text, x, width, y, color, background-color
121   return result
122 }
123 
124 fn draw-text-rightward-from-cursor screen: (addr screen), text: (addr array byte), xmax: int, color: int, background-color: int -> _/eax: int {
125   var cursor-x/eax: int <- copy 0
126   var cursor-y/ecx: int <- copy 0
127   cursor-x, cursor-y <- cursor-position screen
128   var result/eax: int <- draw-text-rightward screen, text, cursor-x, xmax, cursor-y, color, background-color
129   return result
130 }
131 
132 # draw text in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
133 # return the next (x, y) coordinate in raster order where drawing stopped
134 # that way the caller can draw more if given the same min and max bounding-box.
135 # if there isn't enough space, return 0 without modifying the screen
136 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 {
137   var stream-storage: (stream byte 0x100)
138   var stream/esi: (addr stream byte) <- address stream-storage
139   write stream, text
140   # check if we have enough space
141   var xcurr/edx: int <- copy x
142   var ycurr/ecx: int <- copy y
143   {
144     compare ycurr, ymax
145     break-if->=
146     var g/eax: grapheme <- read-grapheme stream
147     compare g, 0xffffffff/end-of-file
148     break-if-=
149     xcurr <- increment
150     compare xcurr, xmax
151     {
152       break-if-<
153       xcurr <- copy xmin
154       ycurr <- increment
155     }
156     loop
157   }
158   compare ycurr, ymax
159   {
160     break-if-<
161     return 0, 0
162   }
163   # we do; actually draw
164   rewind-stream stream
165   xcurr <- copy x
166   ycurr <- copy y
167   {
168     var g/eax: grapheme <- read-grapheme stream
169     compare g, 0xffffffff/end-of-file
170     break-if-=
171     draw-grapheme screen, g, xcurr, ycurr, color, background-color
172     xcurr <- increment
173     compare xcurr, xmax
174     {
175       break-if-<
176       xcurr <- copy xmin
177       ycurr <- increment
178     }
179     loop
180   }
181   set-cursor-position screen, xcurr, ycurr
182   return xcurr, ycurr
183 }
184 
185 fn move-cursor-rightward-and-downward screen: (addr screen), xmin: int, xmax: int {
186   var cursor-x/eax: int <- copy 0
187   var cursor-y/ecx: int <- copy 0
188   cursor-x, cursor-y <- cursor-position screen
189   cursor-x <- increment
190   compare cursor-x, xmax
191   {
192     break-if-<
193     cursor-x <- copy xmin
194     cursor-y <- increment
195   }
196   set-cursor-position screen, cursor-x, cursor-y
197 }
198 
199 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 {
200   var x2/eax: int <- copy 0
201   var y2/ecx: int <- copy 0
202   x2, y2 <- screen-size screen  # width, height
203   x2, y2 <- draw-text-wrapping-right-then-down screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
204   return x2, y2  # cursor-x, cursor-y
205 }
206 
207 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 {
208   var cursor-x/eax: int <- copy 0
209   var cursor-y/ecx: int <- copy 0
210   cursor-x, cursor-y <- cursor-position screen
211   var end-x/edx: int <- copy cursor-x
212   end-x <- increment
213   compare end-x, xmax
214   {
215     break-if-<
216     cursor-x <- copy xmin
217     cursor-y <- increment
218   }
219   cursor-x, cursor-y <- draw-text-wrapping-right-then-down screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
220 }
221 
222 fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
223   var width/eax: int <- copy 0
224   var height/ecx: int <- copy 0
225   width, height <- screen-size screen
226   draw-text-wrapping-right-then-down-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
227 }
228 
229 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 {
230   var stream-storage: (stream byte 0x100)
231   var stream/esi: (addr stream byte) <- address stream-storage
232   write-int32-hex stream, n
233   # check if we have enough space
234   var xcurr/edx: int <- copy x
235   var ycurr/ecx: int <- copy y
236   {
237     compare ycurr, ymax
238     break-if->=
239     var g/eax: grapheme <- read-grapheme stream
240     compare g, 0xffffffff/end-of-file
241     break-if-=
242     xcurr <- increment
243     compare xcurr, xmax
244     {
245       break-if-<
246       xcurr <- copy xmin
247       ycurr <- increment
248     }
249     loop
250   }
251   compare ycurr, ymax
252   {
253     break-if-<
254     return 0, 0
255   }
256   # we do; actually draw
257   rewind-stream stream
258   xcurr <- copy x
259   ycurr <- copy y
260   {
261     var g/eax: grapheme <- read-grapheme stream
262     compare g, 0xffffffff/end-of-file
263     break-if-=
264     draw-grapheme screen, g, xcurr, ycurr, color, background-color
265     xcurr <- increment
266     compare xcurr, xmax
267     {
268       break-if-<
269       xcurr <- copy xmin
270       ycurr <- increment
271     }
272     loop
273   }
274   set-cursor-position screen, xcurr, ycurr
275   return xcurr, ycurr
276 }
277 
278 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 {
279   var x2/eax: int <- copy 0
280   var y2/ecx: int <- copy 0
281   x2, y2 <- screen-size screen  # width, height
282   x2, y2 <- draw-int32-hex-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
283   return x2, y2  # cursor-x, cursor-y
284 }
285 
286 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 {
287   var cursor-x/eax: int <- copy 0
288   var cursor-y/ecx: int <- copy 0
289   cursor-x, cursor-y <- cursor-position screen
290   var end-x/edx: int <- copy cursor-x
291   end-x <- increment
292   compare end-x, xmax
293   {
294     break-if-<
295     cursor-x <- copy xmin
296     cursor-y <- increment
297   }
298   cursor-x, cursor-y <- draw-int32-hex-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
299 }
300 
301 fn draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
302   var width/eax: int <- copy 0
303   var height/ecx: int <- copy 0
304   width, height <- screen-size screen
305   draw-int32-hex-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
306 }
307 
308 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 {
309   var stream-storage: (stream byte 0x100)
310   var stream/esi: (addr stream byte) <- address stream-storage
311   write-int32-decimal stream, n
312   # check if we have enough space
313   var xcurr/edx: int <- copy x
314   var ycurr/ecx: int <- copy y
315   {
316     compare ycurr, ymax
317     break-if->=
318     var g/eax: grapheme <- read-grapheme stream
319     compare g, 0xffffffff/end-of-file
320     break-if-=
321     xcurr <- increment
322     compare xcurr, xmax
323     {
324       break-if-<
325       xcurr <- copy xmin
326       ycurr <- increment
327     }
328     loop
329   }
330   compare ycurr, ymax
331   {
332     break-if-<
333     return 0, 0
334   }
335   # we do; actually draw
336   rewind-stream stream
337   xcurr <- copy x
338   ycurr <- copy y
339   {
340     var g/eax: grapheme <- read-grapheme stream
341     compare g, 0xffffffff/end-of-file
342     break-if-=
343     draw-grapheme screen, g, xcurr, ycurr, color, background-color
344     xcurr <- increment
345     compare xcurr, xmax
346     {
347       break-if-<
348       xcurr <- copy xmin
349       ycurr <- increment
350     }
351     loop
352   }
353   set-cursor-position screen, xcurr, ycurr
354   return xcurr, ycurr
355 }
356 
357 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 {
358   var x2/eax: int <- copy 0
359   var y2/ecx: int <- copy 0
360   x2, y2 <- screen-size screen  # width, height
361   x2, y2 <- draw-int32-decimal-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
362   return x2, y2  # cursor-x, cursor-y
363 }
364 
365 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 {
366   var cursor-x/eax: int <- copy 0
367   var cursor-y/ecx: int <- copy 0
368   cursor-x, cursor-y <- cursor-position screen
369   var end-x/edx: int <- copy cursor-x
370   end-x <- increment
371   compare end-x, xmax
372   {
373     break-if-<
374     cursor-x <- copy xmin
375     cursor-y <- increment
376   }
377   cursor-x, cursor-y <- draw-int32-decimal-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
378 }
379 
380 fn draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
381   var width/eax: int <- copy 0
382   var height/ecx: int <- copy 0
383   width, height <- screen-size screen
384   draw-int32-decimal-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
385 }
386 
387 ## Text direction: down then right
388 
389 # draw a single line of text vertically from x, y to ymax
390 # return the next 'y' coordinate
391 # if there isn't enough space, return 0 without modifying the screen
392 fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
393   var stream-storage: (stream byte 0x100)
394   var stream/esi: (addr stream byte) <- address stream-storage
395   write stream, text
396   # check if we have enough space
397   var ycurr/ecx: int <- copy y
398   {
399     compare ycurr, ymax
400     break-if->
401     var g/eax: grapheme <- read-grapheme stream
402     compare g, 0xffffffff/end-of-file
403     break-if-=
404     ycurr <- increment
405     loop
406   }
407   compare ycurr, ymax
408   {
409     break-if-<=
410     return 0
411   }
412   # we do; actually draw
413   rewind-stream stream
414   ycurr <- copy y
415   {
416     var g/eax: grapheme <- read-grapheme stream
417     compare g, 0xffffffff/end-of-file
418     break-if-=
419     draw-grapheme screen, g, x, ycurr, color, background-color
420     ycurr <- increment
421     loop
422   }
423   set-cursor-position screen, x, ycurr
424   return ycurr
425 }
426 
427 fn draw-text-downward-from-cursor screen: (addr screen), text: (addr array byte), ymax: int, color: int, background-color: int {
428   var cursor-x/eax: int <- copy 0
429   var cursor-y/ecx: int <- copy 0
430   cursor-x, cursor-y <- cursor-position screen
431   var result/eax: int <- draw-text-downward screen, text, cursor-x, cursor-y, ymax, color, background-color
432 }
433 
434 # draw text down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
435 # return the next (x, y) coordinate in raster order where drawing stopped
436 # that way the caller can draw more if given the same min and max bounding-box.
437 # if there isn't enough space, return 0 without modifying the screen
438 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 {
439   var stream-storage: (stream byte 0x100)
440   var stream/esi: (addr stream byte) <- address stream-storage
441   write stream, text
442   # check if we have enough space
443   var xcurr/edx: int <- copy x
444   var ycurr/ecx: int <- copy y
445   {
446     compare xcurr, xmax
447     break-if->=
448     var g/eax: grapheme <- read-grapheme stream
449     compare g, 0xffffffff/end-of-file
450     break-if-=
451     ycurr <- increment
452     compare ycurr, ymax
453     {
454       break-if-<
455       xcurr <- increment
456       ycurr <- copy ymin
457     }
458     loop
459   }
460   compare xcurr, xmax
461   {
462     break-if-<
463     return 0, 0
464   }
465   # we do; actually draw
466   rewind-stream stream
467   xcurr <- copy x
468   ycurr <- copy y
469   {
470     var g/eax: grapheme <- read-grapheme stream
471     compare g, 0xffffffff/end-of-file
472     break-if-=
473     draw-grapheme screen, g, xcurr, ycurr, color, background-color
474     ycurr <- increment
475     compare ycurr, ymax
476     {
477       break-if-<
478       xcurr <- increment
479       ycurr <- copy ymin
480     }
481     loop
482   }
483   set-cursor-position screen, xcurr, ycurr
484   return xcurr, ycurr
485 }
486 
487 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 {
488   var x2/eax: int <- copy 0
489   var y2/ecx: int <- copy 0
490   x2, y2 <- screen-size screen  # width, height
491   x2, y2 <- draw-text-wrapping-down-then-right screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
492   return x2, y2  # cursor-x, cursor-y
493 }
494 
495 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 {
496   var cursor-x/eax: int <- copy 0
497   var cursor-y/ecx: int <- copy 0
498   cursor-x, cursor-y <- cursor-position screen
499   var end-y/edx: int <- copy cursor-y
500   end-y <- increment
501   compare end-y, ymax
502   {
503     break-if-<
504     cursor-x <- increment
505     cursor-y <- copy ymin
506   }
507   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
508 }
509 
510 fn draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
511   var width/eax: int <- copy 0
512   var height/ecx: int <- copy 0
513   width, height <- screen-size screen
514   draw-text-wrapping-down-then-right-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
515 }
516 
517 # hacky error-handling
518 # just go into an infinite loop
519 fn abort e: (addr array byte) {
520   var dummy1/eax: int <- copy 0
521   var dummy2/ecx: int <- copy 0
522   dummy1, dummy2 <- draw-text-wrapping-right-then-down-over-full-screen 0/screen, e, 0/x, 0x2f/y, 0xf/fg=white, 0xc/bg=red
523   {
524     loop
525   }
526 }