.intel_syntax noprefix # syscall numbers .equ SYS_READ, 0 .equ SYS_WRITE, 1 .equ SYS_OPEN, 2 .equ SYS_CLOSE, 3 .equ SYS_POLL, 7 .equ SYS_IOCTL, 16 .equ SYS_EXIT, 60 # ioctl / termios .equ TCGETS, 0x5401 .equ TCSETS, 0x5402 .equ TERMIOS_SIZE, 60 # sizeof(struct termios) on x86_64 # termios c_lflag bits to clear .equ ICANON, 0x2 .equ ECHO, 0x8 .equ ISIG, 0x1 # termios c_iflag bits to clear .equ IGNBRK, 0x1 .equ BRKINT, 0x2 .equ PARMRK, 0x8 .equ ISTRIP, 0x20 .equ INLCR, 0x40 .equ IGNCR, 0x80 .equ ICRNL, 0x100 .equ IXON, 0x400 # termios c_oflag bits to clear .equ OPOST, 0x1 # termios c_cflag .equ CSIZE, 0x30 .equ PARENB, 0x100 .equ CS8, 0x30 .equ CLOCAL, 0x800 .equ CREAD, 0x80 # baud .equ B115200, 0x1002 # open flags .equ O_RDWR, 0x2 .equ O_NOCTTY, 0x100 # poll .equ POLLIN, 0x1 .equ POLLFD_SIZE, 8 # termios field offsets .equ OFLAG_OFF, 4 .equ CFLAG_OFF, 8 .equ LFLAG_OFF, 12 .equ CC_OFF, 17 # c_cc array offset .equ VMIN, 6 # c_cc index .equ VTIME, 5 # c_cc index .equ ISPEED_OFF, 52 .equ OSPEED_OFF, 56 .data devpath: .asciz "/dev/ttyUSB0" banner: .ascii "Holck's minitty - ctrl-d to exit!\r\n" .equ BANNER_LEN, . - banner .bss .align 8 serial_fd: .quad 0 old_termios: .space TERMIOS_SIZE raw_termios: .space TERMIOS_SIZE ser_termios: .space TERMIOS_SIZE pollfds: .space POLLFD_SIZE * 2 # two pollfd structs buf: .space 256 .text .globl _start _start: # Open serial port mov rdi, offset devpath mov rsi, O_RDWR | O_NOCTTY xor rdx, rdx mov rax, SYS_OPEN syscall test rax, rax js exit_fail mov [serial_fd], rax # Get serial termios & configure mov rdi, rax mov rsi, TCGETS lea rdx, [ser_termios] mov rax, SYS_IOCTL syscall # cfmakeraw equivalent for serial lea rbx, [ser_termios] # iflag: clear everything raw clears and dword ptr [rbx], ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON) # oflag: clear OPOST and dword ptr [rbx + OFLAG_OFF], ~OPOST # cflag: CS8, CLOCAL, CREAD, no parity mov eax, [rbx + CFLAG_OFF] and eax, ~(CSIZE | PARENB) or eax, CS8 | CLOCAL | CREAD mov [rbx + CFLAG_OFF], eax # lflag: clear ECHO, ICANON, ISIG and dword ptr [rbx + LFLAG_OFF], ~(ECHO|ICANON|ISIG) # VMIN=0, VTIME=1 mov byte ptr [rbx + CC_OFF + VMIN], 0 mov byte ptr [rbx + CC_OFF + VTIME], 1 # baud 115200 mov dword ptr [rbx + ISPEED_OFF], B115200 mov dword ptr [rbx + OSPEED_OFF], B115200 mov rdi, [serial_fd] mov rsi, TCSETS lea rdx, [ser_termios] mov rax, SYS_IOCTL syscall # Save stdin termios xor rdi, rdi mov rsi, TCGETS lea rdx, [old_termios] mov rax, SYS_IOCTL syscall # Copy old to raw and make raw lea rsi, [old_termios] lea rdi, [raw_termios] mov rcx, TERMIOS_SIZE rep movsb lea rbx, [raw_termios] and dword ptr [rbx], ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON) and dword ptr [rbx + OFLAG_OFF], ~OPOST mov eax, [rbx + CFLAG_OFF] and eax, ~(CSIZE | PARENB) or eax, CS8 mov [rbx + CFLAG_OFF], eax and dword ptr [rbx + LFLAG_OFF], ~(ECHO|ICANON|ISIG) mov byte ptr [rbx + CC_OFF + VMIN], 1 mov byte ptr [rbx + CC_OFF + VTIME], 0 # Set stdin raw xor rdi, rdi mov rsi, TCSETS lea rdx, [raw_termios] mov rax, SYS_IOCTL syscall # Print banner mov rdi, 1 lea rsi, [banner] mov rdx, BANNER_LEN mov rax, SYS_WRITE syscall # Setup pollfds: [0]=stdin, [1]=serial lea rbx, [pollfds] mov dword ptr [rbx], 0 # fd = stdin mov word ptr [rbx + 4], POLLIN # events mov word ptr [rbx + 6], 0 # revents mov eax, [serial_fd] mov dword ptr [rbx + 8], eax # fd = serial mov word ptr [rbx + 12], POLLIN mov word ptr [rbx + 14], 0 poll_loop: lea rdi, [pollfds] mov rsi, 2 mov rdx, -1 # infinite timeout mov rax, SYS_POLL syscall # Check stdin lea rbx, [pollfds] movzx eax, word ptr [rbx + 6] # revents test eax, POLLIN jz check_serial # Read stdin xor rdi, rdi lea rsi, [buf] mov rdx, 256 mov rax, SYS_READ syscall test rax, rax jle done # Scan for Ctrl-D (0x04) mov rcx, rax lea rsi, [buf] scan: cmp byte ptr [rsi], 4 je done inc rsi dec rcx jnz scan # Write to serial mov rdi, [serial_fd] lea rsi, [buf] mov rdx, rax mov rax, SYS_WRITE syscall check_serial: lea rbx, [pollfds] movzx eax, word ptr [rbx + 14] # revents test eax, POLLIN jz poll_loop # Read serial mov rdi, [serial_fd] lea rsi, [buf] mov rdx, 256 mov rax, SYS_READ syscall test rax, rax jle poll_loop # Write to stdout mov rdx, rax mov rdi, 1 lea rsi, [buf] mov rax, SYS_WRITE syscall jmp poll_loop done: # Restore stdin termios xor rdi, rdi mov rsi, TCSETS lea rdx, [old_termios] mov rax, SYS_IOCTL syscall # Close serial mov rdi, [serial_fd] mov rax, SYS_CLOSE syscall xor rdi, rdi mov rax, SYS_EXIT syscall exit_fail: mov rdi, 1 mov rax, SYS_EXIT syscall