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 <- subtract 8  # font-width
 13   set-cursor-position screen, cursor-x, cursor-y
 14 }
 15 
 16 fn cursor-right screen: (addr screen) {
 17   var cursor-x/eax: int <- copy 0
 18   var cursor-y/ecx: int <- copy 0
 19   cursor-x, cursor-y <- cursor-position screen
 20   compare cursor-x, 0x400  # screen-width
 21   {
 22     break-if-<
 23     return
 24   }
 25   cursor-x <- add 8  # font-width
 26   set-cursor-position screen, cursor-x, cursor-y
 27 }
 28 
 29 fn cursor-up screen: (addr screen) {
 30   var cursor-x/eax: int <- copy 0
 31   var cursor-y/ecx: int <- copy 0
 32   cursor-x, cursor-y <- cursor-position screen
 33   compare cursor-y, 0
 34   {
 35     break-if->
 36     return
 37   }
 38   cursor-y <- subtract 0x10  # screen-height
 39   set-cursor-position screen, cursor-x, cursor-y
 40 }
 41 
 42 fn cursor-down screen: (addr screen) {
 43   var cursor-x/eax: int <- copy 0
 44   var cursor-y/ecx: int <- copy 0
 45   cursor-x, cursor-y <- cursor-position screen
 46   compare cursor-y, 0x300  # screen-height
 47   {
 48     break-if-<
 49     return
 50   }
 51   cursor-y <- add 0x10  # screen-height
 52   set-cursor-position screen, cursor-x, cursor-y
 53 }
 54 
 55 fn draw-grapheme-at-cursor screen: (addr screen), g: grapheme, color: int {
 56   var cursor-x/eax: int <- copy 0
 57   var cursor-y/ecx: int <- copy 0
 58   cursor-x, cursor-y <- cursor-position screen
 59   draw-grapheme screen, g, cursor-x, cursor-y, color
 60 }
 61 
 62 # draw a single line of text from x, y to xmax
 63 # return the next 'x' coordinate
 64 # if there isn't enough space, return 0 without modifying the screen
 65 fn draw-text-rightward screen: (addr screen), text: (addr array byte), x: int, xmax: int, y: int, color: int -> _/eax: int {
 66   var stream-storage: (stream byte 0x100)
 67   var stream/esi: (addr stream byte) <- address stream-storage
 68   write stream, text
 69   # check if we have enough space
 70   var xcurr/ecx: int <- copy x
 71   {
 72     compare xcurr, xmax
 73     break-if->
 74     var g/eax: grapheme <- read-grapheme stream
 75     compare g, 0xffffffff  # end-of-file
 76     break-if-=
 77     xcurr <- add 8  # font-width
 78     loop
 79   }
 80   compare xcurr, xmax
 81   {
 82     break-if-<=
 83     return 0
 84   }
 85   # we do; actually draw
 86   rewind-stream stream
 87   xcurr <- copy x
 88   {
 89     var g/eax: grapheme <- read-grapheme stream
 90     compare g, 0xffffffff  # end-of-file
 91     break-if-=
 92     draw-grapheme screen, g, xcurr, y, color
 93     xcurr <- add 8  # font-width
 94     loop
 95   }
 96   set-cursor-position screen, xcurr, y
 97   return xcurr
 98 }
 99 
100 fn draw-text-rightward-from-cursor screen: (addr screen), text: (addr array byte), xmax: int, color: int -> _/eax: int {
101   var cursor-x/eax: int <- copy 0
102   var cursor-y/ecx: int <- copy 0
103   cursor-x, cursor-y <- cursor-position screen
104   var result/eax: int <- draw-text-rightward screen, text, cursor-x, xmax, cursor-y, color
105   return result
106 }
107 
108 # draw text in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
109 # return the next (x, y) coordinate in raster order where drawing stopped
110 # that way the caller can draw more if given the same min and max bounding-box.
111 # if there isn't enough space, return 0 without modifying the screen
112 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 {
113   var stream-storage: (stream byte 0x100)
114   var stream/esi: (addr stream byte) <- address stream-storage
115   write stream, text
116   # check if we have enough space
117   var xcurr/edx: int <- copy x
118   var ycurr/ecx: int <- copy y
119   {
120     compare ycurr, ymax
121     break-if->=
122     var g/eax: grapheme <- read-grapheme stream
123     compare g, 0xffffffff  # end-of-file
124     break-if-=
125     xcurr <- add 8  # font-width
126     compare xcurr, xmax
127     {
128       break-if-<
129       xcurr <- copy xmin
130       ycurr <- add 0x10  # font-height
131     }
132     loop
133   }
134   compare ycurr, ymax
135   {
136     break-if-<
137     return 0, 0
138   }
139   # we do; actually draw
140   rewind-stream stream
141   xcurr <- copy x
142   ycurr <- copy y
143   {
144     var g/eax: grapheme <- read-grapheme stream
145     compare g, 0xffffffff  # end-of-file
146     break-if-=
147     draw-grapheme screen, g, xcurr, ycurr, color
148     xcurr <- add 8  # font-width
149     compare xcurr, xmax
150     {
151       break-if-<
152       xcurr <- copy xmin
153       ycurr <- add 0x10  # font-height
154     }
155     loop
156   }
157   set-cursor-position screen, xcurr, ycurr
158   return xcurr, ycurr
159 }
160 
161 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 {
162   var cursor-x/eax: int <- copy 0
163   var cursor-y/ecx: int <- copy 0
164   cursor-x, cursor-y <- draw-text-wrapping-right-then-down screen, text, 0, 0, 0x400, 0x300, x, y, color  # 1024, 768
165   return cursor-x, cursor-y
166 }
167 
168 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 -> _/eax: int, _/ecx: int {
169   var cursor-x/eax: int <- copy 0
170   var cursor-y/ecx: int <- copy 0
171   cursor-x, cursor-y <- cursor-position screen
172   var end-x/edx: int <- copy cursor-x
173   end-x <- add 8  # font-width
174   compare end-x, xmax
175   {
176     break-if-<
177     cursor-x <- copy xmin
178     cursor-y <- add 0x10  # font-height
179   }
180   cursor-x, cursor-y <- draw-text-wrapping-right-then-down screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color
181   return cursor-x, cursor-y
182 }
183 
184 fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int -> _/eax: int, _/ecx: int {
185   var cursor-x/eax: int <- copy 0
186   var cursor-y/ecx: int <- copy 0
187   cursor-x, cursor-y <- draw-text-wrapping-right-then-down-from-cursor screen, text, 0, 0, 0x400, 0x300, color  # 1024, 768
188   return cursor-x, cursor-y
189 }
190 
191 ## Text direction: down then right
192 
193 # draw a single line of text vertically from x, y to ymax
194 # return the next 'y' coordinate
195 # if there isn't enough space, return 0 without modifying the screen
196 fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y: int, ymax: int, color: int -> _/eax: int {
197   var stream-storage: (stream byte 0x100)
198   var stream/esi: (addr stream byte) <- address stream-storage
199   write stream, text
200   # check if we have enough space
201   var ycurr/ecx: int <- copy y
202   {
203     compare ycurr, ymax
204     break-if->
205     var g/eax: grapheme <- read-grapheme stream
206     compare g, 0xffffffff  # end-of-file
207     break-if-=
208     ycurr <- add 0x10  # font-height
209     loop
210   }
211   compare ycurr, ymax
212   {
213     break-if-<=
214     return 0
215   }
216   # we do; actually draw
217   rewind-stream stream
218   ycurr <- copy y
219   {
220     var g/eax: grapheme <- read-grapheme stream
221     compare g, 0xffffffff  # end-of-file
222     break-if-=
223     draw-grapheme screen, g, x, ycurr, color
224     ycurr <- add 0x10  # font-height
225     loop
226   }
227   set-cursor-position screen, x, ycurr
228   return ycurr
229 }
230 
231 fn draw-text-downward-from-cursor screen: (addr screen), text: (addr array byte), ymax: int, color: int -> _/eax: int {
232   var cursor-x/eax: int <- copy 0
233   var cursor-y/ecx: int <- copy 0
234   cursor-x, cursor-y <- cursor-position screen
235   var result/eax: int <- draw-text-downward screen, text, cursor-x, cursor-y, ymax, color
236   return result
237 }
238 
239 # draw text down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
240 # return the next (x, y) coordinate in raster order where drawing stopped
241 # that way the caller can draw more if given the same min and max bounding-box.
242 # if there isn't enough space, return 0 without modifying the screen
243 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 {
244   var stream-storage: (stream byte 0x100)
245   var stream/esi: (addr stream byte) <- address stream-storage
246   write stream, text
247   # check if we have enough space
248   var xcurr/edx: int <- copy x
249   var ycurr/ecx: int <- copy y
250   {
251     compare xcurr, xmax
252     break-if->=
253     var g/eax: grapheme <- read-grapheme stream
254     compare g, 0xffffffff  # end-of-file
255     break-if-=
256     ycurr <- add 0x10  # font-height
257     compare ycurr, ymax
258     {
259       break-if-<
260       xcurr <- add 8  # font-width
261       ycurr <- copy ymin
262     }
263     loop
264   }
265   compare xcurr, xmax
266   {
267     break-if-<
268     return 0, 0
269   }
270   # we do; actually draw
271   rewind-stream stream
272   xcurr <- copy x
273   ycurr <- copy y
274   {
275     var g/eax: grapheme <- read-grapheme stream
276     compare g, 0xffffffff  # end-of-file
277     break-if-=
278     draw-grapheme screen, g, xcurr, ycurr, color
279     ycurr <- add 0x10  # font-height
280     compare ycurr, ymax
281     {
282       break-if-<
283       xcurr <- add 8  # font-width
284       ycurr <- copy ymin
285     }
286     loop
287   }
288   set-cursor-position screen, xcurr, ycurr
289   return xcurr, ycurr
290 }
291 
292 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 {
293   var cursor-x/eax: int <- copy 0
294   var cursor-y/ecx: int <- copy 0
295   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, 0, 0, 0x400, 0x300, x, y, color  # 1024, 768
296   return cursor-x, cursor-y
297 }
298 
299 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 -> _/eax: int, _/ecx: int {
300   var cursor-x/eax: int <- copy 0
301   var cursor-y/ecx: int <- copy 0
302   cursor-x, cursor-y <- cursor-position screen
303   var end-y/edx: int <- copy cursor-y
304   end-y <- add 0x10  # font-height
305   compare end-y, ymax
306   {
307     break-if-<
308     cursor-x <- add 8  # font-width
309     cursor-y <- copy ymin
310   }
311   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color
312   return cursor-x, cursor-y
313 }
314 
315 fn draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int -> _/eax: int, _/ecx: int {
316   var cursor-x/eax: int <- copy 0
317   var cursor-y/ecx: int <- copy 0
318   cursor-x, cursor-y <- draw-text-wrapping-down-then-right-from-cursor screen, text, 0, 0, 0x400, 0x300, color  # 1024, 768
319   return cursor-x, cursor-y
320 }