2436 lines
79 KiB
ArmAsm
2436 lines
79 KiB
ArmAsm
/*
|
|
* 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><L-^v"
|
|
high2ascii:
|
|
/* translate vga (CP437) chars 0x80 to 0xff to ascii */
|
|
/* these characters are mostly to visually approximate */
|
|
/* line art characters will probably need tweaking */
|
|
/*80*/ .ascii "CueaaaaceeeiiiAAEaAooouuyOUcLYPf"
|
|
/*a0*/ .ascii "aiounNao?--24!<>###||||++||+++++"
|
|
/*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:
|