cpu 8086 org 0x0000 ; configuration for the uart chip for i/o %define uart 0x3F8 %define irq 4 %define clock 115200 %define baud 9600 data equ (uart + 0) ; data register ier equ (uart + 1) fcr equ (uart + 2) isr equ (uart + 2) lcr equ (uart + 3) mcr equ (uart + 4) lsr equ (uart + 5) ; line status register msr equ (uart + 6) absolute 0xE0 ; data in bios data area pstate resw 1 ; parser resume offset parg resb 1 ; accumulator for numerical CSI arguments section .text db 0x55, 0xAA db 0x00 jmp init %defstr uart_s uart %defstr baud_s baud banner db "nero uartctrl: port ", uart_s, " irq ", irq + 0x30," baud ", baud_s, 0x0A, 0x0D, 0 init push ax push dx push bx push si push ds ; set dlab mov dx, lcr in al, dx or al, 0x80 out dx, al ; set divisor mov cx, (clock / baud) ; low byte mov dx, (uart + 0) mov al, cl out dx, al ; high byte mov dx, (uart + 1) mov al, ch out dx, al ; disable dlab mov dx, lcr in al, dx and al, 0x7F out dx, al ; disable fifo mov dx, fcr mov al, 0 out dx, al ; enable RX interrupt mov dx, ier mov al, 1 out dx, al ; set line modes (8 bits, no parity, 1 stopbit) mov dx, lcr mov al, 3 out dx, al ; set modem control lines (unused) mov dx, mcr mov al, 8 out dx, al ; hook boot interrupt xor ax, ax mov ds, ax mov word [4*0x19], int19 mov word [4*0x19+2], cs ; hook video services ; keep original ptr in intvec 47h for chaining mov ax, int10 mov bx, cs xchg word [4*0x10], ax xchg word [4*0x10+2], bx mov word [4*0x47], ax mov word [4*0x47+2], bx ; hook uart irq mov word [4*(0x08+irq)], intu mov word [4*(0x08+irq)+2], cs ; enable IRQ in PIC mov dx, 0x21 in al, dx and al, ~(1 << irq) out dx, al ; reset parser state mov word [0x400+pstate], parse.start call greet pop ds pop si pop bx pop dx pop ax retf greet mov si, banner xor bx, bx .loop mov al, [cs:si] test al, al jz .exit mov ah, 0x0e int 0x10 inc si jmp .loop .exit ret putc mov ah, al ; check if transmit buffer empty .l01 mov dx, lsr in al, dx test al, 0x20 jz .l01 mov al, ah ; send char to uart mov dx, data out dx, al ret pdx: ; this double-call is essentially a 4 times repeating loop call .l1 .l1: call .l2 .l2: ; set up cl for bit shifts mov cl, 4 ; grab highest nibble from dx mov al, dh ; remove highest nibble from dx shl dx, cl ; shift away second-highest nibble that we accidentally copied shr al, cl ; map 0-9 to ascii codes for '0' to '9' add al, 0x30 ; if result is larger than '9', ... cmp al, 0x3a jl pputc ; ... add 7 so we continue at 'A' add al, 7 pputc mov ah, 0x0e xor bx, bx int 0x10 ret ; system bootstrap int19 sti xor ax, ax int 0x16 mov dx, ax call pdx mov al, 0x0A call pputc mov al, 0x0D call pputc jmp int19 ; video services int10 cmp ah, 0x0e jne .chain push ax push dx call putc pop dx pop ax .chain sub sp, 4 push bp mov bp, sp ; stack: BP IP:CS IP:CS FL push ax push ds xor ax, ax mov ds, ax ; copy offset mov ax, [4*0x47] mov [bp+2], ax ; copy segment mov ax, [4*0x47+2] mov [bp+4], ax pop ds pop ax pop bp retf ; uart controller irq intu push ax push dx ; check if we controller has data ready mov dx, isr in al, dx and al, 0xF cmp al, 4 jne ugetc.ret ; set DS to bios data area push bx push ds mov ax, 0x40 mov ds, ax ; receive data mov dx, data in al, dx jmp near [pstate] ; load parser state ugetc pop word [pstate] ; save parser state pop ds pop bx ; send non-specific EOI signal .ret mov al, 0x20 out 0x20, al pop dx pop ax iret ; parse loop ; this is called from inside the uart interrupt ; only call ugetc when nothing on stack, so no nested funcs parse call ugetc .start cmp al, 0x1B je .esc cmp al, 0x7F je .bs .ascii mov dl, al mov bx, kmap cs xlatb mov dh, al ; insert key code into buffer in bios data area .insert mov bx, [0x1C] ; get next empty slot mov [bx], dx ; store key code add bx, 2 cmp bx, 0x3E ; are we beyond last pos? jc .nowrap mov bx, 0x1E ; first position .nowrap cmp bx, [0x1A] ; next to-read slot je parse mov [0x1C], bx ; commit if not full jmp parse .bs mov dx, 0x0E08 jmp .insert .esc call ugetc cmp al, 0x5B je .csi cmp al, 'O' je .vt_o jmp parse .csi mov byte [parg], 0 ; reset argument .csicon call ugetc cmp al, 0x30 jc parse cmp al, 0x3A jnc .nonum sub al, 0x30 mov dl, al mov al, 0xA mul byte [parg] add al, dl mov [parg], al jmp .csicon .nonum cmp al, '~' jne .letter mov al, [parg] mov bx, fkeysn jmp .lookup .vt_o call ugetc ; VT100 \eO... sequences .letter sub al, 0x40 mov bx, fkeysc .lookup cs xlatb cmp al, 0 je parse mov dh, al mov dl, 0 jmp .insert align 128 ; ascii -> XT scancode map ; this assumes US querty layout kmap db 0x00,0x1E,0x30,0x2E, 0x20,0x12,0x21,0x22, 0x23,0x0F,0x24,0x25, 0x26,0x1C,0x31,0x18 db 0x19,0x10,0x13,0x1F, 0x14,0x16,0x2F,0x11, 0x2D,0x15,0x2C,0x01, 0x2B,0x1B,0x07,0x0C db 0x39,0x02,0x03,0x04, 0x05,0x06,0x08,0x28, 0x0A,0x0B,0x09,0x0D, 0x33,0x0C,0x34,0x35 db 0x0B,0x02,0x03,0x04, 0x05,0x06,0x07,0x08, 0x09,0x0A,0x27,0x27, 0x33,0x0D,0x34,0x35 db 0x03,0x1E,0x30,0x2E, 0x20,0x12,0x21,0x22, 0x23,0x17,0x24,0x25, 0x26,0x32,0x31,0x18 db 0x19,0x10,0x13,0x1F, 0x14,0x16,0x2F,0x11, 0x2D,0x15,0x2C,0x1A, 0x2B,0x1B,0x07,0x0C db 0x29,0x1E,0x30,0x2E, 0x20,0x12,0x21,0x22, 0x23,0x17,0x24,0x25, 0x26,0x32,0x31,0x18 db 0x19,0x10,0x13,0x1F, 0x14,0x16,0x2F,0x11, 0x2D,0x15,0x2C,0x1A, 0x2B,0x1B,0x29,0x70 ; function keys by character ; \eO$c, looks up $c to XT scancode ; table is like ascii, except it starts at 0x40 '@' fkeysc db 0,0x48,0x50,0x4D, 0x4B,0,0x4F,0, 0x47,0,0,0, 0,0,0,0 db 0x3B,0x3C,0x3D,0x3E, 0,0,0,0, 0,0,0,0, 0,0,0,0 db 0,0,0,0, 0,0,0,0, 0,0,0,0, 0x42,0,0,0 db 0,0,0,0, 0x3F,0x40,0x41,0x43, 0x44,0,0,0, 0,0,0,0 ; function keys by numbers ; 10 entries per line ; \e[$n~, looks up $n to XT scancode fkeysn db 0,0x47,0x52,0x53,0, 0x49,0x51,0x47,0x4F,0 ; 0-9 db 0,0x3B,0x3C,0x3D,0x3E, 0x3F,0,0x40,0x41,0x42 ; 10-19 db 0x43,0x44,0,0x85,0x86, 0,0,0,0,0 ; 20-29 db 0,0,0,0,0, 0,0,0,0,0 ; 30-39 align 512