diff --git a/Makefile b/Makefile index 00bdacf..eab91cb 100644 --- a/Makefile +++ b/Makefile @@ -82,3 +82,6 @@ qemu-floppy5: fd360.img $(ROMS) qemu-hdd: hdd.img $(ROMS) $(QEMU) $(QEMU_ARGS) -boot c -hda hdd.img + +qemu-uartctrl: uartctrl.rom + $(QEMU) $(QEMU_ARGS) -option-rom uartctrl.rom -serial stdio diff --git a/rom/uartctrl.asm b/rom/uartctrl.asm new file mode 100644 index 0000000..1a8f70d --- /dev/null +++ b/rom/uartctrl.asm @@ -0,0 +1,325 @@ + 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