https://github.com/akkartik/mu/blob/main/baremetal/shell/value.mu
  1 # todo: turn this into a sum type
  2 type value {
  3   type: int
  4   number-data: float  # if type = 0
  5   text-data: (handle array byte)  # if type = 1
  6   array-data: (handle array value)  # if type = 2
  7   boolean-data: boolean  # if type = 3
  8 }
  9 
 10 # top-level? is a hack just for numbers
 11 # we'll eventually need to return a y coordinate as well to render 2D values
 12 fn render-value screen: (addr screen), _val: (addr value), x: int, y: int, top-level?: boolean -> _/eax: int {
 13   var val/esi: (addr value) <- copy _val
 14   var val-type/ecx: (addr int) <- get val, type
 15   compare *val-type, 1/string
 16   {
 17     break-if-!=
 18     var val-ah/eax: (addr handle array byte) <- get val, text-data
 19     var _val-string/eax: (addr array byte) <- lookup *val-ah
 20     var val-string/ecx: (addr array byte) <- copy _val-string
 21     var new-x/eax: int <- render-string screen, val-string, x, y
 22     return new-x
 23   }
 24   compare *val-type, 2/array
 25   {
 26     break-if-!=
 27     var val-ah/eax: (addr handle array value) <- get val, array-data
 28     var _val-array/eax: (addr array value) <- lookup *val-ah
 29     var val-array/edx: (addr array value) <- copy _val-array
 30     var new-x/eax: int <- render-array screen, val-array, x, y
 31     return new-x
 32   }
 33   compare *val-type, 3/boolean
 34   {
 35     break-if-!=
 36     var val/eax: (addr boolean) <- get val, boolean-data
 37     var new-x/eax: int <- render-boolean screen, *val, x, y
 38     return new-x
 39   }
 40   # render ints by default for now
 41   var val-num/eax: (addr float) <- get val, number-data
 42   var new-x/eax: int <- render-number screen, *val-num, x, y, top-level?
 43   return new-x
 44 }
 45 
 46 fn initialize-value-with-integer _self: (addr value), n: int {
 47   var self/esi: (addr value) <- copy _self
 48   var type/eax: (addr int) <- get self, type
 49   copy-to *type, 0/number
 50   var val/xmm0: float <- convert n
 51   var dest/eax: (addr float) <- get self, number-data
 52   copy-to *dest, val
 53 }
 54 
 55 fn initialize-value-with-float _self: (addr value), n: float {
 56   var self/esi: (addr value) <- copy _self
 57   var type/eax: (addr int) <- get self, type
 58   copy-to *type, 0/number
 59   var val/xmm0: float <- copy n
 60   var dest/eax: (addr float) <- get self, number-data
 61   copy-to *dest, val
 62 }
 63 
 64 # synaesthesia
 65 # TODO: right-justify
 66 fn render-number screen: (addr screen), val: float, x: int, y: int, top-level?: boolean -> _/eax: int {
 67   # if we're inside an array, don't color
 68   compare top-level?, 0
 69   {
 70     break-if-!=
 71     var new-x/eax: int <- render-float-decimal screen, val, 3/precision, x, y, 3/fg, 0/bg
 72     return new-x
 73   }
 74   var val-int/eax: int <- convert val
 75   var _bg/eax: int <- hash-color val-int
 76   var bg/ecx: int <- copy _bg
 77   var fg/edx: int <- copy 7
 78   {
 79     compare bg, 2
 80     break-if-!=
 81     fg <- copy 0
 82   }
 83   {
 84     compare bg, 3
 85     break-if-!=
 86     fg <- copy 0
 87   }
 88   {
 89     compare bg, 6
 90     break-if-!=
 91     fg <- copy 0
 92   }
 93   draw-code-point screen, 0x20/space, x, y, fg, bg
 94   increment x
 95   var new-x/eax: int <- render-float-decimal screen, val, 3/precision, x, y, fg, bg
 96   draw-code-point screen, 0x20/space, new-x, y, fg, bg
 97   new-x <- increment
 98   return new-x
 99 }
100 
101 fn hash-color val: int -> _/eax: int {
102   var quotient/eax: int <- copy 0
103   var remainder/edx: int <- copy 0
104   quotient, remainder <- integer-divide val, 7  # assumes that 7 is always the background color
105   return remainder
106 }
107 
108 fn test-render-number {
109   # setup: screen
110   var screen-on-stack: screen
111   var screen/edi: (addr screen) <- address screen-on-stack
112   initialize-screen screen, 0x20, 4
113   # integers render with some padding spaces
114   var new-x/eax: int <- render-number screen, 0/n, 0/x, 0/y, 1/at-top-level
115   check-screen-row screen, 0/y, " 0 ", "F - test-render-number"
116   check-ints-equal new-x, 3, "F - test-render-number: result"
117   # we won't bother testing the background colors; lots of flexibility there
118 }
119 
120 fn initialize-value-with-string _self: (addr value), s: (addr array byte) {
121   var self/esi: (addr value) <- copy _self
122   var type/eax: (addr int) <- get self, type
123   copy-to *type, 1/string
124   var dest/eax: (addr handle array byte) <- get self, text-data
125   copy-array-object s, dest
126 }
127 
128 fn render-string screen: (addr screen), _val: (addr array byte), x: int, y: int -> _/eax: int {
129   var val/esi: (addr array byte) <- copy _val
130   compare val, 0
131   {
132     break-if-!=
133     return x
134   }
135   var orig-len/ecx: int <- length val
136   # truncate to 12 graphemes
137   # TODO: more sophisticated interactive rendering
138   var truncated: (handle array byte)
139   var truncated-ah/eax: (addr handle array byte) <- address truncated
140   substring val, 0, 0xc, truncated-ah
141   var _truncated-string/eax: (addr array byte) <- lookup *truncated-ah
142   var truncated-string/edx: (addr array byte) <- copy _truncated-string
143   var len/ebx: int <- length truncated-string
144   draw-code-point screen, 0x22/double-quote, x, y, 7/fg, 0/bg
145   increment x
146   var new-x/eax: int <- draw-text-rightward-over-full-screen screen, truncated-string, x, y, 7/fg, 0/bg
147   compare len, orig-len
148   {
149     break-if-=
150     new-x <- draw-text-rightward-over-full-screen screen, "...", new-x, y, 7/fg, 0/bg
151   }
152   draw-code-point screen, 0x22/double-quote, new-x, y, 7/fg, 0/bg
153   new-x <- increment
154   return new-x
155 }
156 
157 fn test-render-string {
158   # setup: screen
159   var screen-on-stack: screen
160   var screen/edi: (addr screen) <- address screen-on-stack
161   initialize-screen screen, 0x20, 4
162   # strings render with quotes
163   var new-x/eax: int <- render-string screen, "abc", 0/x, 0/y
164   check-screen-row screen, 0/y, "\"abc\"", "F - test-render-string"
165   check-ints-equal new-x, 5, "F - test-render-string: result"
166 }
167 
168 fn initialize-value-with-array-of-integers _self: (addr value), s: (addr array byte) {
169   # parse s into a temporary array of ints
170   var tmp-storage: (handle array int)
171   var tmp-ah/eax: (addr handle array int) <- address tmp-storage
172   parse-array-of-decimal-ints s, tmp-ah  # leak
173   var _tmp/eax: (addr array int ) <- lookup *tmp-ah
174   var tmp/esi: (addr array int ) <- copy _tmp
175   # load the array into values
176   var self/edi: (addr value) <- copy _self
177   var type/eax: (addr int) <- get self, type
178   copy-to *type, 2/string
179   var dest-array-ah/eax: (addr handle array value) <- get self, array-data
180   var len/ebx: int <- length tmp
181   populate dest-array-ah, len
182   var _dest-array/eax: (addr array value) <- lookup *dest-array-ah
183   var dest-array/edi: (addr array value) <- copy _dest-array
184   var i/eax: int <- copy 0
185   {
186     compare i, len
187     break-if->=
188     var src-addr/ecx: (addr int) <- index tmp, i
189     var src/ecx: int <- copy *src-addr
190     var src-f/xmm0: float <- convert src
191     var dest-offset/edx: (offset value) <- compute-offset dest-array, i
192     var dest-val/edx: (addr value) <- index dest-array, dest-offset
193     var dest/edx: (addr float) <- get dest-val, number-data
194     copy-to *dest, src-f
195     i <- increment
196     loop
197   }
198 }
199 
200 fn render-array screen: (addr screen), _arr: (addr array value), x: int, y: int -> _/eax: int {
201   # don't surround in spaces
202   draw-code-point screen, 0x5b/open-bracket, x, y, 7/fg, 0/bg
203   increment x
204   var arr/esi: (addr array value) <- copy _arr
205   var max/ecx: int <- length arr
206   var i/edx: int <- copy 0
207   var new-x/eax: int <- copy x
208   {
209     compare i, max
210     break-if->=
211     {
212       compare i, 0
213       break-if-=
214       draw-code-point screen, 0x20/space, new-x, y, 7/fg, 0/bg
215       new-x <- increment
216     }
217     var off/ecx: (offset value) <- compute-offset arr, i
218     var x/ecx: (addr value) <- index arr, off
219     new-x <- render-value screen, x, new-x, y, 0/nested
220     i <- increment
221     loop
222   }
223   draw-code-point screen, 0x5d/close-bracket, new-x, y, 7/fg, 0/bg
224   new-x <- increment
225   return new-x
226 }
227 
228 fn test-render-array {
229   # setup: screen
230   var screen-on-stack: screen
231   var screen/edi: (addr screen) <- address screen-on-stack
232   initialize-screen screen, 0x20, 4
233   #
234   var val-storage: value
235   var val/eax: (addr value) <- address val-storage
236   initialize-value-with-array-of-integers val, "0 1 2"
237   var val-array-ah/eax: (addr handle array value) <- get val, array-data
238   var val-array/eax: (addr array value) <- lookup *val-array-ah
239   var new-x/eax: int <- render-array screen, val-array, 0/x, 0/y
240   check-screen-row screen, 0/y, "[0 1 2]", "F - test-render-array"
241   check-ints-equal new-x, 7, "F - test-render-array: result"
242 }
243 
244 fn initialize-value-with-boolean _self: (addr value), _b: boolean {
245   var self/esi: (addr value) <- copy _self
246   var type/eax: (addr int) <- get self, type
247   copy-to *type, 3/boolean
248   var dest/edi: (addr boolean) <- get self, boolean-data
249   var b/esi: boolean <- copy _b
250   copy-to *dest, b
251 }
252 
253 fn render-boolean screen: (addr screen), val: boolean, x: int, y: int -> _/eax: int {
254   var new-x/eax: int <- copy 0
255   compare val, 0/false
256   {
257     break-if-=
258     new-x <- draw-text-rightward-over-full-screen screen, "true", new-x, y, 7/fg, 0/bg
259     return new-x
260   }
261   new-x <- draw-text-rightward-over-full-screen screen, "false", new-x, y, 7/fg, 0/bg
262   return new-x
263 }