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 {
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 }
182 
183 fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int {
184   draw-text-wrapping-right-then-down-from-cursor screen, text, 0, 0, 0x400, 0x300, color  # 1024, 768
185 }
186 
187 ## Text direction: down then right
188 
189 # draw a single line of text vertically from x, y to ymax
190 # return the next 'y' coordinate
191 # if there isn't enough space, return 0 without modifying the screen
192 fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y: int, ymax: int, color: int -> _/eax: int {
193   var stream-storage: (stream byte 0x100)
194   var stream/esi: (addr stream byte) <- address stream-storage
195   write stream, text
196   # check if we have enough space
197   var ycurr/ecx: int <- copy y
198   {
199     compare ycurr, ymax
200     break-if->
201     var g/eax: grapheme <- read-grapheme stream
202     compare g, 0xffffffff  # end-of-file
203     break-if-=
204     ycurr <- add 0x10  # font-height
205     loop
206   }
207   compare ycurr, ymax
208   {
209     break-if-<=
210     return 0
211   }
212   # we do; actually draw
213   rewind-stream stream
214   ycurr <- copy y
215   {
216     var g/eax: grapheme <- read-grapheme stream
217     compare g, 0xffffffff  # end-of-file
218     break-if-=
219     draw-grapheme screen, g, x, ycurr, color
220     ycurr <- add 0x10  # font-height
221     loop
222   }
223   set-cursor-position screen, x, ycurr
224   return ycurr
225 }
226 
227 fn draw-text-downward-from-cursor screen: (addr screen), text: (addr array byte), ymax: int, color: int {
228   var cursor-x/eax: int <- copy 0
229   var cursor-y/ecx: int <- copy 0
230   cursor-x, cursor-y <- cursor-position screen
231   var result/eax: int <- draw-text-downward screen, text, cursor-x, cursor-y, ymax, color
232 }
233 
234 # draw text down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
235 # return the next (x, y) coordinate in raster order where drawing stopped
236 # that way the caller can draw more if given the same min and max bounding-box.
237 # if there isn't enough space, return 0 without modifying the screen
238 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 {
239   var stream-storage: (stream byte 0x100)
240   var stream/esi: (addr stream byte) <- address stream-storage
241   write stream, text
242   # check if we have enough space
243   var xcurr/edx: int <- copy x
244   var ycurr/ecx: int <- copy y
245   {
246     compare xcurr, xmax
247     break-if->=
248     var g/eax: grapheme <- read-grapheme stream
249     compare g, 0xffffffff  # end-of-file
250     break-if-=
251     ycurr <- add 0x10  # font-height
252     compare ycurr, ymax
253     {
254       break-if-<
255       xcurr <- add 8  # font-width
256       ycurr <- copy ymin
257     }
258     loop
259   }
260   compare xcurr, xmax
261   {
262     break-if-<
263     return 0, 0
264   }
265   # we do; actually draw
266   rewind-stream stream
267   xcurr <- copy x
268   ycurr <- copy y
269   {
270     var g/eax: grapheme <- read-grapheme stream
271     compare g, 0xffffffff  # end-of-file
272     break-if-=
273     draw-grapheme screen, g, xcurr, ycurr, color
274     ycurr <- add 0x10  # font-height
275     compare ycurr, ymax
276     {
277       break-if-<
278       xcurr <- add 8  # font-width
279       ycurr <- copy ymin
280     }
281     loop
282   }
283   set-cursor-position screen, xcurr, ycurr
284   return xcurr, ycurr
285 }
286 
287 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 {
288   var cursor-x/eax: int <- copy 0
289   var cursor-y/ecx: int <- copy 0
290   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, 0, 0, 0x400, 0x300, x, y, color  # 1024, 768
291   return cursor-x, cursor-y
292 }
293 
294 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 {
295   var cursor-x/eax: int <- copy 0
296   var cursor-y/ecx: int <- copy 0
297   cursor-x, cursor-y <- cursor-position screen
298   var end-y/edx: int <- copy cursor-y
299   end-y <- add 0x10  # font-height
300   compare end-y, ymax
301   {
302     break-if-<
303     cursor-x <- add 8  # font-width
304     cursor-y <- copy ymin
305   }
306   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color
307 }
308 
309 fn draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int {
310   draw-text-wrapping-down-then-right-from-cursor screen, text, 0, 0, 0x400, 0x300, color  # 1024, 768
311 }