https://github.com/akkartik/mu/blob/main/baremetal/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 }
 21 
 22 fn initialize-screen screen: (addr screen), width: int, height: int {
 23   var screen-addr/esi: (addr screen) <- copy screen
 24   var tmp/eax: int <- copy 0
 25   var dest/edi: (addr int) <- copy 0
 26   # screen->width = width
 27   dest <- get screen-addr, width
 28   tmp <- copy width
 29   copy-to *dest, tmp
 30   # screen->height = height
 31   dest <- get screen-addr, height
 32   tmp <- copy height
 33   copy-to *dest, tmp
 34   # screen->data = new screen-cell[width*height]
 35   {
 36     var data-addr/edi: (addr handle array screen-cell) <- get screen-addr, data
 37     tmp <- multiply width
 38     populate data-addr, tmp
 39   }
 40   # screen->cursor-x = 0
 41   dest <- get screen-addr, cursor-x
 42   copy-to *dest, 0
 43   # screen->cursor-y = 0
 44   dest <- get screen-addr, cursor-y
 45   copy-to *dest, 0
 46 }
 47 
 48 # in graphemes
 49 fn screen-size screen: (addr screen) -> _/eax: int, _/ecx: int {
 50   var width/eax: int <- copy 0
 51   var height/ecx: int <- copy 0
 52   compare screen, 0
 53   {
 54     break-if-!=
 55     return 0x80, 0x30  # 128x48
 56   }
 57   # fake screen
 58   var screen-addr/esi: (addr screen) <- copy screen
 59   var tmp/edx: (addr int) <- get screen-addr, width
 60   width <- copy *tmp
 61   tmp <- get screen-addr, height
 62   height <- copy *tmp
 63   return width, height
 64 }
 65 
 66 # testable screen primitive
 67 # background color isn't configurable yet
 68 fn draw-grapheme screen: (addr screen), g: grapheme, x: int, y: int, color: int {
 69   {
 70     compare screen, 0
 71     break-if-!=
 72     draw-grapheme-on-real-screen g, x, y, color, 0
 73     return
 74   }
 75   # fake screen
 76   var screen-addr/esi: (addr screen) <- copy screen
 77   var idx/ecx: int <- screen-cell-index screen-addr, x, y
 78   var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, 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 color2/edx: grapheme <- copy color
 87   copy-to *dest-color, color2
 88 }
 89 
 90 fn screen-cell-index screen-on-stack: (addr screen), x: int, y: int -> _/ecx: int {
 91   var screen/esi: (addr screen) <- copy screen-on-stack
 92   var height-addr/eax: (addr int) <- get screen, height
 93   var result/ecx: int <- copy y
 94   result <- multiply *height-addr
 95   result <- add x
 96   return result
 97 }
 98 
 99 fn cursor-position screen: (addr screen) -> _/eax: int, _/ecx: int {
100   {
101     compare screen, 0
102     break-if-!=
103     var x/eax: int <- copy 0
104     var y/ecx: int <- copy 0
105     x, y <- cursor-position-on-real-screen
106     return x, y
107   }
108   # fake screen
109   var screen-addr/esi: (addr screen) <- copy screen
110   var cursor-x-addr/eax: (addr int) <- get screen-addr, cursor-x
111   var cursor-y-addr/ecx: (addr int) <- get screen-addr, cursor-y
112   return *cursor-x-addr, *cursor-y-addr
113 }
114 
115 fn set-cursor-position screen: (addr screen), x: int, y: int {
116   {
117     compare screen, 0
118     break-if-!=
119     set-cursor-position-on-real-screen x, y
120     return
121   }
122   # fake screen
123   var screen-addr/esi: (addr screen) <- copy screen
124   # ignore x < 0
125   {
126     compare x, 0
127     break-if->=
128     return
129   }
130   # ignore x >= width
131   {
132     var width-addr/eax: (addr int) <- get screen-addr, width
133     var width/eax: int <- copy *width-addr
134     compare x, width
135     break-if-<=
136     return
137   }
138   # ignore y < 0
139   {
140     compare y, 0
141     break-if->=
142     return
143   }
144   # ignore y >= height
145   {
146     var height-addr/eax: (addr int) <- get screen-addr, height
147     var height/eax: int <- copy *height-addr
148     compare y, height
149     break-if-<
150     return
151   }
152   # screen->cursor-x = x
153   var dest/edi: (addr int) <- get screen-addr, cursor-x
154   var src/eax: int <- copy x
155   copy-to *dest, src
156   # screen->cursor-y = y
157   dest <- get screen-addr, cursor-y
158   src <- copy y
159   copy-to *dest, src
160 }
161 
162 fn show-cursor screen: (addr screen), g: grapheme {
163   {
164     compare screen, 0
165     break-if-!=
166     show-cursor-on-real-screen g
167     return
168   }
169   # fake screen
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   draw-grapheme screen, g, cursor-x, cursor-y, 0  # cursor color not tracked for fake screen
174 }
175 
176 fn clear-screen screen: (addr screen) {
177   {
178     compare screen, 0
179     break-if-!=
180     clear-real-screen
181     return
182   }
183   # fake screen
184   var space/edi: grapheme <- copy 0x20
185   set-cursor-position screen, 0, 0
186   var screen-addr/esi: (addr screen) <- copy screen
187   var y/eax: int <- copy 1
188   var height/ecx: (addr int) <- get screen-addr, height
189   {
190     compare y, *height
191     break-if->
192     var x/edx: int <- copy 1
193     var width/ebx: (addr int) <- get screen-addr, width
194     {
195       compare x, *width
196       break-if->
197       draw-grapheme screen, space, x, y, 0  # color=black
198       x <- increment
199       loop
200     }
201     y <- increment
202     loop
203   }
204   set-cursor-position screen, 0, 0
205 }
206 
207 # there's no grapheme that guarantees to cover every pixel, so we'll bump down
208 # to pixels for a real screen
209 fn clear-real-screen {
210   var y/eax: int <- copy 0
211   {
212     compare y, 0x300  # screen-height = 768
213     break-if->=
214     var x/edx: int <- copy 0
215     {
216       compare x, 0x400  # screen-width = 1024
217       break-if->=
218       pixel-on-real-screen x, y, 0  # black
219       x <- increment
220       loop
221     }
222     y <- increment
223     loop
224   }
225 }
226 
227 fn screen-grapheme-at screen-on-stack: (addr screen), x: int, y: int -> _/eax: grapheme {
228   var screen-addr/esi: (addr screen) <- copy screen-on-stack
229   var idx/ecx: int <- screen-cell-index screen-addr, x, y
230   var result/eax: grapheme <- screen-grapheme-at-idx screen-addr, idx
231   return result
232 }
233 
234 fn screen-grapheme-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: grapheme {
235   var screen-addr/esi: (addr screen) <- copy screen-on-stack
236   var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data
237   var data/eax: (addr array screen-cell) <- lookup *data-ah
238   var idx/ecx: int <- copy idx-on-stack
239   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
240   var cell/eax: (addr screen-cell) <- index data, offset
241   var src/eax: (addr grapheme) <- get cell, data
242   return *src
243 }
244 
245 fn screen-color-at screen-on-stack: (addr screen), x: int, y: int -> _/eax: int {
246   var screen-addr/esi: (addr screen) <- copy screen-on-stack
247   var idx/ecx: int <- screen-cell-index screen-addr, x, y
248   var result/eax: int <- screen-color-at-idx screen-addr, idx
249   return result
250 }
251 
252 fn screen-color-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: int {
253   var screen-addr/esi: (addr screen) <- copy screen-on-stack
254   var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data
255   var data/eax: (addr array screen-cell) <- lookup *data-ah
256   var idx/ecx: int <- copy idx-on-stack
257   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
258   var cell/eax: (addr screen-cell) <- index data, offset
259   var src/eax: (addr int) <- get cell, color
260   var result/eax: int <- copy *src
261   return result
262 }