about summary refs log tree commit diff stats
path: root/src/liolib.c
blob: b0148e83b4f866672edde8a870cb1fe4588bab8d (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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
/*
** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/


#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define liolib_c
#define LUA_LIB

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "teliva.h"



static int pushresult (lua_State *L, int i, const char *filename) {
  int en = errno;  /* calls to Lua API may change this value */
  if (i) {
    lua_pushboolean(L, 1);
    return 1;
  }
  else {
    lua_pushnil(L);
    if (filename)
      lua_pushfstring(L, "%s: %s", filename, strerror(en));
    else
      lua_pushfstring(L, "%s", strerror(en));
    lua_pushinteger(L, en);
    return 3;
  }
}


static void fileerror (lua_State *L, int arg, const char *filename) {
  lua_pushfstring(L, "%s: %s", filename, strerror(errno));
  luaL_argerror(L, arg, lua_tostring(L, -1));
}


#define tofilep(L)	((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))


static int io_type (lua_State *L) {
  void *ud;
  luaL_checkany(L, 1);
  ud = lua_touserdata(L, 1);
  lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
  if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
    lua_pushnil(L);  /* not a file */
  else if (*((FILE **)ud) == NULL)
    lua_pushliteral(L, "closed file");
  else
    lua_pushliteral(L, "file");
  return 1;
}


static FILE *tofile (lua_State *L) {
  FILE **f = tofilep(L);
  if (*f == NULL)
    luaL_error(L, "attempt to use a closed file");
  return *f;
}



/*
** When creating file handles, always creates a `closed' file handle
** before opening the actual file; so, if there is a memory error, the
** file is not left opened.
*/
static FILE **newfile (lua_State *L) {
  FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
  *pf = NULL;  /* file handle is currently `closed' */
  luaL_getmetatable(L, LUA_FILEHANDLE);
  lua_setmetatable(L, -2);
  return pf;
}


/*
** function to close regular files
*/
static int io_fclose (lua_State *L) {
  FILE **p = tofilep(L);
  int ok = (fclose(*p) == 0);
  *p = NULL;
  return pushresult(L, ok, NULL);
}


static int aux_close (lua_State *L) {
  lua_getfenv(L, 1);
  lua_getfield(L, -1, "__close");
  return (lua_tocfunction(L, -1))(L);
}


static int io_close (lua_State *L) {
  tofile(L);  /* make sure argument is a file */
  return aux_close(L);
}


static int io_gc (lua_State *L) {
  FILE *f = *tofilep(L);
  /* ignore closed files */
  if (f != NULL)
    aux_close(L);
  return 0;
}


static int io_tostring (lua_State *L) {
  FILE *f = *tofilep(L);
  if (f == NULL)
    lua_pushliteral(L, "file (closed)");
  else
    lua_pushfstring(L, "file (%p)", f);
  return 1;
}


static char iolib_errbuf[1024] = {0};
static int io_open (lua_State *L) {
  const char *filename = luaL_checkstring(L, 1);
  const char *mode = luaL_optstring(L, 2, "r");
  FILE **pf = newfile(L);
  if (file_operations_allowed)
    *pf = fopen(filename, mode);
  else {
    snprintf(iolib_errbuf, 1024, "app tried to open file '%s'; adjust its permissions (ctrl-p) if that is expected", filename);
    Previous_message = iolib_errbuf;
  }
  return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}


static int io_tmpfile (lua_State *L) {
  FILE **pf = newfile(L);
  *pf = tmpfile();
  return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
}


static int io_readline (lua_State *L);


static void aux_lines (lua_State *L, int idx, int toclose) {
  lua_pushvalue(L, idx);
  lua_pushboolean(L, toclose);  /* close/not close file when finished */
  lua_pushcclosure(L, io_readline, 2);
}


static int f_lines (lua_State *L) {
  tofile(L);  /* check that it's a valid file handle */
  aux_lines(L, 1, 0);
  return 1;
}


static int io_lines (lua_State *L) {
  const char *filename = luaL_checkstring(L, 1);
  FILE **pf = newfile(L);
  if (file_operations_allowed)
    *pf = fopen(filename, "r");
  else {
    snprintf(iolib_errbuf, 1024, "app tried to open file '%s'; adjust its permissions (ctrl-p) if that is expected", filename);
    Previous_message = iolib_errbuf;
  }
  if (*pf == NULL)
    fileerror(L, 1, filename);
  aux_lines(L, lua_gettop(L), 1);
  return 1;
}


/*
** {======================================================
** READ
** =======================================================
*/


static int read_number (lua_State *L, FILE *f) {
  lua_Number d;
  if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
    lua_pushnumber(L, d);
    return 1;
  }
  else {
    lua_pushnil(L);  /* "result" to be removed */
    return 0;  /* read fails */
  }
}


static int test_eof (lua_State *L, FILE *f) {
  int c = getc(f);
  ungetc(c, f);
  lua_pushlstring(L, NULL, 0);
  return (c != EOF);
}


static int read_line (lua_State *L, FILE *f) {
  luaL_Buffer b;
  luaL_buffinit(L, &b);
  for (;;) {
    size_t l;
    char *p = luaL_prepbuffer(&b);
    if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) {  /* eof? */
      luaL_pushresult(&b);  /* close buffer */
      return (lua_objlen(L, -1) > 0);  /* check whether read something */
    }
    l = strlen(p);
    if (l == 0 || p[l-1] != '\n')
      luaL_addsize(&b, l);
    else {
      luaL_addsize(&b, l - 1);  /* do not include `eol' */
      luaL_pushresult(&b);  /* close buffer */
      return 1;  /* read at least an `eol' */
    }
  }
}


static int read_chars (lua_State *L, FILE *f, size_t n) {
  size_t rlen;  /* how much to read */
  size_t nr;  /* number of chars actually read */
  luaL_Buffer b;
  luaL_buffinit(L, &b);
  rlen = LUAL_BUFFERSIZE;  /* try to read that much each time */
  do {
    char *p = luaL_prepbuffer(&b);
    if (rlen > n) rlen = n;  /* cannot read more than asked */
    nr = fread(p, sizeof(char), rlen, f);
    luaL_addsize(&b, nr);
    n -= nr;  /* still have to read `n' chars */
  } while (n > 0 && nr == rlen);  /* until end of count or eof */
  luaL_pushresult(&b);  /* close buffer */
  return (n == 0 || lua_objlen(L, -1) > 0);
}


static int g_read (lua_State *L, FILE *f, int first) {
  int nargs = lua_gettop(L) - 1;
  int success;
  int n;
  clearerr(f);
  if (nargs == 0) {  /* no arguments? */
    success = read_line(L, f);
    n = first+1;  /* to return 1 result */
  }
  else {  /* ensure stack space for all results and for auxlib's buffer */
    luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
    success = 1;
    for (n = first; nargs-- && success; n++) {
      if (lua_type(L, n) == LUA_TNUMBER) {
        size_t l = (size_t)lua_tointeger(L, n);
        success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
      }
      else {
        const char *p = lua_tostring(L, n);
        luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
        switch (p[1]) {
          case 'n':  /* number */
            success = read_number(L, f);
            break;
          case 'l':  /* line */
            success = read_line(L, f);
            break;
          case 'a':  /* file */
            read_chars(L, f, ~((size_t)0));  /* read MAX_SIZE_T chars */
            success = 1; /* always success */
            break;
          default:
            return luaL_argerror(L, n, "invalid format");
        }
      }
    }
  }
  if (ferror(f))
    return pushresult(L, 0, NULL);
  if (!success) {
    lua_pop(L, 1);  /* remove last result */
    lua_pushnil(L);  /* push nil instead */
  }
  return n - first;
}


static int f_read (lua_State *L) {
  return g_read(L, tofile(L), 2);
}


static int io_readline (lua_State *L) {
  FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
  int sucess;
  if (f == NULL)  /* file is already closed? */
    luaL_error(L, "file is already closed");
  sucess = read_line(L, f);
  if (ferror(f))
    return luaL_error(L, "%s", strerror(errno));
  if (sucess) return 1;
  else {  /* EOF */
    if (lua_toboolean(L, lua_upvalueindex(2))) {  /* generator created file? */
      lua_settop(L, 0);
      lua_pushvalue(L, lua_upvalueindex(1));
      aux_close(L);  /* close it */
    }
    return 0;
  }
}

/* }====================================================== */


static int g_write (lua_State *L, FILE *f, int arg) {
  int nargs = lua_gettop(L) - 1;
  int status = 1;
  for (; nargs--; arg++) {
    if (lua_type(L, arg) == LUA_TNUMBER) {
      /* optimization: could be done exactly as for strings */
      status = status &&
          fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
    }
    else {
      size_t l;
      const char *s = luaL_checklstring(L, arg, &l);
      status = status && (fwrite(s, sizeof(char), l, f) == l);
    }
  }
  return pushresult(L, status, NULL);
}


static int f_write (lua_State *L) {
  return g_write(L, tofile(L), 2);
}


static int f_seek (lua_State *L) {
  static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
  static const char *const modenames[] = {"set", "cur", "end", NULL};
  FILE *f = tofile(L);
  int op = luaL_checkoption(L, 2, "cur", modenames);
  long offset = luaL_optlong(L, 3, 0);
  op = fseek(f, offset, mode[op]);
  if (op)
    return pushresult(L, 0, NULL);  /* error */
  else {
    lua_pushinteger(L, ftell(f));
    return 1;
  }
}


static int f_setvbuf (lua_State *L) {
  static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
  static const char *const modenames[] = {"no", "full", "line", NULL};
  FILE *f = tofile(L);
  int op = luaL_checkoption(L, 2, NULL, modenames);
  lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
  int res = setvbuf(f, NULL, mode[op], sz);
  return pushresult(L, res == 0, NULL);
}


static int f_flush (lua_State *L) {
  return pushresult(L, fflush(tofile(L)) == 0, NULL);
}


static const luaL_Reg iolib[] = {
  {"close", io_close},
  /* no 'flush' since Teliva is ncurses-based */
  /* no 'input' since Teliva is ncurses-based */
  {"lines", io_lines},
  {"open", io_open},
  /* no 'output' since Teliva is ncurses-based */
  /* no 'popen' without sandboxing it */
  /* no 'read' since Teliva is ncurses-based */
  {"tmpfile", io_tmpfile},
  {"type", io_type},
  /* no 'write' since Teliva is ncurses-based */
  {NULL, NULL}
};


static const luaL_Reg flib[] = {
  {"close", io_close},
  {"flush", f_flush},
  {"lines", f_lines},
  {"read", f_read},
  {"seek", f_seek},
  {"setvbuf", f_setvbuf},
  {"write", f_write},
  {"__gc", io_gc},
  {"__tostring", io_tostring},
  {NULL, NULL}
};


static void createmeta (lua_State *L) {
  luaL_newmetatable(L, LUA_FILEHANDLE);  /* create metatable for file handles */
  lua_pushvalue(L, -1);  /* push metatable */
  lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
  luaL_register(L, NULL, flib);  /* file methods */
}


static void newfenv (lua_State *L, lua_CFunction cls) {
  lua_createtable(L, 0, 1);
  lua_pushcfunction(L, cls);
  lua_setfield(L, -2, "__close");
}


LUALIB_API int luaopen_io (lua_State *L) {
  createmeta(L);
  newfenv(L, io_fclose);
  lua_replace(L, LUA_ENVIRONINDEX);
  /* open library */
  luaL_register(L, LUA_IOLIBNAME, iolib);
  return 1;
}
an> i <- increment loop } } fn print-grapheme screen: (addr screen), c: grapheme { compare screen, 0 { break-if-!= print-grapheme-to-real-screen c return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var cursor-col-addr/edx: (addr int) <- get screen-addr, cursor-col # adjust cursor if necessary # to avoid premature scrolling it's important to do this lazily, at the last possible time { # next row var num-cols-addr/ecx: (addr int) <- get screen-addr, num-cols var num-cols/ecx: int <- copy *num-cols-addr compare *cursor-col-addr, num-cols break-if-<= copy-to *cursor-col-addr, 1 var cursor-row-addr/ebx: (addr int) <- get screen-addr, cursor-row increment *cursor-row-addr # scroll var num-rows-addr/eax: (addr int) <- get screen-addr, num-rows var num-rows/eax: int <- copy *num-rows-addr compare *cursor-row-addr, num-rows break-if-<= copy-to *cursor-row-addr, num-rows # if (top-index > data size) top-index = 0, otherwise top-index += num-cols $print-grapheme:perform-scroll: { var top-index-addr/ebx: (addr int) <- get screen-addr, top-index var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var max-index/edi: int <- length data compare *top-index-addr, max-index { break-if->= add-to *top-index-addr, num-cols break $print-grapheme:perform-scroll } { break-if-< copy-to *top-index-addr, 0 } } } var idx/ecx: int <- current-screen-cell-index screen-addr #? print-string-to-real-screen "printing grapheme at screen index " #? print-int32-hex-to-real-screen idx #? print-string-to-real-screen ": " var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var offset/ecx: (offset screen-cell) <- compute-offset data, idx var dest-cell/ecx: (addr screen-cell) <- index data, offset var src-cell/eax: (addr screen-cell) <- get screen-addr, curr-attributes copy-object src-cell, dest-cell var dest/eax: (addr grapheme) <- get dest-cell, data var c2/ecx: grapheme <- copy c #? print-grapheme-to-real-screen c2 #? print-string-to-real-screen "\n" copy-to *dest, c2 increment *cursor-col-addr } fn current-screen-cell-index screen-on-stack: (addr screen) -> _/ecx: int { var screen/esi: (addr screen) <- copy screen-on-stack var cursor-row-addr/ecx: (addr int) <- get screen, cursor-row var cursor-col-addr/eax: (addr int) <- get screen, cursor-col var result/ecx: int <- screen-cell-index screen, *cursor-row-addr, *cursor-col-addr return result } fn screen-cell-index screen-on-stack: (addr screen), row: int, col: int -> _/ecx: int { var screen/esi: (addr screen) <- copy screen-on-stack var num-cols-addr/eax: (addr int) <- get screen, num-cols var num-cols/eax: int <- copy *num-cols-addr var result/ecx: int <- copy row result <- subtract 1 result <- multiply num-cols result <- add col result <- subtract 1 # result = (result + top-index) % data length var top-index-addr/eax: (addr int) <- get screen, top-index result <- add *top-index-addr var data-ah/eax: (addr handle array screen-cell) <- get screen, data var data/eax: (addr array screen-cell) <- lookup *data-ah var max-index/eax: int <- length data compare result, max-index { break-if-< result <- subtract max-index } return result } fn screen-grapheme-at screen-on-stack: (addr screen), row: int, col: int -> _/eax: grapheme { var screen-addr/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen-addr, row, col var result/eax: grapheme <- screen-grapheme-at-idx screen-addr, idx return result } fn screen-grapheme-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: grapheme { var screen-addr/esi: (addr screen) <- copy screen-on-stack var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var idx/ecx: int <- copy idx-on-stack var offset/ecx: (offset screen-cell) <- compute-offset data, idx var cell/eax: (addr screen-cell) <- index data, offset var src/eax: (addr grapheme) <- get cell, data return *src } fn screen-color-at screen-on-stack: (addr screen), row: int, col: int -> _/eax: int { var screen-addr/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen-addr, row, col var result/eax: int <- screen-color-at-idx screen-addr, idx return result } fn screen-color-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: int { var screen-addr/esi: (addr screen) <- copy screen-on-stack var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var idx/ecx: int <- copy idx-on-stack var offset/ecx: (offset screen-cell) <- compute-offset data, idx var cell/eax: (addr screen-cell) <- index data, offset var src/eax: (addr int) <- get cell, color var result/eax: int <- copy *src return result } fn screen-background-color-at screen-on-stack: (addr screen), row: int, col: int -> _/eax: int { var screen-addr/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen-addr, row, col var result/eax: int <- screen-background-color-at-idx screen-addr, idx return result } fn screen-background-color-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: int { var screen-addr/esi: (addr screen) <- copy screen-on-stack var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var idx/ecx: int <- copy idx-on-stack var offset/ecx: (offset screen-cell) <- compute-offset data, idx var cell/eax: (addr screen-cell) <- index data, offset var src/eax: (addr int) <- get cell, background-color return *src } fn screen-bold-at? screen-on-stack: (addr screen), row: int, col: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen-addr, row, col var result/eax: boolean <- screen-bold-at-idx? screen-addr, idx return result } fn screen-bold-at-idx? screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var idx/ecx: int <- copy idx-on-stack var offset/ecx: (offset screen-cell) <- compute-offset data, idx var cell/eax: (addr screen-cell) <- index data, offset var src/eax: (addr boolean) <- get cell, bold? return *src } fn screen-underline-at? screen-on-stack: (addr screen), row: int, col: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen-addr, row, col var result/eax: boolean <- screen-underline-at-idx? screen-addr, idx return result } fn screen-underline-at-idx? screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var idx/ecx: int <- copy idx-on-stack var offset/ecx: (offset screen-cell) <- compute-offset data, idx var cell/eax: (addr screen-cell) <- index data, offset var src/eax: (addr boolean) <- get cell, underline? return *src } fn screen-reverse-at? screen-on-stack: (addr screen), row: int, col: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen-addr, row, col var result/eax: boolean <- screen-reverse-at-idx? screen-addr, idx return result } fn screen-reverse-at-idx? screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var idx/ecx: int <- copy idx-on-stack var offset/ecx: (offset screen-cell) <- compute-offset data, idx var cell/eax: (addr screen-cell) <- index data, offset var src/eax: (addr boolean) <- get cell, reverse? return *src } fn screen-blink-at? screen-on-stack: (addr screen), row: int, col: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen-addr, row, col var result/eax: boolean <- screen-blink-at-idx? screen-addr, idx return result } fn screen-blink-at-idx? screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: boolean { var screen-addr/esi: (addr screen) <- copy screen-on-stack var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data var data/eax: (addr array screen-cell) <- lookup *data-ah var idx/ecx: int <- copy idx-on-stack var offset/ecx: (offset screen-cell) <- compute-offset data, idx var cell/eax: (addr screen-cell) <- index data, offset var src/eax: (addr boolean) <- get cell, blink? return *src } fn print-code-point screen: (addr screen), c: code-point { var g/eax: grapheme <- to-grapheme c print-grapheme screen, g } fn print-int32-hex screen: (addr screen), n: int { compare screen, 0 { break-if-!= print-int32-hex-to-real-screen n return } # fake screen var s2: (stream byte 0x100) var s2-addr/esi: (addr stream byte) <- address s2 write-int32-hex s2-addr, n var screen-addr/edi: (addr screen) <- copy screen { var done?/eax: boolean <- stream-empty? s2-addr compare done?, 0 break-if-!= var g/eax: grapheme <- read-grapheme s2-addr print-grapheme screen, g loop } } fn print-int32-hex-bits screen: (addr screen), n: int, bits: int { compare screen, 0 { break-if-!= print-int32-hex-bits-to-real-screen n, bits return } # fake screen var s2: (stream byte 0x100) var s2-addr/esi: (addr stream byte) <- address s2 write-int32-hex-bits s2-addr, n, bits var screen-addr/edi: (addr screen) <- copy screen { var done?/eax: boolean <- stream-empty? s2-addr compare done?, 0 break-if-!= var g/eax: grapheme <- read-grapheme s2-addr print-grapheme screen, g loop } } fn print-int32-decimal screen: (addr screen), n: int { compare screen, 0 { break-if-!= print-int32-decimal-to-real-screen n return } # fake screen var s2: (stream byte 0x100) var s2-addr/esi: (addr stream byte) <- address s2 write-int32-decimal s2-addr, n var screen-addr/edi: (addr screen) <- copy screen { var done?/eax: boolean <- stream-empty? s2-addr compare done?, 0 break-if-!= var g/eax: grapheme <- read-grapheme s2-addr print-grapheme screen, g loop } } fn reset-formatting screen: (addr screen) { compare screen, 0 { break-if-!= reset-formatting-on-real-screen return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var dest/ecx: (addr screen-cell) <- get screen-addr, curr-attributes var default-cell: screen-cell var bg/eax: (addr int) <- get default-cell, background-color copy-to *bg, 7 var default-cell-addr/eax: (addr screen-cell) <- address default-cell copy-object default-cell-addr, dest } fn start-color screen: (addr screen), fg: int, bg: int { compare screen, 0 { break-if-!= start-color-on-real-screen fg, bg return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var attr/ecx: (addr screen-cell) <- get screen-addr, curr-attributes var dest/edx: (addr int) <- get attr, color var src/eax: int <- copy fg copy-to *dest, src var dest/edx: (addr int) <- get attr, background-color var src/eax: int <- copy bg copy-to *dest, src } fn start-bold screen: (addr screen) { compare screen, 0 { break-if-!= start-bold-on-real-screen return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var attr/ecx: (addr screen-cell) <- get screen-addr, curr-attributes var dest/edx: (addr boolean) <- get attr, bold? copy-to *dest, 1 } fn start-underline screen: (addr screen) { compare screen, 0 { break-if-!= start-underline-on-real-screen return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var attr/ecx: (addr screen-cell) <- get screen-addr, curr-attributes var dest/edx: (addr boolean) <- get attr, underline? copy-to *dest, 1 } fn start-reverse-video screen: (addr screen) { compare screen, 0 { break-if-!= start-reverse-video-on-real-screen return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var attr/ecx: (addr screen-cell) <- get screen-addr, curr-attributes var dest/edx: (addr boolean) <- get attr, reverse? copy-to *dest, 1 } fn start-blinking screen: (addr screen) { compare screen, 0 { break-if-!= start-blinking-on-real-screen return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var attr/ecx: (addr screen-cell) <- get screen-addr, curr-attributes var dest/edx: (addr boolean) <- get attr, blink? copy-to *dest, 1 } fn hide-cursor screen: (addr screen) { compare screen, 0 { break-if-!= hide-cursor-on-real-screen return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var hide?/ecx: (addr boolean) <- get screen-addr, cursor-hide? copy-to *hide?, 1 } fn show-cursor screen: (addr screen) { compare screen, 0 { break-if-!= show-cursor-on-real-screen return } # fake screen var screen-addr/esi: (addr screen) <- copy screen var hide?/ecx: (addr boolean) <- get screen-addr, cursor-hide? copy-to *hide?, 0 } # validate data on screen regardless of attributes (color, bold, etc.) # Mu doesn't have multi-line strings, so we provide functions for rows or portions of rows. # Tab characters (that translate into multiple screen cells) not supported. fn check-screen-row screen: (addr screen), row-idx: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-from screen, row-idx, 1, expected, msg } fn check-screen-row-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, row-idx, col-idx # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var expected-grapheme/eax: grapheme <- read-grapheme e-addr # compare graphemes $check-screen-row-from:compare-graphemes: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-from:compare-graphemes } # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= print-string-to-real-screen "." break $check-screen-row-from:compare-graphemes } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") but observed '" print-grapheme-to-real-screen g print-string-to-real-screen "'\n" } idx <- increment increment col-idx loop } } # various variants by screen-cell attribute; spaces in the 'expected' data should not match the attribute fn check-screen-row-in-color screen: (addr screen), fg: int, row-idx: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-in-color-from screen, fg, row-idx, 1, expected, msg } fn check-screen-row-in-color-from screen-on-stack: (addr screen), fg: int, row-idx: int, col-idx: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, row-idx, col-idx # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var _expected-grapheme/eax: grapheme <- read-grapheme e-addr var expected-grapheme/edi: grapheme <- copy _expected-grapheme $check-screen-row-in-color-from:compare-cells: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-in-color-from:compare-cells } # if expected-grapheme is space, a different color is ok { compare expected-grapheme, 0x20 break-if-!= var color/eax: int <- screen-color-at-idx screen, idx compare color, fg break-if-!= $check-screen-row-in-color-from:compare-cells } # compare graphemes $check-screen-row-in-color-from:compare-graphemes: { # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-color-from:compare-graphemes } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") but observed '" print-grapheme-to-real-screen g print-string-to-real-screen "'\n" } $check-screen-row-in-color-from:compare-colors: { var color/eax: int <- screen-color-at-idx screen, idx compare fg, color { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-color-from:compare-colors } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") in color " print-int32-hex-to-real-screen fg print-string-to-real-screen " but observed color " print-int32-hex-to-real-screen color print-string-to-real-screen "\n" } } idx <- increment increment col-idx loop } } # background color is visible even for spaces, so 'expected' behaves as an array of booleans. # non-space = given background must match; space = background must not match fn check-screen-row-in-background-color screen: (addr screen), bg: int, row-idx: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-in-background-color-from screen, bg, row-idx, 1, expected, msg } fn check-screen-row-in-background-color-from screen-on-stack: (addr screen), bg: int, row-idx: int, col-idx: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, row-idx, col-idx # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var _expected-grapheme/eax: grapheme <- read-grapheme e-addr var expected-grapheme/edx: grapheme <- copy _expected-grapheme $check-screen-row-in-background-color-from:compare-cells: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-in-background-color-from:compare-cells } # if expected-grapheme is space, a different color is ok { compare expected-grapheme, 0x20 break-if-!= var color/eax: int <- screen-background-color-at-idx screen, idx compare color, bg break-if-!= $check-screen-row-in-background-color-from:compare-cells } # compare graphemes $check-screen-row-in-background-color-from:compare-graphemes: { # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-background-color-from:compare-graphemes } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") but observed '" print-grapheme-to-real-screen g print-string-to-real-screen "'\n" } $check-screen-row-in-background-color-from:compare-colors: { var color/eax: int <- screen-background-color-at-idx screen, idx compare bg, color { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-background-color-from:compare-colors } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") in background color " print-int32-hex-to-real-screen bg print-string-to-real-screen " but observed color " print-int32-hex-to-real-screen color print-string-to-real-screen "\n" } } idx <- increment increment col-idx loop } } fn check-screen-row-in-bold screen: (addr screen), row-idx: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-in-bold-from screen, row-idx, 1, expected, msg } fn check-screen-row-in-bold-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, row-idx, col-idx # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var _expected-grapheme/eax: grapheme <- read-grapheme e-addr var expected-grapheme/edx: grapheme <- copy _expected-grapheme $check-screen-row-in-bold-from:compare-cells: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-in-bold-from:compare-cells } # if expected-grapheme is space, non-bold is ok { compare expected-grapheme, 0x20 break-if-!= var bold?/eax: boolean <- screen-bold-at-idx? screen, idx compare bold?, 1 break-if-!= $check-screen-row-in-bold-from:compare-cells } # compare graphemes $check-screen-row-in-bold-from:compare-graphemes: { # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-bold-from:compare-graphemes } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") but observed '" print-grapheme-to-real-screen g print-string-to-real-screen "'\n" } $check-screen-row-in-bold-from:compare-bold: { var bold?/eax: boolean <- screen-bold-at-idx? screen, idx compare bold?, 1 { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-bold-from:compare-bold } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") to be in bold\n" } } idx <- increment increment col-idx loop } } fn check-screen-row-in-underline screen: (addr screen), row-idx: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-in-underline-from screen, row-idx, 1, expected, msg } fn check-screen-row-in-underline-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, row-idx, col-idx # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var _expected-grapheme/eax: grapheme <- read-grapheme e-addr var expected-grapheme/edx: grapheme <- copy _expected-grapheme $check-screen-row-in-underline-from:compare-cells: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-in-underline-from:compare-cells } # if expected-grapheme is space, non-underline is ok { compare expected-grapheme, 0x20 break-if-!= var underline?/eax: boolean <- screen-underline-at-idx? screen, idx compare underline?, 1 break-if-!= $check-screen-row-in-underline-from:compare-cells } # compare graphemes $check-screen-row-in-underline-from:compare-graphemes: { # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-underline-from:compare-graphemes } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") but observed '" print-grapheme-to-real-screen g print-string-to-real-screen "'\n" } $check-screen-row-in-underline-from:compare-underline: { var underline?/eax: boolean <- screen-underline-at-idx? screen, idx compare underline?, 1 { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-underline-from:compare-underline } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") to be underlined\n" } } idx <- increment increment col-idx loop } } fn check-screen-row-in-reverse screen: (addr screen), row-idx: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-in-reverse-from screen, row-idx, 1, expected, msg } fn check-screen-row-in-reverse-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, row-idx, col-idx # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var _expected-grapheme/eax: grapheme <- read-grapheme e-addr var expected-grapheme/edx: grapheme <- copy _expected-grapheme $check-screen-row-in-reverse-from:compare-cells: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-in-reverse-from:compare-cells } # if expected-grapheme is space, non-reverse is ok { compare expected-grapheme, 0x20 break-if-!= var reverse?/eax: boolean <- screen-reverse-at-idx? screen, idx compare reverse?, 1 break-if-!= $check-screen-row-in-reverse-from:compare-cells } # compare graphemes $check-screen-row-in-reverse-from:compare-graphemes: { # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-reverse-from:compare-graphemes } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") but observed '" print-grapheme-to-real-screen g print-string-to-real-screen "'\n" } $check-screen-row-in-reverse-from:compare-reverse: { var reverse?/eax: boolean <- screen-reverse-at-idx? screen, idx compare reverse?, 1 { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-reverse-from:compare-reverse } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") to be in reverse-video\n" } } idx <- increment increment col-idx loop } } fn check-screen-row-in-blinking screen: (addr screen), row-idx: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-in-blinking-from screen, row-idx, 1, expected, msg } fn check-screen-row-in-blinking-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, row-idx, col-idx # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var _expected-grapheme/eax: grapheme <- read-grapheme e-addr var expected-grapheme/edx: grapheme <- copy _expected-grapheme $check-screen-row-in-blinking-from:compare-cells: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-in-blinking-from:compare-cells } # if expected-grapheme is space, non-blinking is ok { compare expected-grapheme, 0x20 break-if-!= var blinking?/eax: boolean <- screen-blink-at-idx? screen, idx compare blinking?, 1 break-if-!= $check-screen-row-in-blinking-from:compare-cells } # compare graphemes $check-screen-row-in-blinking-from:compare-graphemes: { # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-blinking-from:compare-graphemes } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") but observed '" print-grapheme-to-real-screen g print-string-to-real-screen "'\n" } $check-screen-row-in-blinking-from:compare-blinking: { var blinking?/eax: boolean <- screen-blink-at-idx? screen, idx compare blinking?, 1 { break-if-!= print-string-to-real-screen "." break $check-screen-row-in-blinking-from:compare-blinking } # otherwise print an error print-string-to-real-screen msg print-string-to-real-screen ": expected '" print-grapheme-to-real-screen expected-grapheme print-string-to-real-screen "' at (" print-int32-hex-to-real-screen row-idx print-string-to-real-screen ", " print-int32-hex-to-real-screen col-idx print-string-to-real-screen ") to be blinking\n" } } idx <- increment increment col-idx loop } } fn test-print-single-grapheme { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c check-screen-row screen, 1/row, "a", "F - test-print-single-grapheme" # top-left corner of the screen } fn test-print-multiple-graphemes { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols print-string screen, "Hello, 世界" check-screen-row screen, 1/row, "Hello, 世界", "F - test-print-multiple-graphemes" } fn test-move-cursor { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols move-cursor screen, 1, 4 var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c check-screen-row screen, 1/row, " a", "F - test-move-cursor" # top row } fn test-move-cursor-zeroes { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols move-cursor screen, 0, 0 var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c check-screen-row screen, 1/row, "a", "F - test-move-cursor-zeroes" # top-left corner of the screen } fn test-move-cursor-zero-row { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols move-cursor screen, 0, 2 var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c check-screen-row screen, 1/row, " a", "F - test-move-cursor-zero-row" # top row } fn test-move-cursor-zero-column { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols move-cursor screen, 4, 0 var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c check-screen-row screen, 4/row, "a", "F - test-move-cursor-zero-column" } fn test-move-cursor-negative-row { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5, 3 move-cursor screen, -1/row, 2/col var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c # no move check-screen-row screen, 1/row, "a", "F - test-move-cursor-negative-row" } fn test-move-cursor-negative-column { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5, 3 move-cursor screen, 2/row, -1/col var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c # no move check-screen-row screen, 1/row, "a", "F - test-move-cursor-negative-column" } fn test-move-cursor-column-too-large { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 3/cols move-cursor screen, 1/row, 4/col var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c # top row is empty check-screen-row screen, 1/row, " ", "F - test-move-cursor-column-too-large" # character shows up on next row check-screen-row screen, 2/row, "a", "F - test-move-cursor-column-too-large" } fn test-move-cursor-column-too-large-saturates { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 3/cols move-cursor screen, 1/row, 6/col var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c # top row is empty check-screen-row screen, 1/row, " ", "F - test-move-cursor-column-too-large-saturates" # top-left corner of the screen # character shows up at the start of next row check-screen-row screen, 2/row, "a", "F - test-move-cursor-column-too-large-saturates" # top-left corner of the screen } fn test-move-cursor-row-too-large { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 3/cols move-cursor screen, 6/row, 2/col var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c # bottom row shows the character check-screen-row screen, 5/row, " a", "F - test-move-cursor-row-too-large" } fn test-move-cursor-row-too-large-saturates { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 3/cols move-cursor screen, 9/row, 2/col var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c # bottom row shows the character check-screen-row screen, 5/row, " a", "F - test-move-cursor-row-too-large-saturates" } fn test-check-screen-row-from { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols move-cursor screen, 1, 4 var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c check-screen-row screen, 1/row, " a", "F - test-check-screen-row-from/baseline" check-screen-row-from screen, 1/row, 4/col, "a", "F - test-check-screen-row-from" } fn test-print-string-overflows-to-next-row { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols print-string screen, "abcdefg" check-screen-row screen, 1/row, "abcd", "F - test-print-string-overflows-to-next-row" check-screen-row screen, 2/row, "efg", "F - test-print-string-overflows-to-next-row" } fn test-check-screen-scrolls-on-overflow { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols # single character starting at bottom right move-cursor screen, 5/rows, 4/cols var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c check-screen-row-from screen, 5/row, 4/col, "a", "F - test-check-screen-scrolls-on-overflow/baseline" # bottom-right corner of the screen # multiple characters starting at bottom right move-cursor screen, 5, 4 print-string screen, "ab" # screen scrolled up one row #? check-screen-row screen, 1/row, " ", "F - test-check-screen-scrolls-on-overflow/x1" #? check-screen-row screen, 2/row, " ", "F - test-check-screen-scrolls-on-overflow/x2" #? check-screen-row screen, 3/row, " ", "F - test-check-screen-scrolls-on-overflow/x3" #? check-screen-row screen, 4/row, " a", "F - test-check-screen-scrolls-on-overflow/x4" #? check-screen-row screen, 5/row, "b ", "F - test-check-screen-scrolls-on-overflow/x5" check-screen-row-from screen, 4/row, 4/col, "a", "F - test-check-screen-scrolls-on-overflow/1" check-screen-row-from screen, 5/row, 1/col, "b", "F - test-check-screen-scrolls-on-overflow/2" } fn test-check-screen-color { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c start-color screen, 1/fg, 0/bg c <- copy 0x62/b print-grapheme screen, c start-color screen, 0/fg, 7/bg c <- copy 0x63/c print-grapheme screen, c check-screen-row-in-color screen, 0/fg, 1/row, "a c", "F - test-check-screen-color" } fn test-check-screen-background-color { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c start-color screen, 0/fg, 1/bg c <- copy 0x62/b print-grapheme screen, c start-color screen, 0/fg, 7/bg c <- copy 0x63/c print-grapheme screen, c check-screen-row-in-background-color screen, 7/bg, 1/row, "a c", "F - test-check-screen-background-color" } fn test-check-screen-bold { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols start-bold screen var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c reset-formatting screen c <- copy 0x62/b print-grapheme screen, c start-bold screen c <- copy 0x63/c print-grapheme screen, c check-screen-row-in-bold screen, 1/row, "a c", "F - test-check-screen-bold" } fn test-check-screen-underline { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols start-underline screen var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c reset-formatting screen c <- copy 0x62/b print-grapheme screen, c start-underline screen c <- copy 0x63/c print-grapheme screen, c check-screen-row-in-underline screen, 1/row, "a c", "F - test-check-screen-underline" } fn test-check-screen-reverse { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols start-reverse-video screen var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c reset-formatting screen c <- copy 0x62/b print-grapheme screen, c start-reverse-video screen c <- copy 0x63/c print-grapheme screen, c check-screen-row-in-reverse screen, 1/row, "a c", "F - test-check-screen-reverse" } fn test-check-screen-blinking { var screen-on-stack: screen var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5/rows, 4/cols start-blinking screen var c/eax: grapheme <- copy 0x61/a print-grapheme screen, c reset-formatting screen c <- copy 0x62/b print-grapheme screen, c start-blinking screen c <- copy 0x63/c print-grapheme screen, c check-screen-row-in-blinking screen, 1/row, "a c", "F - test-check-screen-blinking" } #? fn main -> _/ebx: int { #? #? test-check-screen-color #? run-tests #? return 0 #? }