https://github.com/akkartik/mu/blob/main/apps/tile/surface.mu
  1 # A surface is a large 2-D grid that you can only see a subset of through the
  2 # screen.
  3 # Imagine a pin going through both surface and screen. As we update the
  4 # surface contents, the pinned point stays fixed, providing a sense of
  5 # stability.
  6 
  7 type surface {
  8   screen: (handle screen)
  9   data: (handle array screen-cell)
 10   nrows: int
 11   ncols: int
 12   screen-nrows: int
 13   screen-ncols: int
 14   pin-row: int  # 1-indexed
 15   pin-col: int  # 1-indexed
 16   pin-screen-row: int  # 1-indexed
 17   pin-screen-col: int  # 1-indexed
 18 }
 19 
 20 # intended mostly for tests; could be slow
 21 fn initialize-surface-with _self: (addr surface), in: (addr array byte) {
 22   var self/esi: (addr surface) <- copy _self
 23   # fill in nrows, ncols
 24   var nrows/ecx: int <- num-lines in
 25   var dest/eax: (addr int) <- get self, nrows
 26   copy-to *dest, nrows
 27   var ncols/edx: int <- first-line-length in  # assume all lines are the same length
 28   dest <- get self, ncols
 29   copy-to *dest, ncols
 30   # fill in data
 31   var len/ecx: int <- copy nrows
 32   len <- multiply ncols
 33   var out/edi: (addr surface) <- copy _self
 34   var data/eax: (addr handle array screen-cell) <- get out, data
 35   populate data, len
 36   var data-addr/eax: (addr array screen-cell) <- lookup *data
 37   fill-in data-addr, in
 38   # fill in screen-nrows, screen-ncols
 39   {
 40     var screen-ah/eax: (addr handle screen) <- get self, screen
 41     var _screen-addr/eax: (addr screen) <- lookup *screen-ah
 42     var screen-addr/edi: (addr screen) <- copy _screen-addr
 43     var nrows/eax: int <- copy 0
 44     var ncols/ecx: int <- copy 0
 45     nrows, ncols <- screen-size screen-addr
 46     var dest/edi: (addr int) <- get self, screen-nrows
 47     copy-to *dest, nrows
 48     dest <- get self, screen-ncols
 49     copy-to *dest, ncols
 50   }
 51 }
 52 
 53 fn pin-surface-at _self: (addr surface), r: int, c: int {
 54   var self/esi: (addr surface) <- copy _self
 55   var dest/ecx: (addr int) <- get self, pin-row
 56   var tmp/eax: int <- copy r
 57   copy-to *dest, tmp
 58   dest <- get self, pin-col
 59   tmp <- copy c
 60   copy-to *dest, tmp
 61 }
 62 
 63 fn pin-surface-to _self: (addr surface), sr: int, sc: int {
 64   var self/esi: (addr surface) <- copy _self
 65   var dest/ecx: (addr int) <- get self, pin-screen-row
 66   var tmp/eax: int <- copy sr
 67   copy-to *dest, tmp
 68   dest <- get self, pin-screen-col
 69   tmp <- copy sc
 70   copy-to *dest, tmp
 71 }
 72 
 73 fn render-surface _self: (addr surface) {
 74 #?   print-string-to-real-screen "render-surface\n"
 75   var self/esi: (addr surface) <- copy _self
 76   # clear screen
 77   var screen-ah/eax: (addr handle screen) <- get self, screen
 78   var screen/eax: (addr screen) <- lookup *screen-ah
 79   clear-screen screen
 80   #
 81   var nrows/edx: (addr int) <- get self, screen-nrows
 82   var ncols/ebx: (addr int) <- get self, screen-ncols
 83   var screen-row/ecx: int <- copy 1
 84   {
 85     compare screen-row, *nrows
 86     break-if->
 87     var screen-col/eax: int <- copy 1
 88     {
 89       compare screen-col, *ncols
 90       break-if->
 91 #?       print-string-to-real-screen "X"
 92       print-surface-cell-at self, screen-row, screen-col
 93       screen-col <- increment
 94       loop
 95     }
 96 #?     print-string-to-real-screen "\n"
 97     screen-row <- increment
 98     loop
 99   }
100 }
101 
102 fn print-surface-cell-at _self: (addr surface), screen-row: int, screen-col: int {
103   var self/esi: (addr surface) <- copy _self
104   var row/ecx: int <- screen-row-to-surface self, screen-row
105   var col/edx: int <- screen-col-to-surface self, screen-col
106   var data-ah/edi: (addr handle array screen-cell) <- get self, data
107   var _data-addr/eax: (addr array screen-cell) <- lookup *data-ah
108   var data-addr/edi: (addr array screen-cell) <- copy _data-addr
109   var idx/eax: int <- surface-screen-cell-index self, row, col
110   # if out of bounds, print ' '
111   compare idx, 0
112   {
113     break-if->=
114     var space/ecx: grapheme <- copy 0x20
115     var screen-ah/edi: (addr handle screen) <- get self, screen
116     var screen/eax: (addr screen) <- lookup *screen-ah
117     print-grapheme screen, space
118     return
119   }
120   # otherwise print the appropriate screen-cell
121   var offset/ecx: (offset screen-cell) <- compute-offset data-addr, idx
122   var src/ecx: (addr screen-cell) <- index data-addr, offset
123   var screen-ah/edi: (addr handle screen) <- get self, screen
124   var screen/eax: (addr screen) <- lookup *screen-ah
125   print-screen-cell screen, src
126 }
127 
128 # print a cell with all its formatting at the cursor location
129 fn print-screen-cell screen: (addr screen), _cell: (addr screen-cell) {
130   var cell/esi: (addr screen-cell) <- copy _cell
131   reset-formatting screen
132   var fg/eax: (addr int) <- get cell, color
133   var bg/ecx: (addr int) <- get cell, background-color
134   start-color screen, *fg, *bg
135   var tmp/eax: (addr boolean) <- get cell, bold?
136   {
137     compare *tmp, 0
138     break-if-=
139     start-bold screen
140   }
141   {
142     tmp <- get cell, underline?
143     compare *tmp, 0
144     break-if-=
145     start-underline screen
146   }
147   {
148     tmp <- get cell, reverse?
149     compare *tmp, 0
150     break-if-=
151     start-reverse-video screen
152   }
153   {
154     tmp <- get cell, blink?
155     compare *tmp, 0
156     break-if-=
157     start-blinking screen
158   }
159   var g/eax: (addr grapheme) <- get cell, data
160   print-grapheme screen, *g
161 #?   var g2/eax: grapheme <- copy *g
162 #?   var g3/eax: int <- copy g2
163 #?   print-int32-hex-to-real-screen g3
164 #?   print-string-to-real-screen "\n"
165 }
166 
167 fn surface-screen-cell-index _self: (addr surface), row: int, col: int -> _/eax: int {
168   var self/esi: (addr surface) <- copy _self
169 #?   print-int32-hex-to-real-screen row
170 #?   print-string-to-real-screen ", "
171 #?   print-int32-hex-to-real-screen col
172 #?   print-string-to-real-screen "\n"
173   var result/eax: int <- copy -1
174   {
175     compare row, 1
176     break-if-<
177     compare col, 1
178     break-if-<
179     var nrows-addr/ecx: (addr int) <- get self, nrows
180     var nrows/ecx: int <- copy *nrows-addr
181     compare row, nrows
182     break-if->
183     var ncols-addr/ecx: (addr int) <- get self, ncols
184     var ncols/ecx: int <- copy *ncols-addr
185     compare col, ncols
186     break-if->
187   #?   print-string-to-real-screen "!\n"
188     result <- copy row
189     result <- subtract 1
190     result <- multiply ncols
191     result <- add col
192     result <- subtract 1
193   }
194   return result
195 }
196 
197 fn screen-row-to-surface _self: (addr surface), screen-row: int -> _/ecx: int {
198   var self/esi: (addr surface) <- copy _self
199   var result/ecx: int <- copy screen-row
200   var tmp/eax: (addr int) <- get self, pin-row
201   result <- add *tmp
202   tmp <- get self, pin-screen-row
203   result <- subtract *tmp
204   return result
205 }
206 
207 fn max _a: int, b: int -> _/eax: int {
208   var a/eax: int <- copy _a
209   compare a, b
210   {
211     break-if->
212     return b
213   }
214   return a
215 }
216 
217 fn min _a: int, b: int -> _/eax: int {
218   var a/eax: int <- copy _a
219   compare a, b
220   {
221     break-if->
222     return a
223   }
224   return b
225 }
226 
227 fn screen-col-to-surface _self: (addr surface), screen-col: int -> _/edx: int {
228   var self/esi: (addr surface) <- copy _self
229   var result/edx: int <- copy screen-col
230   var tmp/eax: (addr int) <- get self, pin-col
231   result <- add *tmp
232   tmp <- get self, pin-screen-col
233   result <- subtract *tmp
234   return result
235 }
236 
237 fn surface-row-to-screen _self: (addr surface), row: int -> _/ecx: int {
238   var self/esi: (addr surface) <- copy _self
239   var result/ecx: int <- copy row
240   var tmp/eax: (addr int) <- get self, pin-screen-row
241   result <- add *tmp
242   tmp <- get self, pin-row
243   result <- subtract *tmp
244   return result
245 }
246 
247 fn surface-col-to-screen _self: (addr surface), col: int -> _/edx: int {
248   var self/esi: (addr surface) <- copy _self
249   var result/edx: int <- copy col
250   var tmp/eax: (addr int) <- get self, pin-screen-col
251   result <- add *tmp
252   tmp <- get self, pin-col
253   result <- subtract *tmp
254   return result
255 }
256 
257 # assumes last line doesn't end in '\n'
258 fn num-lines in: (addr array byte) -> _/ecx: int {
259   var s: (stream byte 0x100)
260   var s-addr/esi: (addr stream byte) <- address s
261   write s-addr, in
262   var result/ecx: int <- copy 1
263   {
264     var done?/eax: boolean <- stream-empty? s-addr
265     compare done?, 0  # false
266     break-if-!=
267     var g/eax: grapheme <- read-grapheme s-addr
268     compare g, 0xa  # newline
269     loop-if-!=
270     result <- increment
271     loop
272   }
273   return result
274 }
275 
276 fn first-line-length in: (addr array byte) -> _/edx: int {
277   var s: (stream byte 0x100)
278   var s-addr/esi: (addr stream byte) <- address s
279   write s-addr, in
280   var result/edx: int <- copy 0
281   {
282     var done?/eax: boolean <- stream-empty? s-addr
283     compare done?, 0  # false
284     break-if-!=
285     var g/eax: grapheme <- read-grapheme s-addr
286     compare g, 0xa  # newline
287     break-if-=
288     result <- increment
289     loop
290   }
291   return result
292 }
293 
294 fn fill-in _out: (addr array screen-cell), in: (addr array byte) {
295   var s: (stream byte 0x100)
296   var out/edi: (addr array screen-cell) <- copy _out
297   var s-addr/esi: (addr stream byte) <- address s
298   write s-addr, in
299   var idx/ecx: int <- copy 0
300   {
301     var done?/eax: boolean <- stream-empty? s-addr
302     compare done?, 0  # false
303     break-if-!=
304     var g/eax: grapheme <- read-grapheme s-addr
305     compare g, 0xa  # newline
306     loop-if-=
307     var offset/edx: (offset screen-cell) <- compute-offset out, idx
308     var dest/edx: (addr screen-cell) <- index out, offset
309     var dest2/edx: (addr grapheme) <- get dest, data
310     copy-to *dest2, g
311     idx <- increment
312     loop
313   }
314 }
315 
316 # pin (1, 1) to (1, 1) on screen
317 fn test-surface-pin-at-origin {
318   var s: surface
319   var s-addr/esi: (addr surface) <- address s
320   # surface contents are a fixed grid with 8 rows and 6 columns
321   # (strip vowels second time around to break vertical alignment of letters)
322   initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
323   pin-surface-at s-addr, 1, 1  # surface row and column
324   pin-surface-to s-addr, 1, 1  # screen row and column
325   render-surface s-addr
326   var screen-ah/eax: (addr handle screen) <- get s-addr, screen
327   var screen-addr/eax: (addr screen) <- lookup *screen-ah
328   check-screen-row screen-addr, 1, "abcd", "F - test-surface-pin-at-origin"
329   check-screen-row screen-addr, 2, "ghij", "F - test-surface-pin-at-origin"
330   check-screen-row screen-addr, 3, "mnop", "F - test-surface-pin-at-origin"
331 }
332 
333 # pin (1, 1) to (2, 1) on screen; screen goes past edge of the universe
334 fn test-surface-pin-2 {
335   var s: surface
336   var s-addr/esi: (addr surface) <- address s
337   # surface contents are a fixed grid with 8 rows and 6 columns
338   # (strip vowels second time around to break vertical alignment of letters)
339   initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
340   pin-surface-at s-addr, 1, 1  # surface row and column
341   pin-surface-to s-addr, 2, 1  # screen row and column
342   render-surface s-addr
343   var screen-ah/eax: (addr handle screen) <- get s-addr, screen
344   var screen-addr/eax: (addr screen) <- lookup *screen-ah
345   # surface edge reached (should seldom happen in the app)
346   check-screen-row screen-addr, 1, "    ", "F - test-surface-pin-2"
347   check-screen-row screen-addr, 2, "abcd", "F - test-surface-pin-2"
348   check-screen-row screen-addr, 3, "ghij", "F - test-surface-pin-2"
349 }
350 
351 # pin (2, 1) to (1, 1) on screen
352 fn test-surface-pin-3 {
353   var s: surface
354   var s-addr/esi: (addr surface) <- address s
355   # surface contents are a fixed grid with 8 rows and 6 columns
356   # (strip vowels second time around to break vertical alignment of letters)
357   initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
358   pin-surface-at s-addr, 2, 1  # surface row and column
359   pin-surface-to s-addr, 1, 1  # screen row and column
360   render-surface s-addr
361   var screen-ah/eax: (addr handle screen) <- get s-addr, screen
362   var screen-addr/eax: (addr screen) <- lookup *screen-ah
363   check-screen-row screen-addr, 1, "ghij", "F - test-surface-pin-3"
364   check-screen-row screen-addr, 2, "mnop", "F - test-surface-pin-3"
365   check-screen-row screen-addr, 3, "stuv", "F - test-surface-pin-3"
366 }
367 
368 # pin (1, 1) to (1, 2) on screen; screen goes past edge of the universe
369 fn test-surface-pin-4 {
370   var s: surface
371   var s-addr/esi: (addr surface) <- address s
372   # surface contents are a fixed grid with 8 rows and 6 columns
373   # (strip vowels second time around to break vertical alignment of letters)
374   initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
375   pin-surface-at s-addr, 1, 1  # surface row and column
376   pin-surface-to s-addr, 1, 2  # screen row and column
377   render-surface s-addr
378   var screen-ah/eax: (addr handle screen) <- get s-addr, screen
379   var screen-addr/eax: (addr screen) <- lookup *screen-ah
380   # surface edge reached (should seldom happen in the app)
381   check-screen-row screen-addr, 1, " abc", "F - test-surface-pin-4"
382   check-screen-row screen-addr, 2, " ghi", "F - test-surface-pin-4"
383   check-screen-row screen-addr, 3, " mno", "F - test-surface-pin-4"
384 }
385 
386 # pin (1, 2) to (1, 1) on screen
387 fn test-surface-pin-5 {
388   var s: surface
389   var s-addr/esi: (addr surface) <- address s
390   # surface contents are a fixed grid with 8 rows and 6 columns
391   # (strip vowels second time around to break vertical alignment of letters)
392   initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
393   pin-surface-at s-addr, 1, 2  # surface row and column
394   pin-surface-to s-addr, 1, 1  # screen row and column
395   render-surface s-addr
396   var screen-ah/eax: (addr handle screen) <- get s-addr, screen
397   var screen-addr/eax: (addr screen) <- lookup *screen-ah
398   check-screen-row screen-addr, 1, "bcde", "F - test-surface-pin-5"
399   check-screen-row screen-addr, 2, "hijk", "F - test-surface-pin-5"
400   check-screen-row screen-addr, 3, "nopq", "F - test-surface-pin-5"
401 }
402 
403 fn initialize-surface-with-fake-screen _self: (addr surface), nrows: int, ncols: int, in: (addr array byte) {
404   var self/esi: (addr surface) <- copy _self
405   # fill in screen
406   var screen-ah/eax: (addr handle screen) <- get self, screen
407   allocate screen-ah
408   var screen-addr/eax: (addr screen) <- lookup *screen-ah
409   initialize-screen screen-addr, nrows, ncols
410   # fill in everything else
411   initialize-surface-with self, in
412 }