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 # . save registers 163 50/push-eax 164 # 165 (read 0 *(ebp+8)) # => eax 166 $read-line-from-real-keyboard:end: 167 # . restore registers 168 58/pop-to-eax 169 # . epilogue 170 89/<- %esp 5/r32/ebp 171 5d/pop-to-ebp 172 c3/return 173 174 == data 175 176 # iflags: octal hex 177 # IGNBRK 0000001 0x0001 178 # BRKINT 0000002 0x0002 179 # IGNPAR 0000004 0x0004 180 # PARMRK 0000010 0x0008 181 # INPCK 0000020 0x0010 182 # ISTRIP 0000040 0x0020 183 # INLCR 0000100 0x0040 184 # IGNCR 0000200 0x0080 185 # ICRNL 0000400 0x0100 186 # IUCLC 0001000 0x0200 187 # IXON 0002000 0x0400 188 # IXANY 0004000 0x0800 189 # IXOFF 0010000 0x1000 190 # IMAXBEL 0020000 0x2000 191 # IUTF8 0040000 0x4000 192 193 # lflags: 194 # ISIG 0000001 0x0001 195 # ICANON 0000002 0x0002 196 # ECHO 0000010 0x0008 197 # ECHOE 0000020 0x0010 198 # ECHOK 0000040 0x0020 199 # ECHONL 0000100 0x0040 200 # NOFLSH 0000200 0x0080 201 # TOSTOP 0000400 0x0100 202 # IEXTEN 0100000 0x8000 203 204 # recipe for raw mode according to the termios.3 manpage on Linux: 205 # termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 206 # termios_p->c_oflag &= ~OPOST; 207 # termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 208 # termios_p->c_cflag &= ~(CSIZE | PARENB); 209 # termios_p->c_cflag |= CS8; 210 211 Keyboard-immediate-mode-iflags: # (addr tcflag_t) 212 #? 0xfffffa14 # ~IGNBRK & ~BRKINT & ~PARMRK & ~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON 213 0xffffffff/imm32 214 215 Keyboard-immediate-mode-lflags: # (addr tcflag_t) 216 #? 0xffff7fb4/imm32 # ~ICANON & ~ISIG & ~IEXTEN & ~ECHO & ~ECHONL 217 0xffffffb5/imm32 # ~ICANON & ~ECHO & ~ECHONL 218 219 Keyboard-type-mode-iflags: # (addr tcflag_t) 220 0x00000000/imm32 # ~Keyboard-immediate-mode-iflags 221 222 Keyboard-type-mode-lflags: # (addr tcflag_t) 223 0x0000004a/imm32 # ~Keyboard-immediate-mode-lflags