about summary refs log tree commit diff stats
path: root/shell/primitives.mu
blob: 66bc378681b8961f0512eac5375897789305b3c4 (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
generated by cgit-pink 1.4.1-2-gfad0 (git 2.36.2.497.gbbea4dcf42) at 2024-11-29 03:23:34 +0000
 


 id='n2898' href='#n2898'>2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
# Primitives are functions that are implemented directly in Mu.
# They always evaluate all their arguments.

fn initialize-primitives _self: (addr global-table) {
  var self/esi: (addr global-table) <- copy _self
  # for numbers
  append-primitive self, "+"
  append-primitive self, "-"
  append-primitive self, "*"
  append-primitive self, "/"
  append-primitive self, "%"
  append-primitive self, "sqrt"
  append-primitive self, "abs"
  append-primitive self, "sgn"
  append-primitive self, "<"
  append-primitive self, ">"
  append-primitive self, "<="
  append-primitive self, ">="
  # generic
  append-primitive self, "apply"
  append-primitive self, "="
  append-primitive self, "no"
  append-primitive self, "not"
  append-primitive self, "dbg"
  # for pairs
  append-primitive self, "car"
  append-primitive self, "cdr"
  append-primitive self, "cons"
  append-primitive self, "cons?"
  # for screens
  append-primitive self, "print"
  append-primitive self, "clear"
  append-primitive self, "lines"
  append-primitive self, "columns"
  append-primitive self, "up"
  append-primitive self, "down"
  append-primitive self, "left"
  append-primitive self, "right"
  append-primitive self, "cr"
  append-primitive self, "pixel"
  append-primitive self, "line"
  append-primitive self, "hline"
  append-primitive self, "vline"
  append-primitive self, "circle"
  append-primitive self, "bezier"
  append-primitive self, "width"
  append-primitive self, "height"
  append-primitive self, "new_screen"
  append-primitive self, "blit"
  # for keyboards
  append-primitive self, "key"
  # for streams
  append-primitive self, "stream"
  append-primitive self, "write"
  append-primitive self, "read"
  append-primitive self, "rewind"
  # misc
  append-primitive self, "abort"
  # keep sync'd with render-primitives
}

# Slightly misnamed; renders primitives as well as special forms that don't
# evaluate all their arguments.
fn render-primitives screen: (addr screen), xmin: int, xmax: int, ymax: int {
  var y/ecx: int <- copy ymax
  y <- subtract 0x11/primitives-border
  clear-rect screen, xmin, y, xmax, ymax, 0xdc/bg=green-bg
  y <- increment
  var right-min/edx: int <- copy xmax
  right-min <- subtract 0x1e/primitives-divider
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "primitives", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "fn apply set if while", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "booleans", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "= and or not", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "lists", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "cons car cdr no cons?", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "numbers", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "+ - * / %", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "< > <= >=", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "sqrt abs sgn", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg
#?   {
#?     compare screen, 0
#?     break-if-!=
#?     var foo/eax: byte <- read-key 0/keyboard
#?     compare foo, 0
#?     loop-if-=
#?   }
  y <- copy ymax
  y <- subtract 0x10/primitives-border
  var left-max/edx: int <- copy xmax
  left-max <- subtract 0x20/primitives-divider
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "cursor graphics", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  print", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": screen a -> a", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  lines columns", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": screen -> number", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  up down left right", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": screen", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  cr", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": screen   ", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, "# move cursor down and to left margin", tmpx, left-max, y, 0x38/fg=trace, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "pixel graphics", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  circle bezier line hline vline pixel", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  width height", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": screen -> number", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  new_screen", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": number number -> screen ", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  clear blit", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": screen", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  key", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": keyboard -> grapheme?", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "streams", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  stream", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": -> stream ", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  write", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": stream grapheme -> stream", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  rewind clear", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": stream", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy xmin
  tmpx <- draw-text-rightward screen, "  read", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": stream -> grapheme", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg
}

fn primitive-global? _x: (addr global) -> _/eax: boolean {
  var x/eax: (addr global) <- copy _x
  var value-ah/eax: (addr handle cell) <- get x, value
  var value/eax: (addr cell) <- lookup *value-ah
  compare value, 0/null
  {
    break-if-!=
    return 0/false
  }
  {
    var value-type/eax: (addr int) <- get value, type
    compare *value-type, 4/primitive
    break-if-=
    return 0/false
  }
  return 1/true
}

fn append-primitive _self: (addr global-table), name: (addr array byte) {
  var self/esi: (addr global-table) <- copy _self
  compare self, 0
  {
    break-if-!=
    abort "append primitive"
    return
  }
  var final-index-addr/ecx: (addr int) <- get self, final-index
  increment *final-index-addr
  var curr-index/ecx: int <- copy *final-index-addr
  var data-ah/eax: (addr handle array global) <- get self, data
  var data/eax: (addr array global) <- lookup *data-ah
  var curr-offset/esi: (offset global) <- compute-offset data, curr-index
  var curr/esi: (addr global) <- index data, curr-offset
  var curr-name-ah/eax: (addr handle array byte) <- get curr, name
  copy-array-object name, curr-name-ah
  var curr-value-ah/eax: (addr handle cell) <- get curr, value
  new-primitive-function curr-value-ah, curr-index
}

# a little strange; goes from value to name and selects primitive based on name
fn apply-primitive _f: (addr cell), args-ah: (addr handle cell), out: (addr handle cell), _globals: (addr global-table), trace: (addr trace) {
  var f/esi: (addr cell) <- copy _f
  var f-index-a/ecx: (addr int) <- get f, index-data
  var f-index/ecx: int <- copy *f-index-a
  var globals/eax: (addr global-table) <- copy _globals
  compare globals, 0
  {
    break-if-!=
    abort "apply primitive"
    return
  }
  var global-data-ah/eax: (addr handle array global) <- get globals, data
  var global-data/eax: (addr array global) <- lookup *global-data-ah
  var f-offset/ecx: (offset global) <- compute-offset global-data, f-index
  var f-value/ecx: (addr global) <- index global-data, f-offset
  var f-name-ah/ecx: (addr handle array byte) <- get f-value, name
  var f-name/eax: (addr array byte) <- lookup *f-name-ah
  {
    var add?/eax: boolean <- string-equal? f-name, "+"
    compare add?, 0/false
    break-if-=
    apply-add args-ah, out, trace
    return
  }
  {
    var subtract?/eax: boolean <- string-equal? f-name, "-"
    compare subtract?, 0/false
    break-if-=
    apply-subtract args-ah, out, trace
    return
  }
  {
    var multiply?/eax: boolean <- string-equal? f-name, "*"
    compare multiply?, 0/false
    break-if-=
    apply-multiply args-ah, out, trace
    return
  }
  {
    var divide?/eax: boolean <- string-equal? f-name, "/"
    compare divide?, 0/false
    break-if-=
    apply-divide args-ah, out, trace
    return
  }
  # '%' is the remainder operator, because modulo isn't really meaningful for
  # non-integers
  #
  # I considered calling this operator 'rem', but I want to follow Arc in
  # using 'rem' for filtering out elements from lists.
  #   https://arclanguage.github.io/ref/list.html#rem
  {
    var remainder?/eax: boolean <- string-equal? f-name, "%"
    compare remainder?, 0/false
    break-if-=
    apply-remainder args-ah, out, trace
    return
  }
  {
    var square-root?/eax: boolean <- string-equal? f-name, "sqrt"
    compare square-root?, 0/false
    break-if-=
    apply-square-root args-ah, out, trace
    return
  }
  {
    var abs?/eax: boolean <- string-equal? f-name, "abs"
    compare abs?, 0/false
    break-if-=
    apply-abs args-ah, out, trace
    return
  }
  {
    var sgn?/eax: boolean <- string-equal? f-name, "sgn"
    compare sgn?, 0/false
    break-if-=
    apply-sgn args-ah, out, trace
    return
  }
  {
    var car?/eax: boolean <- string-equal? f-name, "car"
    compare car?, 0/false
    break-if-=
    apply-car args-ah, out, trace
    return
  }
  {
    var cdr?/eax: boolean <- string-equal? f-name, "cdr"
    compare cdr?, 0/false
    break-if-=
    apply-cdr args-ah, out, trace
    return
  }
  {
    var cons?/eax: boolean <- string-equal? f-name, "cons"
    compare cons?, 0/false
    break-if-=
    apply-cons args-ah, out, trace
    return
  }
  {
    var cons-check?/eax: boolean <- string-equal? f-name, "cons?"
    compare cons-check?, 0/false
    break-if-=
    apply-cons-check args-ah, out, trace
    return
  }
  {
    var cell-isomorphic?/eax: boolean <- string-equal? f-name, "="
    compare cell-isomorphic?, 0/false
    break-if-=
    apply-cell-isomorphic args-ah, out, trace
    return
  }
  {
    var not?/eax: boolean <- string-equal? f-name, "no"
    compare not?, 0/false
    break-if-=
    apply-not args-ah, out, trace
    return
  }
  {
    var not?/eax: boolean <- string-equal? f-name, "not"
    compare not?, 0/false
    break-if-=
    apply-not args-ah, out, trace
    return
  }
  {
    var debug?/eax: boolean <- string-equal? f-name, "dbg"
    compare debug?, 0/false
    break-if-=
    apply-debug args-ah, out, trace
    return
  }
  {
    var lesser?/eax: boolean <- string-equal? f-name, "<"
    compare lesser?, 0/false
    break-if-=
    apply-< args-ah, out, trace
    return
  }
  {
    var greater?/eax: boolean <- string-equal? f-name, ">"
    compare greater?, 0/false
    break-if-=
    apply-> args-ah, out, trace
    return
  }
  {
    var lesser-or-equal?/eax: boolean <- string-equal? f-name, "<="
    compare lesser-or-equal?, 0/false
    break-if-=
    apply-<= args-ah, out, trace
    return
  }
  {
    var greater-or-equal?/eax: boolean <- string-equal? f-name, ">="
    compare greater-or-equal?, 0/false
    break-if-=
    apply->= args-ah, out, trace
    return
  }
  {
    var print?/eax: boolean <- string-equal? f-name, "print"
    compare print?, 0/false
    break-if-=
    apply-print args-ah, out, trace
    return
  }
  {
    var clear?/eax: boolean <- string-equal? f-name, "clear"
    compare clear?, 0/false
    break-if-=
    apply-clear args-ah, out, trace
    return
  }
  {
    var lines?/eax: boolean <- string-equal? f-name, "lines"
    compare lines?, 0/false
    break-if-=
    apply-lines args-ah, out, trace
    return
  }
  {
    var columns?/eax: boolean <- string-equal? f-name, "columns"
    compare columns?, 0/false
    break-if-=
    apply-columns args-ah, out, trace
    return
  }
  {
    var up?/eax: boolean <- string-equal? f-name, "up"
    compare up?, 0/false
    break-if-=
    apply-up args-ah, out, trace
    return
  }
  {
    var down?/eax: boolean <- string-equal? f-name, "down"
    compare down?, 0/false
    break-if-=
    apply-down args-ah, out, trace
    return
  }
  {
    var left?/eax: boolean <- string-equal? f-name, "left"
    compare left?, 0/false
    break-if-=
    apply-left args-ah, out, trace
    return
  }
  {
    var right?/eax: boolean <- string-equal? f-name, "right"
    compare right?, 0/false
    break-if-=
    apply-right args-ah, out, trace
    return
  }
  {
    var cr?/eax: boolean <- string-equal? f-name, "cr"
    compare cr?, 0/false
    break-if-=
    apply-cr args-ah, out, trace
    return
  }
  {
    var pixel?/eax: boolean <- string-equal? f-name, "pixel"
    compare pixel?, 0/false
    break-if-=
    apply-pixel args-ah, out, trace
    return
  }
  {
    var line?/eax: boolean <- string-equal? f-name, "line"
    compare line?, 0/false
    break-if-=
    apply-line args-ah, out, trace
    return
  }
  {
    var hline?/eax: boolean <- string-equal? f-name, "hline"
    compare hline?, 0/false
    break-if-=
    apply-hline args-ah, out, trace
    return
  }
  {
    var vline?/eax: boolean <- string-equal? f-name, "vline"
    compare vline?, 0/false
    break-if-=
    apply-vline args-ah, out, trace
    return
  }
  {
    var circle?/eax: boolean <- string-equal? f-name, "circle"
    compare circle?, 0/false
    break-if-=
    apply-circle args-ah, out, trace
    return
  }
  {
    var bezier?/eax: boolean <- string-equal? f-name, "bezier"
    compare bezier?, 0/false
    break-if-=
    apply-bezier args-ah, out, trace
    return
  }
  {
    var width?/eax: boolean <- string-equal? f-name, "width"
    compare width?, 0/false
    break-if-=
    apply-width args-ah, out, trace
    return
  }
  {
    var height?/eax: boolean <- string-equal? f-name, "height"
    compare height?, 0/false
    break-if-=
    apply-height args-ah, out, trace
    return
  }
  {
    var screen?/eax: boolean <- string-equal? f-name, "new_screen"
    compare screen?, 0/false
    break-if-=
    apply-new-screen args-ah, out, trace
    return
  }
  {
    var blit?/eax: boolean <- string-equal? f-name, "blit"
    compare blit?, 0/false
    break-if-=
    apply-blit args-ah, out, trace
    return
  }
  {
    var wait-for-key?/eax: boolean <- string-equal? f-name, "key"
    compare wait-for-key?, 0/false
    break-if-=
    apply-wait-for-key args-ah, out, trace
    return
  }
  {
    var stream?/eax: boolean <- string-equal? f-name, "stream"
    compare stream?, 0/false
    break-if-=
    apply-stream args-ah, out, trace
    return
  }
  {
    var write?/eax: boolean <- string-equal? f-name, "write"
    compare write?, 0/false
    break-if-=
    apply-write args-ah, out, trace
    return
  }
  {
    var rewind?/eax: boolean <- string-equal? f-name, "rewind"
    compare rewind?, 0/false
    break-if-=
    apply-rewind args-ah, out, trace
    return
  }
  {
    var read?/eax: boolean <- string-equal? f-name, "read"
    compare read?, 0/false
    break-if-=
    apply-read args-ah, out, trace
    return
  }
  {
    var abort?/eax: boolean <- string-equal? f-name, "abort"
    compare abort?, 0/false
    break-if-=
    apply-abort args-ah, out, trace
    return
  }
  abort "unknown primitive function"
}

fn apply-add _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply +"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to + are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "+ needs 2 args but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for + is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  # args->right->left->value
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "+ encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "+ needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for + is not a number"
    return
  }
  var second-value/edx: (addr float) <- get second, number-data
  # add
  var result/xmm0: float <- copy *first-value
  result <- add *second-value
  new-float out, result
}

fn test-evaluate-missing-arg-in-add {
  var t-storage: trace
  var t/edi: (addr trace) <- address t-storage
  initialize-trace t, 0x100/max-depth, 0x100/capacity, 0/visible  # we don't use trace UI
  #
  var nil-storage: (handle cell)
  var nil-ah/ecx: (addr handle cell) <- address nil-storage
  allocate-pair nil-ah
  var one-storage: (handle cell)
  var one-ah/edx: (addr handle cell) <- address one-storage
  new-integer one-ah, 1
  var add-storage: (handle cell)
  var add-ah/ebx: (addr handle cell) <- address add-storage
  new-symbol add-ah, "+"
  # input is (+ 1)
  var tmp-storage: (handle cell)
  var tmp-ah/esi: (addr handle cell) <- address tmp-storage
  new-pair tmp-ah, *one-ah, *nil-ah
  new-pair tmp-ah, *add-ah, *tmp-ah
#?   dump-cell tmp-ah
  #
  var globals-storage: global-table
  var globals/edx: (addr global-table) <- address globals-storage
  initialize-globals globals
  #
  evaluate tmp-ah, tmp-ah, *nil-ah, globals, t, 0/no-screen, 0/no-keyboard, 0/definitions-created, 0/call-number
  # no crash
}

fn apply-subtract _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply -"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to - are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "- needs 2 args but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for - is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  # args->right->left->value
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "- encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "- needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for - is not a number"
    return
  }
  var second-value/edx: (addr float) <- get second, number-data
  # subtract
  var result/xmm0: float <- copy *first-value
  result <- subtract *second-value
  new-float out, result
}

fn apply-multiply _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply *"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to * are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "* needs 2 args but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for * is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  # args->right->left->value
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "* encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "* needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for * is not a number"
    return
  }
  var second-value/edx: (addr float) <- get second, number-data
  # multiply
  var result/xmm0: float <- copy *first-value
  result <- multiply *second-value
  new-float out, result
}

fn apply-divide _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply /"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to / are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "/ needs 2 args but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for / is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  # args->right->left->value
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "/ encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "/ needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for / is not a number"
    return
  }
  var second-value/edx: (addr float) <- get second, number-data
  # divide
  var result/xmm0: float <- copy *first-value
  result <- divide *second-value
  new-float out, result
}

fn apply-remainder _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply %"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to % are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "% needs 2 args but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for % is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  # args->right->left->value
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "% encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "% needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for % is not a number"
    return
  }
  var second-value/edx: (addr float) <- get second, number-data
  # divide
  var quotient/xmm0: float <- copy *first-value
  quotient <- divide *second-value
  var quotient-int/eax: int <- truncate quotient
  quotient <- convert quotient-int
  var sub-result/xmm1: float <- copy quotient
  sub-result <- multiply *second-value
  var result/xmm0: float <- copy *first-value
  result <- subtract sub-result
  new-float out, result
}

fn apply-square-root _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply sqrt"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to sqrt are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "sqrt needs 1 arg but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "arg for sqrt is not a number"
    return
  }
  var first-value/eax: (addr float) <- get first, number-data
  # square-root
  var result/xmm0: float <- square-root *first-value
  new-float out, result
}

fn apply-abs _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply abs"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to abs are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "abs needs 1 arg but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "arg for abs is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  #
  var result/xmm0: float <- copy *first-value
  var zero: float
  compare result, zero
  {
    break-if-float>=
    var neg1/eax: int <- copy -1
    var neg1-f/xmm1: float <- convert neg1
    result <- multiply neg1-f
  }
  new-float out, result
}

fn apply-sgn _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply sgn"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to sgn are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "sgn needs 1 arg but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "arg for sgn is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  #
  var result/xmm0: float <- copy *first-value
  var zero: float
  $apply-sgn:core: {
    compare result, zero
    break-if-=
    {
      break-if-float>
      var neg1/eax: int <- copy -1
      result <- convert neg1
      break $apply-sgn:core
    }
    {
      break-if-float<
      var one/eax: int <- copy 1
      result <- convert one
      break $apply-sgn:core
    }
  }
  new-float out, result
}

fn apply-car _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply car"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to car are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "car needs 1 arg but got 0"
    return
  }
  # args->left
  var first-ah/edx: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 0/pair
    break-if-=
    error trace, "arg for car is not a pair"
    return
  }
  # nil? return nil
  {
    var nil?/eax: boolean <- nil? first
    compare nil?, 0/false
    break-if-=
    copy-object first-ah, out
    return
  }
  # car
  var result/eax: (addr handle cell) <- get first, left
  copy-object result, out
}

fn apply-cdr _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply cdr"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to cdr are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "cdr needs 1 arg but got 0"
    return
  }
  # args->left
  var first-ah/edx: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 0/pair
    break-if-=
    error trace, "arg for cdr is not a pair"
    return
  }
  # nil? return nil
  {
    var nil?/eax: boolean <- nil? first
    compare nil?, 0/false
    break-if-=
    copy-object first-ah, out
    return
  }
  # cdr
  var result/eax: (addr handle cell) <- get first, right
  copy-object result, out
}

fn apply-cons _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply cons"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'cons' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "cons needs 2 args but got 0"
    return
  }
  # args->left
  var first-ah/ecx: (addr handle cell) <- get args, left
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'cons' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'cons' needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  # cons
  new-pair out, *first-ah, *second-ah
}

fn apply-cons-check _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply cons?"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to cons? are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "cons? needs 1 arg but got 0"
    return
  }
  # args->left
  var first-ah/edx: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 0/pair
    break-if-=
    nil out
    return
  }
  new-integer out, 1
}


fn apply-cell-isomorphic _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply '='"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to '=' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'=' needs 2 args but got 0"
    return
  }
  # args->left
  var first-ah/ecx: (addr handle cell) <- get args, left
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'=' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'=' needs 2 args but got 1"
    return
  }
  var second-ah/edx: (addr handle cell) <- get right, left
  # compare
  var _first/eax: (addr cell) <- lookup *first-ah
  var first/ecx: (addr cell) <- copy _first
  var second/eax: (addr cell) <- lookup *second-ah
  var match?/eax: boolean <- cell-isomorphic? first, second, trace
  compare match?, 0/false
  {
    break-if-!=
    nil out
    return
  }
  new-integer out, 1/true
}

fn apply-not _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'not'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'not' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'not' needs 1 arg but got 0"
    return
  }
  # args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  # not
  var nil?/eax: boolean <- nil? first
  compare nil?, 0/false
  {
    break-if-!=
    nil out
    return
  }
  new-integer out, 1
}

fn apply-debug _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'debug'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'debug' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'debug' needs 1 arg but got 0"
    return
  }
  # dump args->left uglily to screen and wait for a keypress
  var first-ah/eax: (addr handle cell) <- get args, left
  dump-cell-from-cursor-over-full-screen first-ah, 7/fg 0/bg
  {
    var foo/eax: byte <- read-key 0/keyboard
    compare foo, 0
    loop-if-=
  }
  # return nothing
}

fn apply-< _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply '<'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to '<' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'<' needs 2 args but got 0"
    return
  }
  # args->left
  var first-ah/ecx: (addr handle cell) <- get args, left
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'<' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'<' needs 2 args but got 1"
    return
  }
  var second-ah/edx: (addr handle cell) <- get right, left
  # compare
  var _first/eax: (addr cell) <- lookup *first-ah
  var first/ecx: (addr cell) <- copy _first
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for '<' is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  var first-float/xmm0: float <- copy *first-value
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for '<' is not a number"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  compare first-float, *second-value
  {
    break-if-float<
    nil out
    return
  }
  new-integer out, 1/true
}

fn apply-> _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply '>'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to '>' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'>' needs 2 args but got 0"
    return
  }
  # args->left
  var first-ah/ecx: (addr handle cell) <- get args, left
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'>' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'>' needs 2 args but got 1"
    return
  }
  var second-ah/edx: (addr handle cell) <- get right, left
  # compare
  var _first/eax: (addr cell) <- lookup *first-ah
  var first/ecx: (addr cell) <- copy _first
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for '>' is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  var first-float/xmm0: float <- copy *first-value
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for '>' is not a number"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  compare first-float, *second-value
  {
    break-if-float>
    nil out
    return
  }
  new-integer out, 1/true
}

fn apply-<= _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply '<='"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to '<=' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'<=' needs 2 args but got 0"
    return
  }
  # args->left
  var first-ah/ecx: (addr handle cell) <- get args, left
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'<=' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'<=' needs 2 args but got 1"
    return
  }
  var second-ah/edx: (addr handle cell) <- get right, left
  # compare
  var _first/eax: (addr cell) <- lookup *first-ah
  var first/ecx: (addr cell) <- copy _first
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for '<=' is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  var first-float/xmm0: float <- copy *first-value
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for '<=' is not a number"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  compare first-float, *second-value
  {
    break-if-float<=
    nil out
    return
  }
  new-integer out, 1/true
}

fn apply->= _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply '>='"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to '>=' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'>=' needs 2 args but got 0"
    return
  }
  # args->left
  var first-ah/ecx: (addr handle cell) <- get args, left
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'>=' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'>=' needs 2 args but got 1"
    return
  }
  var second-ah/edx: (addr handle cell) <- get right, left
  # compare
  var _first/eax: (addr cell) <- lookup *first-ah
  var first/ecx: (addr cell) <- copy _first
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for '>=' is not a number"
    return
  }
  var first-value/ecx: (addr float) <- get first, number-data
  var first-float/xmm0: float <- copy *first-value
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for '>=' is not a number"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  compare first-float, *second-value
  {
    break-if-float>=
    nil out
    return
  }
  new-integer out, 1/true
}

fn apply-print _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'print'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'print' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'print' needs 2 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'print' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/ecx: (addr screen) <- copy _screen
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'print' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'print' needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var stream-storage: (stream byte 0x100)
  var stream/edi: (addr stream byte) <- address stream-storage
  print-cell second-ah, stream, trace
  draw-stream-wrapping-right-then-down-from-cursor-over-full-screen screen, stream, 7/fg, 0/bg
  # return what was printed
  copy-object second-ah, out
}

fn apply-clear _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'clear'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'clear' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'clear' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  var first-type/ecx: (addr int) <- get first, type
  compare *first-type, 3/stream
  {
    break-if-!=
    var stream-data-ah/eax: (addr handle stream byte) <- get first, text-data
    var _stream-data/eax: (addr stream byte) <- lookup *stream-data-ah
    var stream-data/ebx: (addr stream byte) <- copy _stream-data
    clear-stream stream-data
    return
  }
  compare *first-type, 5/screen
  {
    break-if-!=
    var screen-ah/eax: (addr handle screen) <- get first, screen-data
    var _screen/eax: (addr screen) <- lookup *screen-ah
    var screen/ecx: (addr screen) <- copy _screen
    clear-screen screen
    return
  }
  error trace, "first arg for 'clear' is not a screen or a stream"
}

fn apply-up _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'up'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'up' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'up' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'up' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/ecx: (addr screen) <- copy _screen
  #
  move-cursor-up screen
}

fn apply-down _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'down'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'down' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'down' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'down' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/ecx: (addr screen) <- copy _screen
  #
  move-cursor-down screen
}

fn apply-left _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'left'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'left' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'left' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'left' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/ecx: (addr screen) <- copy _screen
  #
  move-cursor-left screen
}

fn apply-right _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'right'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'right' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'right' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'right' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/ecx: (addr screen) <- copy _screen
  #
  move-cursor-right screen
}

fn apply-cr _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'cr'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'cr' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'cr' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'cr' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/ecx: (addr screen) <- copy _screen
  #
  move-cursor-to-left-margin-of-next-line screen
}

fn apply-pixel _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'pixel'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'pixel' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'pixel' needs 4 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'pixel' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edi: (addr screen) <- copy _screen
  # x = args->right->left->value
  var rest-ah/eax: (addr handle cell) <- get args, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'pixel' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'pixel' needs 4 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get rest, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'pixel' is not an int (x coordinate)"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var x/edx: int <- convert *second-value
  # y = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'pixel' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'pixel' needs 4 args but got 2"
    return
  }
  var third-ah/eax: (addr handle cell) <- get rest, left
  var third/eax: (addr cell) <- lookup *third-ah
  {
    var third-type/eax: (addr int) <- get third, type
    compare *third-type, 1/number
    break-if-=
    error trace, "third arg for 'pixel' is not an int (y coordinate)"
    return
  }
  var third-value/eax: (addr float) <- get third, number-data
  var y/ebx: int <- convert *third-value
  # color = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'pixel' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'pixel' needs 4 args but got 3"
    return
  }
  var fourth-ah/eax: (addr handle cell) <- get rest, left
  var fourth/eax: (addr cell) <- lookup *fourth-ah
  {
    var fourth-type/eax: (addr int) <- get fourth, type
    compare *fourth-type, 1/number
    break-if-=
    error trace, "fourth arg for 'pixel' is not an int (color; 0..0xff)"
    return
  }
  var fourth-value/eax: (addr float) <- get fourth, number-data
  var color/eax: int <- convert *fourth-value
  pixel screen, x, y, color
  # return nothing
}

fn apply-line _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'line'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'line' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'line' needs 6 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'line' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edi: (addr screen) <- copy _screen
  # x1 = args->right->left->value
  var rest-ah/eax: (addr handle cell) <- get args, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'line' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'line' needs 6 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get rest, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'line' is not a number (screen x coordinate of start point)"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var x1/edx: int <- convert *second-value
  # y1 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'line' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'line' needs 6 args but got 2"
    return
  }
  var third-ah/eax: (addr handle cell) <- get rest, left
  var third/eax: (addr cell) <- lookup *third-ah
  {
    var third-type/eax: (addr int) <- get third, type
    compare *third-type, 1/number
    break-if-=
    error trace, "third arg for 'line' is not a number (screen y coordinate of start point)"
    return
  }
  var third-value/eax: (addr float) <- get third, number-data
  var y1/ebx: int <- convert *third-value
  # x2 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'line' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'line' needs 6 args but got 3"
    return
  }
  var fourth-ah/eax: (addr handle cell) <- get rest, left
  var fourth/eax: (addr cell) <- lookup *fourth-ah
  {
    var fourth-type/eax: (addr int) <- get fourth, type
    compare *fourth-type, 1/number
    break-if-=
    error trace, "fourth arg for 'line' is not a number (screen x coordinate of end point)"
    return
  }
  var fourth-value/eax: (addr float) <- get fourth, number-data
  var x2/ecx: int <- convert *fourth-value
  # y2 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'line' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'line' needs 6 args but got 4"
    return
  }
  var fifth-ah/eax: (addr handle cell) <- get rest, left
  var fifth/eax: (addr cell) <- lookup *fifth-ah
  {
    var fifth-type/eax: (addr int) <- get fifth, type
    compare *fifth-type, 1/number
    break-if-=
    error trace, "fifth arg for 'line' is not a number (screen y coordinate of end point)"
    return
  }
  var fifth-value/eax: (addr float) <- get fifth, number-data
  var tmp/eax: int <- convert *fifth-value
  var y2: int
  copy-to y2, tmp
  # color = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'line' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'line' needs 6 args but got 5"
    return
  }
  var sixth-ah/eax: (addr handle cell) <- get rest, left
  var sixth/eax: (addr cell) <- lookup *sixth-ah
  {
    var sixth-type/eax: (addr int) <- get sixth, type
    compare *sixth-type, 1/number
    break-if-=
    error trace, "sixth arg for 'line' is not an int (color; 0..0xff)"
    return
  }
  var sixth-value/eax: (addr float) <- get sixth, number-data
  var color/eax: int <- convert *sixth-value
  draw-line screen, x1, y1, x2, y2, color
  # return nothing
}

fn apply-hline _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'hline'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'hline' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'hline' needs 5 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'hline' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edi: (addr screen) <- copy _screen
  # y = args->right->left->value
  var rest-ah/eax: (addr handle cell) <- get args, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'hline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'hline' needs 5 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get rest, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'hline' is not a number (screen y coordinate)"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var y/edx: int <- convert *second-value
  # x1 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'hline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'hline' needs 5 args but got 2"
    return
  }
  var third-ah/eax: (addr handle cell) <- get rest, left
  var third/eax: (addr cell) <- lookup *third-ah
  {
    var third-type/eax: (addr int) <- get third, type
    compare *third-type, 1/number
    break-if-=
    error trace, "third arg for 'hline' is not a number (screen x coordinate of start point)"
    return
  }
  var third-value/eax: (addr float) <- get third, number-data
  var x1/ebx: int <- convert *third-value
  # x2 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'hline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'hline' needs 5 args but got 3"
    return
  }
  var fourth-ah/eax: (addr handle cell) <- get rest, left
  var fourth/eax: (addr cell) <- lookup *fourth-ah
  {
    var fourth-type/eax: (addr int) <- get fourth, type
    compare *fourth-type, 1/number
    break-if-=
    error trace, "fourth arg for 'hline' is not a number (screen x coordinate of end point)"
    return
  }
  var fourth-value/eax: (addr float) <- get fourth, number-data
  var x2/ecx: int <- convert *fourth-value
  # color = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'hline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'hline' needs 5 args but got 5"
    return
  }
  var fifth-ah/eax: (addr handle cell) <- get rest, left
  var fifth/eax: (addr cell) <- lookup *fifth-ah
  {
    var fifth-type/eax: (addr int) <- get fifth, type
    compare *fifth-type, 1/number
    break-if-=
    error trace, "fifth arg for 'hline' is not an int (color; 0..0xff)"
    return
  }
  var fifth-value/eax: (addr float) <- get fifth, number-data
  var color/eax: int <- convert *fifth-value
  draw-horizontal-line screen, y, x1, x2, color
  # return nothing
}

fn apply-vline _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'vline'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'vline' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'vline' needs 5 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'vline' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edi: (addr screen) <- copy _screen
  # x = args->right->left->value
  var rest-ah/eax: (addr handle cell) <- get args, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'vline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'vline' needs 5 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get rest, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'vline' is not a number (screen x coordinate)"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var x/edx: int <- convert *second-value
  # y1 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'vline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'vline' needs 5 args but got 2"
    return
  }
  var third-ah/eax: (addr handle cell) <- get rest, left
  var third/eax: (addr cell) <- lookup *third-ah
  {
    var third-type/eax: (addr int) <- get third, type
    compare *third-type, 1/number
    break-if-=
    error trace, "third arg for 'vline' is not a number (screen y coordinate of start point)"
    return
  }
  var third-value/eax: (addr float) <- get third, number-data
  var y1/ebx: int <- convert *third-value
  # y2 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'vline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'vline' needs 5 args but got 3"
    return
  }
  var fourth-ah/eax: (addr handle cell) <- get rest, left
  var fourth/eax: (addr cell) <- lookup *fourth-ah
  {
    var fourth-type/eax: (addr int) <- get fourth, type
    compare *fourth-type, 1/number
    break-if-=
    error trace, "fourth arg for 'vline' is not a number (screen y coordinate of end point)"
    return
  }
  var fourth-value/eax: (addr float) <- get fourth, number-data
  var y2/ecx: int <- convert *fourth-value
  # color = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'vline' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'vline' needs 5 args but got 5"
    return
  }
  var fifth-ah/eax: (addr handle cell) <- get rest, left
  var fifth/eax: (addr cell) <- lookup *fifth-ah
  {
    var fifth-type/eax: (addr int) <- get fifth, type
    compare *fifth-type, 1/number
    break-if-=
    error trace, "fifth arg for 'vline' is not an int (color; 0..0xff)"
    return
  }
  var fifth-value/eax: (addr float) <- get fifth, number-data
  var color/eax: int <- convert *fifth-value
  draw-vertical-line screen, x, y1, y2, color
  # return nothing
}

fn apply-circle _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'circle'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'circle' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'circle' needs 5 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'circle' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edi: (addr screen) <- copy _screen
  # cx = args->right->left->value
  var rest-ah/eax: (addr handle cell) <- get args, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'circle' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'circle' needs 5 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get rest, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'circle' is not a number (screen x coordinate of center)"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var cx/edx: int <- convert *second-value
  # cy = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'circle' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'circle' needs 5 args but got 2"
    return
  }
  var third-ah/eax: (addr handle cell) <- get rest, left
  var third/eax: (addr cell) <- lookup *third-ah
  {
    var third-type/eax: (addr int) <- get third, type
    compare *third-type, 1/number
    break-if-=
    error trace, "third arg for 'circle' is not a number (screen y coordinate of center)"
    return
  }
  var third-value/eax: (addr float) <- get third, number-data
  var cy/ebx: int <- convert *third-value
  # r = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'circle' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'circle' needs 5 args but got 3"
    return
  }
  var fourth-ah/eax: (addr handle cell) <- get rest, left
  var fourth/eax: (addr cell) <- lookup *fourth-ah
  {
    var fourth-type/eax: (addr int) <- get fourth, type
    compare *fourth-type, 1/number
    break-if-=
    error trace, "fourth arg for 'circle' is not a number (screen radius)"
    return
  }
  var fourth-value/eax: (addr float) <- get fourth, number-data
  var r/ecx: int <- convert *fourth-value
  # color = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'circle' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'circle' needs 5 args but got 5"
    return
  }
  var fifth-ah/eax: (addr handle cell) <- get rest, left
  var fifth/eax: (addr cell) <- lookup *fifth-ah
  {
    var fifth-type/eax: (addr int) <- get fifth, type
    compare *fifth-type, 1/number
    break-if-=
    error trace, "fifth arg for 'circle' is not an int (color; 0..0xff)"
    return
  }
  var fifth-value/eax: (addr float) <- get fifth, number-data
  var color/eax: int <- convert *fifth-value
  draw-circle screen, cx, cy, r, color
  # return nothing
}

fn apply-bezier _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'bezier'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'bezier' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'bezier' needs 8 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'bezier' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edi: (addr screen) <- copy _screen
  # x0 = args->right->left->value
  var rest-ah/eax: (addr handle cell) <- get args, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'bezier' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'bezier' needs 8 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get rest, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'bezier' is not a number (screen x coordinate of start point)"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var x0/edx: int <- convert *second-value
  # y0 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'bezier' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'bezier' needs 8 args but got 2"
    return
  }
  var third-ah/eax: (addr handle cell) <- get rest, left
  var third/eax: (addr cell) <- lookup *third-ah
  {
    var third-type/eax: (addr int) <- get third, type
    compare *third-type, 1/number
    break-if-=
    error trace, "third arg for 'bezier' is not a number (screen y coordinate of start point)"
    return
  }
  var third-value/eax: (addr float) <- get third, number-data
  var y0/ebx: int <- convert *third-value
  # x1 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'bezier' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'bezier' needs 8 args but got 3"
    return
  }
  var fourth-ah/eax: (addr handle cell) <- get rest, left
  var fourth/eax: (addr cell) <- lookup *fourth-ah
  {
    var fourth-type/eax: (addr int) <- get fourth, type
    compare *fourth-type, 1/number
    break-if-=
    error trace, "fourth arg for 'bezier' is not a number (screen x coordinate of control point)"
    return
  }
  var fourth-value/eax: (addr float) <- get fourth, number-data
  var tmp/eax: int <- convert *fourth-value
  var x1: int
  copy-to x1, tmp
  # y1 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'bezier' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'bezier' needs 8 args but got 4"
    return
  }
  var fifth-ah/eax: (addr handle cell) <- get rest, left
  var fifth/eax: (addr cell) <- lookup *fifth-ah
  {
    var fifth-type/eax: (addr int) <- get fifth, type
    compare *fifth-type, 1/number
    break-if-=
    error trace, "fifth arg for 'bezier' is not a number (screen y coordinate of control point)"
    return
  }
  var fifth-value/eax: (addr float) <- get fifth, number-data
  var tmp/eax: int <- convert *fifth-value
  var y1: int
  copy-to y1, tmp
  # x2 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  var rest/esi: (addr cell) <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'bezier' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'bezier' needs 8 args but got 3"
    return
  }
  var sixth-ah/eax: (addr handle cell) <- get rest, left
  var sixth/eax: (addr cell) <- lookup *sixth-ah
  {
    var sixth-type/eax: (addr int) <- get sixth, type
    compare *sixth-type, 1/number
    break-if-=
    error trace, "sixth arg for 'bezier' is not a number (screen x coordinate of end point)"
    return
  }
  var sixth-value/eax: (addr float) <- get sixth, number-data
  var tmp/eax: int <- convert *sixth-value
  var x2: int
  copy-to x2, tmp
  # y2 = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'bezier' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'bezier' needs 8 args but got 4"
    return
  }
  var seventh-ah/eax: (addr handle cell) <- get rest, left
  var seventh/eax: (addr cell) <- lookup *seventh-ah
  {
    var seventh-type/eax: (addr int) <- get seventh, type
    compare *seventh-type, 1/number
    break-if-=
    error trace, "seventh arg for 'bezier' is not a number (screen y coordinate of end point)"
    return
  }
  var seventh-value/eax: (addr float) <- get seventh, number-data
  var tmp/eax: int <- convert *seventh-value
  var y2: int
  copy-to y2, tmp
  # color = rest->right->left->value
  var rest-ah/eax: (addr handle cell) <- get rest, right
  var _rest/eax: (addr cell) <- lookup *rest-ah
  rest <- copy _rest
  {
    var rest-type/eax: (addr int) <- get rest, type
    compare *rest-type, 0/pair
    break-if-=
    error trace, "'bezier' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'bezier' needs 8 args but got 5"
    return
  }
  var eighth-ah/eax: (addr handle cell) <- get rest, left
  var eighth/eax: (addr cell) <- lookup *eighth-ah
  {
    var eighth-type/eax: (addr int) <- get eighth, type
    compare *eighth-type, 1/number
    break-if-=
    error trace, "eighth arg for 'bezier' is not an int (color; 0..0xff)"
    return
  }
  var eighth-value/eax: (addr float) <- get eighth, number-data
  var color/eax: int <- convert *eighth-value
  draw-monotonic-bezier screen, x0, y0, x1, y1, x2, y2, color
  # return nothing
}

fn apply-wait-for-key _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'key'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'key' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'key' needs 1 arg but got 0"
    return
  }
  # keyboard = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 6/keyboard
    break-if-=
    error trace, "first arg for 'key' is not a keyboard"
    return
  }
  var keyboard-ah/eax: (addr handle gap-buffer) <- get first, keyboard-data
  var _keyboard/eax: (addr gap-buffer) <- lookup *keyboard-ah
  var keyboard/ecx: (addr gap-buffer) <- copy _keyboard
  var result/eax: int <- wait-for-key keyboard
  # return key typed
  new-integer out, result
}

fn wait-for-key keyboard: (addr gap-buffer) -> _/eax: int {
  # if keyboard is 0, use real keyboard
  {
    compare keyboard, 0/real-keyboard
    break-if-!=
    var key/eax: byte <- read-key 0/real-keyboard
    var result/eax: int <- copy key
    return result
  }
  # otherwise read from fake keyboard
  var g/eax: grapheme <- read-from-gap-buffer keyboard
  var result/eax: int <- copy g
  return result
}

fn apply-stream _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply stream"
  allocate-stream out
}

fn apply-write _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'write'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'write' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'write' needs 2 args but got 0"
    return
  }
  # stream = args->left
  var first-ah/edx: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 3/stream
    break-if-=
    error trace, "first arg for 'write' is not a stream"
    return
  }
  var stream-data-ah/eax: (addr handle stream byte) <- get first, text-data
  var _stream-data/eax: (addr stream byte) <- lookup *stream-data-ah
  var stream-data/ebx: (addr stream byte) <- copy _stream-data
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'write' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'write' needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'write' is not a number/grapheme"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var x-float/xmm0: float <- copy *second-value
  var x/eax: int <- convert x-float
  var x-grapheme/eax: grapheme <- copy x
  write-grapheme stream-data, x-grapheme
  # return the stream
  copy-object first-ah, out
}

fn apply-rewind _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'rewind'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'rewind' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'rewind' needs 1 arg but got 0"
    return
  }
  # stream = args->left
  var first-ah/edx: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 3/stream
    break-if-=
    error trace, "first arg for 'rewind' is not a stream"
    return
  }
  var stream-data-ah/eax: (addr handle stream byte) <- get first, text-data
  var _stream-data/eax: (addr stream byte) <- lookup *stream-data-ah
  var stream-data/ebx: (addr stream byte) <- copy _stream-data
  rewind-stream stream-data
  copy-object first-ah, out
}

fn apply-read _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'read'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'read' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'read' needs 1 arg but got 0"
    return
  }
  # stream = args->left
  var first-ah/edx: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 3/stream
    break-if-=
    error trace, "first arg for 'read' is not a stream"
    return
  }
  var stream-data-ah/eax: (addr handle stream byte) <- get first, text-data
  var _stream-data/eax: (addr stream byte) <- lookup *stream-data-ah
  var stream-data/ebx: (addr stream byte) <- copy _stream-data
#?   rewind-stream stream-data
  var result-grapheme/eax: grapheme <- read-grapheme stream-data
  var result/eax: int <- copy result-grapheme
  new-integer out, result
}

fn apply-lines _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'lines'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'lines' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'lines' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'lines' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edx: (addr screen) <- copy _screen
  # compute dimensions
  var dummy/eax: int <- copy 0
  var height/ecx: int <- copy 0
  dummy, height <- screen-size screen
  var result/xmm0: float <- convert height
  new-float out, result
}

fn apply-abort _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  abort "aa"
}

fn apply-columns _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'columns'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'columns' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'columns' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'columns' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edx: (addr screen) <- copy _screen
  # compute dimensions
  var width/eax: int <- copy 0
  var dummy/ecx: int <- copy 0
  width, dummy <- screen-size screen
  var result/xmm0: float <- convert width
  new-float out, result
}

fn apply-width _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'width'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'width' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'width' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'width' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edx: (addr screen) <- copy _screen
  # compute dimensions
  var width/eax: int <- copy 0
  var dummy/ecx: int <- copy 0
  width, dummy <- screen-size screen
  width <- shift-left 3/log2-font-width
  var result/xmm0: float <- convert width
  new-float out, result
}

fn apply-height _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'height'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'height' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'height' needs 1 arg but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'height' is not a screen"
    return
  }
  var screen-ah/eax: (addr handle screen) <- get first, screen-data
  var _screen/eax: (addr screen) <- lookup *screen-ah
  var screen/edx: (addr screen) <- copy _screen
  # compute dimensions
  var dummy/eax: int <- copy 0
  var height/ecx: int <- copy 0
  dummy, height <- screen-size screen
  height <- shift-left 4/log2-font-height
  var result/xmm0: float <- convert height
  new-float out, result
}

fn apply-new-screen _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'screen'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'screen' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'screen' needs 2 args but got 0"
    return
  }
  # args->left->value
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 1/number
    break-if-=
    error trace, "first arg for 'screen' is not a number (screen width in pixels)"
    return
  }
  var first-value-a/ecx: (addr float) <- get first, number-data
  var first-value/ecx: int <- convert *first-value-a
  # args->right->left->value
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'screen' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'screen' needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 1/number
    break-if-=
    error trace, "second arg for 'screen' is not a number (screen height in pixels)"
    return
  }
  var second-value-a/edx: (addr float) <- get second, number-data
  var second-value/edx: int <- convert *second-value-a
  # create fake screen
  new-fake-screen out, first-value, second-value, 1/pixel-graphics
}

fn apply-blit _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'blit'"
  var args-ah/eax: (addr handle cell) <- copy _args-ah
  var _args/eax: (addr cell) <- lookup *args-ah
  var args/esi: (addr cell) <- copy _args
  {
    var args-type/eax: (addr int) <- get args, type
    compare *args-type, 0/pair
    break-if-=
    error trace, "args to 'blit' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'blit' needs 2 args but got 0"
    return
  }
  # screen = args->left
  var first-ah/eax: (addr handle cell) <- get args, left
  var first/eax: (addr cell) <- lookup *first-ah
  {
    var first-type/eax: (addr int) <- get first, type
    compare *first-type, 5/screen
    break-if-=
    error trace, "first arg for 'blit' is not a screen"
    return
  }
  var src-ah/eax: (addr handle screen) <- get first, screen-data
  var _src/eax: (addr screen) <- lookup *src-ah
  var src/ecx: (addr screen) <- copy _src
  # args->right->left
  var right-ah/eax: (addr handle cell) <- get args, right
  var right/eax: (addr cell) <- lookup *right-ah
  {
    var right-type/eax: (addr int) <- get right, type
    compare *right-type, 0/pair
    break-if-=
    error trace, "'blit' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'blit' needs 2 args but got 1"
    return
  }
  var second-ah/eax: (addr handle cell) <- get right, left
  var second/eax: (addr cell) <- lookup *second-ah
  {
    var second-type/eax: (addr int) <- get second, type
    compare *second-type, 5/screen
    break-if-=
    error trace, "second arg for 'blit' is not a screen"
    return
  }
  var dest-ah/eax: (addr handle screen) <- get second, screen-data
  var dest/eax: (addr screen) <- lookup *dest-ah
  #
  convert-graphemes-to-pixels src
  copy-pixels src, dest
}