2 * Copyright 2010 Google Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #define BUILD_CL "$Id$"
26 /* option rom header */
30 .size _start, .-_start
33 .type legacy_entry,@function
36 /* pnp entry here to avoid changing PnP table as code moves */
43 * Patched at option rom init to be a far jump to old int 10h isr
47 .byte 0xea /* jmp absolute segment:offset */
48 old_int10h: /* store what was at offset 0x40 */
49 .word 0xf065 /* placeholder for chained ISR offset */
50 /* if the chained segment is detected as 0xc000, use 80 cols only */
51 /* since it's assumed that a vga card is attached and 80 cols max */
53 .word 0xf000 /* placeholder for chained ISR segment */
57 * Patched at option rom init to be a far jump to old int 16h isr
61 .byte 0xea /* jmp absolute segment:offset */
62 old_int16h: /* store what was at offset 0x58 */
63 .word 0xe82e /* placeholder for chained ISR offset */
64 .word 0xf000 /* placeholder for chained ISR segment */
66 .word 0 /* offset to PCI data, 0 = none */
67 .word pnp_table /* offset to PnP expansion header */
70 /* FIXME: **** PnP header currently disabled by PoO **** */
71 /* legacy entry only called once, PnP entry called multiple times */
72 /* The code isn't yet written to deal with multiple inits properly */
73 .ascii "$PoO" /* PnP expansion header signature */
74 .byte 1 /* structure revision */
75 .byte 2 /* length in 16-byte increments */
76 .word 0 /* offset of next header, 0 if none */
77 .byte 0 /* reserved */
78 .byte 0x52 /* checksum - update manually! FIXME */
79 .long 0 /* device identifier */
80 .word mfg_string /* pointer to manufacturer string */
81 .word prod_string /* pointer to product name string */
82 .byte 3, 0x80, 0x80 /* device type code = other display */
83 .byte 0xe3 /* device indicators, kbd/display dev */
84 .word 0 /* boot connection vector, 0 if none */
85 .word 0 /* disconnect vector, 0 if none */
86 .word pnp_init /* bootstrap entry vector */
87 .word 0 /* reserved */
88 .word 0 /* static resource information vector */
90 /* WARNING: changing mfg_string / prod_string locations will */
91 /* affect pnp table above -- recalculate checksum manually! */
95 .ascii "Serial Graphics Adapter "
97 .asciz BUILD_SHORT_DATE
108 .byte 80 /* overwritten at rom init with detected value */
110 .byte 24 /* overwritten at rom init with detected value */
111 term_init_string: /* terminal reply: \033[n;mR n=row, m=col */
112 .asciz "\033[1;256r\033[256;256H\033[6n"
113 /* reset the scroll, move to col 256, row 256, ask current position */
114 /* bios cursor positions >255 rows or cols can't be used anyway */
121 * do_old_irq3 - exception 0x0b, int 0x0a
123 * Patched at option rom init to be a far jump to old irq 3 isr
127 .byte 0xea /* jmp absolute segment:offset */
128 old_irq3: /* store what was at offset 0x28 */
129 .word 0xeef3 /* placeholder for chained ISR offset */
130 .word 0xf000 /* placeholder for chained ISR segment */
133 * do_old_irq4 - exception 0x0c, int 0x0b
135 * Patched at option rom init to be a far jump to old irq 4 isr
139 .byte 0xea /* jmp absolute segment:offset */
140 old_irq4: /* store what was at offset 0x2c */
141 .word 0xeef3 /* placeholder for chained ISR offset */
142 .word 0xf000 /* placeholder for chained ISR segment */
147 * Patched at option rom init to be a far jump to old int 14h isr
151 .byte 0xea /* jmp absolute segment:offset */
152 old_int14h: /* store what was at offset 0x50 */
153 .word 0xe739 /* placeholder for chained ISR offset */
154 .word 0xf000 /* placeholder for chained ISR segment */
156 .align 16, 0xff /* aligning this table only makes hexdump prettier */
157 /* ascii -> scancode, bit 7=shifted, char < 32 = +ctrl */
158 /* except chars 8, 9, 13, 27 (bs, tab, enter, esc) */
159 /* most int16h consumers will probably never use */
161 /*00*/ .byte 0x00, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22
162 /*08*/ .byte 0x0e, 0x17, 0x24, 0x25, 0x26, 0x1c, 0x31, 0x18
163 /*10*/ .byte 0x19, 0x0f, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11
164 /*18*/ .byte 0x2d, 0x15, 0x2c, 0x01, 0x2b, 0x1b, 0x87, 0x8c
165 /*20*/ .byte 0x39, 0x82, 0xa8, 0x84, 0x85, 0x86, 0x88, 0x28
166 /*28*/ .byte 0x8a, 0x8b, 0x89, 0x8d, 0x33, 0x0c, 0x34, 0x35
167 /*30*/ .byte 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
168 /*38*/ .byte 0x09, 0x0a, 0xa7, 0x27, 0xb3, 0x0d, 0x34, 0xb5
169 /*40*/ .byte 0x83, 0x9e, 0xb0, 0xae, 0xa0, 0x92, 0xa1, 0xa2
170 /*48*/ .byte 0xa3, 0x97, 0xa4, 0xa5, 0xa6, 0xb2, 0xb1, 0x98
171 /*50*/ .byte 0x99, 0x90, 0x93, 0x9f, 0x94, 0x96, 0xaf, 0x91
172 /*58*/ .byte 0xad, 0x95, 0xac, 0x1a, 0x2b, 0x1b, 0x87, 0x8c
173 /*60*/ .byte 0x29, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22
174 /*68*/ .byte 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18
175 /*70*/ .byte 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11
176 /*78*/ .byte 0x2d, 0x15, 0x2c, 0x9a, 0xab, 0x9b, 0xa9, 0x0e
178 /* TABLES FOR NON-ASCII VGA CHARACTERS (CP437) TO ASCII */
179 /* Unicode at: http://en.wikipedia.org/wiki/Code_page_437 */
182 /* translate vga (CP437) first 32 characters to ascii */
183 /* for char 0, update the cursor position, but output nothing */
184 /* lilo uses this "trick" for a background attribute update */
185 .ascii "\0@@v***........*><|!PS-|^v><L-^v"
187 /* translate vga (CP437) chars 0x80 to 0xff to ascii */
188 /* these characters are mostly to visually approximate */
189 /* line art characters will probably need tweaking */
190 /*80*/ .ascii "CueaaaaceeeiiiAAEaAooouuyOUcLYPf"
191 /*a0*/ .ascii "aiounNao?--24!<>###||||++||+++++"
192 /*c0*/ .ascii "+--|-+||++--|-+----++++++++#-||-"
193 /*e0*/ .ascii "abgpesut00osiye^=+><||-=...vn2* "
196 /* vga text color is IRGB, ansi color is BGR */
197 /* this table is effectively a nibble bit-reverse */
198 .byte 0, 4, 2, 6, 1, 5, 3, 7
200 serial_port_base_address:
203 /* in-memory console log
205 * It's expected that the EBDA contains a magic signature
206 * like 0xdeadbabe, followed by a byte of flags, followed
207 * by a 32-bit buffer pointer, followed by a 16-bit start
208 * index, followed by a 16-bit end index, followed by 16-
209 * bit logged character count, followed by an 8-bit flag.
212 #define MEMCONSOLE_BUFFER_SIZE 32768
213 #define MEMCONSOLE_SIGNATURE 0xdeadbabe
214 #define MEMCONSOLE_ENDINDEX_OFF 0x0b
215 #define SGABIOS_EBDA_SIGNATURE 0x00414753
217 memconsole_buffer_start: /* pulled from ebda struct */
218 .long 0x00000000 /* 0 = not found/no logging */
219 memconsole_ebda_deadbabe_offset: /* bytes from start of ebda */
220 .word 0x0000 /* 40:0e contains ebda seg */
221 sgabios_ebda_logbuf_offset: /* bytes from start of ebda */
222 .word 0x0000 /* 40:0e contains ebda seg */
227 * Initialize the option rom variables associated with logging
228 * of the legacy console output
230 * If these variables are left at zero, no logging will occur
232 * There are no parameters
233 * All registers except flags should be preserved
241 popw %ds /* ds = 0x40 */
242 pushw BDA_EBDA /* push word at 0x0e */
243 popw %es /* es = EBDA_SEG */
244 /* search for memconsole signature in ebda */
245 movl $MEMCONSOLE_SIGNATURE, %eax
246 xorw %di, %di /* start at zero */
247 movzbw %es:(%di), %cx /* cx = size of EBDA in KB */
248 shlw $8, %cx /* cx = (cx * 1024) / 4 */
251 scasl /* search until sig found */
252 subw $4, %di /* scasl always increments di, undo */
253 cmpl %eax, %es:(%di) /* is signature here? */
254 jnz setup_memconsole_end /* bail if so */
255 movw %di, %cs:memconsole_ebda_deadbabe_offset /* save offset */
256 movl %es:5(%di), %eax /* get 32-bit buffer base address */
257 movl %eax, %cs:memconsole_buffer_start
258 setup_memconsole_end:
265 * memconsole_log_char
267 * Log the character passed in %al to the next available memory
268 * console log position, if any.
270 * If memconsole_buffer_start is zero, no logging will occur
272 * %al = character to be logged
273 * All registers except flags should be preserved
282 popw %ds /* ds = 0x40 */
283 pushw BDA_EBDA /* push word at 0x0e */
284 popw %es /* es = EBDA_SEG */
285 movw %ax, %si /* %si = %al = byte to write */
286 movl %cs:memconsole_buffer_start, %ebp
287 movw %cs:memconsole_ebda_deadbabe_offset, %di
288 addw $MEMCONSOLE_ENDINDEX_OFF, %di /* %di points to char pos */
290 jz memconsole_log_tail /* bufptr==0, no logging */
291 movw %es:(%di), %bx /* bx = current position in buffer */
292 cmpw $MEMCONSOLE_BUFFER_SIZE, %bx /* at end of buffer? */
293 jnc memconsole_log_tail /* don't log any more if so */
294 cmpb $0xd, %al /* is the char CR? */
295 jz memconsole_log_tail /* if so, ignore it */
296 cmpb $0x8, %al /* is the char backspace? */
297 jnz memconsole_update_fsbase /* if not, log char as usual... */
298 orw %bx, %bx /* make sure ptr isn't already zero */
299 jz memconsole_log_tail /* if so, bail */
300 decw %bx /* else point to previous character */
301 jmp memconsole_update_end_ptr /* and go directly to save it */
302 memconsole_update_fsbase:
303 movl $0xc0000100, %ecx /* ecx = IA32_FS_BASE (AMD64+) */
304 rdmsr /* read what was there before */
305 pushl %eax /* save away previous FS_BASE eax */
306 pushl %edx /* save away previous FS_BASE edx */
307 xorl %edx, %edx /* clear high 32 bits */
308 movl %ebp, %eax /* eax = memconsole buffer start */
309 wrmsr /* fs_base = memconsole buffer start */
310 movw %si, %ax /* %ax = saved value on entry */
311 movb %al, %fs:(%bx) /* log character */
312 popl %edx /* restore previous FS_BASE edx */
313 popl %eax /* restore previous FS_BASE eax */
314 wrmsr /* write what was there before */
315 incw %bx /* update character count */
316 memconsole_update_end_ptr:
317 movw %bx, %es:(%di) /* save new end pointer */
318 addw $2, %di /* numchars stored at next word */
319 movw %bx, %es:(%di) /* save new numchar value */
327 /* sgabioslog_setup_ebda
329 * SGABIOS makes its own 1KB EBDA allocation to save non-
330 * translated characters with associated cursor positions
331 * for the last 256 characters output. This is organized
332 * with 256 bytes reserved for houskeeping, 256 bytes for
333 * the raw character codes, and 512 bytes of 16bit cursor
334 * positions to record the associated position for each.
336 * The first 4 bytes contain "SGA\0" followed by a 16-bit
337 * size of the allocation in bytes, followed by a 16-bit
338 * index indicating the next spot to be overwritten.
340 * There are no parameters
341 * All registers should be preserved
344 sgabioslog_setup_ebda:
350 popw %ds /* ds = 0x40 */
351 movw BDA_EBDA, %ax /* ax = old ebda segment from 0x0e */
352 subw $SGABIOS_EBDA_DELTA, %ax
353 movw %ax, %es /* es = new EBDA segment start */
354 cmpw $EBDA_MIN_SEG, %ax /* is there room for the allocation? */
355 jc sgabioslog_setup_ebda_tail /* if not, don't change anything */
356 cli /* paranoid in case irq uses EBDA */
357 movw %ax, BDA_EBDA /* save new EBDA segment start */
358 subw $SGABIOS_EBDA_KB, BDA_MEM_SIZE /* subtract extra allocation */
359 movw %ax, %ds /* ds = new EBDA segment start */
360 movw $SGABIOS_EBDA_BYTES, %si /* si = offset of first byte to move */
361 movzbw (%si), %cx /* cx = number of KB in EBDA */
362 addb $SGABIOS_EBDA_KB, (%si) /* update EBDA size in kb */
363 shlw $10, %cx /* cx = KB * 1024 = bytes in EBDA */
364 movw %cx, %cs:sgabios_ebda_logbuf_offset /* new ebda space */
365 xorw %di, %di /* di = new EBDA start */
368 movsb /* move ebda by SGABIOS_EBDA_BYTES */
369 movw %cs:sgabios_ebda_logbuf_offset, %bx /* bx = new buffer */
370 movl $SGABIOS_EBDA_SIGNATURE, (%bx) /* setup signature */
371 movw $SGABIOS_EBDA_BYTES, 4(%bx) /* bytes in new ebda buffer */
372 movw $0, 6(%bx) /* next log index, new ebda buffer */
373 sgabioslog_setup_ebda_tail:
381 * sgabioslog_save_char
383 * Like memconsole_log_char, except the original, untranslated
384 * character is expected to be given in the %al register.
386 * The original character and its corresponding cursor position
387 * are logged to the sgabios ebda memory allocation.
389 * %al = character to be logged
390 * All registers except flags should be preserved
393 sgabioslog_save_char:
398 popw %ds /* ds = 0x40 */
399 pushw BDA_EBDA /* push word at 0x0e */
400 popw %es /* es = EBDA_SEG */
401 movw %cs:sgabios_ebda_logbuf_offset, %di
402 orw %di, %di /* is offset zero? */
403 jz sgabioslog_save_tail /* if so, bail */
404 cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di)
405 jnz sgabioslog_save_tail /* bail if magic not found */
406 movw %es:6(%di), %bx /* bx = index of next char output */
407 movb %al, %es:SGABIOS_EBDA_LOG_START(%bx,%di) /* store character */
408 movzbw %bl, %ax /* %ax = next cursor buffer index */
409 shlw $1, %ax /* %ax = offset to cursor storage */
410 call get_current_cursor /* %dh = row, %dl = column */
411 addw $SGABIOS_EBDA_POS_START, %di /* cursor storage */
412 addw %ax, %di /* %di = next cursor storage offset */
413 movw %dx, %es:(%di) /* save position for logged char */
414 incw %bx /* point to next char to log */
415 cmpw $SGABIOS_EBDA_LOG_SIZE, %bx
416 jnz sgabioslog_save_index
417 xorw %bx, %bx /* wrap around to start */
418 sgabioslog_save_index:
419 movw %cs:sgabios_ebda_logbuf_offset, %di
420 movw %bx, %es:6(%di) /* save new index */
421 sgabioslog_save_tail:
428 * sgabioslog_get_char
430 * Return the character at current cursor position, last recorded
431 * to sgabios ebda allocation, if available.
433 * If the current cursor postition contains one of the last 256 characters
434 * written to the ebda buffer, return that character, else return 0.
436 * If sgabios_ebdda_logbuf_offset is zero, %al will be 0 and zf set
438 * All registers except flags and %al should be preserved
444 movb $0, 14(%bp) /* %al on stack = 0 */
448 popw %ds /* ds = 0x40 */
449 pushw BDA_EBDA /* push word at 0x0e */
450 popw %es /* es = EBDA_SEG */
451 movw %cs:sgabios_ebda_logbuf_offset, %di
453 jz sgabioslog_get_tail /* offset==0, no logging */
454 cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di)
455 jnz sgabioslog_get_tail /* bail if magic not found */
456 call get_current_cursor /* dh = row, dl = col */
457 std /* scan backwards in mem */
458 movw %es:6(%di), %bx /* bx = index of next char output */
459 decw %bx /* %bx = offset of last char in buf */
460 jnc sgabioslog_got_pos
461 addw $SGABIOS_EBDA_LOG_SIZE, %bx /* bx position wrap around */
463 movw %bx, %ax /* %ax = last cursor pos written */
464 shlw $1, %ax /* %ax = offset of last cursor pos */
465 addw $SGABIOS_EBDA_POS_START, %di /* %di = first cursor position */
466 addw %ax, %di /* %di = offset in ebda */
467 movw %dx, %ax /* %ax = cursor pos to compare */
468 movw %bx, %cx /* %cx = positions before wrap */
469 jcxz sgabioslog_cmp_wrap /* if zero, try from end next */
471 scasw /* search until position match */
472 addw $2, %di /* scasd always decrements di, undo */
473 cmpw %ax, %es:(%di) /* did it really match? */
474 jz sgabioslog_cursor_match /* if so, do something */
476 movw %cs:sgabios_ebda_logbuf_offset, %di
477 addw $SGABIOS_EBDA_POS_LAST, %di /* %di = last cursor storage */
478 movw $SGABIOS_EBDA_LOG_SIZE, %cx /* %cx = compare all positions */
480 scasw /* search until position match */
481 addw $2, %di /* scasd always decrements di, undo */
482 cmpw %ax, %es:(%di) /* did it really match? */
483 jnz sgabioslog_get_tail /* if not, bail */
484 sgabioslog_cursor_match:
485 /* %di contains the EBDA offset of the matching position */
486 /* convert this into a memconsole offset */
487 subw $512, %di /* take off the storage offset */
488 subw %cs:sgabios_ebda_logbuf_offset, %di /* and ebda offset */
489 shrw $1, %di /* %di = char position index */
490 addw %cs:sgabios_ebda_logbuf_offset, %di /* add back ebda offset */
491 addw $SGABIOS_EBDA_LOG_START, %di /* and add back log offset */
492 movb %es:(%di), %al /* get related saved character */
493 movb %al, 14(%bp) /* %al on stack = logged char */
503 * When an escape key is detected, the input routines will attempt to
504 * capture as many characters as arrive up until a timeout, or six,
507 * This table is intended to decide what the characters after the
508 * initial escape key translate to in terms of high and low bytes
509 * that go into the keyboard buffer the high byte is the scancode,
510 * the low byte is ascii, but for special keys this is usually 0xe0
513 * This table is formatted so that the first word is a scancode +
514 * ascii pair (as returned by int 16h, ah = 10h or 11h). Immediately
515 * following is a nul-terminated ascii string to match in order to
516 * use the corresponding scancode+ascii word.
518 * The search through this table is terminated by a match or finding
519 * a 0 scancode+ascii word.
521 * FIXME: all the low bytes are now zero, get rid of them?
525 .asciz "[[A" /* F1/screen */
528 .asciz "OP" /* F1/xterm/ansi */
531 .asciz "[11~" /* F1/vt400 */
534 .asciz "[[B" /* F2/screen */
537 .asciz "OQ" /* F2/xterm/ansi */
540 .asciz "[12~" /* F2/vt400 */
543 .asciz "[[C" /* F3/screen */
546 .asciz "OR" /* F3/xterm/ansi */
549 .asciz "[13~" /* F3/vt400 */
552 .asciz "[[D" /* F4/screen */
555 .asciz "OS" /* F4/xterm/ansi */
558 .asciz "[14~" /* F4/vt400 */
561 .asciz "[[E" /* F5/screen */
564 .asciz "[15~" /* F5/xterm */
567 .asciz "OT" /* F5/ansi */
570 .asciz "[17~" /* F6/screen/vt220/xterm/vt400 */
573 .asciz "OU" /* F6/ansi */
576 .asciz "[18~" /* F7/screen/vt220/xterm/vt400 */
579 .asciz "OV" /* F7/ansi */
582 .asciz "[19~" /* F8/screen/vt220/xterm/vt400 */
585 .asciz "OW" /* F8/ansi */
588 .asciz "[20~" /* F9/screen/vt220/xterm/vt400 */
591 .asciz "OX" /* F9/ansi */
594 .asciz "[21~" /* F10/screen/vt220/xterm/vt400 */
597 .asciz "OY" /* F10/ansi */
600 .asciz "[23~" /* F11/screen/xterm/vt400 */
603 .asciz "OZ" /* F11/ansi */
606 .asciz "[24~" /* F12/screen/xterm/vt400 */
608 .byte 0x52 /* Insert */
609 .asciz "[2~" /* Insert/screen/vt102/xterm */
611 .byte 0x53 /* Delete */
612 .asciz "[3~" /* Delete/screen/vt102/xterm */
614 .byte 0x4b /* Left */
615 .asciz "OD" /* Left/screen/vt102 */
617 .byte 0x4b /* Left */
618 .asciz "[D" /* Left/xterm */
620 .byte 0x47 /* Home */
621 .asciz "[1~" /* Home/screen/vt102 */
623 .byte 0x47 /* Home */
624 .asciz "[H" /* Home/xterm */
627 .asciz "[4~" /* End/screen/vt102 */
630 .asciz "[F" /* End/xterm */
633 .asciz "OA" /* Up/screen/vt102 app */
636 .asciz "[A" /* Up/xterm/vt102 ansi */
638 .byte 0x50 /* Down */
639 .asciz "OB" /* Down/screen/vt102 app */
641 .byte 0x50 /* Down */
642 .asciz "[B" /* Down/xterm/vt102 ansi */
644 .byte 0x49 /* PageUp */
645 .asciz "[5~" /* PageUp/screen/vt102/xterm */
647 .byte 0x51 /* PageDown */
648 .asciz "[6~" /* PageDown/screen/vt102/xterm */
650 .byte 0x4d /* Right */
651 .asciz "OC" /* Right/screen/vt102 app */
653 .byte 0x4d /* Right */
654 .asciz "[C" /* Right/xterm/vt102 ansi */
656 .byte 0 /* end of table marker */
660 * Initialize serial port to 115200,8n1
661 * Serial interrupts disabled
663 * All registers except flags preserved
670 movw %cs:serial_port_base_address, %dx
671 addw $IER_OFFSET, %dx
673 outb %al, %dx /* disable all serial interrupts */
674 addw $(LCR_OFFSET - IER_OFFSET), %dx /* LCR */
675 movb $(LCR_VALUE|LCR_DLAB), %al
676 outb %al, %dx /* enable divisor access */
677 movw %cs:serial_port_base_address, %dx
678 movw $(PORT_DIVISOR/PORT_SPEED), %bx
679 movb %bl, %al /* al = lsb of divisor */
680 outb %al, %dx /* set divisor latch lsb */
681 movb %bh, %al /* al = msb of divisor */
683 outb %al, %dx /* set divisor latch msb */
684 movw %cs:serial_port_base_address, %dx
685 addw $LCR_OFFSET, %dx
687 outb %al, %dx /* disable divisor access */
688 addw $(MCR_OFFSET - LCR_OFFSET), %dx /* MCR */
689 movb $MCR_DTRRTS, %al
690 outb %al, %dx /* enable DTR + RTS */
691 movw %cs:serial_port_base_address, %dx
692 addw $FCR_OFFSET, %dx
693 movb $FCR_FIFO_ENABLE, %al
694 outb %al, %dx /* enable FIFOs */
703 * return serial line status register in %al
704 * return offset to serial port line status register io port in %dx
705 * all other registers except flags unchanged
707 * if status == 0xff return ZF=1, else return ZF=0
711 movw %cs:serial_port_base_address, %dx
712 addw $LSR_OFFSET, %dx
720 * get serial byte in %al, scancode in %ah [FIXME: EFI console input]
722 * all registers except %ax preserved
730 call get_serial_lsr /* get serial lsr in %al */
731 jz get_byte_tail /* no port present... */
732 testb $1, %al /* bit 0 of LSR = 1 = data available */
733 jz get_byte_tail /* no input waiting */
734 /* new character found on serial port */
735 /* convert it to a scancode */
736 movw %cs:serial_port_base_address, %dx
737 inb %dx, %al /* al = serial input char */
738 testb $0x80, %al /* non-ascii char received? */
739 jnz next_serial_char /* throw char away */
740 movb %al, %dl /* dl = character read */
743 popw %ds /* ds = cs */
744 movw $ascii2scan, %bx /* table to translate ascii->scan */
745 xlatb /* translate char to scancode */
747 /* shift status is ignored at this point, may be used later */
748 andb $0x7f, %al /* strip shift status from table */
749 movb %al, %ah /* scancode goes in high byte */
750 movb %dl, %al /* "translated" ascii in lower byte */
751 cmpb $0x7f, %al /* Did the user transmit ascii DEL? */
752 jnz get_byte_not_del /* if not, don't do anything to al */
753 movb $0x08, %al /* else delete becomes backspace */
755 testw %ax, %ax /* clear zero flag */
764 * get serial byte in %al, scancode in %ah [FIXME: EFI console input]
765 * retry up to 65536 times for an expected input byte
767 * all registers except %ax preserved
777 loopz poll_byte_retry /* repeat while zf set or cx != 0 */
784 * after an escape character, poll for terminal keys that generate
785 * an escape code plus multiple bytes (up to four).
787 * if no byte is waiting, all registers preserved except flags
788 * if more bytes are waiting, all registers preserved except %ax and flags
792 pushw %bp /* bp points to temp buffer on stack */
793 pushw %bx /* bx points to multibyteinput table */
794 pushw %cx /* cx will count chars */
795 pushw %ax /* ax will receive chars */
796 pushl $0 /* make space on stack for 4 chars */
797 xorw %cx, %cx /* cx = 0 */
798 movw %sp, %bp /* point bp at temp data */
799 call poll_byte /* is a character waiting? */
800 jz get_multibyte_tail /* if not, bail */
802 movb %al, (%bp) /* store char received */
803 incb %cl /* mark one char received */
804 incw %bp /* point to next char */
805 cmpb $4, %cl /* got enough chars? */
806 jz got_multibyte /* no strings longer than 4 chars */
807 call poll_byte /* is another char waiting? */
808 jnz get_multibyte_store /* store a new one if it's there */
810 movw $multibyteinput, %bx /* point to first scancode */
811 got_multibyte_findkey:
812 movw %sp, %bp /* bp = start of buffer */
813 movb %cs:(%bx), %ah /* ah = scancode */
814 incw %bx /* bx = start of test string */
815 orb %ah, %ah /* is it zero? */
816 jz get_multibyte_tail /* if so, bail, key not found */
817 got_multibyte_nextchar:
818 movb %cs:(%bx), %ch /* ch = test char to compare */
819 incw %bx /* point to next char */
820 orb %ch, %ch /* is char to compare NUL? */
821 jz got_multibyte_key /* matched to end of a string! */
822 cmpb %ch, (%bp) /* input tmp buf equal to test char? */
823 jnz got_multibyte_try_next_key
824 /* note: expected that test string will be nul before input string */
825 /* no attempt is made to ensure no more than 4 bytes stack read */
826 incw %bp /* point to next input */
827 jmp got_multibyte_nextchar
828 got_multibyte_try_next_key: /* align to next scancode/ascii pair */
829 movb %cs:(%bx), %ch /* ch = test char to compare */
830 incw %bx /* point to next char */
831 orb %ch, %ch /* is char to compare NUL? */
832 jnz got_multibyte_try_next_key
833 jmp got_multibyte_findkey
835 xorb %al, %al /* ascii value = 0 for special keys */
837 movw %ax, 4(%bp) /* overwrite old %ax value with key */
839 addw $4, %sp /* pop temp space */
849 * send character in %al to serial port [FIXME: EFI console out]
851 * all registers preserved except flags
859 testb $0x80, %al /* don't send non-ascii chars */
860 jnz send_tail /* these should be translated earlier */
861 movb %al, %ah /* save char to output in %ah */
862 movw $0xFFF0, %cx /* only retry 65520 times */
864 call get_serial_lsr /* get serial lsr in %al */
865 testb $TRANSMIT_READY_BIT, %al
866 loopz serial_ready_test /* if !tx ready, loop while cx!=0 */
868 movw %cs:serial_port_base_address, %dx
879 * translate vga character in %al to ascii
882 * al = translated character
884 * all registers except %al preserved
892 popw %ds /* ds = cs */
894 jz translate_char_ctrl
896 movw $high2ascii, %bx
900 jnc translate_char_tail
901 movw $ctrl2ascii, %bx
911 * translate vga character in %al to ascii
912 * unless %al == 7, 8, 10, or 13 (bell, bs, lf, cr)
915 * al = translated character
917 * all registers except %al preserved
922 cmpb $0x07, %al /* bell */
923 jz translate_char_tty_tail
924 cmpb $0x08, %al /* backspace */
925 jz translate_char_tty_tail
926 cmpb $0x0a, %al /* LF */
927 jz translate_char_tty_tail
928 cmpb $0x0d, %al /* CR */
929 jz translate_char_tty_tail
931 translate_char_tty_tail:
937 * send character 0 - 255 in %al out through serial port
938 * increment cursor position without control processing
940 * send_byte is used for data that isn't tracked
942 * send_char is used for text that should be tracked
943 * send_char outputs all characters as non-control chars
946 * al = translated character
948 * all registers except %al preserved
953 call sgabioslog_save_char /* save original char+pos */
955 jmp send_char_tty_out
956 /* after ctrl translation, same as send_char_tty */
961 * send character 0 - 255 in %al out through serial port
962 * increment cursor position *with* control processing
963 * for bell, linefeed, cr, and backspace (others all printable)
965 * send_byte is used for data that isn't tracked
967 * send_char_tty is used for text that should be tracked
970 * al = translated character
972 * all registers except %al preserved
977 /* send character 0 - 255 in %al out through serial port */
978 /* increment cursor position with CR/LF/Backspace processing */
980 call sgabioslog_save_char /* save original char+pos */
981 call translate_char_tty
984 call update_serial_cursor
985 call get_current_cursor /* vga cursor in %dx */
986 cmpb $0x0d, %al /* CR */
987 jnz send_char_tty_nul /* if not CR, check for NUL */
988 orb %dl, %dl /* already at col 0? */
989 jz send_char_tty_tail /* no need to re-send CR */
991 orb %al, %al /* %al == 0 ? (nul) */
992 /* more than likely, we have NUL at this point because the caller */
993 /* tried to read a char using int $0x10, %ah=8, and is trying */
994 /* to re-output it with different attributes - for now send nothing */
995 jz send_char_tty_tail
997 call memconsole_log_char /* log character sent */
999 cmpb $0x07, %al /* bell */
1000 jz send_char_tty_tail /* no cursor update for bell */
1001 cmpb $0x08, %al /* backspace */
1002 jz send_char_tty_backspace
1003 cmpb $0x0a, %al /* LF */
1005 cmpb $0x0d, %al /* CR */
1008 jmp send_char_tty_tail
1009 send_char_tty_backspace:
1011 jz send_char_tty_tail
1013 jmp send_char_tty_tail
1016 jmp send_char_tty_tail
1020 cmpb %cs:term_cols, %dl
1021 jc send_char_tty_check_rows
1022 movb %cs:term_cols, %dl
1023 decb %dl /* dl = cols - 1 */
1024 send_char_tty_check_rows:
1025 cmpb %cs:term_rows, %dh
1026 jc send_char_tty_save_cursor
1027 movb %cs:term_rows, %dh
1028 decb %dh /* dh = rows - 1 */
1029 send_char_tty_save_cursor:
1030 call set_current_cursor
1034 /* save current position as the serial terminal position */
1035 /* since a character was just output at that position */
1036 movw %dx, BDA_SERIAL_POS
1044 * send nul terminated string pointed to by %ds:%si
1045 * to serial port without text tracking
1047 * indended to be used for multi-byte send_byte
1049 * all registers preserved except flags
1070 * send cx chars in string pointed to by %ds:%si
1071 * to serial port with tty tracking
1073 * indended to be used for multi-byte send_char_tty
1075 * all registers preserved except flags
1085 loop send_string_loop
1093 * send cx chars in string pointed to by %ds:%si
1094 * with interleaved attribute data
1096 * indended to be used for multi-byte send_char_tty
1097 * with interleaved vga attribute updates
1099 * all registers preserved except flags
1107 send_attr_string_loop:
1112 call send_attribute /* send attribute in %bl */
1113 loop send_attr_string_loop
1122 * send ascii version of number in %al to serial port
1124 * intended for ansi cursor positions and attributes,
1125 * so cursor position is not tracked/updated
1127 * all registers preserved except flags
1133 aam /* ah = al/10, al = al mod 10 */
1134 movw %ax, %bx /* bh = al/10, bl = al mod 10 */
1136 aam /* ah = bh/10, al = bh mod 10 */
1137 movb %al, %bh /* bh = 10s digit, bl = 1s digit */
1138 movb %ah, %al /* ah = al = 100s digit */
1139 testb %al, %al /* is there a 100s digit? */
1140 jz send_tens /* move to tens if not */
1141 orb $0x30, %al /* al = ascii value of digit */
1144 orb %bh, %ah /* bh = 10s, ah = 100s digits */
1145 jz send_ones /* non-zero = must send tens */
1146 movb %bh, %al /* al = bh = 10s digit */
1147 orb $0x30, %al /* al = ascii value of digit */
1150 movb %bl, %al /* al = bl = 1s digit */
1151 orb $0x30, %al /* al = ascii value of digit */
1160 * send CRLF to serial port
1162 * FIXME: used at vga init and for scrolling terminal
1163 * so position is not tracked. Callers of this routine
1164 * predate the code that does smart tty/cursor output.
1166 * Callers should probably be changed to use those
1167 * routines or send_crlf changed to use them and
1168 * terminal scrolling fixed to use linefeed only.
1170 * all registers preserved except flags
1184 * send ESCAPE [ to serial port
1186 * output is not tracked since these are control sequences
1188 * all registers preserved except flags
1191 send_ansi_csi: /* transmit ESC [ */
1193 movb $0x1b, %al /* escape */
1195 movb $0x5b, %al /* [ */
1200 * send_ansi_csi_2num
1202 * send ESC [ %dh ; %dl to serial port
1204 * since both position and attribute updates generally have
1205 * two parameters, this function converts values in dx to
1206 * two ascii numbers. It's expected that the caller will
1207 * output the final trailing H or m or whatever is required.
1209 * output is not tracked since these are control sequences
1211 * all registers preserved except flags
1215 /* send ESC [ %dh ; %dl */
1217 call send_ansi_csi /* esc [ */
1220 movb $0x3b, %al /* semicolon */
1228 * send_ansi_cursor_pos
1230 * send ESC [ %dh+1 ; %dl+1 to serial port to position
1233 * since both position and attribute updates generally have
1234 * two parameters, this function converts values in dx to
1235 * two ascii numbers, after adding 1 to both dh and dl.
1237 * output is not tracked since this is a control sequence
1239 * all registers preserved except flags
1242 send_ansi_cursor_pos:
1245 addw $0x0101, %dx /* dh += 1, dl += 1 */
1246 call send_ansi_csi_2num /* send esc [ %dh+1;%dl+1 */
1247 movb $0x48, %al /* H */
1256 * send ansi attribute change ESC [ 4x ; 3y ; (1|22)m
1257 * if the attribute has changed since last sent (stored in bda)
1259 * output is not tracked since this is a control sequence
1261 * all registers preserved except flags
1265 andb $0x7f, %bl /* ansi has no bright bg */
1272 popw %es /* es = 0x40 */
1274 popw %ds /* ds = cs */
1275 cmpb %es:BDA_COLOR_VAL, %bl
1276 jz send_attribute_tail
1277 cmpb $0x07, %bl /* is it white on black? */
1278 jnz send_attribute_color
1279 /* for white on black, send esc [ m */
1281 jmp send_attribute_m /* send the m, return */
1282 send_attribute_color:
1283 movb %bl, %ah /* ah = attribute */
1284 movw $colortable, %bx
1286 andb $7, %al /* al = fg attr */
1287 xlatb /* al = fg ansi num */
1288 movb %al, %dl /* dl = fg ansi num */
1290 shrb $4, %al /* al = bg attr */
1291 xlatb /* al = bg ansi num */
1292 movb %al, %dh /* dh = bg ansi num */
1293 addw $0x281e, %dx /* 3x=setfg, 4x=setbg */
1294 call send_ansi_csi_2num
1295 movb $0x3b, %al /* semicolon */
1297 shlb $4, %ah /* bright text? */
1298 sets %al /* if bit 7, al = 1 */
1299 js send_attribute_intensity
1300 movb $22, %al /* 22 = normal intensity */
1301 send_attribute_intensity:
1302 call send_number /* either 22 or 1 */
1304 movb $0x6d, %al /* m */
1306 send_attribute_tail:
1309 /* mark attribute in %bl the current one */
1310 movb %bl, %es:BDA_COLOR_VAL
1319 * common code for both interrupt-driven and non-interrupt
1320 * driven serial input. Called only when LSR bit 1 is set.
1322 * No parameters, no return values
1324 * Preserves all registers
1329 /* be paranoid about int 9h happening during update */
1333 /* next char input buffer is at 0x40:0x1c */
1335 popw %ds /* es = 0x40 */
1336 call get_byte /* next scancode/byte in %ax */
1337 cmpb $0x1b, %al /* look for escape */
1338 jnz serial_gotkey /* not escape, don't look for more bytes */
1339 call get_multibyte /* look for any chars after escape */
1341 movw KBD_TAIL, %bx /* bx = keyboard tail pointer */
1342 movw %ax, (%bx) /* store key in buffer */
1343 addw $2, %bx /* point to next location */
1344 cmpw $KBD_BUF_END, %bx /* did the buffer wrap? */
1346 movw $KBD_BUF_START, %bx
1348 movw %bx, KBD_TAIL /* update tail pointer to show key */
1357 * entry point for irq 3 / int 0x0b / exception 11
1359 * Called when COM2 or COM4 have characters pending
1361 * The segment not present exception should never happen
1362 * in real mode 16-bit code like this, but just to be safe,
1363 * if this interrupt is invoked and no characters are
1364 * pending on the port found in serial_port_base_address,
1365 * this routine will chain to the original handler.
1367 * If characters are found pending, they will be processed
1368 * and control returned via iret.
1375 /* placeholder, this shouldn't ever happen */
1376 /* no interrupts are configured outside COM1 */
1377 call get_serial_lsr /* get serial lsr in %al */
1378 jz chain_irq3 /* no port present... */
1379 testb $1, %al /* bit 0 of LSR = 1 = data available */
1380 jz chain_irq3 /* no input waiting */
1381 call serial_get_input /* get input and stuff kbd buffer */
1383 outb %al, $0x20 /* send non-specific EOI */
1396 * entry point for irq 4 / int 0x0c / exception 12
1398 * Called when COM1 or COM3 have characters pending
1400 * The stack fault exception may occur if code attempts to
1401 * read from sp:0xffff, so if this interrupt is invoked and
1402 * no characters are pending on the port found in
1403 * serial_port_base_address, this routine will chain to the
1406 * If characters are found pending, they will be processed
1407 * and control returned via iret.
1414 call get_serial_lsr /* get serial lsr in %al */
1415 jz chain_irq4 /* no port present... */
1416 testb $1, %al /* bit 0 of LSR = 1 = data available */
1417 jz chain_irq4 /* no input waiting */
1418 call serial_get_input /* get input and stuff kbd buffer */
1420 outb %al, $0x20 /* send non-specific EOI */
1433 * entry point for int 14h
1439 addw $16, %bp /* bp points to return address */
1440 orb %ah, %ah /* fn 0x00, initialize port */
1442 cmpb $0x04, %ah /* fn 0x04, extended intialize */
1445 /* check for init port = current port */
1448 popw %ds /* ds = 0x40 */
1449 movw %dx, %bx /* bx = port number */
1450 shlw $1, %bx /* bx = port number * 2 */
1451 andw $7, %bx /* bx = bda offset of serial io addr */
1452 movw (%bx), %cx /* cx = io address of port to init */
1453 popw %ds /* restore original ds */
1454 cmpw %cx, %cs:serial_port_base_address
1455 jnz chain_isr14h /* if different, don't get in the way */
1456 /* init port == current port */
1458 /* LILO 22.6 HACK STARTS HERE */
1459 movw (%bp), %bx /* return address for int 14h call */
1460 movw 2(%bp), %ds /* return segment for int 14h call */
1461 cmpl $0x4f4c494c, 0x06 /* does segment have lilo signature? */
1462 jnz int14h_init_tail /* not lilo, bail on hack */
1463 cmpw $0x0616, 0x0a /* does version match lilo 22.6? */
1464 jnz int14h_init_tail /* unknown lilo release, bail on hack */
1465 movb $0, 0x12 /* set lilo com port = 0 */
1466 movl $0x90c3585a, (%bx) /* return code= pop dx;pop ax;ret;nop */
1467 /* now lilo 22.6's own serial out is permanently disabled */
1468 /* this prevents double-character output from int10h + serial */
1469 /* this also prevents lilo from stealing serial input chars */
1470 /* END LILO 22.6 HACK */
1474 pushw %dx /* get_serial_lsr trashes %dx */
1475 call get_serial_lsr /* return serial status in %al */
1476 xorb %ah, %ah /* return serial status in %ax */
1477 popw %dx /* restore %dx */
1486 * entry point for int 16h
1488 * keyboard characters are usually retrieved by calling
1489 * int 16h, generally placed in the keyboard buffer by
1490 * irq 1 (int 9h). Poll serial port for new data before
1491 * chaining to int 16h to fake irq 1 behavior
1493 * all registers preserved except flags (later iret will restore)
1494 * bda updated with a new keypress if available
1496 * FIXME: handle multi-byte keypresses like cursor up/down
1497 * to send proper scancodes for navigating lilo menus
1503 /* each time int 16h is invoked, fake an int 9h */
1504 /* except read the serial input buffer */
1505 /* then chain to the original int 16h for processing */
1507 jz chain_isr16h /* no port present... */
1508 testb $1, %al /* bit 0 of LSR = 1 = data available */
1509 jz chain_isr16h /* no input waiting */
1510 call serial_get_input /* get input and stuff kbd buffer */
1511 /* for now, leave remaining chars pending in serial fifo */
1512 /* int 16h callers only get one char at a time anyway */
1519 * update serial_cursor
1521 * figure out where the cursor was, and where it's going
1522 * use the minimal amount of serial output to get it there
1523 * input: vga cursor and serial cursor positions stored in BDA
1525 * all registers preserved except flags
1526 * bda updated with new position for serial console cursor
1528 update_serial_cursor:
1534 popw %ds /* ds = 0x40 */
1535 call get_current_cursor /* dh = row, dl = col */
1536 movw BDA_SERIAL_POS, %bx /* bh = row, bl = col */
1537 subb %dl, %bl /* -col update */
1538 negb %bl /* col update */
1539 subb %dh, %bh /* -row update */
1540 negb %bh /* row update */
1541 /* handle a few special movement cases */
1542 /* cr, lf, bs, bs+bs, space, else send full ansi position */
1543 orb %dl, %dl /* column zero? */
1544 jnz update_serial_cursor_lf
1545 movb $0x0d, %al /* CR */
1547 xorb %bl, %bl /* mark no diff in col */
1548 update_serial_cursor_lf:
1549 cmpb $1, %bh /* +1 row? */
1550 jnz update_serial_cursor_bs
1551 movb $0x0a, %al /* LF */
1553 xorb %bh, %bh /* mark no diff in row */
1554 update_serial_cursor_bs:
1555 cmpb $-1, %bl /* one char back */
1556 jz update_serial_cursor_one_bs
1557 cmpb $-2, %bl /* two chars back */
1558 jnz update_serial_cursor_space /* check for space */
1559 movb $0x08, %al /* BS */
1561 update_serial_cursor_one_bs:
1562 movb $0x08, %al /* BS */
1564 xorb %bl, %bl /* mark no diff in col */
1565 update_serial_cursor_space:
1566 cmpb $1, %bl /* one char forward */
1567 jnz update_serial_cursor_up
1568 movb $0x20, %al /* space */
1570 xorb %bl, %bl /* mark no diff in col */
1571 update_serial_cursor_up:
1572 cmpb $-1, %bh /* -1 row? */
1573 jnz update_serial_cursor_full /* do full ansi pos update */
1574 call send_ansi_csi /* send ESC [ A (cursor up) */
1575 movb $0x41, %al /* A */
1577 xorb %bh, %bh /* mark no diff in row */
1578 update_serial_cursor_full:
1579 orw %bx, %bx /* diff = 0? */
1580 jz update_serial_cursor_done
1581 call send_ansi_cursor_pos /* set cursor pos from dh,dl */
1582 update_serial_cursor_done:
1583 movw %dx, BDA_SERIAL_POS
1593 * handle int 10h, function 0eh
1595 * ah = 0x0e write teletype character
1596 * al = character ascii code
1597 * bh = display page number
1599 * all registers except %al preserved
1600 * caller will restore all registers
1605 movb $0x07, %bl /* black bg, white fg */
1614 * handle int 10h, function 09h
1616 * ah = 0x09 write attribute/character at current cursor position
1617 * al = character ascii code
1618 * bh = display page number
1619 * bl = character attribute
1620 * cx = repetition count
1622 * does not update cursor position
1623 * all registers except %cx and %al preserved
1624 * caller will restore all registers
1628 call send_attribute /* send attribute in %bl */
1629 jmp write_char_common
1634 * handle int 10h, function 0ah
1636 * ah = 0x0a write character at current cursor position
1637 * al = character ascii code
1638 * bh = display page number
1639 * cx = repetition count
1641 * does not update cursor position
1642 * all registers except %cx and %al preserved
1643 * caller will restore all registers
1648 movb $0x07, %bl /* black bg, white fg */
1652 call get_current_cursor
1654 /* make cx=0 and cx=1 only output one char */
1660 /* put cursor back where it was on entry */
1661 call set_current_cursor
1667 * handle int 10h, function 13h
1669 * ah = 0x13 write character at current cursor position
1670 * al = 0, data = char, ..., no cursor update
1671 * al = 1, data = char, ..., cursor at end of string
1672 * al = 2, data = char+attr, ..., no cursor update
1673 * al = 3, data = char+attr, ..., cursor at end of string
1674 * bh = display page number
1675 * bl = character attribute for all chars (if al = 0 or 1)
1676 * cx = characters in string (attributes don't count)
1677 * dh = cursor row start
1678 * dl = cursor column start
1679 * es:bp = pointer to source text string in memory
1681 * all registers preserved except flags
1682 * caller will restore all registers
1685 call set_cursor_position
1689 popw %ds /* ds = es */
1690 movw %bp, %si /* si = bp */
1692 jnz write_attr_string
1693 call send_attribute /* send attribute in %bl */
1695 jz write_string_empty
1696 call send_string /* plaintext out */
1698 jmp write_string_update_cursor
1700 call send_attr_string /* text+attrib out */
1701 write_string_update_cursor:
1702 testb $1, %al /* cursor update? */
1703 jnz write_string_tail /* yes? already happened */
1704 /* restore entry cursor position if no update */
1705 call set_cursor_position
1712 * set_cursor_position
1714 * handle int 10h, function 02h
1716 * ah = 0x02 set cursor position
1717 * bh = display page number
1719 * dl = cursor column
1721 * update bda cursor position with value in %dx
1722 * serial console cursor only updated on text output
1723 * this routine also called by set_current_cursor
1724 * which won't bother setting ah = 2
1726 * all registers preserved except flags
1729 set_cursor_position:
1733 popw %ds /* ds = 0x40 */
1734 movzbw %bh, %ax /* ax = page number */
1735 andb $0x07, %al /* prevent invalid page number */
1736 shlb $1, %al /* calculate word offset */
1737 addb $BDA_CURSOR_BUF, %al /* ax = cursor save offset */
1738 movw %ax, %bx /* bx = cursor save offset */
1739 movw %dx, (%bx) /* save new cursor value */
1745 * set_current_cursor
1747 * get current display page number and call set_cursor_positon
1748 * to store the row/column value in dx to the bda
1750 * all registers preserved except flags
1757 popw %ds /* ds = 0x40 */
1758 movb BDA_ACTIVE_PAGE, %bh
1759 call set_cursor_position
1767 * read cursor position for page %bh from bda into %dx
1771 * dl = cursor column
1772 * ch = cursor start scanline
1773 * cl = cursor end scanline
1775 * all registers except %dx, %cx preserved
1781 popw %ds /* ds = 0x40 */
1782 movzbw %bh, %bx /* dx = current page */
1785 addb $BDA_CURSOR_BUF, %bl
1786 movw (%bx), %dx /* get cursor pos */
1787 movw BDA_CURSOR_SCAN, %cx
1793 * get_current_cursor
1795 * read cursor position for current page from bda into %dx
1799 * dl = cursor column
1801 * all registers except %dx preserved
1809 popw %ds /* ds = 0x40 */
1810 movb BDA_ACTIVE_PAGE, %bh
1811 call get_cursor_common
1818 * get_cursor_position
1820 * handle int 10h, function 03h
1822 * ah = 0x02 get cursor position
1823 * bh = display page number
1827 * ch = cursor start scanline
1828 * ch = cursor end scanline
1830 * dl = cursor column
1832 * all registers except %ax, %cx, %dx preserved
1835 get_cursor_position:
1836 call bail_if_vga_attached /* does not return if vga attached */
1837 popw %ax /* not chaining, pop fake return address */
1838 popw %dx /* not chaining, pop saved cursor position */
1839 popaw /* not chaining to old int 10h, pop saved state */
1840 call get_cursor_common
1845 * return_current_video_state
1847 * handle int 10h, function 0fh
1849 * ah = 0x0f return current video state
1852 * ah = number of columns on screen (from 40:4a)
1853 * al = current video mode setting (from 40:49)
1854 * bh = active display page number (from 40:62)
1856 * all registers except %ax and %bh preserved
1859 read_current_video_state:
1860 call bail_if_vga_attached /* does not return if vga attached */
1861 popw %ax /* not chaining, pop fake return address */
1862 popw %dx /* not chaining, pop saved cursor position */
1863 popaw /* not chaining to old int 10h, pop saved state */
1866 popw %ds /* ds = 0x40 */
1868 movb BDA_MODE_NUM, %al
1869 movb BDA_ACTIVE_PAGE, %bh
1876 * handle int 10h, function 08h
1878 * ah = 0x08 read character/attribute from screen
1881 * ah = attribute at current cursor position
1882 * al = character read from current cursor position
1884 * all registers preserved except %ax and flags
1888 call bail_if_vga_attached /* does not return if vga attached */
1889 popw %ax /* not chaining, pop fake return address */
1890 popw %dx /* not chaining, pop saved cursor position */
1891 popaw /* not chaining to old int 10h, pop saved state */
1894 popw %ds /* ds = 0x40 */
1895 movb BDA_COLOR_VAL, %ah /* return last color value */
1896 call sgabioslog_get_char
1903 * handle int 10h, function 00h
1905 * ah = 0x00 set video mode
1908 * unless bit 7 of al = 1, setting mode clears screen
1910 * all registers preserved except %bh, %dx, flags
1914 testb $0x80, %al /* preserve screen flag? */
1915 jnz set_video_mode_tail
1917 movb $0x32, %al /* 2 */
1919 movb $0x4a, %al /* J */
1921 set_video_mode_tail:
1922 movb $0x07, %bl /* white on black text */
1923 call send_attribute /* send attribute in %bl */
1924 /* set cursor position to 0,0 */
1925 xorb %bh, %bh /* page 0 */
1927 jmp set_cursor_position
1932 * handle int 10h, function 06h
1934 * ah = 0x06 scroll current page up
1935 * al = scroll distance in character rows (0 blanks entire area)
1936 * bh = attribute to used on blanked lines
1937 * ch = top row (upper left corner) of window
1938 * cl = left-most column (upper left corner) of window
1939 * dh = bottom row (lower right corner) of window
1940 * dl = right-most column (lower right corner) of window
1942 * all registers preserved except flags
1948 call get_current_cursor /* save current cursor */
1949 movw %dx, %si /* si = vga cursor pos */
1951 cmpb $0, %al /* al = 0 = clear window */
1952 jz scroll_common_clear
1954 call send_ansi_csi /* CSI [ %al S */
1956 movb $0x53, %al /* S */
1963 * scroll_common_clear
1965 * common tail for up/down scrolls to clear window specified
1968 * stack should contain saved copy of si
1969 * si = original vga cursor position on service entry
1971 * bh = attribute to used on blanked lines
1972 * ch = top row (upper left corner) of window
1973 * cl = left-most column (upper left corner) of window
1974 * dh = bottom row (lower right corner) of window
1975 * dl = right-most column (lower right corner) of window
1977 scroll_common_clear:
1979 xchgb %bl, %bh /* bl = attribute, bh = old bl */
1980 call send_attribute /* send attribute in %bl */
1981 xchgb %bl, %bh /* restore bx */
1984 popw %ds /* ds = 0x40 */
1985 /* check to see if region is full screen, and attribute default */
1986 orw %cx, %cx /* is top left 0,0? */
1987 jnz scroll_common_window /* no, handle window */
1988 cmpb $0x07, %bh /* is attribute white on black? */
1989 jnz scroll_common_window /* no, must write spaces */
1990 #ifdef LILO_CLEAR_WORKAROUND_NOT_REQUIRED
1991 cmpb %cs:term_cols, %dl /* is right less than cols ? */
1992 jc scroll_common_window /* if so, handle window */
1993 cmpb %cs:term_rows, %dh /* is bottom less than rows ? */
1994 jc scroll_common_window /* if so, handle window */
1996 /* safe to send standard clear screen sequence */
1997 call send_ansi_csi /* send ESC [ */
1998 movb $0x32, %al /* 2 */
2000 movb $0x4a, %al /* J */
2002 jmp scroll_common_tail
2003 scroll_common_window:
2005 movw %cx, %dx /* dx = upper right */
2006 call set_current_cursor
2009 /* setup cx with count of chars to clear per row */
2012 addb %dl, %cl /* cl = dl - cl */
2013 incb %cl /* start = end col = clear 1 col */
2014 cmpb %cs:term_cols, %cl /* is count < cols? */
2015 jc scroll_common_row_ok /* if so then skip limit */
2016 movb %cs:term_cols, %cl /* limit count to cols */
2017 scroll_common_row_ok:
2018 jz scroll_common_row_done /* count == 0 ? */
2019 movb $0x20, %al /* space */
2020 scroll_common_space_loop:
2022 loop scroll_common_space_loop /* send cx spaces */
2023 scroll_common_row_done:
2025 incb %ch /* top left now next row */
2027 jbe scroll_common_window /* do next row */
2032 movw %si, %dx /* dx = saved vga cursor pos */
2033 call set_current_cursor /* restore saved cursor */
2041 * handle int 10h, function 07h
2043 * ah = 0x07 scroll current page down
2044 * al = scroll distance in character rows (0 blanks entire area)
2045 * bh = attribute to used on blanked lines
2046 * ch = top row (upper left corner) of window
2047 * cl = left-most column (upper left corner) of window
2048 * dh = bottom row (lower right corner) of window
2049 * dl = right-most column (lower right corner) of window
2051 * FIXME: this routine doesn't handle windowing, it currently
2052 * only handles one line screen scrolls and erasing entire screen
2054 * all registers preserved except flags
2060 call get_current_cursor /* save current cursor */
2061 movw %dx, %si /* si = vga cursor pos */
2063 cmpb $0, %al /* al = 0 = clear window */
2064 jz scroll_common_clear
2066 call send_ansi_csi /* CSI [ %al T */
2068 movb $0x54, %al /* T */
2075 * bail_if_vga_attached
2077 * Check for vga installed, if not, return to caller.
2078 * If so, pop return address, return to chain_isr_10h
2080 * expected that routine calling this one has chain_isr_10h
2081 * as the next item on the stack
2083 * all registers except flags and sp preserved
2086 bail_if_vga_attached:
2087 cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */
2088 jnz bail_tail /* if not, don't modify stack */
2089 addw $2, %sp /* else drop first return address */
2091 ret /* return to caller or chain_isr_10h */
2096 * entry point for int 10h
2098 * save all registers, force return to chain to previous int10h isr
2099 * decide which function in ah needs to be dispatched
2101 * ah = 0x00 set mode
2102 * ah = 0x01 set cursor type
2103 * ah = 0x02 set cursor position
2104 * ah = 0x03 read cursor position
2105 * ah = 0x04 read light pen position
2106 * ah = 0x05 set active display page
2107 * ah = 0x06 scroll active page up
2108 * ah = 0x07 scroll active page down
2109 * ah = 0x08 read attribute/character at cursor
2110 * ah = 0x09 write attribute/character at cursor
2111 * ah = 0x0a write character at cursor position
2112 * ah = 0x0b set color palette
2113 * ah = 0x0c write pixel
2114 * ah = 0x0d read pixel
2115 * ah = 0x0e write teletype
2116 * ah = 0x0f read current video state
2117 * ah = 0x10 set individual palette registers
2118 * ah = 0x11 character generation (font control/info)
2119 * ah = 0x12 alternate select (video control/info)
2120 * ah = 0x13 write string
2121 * ah = 0x1a read/write display combination code
2122 * ah = 0x1b return functionality/state information
2123 * ah = 0x1c save/restore video state
2124 * ah = 0x4f vesa bios calls
2125 * all registers preserved except flags (later iret will restore)
2130 call get_current_cursor
2131 pushw %dx /* save current cursor */
2132 pushw %bp /* need bp for indexing off stack */
2133 movw %sp, %bp /* bp = sp */
2134 movw 14(%bp), %dx /* restore dx from earlier pushaw */
2135 popw %bp /* restore old bp */
2136 pushw $chain_isr10h /* force return to chain_isr10h */
2143 jmp set_cursor_position
2147 jmp get_cursor_position
2155 jmp scroll_page_down
2175 jmp read_current_video_state
2181 popw %ax /* pop chain_isr10h return address */
2183 popw %dx /* pop saved cursor */
2184 cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */
2185 jnz chain_post_cursor /* if not, don't restore the cursor */
2186 call set_current_cursor /* restore cursor if vga attached */
2194 * handle PnP initialization of option rom
2196 * es:di = pointer to PnP structure
2197 * ax = indication as to which vectors should be hooked
2198 * by specifying th type of boot device this has
2200 * bit 7..3= reserved(0)
2201 * bit 2 = 1 = connect as IPL (int 13h)
2202 * bit 1 = 1 = connect as primary video (int 10h)
2203 * bit 0 = 1 = connect as primary input (int 9h)
2204 * bx = card select number (probably 0xffff)
2205 * dx = read data port address (probably 0xffff)
2208 * ax = initialization status
2209 * bit 8 = 1 = IPL device supports int 13h block dev format
2210 * bit 7 = 1 = Output device supports int 10h char output
2211 * bit 6 = 1 = Input device supports int 9h char input
2212 * bit 5..4 = 00 = no IPL device attached
2213 * 01 = unknown whether or not IPL device attached
2214 * 10 = IPL device attached (RPL devices have connection)
2216 * bit 3..2 = 00 = no display device attached
2217 * 01 = unknown whether or not display device attached
2218 * 10 = display device attached
2220 * bit 1..0 = 00 = no input device attached
2221 * 01 = unknown whether or not input device attached
2222 * 10 = input device attached
2225 * all registers preserved except %ax
2229 /* FIXME: this is *wrong* -- init only what bios says to init */
2230 movw $0xca, %ax /* 0xca = attached int 10h, 9h display, input */
2235 * legacy option rom entry point
2237 * all registers preserved
2241 /* this is probably paranoid about register preservation */
2243 cli /* more paranoia */
2248 popw %es /* es = 0 */
2250 popw %ds /* ds = cs */
2251 /* get original ISR */
2252 movl %es:0x28, %eax /* eax = old irq 3/int 0bh */
2253 movl %eax, old_irq3 /* save away old irq 4/int 0bh */
2254 movl %es:0x2c, %eax /* eax = old irq 4/int 0ch */
2255 movl %eax, old_irq4 /* save away old irq 4/int 0ch */
2256 movl %es:0x40, %eax /* eax = old int 10h */
2257 movl %eax, old_int10h /* save away old int 10h */
2258 movl %es:0x50, %eax /* eax = old int 14h */
2259 movl %eax, old_int14h /* save away old int 14h */
2260 movl %es:0x58, %eax /* eax = old int 16h */
2261 movl %eax, old_int16h /* save away old int 16h */
2262 movw $irq3_isr, %es:0x28 /* new irq 3 offset */
2263 movw %cs, %es:0x2a /* write new irq 3 seg */
2264 movw $irq4_isr, %es:0x2c /* new irq 4 offset */
2265 movw %cs, %es:0x2e /* write new irq 4 seg */
2266 movw $int10h_isr, %es:0x40 /* new int 10h offset */
2267 movw %cs, %es:0x42 /* write new int10h seg */
2268 movw $int14h_isr, %es:0x50 /* new int 14h offset */
2269 movw %cs, %es:0x52 /* write new int14h seg */
2270 movw $int16h_isr, %es:0x58 /* new int 16h offset */
2271 movw %cs, %es:0x5a /* write new int16h seg */
2272 /* empty input buffer to prepare for terminal sizing */
2273 call init_serial_port
2276 jnz input_clear_loop
2277 movw $term_init_string, %si
2281 popw %ds /* ds = 0x40 */
2282 popw %es /* es = 0x40 */
2283 movw $BDA_CURSOR_BUF, %di
2285 /* get input from terminal until timeout found */
2286 /* store input at 40:50 - 40:5e (cursor pos) */
2290 cmpw $0x5f, %di /* 14 characters max */
2291 jnz input_timeout_loop /* good for more data */
2293 xorb %al, %al /* nul terminate input */
2295 cmpw $0x58, %di /* less than 8 chars? */
2296 jc resize_end /* too small to have valid data */
2297 movw $BDA_CURSOR_BUF, %si /* point to start */
2298 lodsw /* ax = first 2 chars */
2299 cmpw $0x5b1b, %ax /* was it "ESC[" ? */
2300 jnz resize_end /* reply starts ESC[row;colR */
2301 xorb %bl, %bl /* bl = ascii->int conversion */
2303 lodsb /* al = next char */
2305 jc resize_end /* char < 0x30 invalid */
2306 cmpb $0x3a, %al /* is char < 0x3a */
2308 andb $0x0f, %al /* al = 0 - 9 */
2309 movb %bl, %ah /* ah = last conversion */
2310 aad /* ax = (al + ah * 10) & 0xff */
2311 movb %al, %bl /* bl = row ascii->int conversion */
2312 jmp input_first_number
2314 /* at this point bl should contain rows, al = ; */
2315 /* sanity check, bail if invalid */
2317 jnz resize_end /* invalid input found */
2318 cmpb $0x0a, %bl /* less than 10 rows? */
2319 jc suspect_loopback /* consider input invalid */
2320 xorb %bh, %bh /* bh = col ascii->int conversion */
2321 input_second_number:
2322 lodsb /* al = next char */
2324 jc resize_end /* char < 0x30 invalid */
2325 cmpb $0x3a, %al /* is char < 0x3a */
2327 andb $0x0f, %al /* al = 0 - 9 */
2328 movb %bh, %ah /* ah = last conversion */
2329 aad /* ax = (al + ah * 10) & 0xff */
2330 movb %al, %bh /* bh = ascii->int conversion */
2331 jmp input_second_number
2333 cmpb $0x52, %al /* is al = 'R' ? */
2334 jnz suspect_loopback /* invalid input found */
2335 movb %bl, %cs:term_rows /* save away bl rows value */
2336 cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */
2337 jz resize_end /* if so, leave term_cols at 80 */
2338 movb %bh, %cs:term_cols /* save away bh cols value */
2342 * characters were received that look like what we sent out
2343 * at this point, assume that a loopback device was plugged in
2344 * and disable any future serial port reads or writes, by pointing
2345 * output to port 0x2e8 (COM4) instead of 0x3f8 -- it's expected
2346 * that this is safe since a real port responds correctly and a
2347 * missing port will respond with 0xff which will terminate the
2348 * loop that waits for the "right" status on the port.
2350 movw $0x2e8, %cs:serial_port_base_address
2352 /* clear (hopefully) overwritten cursor position buffer */
2354 movw $BDA_CURSOR_BUF, %di
2358 stosb /* fill 40:50 - 40:5f with 0 */
2360 popw %ds /* ds = cs */
2361 call get_byte /* flush any remaining "wrong" input */
2363 call send_crlf /* place cursor on start of last line */
2364 movw $mfg_string, %si
2367 movw $prod_string, %si
2370 movw $long_version, %si
2373 /* if vga attached, skip terminal message and bda setup... */
2374 cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */
2375 jz post_bda_init_tail /* if so, don't modify BDA */
2376 /* show detected terminal size, or default if none detected */
2377 movw $term_info, %si
2380 popw %ds /* ds = 0x40 */
2381 movb %cs:term_cols, %al
2382 movb %al, BDA_COLS /* 40:4a = number of character cols */
2383 movb $0, BDA_CURSOR_COL /* 40:51 = cursor0 col */
2385 movb $0x78, %al /* x */
2387 movb %cs:term_rows, %al
2389 decb %ah /* ah = rows-1 */
2390 movb %ah, BDA_ROWS /* 40:84 = num character rows - 1 */
2391 movb %ah, BDA_CURSOR_ROW /* 40:50 = cursor0 row */
2394 movb $3, BDA_MODE_NUM
2395 movb $0x29, BDA_MODE_SEL
2396 movw $VGA_IO_BASE, BDA_6845_ADDR
2397 movw $0x4000, BDA_PAGE_SIZE /* 16KB per video page */
2398 /* to avoid ansi colors every character, store last attribute */
2399 movb $0x07, BDA_COLOR_VAL /* 07 = black bg, white fg */
2401 movw $_start, BDA_ROM_OFF
2402 movw %ax, BDA_ROM_SEG
2404 /* copy BDA rows/cols to sgabios location... */
2405 /* if vga card is installed, reuse those values... */
2406 /* if no vga card is installed, this shouldn't change anything */
2408 popw %ds /* ds = 0x40 */
2410 incb %al /* bda holds rows-1 */
2411 movb %al, %cs:term_rows /* sgabios rows */
2413 movb %ah, %cs:term_cols /* sgabios cols */
2414 /* setup in-memory logging of console if desired... */
2415 call setup_memconsole
2416 /* setup logging of last 256 characters output, if ebda has room */
2417 call sgabioslog_setup_ebda
2418 movw $ebda_info, %si
2420 movw %cs:sgabios_ebda_logbuf_offset, %ax