1 # Primitives for keyboard control. 2 # Require Linux and a modern terminal. 3 4 == code 5 6 enable-keyboard-immediate-mode: 7 # . prologue 8 55/push-ebp 9 89/<- %ebp 4/r32/esp 10 # . save registers 11 50/push-eax 12 51/push-ecx 13 52/push-edx 14 53/push-ebx 15 56/push-esi 16 57/push-edi 17 # 18 (_maybe-open-terminal) 19 # var terminal-info/esi: (addr termios) 20 # termios is a type from the Linux kernel. We don't care how large it is. 21 81 5/subop/subtract %esp 0x100/imm32 22 89/<- %esi 4/r32/esp 23 # ioctl(*Terminal-file-descriptor, TCGETS, terminal-info) 24 89/<- %edx 6/r32/esi 25 b9/copy-to-ecx 0x5401/imm32/TCGETS 26 8b/-> *Terminal-file-descriptor 3/r32/ebx 27 e8/call syscall_ioctl/disp32 28 # terminal-info->c_iflags &= Keyboard-immediate-mode-iflags 29 #? (write-buffered Stderr "iflags before: ") 30 #? (write-int32-hex-buffered Stderr *esi) 31 #? (write-buffered Stderr Newline) 32 #? (flush Stderr) 33 8b/-> *esi 0/r32/eax # Termios-c_iflag 34 23/and *Keyboard-immediate-mode-iflags 0/r32/eax 35 89/<- *esi 0/r32/eax # Termios-c_iflag 36 #? (write-buffered Stderr "iflags after: ") 37 #? (write-int32-hex-buffered Stderr *esi) 38 #? (write-buffered Stderr Newline) 39 #? (flush Stderr) 40 # terminal-info->c_lflags &= Keyboard-immediate-mode-lflags 41 #? (write-buffered Stderr "lflags before: ") 42 #? (write-int32-hex-buffered Stderr *(esi+0xc)) 43 #? (write-buffered Stderr Newline) 44 #? (flush Stderr) 45 8b/-> *(esi+0xc) 0/r32/eax # Termios-c_lflag 46 23/and *Keyboard-immediate-mode-lflags 0/r32/eax 47 89/<- *(esi+0xc) 0/r32/eax # Termios-c_lflag 48 #? (write-buffered Stderr "lflags after: ") 49 #? (write-int32-hex-buffered Stderr *(esi+0xc)) 50 #? (write-buffered Stderr Newline) 51 #? (flush Stderr) 52 # ioctl(*Terminal-file-descriptor, TCSETS, terminal-info) 53 89/<- %edx 6/r32/esi 54 b9/copy-to-ecx 0x5402/imm32/TCSETS 55 8b/-> *Terminal-file-descriptor 3/r32/ebx 56 e8/call syscall_ioctl/disp32 57 $enable-keyboard-immediate-mode:end: 58 # . reclaim locals 59 81 0/subop/add %esp 0x100/imm32 60 # . restore registers 61 5f/pop-to-edi 62 5e/pop-to-esi 63 5b/pop-to-ebx 64 5a/pop-to-edx 65 59/pop-to-ecx 66 58/pop-to-eax 67 # . epilogue 68 89/<- %esp 5/r32/ebp 69 5d/pop-to-ebp 70 c3/return 71 72 enable-keyboard-type-mode: 73 # . prologue 74 55/push-ebp 75 89/<- %ebp 4/r32/esp 76 # . save registers 77 50/push-eax 78 51/push-ecx 79 52/push-edx 80 53/push-ebx 81 56/push-esi 82 57/push-edi 83 # 84 (_maybe-open-terminal) 85 # var terminal-info/esi: (addr termios) 86 # termios is a type from the Linux kernel. We don't care how large it is. 87 81 5/subop/subtract %esp 0x100/imm32 88 89/<- %esi 4/r32/esp 89 # ioctl(*Terminal-file-descriptor, TCGETS, terminal-info) 90 89/<- %edx 6/r32/esi 91 b9/copy-to-ecx 0x5401/imm32/TCGETS 92 8b/-> *Terminal-file-descriptor 3/r32/ebx 93 e8/call syscall_ioctl/disp32 94 # terminal-info->c_iflags |= Keyboard-type-mode-iflags 95 8b/-> *esi 0/r32/eax # Termios-c_iflag 96 0b/or *Keyboard-type-mode-iflags 0/r32/eax 97 89/<- *esi 0/r32/eax # Termios-c_iflag 98 # terminal-info->c_lflags |= Keyboard-type-mode-lflags 99 8b/-> *(esi+0xc) 0/r32/eax # Termios-c_lflag 100 0b/or *Keyboard-type-mode-lflags 0/r32/eax 101 89/<- *(esi+0xc) 0/r32/eax # Termios-c_lflag 102 # ioctl(*Terminal-file-descriptor, TCSETS, terminal-info) 103 89/<- %edx 6/r32/esi 104 b9/copy-to-ecx 0x5402/imm32/TCSETS 105 8b/-> *Terminal-file-descriptor 3/r32/ebx 106 e8/call syscall_ioctl/disp32 107 $enable-keyboard-type-mode:end: 108 # . reclaim locals 109 81 0/subop/add %esp 0x100/imm32 110 # . restore registers 111 5f/pop-to-edi 112 5e/pop-to-esi 113 5b/pop-to-ebx 114 5a/pop-to-edx 115 59/pop-to-ecx 116 58/pop-to-eax 117 # . epilogue 118 89/<- %esp 5/r32/ebp 119 5d/pop-to-ebp 120 c3/return 121 122 # read keys or escapes up to 4 bytes 123 # 124 # fun fact: terminal escapes and graphemes in utf-8 don't conflict! 125 # - in graphemes all but the first/lowest byte will have a 1 in the MSB (be 126 # greater than 0x7f) 127 # - in escapes every byte will have a 0 in the MSB 128 # the two categories overlap only when the first/lowest byte is 0x1b or 'esc' 129 # 130 # Only use this in immediate mode; in type (typewriter) mode 4 bytes may get 131 # parts of multiple keys. 132 read-key-from-real-keyboard: # -> result/eax: grapheme 133 # . prologue 134 55/push-ebp 135 89/<- %ebp 4/r32/esp 136 # . save registers 137 51/push-ecx 138 # var buf/ecx: (stream byte 4) 139 68/push 0/imm32/data 140 68/push 4/imm32/size 141 68/push 0/imm32/read 142 68/push 0/imm32/write 143 89/<- %ecx 4/r32/esp 144 # 145 (read 0 %ecx) # => eax 146 8b/-> *(ecx+0xc) 0/r32/eax 147 $read-key-from-real-keyboard:end: 148 # . reclaim locals 149 81 0/subop/add %esp 0x10/imm32 150 # . restore registers 151 59/pop-to-ecx 152 # . epilogue 153 89/<- %esp 5/r32/ebp 154 5d/pop-to-ebp 155 c3/return 156 157 # use this in type mode 158 read-line-from-real-keyboard: # out: (addr stream byte) 159 # . prologue 160 55/push-ebp 161 89/<- %ebp 4/r32/esp 162 # 163 (read-line-buffered Stdin *(ebp+8)) 164 $read-line-from-real-keyboard:end: 165 # . epilogue 166 89/<- %esp 5/r32/ebp 167 5d/pop-to-ebp 168 c3/return 169 170 == data 171 172 # iflags: octal hex 173 # IGNBRK 0000001 0x0001 174 # BRKINT 0000002 0x0002 175 # IGNPAR 0000004 0x0004 176 # PARMRK 0000010 0x0008 177 # INPCK 0000020 0x0010 178 # ISTRIP 0000040 0x0020 179 # INLCR 0000100 0x0040 180 # IGNCR 0000200 0x0080 181 # ICRNL 0000400 0x0100 182 # IUCLC 0001000 0x0200 183 # IXON 0002000 0x0400 184 # IXANY 0004000 0x0800 185 # IXOFF 0010000 0x1000 186 # IMAXBEL 0020000 0x2000 187 # IUTF8 0040000 0x4000 188 189 # lflags: 190 # ISIG 0000001 0x0001 191 # ICANON 0000002 0x0002 192 # ECHO 0000010 0x0008 193 # ECHOE 0000020 0x0010 194 # ECHOK 0000040 0x0020 195 # ECHONL 0000100 0x0040 196 # NOFLSH 0000200 0x0080 197 # TOSTOP 0000400 0x0100 198 # IEXTEN 0100000 0x8000 199 200 # recipe for raw mode according to the termios.3 manpage on Linux: 201 # termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 202 # termios_p->c_oflag &= ~OPOST; 203 # termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 204 # termios_p->c_cflag &= ~(CSIZE | PARENB); 205 # termios_p->c_cflag |= CS8; 206 207 Keyboard-immediate-mode-iflags: # (addr tcflag_t) 208 #? 0xfffffa14 # ~IGNBRK & ~BRKINT & ~PARMRK & ~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON 209 0xffffffff/imm32 210 211 Keyboard-immediate-mode-lflags: # (addr tcflag_t) 212 #? 0xffff7fb4/imm32 # ~ICANON & ~ISIG & ~IEXTEN & ~ECHO & ~ECHONL 213 0xffffffb5/imm32 # ~ICANON & ~ECHO & ~ECHONL 214 215 Keyboard-type-mode-iflags: # (addr tcflag_t) 216 0x00000000/imm32 # ~Keyboard-immediate-mode-iflags 217 218 Keyboard-type-mode-lflags: # (addr tcflag_t) 219 0x0000004a/imm32 # ~Keyboard-immediate-mode-lflags