cpu 8086 org 0x0000 ; configuration for the uart chip for i/o %define uart 0x3F8 %define irq 4 %define clock 115200 %define baud 9600 %ifndef CODEPAGE %define CODEPAGE 437 %endif 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 curcs resb 1 ; current charset we put remote term in 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 ; DS = interrupt table xor ax, ax mov ds, ax ; 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 ; reset parser state mov word [0x400+pstate], parse.start ; 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 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 ; set charset ; IN AL '0', 'A' or 'B' setcs test al, al jz .fret push ds ; set DS mov dx, 0x40 mov ds, dx cmp al, [curcs] je .ret mov [curcs], al mov al, 0x1B call putc mov al, '(' call putc mov al, [curcs] call putc .ret pop ds .fret 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 ; video services int10 cmp ah, 0x0e jne .chain push bx push dx push ax ; activate character set for current char mov bx, csmap cs xlatb call setcs pop ax push ax ; look up what we need to write out mov bx, ecmap cs xlatb call putc pop ax pop dx pop bx .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 ; number parsing 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, '~' ; \e[...$c handling jne .letter mov al, [parg] ; \e[$n~ lookup $n mov bx, fkeysn jmp .lookup .vt_o call ugetc ; VT100 \eO... sequences .letter sub al, 0x40 ; look up special key by letter mov bx, fkeysc .lookup cmp al, 0x40 ; special key lookup, bx assumed jnc parse cs xlatb cmp al, 0 je parse mov dh, al mov dl, 0 jmp .insert align 128 ; ascii -> XT scancode map %if CODEPAGE = 437 ; US Keyboard 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 ; character map ; resolves codepage character to character in charset (see next table) ecmap db 0x00,0x01,0x02,0x03, 0x60,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B, 0x0C,0x0D,0x0E,0x0F db 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x1C,0x1D,0x1E,0x1F db 0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B, 0x2C,0x2D,0x2E,0x2F db 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B, 0x3C,0x3D,0x3E,0x3F db 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B, 0x4C,0x4D,0x4E,0x4F db 0x50,0x51,0x52,0x53, 0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B, 0x5C,0x5D,0x5E,0x5F db 0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B, 0x6C,0x6D,0x6E,0x6F db 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B, 0x7C,0x7D,0x7E,0x7F db 0x47,0x7c,0x69,0x62, 0x64,0x60,0x65,0x67, 0x6A,0x6B,0x68,0x6f, 0x6e,0x6c,0x44,0x45 db 0x49,0x66,0x46,0x74, 0x76,0x72,0x7b,0x79, 0x7d,0x56,0x5C,0x22, 0x23,0x25,'P' ,'f' db 0x61,0x6D,0x73,0x7a, 0x71,0x51,0x2A,0x3A, 0x3F,0x2C,0x2C,0x3d, 0x3c,0x21,0x2b,0x3b db 0x61,0x61,0x61,0x78, 0x75,0x75,0x75,0x6B, 0x6B,0x75,0x78,0x6B, 0x6a,0x6a,0x6a,0x6b db 0x6D,0x76,0x77,0x74, 0x71,0x6E,0x74,0x74, 0x6D,0x6C,0x76,0x77, 0x74,0x71,0x6E,0x76 db 0x76,0x77,0x77,0x6D, 0x6D,0x6C,0x6C,0x6E, 0x6E,0x6A,0x6C,0x61, 0x61,0x61,0x61,0x61 db 0x61,0x62,0x47,0x70, 0x53,0x73,0x35,0x74, 0x46,0x6A,0x57,0x64, 0x42,0x66,0x65,0x5C db 0x4F,0x67,0x7A,0x79, 0x24,0x25,0x43,0x49, 0x66,0x7E,0x7E,0x21, 0x32,0x32,0x61,0x5F ; character set map ; resolves codepage character to DEC charset (A,B,0,1,<) csmap db 0,0,0,0, '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, 0,0,0,0 db 0,'B','B','B', 'B','B','B','B', 'B','B','B','B', 'B','B','B','B' db 'B','B','B','B', 'B','B','B','B', 'B','B','B','B', 'B','B','B','B' db 'B','B','B','B', 'B','B','B','B', 'B','B','B','B', 'B','B','B','B' db 'B','B','B','B', 'B','B','B','B', 'B','B','B','B', 'B','B','B','B' db 'B','B','B','B', 'B','B','B','B', 'B','B','B','B', 'B','B','B','B' db 'B','B','B','B', 'B','B','B','B', 'B','B','B','B', 'B','B','B',0 db 'A','A','A','A', 'A','A','A','A', 'A','A','A','A', 'A','A','A','A' db 'A','A','A','A', 'A','A','A','A', 'A','A','A','A', 'A','A','B','B' db 'A','A','A','A', 'A','A','A','A', 'A','A','A','A', 'A','A','A','A' db '0','0','0','0', '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', '0','0','0','0' db '0','0','0','0', '0','0','0','0', '0','0','0','0', '0','0','0','0' db '>','>','>','>', '>','>','A','>', '>','>','>','>', '>','>','>','>' db '>','0','0','0', '>','>','>','>', '0','0','0','>', 'A','A','0','0' %endif ; 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