# Primitives for screen control. # Require Linux and a modern terminal. == code enable-screen-grid-mode: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (flush Stdout) (flush Stderr) # switch to second screen buffer (write 1 Esc) (write 1 "[?1049h") # (clear-real-screen) $enable-screen-grid-mode:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return enable-screen-type-mode: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # switch to first screen buffer (write 1 Esc) (write 1 "[?1049l") $enable-screen-type-mode:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return real-screen-size: # -> nrows/eax: int, ncols/ecx: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 52/push-edx 53/push-ebx 56/push-esi 57/push-edi # (_maybe-open-terminal) # var window-size-info/esi: (addr winsize) # winsize is a type from the Linux kernel. We don't care how large it is. 81 5/subop/subtract %esp 0x40/imm32 89/<- %esi 4/r32/esp # ioctl(*Terminal-file-descriptor, TIOCGWINSZ, window-size-info) 89/<- %edx 6/r32/esi b9/copy-to-ecx 0x5413/imm32/TIOCGWINSZ 8b/-> *Terminal-file-descriptor 3/r32/ebx e8/call syscall_ioctl/disp32 # some bitworking to extract 2 16-bit shorts 8b/-> *esi 0/r32/eax 81 4/subop/and %eax 0xffff/imm32 8b/-> *esi 1/r32/ecx c1/shift 5/subop/logical-right %ecx 0x10/imm8 $real-screen-size:end: # . reclaim locals 81 0/subop/add %esp 0x40/imm32 # . restore registers 5f/pop-to-edi 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return clear-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write 1 Esc) (write 1 "[H") (write 1 Esc) (write 1 "[2J") $clear-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # row and col count from the top-left as (1, 1) move-cursor-on-real-screen: # row: int, column: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var buf/ecx: (stream byte 32) 81 5/subop/subtract %esp 0x20/imm32 68/push 0x20/imm32/size 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %ecx 4/r32/esp # construct directive in buf (write %ecx Esc) (write %ecx "[") (write-int32-decimal %ecx *(ebp+8)) (write %ecx ";") (write-int32-decimal %ecx *(ebp+0xc)) (write %ecx "H") # flush (write-stream 2 %ecx) $move-cursor-on-real-screen:end: # . reclaim locals 81 0/subop/add %esp 0x2c/imm32 # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return print-string-to-real-screen: # s: (addr array byte) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout *(ebp+8)) #? (flush Stdout) $print-string-to-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return print-slice-to-real-screen: # s: (addr slice) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-slice-buffered Stdout *(ebp+8)) #? (flush Stdout) $print-slice-to-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return print-stream-to-real-screen: # s: (addr stream byte) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-stream-data Stdout *(ebp+8)) #? (flush Stdout) $print-stream-to-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # print a grapheme in utf-8 (only up to 4 bytes so far) print-grapheme-to-real-screen: # c: grapheme # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax # var curr/eax: byte = 0 b8/copy-to-eax 0/imm32 # curr = *(ebp+8) 8a/byte-> *(ebp+8) 0/r32/al # if (curr == 0) return 3d/compare-eax-and 0/imm32 74/jump-if-= $print-grapheme-to-real-screen:end/disp8 # (print-byte-to-real-screen %eax) # curr = *(ebp+9) 8a/byte-> *(ebp+9) 0/r32/al # if (curr == 0) return 3d/compare-eax-and 0/imm32 74/jump-if-= $print-grapheme-to-real-screen:end/disp8 # (print-byte-to-real-screen %eax) # curr = *(ebp+10) 8a/byte-> *(ebp+0xa) 0/r32/al # if (curr == 0) return 3d/compare-eax-and 0/imm32 74/jump-if-= $print-grapheme-to-real-screen:end/disp8 # (print-byte-to-real-screen %eax) # curr = *(ebp+11) 8a/byte-> *(ebp+0xb) 0/r32/al # if (curr == 0) return 3d/compare-eax-and 0/imm32 74/jump-if-= $print-grapheme-to-real-screen:end/disp8 # (print-byte-to-real-screen %eax) $print-grapheme-to-real-screen:end: # . restore registers 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return print-byte-to-real-screen: # c: byte # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var s/ecx: (addr array byte) ff 6/subop/push *(ebp+8) 68/push 1/imm32/size 89/<- %ecx 4/r32/esp (write-buffered Stdout %ecx) #? (flush Stdout) $print-byte-to-real-screen:end: # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return flush-stdout: (flush Stdout) c3/return print-int32-hex-to-real-screen: # n: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-int32-hex-buffered Stdout *(ebp+8)) #? (flush Stdout) $print-int32-hex-to-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return print-int32-decimal-to-real-screen: # n: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-int32-decimal-buffered Stdout *(ebp+8)) #? (flush Stdout) $print-int32-decimal-to-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return write-int32-decimal-buffered: # f: (addr buffered-file), n: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var ecx: (stream byte 16) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/size 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %ecx 4/r32/esp (write-int32-decimal %ecx *(ebp+0xc)) (write-stream-data *(ebp+8) %ecx) $write-int32-decimal-buffered:end: # . reclaim locals 81 0/subop/add %esp 0x1c/imm32 # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return reset-formatting-on-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout Esc) (write-buffered Stdout "(B") (write-buffered Stdout Esc) (write-buffered Stdout "[m") $reset-formatting-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return start-color-on-real-screen: # fg: int, bg: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var buf/ecx: (stream byte 32) 81 5/subop/subtract %esp 0x20/imm32 68/push 0x20/imm32/size 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %ecx 4/r32/esp # construct directive in buf # . set fg (write %ecx Esc) (write %ecx "[38;5;") (write-int32-decimal %ecx *(ebp+8)) (write %ecx "m") # . set bg (write %ecx Esc) (write %ecx "[48;5;") (write-int32-decimal %ecx *(ebp+0xc)) (write %ecx "m") # flush (write-stream-data Stdout %ecx) $start-color-on-real-screen:end: # . reclaim locals 81 0/subop/add %esp 0x2c/imm32 # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return start-bold-on-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout Esc) (write-buffered Stdout "[1m") $start-bold-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return start-underline-on-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout Esc) (write-buffered Stdout "[4m") $start-underline-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return start-reverse-video-on-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout Esc) (write-buffered Stdout "[7m") $start-reverse-video-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # might require enabling blinking in your terminal program start-blinking-on-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout Esc) (write-buffered Stdout "[5m") $start-blinking-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return hide-cursor-on-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout Esc) (write-buffered Stdout "[?25l") $hide-cursor-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return show-cursor-on-real-screen: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered Stdout Esc) (write-buffered Stdout "[?12l") (write-buffered Stdout Esc) (write-buffered Stdout "[?25h") $show-cursor-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # This is a low-level detail; I don't think everything should be a file. # # Open "/dev/tty" if necessary and cache its file descriptor in Terminal-file-descriptor # where later primitives can use it. _maybe-open-terminal: 81 7/subop/compare *Terminal-file-descriptor -1/imm32 75/jump-if-!= $_maybe-open-terminal:epilogue/disp8 # . save registers 50/push-eax 51/push-ecx 53/push-ebx # open("/dev/tty", O_RDWR) bb/copy-to-ebx Terminal-filename/imm32 b9/copy-to-ecx 2/imm32/O_RDWR e8/call syscall_open/disp32 89/<- *Terminal-file-descriptor 0/r32/eax $_maybe-open-terminal:end: # . restore registers 5b/pop-to-ebx 59/pop-to-ecx 58/pop-to-eax $_maybe-open-terminal:epilogue: c3/return == data Terminal-file-descriptor: # (addr int) -1/imm32 Esc: # (addr array byte) # size 1/imm32 # data 0x1b Terminal-filename: # (addr kernel-string) # "/dev/null" 2f/slash 64/d 65/e 76/v 2f/slash 74/t 74/t 79/y 0/nul