about summary refs log blame commit diff stats
path: root/dwm.1
blob: 0e107785bc60c4433f3dcb11f162387809721409 (plain) (tree)
1
2
3
4
5
6
7
8
9
                      
        
                             
            
      

               
                                                                           
                                                                              
                                                              
  
                                                                              
                                                                          
                                                                           


                                                                           
  
                                                                           
                                                                  
  
                                                                              
                                                                         
                                                                 
                                                                    

                                                                           

                                                                        
  
                                                                    
           

      
                                                          
         

              

                                              
   
          
                                                                              
                                                
   
          
                                                                             
   
                
                                                            
   
                
                                                                      
                     
   
                      
     
          
   


                       








                     
              
                                            
   
          
                  
   
          
                      
   





                          
          
                                                          
   











                                                                     
                      
     
       
                      
   
                 
                                 
   
                               
          
      
                           
   
               
                     

       
   
          
                              
   
                        
                           

                     
   
                 
         
                  
   
                
                                                                                        
   
                
                                                        
   
                
                                                                                          
                 
                                                                            
                                            

             
        
                          
         
                                                      
            



                                                                                   


                                                                                  

                          
  
                                     





                                                                                   
.TH DWM 1 dwm\-VERSION
.SH NAME
dwm \- dynamic window manager
.SH SYNOPSIS
.B dwm
.RB [ \-v ]
.SH DESCRIPTION
dwm is a dynamic window manager for X. It manages windows in tiled, monocle
and floating layouts. Either layout can be applied dynamically, optimising the
environment for the application in use and the task performed.
.P
In tiled layouts windows are managed in a master and stacking area. The master
area contains the window which currently needs most attention, whereas the
stacking area contains all other windows. In monocle layout all windows are
maximised to the screen size. In floating layout windows can be resized and
moved freely. Dialog windows are always managed floating, regardless of the
layout applied.
.P
Windows are grouped by tags. Each window can be tagged with one or multiple
tags. Selecting certain tags displays all windows with these tags.
.P
dwm contains a small status bar which displays all available tags, the layout,
the title of the focused window, and the text read from standard input. A
floating window is indicated with an empty square and a maximised
floating window is indicated with a filled square before the windows
title.  The selected tags are indicated with a different color. The tags of
the focused window are indicated with a filled square in the top left
corner.  The tags which are applied to one or more windows are indicated
with an empty square in the top left corner.
.P
dwm draws a small border around windows to indicate the focus state.
.SH OPTIONS
.TP
.B \-v
prints version information to standard output, then exits.
.SH USAGE
.SS Status bar
.TP
.B Standard input
is read and displayed in the status text area.
.TP
.B Button1
click on a tag label to display all windows with that tag, click on the layout
label toggles between tiled and floating layout.
.TP
.B Button3
click on a tag label adds/removes all windows with that tag to/from the view.
.TP
.B Mod1\-Button1
click on a tag label applies that tag to the focused window.
.TP
.B Mod1\-Button3
click on a tag label adds/removes that tag to/from the focused window.
.SS Keyboard commands
.TP
.B Mod1\-Shift\-Return
Start
.BR xterm.
.TP
.B Mod1\-b
Toggles bar on and off.
.TP
.B Mod1\-t
Sets tiled layout.
.TP
.B Mod1\-f
Sets floating layout.
.TP
.B Mod1\-f
Sets monocle layout.
.TP
.B Mod1\-space
Toggles between current and previous layout.
.TP
.B Mod1\-j
Focus next window.
.TP
.B Mod1\-k
Focus previous window.
.TP
.B Mod1\-h
Decrease master area size.
.TP
.B Mod1\-l
Increase master area size.
.TP
.B Mod1\-m
Toggle between maximisation meta-layout and active layout.
.TP
.B Mod1\-Return
Zooms/cycles focused window to/from master area (tiled layouts only).
.TP
.B Mod1\-Shift\-c
Close focused window.
.TP
.B Mod1\-Shift\-space
Toggle focused window between tiled and floating state.
.TP
.B Mod1\-Tab
Toggles to the previously selected tags.
.TP
.B Mod1\-Shift\-[1..n]
Apply
.RB nth
tag to focused window.
.TP
.B Mod1\-Shift\-0
Apply all tags to focused window.
.TP
.B Mod1\-Control\-Shift\-[1..n]
Add/remove
.B nth
tag to/from focused window.
.TP
.B Mod1\-[1..n]
View all windows with
.BR nth
tag.
.TP
.B Mod1\-0
View all windows with any tag.
.TP
.B Mod1\-Control\-[1..n]
Add/remove all windows with
.BR nth
tag to/from the view.
.TP
.B Mod1\-Shift\-q
Quit dwm.
.SS Mouse commands
.TP
.B Mod1\-Button1
Move focused window while dragging. Tiled windows will be toggled to the floating state.
.TP
.B Mod1\-Button2
Toggles focused window between floating and tiled state.
.TP
.B Mod1\-Button3
Resize focused window while dragging. Tiled windows will be toggled to the floating state.
.SH CUSTOMIZATION
dwm is customized by creating a custom config.h and (re)compiling the source
code. This keeps it fast, secure and simple.
.SH SEE ALSO
.BR dmenu (1)
.SH BUGS
The status bar may display
.BR "EOF"
when dwm has been started by an X session manager like
.BR xdm (1),
because those close standard output before executing dwm.
.P
Java applications which use the XToolkit/XAWT backend may draw grey windows
only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early
JDK 1.6 versions, because it assumes a reparenting window manager. As a workaround
you can use JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or you
can set the following environment variable (to use the older Motif
backend instead):
.BR AWT_TOOLKIT=MToolkit .
.P
GTK 2.10.9+ versions contain a broken
.BR Save\-As
file dialog implementation,
which requests to reconfigure its window size in an endless loop. However, its
window is still respondable during this state, so you can simply ignore the flicker
until a new GTK version appears, which will fix this bug, approximately
GTK 2.10.12+ versions.
='#n2567'>2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 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 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970
# 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"
  append-primitive self, "len"
  # 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"
  # for arrays
  append-primitive self, "array"
  append-primitive self, "populate"
  append-primitive self, "index"
  append-primitive self, "iset"
  # for images
  append-primitive self, "img"
  # 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? len", 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
  y <- increment
  set-cursor-position screen, right-min, y
  draw-text-wrapping-right-then-down-from-cursor screen, "arrays", 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, "  array index iset len", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy right-min
  tmpx <- draw-text-rightward screen, "  populate", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": int _ -> array", tmpx, xmax, y, 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, "images", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg
  y <- increment
  var tmpx/eax: int <- copy right-min
  tmpx <- draw-text-rightward screen, "  img", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
  tmpx <- draw-text-rightward screen, ": screen stream x y w h", tmpx, xmax, y, 7/fg=grey, 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 _ -> _", 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, "  clear", 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, "input", 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 primitive?/eax: boolean <- primitive? value
  return primitive?
}

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 len?/eax: boolean <- string-equal? f-name, "len"
    compare len?, 0/false
    break-if-=
    apply-len 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 array?/eax: boolean <- string-equal? f-name, "array"
    compare array?, 0/false
    break-if-=
    apply-array args-ah, out, trace
    return
  }
  {
    var populate?/eax: boolean <- string-equal? f-name, "populate"
    compare populate?, 0/false
    break-if-=
    apply-populate args-ah, out, trace
    return
  }
  {
    var index?/eax: boolean <- string-equal? f-name, "index"
    compare index?, 0/false
    break-if-=
    apply-index args-ah, out, trace
    return
  }
  {
    var iset?/eax: boolean <- string-equal? f-name, "iset"
    compare iset?, 0/false
    break-if-=
    apply-iset args-ah, out, trace
    return
  }
  {
    var render-image?/eax: boolean <- string-equal? f-name, "img"
    compare render-image?, 0/false
    break-if-=
    apply-render-image 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-len _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply len"
  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 len are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "len 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-pair?/eax: boolean <- pair? first
      compare first-pair?, 0/false
    }
    break-if-=
    var result/eax: int <- list-length first
    new-integer out, result
    return
  }
  {
    {
      var first-array?/eax: boolean <- array? first
      compare first-array?, 0/false
    }
    break-if-=
    var result/eax: int <- array-length first
    new-integer out, result
    return
  }
  nil out
}

fn list-length in: (addr cell) -> _/eax: int {
  var curr/ecx: (addr cell) <- copy in
  var result/edi: int <- copy 0
  {
    var pair?/eax: boolean <- pair? curr
    {
      compare pair?, 0/false
      break-if-!=
      abort "len: ran into a non-cons"
    }
    var nil?/eax: boolean <- nil? curr
    compare nil?, 0/false
    break-if-!=
    result <- increment
    var next-ah/eax: (addr handle cell) <- get curr, right
    var next/eax: (addr cell) <- lookup *next-ah
    curr <- copy next
    loop
  }
  return result
}

fn array-length _in: (addr cell) -> _/eax: int {
  var in/esi: (addr cell) <- copy _in
  var in-data-ah/eax: (addr handle array handle cell) <- get in, array-data
  var in-data/eax: (addr array handle cell) <- lookup *in-data-ah
  var result/eax: int <- length in-data
  return result
}

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-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
}

fn apply-array _args-ah: (addr handle cell), _out-ah: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'array'"
  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 'array' are not a list"
    return
  }
  var capacity/eax: int <- list-length args
  var out-ah/edi: (addr handle cell) <- copy _out-ah
  new-array out-ah, capacity
  var out/eax: (addr cell) <- lookup *out-ah
  var out-data-ah/eax: (addr handle array handle cell) <- get out, array-data
  var _out-data/eax: (addr array handle cell) <- lookup *out-data-ah
  var out-data/edi: (addr array handle cell) <- copy _out-data
  var i/ecx: int <- copy 0
  {
    var done?/eax: boolean <- nil? args
    compare done?, 0/false
    break-if-!=
    var curr-ah/eax: (addr handle cell) <- get args, left
    var dest-ah/edx: (addr handle cell) <- index out-data, i
    copy-object curr-ah, dest-ah
    # update loop variables
    i <- increment
    var next-ah/eax: (addr handle cell) <- get args, right
    var next/eax: (addr cell) <- lookup *next-ah
    args <- copy next
    loop
  }
}

fn apply-populate _args-ah: (addr handle cell), _out-ah: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'populate'"
  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 'populate' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'populate' 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, "'populate' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'populate' needs 2 args but got 1"
    return
  }
  var second-ah/edx: (addr handle cell) <- get right, 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 'populate' is not a number"
    return
  }
  var first-value/eax: (addr float) <- get first, number-data
  var capacity/ecx: int <- convert *first-value
  var out-ah/edi: (addr handle cell) <- copy _out-ah
  new-array out-ah, capacity
  var out/eax: (addr cell) <- lookup *out-ah
  var data-ah/eax: (addr handle array handle cell) <- get out, array-data
  var data/eax: (addr array handle cell) <- lookup *data-ah
  var i/ebx: int <- copy 0
  {
    compare i, capacity
    break-if->=
    var curr-ah/ecx: (addr handle cell) <- index data, i
    copy-object second-ah, curr-ah
    i <- increment
    loop
  }
}

fn apply-index _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'index'"
  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 'index' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'index' 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, "'index' encountered non-pair"
    return
  }
  {
    var nil?/eax: boolean <- nil? right
    compare nil?, 0/false
    break-if-=
    error trace, "'index' needs 2 args but got 1"
    return
  }
  var second-ah/edx: (addr handle cell) <- get right, left
  # index
  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, 7/array
    break-if-=
    error trace, "first arg for 'index' is not an array"
    return
  }
  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 'index' is not a number"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var index/edx: int <- truncate *second-value
  var data-ah/eax: (addr handle array handle cell) <- get first, array-data
  var data/eax: (addr array handle cell) <- lookup *data-ah
  {
    var len/eax: int <- length data
    compare index, len
    break-if-<
    error trace, "index: too few elements in array"
    compare index, len
    {
      break-if-<=
      error trace, "foo"
    }
    return
  }
  var offset/edx: (offset handle cell) <- compute-offset data, index
  var src/eax: (addr handle cell) <- index data, offset
  copy-object src, out
}

fn apply-iset _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'iset'"
  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 'iset' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'iset' needs 3 args but got 0"
    return
  }
  # array = 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, 7/array
    break-if-=
    error trace, "first arg for 'iset' is not an array"
    return
  }
  var array-ah/eax: (addr handle array handle cell) <- get first, array-data
  var _array/eax: (addr array handle cell) <- lookup *array-ah
  var array/ecx: (addr array handle cell) <- copy _array
  # idx = 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, "'iset' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'iset' needs 3 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 'iset' is not an int (index)"
    return
  }
  var second-value/eax: (addr float) <- get second, number-data
  var idx/eax: int <- truncate *second-value
  # offset based on idx after bounds check
  var max/edx: int <- length array
  compare idx, max
  {
    break-if-<
    error trace, "iset: too few elements in array"
    return
  }
  var offset/edx: (offset handle cell) <- compute-offset array, idx
  # val = rest->right->left
  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, "'iset' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'iset' needs 3 args but got 2"
    return
  }
  var val-ah/eax: (addr handle cell) <- get rest, left
  # copy
  var dest/edi: (addr handle cell) <- index array, offset
  copy-object val-ah, dest
  # return nothing
}

fn apply-render-image _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
  trace-text trace, "eval", "apply 'img'"
  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 'img' are not a list"
    return
  }
  var empty-args?/eax: boolean <- nil? args
  compare empty-args?, 0/false
  {
    break-if-=
    error trace, "'img' 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 'img' 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, "'img' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'img' 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, 3/stream
    break-if-=
    error trace, "second arg for 'img' is not a stream (image data in ascii netpbm)"
    return
  }
  var img-data-ah/eax: (addr handle stream byte) <- get second, text-data
  var img-data/eax: (addr stream byte) <- lookup *img-data-ah
  var img-h: (handle cell)
  var img-ah/ecx: (addr handle cell) <- address img-h
  new-image img-ah, img-data
  # x = 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, "'img' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'img' 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 'img' is not a number (screen x coordinate of top left)"
    return
  }
  var third-value/eax: (addr float) <- get third, number-data
  var x/ebx: int <- convert *third-value
  # y = 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, "'img' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'img' 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 'img' is not a number (screen x coordinate of end point)"
    return
  }
  var fourth-value/eax: (addr float) <- get fourth, number-data
  var y/ecx: int <- convert *fourth-value
  # w = 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, "'img' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'img' 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 'img' 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 w: int
  copy-to w, tmp
  # h = 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, "'img' encountered non-pair"
    return
  }
  {
    var rest-nil?/eax: boolean <- nil? rest
    compare rest-nil?, 0/false
    break-if-=
    error trace, "'img' 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 'img' is not an int (height)"
    return
  }
  var sixth-value/eax: (addr float) <- get sixth, number-data
  var tmp/eax: int <- convert *sixth-value
  var h: int
  copy-to h, tmp
  #
  var img-cell-ah/eax: (addr handle cell) <- address img-h
  var img-cell/eax: (addr cell) <- lookup *img-cell-ah
  var img-ah/eax: (addr handle image) <- get img-cell, image-data
  var img/eax: (addr image) <- lookup *img-ah
  render-image screen, img, x y, w h
  # return nothing
}

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