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