about summary refs log tree commit diff stats
path: root/apps/tile/surface.mu
blob: 9f520a3c73f61d4681222558fb7cfb5bd29a5a92 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# A surface is a large 2-D grid that you can only see a subset of through the
# screen.
# Imagine a pin going through both surface and screen. As we update the
# surface contents, the pinned point stays fixed, providing a sense of
# stability.

type surface {
  screen: (handle screen)
  data: (handle array screen-cell)
  nrows: int
  ncols: int
  screen-nrows: int
  screen-ncols: int
  pin-row: int  # 1-indexed
  pin-col: int  # 1-indexed
  pin-screen-row: int  # 1-indexed
  pin-screen-col: int  # 1-indexed
}

# intended mostly for tests; could be slow
fn initialize-surface-with _self: (addr surface), in: (addr array byte) {
  var self/esi: (addr surface) <- copy _self
  # fill in nrows, ncols
  var nrows/ecx: int <- num-lines in
  var dest/eax: (addr int) <- get self, nrows
  copy-to *dest, nrows
  var ncols/edx: int <- first-line-length in  # assume all lines are the same length
  dest <- get self, ncols
  copy-to *dest, ncols
  # fill in data
  var len/ecx: int <- copy nrows
  len <- multiply ncols
  var out/edi: (addr surface) <- copy _self
  var data/eax: (addr handle array screen-cell) <- get out, data
  populate data, len
  var data-addr/eax: (addr array screen-cell) <- lookup *data
  fill-in data-addr, in
  # fill in screen-nrows, screen-ncols
  {
    var screen-ah/eax: (addr handle screen) <- get self, screen
    var _screen-addr/eax: (addr screen) <- lookup *screen-ah
    var screen-addr/edi: (addr screen) <- copy _screen-addr
    var nrows/eax: int <- copy 0
    var ncols/ecx: int <- copy 0
    nrows, ncols <- screen-size screen-addr
    var dest/edi: (addr int) <- get self, screen-nrows
    copy-to *dest, nrows
    dest <- get self, screen-ncols
    copy-to *dest, ncols
  }
}

fn pin-surface-at _self: (addr surface), r: int, c: int {
  var self/esi: (addr surface) <- copy _self
  var dest/ecx: (addr int) <- get self, pin-row
  var tmp/eax: int <- copy r
  copy-to *dest, tmp
  dest <- get self, pin-col
  tmp <- copy c
  copy-to *dest, tmp
}

fn pin-surface-to _self: (addr surface), sr: int, sc: int {
  var self/esi: (addr surface) <- copy _self
  var dest/ecx: (addr int) <- get self, pin-screen-row
  var tmp/eax: int <- copy sr
  copy-to *dest, tmp
  dest <- get self, pin-screen-col
  tmp <- copy sc
  copy-to *dest, tmp
}

fn render-surface _self: (addr surface) {
#?   print-string-to-real-screen "render-surface\n"
  var self/esi: (addr surface) <- copy _self
  # clear screen
  var screen-ah/eax: (addr handle screen) <- get self, screen
  var screen/eax: (addr screen) <- lookup *screen-ah
  clear-screen screen
  #
  var nrows/edx: (addr int) <- get self, screen-nrows
  var ncols/ebx: (addr int) <- get self, screen-ncols
  var screen-row/ecx: int <- copy 1
  {
    compare screen-row, *nrows
    break-if->
    var screen-col/eax: int <- copy 1
    {
      compare screen-col, *ncols
      break-if->
#?       print-string-to-real-screen "X"
      print-surface-cell-at self, screen-row, screen-col
      screen-col <- increment
      loop
    }
#?     print-string-to-real-screen "\n"
    screen-row <- increment
    loop
  }
}

fn print-surface-cell-at _self: (addr surface), screen-row: int, screen-col: int {
$print-surface-cell-at:body: {
  var self/esi: (addr surface) <- copy _self
  var row/ecx: int <- screen-row-to-surface self, screen-row
  var col/edx: int <- screen-col-to-surface self, screen-col
  var data-ah/edi: (addr handle array screen-cell) <- get self, data
  var _data-addr/eax: (addr array screen-cell) <- lookup *data-ah
  var data-addr/edi: (addr array screen-cell) <- copy _data-addr
  var idx/eax: int <- surface-screen-cell-index self, row, col
  # if out of bounds, print ' '
  compare idx, 0
  {
    break-if->=
    var space/ecx: grapheme <- copy 0x20
    var screen-ah/edi: (addr handle screen) <- get self, screen
    var screen/eax: (addr screen) <- lookup *screen-ah
    print-grapheme screen, space
    break $print-surface-cell-at:body
  }
  # otherwise print the appropriate screen-cell
  var offset/ecx: (offset screen-cell) <- compute-offset data-addr, idx
  var src/ecx: (addr screen-cell) <- index data-addr, offset
  var screen-ah/edi: (addr handle screen) <- get self, screen
  var screen/eax: (addr screen) <- lookup *screen-ah
  print-screen-cell screen, src
}
}

# print a cell with all its formatting at the cursor location
fn print-screen-cell screen: (addr screen), _cell: (addr screen-cell) {
  var cell/esi: (addr screen-cell) <- copy _cell
  reset-formatting screen
  var fg/eax: (addr int) <- get cell, color
  var bg/ecx: (addr int) <- get cell, background-color
  start-color screen, *fg, *bg
  var tmp/eax: (addr boolean) <- get cell, bold?
  {
    compare *tmp, 0
    break-if-=
    start-bold screen
  }
  {
    tmp <- get cell, underline?
    compare *tmp, 0
    break-if-=
    start-underline screen
  }
  {
    tmp <- get cell, reverse?
    compare *tmp, 0
    break-if-=
    start-reverse-video screen
  }
  {
    tmp <- get cell, blink?
    compare *tmp, 0
    break-if-=
    start-blinking screen
  }
  var g/eax: (addr grapheme) <- get cell, data
  print-grapheme screen, *g
#?   var g2/eax: grapheme <- copy *g
#?   var g3/eax: int <- copy g2
#?   print-int32-hex-to-real-screen g3
#?   print-string-to-real-screen "\n"
}

fn surface-screen-cell-index _self: (addr surface), row: int, col: int -> _/eax: int {
  var self/esi: (addr surface) <- copy _self
#?   print-int32-hex-to-real-screen row
#?   print-string-to-real-screen ", "
#?   print-int32-hex-to-real-screen col
#?   print-string-to-real-screen "\n"
  var result/eax: int <- copy -1
  {
    compare row, 1
    break-if-<
    compare col, 1
    break-if-<
    var nrows-addr/ecx: (addr int) <- get self, nrows
    var nrows/ecx: int <- copy *nrows-addr
    compare row, nrows
    break-if->
    var ncols-addr/ecx: (addr int) <- get self, ncols
    var ncols/ecx: int <- copy *ncols-addr
    compare col, ncols
    break-if->
  #?   print-string-to-real-screen "!\n"
    result <- copy row
    result <- subtract 1
    result <- multiply ncols
    result <- add col
    result <- subtract 1
  }
  return result
}

fn screen-row-to-surface _self: (addr surface), screen-row: int -> _/ecx: int {
  var self/esi: (addr surface) <- copy _self
  var result/ecx: int <- copy screen-row
  var tmp/eax: (addr int) <- get self, pin-row
  result <- add *tmp
  tmp <- get self, pin-screen-row
  result <- subtract *tmp
  return result
}

fn max _a: int, b: int -> _/eax: int {
  var a/eax: int <- copy _a
  compare a, b
  {
    break-if->
    return b
  }
  return a
}

fn min _a: int, b: int -> _/eax: int {
  var a/eax: int <- copy _a
  compare a, b
  {
    break-if->
    return a
  }
  return b
}

fn screen-col-to-surface _self: (addr surface), screen-col: int -> _/edx: int {
  var self/esi: (addr surface) <- copy _self
  var result/edx: int <- copy screen-col
  var tmp/eax: (addr int) <- get self, pin-col
  result <- add *tmp
  tmp <- get self, pin-screen-col
  result <- subtract *tmp
  return result
}

fn surface-row-to-screen _self: (addr surface), row: int -> _/ecx: int {
  var self/esi: (addr surface) <- copy _self
  var result/ecx: int <- copy row
  var tmp/eax: (addr int) <- get self, pin-screen-row
  result <- add *tmp
  tmp <- get self, pin-row
  result <- subtract *tmp
  return result
}

fn surface-col-to-screen _self: (addr surface), col: int -> _/edx: int {
  var self/esi: (addr surface) <- copy _self
  var result/edx: int <- copy col
  var tmp/eax: (addr int) <- get self, pin-screen-col
  result <- add *tmp
  tmp <- get self, pin-col
  result <- subtract *tmp
  return result
}

# assumes last line doesn't end in '\n'
fn num-lines in: (addr array byte) -> _/ecx: int {
  var s: (stream byte 0x100)
  var s-addr/esi: (addr stream byte) <- address s
  write s-addr, in
  var result/ecx: int <- copy 1
  {
    var done?/eax: boolean <- stream-empty? s-addr
    compare done?, 0  # false
    break-if-!=
    var g/eax: grapheme <- read-grapheme s-addr
    compare g, 0xa  # newline
    loop-if-!=
    result <- increment
    loop
  }
  return result
}

fn first-line-length in: (addr array byte) -> _/edx: int {
  var s: (stream byte 0x100)
  var s-addr/esi: (addr stream byte) <- address s
  write s-addr, in
  var result/edx: int <- copy 0
  {
    var done?/eax: boolean <- stream-empty? s-addr
    compare done?, 0  # false
    break-if-!=
    var g/eax: grapheme <- read-grapheme s-addr
    compare g, 0xa  # newline
    break-if-=
    result <- increment
    loop
  }
  return result
}

fn fill-in _out: (addr array screen-cell), in: (addr array byte) {
  var s: (stream byte 0x100)
  var out/edi: (addr array screen-cell) <- copy _out
  var s-addr/esi: (addr stream byte) <- address s
  write s-addr, in
  var idx/ecx: int <- copy 0
  {
    var done?/eax: boolean <- stream-empty? s-addr
    compare done?, 0  # false
    break-if-!=
    var g/eax: grapheme <- read-grapheme s-addr
    compare g, 0xa  # newline
    loop-if-=
    var offset/edx: (offset screen-cell) <- compute-offset out, idx
    var dest/edx: (addr screen-cell) <- index out, offset
    var dest2/edx: (addr grapheme) <- get dest, data
    copy-to *dest2, g
    idx <- increment
    loop
  }
}

# pin (1, 1) to (1, 1) on screen
fn test-surface-pin-at-origin {
  var s: surface
  var s-addr/esi: (addr surface) <- address s
  # surface contents are a fixed grid with 8 rows and 6 columns
  # (strip vowels second time around to break vertical alignment of letters)
  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
  pin-surface-at s-addr, 1, 1  # surface row and column
  pin-surface-to s-addr, 1, 1  # screen row and column
  render-surface s-addr
  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
  var screen-addr/eax: (addr screen) <- lookup *screen-ah
  check-screen-row screen-addr, 1, "abcd", "F - test-surface-pin-at-origin"
  check-screen-row screen-addr, 2, "ghij", "F - test-surface-pin-at-origin"
  check-screen-row screen-addr, 3, "mnop", "F - test-surface-pin-at-origin"
}

# pin (1, 1) to (2, 1) on screen; screen goes past edge of the universe
fn test-surface-pin-2 {
  var s: surface
  var s-addr/esi: (addr surface) <- address s
  # surface contents are a fixed grid with 8 rows and 6 columns
  # (strip vowels second time around to break vertical alignment of letters)
  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
  pin-surface-at s-addr, 1, 1  # surface row and column
  pin-surface-to s-addr, 2, 1  # screen row and column
  render-surface s-addr
  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
  var screen-addr/eax: (addr screen) <- lookup *screen-ah
  # surface edge reached (should seldom happen in the app)
  check-screen-row screen-addr, 1, "    ", "F - test-surface-pin-2"
  check-screen-row screen-addr, 2, "abcd", "F - test-surface-pin-2"
  check-screen-row screen-addr, 3, "ghij", "F - test-surface-pin-2"
}

# pin (2, 1) to (1, 1) on screen
fn test-surface-pin-3 {
  var s: surface
  var s-addr/esi: (addr surface) <- address s
  # surface contents are a fixed grid with 8 rows and 6 columns
  # (strip vowels second time around to break vertical alignment of letters)
  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
  pin-surface-at s-addr, 2, 1  # surface row and column
  pin-surface-to s-addr, 1, 1  # screen row and column
  render-surface s-addr
  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
  var screen-addr/eax: (addr screen) <- lookup *screen-ah
  check-screen-row screen-addr, 1, "ghij", "F - test-surface-pin-3"
  check-screen-row screen-addr, 2, "mnop", "F - test-surface-pin-3"
  check-screen-row screen-addr, 3, "stuv", "F - test-surface-pin-3"
}

# pin (1, 1) to (1, 2) on screen; screen goes past edge of the universe
fn test-surface-pin-4 {
  var s: surface
  var s-addr/esi: (addr surface) <- address s
  # surface contents are a fixed grid with 8 rows and 6 columns
  # (strip vowels second time around to break vertical alignment of letters)
  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
  pin-surface-at s-addr, 1, 1  # surface row and column
  pin-surface-to s-addr, 1, 2  # screen row and column
  render-surface s-addr
  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
  var screen-addr/eax: (addr screen) <- lookup *screen-ah
  # surface edge reached (should seldom happen in the app)
  check-screen-row screen-addr, 1, " abc", "F - test-surface-pin-4"
  check-screen-row screen-addr, 2, " ghi", "F - test-surface-pin-4"
  check-screen-row screen-addr, 3, " mno", "F - test-surface-pin-4"
}

# pin (1, 2) to (1, 1) on screen
fn test-surface-pin-5 {
  var s: surface
  var s-addr/esi: (addr surface) <- address s
  # surface contents are a fixed grid with 8 rows and 6 columns
  # (strip vowels second time around to break vertical alignment of letters)
  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
  pin-surface-at s-addr, 1, 2  # surface row and column
  pin-surface-to s-addr, 1, 1  # screen row and column
  render-surface s-addr
  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
  var screen-addr/eax: (addr screen) <- lookup *screen-ah
  check-screen-row screen-addr, 1, "bcde", "F - test-surface-pin-5"
  check-screen-row screen-addr, 2, "hijk", "F - test-surface-pin-5"
  check-screen-row screen-addr, 3, "nopq", "F - test-surface-pin-5"
}

fn initialize-surface-with-fake-screen _self: (addr surface), nrows: int, ncols: int, in: (addr array byte) {
  var self/esi: (addr surface) <- copy _self
  # fill in screen
  var screen-ah/eax: (addr handle screen) <- get self, screen
  allocate screen-ah
  var screen-addr/eax: (addr screen) <- lookup *screen-ah
  initialize-screen screen-addr, nrows, ncols
  # fill in everything else
  initialize-surface-with self, in
}