/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "sgabios.h" #define BUILD_CL "$Id$" .code16 .text .section ".init","ax" .globl _start .type _start,@object _start: /* option rom header */ .byte 0x55 .byte 0xaa .byte _rom_size_byte .size _start, .-_start .globl legacy_entry .type legacy_entry,@function legacy_entry: jmp sga_init /* pnp entry here to avoid changing PnP table as code moves */ pnp_init: jmp pnp_sga_init /* * do_old_int10h * * Patched at option rom init to be a far jump to old int 10h isr * */ do_old_int10h: .byte 0xea /* jmp absolute segment:offset */ old_int10h: /* store what was at offset 0x40 */ .word 0xf065 /* placeholder for chained ISR offset */ /* if the chained segment is detected as 0xc000, use 80 cols only */ /* since it's assumed that a vga card is attached and 80 cols max */ old_int10h_seg: .word 0xf000 /* placeholder for chained ISR segment */ /* * do_old_int16h * * Patched at option rom init to be a far jump to old int 16h isr * */ do_old_int16h: .byte 0xea /* jmp absolute segment:offset */ old_int16h: /* store what was at offset 0x58 */ .word 0xe82e /* placeholder for chained ISR offset */ .word 0xf000 /* placeholder for chained ISR segment */ .org 0x18 .word 0 /* offset to PCI data, 0 = none */ .word pnp_table /* offset to PnP expansion header */ .org 0x20 pnp_table: /* FIXME: **** PnP header currently disabled by PoO **** */ /* legacy entry only called once, PnP entry called multiple times */ /* The code isn't yet written to deal with multiple inits properly */ .ascii "$PoO" /* PnP expansion header signature */ .byte 1 /* structure revision */ .byte 2 /* length in 16-byte increments */ .word 0 /* offset of next header, 0 if none */ .byte 0 /* reserved */ .byte 0x52 /* checksum - update manually! FIXME */ .long 0 /* device identifier */ .word mfg_string /* pointer to manufacturer string */ .word prod_string /* pointer to product name string */ .byte 3, 0x80, 0x80 /* device type code = other display */ .byte 0xe3 /* device indicators, kbd/display dev */ .word 0 /* boot connection vector, 0 if none */ .word 0 /* disconnect vector, 0 if none */ .word pnp_init /* bootstrap entry vector */ .word 0 /* reserved */ .word 0 /* static resource information vector */ /* WARNING: changing mfg_string / prod_string locations will */ /* affect pnp table above -- recalculate checksum manually! */ mfg_string: .asciz "Google, Inc." prod_string: .ascii "Serial Graphics Adapter " build_date: .asciz BUILD_SHORT_DATE long_version: .ascii "SGABIOS " .ascii BUILD_CL .ascii " (" .ascii BUILD_USER .ascii "@" .ascii BUILD_HOST .ascii ") " .asciz BUILD_DATE term_cols: .byte 80 /* overwritten at rom init with detected value */ term_rows: .byte 24 /* overwritten at rom init with detected value */ term_init_string: /* terminal reply: \033[n;mR n=row, m=col */ .asciz "\033[1;256r\033[256;256H\033[6n" /* reset the scroll, move to col 256, row 256, ask current position */ /* bios cursor positions >255 rows or cols can't be used anyway */ term_info: .asciz "Term: " ebda_info: .asciz "EBDA: " /* * do_old_irq3 - exception 0x0b, int 0x0a * * Patched at option rom init to be a far jump to old irq 3 isr * */ do_old_irq3: .byte 0xea /* jmp absolute segment:offset */ old_irq3: /* store what was at offset 0x28 */ .word 0xeef3 /* placeholder for chained ISR offset */ .word 0xf000 /* placeholder for chained ISR segment */ /* * do_old_irq4 - exception 0x0c, int 0x0b * * Patched at option rom init to be a far jump to old irq 4 isr * */ do_old_irq4: .byte 0xea /* jmp absolute segment:offset */ old_irq4: /* store what was at offset 0x2c */ .word 0xeef3 /* placeholder for chained ISR offset */ .word 0xf000 /* placeholder for chained ISR segment */ /* * do_old_int14h * * Patched at option rom init to be a far jump to old int 14h isr * */ do_old_int14h: .byte 0xea /* jmp absolute segment:offset */ old_int14h: /* store what was at offset 0x50 */ .word 0xe739 /* placeholder for chained ISR offset */ .word 0xf000 /* placeholder for chained ISR segment */ .align 16, 0xff /* aligning this table only makes hexdump prettier */ /* ascii -> scancode, bit 7=shifted, char < 32 = +ctrl */ /* except chars 8, 9, 13, 27 (bs, tab, enter, esc) */ /* most int16h consumers will probably never use */ ascii2scan: /*00*/ .byte 0x00, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22 /*08*/ .byte 0x0e, 0x17, 0x24, 0x25, 0x26, 0x1c, 0x31, 0x18 /*10*/ .byte 0x19, 0x0f, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11 /*18*/ .byte 0x2d, 0x15, 0x2c, 0x01, 0x2b, 0x1b, 0x87, 0x8c /*20*/ .byte 0x39, 0x82, 0xa8, 0x84, 0x85, 0x86, 0x88, 0x28 /*28*/ .byte 0x8a, 0x8b, 0x89, 0x8d, 0x33, 0x0c, 0x34, 0x35 /*30*/ .byte 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 /*38*/ .byte 0x09, 0x0a, 0xa7, 0x27, 0xb3, 0x0d, 0x34, 0xb5 /*40*/ .byte 0x83, 0x9e, 0xb0, 0xae, 0xa0, 0x92, 0xa1, 0xa2 /*48*/ .byte 0xa3, 0x97, 0xa4, 0xa5, 0xa6, 0xb2, 0xb1, 0x98 /*50*/ .byte 0x99, 0x90, 0x93, 0x9f, 0x94, 0x96, 0xaf, 0x91 /*58*/ .byte 0xad, 0x95, 0xac, 0x1a, 0x2b, 0x1b, 0x87, 0x8c /*60*/ .byte 0x29, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22 /*68*/ .byte 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18 /*70*/ .byte 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11 /*78*/ .byte 0x2d, 0x15, 0x2c, 0x9a, 0xab, 0x9b, 0xa9, 0x0e /* TABLES FOR NON-ASCII VGA CHARACTERS (CP437) TO ASCII */ /* Unicode at: http://en.wikipedia.org/wiki/Code_page_437 */ ctrl2ascii: /* translate vga (CP437) first 32 characters to ascii */ /* for char 0, update the cursor position, but output nothing */ /* lilo uses this "trick" for a background attribute update */ .ascii "\0@@v***........*><|!PS-|^v>###||||++||+++++" /*c0*/ .ascii "+--|-+||++--|-+----++++++++#-||-" /*e0*/ .ascii "abgpesut00osiye^=+><||-=...vn2* " colortable: /* vga text color is IRGB, ansi color is BGR */ /* this table is effectively a nibble bit-reverse */ .byte 0, 4, 2, 6, 1, 5, 3, 7 serial_port_base_address: .word COM_BASE_ADDR /* in-memory console log * * It's expected that the EBDA contains a magic signature * like 0xdeadbabe, followed by a byte of flags, followed * by a 32-bit buffer pointer, followed by a 16-bit start * index, followed by a 16-bit end index, followed by 16- * bit logged character count, followed by an 8-bit flag. */ #define MEMCONSOLE_BUFFER_SIZE 32768 #define MEMCONSOLE_SIGNATURE 0xdeadbabe #define MEMCONSOLE_ENDINDEX_OFF 0x0b #define SGABIOS_EBDA_SIGNATURE 0x00414753 memconsole_buffer_start: /* pulled from ebda struct */ .long 0x00000000 /* 0 = not found/no logging */ memconsole_ebda_deadbabe_offset: /* bytes from start of ebda */ .word 0x0000 /* 40:0e contains ebda seg */ sgabios_ebda_logbuf_offset: /* bytes from start of ebda */ .word 0x0000 /* 40:0e contains ebda seg */ /* * setup_memconsole * * Initialize the option rom variables associated with logging * of the legacy console output * * If these variables are left at zero, no logging will occur * * There are no parameters * All registers except flags should be preserved */ setup_memconsole: pushaw pushw %ds pushw %es pushw $BDA_SEG popw %ds /* ds = 0x40 */ pushw BDA_EBDA /* push word at 0x0e */ popw %es /* es = EBDA_SEG */ /* search for memconsole signature in ebda */ movl $MEMCONSOLE_SIGNATURE, %eax xorw %di, %di /* start at zero */ movzbw %es:(%di), %cx /* cx = size of EBDA in KB */ shlw $8, %cx /* cx = (cx * 1024) / 4 */ cld repnz scasl /* search until sig found */ subw $4, %di /* scasl always increments di, undo */ cmpl %eax, %es:(%di) /* is signature here? */ jnz setup_memconsole_end /* bail if so */ movw %di, %cs:memconsole_ebda_deadbabe_offset /* save offset */ movl %es:5(%di), %eax /* get 32-bit buffer base address */ movl %eax, %cs:memconsole_buffer_start setup_memconsole_end: popw %es popw %ds popaw ret /* * memconsole_log_char * * Log the character passed in %al to the next available memory * console log position, if any. * * If memconsole_buffer_start is zero, no logging will occur * * %al = character to be logged * All registers except flags should be preserved */ memconsole_log_char: pushaw pushw %ds pushw %es pushw %fs pushw $BDA_SEG popw %ds /* ds = 0x40 */ pushw BDA_EBDA /* push word at 0x0e */ popw %es /* es = EBDA_SEG */ movw %ax, %si /* %si = %al = byte to write */ movl %cs:memconsole_buffer_start, %ebp movw %cs:memconsole_ebda_deadbabe_offset, %di addw $MEMCONSOLE_ENDINDEX_OFF, %di /* %di points to char pos */ orl %ebp, %ebp jz memconsole_log_tail /* bufptr==0, no logging */ movw %es:(%di), %bx /* bx = current position in buffer */ cmpw $MEMCONSOLE_BUFFER_SIZE, %bx /* at end of buffer? */ jnc memconsole_log_tail /* don't log any more if so */ cmpb $0xd, %al /* is the char CR? */ jz memconsole_log_tail /* if so, ignore it */ cmpb $0x8, %al /* is the char backspace? */ jnz memconsole_update_fsbase /* if not, log char as usual... */ orw %bx, %bx /* make sure ptr isn't already zero */ jz memconsole_log_tail /* if so, bail */ decw %bx /* else point to previous character */ jmp memconsole_update_end_ptr /* and go directly to save it */ memconsole_update_fsbase: movl $0xc0000100, %ecx /* ecx = IA32_FS_BASE (AMD64+) */ rdmsr /* read what was there before */ pushl %eax /* save away previous FS_BASE eax */ pushl %edx /* save away previous FS_BASE edx */ xorl %edx, %edx /* clear high 32 bits */ movl %ebp, %eax /* eax = memconsole buffer start */ wrmsr /* fs_base = memconsole buffer start */ movw %si, %ax /* %ax = saved value on entry */ movb %al, %fs:(%bx) /* log character */ popl %edx /* restore previous FS_BASE edx */ popl %eax /* restore previous FS_BASE eax */ wrmsr /* write what was there before */ incw %bx /* update character count */ memconsole_update_end_ptr: movw %bx, %es:(%di) /* save new end pointer */ addw $2, %di /* numchars stored at next word */ movw %bx, %es:(%di) /* save new numchar value */ memconsole_log_tail: popw %fs popw %es popw %ds popaw ret /* sgabioslog_setup_ebda * * SGABIOS makes its own 1KB EBDA allocation to save non- * translated characters with associated cursor positions * for the last 256 characters output. This is organized * with 256 bytes reserved for houskeeping, 256 bytes for * the raw character codes, and 512 bytes of 16bit cursor * positions to record the associated position for each. * * The first 4 bytes contain "SGA\0" followed by a 16-bit * size of the allocation in bytes, followed by a 16-bit * index indicating the next spot to be overwritten. * * There are no parameters * All registers should be preserved */ sgabioslog_setup_ebda: pushf pushaw pushw %ds pushw %es pushw $BDA_SEG popw %ds /* ds = 0x40 */ movw BDA_EBDA, %ax /* ax = old ebda segment from 0x0e */ subw $SGABIOS_EBDA_DELTA, %ax movw %ax, %es /* es = new EBDA segment start */ cmpw $EBDA_MIN_SEG, %ax /* is there room for the allocation? */ jc sgabioslog_setup_ebda_tail /* if not, don't change anything */ cli /* paranoid in case irq uses EBDA */ movw %ax, BDA_EBDA /* save new EBDA segment start */ subw $SGABIOS_EBDA_KB, BDA_MEM_SIZE /* subtract extra allocation */ movw %ax, %ds /* ds = new EBDA segment start */ movw $SGABIOS_EBDA_BYTES, %si /* si = offset of first byte to move */ movzbw (%si), %cx /* cx = number of KB in EBDA */ addb $SGABIOS_EBDA_KB, (%si) /* update EBDA size in kb */ shlw $10, %cx /* cx = KB * 1024 = bytes in EBDA */ movw %cx, %cs:sgabios_ebda_logbuf_offset /* new ebda space */ xorw %di, %di /* di = new EBDA start */ cld rep movsb /* move ebda by SGABIOS_EBDA_BYTES */ movw %cs:sgabios_ebda_logbuf_offset, %bx /* bx = new buffer */ movl $SGABIOS_EBDA_SIGNATURE, (%bx) /* setup signature */ movw $SGABIOS_EBDA_BYTES, 4(%bx) /* bytes in new ebda buffer */ movw $0, 6(%bx) /* next log index, new ebda buffer */ sgabioslog_setup_ebda_tail: popw %es popw %ds popaw popf ret /* * sgabioslog_save_char * * Like memconsole_log_char, except the original, untranslated * character is expected to be given in the %al register. * * The original character and its corresponding cursor position * are logged to the sgabios ebda memory allocation. * * %al = character to be logged * All registers except flags should be preserved */ sgabioslog_save_char: pushaw pushw %ds pushw %es pushw $BDA_SEG popw %ds /* ds = 0x40 */ pushw BDA_EBDA /* push word at 0x0e */ popw %es /* es = EBDA_SEG */ movw %cs:sgabios_ebda_logbuf_offset, %di orw %di, %di /* is offset zero? */ jz sgabioslog_save_tail /* if so, bail */ cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di) jnz sgabioslog_save_tail /* bail if magic not found */ movw %es:6(%di), %bx /* bx = index of next char output */ movb %al, %es:SGABIOS_EBDA_LOG_START(%bx,%di) /* store character */ movzbw %bl, %ax /* %ax = next cursor buffer index */ shlw $1, %ax /* %ax = offset to cursor storage */ call get_current_cursor /* %dh = row, %dl = column */ addw $SGABIOS_EBDA_POS_START, %di /* cursor storage */ addw %ax, %di /* %di = next cursor storage offset */ movw %dx, %es:(%di) /* save position for logged char */ incw %bx /* point to next char to log */ cmpw $SGABIOS_EBDA_LOG_SIZE, %bx jnz sgabioslog_save_index xorw %bx, %bx /* wrap around to start */ sgabioslog_save_index: movw %cs:sgabios_ebda_logbuf_offset, %di movw %bx, %es:6(%di) /* save new index */ sgabioslog_save_tail: popw %es popw %ds popaw ret /* * sgabioslog_get_char * * Return the character at current cursor position, last recorded * to sgabios ebda allocation, if available. * * If the current cursor postition contains one of the last 256 characters * written to the ebda buffer, return that character, else return 0. * * If sgabios_ebdda_logbuf_offset is zero, %al will be 0 and zf set * * All registers except flags and %al should be preserved */ sgabioslog_get_char: pushaw movw %sp, %bp movb $0, 14(%bp) /* %al on stack = 0 */ pushw %ds pushw %es pushw $BDA_SEG popw %ds /* ds = 0x40 */ pushw BDA_EBDA /* push word at 0x0e */ popw %es /* es = EBDA_SEG */ movw %cs:sgabios_ebda_logbuf_offset, %di orw %di, %di jz sgabioslog_get_tail /* offset==0, no logging */ cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di) jnz sgabioslog_get_tail /* bail if magic not found */ call get_current_cursor /* dh = row, dl = col */ std /* scan backwards in mem */ movw %es:6(%di), %bx /* bx = index of next char output */ decw %bx /* %bx = offset of last char in buf */ jnc sgabioslog_got_pos addw $SGABIOS_EBDA_LOG_SIZE, %bx /* bx position wrap around */ sgabioslog_got_pos: movw %bx, %ax /* %ax = last cursor pos written */ shlw $1, %ax /* %ax = offset of last cursor pos */ addw $SGABIOS_EBDA_POS_START, %di /* %di = first cursor position */ addw %ax, %di /* %di = offset in ebda */ movw %dx, %ax /* %ax = cursor pos to compare */ movw %bx, %cx /* %cx = positions before wrap */ jcxz sgabioslog_cmp_wrap /* if zero, try from end next */ repnz scasw /* search until position match */ addw $2, %di /* scasd always decrements di, undo */ cmpw %ax, %es:(%di) /* did it really match? */ jz sgabioslog_cursor_match /* if so, do something */ sgabioslog_cmp_wrap: movw %cs:sgabios_ebda_logbuf_offset, %di addw $SGABIOS_EBDA_POS_LAST, %di /* %di = last cursor storage */ movw $SGABIOS_EBDA_LOG_SIZE, %cx /* %cx = compare all positions */ repnz scasw /* search until position match */ addw $2, %di /* scasd always decrements di, undo */ cmpw %ax, %es:(%di) /* did it really match? */ jnz sgabioslog_get_tail /* if not, bail */ sgabioslog_cursor_match: /* %di contains the EBDA offset of the matching position */ /* convert this into a memconsole offset */ subw $512, %di /* take off the storage offset */ subw %cs:sgabios_ebda_logbuf_offset, %di /* and ebda offset */ shrw $1, %di /* %di = char position index */ addw %cs:sgabios_ebda_logbuf_offset, %di /* add back ebda offset */ addw $SGABIOS_EBDA_LOG_START, %di /* and add back log offset */ movb %es:(%di), %al /* get related saved character */ movb %al, 14(%bp) /* %al on stack = logged char */ sgabioslog_get_tail: popw %es popw %ds popaw ret /* * multibyteinput * * When an escape key is detected, the input routines will attempt to * capture as many characters as arrive up until a timeout, or six, * whichever is less. * * This table is intended to decide what the characters after the * initial escape key translate to in terms of high and low bytes * that go into the keyboard buffer the high byte is the scancode, * the low byte is ascii, but for special keys this is usually 0xe0 * or 0x00. * * This table is formatted so that the first word is a scancode + * ascii pair (as returned by int 16h, ah = 10h or 11h). Immediately * following is a nul-terminated ascii string to match in order to * use the corresponding scancode+ascii word. * * The search through this table is terminated by a match or finding * a 0 scancode+ascii word. * * FIXME: all the low bytes are now zero, get rid of them? */ multibyteinput: .byte 0x3b /* F1 */ .asciz "[[A" /* F1/screen */ .byte 0x3b /* F1 */ .asciz "OP" /* F1/xterm/ansi */ .byte 0x3b /* F1 */ .asciz "[11~" /* F1/vt400 */ .byte 0x3c /* F2 */ .asciz "[[B" /* F2/screen */ .byte 0x3c /* F2 */ .asciz "OQ" /* F2/xterm/ansi */ .byte 0x3c /* F2 */ .asciz "[12~" /* F2/vt400 */ .byte 0x3d /* F3 */ .asciz "[[C" /* F3/screen */ .byte 0x3d /* F3 */ .asciz "OR" /* F3/xterm/ansi */ .byte 0x3d /* F3 */ .asciz "[13~" /* F3/vt400 */ .byte 0x3e /* F4 */ .asciz "[[D" /* F4/screen */ .byte 0x3e /* F4 */ .asciz "OS" /* F4/xterm/ansi */ .byte 0x3e /* F4 */ .asciz "[14~" /* F4/vt400 */ .byte 0x3f /* F5 */ .asciz "[[E" /* F5/screen */ .byte 0x3f /* F5 */ .asciz "[15~" /* F5/xterm */ .byte 0x3f /* F5 */ .asciz "OT" /* F5/ansi */ .byte 0x40 /* F6 */ .asciz "[17~" /* F6/screen/vt220/xterm/vt400 */ .byte 0x40 /* F6 */ .asciz "OU" /* F6/ansi */ .byte 0x41 /* F7 */ .asciz "[18~" /* F7/screen/vt220/xterm/vt400 */ .byte 0x41 /* F7 */ .asciz "OV" /* F7/ansi */ .byte 0x42 /* F8 */ .asciz "[19~" /* F8/screen/vt220/xterm/vt400 */ .byte 0x42 /* F8 */ .asciz "OW" /* F8/ansi */ .byte 0x43 /* F9 */ .asciz "[20~" /* F9/screen/vt220/xterm/vt400 */ .byte 0x43 /* F9 */ .asciz "OX" /* F9/ansi */ .byte 0x44 /* F10 */ .asciz "[21~" /* F10/screen/vt220/xterm/vt400 */ .byte 0x44 /* F10 */ .asciz "OY" /* F10/ansi */ .byte 0x85 /* F11 */ .asciz "[23~" /* F11/screen/xterm/vt400 */ .byte 0x85 /* F11 */ .asciz "OZ" /* F11/ansi */ .byte 0x86 /* F12 */ .asciz "[24~" /* F12/screen/xterm/vt400 */ .byte 0x52 /* Insert */ .asciz "[2~" /* Insert/screen/vt102/xterm */ .byte 0x53 /* Delete */ .asciz "[3~" /* Delete/screen/vt102/xterm */ .byte 0x4b /* Left */ .asciz "OD" /* Left/screen/vt102 */ .byte 0x4b /* Left */ .asciz "[D" /* Left/xterm */ .byte 0x47 /* Home */ .asciz "[1~" /* Home/screen/vt102 */ .byte 0x47 /* Home */ .asciz "[H" /* Home/xterm */ .byte 0x4f /* End */ .asciz "[4~" /* End/screen/vt102 */ .byte 0x4f /* End */ .asciz "[F" /* End/xterm */ .byte 0x48 /* Up */ .asciz "OA" /* Up/screen/vt102 app */ .byte 0x48 /* Up */ .asciz "[A" /* Up/xterm/vt102 ansi */ .byte 0x50 /* Down */ .asciz "OB" /* Down/screen/vt102 app */ .byte 0x50 /* Down */ .asciz "[B" /* Down/xterm/vt102 ansi */ .byte 0x49 /* PageUp */ .asciz "[5~" /* PageUp/screen/vt102/xterm */ .byte 0x51 /* PageDown */ .asciz "[6~" /* PageDown/screen/vt102/xterm */ .byte 0x4d /* Right */ .asciz "OC" /* Right/screen/vt102 app */ .byte 0x4d /* Right */ .asciz "[C" /* Right/xterm/vt102 ansi */ .byte 0 /* end of table marker */ /* init_serial_port * * Initialize serial port to 115200,8n1 * Serial interrupts disabled * * All registers except flags preserved */ init_serial_port: pushw %ax pushw %dx pushw %bx movw %cs:serial_port_base_address, %dx addw $IER_OFFSET, %dx xorb %al, %al outb %al, %dx /* disable all serial interrupts */ addw $(LCR_OFFSET - IER_OFFSET), %dx /* LCR */ movb $(LCR_VALUE|LCR_DLAB), %al outb %al, %dx /* enable divisor access */ movw %cs:serial_port_base_address, %dx movw $(PORT_DIVISOR/PORT_SPEED), %bx movb %bl, %al /* al = lsb of divisor */ outb %al, %dx /* set divisor latch lsb */ movb %bh, %al /* al = msb of divisor */ incw %dx outb %al, %dx /* set divisor latch msb */ movw %cs:serial_port_base_address, %dx addw $LCR_OFFSET, %dx movb $LCR_VALUE, %al outb %al, %dx /* disable divisor access */ addw $(MCR_OFFSET - LCR_OFFSET), %dx /* MCR */ movb $MCR_DTRRTS, %al outb %al, %dx /* enable DTR + RTS */ movw %cs:serial_port_base_address, %dx addw $FCR_OFFSET, %dx movb $FCR_FIFO_ENABLE, %al outb %al, %dx /* enable FIFOs */ popw %bx popw %dx popw %ax ret /* get_serial_lsr * * return serial line status register in %al * return offset to serial port line status register io port in %dx * all other registers except flags unchanged * * if status == 0xff return ZF=1, else return ZF=0 */ get_serial_lsr: movw %cs:serial_port_base_address, %dx addw $LSR_OFFSET, %dx inb %dx, %al cmpb $0xff, %al ret /* * get_byte * * get serial byte in %al, scancode in %ah [FIXME: EFI console input] * * all registers except %ax preserved * */ get_byte: pushw %dx pushw %bx next_serial_char: call get_serial_lsr /* get serial lsr in %al */ jz get_byte_tail /* no port present... */ testb $1, %al /* bit 0 of LSR = 1 = data available */ jz get_byte_tail /* no input waiting */ /* new character found on serial port */ /* convert it to a scancode */ movw %cs:serial_port_base_address, %dx inb %dx, %al /* al = serial input char */ testb $0x80, %al /* non-ascii char received? */ jnz next_serial_char /* throw char away */ movb %al, %dl /* dl = character read */ pushw %ds pushw %cs popw %ds /* ds = cs */ movw $ascii2scan, %bx /* table to translate ascii->scan */ xlatb /* translate char to scancode */ popw %ds /* shift status is ignored at this point, may be used later */ andb $0x7f, %al /* strip shift status from table */ movb %al, %ah /* scancode goes in high byte */ movb %dl, %al /* "translated" ascii in lower byte */ cmpb $0x7f, %al /* Did the user transmit ascii DEL? */ jnz get_byte_not_del /* if not, don't do anything to al */ movb $0x08, %al /* else delete becomes backspace */ get_byte_not_del: testw %ax, %ax /* clear zero flag */ get_byte_tail: popw %bx popw %dx ret /* * poll_byte * * get serial byte in %al, scancode in %ah [FIXME: EFI console input] * retry up to 65536 times for an expected input byte * * all registers except %ax preserved * */ poll_byte: pushw %cx xorw %cx, %cx poll_byte_retry: inb $0xed, %al call get_byte loopz poll_byte_retry /* repeat while zf set or cx != 0 */ popw %cx ret /* * get_multibyte * * after an escape character, poll for terminal keys that generate * an escape code plus multiple bytes (up to four). * * if no byte is waiting, all registers preserved except flags * if more bytes are waiting, all registers preserved except %ax and flags * */ get_multibyte: pushw %bp /* bp points to temp buffer on stack */ pushw %bx /* bx points to multibyteinput table */ pushw %cx /* cx will count chars */ pushw %ax /* ax will receive chars */ pushl $0 /* make space on stack for 4 chars */ xorw %cx, %cx /* cx = 0 */ movw %sp, %bp /* point bp at temp data */ call poll_byte /* is a character waiting? */ jz get_multibyte_tail /* if not, bail */ get_multibyte_store: movb %al, (%bp) /* store char received */ incb %cl /* mark one char received */ incw %bp /* point to next char */ cmpb $4, %cl /* got enough chars? */ jz got_multibyte /* no strings longer than 4 chars */ call poll_byte /* is another char waiting? */ jnz get_multibyte_store /* store a new one if it's there */ got_multibyte: movw $multibyteinput, %bx /* point to first scancode */ got_multibyte_findkey: movw %sp, %bp /* bp = start of buffer */ movb %cs:(%bx), %ah /* ah = scancode */ incw %bx /* bx = start of test string */ orb %ah, %ah /* is it zero? */ jz get_multibyte_tail /* if so, bail, key not found */ got_multibyte_nextchar: movb %cs:(%bx), %ch /* ch = test char to compare */ incw %bx /* point to next char */ orb %ch, %ch /* is char to compare NUL? */ jz got_multibyte_key /* matched to end of a string! */ cmpb %ch, (%bp) /* input tmp buf equal to test char? */ jnz got_multibyte_try_next_key /* note: expected that test string will be nul before input string */ /* no attempt is made to ensure no more than 4 bytes stack read */ incw %bp /* point to next input */ jmp got_multibyte_nextchar got_multibyte_try_next_key: /* align to next scancode/ascii pair */ movb %cs:(%bx), %ch /* ch = test char to compare */ incw %bx /* point to next char */ orb %ch, %ch /* is char to compare NUL? */ jnz got_multibyte_try_next_key jmp got_multibyte_findkey got_multibyte_key: xorb %al, %al /* ascii value = 0 for special keys */ movw %sp, %bp movw %ax, 4(%bp) /* overwrite old %ax value with key */ get_multibyte_tail: addw $4, %sp /* pop temp space */ popw %ax popw %cx popw %bx popw %bp ret /* * send_byte * * send character in %al to serial port [FIXME: EFI console out] * * all registers preserved except flags * */ send_byte: pushw %ax pushw %dx pushw %cx testb $0x80, %al /* don't send non-ascii chars */ jnz send_tail /* these should be translated earlier */ movb %al, %ah /* save char to output in %ah */ movw $0xFFF0, %cx /* only retry 65520 times */ serial_ready_test: call get_serial_lsr /* get serial lsr in %al */ testb $TRANSMIT_READY_BIT, %al loopz serial_ready_test /* if !tx ready, loop while cx!=0 */ movb %ah, %al movw %cs:serial_port_base_address, %dx outb %al, %dx send_tail: popw %cx popw %dx popw %ax ret /* * translate_char * * translate vga character in %al to ascii * * returns: * al = translated character * * all registers except %al preserved * */ translate_char: pushw %bx pushw %ds pushw %cs popw %ds /* ds = cs */ testb $0x80, %al jz translate_char_ctrl andb $0x7f, %al movw $high2ascii, %bx xlatb translate_char_ctrl: cmpb $0x20, %al jnc translate_char_tail movw $ctrl2ascii, %bx xlatb translate_char_tail: popw %ds popw %bx ret /* * translate_char_tty * * translate vga character in %al to ascii * unless %al == 7, 8, 10, or 13 (bell, bs, lf, cr) * * returns: * al = translated character * * all registers except %al preserved * */ translate_char_tty: cmpb $0x07, %al /* bell */ jz translate_char_tty_tail cmpb $0x08, %al /* backspace */ jz translate_char_tty_tail cmpb $0x0a, %al /* LF */ jz translate_char_tty_tail cmpb $0x0d, %al /* CR */ jz translate_char_tty_tail call translate_char translate_char_tty_tail: ret /* * send_char * * send character 0 - 255 in %al out through serial port * increment cursor position without control processing * * send_byte is used for data that isn't tracked * * send_char is used for text that should be tracked * send_char outputs all characters as non-control chars * * returns: * al = translated character * * all registers except %al preserved * */ send_char: call sgabioslog_save_char /* save original char+pos */ call translate_char jmp send_char_tty_out /* after ctrl translation, same as send_char_tty */ /* * send_char_tty * * send character 0 - 255 in %al out through serial port * increment cursor position *with* control processing * for bell, linefeed, cr, and backspace (others all printable) * * send_byte is used for data that isn't tracked * * send_char_tty is used for text that should be tracked * * returns: * al = translated character * * all registers except %al preserved * */ /* send character 0 - 255 in %al out through serial port */ /* increment cursor position with CR/LF/Backspace processing */ send_char_tty: call sgabioslog_save_char /* save original char+pos */ call translate_char_tty send_char_tty_out: pushw %dx call update_serial_cursor call get_current_cursor /* vga cursor in %dx */ cmpb $0x0d, %al /* CR */ jnz send_char_tty_nul /* if not CR, check for NUL */ orb %dl, %dl /* already at col 0? */ jz send_char_tty_tail /* no need to re-send CR */ send_char_tty_nul: orb %al, %al /* %al == 0 ? (nul) */ /* more than likely, we have NUL at this point because the caller */ /* tried to read a char using int $0x10, %ah=8, and is trying */ /* to re-output it with different attributes - for now send nothing */ jz send_char_tty_tail send_char_tty_write: call memconsole_log_char /* log character sent */ call send_byte cmpb $0x07, %al /* bell */ jz send_char_tty_tail /* no cursor update for bell */ cmpb $0x08, %al /* backspace */ jz send_char_tty_backspace cmpb $0x0a, %al /* LF */ jz send_char_tty_lf cmpb $0x0d, %al /* CR */ jz send_char_tty_cr incb %dl jmp send_char_tty_tail send_char_tty_backspace: orb %dl, %dl jz send_char_tty_tail decb %dl jmp send_char_tty_tail send_char_tty_lf: incb %dh jmp send_char_tty_tail send_char_tty_cr: xorb %dl, %dl send_char_tty_tail: cmpb %cs:term_cols, %dl jc send_char_tty_check_rows movb %cs:term_cols, %dl decb %dl /* dl = cols - 1 */ send_char_tty_check_rows: cmpb %cs:term_rows, %dh jc send_char_tty_save_cursor movb %cs:term_rows, %dh decb %dh /* dh = rows - 1 */ send_char_tty_save_cursor: call set_current_cursor pushw %ds pushw $BDA_SEG popw %ds /* save current position as the serial terminal position */ /* since a character was just output at that position */ movw %dx, BDA_SERIAL_POS popw %ds popw %dx ret /* * send_asciz_out * * send nul terminated string pointed to by %ds:%si * to serial port without text tracking * * indended to be used for multi-byte send_byte * * all registers preserved except flags */ send_asciz_out: pushw %ax pushw %si cld send_asciz_loop: lodsb test %al,%al jz send_asciz_end call send_byte jmp send_asciz_loop send_asciz_end: popw %si popw %ax ret /* * send_string * * send cx chars in string pointed to by %ds:%si * to serial port with tty tracking * * indended to be used for multi-byte send_char_tty * * all registers preserved except flags */ send_string: pushw %ax pushw %si cld send_string_loop: lodsb call send_char_tty loop send_string_loop popw %si popw %ax ret /* * send_string * * send cx chars in string pointed to by %ds:%si * with interleaved attribute data * * indended to be used for multi-byte send_char_tty * with interleaved vga attribute updates * * all registers preserved except flags */ send_attr_string: pushw %ax pushw %bx pushw %si cld send_attr_string_loop: lodsb call send_char_tty lodsb movb %al, %bl call send_attribute /* send attribute in %bl */ loop send_attr_string_loop popw %si popw %bx popw %ax ret /* * send_number * * send ascii version of number in %al to serial port * * intended for ansi cursor positions and attributes, * so cursor position is not tracked/updated * * all registers preserved except flags */ send_number: pushw %ax pushw %bx aam /* ah = al/10, al = al mod 10 */ movw %ax, %bx /* bh = al/10, bl = al mod 10 */ movb %bh, %al aam /* ah = bh/10, al = bh mod 10 */ movb %al, %bh /* bh = 10s digit, bl = 1s digit */ movb %ah, %al /* ah = al = 100s digit */ testb %al, %al /* is there a 100s digit? */ jz send_tens /* move to tens if not */ orb $0x30, %al /* al = ascii value of digit */ call send_byte send_tens: orb %bh, %ah /* bh = 10s, ah = 100s digits */ jz send_ones /* non-zero = must send tens */ movb %bh, %al /* al = bh = 10s digit */ orb $0x30, %al /* al = ascii value of digit */ call send_byte send_ones: movb %bl, %al /* al = bl = 1s digit */ orb $0x30, %al /* al = ascii value of digit */ call send_byte popw %bx popw %ax ret /* * send_crlf * * send CRLF to serial port * * FIXME: used at vga init and for scrolling terminal * so position is not tracked. Callers of this routine * predate the code that does smart tty/cursor output. * * Callers should probably be changed to use those * routines or send_crlf changed to use them and * terminal scrolling fixed to use linefeed only. * * all registers preserved except flags */ send_crlf: pushw %ax movb $0x0d, %al call send_byte movb $0x0a, %al call send_byte popw %ax ret /* * send_ansi_csi * * send ESCAPE [ to serial port * * output is not tracked since these are control sequences * * all registers preserved except flags */ send_ansi_csi: /* transmit ESC [ */ pushw %ax movb $0x1b, %al /* escape */ call send_byte movb $0x5b, %al /* [ */ call send_byte popw %ax ret /* * send_ansi_csi_2num * * send ESC [ %dh ; %dl to serial port * * since both position and attribute updates generally have * two parameters, this function converts values in dx to * two ascii numbers. It's expected that the caller will * output the final trailing H or m or whatever is required. * * output is not tracked since these are control sequences * * all registers preserved except flags */ send_ansi_csi_2num: /* send ESC [ %dh ; %dl */ pushw %ax call send_ansi_csi /* esc [ */ movb %dh, %al call send_number movb $0x3b, %al /* semicolon */ call send_byte movb %dl, %al call send_number popw %ax ret /* * send_ansi_cursor_pos * * send ESC [ %dh+1 ; %dl+1 to serial port to position * cursor * * since both position and attribute updates generally have * two parameters, this function converts values in dx to * two ascii numbers, after adding 1 to both dh and dl. * * output is not tracked since this is a control sequence * * all registers preserved except flags */ send_ansi_cursor_pos: pushw %ax pushw %dx addw $0x0101, %dx /* dh += 1, dl += 1 */ call send_ansi_csi_2num /* send esc [ %dh+1;%dl+1 */ movb $0x48, %al /* H */ call send_byte popw %dx popw %ax ret /* * send_attribute * * send ansi attribute change ESC [ 4x ; 3y ; (1|22)m * if the attribute has changed since last sent (stored in bda) * * output is not tracked since this is a control sequence * * all registers preserved except flags */ send_attribute: andb $0x7f, %bl /* ansi has no bright bg */ pushw %ds pushw %es pushw %ax pushw %bx pushw %dx pushw $BDA_SEG popw %es /* es = 0x40 */ pushw %cs popw %ds /* ds = cs */ cmpb %es:BDA_COLOR_VAL, %bl jz send_attribute_tail cmpb $0x07, %bl /* is it white on black? */ jnz send_attribute_color /* for white on black, send esc [ m */ call send_ansi_csi jmp send_attribute_m /* send the m, return */ send_attribute_color: movb %bl, %ah /* ah = attribute */ movw $colortable, %bx movb %ah, %al andb $7, %al /* al = fg attr */ xlatb /* al = fg ansi num */ movb %al, %dl /* dl = fg ansi num */ movb %ah, %al shrb $4, %al /* al = bg attr */ xlatb /* al = bg ansi num */ movb %al, %dh /* dh = bg ansi num */ addw $0x281e, %dx /* 3x=setfg, 4x=setbg */ call send_ansi_csi_2num movb $0x3b, %al /* semicolon */ call send_byte shlb $4, %ah /* bright text? */ sets %al /* if bit 7, al = 1 */ js send_attribute_intensity movb $22, %al /* 22 = normal intensity */ send_attribute_intensity: call send_number /* either 22 or 1 */ send_attribute_m: movb $0x6d, %al /* m */ call send_byte send_attribute_tail: popw %dx popw %bx /* mark attribute in %bl the current one */ movb %bl, %es:BDA_COLOR_VAL popw %ax popw %es popw %ds ret /* * serial_get_input * * common code for both interrupt-driven and non-interrupt * driven serial input. Called only when LSR bit 1 is set. * * No parameters, no return values * * Preserves all registers */ serial_get_input: pushf /* be paranoid about int 9h happening during update */ cli pushaw pushw %ds /* next char input buffer is at 0x40:0x1c */ pushw $BDA_SEG popw %ds /* es = 0x40 */ call get_byte /* next scancode/byte in %ax */ cmpb $0x1b, %al /* look for escape */ jnz serial_gotkey /* not escape, don't look for more bytes */ call get_multibyte /* look for any chars after escape */ serial_gotkey: movw KBD_TAIL, %bx /* bx = keyboard tail pointer */ movw %ax, (%bx) /* store key in buffer */ addw $2, %bx /* point to next location */ cmpw $KBD_BUF_END, %bx /* did the buffer wrap? */ jb kbd_buf_no_wrap movw $KBD_BUF_START, %bx kbd_buf_no_wrap: movw %bx, KBD_TAIL /* update tail pointer to show key */ popw %ds popaw popf ret /* * irq3_isr * * entry point for irq 3 / int 0x0b / exception 11 * * Called when COM2 or COM4 have characters pending * * The segment not present exception should never happen * in real mode 16-bit code like this, but just to be safe, * if this interrupt is invoked and no characters are * pending on the port found in serial_port_base_address, * this routine will chain to the original handler. * * If characters are found pending, they will be processed * and control returned via iret. */ irq3_isr: #if 0 pushw %ax pushw %dx /* placeholder, this shouldn't ever happen */ /* no interrupts are configured outside COM1 */ call get_serial_lsr /* get serial lsr in %al */ jz chain_irq3 /* no port present... */ testb $1, %al /* bit 0 of LSR = 1 = data available */ jz chain_irq3 /* no input waiting */ call serial_get_input /* get input and stuff kbd buffer */ movb $0x20, %al outb %al, $0x20 /* send non-specific EOI */ popw %dx popw %ax iret chain_irq3: popw %dx popw %ax #endif jmp do_old_irq3 /* * irq4_isr * * entry point for irq 4 / int 0x0c / exception 12 * * Called when COM1 or COM3 have characters pending * * The stack fault exception may occur if code attempts to * read from sp:0xffff, so if this interrupt is invoked and * no characters are pending on the port found in * serial_port_base_address, this routine will chain to the * original handler. * * If characters are found pending, they will be processed * and control returned via iret. */ irq4_isr: #if 0 pushw %ax pushw %dx call get_serial_lsr /* get serial lsr in %al */ jz chain_irq4 /* no port present... */ testb $1, %al /* bit 0 of LSR = 1 = data available */ jz chain_irq4 /* no input waiting */ call serial_get_input /* get input and stuff kbd buffer */ movb $0x20, %al outb %al, $0x20 /* send non-specific EOI */ popw %dx popw %ax iret chain_irq4: popw %dx popw %ax #endif jmp do_old_irq4 /* * int14h_isr * * entry point for int 14h * */ int14h_isr: pushaw movw %sp, %bp addw $16, %bp /* bp points to return address */ orb %ah, %ah /* fn 0x00, initialize port */ jz int14h_init_port cmpb $0x04, %ah /* fn 0x04, extended intialize */ jnz chain_isr14h int14h_init_port: /* check for init port = current port */ pushw %ds pushw $BDA_SEG popw %ds /* ds = 0x40 */ movw %dx, %bx /* bx = port number */ shlw $1, %bx /* bx = port number * 2 */ andw $7, %bx /* bx = bda offset of serial io addr */ movw (%bx), %cx /* cx = io address of port to init */ popw %ds /* restore original ds */ cmpw %cx, %cs:serial_port_base_address jnz chain_isr14h /* if different, don't get in the way */ /* init port == current port */ pushw %ds /* LILO 22.6 HACK STARTS HERE */ movw (%bp), %bx /* return address for int 14h call */ movw 2(%bp), %ds /* return segment for int 14h call */ cmpl $0x4f4c494c, 0x06 /* does segment have lilo signature? */ jnz int14h_init_tail /* not lilo, bail on hack */ cmpw $0x0616, 0x0a /* does version match lilo 22.6? */ jnz int14h_init_tail /* unknown lilo release, bail on hack */ movb $0, 0x12 /* set lilo com port = 0 */ movl $0x90c3585a, (%bx) /* return code= pop dx;pop ax;ret;nop */ /* now lilo 22.6's own serial out is permanently disabled */ /* this prevents double-character output from int10h + serial */ /* this also prevents lilo from stealing serial input chars */ /* END LILO 22.6 HACK */ int14h_init_tail: popw %ds popaw pushw %dx /* get_serial_lsr trashes %dx */ call get_serial_lsr /* return serial status in %al */ xorb %ah, %ah /* return serial status in %ax */ popw %dx /* restore %dx */ iret chain_isr14h: popaw jmp do_old_int14h /* * int16h_isr * * entry point for int 16h * * keyboard characters are usually retrieved by calling * int 16h, generally placed in the keyboard buffer by * irq 1 (int 9h). Poll serial port for new data before * chaining to int 16h to fake irq 1 behavior * * all registers preserved except flags (later iret will restore) * bda updated with a new keypress if available * * FIXME: handle multi-byte keypresses like cursor up/down * to send proper scancodes for navigating lilo menus */ int16h_isr: pushw %ax pushw %dx /* each time int 16h is invoked, fake an int 9h */ /* except read the serial input buffer */ /* then chain to the original int 16h for processing */ call get_serial_lsr jz chain_isr16h /* no port present... */ testb $1, %al /* bit 0 of LSR = 1 = data available */ jz chain_isr16h /* no input waiting */ call serial_get_input /* get input and stuff kbd buffer */ /* for now, leave remaining chars pending in serial fifo */ /* int 16h callers only get one char at a time anyway */ chain_isr16h: popw %dx popw %ax jmp do_old_int16h /* * update serial_cursor * * figure out where the cursor was, and where it's going * use the minimal amount of serial output to get it there * input: vga cursor and serial cursor positions stored in BDA * * all registers preserved except flags * bda updated with new position for serial console cursor */ update_serial_cursor: pushw %ax pushw %bx pushw %dx pushw %ds pushw $BDA_SEG popw %ds /* ds = 0x40 */ call get_current_cursor /* dh = row, dl = col */ movw BDA_SERIAL_POS, %bx /* bh = row, bl = col */ subb %dl, %bl /* -col update */ negb %bl /* col update */ subb %dh, %bh /* -row update */ negb %bh /* row update */ /* handle a few special movement cases */ /* cr, lf, bs, bs+bs, space, else send full ansi position */ orb %dl, %dl /* column zero? */ jnz update_serial_cursor_lf movb $0x0d, %al /* CR */ call send_byte xorb %bl, %bl /* mark no diff in col */ update_serial_cursor_lf: cmpb $1, %bh /* +1 row? */ jnz update_serial_cursor_bs movb $0x0a, %al /* LF */ call send_byte xorb %bh, %bh /* mark no diff in row */ update_serial_cursor_bs: cmpb $-1, %bl /* one char back */ jz update_serial_cursor_one_bs cmpb $-2, %bl /* two chars back */ jnz update_serial_cursor_space /* check for space */ movb $0x08, %al /* BS */ call send_byte update_serial_cursor_one_bs: movb $0x08, %al /* BS */ call send_byte xorb %bl, %bl /* mark no diff in col */ update_serial_cursor_space: cmpb $1, %bl /* one char forward */ jnz update_serial_cursor_up movb $0x20, %al /* space */ call send_byte xorb %bl, %bl /* mark no diff in col */ update_serial_cursor_up: cmpb $-1, %bh /* -1 row? */ jnz update_serial_cursor_full /* do full ansi pos update */ call send_ansi_csi /* send ESC [ A (cursor up) */ movb $0x41, %al /* A */ call send_byte xorb %bh, %bh /* mark no diff in row */ update_serial_cursor_full: orw %bx, %bx /* diff = 0? */ jz update_serial_cursor_done call send_ansi_cursor_pos /* set cursor pos from dh,dl */ update_serial_cursor_done: movw %dx, BDA_SERIAL_POS popw %ds popw %dx popw %bx popw %ax ret /* * write_teletype * * handle int 10h, function 0eh * * ah = 0x0e write teletype character * al = character ascii code * bh = display page number * * all registers except %al preserved * caller will restore all registers */ write_teletype: pushw %bx movb $0x07, %bl /* black bg, white fg */ call send_attribute popw %bx call send_char_tty ret /* * write_attr_char * * handle int 10h, function 09h * * ah = 0x09 write attribute/character at current cursor position * al = character ascii code * bh = display page number * bl = character attribute * cx = repetition count * * does not update cursor position * all registers except %cx and %al preserved * caller will restore all registers */ write_attr_char: call send_attribute /* send attribute in %bl */ jmp write_char_common /* * write_char * * handle int 10h, function 0ah * * ah = 0x0a write character at current cursor position * al = character ascii code * bh = display page number * cx = repetition count * * does not update cursor position * all registers except %cx and %al preserved * caller will restore all registers */ write_char: pushw %bx movb $0x07, %bl /* black bg, white fg */ call send_attribute popw %bx write_char_common: call get_current_cursor call send_char /* make cx=0 and cx=1 only output one char */ cmpw $1, %cx jbe write_char_tail decw %cx jmp write_char write_char_tail: /* put cursor back where it was on entry */ call set_current_cursor ret /* * write_string * * handle int 10h, function 13h * * ah = 0x13 write character at current cursor position * al = 0, data = char, ..., no cursor update * al = 1, data = char, ..., cursor at end of string * al = 2, data = char+attr, ..., no cursor update * al = 3, data = char+attr, ..., cursor at end of string * bh = display page number * bl = character attribute for all chars (if al = 0 or 1) * cx = characters in string (attributes don't count) * dh = cursor row start * dl = cursor column start * es:bp = pointer to source text string in memory * * all registers preserved except flags * caller will restore all registers */ write_string: call set_cursor_position pushw %ds pushw %es pushw %es popw %ds /* ds = es */ movw %bp, %si /* si = bp */ testb $2, %al jnz write_attr_string call send_attribute /* send attribute in %bl */ test %cx, %cx jz write_string_empty call send_string /* plaintext out */ write_string_empty: jmp write_string_update_cursor write_attr_string: call send_attr_string /* text+attrib out */ write_string_update_cursor: testb $1, %al /* cursor update? */ jnz write_string_tail /* yes? already happened */ /* restore entry cursor position if no update */ call set_cursor_position write_string_tail: popw %es popw %ds ret /* * set_cursor_position * * handle int 10h, function 02h * * ah = 0x02 set cursor position * bh = display page number * dh = cursor row * dl = cursor column * * update bda cursor position with value in %dx * serial console cursor only updated on text output * this routine also called by set_current_cursor * which won't bother setting ah = 2 * * all registers preserved except flags */ set_cursor_position: pushw %ax pushw %bx pushw %ds pushw $BDA_SEG popw %ds /* ds = 0x40 */ movzbw %bh, %ax /* ax = page number */ andb $0x07, %al /* prevent invalid page number */ shlb $1, %al /* calculate word offset */ addb $BDA_CURSOR_BUF, %al /* ax = cursor save offset */ movw %ax, %bx /* bx = cursor save offset */ movw %dx, (%bx) /* save new cursor value */ popw %ds popw %bx popw %ax ret /* * set_current_cursor * * get current display page number and call set_cursor_positon * to store the row/column value in dx to the bda * * all registers preserved except flags */ set_current_cursor: pushw %ds pushw %bx pushw $BDA_SEG popw %ds /* ds = 0x40 */ movb BDA_ACTIVE_PAGE, %bh call set_cursor_position popw %bx popw %ds ret /* * get_cursor_common * * read cursor position for page %bh from bda into %dx * * returns: * dh = cursor row * dl = cursor column * ch = cursor start scanline * cl = cursor end scanline * * all registers except %dx, %cx preserved */ get_cursor_common: pushw %bx pushw %ds pushw $BDA_SEG popw %ds /* ds = 0x40 */ movzbw %bh, %bx /* dx = current page */ andb $7, %bl shlb $1, %bl addb $BDA_CURSOR_BUF, %bl movw (%bx), %dx /* get cursor pos */ movw BDA_CURSOR_SCAN, %cx popw %ds popw %bx ret /* * get_current_cursor * * read cursor position for current page from bda into %dx * * returns: * dh = cursor row * dl = cursor column * * all registers except %dx preserved */ get_current_cursor: pushw %ds pushw %bx pushw %cx pushw $BDA_SEG popw %ds /* ds = 0x40 */ movb BDA_ACTIVE_PAGE, %bh call get_cursor_common popw %cx popw %bx popw %ds ret /* * get_cursor_position * * handle int 10h, function 03h * * ah = 0x02 get cursor position * bh = display page number * * returns: * ax = 0 * ch = cursor start scanline * ch = cursor end scanline * dh = cursor row * dl = cursor column * * all registers except %ax, %cx, %dx preserved */ get_cursor_position: call bail_if_vga_attached /* does not return if vga attached */ popw %ax /* not chaining, pop fake return address */ popw %dx /* not chaining, pop saved cursor position */ popaw /* not chaining to old int 10h, pop saved state */ call get_cursor_common xorw %ax, %ax iret /* * return_current_video_state * * handle int 10h, function 0fh * * ah = 0x0f return current video state * * returns: * ah = number of columns on screen (from 40:4a) * al = current video mode setting (from 40:49) * bh = active display page number (from 40:62) * * all registers except %ax and %bh preserved */ read_current_video_state: call bail_if_vga_attached /* does not return if vga attached */ popw %ax /* not chaining, pop fake return address */ popw %dx /* not chaining, pop saved cursor position */ popaw /* not chaining to old int 10h, pop saved state */ pushw %ds pushw $BDA_SEG popw %ds /* ds = 0x40 */ movb BDA_COLS, %ah movb BDA_MODE_NUM, %al movb BDA_ACTIVE_PAGE, %bh popw %ds iret /* * read_attr_char * * handle int 10h, function 08h * * ah = 0x08 read character/attribute from screen * * returns: * ah = attribute at current cursor position * al = character read from current cursor position * * all registers preserved except %ax and flags */ read_attr_char: call bail_if_vga_attached /* does not return if vga attached */ popw %ax /* not chaining, pop fake return address */ popw %dx /* not chaining, pop saved cursor position */ popaw /* not chaining to old int 10h, pop saved state */ pushw %ds pushw $BDA_SEG popw %ds /* ds = 0x40 */ movb BDA_COLOR_VAL, %ah /* return last color value */ call sgabioslog_get_char popw %ds iret /* * set_video_mode * * handle int 10h, function 00h * * ah = 0x00 set video mode * al = video mode * * unless bit 7 of al = 1, setting mode clears screen * * all registers preserved except %bh, %dx, flags */ set_video_mode: testb $0x80, %al /* preserve screen flag? */ jnz set_video_mode_tail call send_ansi_csi movb $0x32, %al /* 2 */ call send_byte movb $0x4a, %al /* J */ call send_byte set_video_mode_tail: movb $0x07, %bl /* white on black text */ call send_attribute /* send attribute in %bl */ /* set cursor position to 0,0 */ xorb %bh, %bh /* page 0 */ xorw %dx, %dx jmp set_cursor_position /* * scroll_page_up * * handle int 10h, function 06h * * ah = 0x06 scroll current page up * al = scroll distance in character rows (0 blanks entire area) * bh = attribute to used on blanked lines * ch = top row (upper left corner) of window * cl = left-most column (upper left corner) of window * dh = bottom row (lower right corner) of window * dl = right-most column (lower right corner) of window * * all registers preserved except flags */ scroll_page_up: pushw %si pushw %dx call get_current_cursor /* save current cursor */ movw %dx, %si /* si = vga cursor pos */ popw %dx cmpb $0, %al /* al = 0 = clear window */ jz scroll_common_clear pushw %ax call send_ansi_csi /* CSI [ %al S */ call send_number movb $0x53, %al /* S */ call send_byte popw %dx popw %si ret /* * scroll_common_clear * * common tail for up/down scrolls to clear window specified * in %cx and %dx. * * stack should contain saved copy of si * si = original vga cursor position on service entry * * bh = attribute to used on blanked lines * ch = top row (upper left corner) of window * cl = left-most column (upper left corner) of window * dh = bottom row (lower right corner) of window * dl = right-most column (lower right corner) of window */ scroll_common_clear: pushw %ax xchgb %bl, %bh /* bl = attribute, bh = old bl */ call send_attribute /* send attribute in %bl */ xchgb %bl, %bh /* restore bx */ pushw %ds pushw $BDA_SEG popw %ds /* ds = 0x40 */ /* check to see if region is full screen, and attribute default */ orw %cx, %cx /* is top left 0,0? */ jnz scroll_common_window /* no, handle window */ cmpb $0x07, %bh /* is attribute white on black? */ jnz scroll_common_window /* no, must write spaces */ #ifdef LILO_CLEAR_WORKAROUND_NOT_REQUIRED cmpb %cs:term_cols, %dl /* is right less than cols ? */ jc scroll_common_window /* if so, handle window */ cmpb %cs:term_rows, %dh /* is bottom less than rows ? */ jc scroll_common_window /* if so, handle window */ #endif /* safe to send standard clear screen sequence */ call send_ansi_csi /* send ESC [ */ movb $0x32, %al /* 2 */ call send_byte movb $0x4a, %al /* J */ call send_byte jmp scroll_common_tail scroll_common_window: pushw %dx movw %cx, %dx /* dx = upper right */ call set_current_cursor popw %dx pushw %cx /* setup cx with count of chars to clear per row */ xorb %ch, %ch negb %cl addb %dl, %cl /* cl = dl - cl */ incb %cl /* start = end col = clear 1 col */ cmpb %cs:term_cols, %cl /* is count < cols? */ jc scroll_common_row_ok /* if so then skip limit */ movb %cs:term_cols, %cl /* limit count to cols */ scroll_common_row_ok: jz scroll_common_row_done /* count == 0 ? */ movb $0x20, %al /* space */ scroll_common_space_loop: call send_char loop scroll_common_space_loop /* send cx spaces */ scroll_common_row_done: popw %cx incb %ch /* top left now next row */ cmpb %dh, %ch jbe scroll_common_window /* do next row */ scroll_common_tail: popw %ds popw %ax pushw %dx movw %si, %dx /* dx = saved vga cursor pos */ call set_current_cursor /* restore saved cursor */ popw %dx popw %si ret /* * scroll_page_down * * handle int 10h, function 07h * * ah = 0x07 scroll current page down * al = scroll distance in character rows (0 blanks entire area) * bh = attribute to used on blanked lines * ch = top row (upper left corner) of window * cl = left-most column (upper left corner) of window * dh = bottom row (lower right corner) of window * dl = right-most column (lower right corner) of window * * FIXME: this routine doesn't handle windowing, it currently * only handles one line screen scrolls and erasing entire screen * * all registers preserved except flags */ scroll_page_down: pushw %si pushw %dx call get_current_cursor /* save current cursor */ movw %dx, %si /* si = vga cursor pos */ popw %dx cmpb $0, %al /* al = 0 = clear window */ jz scroll_common_clear pushw %ax call send_ansi_csi /* CSI [ %al T */ call send_number movb $0x54, %al /* T */ call send_byte popw %dx popw %si ret /* * bail_if_vga_attached * * Check for vga installed, if not, return to caller. * If so, pop return address, return to chain_isr_10h * * expected that routine calling this one has chain_isr_10h * as the next item on the stack * * all registers except flags and sp preserved */ bail_if_vga_attached: cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ jnz bail_tail /* if not, don't modify stack */ addw $2, %sp /* else drop first return address */ bail_tail: ret /* return to caller or chain_isr_10h */ /* * int10h_isr * * entry point for int 10h * * save all registers, force return to chain to previous int10h isr * decide which function in ah needs to be dispatched * * ah = 0x00 set mode * ah = 0x01 set cursor type * ah = 0x02 set cursor position * ah = 0x03 read cursor position * ah = 0x04 read light pen position * ah = 0x05 set active display page * ah = 0x06 scroll active page up * ah = 0x07 scroll active page down * ah = 0x08 read attribute/character at cursor * ah = 0x09 write attribute/character at cursor * ah = 0x0a write character at cursor position * ah = 0x0b set color palette * ah = 0x0c write pixel * ah = 0x0d read pixel * ah = 0x0e write teletype * ah = 0x0f read current video state * ah = 0x10 set individual palette registers * ah = 0x11 character generation (font control/info) * ah = 0x12 alternate select (video control/info) * ah = 0x13 write string * ah = 0x1a read/write display combination code * ah = 0x1b return functionality/state information * ah = 0x1c save/restore video state * ah = 0x4f vesa bios calls * all registers preserved except flags (later iret will restore) */ int10h_isr: pushaw call get_current_cursor pushw %dx /* save current cursor */ pushw %bp /* need bp for indexing off stack */ movw %sp, %bp /* bp = sp */ movw 14(%bp), %dx /* restore dx from earlier pushaw */ popw %bp /* restore old bp */ pushw $chain_isr10h /* force return to chain_isr10h */ testb %ah, %ah jnz int10h_02 jmp set_video_mode int10h_02: cmpb $0x02, %ah jnz int10h_03 jmp set_cursor_position int10h_03: cmpb $0x03, %ah jnz int10h_06 jmp get_cursor_position int10h_06: cmpb $0x06, %ah jnz int10h_07 jmp scroll_page_up int10h_07: cmpb $0x07, %ah jnz int10h_08 jmp scroll_page_down int10h_08: cmpb $0x08, %ah jnz int10h_09 jmp read_attr_char int10h_09: cmpb $0x09, %ah jnz int10h_0a jmp write_attr_char int10h_0a: cmpb $0x0a, %ah jnz int10h_0e jmp write_char int10h_0e: cmpb $0x0e, %ah jnz int10h_0f jmp write_teletype int10h_0f: cmpb $0x0f, %ah jnz int10h_13 jmp read_current_video_state int10h_13: cmpb $0x13, %ah jnz int10h_default jmp write_string int10h_default: popw %ax /* pop chain_isr10h return address */ chain_isr10h: popw %dx /* pop saved cursor */ cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ jnz chain_post_cursor /* if not, don't restore the cursor */ call set_current_cursor /* restore cursor if vga attached */ chain_post_cursor: popaw jmp do_old_int10h /* * pnp_sga_init * * handle PnP initialization of option rom * * es:di = pointer to PnP structure * ax = indication as to which vectors should be hooked * by specifying th type of boot device this has * been selected as * bit 7..3= reserved(0) * bit 2 = 1 = connect as IPL (int 13h) * bit 1 = 1 = connect as primary video (int 10h) * bit 0 = 1 = connect as primary input (int 9h) * bx = card select number (probably 0xffff) * dx = read data port address (probably 0xffff) * * return: * ax = initialization status * bit 8 = 1 = IPL device supports int 13h block dev format * bit 7 = 1 = Output device supports int 10h char output * bit 6 = 1 = Input device supports int 9h char input * bit 5..4 = 00 = no IPL device attached * 01 = unknown whether or not IPL device attached * 10 = IPL device attached (RPL devices have connection) * 11 = reserved * bit 3..2 = 00 = no display device attached * 01 = unknown whether or not display device attached * 10 = display device attached * 11 = reserved * bit 1..0 = 00 = no input device attached * 01 = unknown whether or not input device attached * 10 = input device attached * 11 = reserved * * all registers preserved except %ax */ pnp_sga_init: /* FIXME: this is *wrong* -- init only what bios says to init */ movw $0xca, %ax /* 0xca = attached int 10h, 9h display, input */ /* * sga_init * * legacy option rom entry point * * all registers preserved */ sga_init: /* this is probably paranoid about register preservation */ pushfw cli /* more paranoia */ pushaw pushw %ds pushw %es pushw $0 popw %es /* es = 0 */ pushw %cs popw %ds /* ds = cs */ /* get original ISR */ movl %es:0x28, %eax /* eax = old irq 3/int 0bh */ movl %eax, old_irq3 /* save away old irq 4/int 0bh */ movl %es:0x2c, %eax /* eax = old irq 4/int 0ch */ movl %eax, old_irq4 /* save away old irq 4/int 0ch */ movl %es:0x40, %eax /* eax = old int 10h */ movl %eax, old_int10h /* save away old int 10h */ movl %es:0x50, %eax /* eax = old int 14h */ movl %eax, old_int14h /* save away old int 14h */ movl %es:0x58, %eax /* eax = old int 16h */ movl %eax, old_int16h /* save away old int 16h */ movw $irq3_isr, %es:0x28 /* new irq 3 offset */ movw %cs, %es:0x2a /* write new irq 3 seg */ movw $irq4_isr, %es:0x2c /* new irq 4 offset */ movw %cs, %es:0x2e /* write new irq 4 seg */ movw $int10h_isr, %es:0x40 /* new int 10h offset */ movw %cs, %es:0x42 /* write new int10h seg */ movw $int14h_isr, %es:0x50 /* new int 14h offset */ movw %cs, %es:0x52 /* write new int14h seg */ movw $int16h_isr, %es:0x58 /* new int 16h offset */ movw %cs, %es:0x5a /* write new int16h seg */ /* empty input buffer to prepare for terminal sizing */ call init_serial_port input_clear_loop: call get_byte jnz input_clear_loop movw $term_init_string, %si call send_asciz_out push $BDA_SEG push $BDA_SEG popw %ds /* ds = 0x40 */ popw %es /* es = 0x40 */ movw $BDA_CURSOR_BUF, %di input_timeout_loop: /* get input from terminal until timeout found */ /* store input at 40:50 - 40:5e (cursor pos) */ call poll_byte jz input_timeout stosb /* es:di */ cmpw $0x5f, %di /* 14 characters max */ jnz input_timeout_loop /* good for more data */ input_timeout: xorb %al, %al /* nul terminate input */ stosb cmpw $0x58, %di /* less than 8 chars? */ jc resize_end /* too small to have valid data */ movw $BDA_CURSOR_BUF, %si /* point to start */ lodsw /* ax = first 2 chars */ cmpw $0x5b1b, %ax /* was it "ESC[" ? */ jnz resize_end /* reply starts ESC[row;colR */ xorb %bl, %bl /* bl = ascii->int conversion */ input_first_number: lodsb /* al = next char */ cmpb $0x30, %al jc resize_end /* char < 0x30 invalid */ cmpb $0x3a, %al /* is char < 0x3a */ jnc input_semicolon andb $0x0f, %al /* al = 0 - 9 */ movb %bl, %ah /* ah = last conversion */ aad /* ax = (al + ah * 10) & 0xff */ movb %al, %bl /* bl = row ascii->int conversion */ jmp input_first_number input_semicolon: /* at this point bl should contain rows, al = ; */ /* sanity check, bail if invalid */ cmpb $0x3b, %al jnz resize_end /* invalid input found */ cmpb $0x0a, %bl /* less than 10 rows? */ jc suspect_loopback /* consider input invalid */ xorb %bh, %bh /* bh = col ascii->int conversion */ input_second_number: lodsb /* al = next char */ cmpb $0x30, %al jc resize_end /* char < 0x30 invalid */ cmpb $0x3a, %al /* is char < 0x3a */ jnc input_final_r andb $0x0f, %al /* al = 0 - 9 */ movb %bh, %ah /* ah = last conversion */ aad /* ax = (al + ah * 10) & 0xff */ movb %al, %bh /* bh = ascii->int conversion */ jmp input_second_number input_final_r: cmpb $0x52, %al /* is al = 'R' ? */ jnz suspect_loopback /* invalid input found */ movb %bl, %cs:term_rows /* save away bl rows value */ cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ jz resize_end /* if so, leave term_cols at 80 */ movb %bh, %cs:term_cols /* save away bh cols value */ jmp resize_end suspect_loopback: /* * characters were received that look like what we sent out * at this point, assume that a loopback device was plugged in * and disable any future serial port reads or writes, by pointing * output to port 0x2e8 (COM4) instead of 0x3f8 -- it's expected * that this is safe since a real port responds correctly and a * missing port will respond with 0xff which will terminate the * loop that waits for the "right" status on the port. */ movw $0x2e8, %cs:serial_port_base_address resize_end: /* clear (hopefully) overwritten cursor position buffer */ xorb %al, %al movw $BDA_CURSOR_BUF, %di movw $0x10, %cx cld rep stosb /* fill 40:50 - 40:5f with 0 */ pushw %cs popw %ds /* ds = cs */ call get_byte /* flush any remaining "wrong" input */ jnz resize_end call send_crlf /* place cursor on start of last line */ movw $mfg_string, %si call send_asciz_out call send_crlf movw $prod_string, %si call send_asciz_out call send_crlf movw $long_version, %si call send_asciz_out call send_crlf /* if vga attached, skip terminal message and bda setup... */ cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ jz post_bda_init_tail /* if so, don't modify BDA */ /* show detected terminal size, or default if none detected */ movw $term_info, %si call send_asciz_out pushw $BDA_SEG popw %ds /* ds = 0x40 */ movb %cs:term_cols, %al movb %al, BDA_COLS /* 40:4a = number of character cols */ movb $0, BDA_CURSOR_COL /* 40:51 = cursor0 col */ call send_number movb $0x78, %al /* x */ call send_byte movb %cs:term_rows, %al movb %al, %ah decb %ah /* ah = rows-1 */ movb %ah, BDA_ROWS /* 40:84 = num character rows - 1 */ movb %ah, BDA_CURSOR_ROW /* 40:50 = cursor0 row */ call send_number call send_crlf movb $3, BDA_MODE_NUM movb $0x29, BDA_MODE_SEL movw $VGA_IO_BASE, BDA_6845_ADDR movw $0x4000, BDA_PAGE_SIZE /* 16KB per video page */ /* to avoid ansi colors every character, store last attribute */ movb $0x07, BDA_COLOR_VAL /* 07 = black bg, white fg */ movw %cs, %ax movw $_start, BDA_ROM_OFF movw %ax, BDA_ROM_SEG post_bda_init_tail: /* copy BDA rows/cols to sgabios location... */ /* if vga card is installed, reuse those values... */ /* if no vga card is installed, this shouldn't change anything */ pushw $BDA_SEG popw %ds /* ds = 0x40 */ movb BDA_ROWS, %al incb %al /* bda holds rows-1 */ movb %al, %cs:term_rows /* sgabios rows */ movb BDA_COLS, %ah movb %ah, %cs:term_cols /* sgabios cols */ /* setup in-memory logging of console if desired... */ call setup_memconsole /* setup logging of last 256 characters output, if ebda has room */ call sgabioslog_setup_ebda movw $ebda_info, %si call send_asciz_out movw %cs:sgabios_ebda_logbuf_offset, %ax xchgb %ah, %al call send_number movb $0x20, %al call send_byte movb %ah, %al call send_number call send_crlf popw %es popw %ds popaw popf lret _end_sgabios: