fix typo in the svn:keywords property
[sgabios.git] / sgabios.S
1 /*
2  * Copyright 2007 Google Inc.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "version.h"
18
19 /* don't allocate ebda if new value at 0x40e will be less than this */
20 #define EBDA_MIN_SEG            0x9800
21 #define SGABIOS_EBDA_KB         1
22 /* note: no testing has yet been done logging other than 256 bytes */
23 #define SGABIOS_EBDA_BYTES      (SGABIOS_EBDA_KB*1024)
24 #define SGABIOS_EBDA_DELTA      (SGABIOS_EBDA_BYTES/16)
25 #define SGABIOS_EBDA_LOG_START  256
26 #define SGABIOS_EBDA_LOG_SIZE   256
27 #define SGABIOS_EBDA_POS_START  (SGABIOS_EBDA_LOG_START+SGABIOS_EBDA_LOG_SIZE)
28 #define SGABIOS_EBDA_POS_LAST   (SGABIOS_EBDA_POS_START+(SGABIOS_EBDA_LOG_SIZE*2)-2)
29
30 /* serial costants that may require modification */
31 #define COM_BASE_ADDR           0x3f8
32 #define PORT_SPEED              115200
33 #define LCR_VALUE               0x13    /* 8n1 */
34
35 /* serial constants below shouldn't require modification */
36 #define IER_OFFSET              0x01
37 #define FCR_OFFSET              0x02
38 #define LCR_OFFSET              0x03
39 #define MCR_OFFSET              0x04
40 #define LSR_OFFSET              0x05
41 #define MSR_OFFSET              0x06
42 #define SCR_OFFSET              0x07
43 #define LCR_DLAB                0x80
44 #define MCR_DTRRTS              0x03
45 #define FCR_FIFO_ENABLE         0x01
46 #define PORT_DIVISOR            115200
47 #define TRANSMIT_READY_BIT      0x20
48 #define BIOS_BUILD_VERSION      "$Id$"
49
50 #define KBD_HEAD                0x1a
51 #define KBD_TAIL                0x1c
52 #define KBD_BUF_START           0x1e
53 #define KBD_BUF_END             0x3e
54
55 #define VGA_IO_BASE             0x3d4
56 #define BDA_SEG                 0x40
57 #define BDA_EBDA                0x0e
58 #define BDA_MEM_SIZE            0x13
59 #define BDA_MODE_NUM            0x49
60 #define BDA_COLS                0x4a
61 #define BDA_PAGE_SIZE           0x4c
62 /* BDA word 40:0c traditionally holds the LPT3 io port address... */
63 /* Reuse it for tracking where the serial console cursor was left */
64 /* Don't send ansi cursor pos update without text ready to output */
65 /* Some operations don't update cursor position, but next int 10h */
66 /* call is often one that might update to where cursor already is */
67 #define BDA_SERIAL_POS          0x0c
68 #define BDA_CURSOR_BUF          0x50
69 #define BDA_CURSOR_COL          0x50
70 #define BDA_CURSOR_ROW          0x51
71 #define BDA_CURSOR_SCAN         0x60
72 #define BDA_ACTIVE_PAGE         0x62
73 #define BDA_6845_ADDR           0x63
74 #define BDA_MODE_SEL            0x65
75 #define BDA_COLOR_VAL           0x66
76 #define BDA_ROM_OFF             0x67
77 #define BDA_ROM_SEG             0x69
78 #define BDA_ROWS                0x84
79
80 .code16
81 .org 0x0
82 .text
83 .global _start
84 _start:
85         /* option rom header */
86         .byte 0x55
87         .byte 0xaa
88         .byte (511 + _end_sgabios - _start)/512
89         /* legacy entry at offset 3 */
90         jmp pnp_sga_init
91         /* pnp entry here to avoid changing PnP table as code moves */
92 pnp_init:
93         jmp pnp_sga_init
94
95 /*
96  * do_old_int10h
97  *
98  * Patched at option rom init to be a far jump to old int 10h isr
99  *
100  */
101 do_old_int10h:
102         .byte 0xea              /* jmp absolute segment:offset */
103 old_int10h:                     /* store what was at offset 0x40 */
104         .word 0xf065            /* placeholder for chained ISR offset */
105         /* if the chained segment is detected as 0xc000, use 80 cols only */
106         /* since it's assumed that a vga card is attached and 80 cols max */
107 old_int10h_seg:
108         .word 0xf000            /* placeholder for chained ISR segment */
109 /*
110  * do_old_int16h
111  *
112  * Patched at option rom init to be a far jump to old int 16h isr
113  *
114  */
115 do_old_int16h:
116         .byte 0xea              /* jmp absolute segment:offset */
117 old_int16h:                     /* store what was at offset 0x58 */
118         .word 0xe82e            /* placeholder for chained ISR offset */
119         .word 0xf000            /* placeholder for chained ISR segment */
120 .org 0x18
121         .word 0                 /* offset to PCI data, 0 = none */
122         .word pnp_table         /* offset to PnP expansion header */
123 .org 0x20
124 pnp_table:
125         /* FIXME: **** PnP header currently disabled by PoO **** */
126         /* legacy entry only called once, PnP entry called multiple times */
127         /* The code isn't yet written to deal with multiple inits properly */
128         .ascii "$PoO"           /* PnP expansion header signature */
129         .byte 1                 /* structure revision */
130         .byte 2                 /* length in 16-byte increments */
131         .word 0                 /* offset of next header, 0 if none */
132         .byte 0                 /* reserved */
133         .byte 0x52              /* checksum - update manually! FIXME */
134         .long 0                 /* device identifier */
135         .word mfg_string        /* pointer to manufacturer string */
136         .word prod_string       /* pointer to product name string */
137         .byte 3, 0x80, 0x80     /* device type code = other display */
138         .byte 0xe3              /* device indicators, input/display dev */
139         .word 0                 /* boot connection vector, 0 if none */
140         .word 0                 /* disconnect vector, 0 if none */
141         .word pnp_init          /* bootstrap entry vector */
142         .word 0                 /* reserved */
143         .word 0                 /* static resource information vector */
144
145         /* WARNING: changing mfg_string / prod_string locations will */
146         /* affect pnp table above -- recalculate checksum manually! */
147 mfg_string:
148         .asciz "Google, Inc."
149 prod_string:
150         .ascii "Serial Graphics Adapter "
151 build_date:
152         .asciz BIOS_BUILD_DATE
153 long_version:
154         .ascii "SGABIOS Version "
155         .ascii BIOS_BUILD_VERSION
156         .ascii " ("
157         .ascii BIOS_BUILD_HOST
158         .ascii ") "
159         .asciz BIOS_FULL_DATE
160 term_cols:
161         .byte 80        /* overwritten at rom init with detected value */
162 term_rows:
163         .byte 24        /* overwritten at rom init with detected value */
164 term_init_string:       /* terminal reply: \033[n;mR n=row, m=col */
165         .asciz "\033[1;256r\033[256;256H\033[6n"
166         /* reset the scroll, move to col 256, row 256, ask current position */
167         /* bios cursor positions >255 rows or cols can't be used anyway */
168 term_info:
169         .asciz "Term: "
170 ebda_info:
171         .asciz "EBDA: "
172
173 /*
174  * do_old_irq3 - exception 0x0b, int 0x0a
175  *
176  * Patched at option rom init to be a far jump to old irq 3 isr
177  *
178  */
179 do_old_irq3:
180         .byte 0xea              /* jmp absolute segment:offset */
181 old_irq3:                       /* store what was at offset 0x28 */
182         .word 0xeef3            /* placeholder for chained ISR offset */
183         .word 0xf000            /* placeholder for chained ISR segment */
184
185 /*
186  * do_old_irq4 - exception 0x0c, int 0x0b
187  *
188  * Patched at option rom init to be a far jump to old irq 4 isr
189  *
190  */
191 do_old_irq4:
192         .byte 0xea              /* jmp absolute segment:offset */
193 old_irq4:                       /* store what was at offset 0x2c */
194         .word 0xeef3            /* placeholder for chained ISR offset */
195         .word 0xf000            /* placeholder for chained ISR segment */
196
197 /*
198  * do_old_int14h
199  *
200  * Patched at option rom init to be a far jump to old int 14h isr
201  *
202  */
203 do_old_int14h:
204         .byte 0xea              /* jmp absolute segment:offset */
205 old_int14h:                     /* store what was at offset 0x50 */
206         .word 0xe739            /* placeholder for chained ISR offset */
207         .word 0xf000            /* placeholder for chained ISR segment */
208
209 .align 16, 0xff         /* aligning this table only makes hexdump prettier */
210 /* ascii -> scancode, bit 7=shifted, char < 32 = +ctrl */
211 /* except chars 8, 9, 13, 27 (bs, tab, enter, esc) */
212 /* most int16h consumers will probably never use */
213 ascii2scan:
214 /*00*/  .byte 0x00, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22 
215 /*08*/  .byte 0x0e, 0x17, 0x24, 0x25, 0x26, 0x1c, 0x31, 0x18 
216 /*10*/  .byte 0x19, 0x0f, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11 
217 /*18*/  .byte 0x2d, 0x15, 0x2c, 0x01, 0x2b, 0x1b, 0x87, 0x8c 
218 /*20*/  .byte 0x39, 0x82, 0xa8, 0x84, 0x85, 0x86, 0x88, 0x28 
219 /*28*/  .byte 0x8a, 0x8b, 0x89, 0x8d, 0x33, 0x0c, 0x34, 0x35 
220 /*30*/  .byte 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 
221 /*38*/  .byte 0x09, 0x0a, 0xa7, 0x27, 0xb3, 0x0d, 0x34, 0xb5 
222 /*40*/  .byte 0x83, 0x9e, 0xb0, 0xae, 0xa0, 0x92, 0xa1, 0xa2 
223 /*48*/  .byte 0xa3, 0x97, 0xa4, 0xa5, 0xa6, 0xb2, 0xb1, 0x98 
224 /*50*/  .byte 0x99, 0x90, 0x93, 0x9f, 0x94, 0x96, 0xaf, 0x91 
225 /*58*/  .byte 0xad, 0x95, 0xac, 0x1a, 0x2b, 0x1b, 0x87, 0x8c 
226 /*60*/  .byte 0x29, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22 
227 /*68*/  .byte 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18 
228 /*70*/  .byte 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11 
229 /*78*/  .byte 0x2d, 0x15, 0x2c, 0x9a, 0xab, 0x9b, 0xa9, 0x0e 
230
231 /* TABLES FOR NON-ASCII VGA CHARACTERS (CP437) TO ASCII */
232 /* Unicode at: http://en.wikipedia.org/wiki/Code_page_437 */
233
234 ctrl2ascii:
235 /* translate vga (CP437) first 32 characters to ascii */
236 /* for char 0, update the cursor position, but output nothing */
237 /* lilo uses this "trick" for a background attribute update */
238         .ascii "\0@@v***........*><|!PS-|^v><L-^v"
239 high2ascii:
240 /* translate vga (CP437) chars 0x80 to 0xff to ascii */
241 /* these characters are mostly to visually approximate */
242 /* line art characters will probably need tweaking */
243 /*80*/  .ascii "CueaaaaceeeiiiAAEaAooouuyOUcLYPf"
244 /*a0*/  .ascii "aiounNao?--24!<>###||||++||+++++"
245 /*c0*/  .ascii "+--|-+||++--|-+----++++++++#-||-"
246 /*e0*/  .ascii "abgpesut00osiye^=+><||-=...vn2* "
247
248 colortable:
249 /* vga text color is IRGB, ansi color is BGR */
250 /* this table is effectively a nibble bit-reverse */
251         .byte 0, 4, 2, 6, 1, 5, 3, 7
252
253 serial_port_base_address:
254         .word COM_BASE_ADDR
255
256 /* in-memory console log
257  *
258  * It's expected that the EBDA contains a magic signature
259  * like 0xdeadbabe, followed by a byte of flags, followed
260  * by a 32-bit buffer pointer, followed by a 16-bit start
261  * index, followed by a 16-bit end index, followed by 16-
262  * bit logged character count, followed by an 8-bit flag.
263  */
264
265 #define MEMCONSOLE_BUFFER_SIZE  32768
266 #define MEMCONSOLE_SIGNATURE    0xdeadbabe
267 #define MEMCONSOLE_ENDINDEX_OFF 0x0b
268 #define SGABIOS_EBDA_SIGNATURE  0x00414753
269
270 memconsole_buffer_start:                /* pulled from ebda struct */
271         .long   0x00000000              /* 0 = not found/no logging */
272 memconsole_ebda_deadbabe_offset:        /* bytes from start of ebda */
273         .word   0x0000                  /* 40:0e contains ebda seg */
274 sgabios_ebda_logbuf_offset:             /* bytes from start of ebda */
275         .word   0x0000                  /* 40:0e contains ebda seg */
276
277 /*
278  * setup_memconsole
279  *
280  * Initialize the option rom variables associated with logging
281  * of the legacy console output
282  *
283  * If these variables are left at zero, no logging will occur
284  *
285  * There are no parameters
286  * All registers except flags should be preserved
287  */
288
289 setup_memconsole:
290         pushaw
291         pushw %ds
292         pushw %es
293         pushw $BDA_SEG
294         popw %ds                /* ds = 0x40 */
295         pushw BDA_EBDA          /* push word at 0x0e */
296         popw %es                /* es = EBDA_SEG */
297         /* search for memconsole signature in ebda */
298         movl $MEMCONSOLE_SIGNATURE, %eax
299         xorw %di, %di           /* start at zero */
300         movzbw %es:(%di), %cx   /* cx = size of EBDA in KB */
301         shlw $8, %cx            /* cx = (cx * 1024) / 4 */
302         cld
303         repnz
304         scasl                   /* search until sig found */
305         subw $4, %di            /* scasl always increments di, undo */
306         cmpl %eax, %es:(%di)    /* is signature here? */
307         jnz setup_memconsole_end        /* bail if so */
308         movw %di, %cs:memconsole_ebda_deadbabe_offset   /* save offset */
309         movl %es:5(%di), %eax   /* get 32-bit buffer base address */
310         movl %eax, %cs:memconsole_buffer_start
311 setup_memconsole_end:
312         popw %es
313         popw %ds
314         popaw
315         ret
316
317 /*
318  * memconsole_log_char
319  *
320  * Log the character passed in %al to the next available memory
321  * console log position, if any.
322  *
323  * If memconsole_buffer_start is zero, no logging will occur
324  *
325  * %al = character to be logged
326  * All registers except flags should be preserved
327  */
328
329 memconsole_log_char:
330         pushaw
331         pushw %ds
332         pushw %es
333         pushw %fs
334         pushw $BDA_SEG
335         popw %ds                /* ds = 0x40 */
336         pushw BDA_EBDA          /* push word at 0x0e */
337         popw %es                /* es = EBDA_SEG */
338         movw %ax, %si                   /* %si = %al = byte to write */
339         movl %cs:memconsole_buffer_start, %ebp
340         movw %cs:memconsole_ebda_deadbabe_offset, %di
341         addw $MEMCONSOLE_ENDINDEX_OFF, %di      /* %di points to char pos */
342         orl %ebp, %ebp
343         jz memconsole_log_tail          /* bufptr==0, no logging */
344         movw %es:(%di), %bx             /* bx = current position in buffer */
345         cmpw $MEMCONSOLE_BUFFER_SIZE, %bx       /* at end of buffer? */
346         jnc memconsole_log_tail         /* don't log any more if so */
347         cmpb $0xd, %al                  /* is the char CR? */
348         jz memconsole_log_tail          /* if so, ignore it */
349         cmpb $0x8, %al                  /* is the char backspace? */
350         jnz memconsole_update_fsbase    /* if not, log char as usual... */
351         orw %bx, %bx                    /* make sure ptr isn't already zero */
352         jz memconsole_log_tail          /* if so, bail */
353         decw %bx                        /* else point to previous character */
354         jmp memconsole_update_end_ptr   /* and go directly to save it */
355 memconsole_update_fsbase:
356         movl $0xc0000100, %ecx          /* ecx = IA32_FS_BASE (AMD64+) */
357         rdmsr                           /* read what was there before */
358         pushl %eax                      /* save away previous FS_BASE eax */
359         pushl %edx                      /* save away previous FS_BASE edx */
360         xorl %edx, %edx                 /* clear high 32 bits */
361         movl %ebp, %eax                 /* eax = memconsole buffer start */
362         wrmsr                           /* fs_base = memconsole buffer start */
363         movw %si, %ax                   /* %ax = saved value on entry */
364         movb %al, %fs:(%bx)             /* log character */
365         popl %edx                       /* restore previous FS_BASE edx */
366         popl %eax                       /* restore previous FS_BASE eax */
367         wrmsr                           /* write what was there before */
368         incw %bx                        /* update character count */
369 memconsole_update_end_ptr:
370         movw %bx, %es:(%di)             /* save new end pointer */
371         addw $2, %di                    /* numchars stored at next word */
372         movw %bx, %es:(%di)             /* save new numchar value */
373 memconsole_log_tail:
374         popw %fs
375         popw %es
376         popw %ds
377         popaw
378         ret
379
380 /* sgabioslog_setup_ebda
381  *
382  * SGABIOS makes its own 1KB EBDA allocation to save non-
383  * translated characters with associated cursor positions
384  * for the last 256 characters output.  This is organized
385  * with 256 bytes reserved for houskeeping, 256 bytes for
386  * the raw character codes, and 512 bytes of 16bit cursor
387  * positions to record the associated position for each.
388  *
389  * The first 4 bytes contain "SGA\0" followed by a 16-bit
390  * size of the allocation in bytes,  followed by a 16-bit
391  * index indicating the next spot to be overwritten.
392  * 
393  * There are no parameters
394  * All registers should be preserved
395  */
396
397 sgabioslog_setup_ebda:
398         pushf
399         pushaw
400         pushw %ds
401         pushw %es
402         pushw $BDA_SEG
403         popw %ds                        /* ds = 0x40 */
404         movw BDA_EBDA, %ax              /* ax = old ebda segment from 0x0e */
405         subw $SGABIOS_EBDA_DELTA, %ax
406         movw %ax, %es                   /* es = new EBDA segment start */
407         cmpw $EBDA_MIN_SEG, %ax         /* is there room for the allocation? */
408         jc sgabioslog_setup_ebda_tail   /* if not, don't change anything */
409         cli                             /* paranoid in case irq uses EBDA */
410         movw %ax, BDA_EBDA              /* save new EBDA segment start */
411         subw $SGABIOS_EBDA_KB, BDA_MEM_SIZE     /* subtract extra allocation */
412         movw %ax, %ds                   /* ds = new EBDA segment start */
413         movw $SGABIOS_EBDA_BYTES, %si   /* si = offset of first byte to move */
414         movzbw (%si), %cx               /* cx = number of KB in EBDA */
415         addb $SGABIOS_EBDA_KB, (%si)    /* update EBDA size in kb */
416         shlw $10, %cx                   /* cx = KB * 1024 = bytes in EBDA */
417         movw %cx, %cs:sgabios_ebda_logbuf_offset        /* new ebda space */
418         xorw %di, %di                   /* di = new EBDA start */
419         cld
420         rep
421         movsb                           /* move ebda by SGABIOS_EBDA_BYTES */
422         movw %cs:sgabios_ebda_logbuf_offset, %bx        /* bx = new buffer */
423         movl $SGABIOS_EBDA_SIGNATURE, (%bx)     /* setup signature */
424         movw $SGABIOS_EBDA_BYTES, 4(%bx)        /* bytes in new ebda buffer */
425         movw $0, 6(%bx)                 /* next log index, new ebda buffer */
426 sgabioslog_setup_ebda_tail:
427         popw %es
428         popw %ds
429         popaw
430         popf
431         ret
432
433 /*
434  * sgabioslog_save_char
435  *
436  * Like memconsole_log_char, except the original, untranslated
437  * character is expected to be given in the %al register.
438  *
439  * The original character and its corresponding cursor position
440  * are logged to the sgabios ebda memory allocation.
441  *
442  * %al = character to be logged
443  * All registers except flags should be preserved
444  */
445
446 sgabioslog_save_char:
447         pushaw
448         pushw %ds
449         pushw %es
450         pushw $BDA_SEG
451         popw %ds                /* ds = 0x40 */
452         pushw BDA_EBDA          /* push word at 0x0e */
453         popw %es                /* es = EBDA_SEG */
454         movw %cs:sgabios_ebda_logbuf_offset, %di
455         orw %di, %di            /* is offset zero? */
456         jz sgabioslog_save_tail /* if so, bail */
457         cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di)
458         jnz sgabioslog_save_tail        /* bail if magic not found */
459         movw %es:6(%di), %bx    /* bx = index of next char output */
460         movb %al, %es:SGABIOS_EBDA_LOG_START(%bx,%di)   /* store character */
461         movzbw %bl, %ax         /* %ax = next cursor buffer index */
462         shlw $1, %ax            /* %ax = offset to cursor storage */
463         call get_current_cursor         /* %dh = row, %dl = column */
464         addw $SGABIOS_EBDA_POS_START, %di       /* cursor storage */
465         addw %ax, %di           /* %di = next cursor storage offset */
466         movw %dx, %es:(%di)     /* save position for logged char */
467         incw %bx                /* point to next char to log */
468         cmpw $SGABIOS_EBDA_LOG_SIZE, %bx
469         jnz sgabioslog_save_index
470         xorw %bx, %bx           /* wrap around to start */
471 sgabioslog_save_index:
472         movw %cs:sgabios_ebda_logbuf_offset, %di
473         movw %bx, %es:6(%di)    /* save new index */
474 sgabioslog_save_tail:
475         popw %es
476         popw %ds
477         popaw
478         ret
479
480 /*
481  * sgabioslog_get_char
482  *
483  * Return the character at current cursor position, last recorded
484  * to sgabios ebda allocation, if available.
485  *
486  * If the current cursor postition contains one of the last 256 characters
487  * written to the ebda buffer, return that character, else return 0.
488  *
489  * If sgabios_ebdda_logbuf_offset is zero, %al will be 0 and zf set
490  *
491  * All registers except flags and %al should be preserved
492  */
493
494 sgabioslog_get_char:
495         pushaw
496         movw %sp, %bp
497         movb $0, 14(%bp)                /* %al on stack = 0 */
498         pushw %ds
499         pushw %es
500         pushw $BDA_SEG
501         popw %ds                /* ds = 0x40 */
502         pushw BDA_EBDA          /* push word at 0x0e */
503         popw %es                /* es = EBDA_SEG */
504         movw %cs:sgabios_ebda_logbuf_offset, %di
505         orw %di, %di
506         jz sgabioslog_get_tail          /* offset==0, no logging */
507         cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di)
508         jnz sgabioslog_get_tail         /* bail if magic not found */
509         call get_current_cursor         /* dh = row, dl = col */
510         std                             /* scan backwards in mem */
511         movw %es:6(%di), %bx            /* bx = index of next char output */
512         decw %bx                        /* %bx = offset of last char in buf */
513         jnc sgabioslog_got_pos
514         addw $SGABIOS_EBDA_LOG_SIZE, %bx        /* bx position wrap around */
515 sgabioslog_got_pos:
516         movw %bx, %ax                   /* %ax = last cursor pos written */
517         shlw $1, %ax                    /* %ax = offset of last cursor pos */
518         addw $SGABIOS_EBDA_POS_START, %di /* %di = first cursor position */
519         addw %ax, %di                   /* %di = offset in ebda */
520         movw %dx, %ax                   /* %ax = cursor pos to compare */
521         movw %bx, %cx                   /* %cx = positions before wrap */
522         jcxz sgabioslog_cmp_wrap        /* if zero, try from end next */
523         repnz
524         scasw                           /* search until position match */
525         addw $2, %di            /* scasd always decrements di, undo */
526         cmpw %ax, %es:(%di)             /* did it really match? */
527         jz sgabioslog_cursor_match      /* if so, do something */
528 sgabioslog_cmp_wrap:
529         movw %cs:sgabios_ebda_logbuf_offset, %di
530         addw $SGABIOS_EBDA_POS_LAST, %di /* %di = last cursor storage */
531         movw $SGABIOS_EBDA_LOG_SIZE, %cx /* %cx = compare all positions */
532         repnz
533         scasw                           /* search until position match */
534         addw $2, %di            /* scasd always decrements di, undo */
535         cmpw %ax, %es:(%di)             /* did it really match? */
536         jnz sgabioslog_get_tail         /* if not, bail */
537 sgabioslog_cursor_match:
538         /* %di contains the EBDA offset of the matching position */
539         /* convert this into a memconsole offset */
540         subw $512, %di                  /* take off the storage offset */
541         subw %cs:sgabios_ebda_logbuf_offset, %di /* and ebda offset */
542         shrw $1, %di                    /* %di = char position index */
543         addw %cs:sgabios_ebda_logbuf_offset, %di /* add back ebda offset */
544         addw $SGABIOS_EBDA_LOG_START, %di /* and add back log offset */
545         movb %es:(%di), %al             /* get related saved character */
546         movb %al, 14(%bp)               /* %al on stack = logged char */
547 sgabioslog_get_tail:
548         popw %es
549         popw %ds
550         popaw
551         ret
552
553 /*
554  * multibyteinput
555  *
556  * When an escape key is detected, the input routines will attempt to
557  * capture as many characters as arrive up until a timeout, or six,
558  * whichever is less.
559  *
560  * This table is intended to decide what the characters after the
561  * initial escape key translate to in terms of high and low bytes
562  * that go into the keyboard buffer the high byte is the scancode,
563  * the low byte is ascii, but for special keys this is usually 0xe0
564  * or 0x00.
565  *
566  * This table is formatted so that the first word is a scancode +
567  * ascii pair (as returned by int 16h, ah = 10h or 11h).  Immediately
568  * following is a nul-terminated ascii string to match in order to
569  * use the corresponding scancode+ascii word.
570  *
571  * The search through this table is terminated by a match or finding
572  * a 0 scancode+ascii word.
573  *
574  * FIXME: all the low bytes are now zero, get rid of them?
575  */
576 multibyteinput:
577         .byte 0x3b      /* F1 */
578         .asciz "[[A"    /* F1/screen */
579
580         .byte 0x3b      /* F1 */
581         .asciz "OP"     /* F1/xterm/ansi */
582
583         .byte 0x3b      /* F1 */
584         .asciz "[11~"   /* F1/vt400 */
585
586         .byte 0x3c      /* F2 */
587         .asciz "[[B"    /* F2/screen */
588
589         .byte 0x3c      /* F2 */
590         .asciz "OQ"     /* F2/xterm/ansi */
591
592         .byte 0x3c      /* F2 */
593         .asciz "[12~"   /* F2/vt400 */
594
595         .byte 0x3d      /* F3 */
596         .asciz "[[C"    /* F3/screen */
597
598         .byte 0x3d      /* F3 */
599         .asciz "OR"     /* F3/xterm/ansi */
600
601         .byte 0x3d      /* F3 */
602         .asciz "[13~"   /* F3/vt400 */
603
604         .byte 0x3e      /* F4 */
605         .asciz "[[D"    /* F4/screen */
606
607         .byte 0x3e      /* F4 */
608         .asciz "OS"     /* F4/xterm/ansi */
609
610         .byte 0x3e      /* F4 */
611         .asciz "[14~"   /* F4/vt400 */
612
613         .byte 0x3f      /* F5 */
614         .asciz "[[E"    /* F5/screen */
615
616         .byte 0x3f      /* F5 */
617         .asciz "[15~"   /* F5/xterm */
618
619         .byte 0x3f      /* F5 */
620         .asciz "OT"     /* F5/ansi */
621
622         .byte 0x40      /* F6 */
623         .asciz "[17~"   /* F6/screen/vt220/xterm/vt400 */
624
625         .byte 0x40      /* F6 */
626         .asciz "OU"     /* F6/ansi */
627
628         .byte 0x41      /* F7 */
629         .asciz "[18~"   /* F7/screen/vt220/xterm/vt400 */
630
631         .byte 0x41      /* F7 */
632         .asciz "OV"     /* F7/ansi */
633
634         .byte 0x42      /* F8 */
635         .asciz "[19~"   /* F8/screen/vt220/xterm/vt400 */
636
637         .byte 0x42      /* F8 */
638         .asciz "OW"     /* F8/ansi */
639
640         .byte 0x43      /* F9 */
641         .asciz "[20~"   /* F9/screen/vt220/xterm/vt400 */
642
643         .byte 0x43      /* F9 */
644         .asciz "OX"     /* F9/ansi */
645
646         .byte 0x44      /* F10 */
647         .asciz "[21~"   /* F10/screen/vt220/xterm/vt400 */
648
649         .byte 0x44      /* F10 */
650         .asciz "OY"     /* F10/ansi */
651
652         .byte 0x85      /* F11 */
653         .asciz "[23~"   /* F11/screen/xterm/vt400 */
654
655         .byte 0x85      /* F11 */
656         .asciz "OZ"     /* F11/ansi */
657
658         .byte 0x86      /* F12 */
659         .asciz "[24~"   /* F12/screen/xterm/vt400 */
660
661         .byte 0x52      /* Insert */
662         .asciz "[2~"    /* Insert/screen/vt102/xterm */
663
664         .byte 0x53      /* Delete */
665         .asciz "[3~"    /* Delete/screen/vt102/xterm */
666
667         .byte 0x4b      /* Left */
668         .asciz "OD"     /* Left/screen/vt102 */
669
670         .byte 0x4b      /* Left */
671         .asciz "[D"     /* Left/xterm */
672
673         .byte 0x47      /* Home */
674         .asciz "[1~"    /* Home/screen/vt102 */
675
676         .byte 0x47      /* Home */
677         .asciz "[H"     /* Home/xterm */
678
679         .byte 0x4f      /* End */
680         .asciz "[4~"    /* End/screen/vt102 */
681
682         .byte 0x4f      /* End */
683         .asciz "[F"     /* End/xterm */
684
685         .byte 0x48      /* Up */
686         .asciz "OA"     /* Up/screen/vt102 app */
687
688         .byte 0x48      /* Up */
689         .asciz "[A"     /* Up/xterm/vt102 ansi */
690
691         .byte 0x50      /* Down */
692         .asciz "OB"     /* Down/screen/vt102 app */
693
694         .byte 0x50      /* Down */
695         .asciz "[B"     /* Down/xterm/vt102 ansi */
696
697         .byte 0x49      /* PageUp */
698         .asciz "[5~"    /* PageUp/screen/vt102/xterm */
699
700         .byte 0x51      /* PageDown */
701         .asciz "[6~"    /* PageDown/screen/vt102/xterm */
702
703         .byte 0x4d      /* Right */
704         .asciz "OC"     /* Right/screen/vt102 app */
705
706         .byte 0x4d      /* Right */
707         .asciz "[C"     /* Right/xterm/vt102 ansi */
708
709         .byte 0         /* end of table marker */
710
711 /* init_serial_port
712  *
713  * Initialize serial port to 115200,8n1
714  * Serial interrupts disabled
715  *
716  * All registers except flags preserved
717  */
718
719 init_serial_port:
720         pushw %ax
721         pushw %dx
722         pushw %bx
723         movw %cs:serial_port_base_address, %dx
724         addw $IER_OFFSET, %dx
725         xorb %al, %al
726         outb %al, %dx   /* disable all serial interrupts */
727         addw $(LCR_OFFSET - IER_OFFSET), %dx    /* LCR */
728         movb $(LCR_VALUE|LCR_DLAB), %al
729         outb %al, %dx   /* enable divisor access */
730         movw %cs:serial_port_base_address, %dx
731         movw $(PORT_DIVISOR/PORT_SPEED), %bx
732         movb %bl, %al   /* al = lsb of divisor */
733         outb %al, %dx   /* set divisor latch lsb */
734         movb %bh, %al   /* al = msb of divisor */
735         incw %dx
736         outb %al, %dx   /* set divisor latch msb */
737         movw %cs:serial_port_base_address, %dx
738         addw $LCR_OFFSET, %dx
739         movb $LCR_VALUE, %al
740         outb %al, %dx   /* disable divisor access */
741         addw $(MCR_OFFSET - LCR_OFFSET), %dx    /* MCR */
742         movb $MCR_DTRRTS, %al
743         outb %al, %dx   /* enable DTR + RTS */
744         movw %cs:serial_port_base_address, %dx
745         addw $FCR_OFFSET, %dx
746         movb $FCR_FIFO_ENABLE, %al
747         outb %al, %dx   /* enable FIFOs */
748         popw %bx
749         popw %dx
750         popw %ax
751         ret
752
753
754 /* get_serial_lsr
755  *
756  * return serial line status register in %al
757  * return offset to serial port line status register io port in %dx
758  * all other registers except flags unchanged
759  *
760  * if status == 0xff  return ZF=1, else return ZF=0
761  */
762
763 get_serial_lsr:
764         movw %cs:serial_port_base_address, %dx
765         addw $LSR_OFFSET, %dx
766         inb %dx, %al
767         cmpb $0xff, %al
768         ret
769         
770 /*
771  * get_byte
772  *
773  * get serial byte in %al, scancode in %ah [FIXME: EFI console input]
774  *
775  * all registers except %ax preserved
776  *
777  */
778
779 get_byte:
780         pushw %dx
781         pushw %bx
782 next_serial_char:
783         call get_serial_lsr     /* get serial lsr in %al */
784         jz get_byte_tail        /* no port present... */
785         testb $1, %al   /* check bit 0 of LSR for data available */
786         jz get_byte_tail        /* no input waiting */
787         /* new character found on serial port */
788         /* convert it to a scancode */
789         movw %cs:serial_port_base_address, %dx
790         inb %dx, %al            /* al = serial input char */
791         testb $0x80, %al        /* non-ascii char received? */
792         jnz next_serial_char    /* throw char away */
793         movb %al, %dl           /* dl = character read */
794         pushw %ds
795         pushw %cs
796         popw %ds                /* ds = cs */
797         movw $ascii2scan, %bx   /* table to translate ascii->scan */
798         xlatb                   /* translate char to scancode */
799         popw %ds
800         /* shift status is ignored at this point, may be used later */
801         andb $0x7f, %al         /* strip shift status from table */
802         movb %al, %ah           /* scancode goes in high byte */
803         movb %dl, %al           /* "translated" ascii in lower byte */
804         cmpb $0x7f, %al         /* Did the user transmit ascii DEL? */
805         jnz get_byte_not_del    /* if not, don't do anything to al */
806         movb $0x08, %al         /* else delete becomes backspace */
807 get_byte_not_del:
808         testw %ax, %ax          /* clear zero flag */
809 get_byte_tail:
810         popw %bx
811         popw %dx
812         ret
813
814 /*
815  * poll_byte
816  *
817  * get serial byte in %al, scancode in %ah [FIXME: EFI console input]
818  * retry up to 65536 times for an expected input byte
819  *
820  * all registers except %ax preserved
821  *
822  */
823
824 poll_byte:
825         pushw %cx
826         xorw %cx, %cx
827 poll_byte_retry:
828         inb $0xed, %al
829         call get_byte
830         loopz poll_byte_retry   /* repeat while zf set or cx != 0 */
831         popw %cx
832         ret
833
834 /*
835  * get_multibyte
836  *
837  * after an escape character, poll for terminal keys that generate
838  * an escape code plus multiple bytes (up to four).
839  *
840  * if no byte is waiting, all registers preserved except flags
841  * if more bytes are waiting, all registers preserved except %ax and flags
842  *
843  */
844 get_multibyte:
845         pushw %bp               /* bp points to temp buffer on stack */
846         pushw %bx               /* bx points to multibyteinput table */
847         pushw %cx               /* cx will count chars */
848         pushw %ax               /* ax will receive chars */
849         pushl $0                /* make space on stack for 4 chars */
850         xorw %cx, %cx           /* cx = 0 */
851         movw %sp, %bp           /* point bp at temp data */
852         call poll_byte          /* is a character waiting? */
853         jz get_multibyte_tail   /* if not, bail */
854 get_multibyte_store:
855         movb %al, (%bp)         /* store char received */
856         incb %cl                /* mark one char received */
857         incw %bp                /* point to next char */
858         cmpb $4, %cl            /* got enough chars? */
859         jz got_multibyte        /* no strings longer than 4 chars */
860         call poll_byte          /* is another char waiting? */
861         jnz get_multibyte_store /* store a new one if it's there */
862 got_multibyte:
863         movw $multibyteinput, %bx       /* point to first scancode */
864 got_multibyte_findkey:
865         movw %sp, %bp           /* bp = start of buffer */
866         movb %cs:(%bx), %ah     /* ah = scancode */
867         incw %bx                /* bx = start of test string */
868         orb %ah, %ah            /* is it zero? */
869         jz get_multibyte_tail   /* if so, bail, key not found */
870 got_multibyte_nextchar:
871         movb %cs:(%bx), %ch     /* ch = test char to compare */
872         incw %bx                /* point to next char */
873         orb %ch, %ch            /* is char to compare NUL? */
874         jz got_multibyte_key    /* matched to end of a string! */
875         cmpb %ch, (%bp)         /* is input tmp buf equal to test char? */
876         jnz got_multibyte_try_next_key
877         /* note: expected that test string will be nul before input string */
878         /* no attempt is made to ensure no more than 4 bytes stack read */
879         incw %bp                /* point to next input */
880         jmp got_multibyte_nextchar
881 got_multibyte_try_next_key:     /* align to next scancode/ascii pair */
882         movb %cs:(%bx), %ch     /* ch = test char to compare */
883         incw %bx                /* point to next char */
884         orb %ch, %ch            /* is char to compare NUL? */
885         jnz got_multibyte_try_next_key
886         jmp got_multibyte_findkey
887 got_multibyte_key:
888         xorb %al, %al           /* ascii value = 0 for special keys */
889         movw %sp, %bp
890         movw %ax, 4(%bp)        /* overwrite old %ax value with new key */
891 get_multibyte_tail:
892         addw $4, %sp            /* pop temp space */
893         popw %ax
894         popw %cx
895         popw %bx
896         popw %bp
897         ret
898
899 /*
900  * send_byte
901  *
902  * send character in %al to serial port [FIXME: EFI console out]
903  *
904  * all registers preserved except flags
905  *
906  */
907
908 send_byte:
909         pushw %ax
910         pushw %dx
911         pushw %cx
912         testb $0x80, %al        /* don't send non-ascii chars */
913         jnz send_tail           /* these should be translated earlier */
914         movb %al, %ah           /* save char to output in %ah */
915         movw $0xFFF0, %cx       /* only retry 65520 times */
916 serial_ready_test:
917         call get_serial_lsr     /* get serial lsr in %al */
918         testb $TRANSMIT_READY_BIT, %al
919         loopz serial_ready_test /* if !TRANSMIT_READY_BIT, loop while cx!=0 */
920         movb %ah, %al
921         movw %cs:serial_port_base_address, %dx
922         outb %al, %dx
923 send_tail:
924         popw %cx
925         popw %dx
926         popw %ax
927         ret
928
929 /*
930  * translate_char
931  *
932  * translate vga character in %al to ascii
933  *
934  * returns:
935  * al = translated character
936  *
937  * all registers except %al preserved
938  *
939  */
940
941 translate_char:
942         pushw %bx
943         pushw %ds
944         pushw %cs
945         popw %ds                /* ds = cs */
946         testb $0x80, %al
947         jz translate_char_ctrl
948         andb $0x7f, %al
949         movw $high2ascii, %bx
950         xlatb
951 translate_char_ctrl:
952         cmpb $0x20, %al
953         jnc translate_char_tail
954         movw $ctrl2ascii, %bx
955         xlatb
956 translate_char_tail:
957         popw %ds
958         popw %bx
959         ret
960
961 /*
962  * translate_char_tty
963  *
964  * translate vga character in %al to ascii
965  * unless %al == 7, 8, 10, or 13 (bell, bs, lf, cr)
966  *
967  * returns:
968  * al = translated character
969  *
970  * all registers except %al preserved
971  *
972  */
973
974 translate_char_tty:
975         cmpb $0x07, %al                 /* bell */
976         jz translate_char_tty_tail
977         cmpb $0x08, %al                 /* backspace */
978         jz translate_char_tty_tail
979         cmpb $0x0a, %al                 /* LF */
980         jz translate_char_tty_tail
981         cmpb $0x0d, %al                 /* CR */
982         jz translate_char_tty_tail
983         call translate_char
984 translate_char_tty_tail:
985         ret
986
987 /*
988  * send_char
989  *
990  * send character 0 - 255 in %al out through serial port
991  * increment cursor position without control processing
992  *
993  * send_byte is used for data that isn't tracked
994  *
995  * send_char is used for text that should be tracked
996  * send_char outputs all characters as non-control chars
997  *
998  * returns:
999  * al = translated character
1000  *
1001  * all registers except %al preserved
1002  *
1003  */
1004
1005 send_char:
1006         call sgabioslog_save_char               /* save original char+pos */
1007         call translate_char
1008         jmp send_char_tty_out
1009         /* after ctrl translation, same as send_char_tty */
1010
1011 /*
1012  * send_char_tty
1013  *
1014  * send character 0 - 255 in %al out through serial port
1015  * increment cursor position *with* control processing
1016  * for bell, linefeed, cr, and backspace (others all printable)
1017  *
1018  * send_byte is used for data that isn't tracked
1019  *
1020  * send_char_tty is used for text that should be tracked
1021  *
1022  * returns:
1023  * al = translated character
1024  *
1025  * all registers except %al preserved
1026  *
1027  */
1028
1029
1030 /* send character 0 - 255 in %al out through serial port */
1031 /* increment cursor position with CR/LF/Backspace processing */
1032 send_char_tty:
1033         call sgabioslog_save_char               /* save original char+pos */
1034         call translate_char_tty
1035 send_char_tty_out:
1036         pushw %dx
1037         call update_serial_cursor
1038         call get_current_cursor         /* vga cursor in %dx */
1039         cmpb $0x0d, %al                 /* CR */
1040         jnz send_char_tty_nul           /* if not CR, check for NUL */
1041         orb %dl, %dl                    /* already at col 0? */
1042         jz send_char_tty_tail           /* no need to re-send CR */
1043 send_char_tty_nul:
1044         orb %al, %al                    /* %al == 0 ? (nul) */
1045         /* more than likely, we have NUL at this point because the caller */
1046         /* tried to read a char using int $0x10, %ah=8, and is trying */
1047         /* to re-output it with different attributes - for now send nothing */
1048         jz send_char_tty_tail
1049 send_char_tty_write:
1050         call memconsole_log_char        /* log character sent */
1051         call send_byte
1052         cmpb $0x07, %al                 /* bell */
1053         jz send_char_tty_tail           /* no cursor update for bell */
1054         cmpb $0x08, %al                 /* backspace */
1055         jz send_char_tty_backspace
1056         cmpb $0x0a, %al                 /* LF */
1057         jz send_char_tty_lf
1058         cmpb $0x0d, %al                 /* CR */
1059         jz send_char_tty_cr
1060         incb %dl
1061         jmp send_char_tty_tail
1062 send_char_tty_backspace:
1063         orb %dl, %dl
1064         jz send_char_tty_tail
1065         decb %dl
1066         jmp send_char_tty_tail
1067 send_char_tty_lf:
1068         incb %dh
1069         jmp send_char_tty_tail
1070 send_char_tty_cr:
1071         xorb %dl, %dl
1072 send_char_tty_tail:
1073         cmpb %cs:term_cols, %dl
1074         jc send_char_tty_check_rows
1075         movb %cs:term_cols, %dl
1076         decb %dl                /* dl = cols - 1 */
1077 send_char_tty_check_rows:
1078         cmpb %cs:term_rows, %dh
1079         jc send_char_tty_save_cursor
1080         movb %cs:term_rows, %dh
1081         decb %dh                /* dh = rows - 1 */
1082 send_char_tty_save_cursor:
1083         call set_current_cursor
1084         pushw %ds
1085         pushw $BDA_SEG
1086         popw %ds
1087         /* save current position as the serial terminal position */
1088         /* since a character was just output at that position */
1089         movw %dx, BDA_SERIAL_POS
1090         popw %ds
1091         popw %dx
1092         ret
1093
1094 /*
1095  * send_asciz_out
1096  *
1097  * send nul terminated string pointed to by %ds:%si
1098  * to serial port without text tracking
1099  *
1100  * indended to be used for multi-byte send_byte
1101  *
1102  * all registers preserved except flags
1103  */
1104
1105 send_asciz_out:
1106         pushw %ax
1107         pushw %si
1108         cld
1109 send_asciz_loop:
1110         lodsb
1111         test %al,%al
1112         jz send_asciz_end
1113         call send_byte
1114         jmp send_asciz_loop
1115 send_asciz_end:
1116         popw %si
1117         popw %ax
1118         ret
1119
1120 /*
1121  * send_string
1122  *
1123  * send cx chars in string pointed to by %ds:%si
1124  * to serial port with tty tracking
1125  * 
1126  * indended to be used for multi-byte send_char_tty
1127  *
1128  * all registers preserved except flags
1129  */
1130
1131 send_string:
1132         pushw %ax
1133         pushw %si
1134         cld
1135 send_string_loop:
1136         lodsb
1137         call send_char_tty
1138         loop send_string_loop
1139         popw %si
1140         popw %ax
1141         ret
1142
1143 /*
1144  * send_string
1145  *
1146  * send cx chars in string pointed to by %ds:%si
1147  * with interleaved attribute data
1148  * 
1149  * indended to be used for multi-byte send_char_tty
1150  * with interleaved vga attribute updates
1151  *
1152  * all registers preserved except flags
1153  */
1154
1155 send_attr_string:
1156         pushw %ax
1157         pushw %bx
1158         pushw %si
1159         cld
1160 send_attr_string_loop:
1161         lodsb
1162         call send_char_tty
1163         lodsb
1164         movb %al, %bl
1165         call send_attribute             /* send attribute in %bl */
1166         loop send_attr_string_loop
1167         popw %si
1168         popw %bx
1169         popw %ax
1170         ret
1171
1172 /*
1173  * send_number
1174  *
1175  * send ascii version of number in %al to serial port
1176  * 
1177  * intended for ansi cursor positions and attributes,
1178  * so cursor position is not tracked/updated
1179  *
1180  * all registers preserved except flags
1181  */
1182
1183 send_number:
1184         pushw %ax
1185         pushw %bx
1186         aam             /* ah = al/10, al = al mod 10 */
1187         movw %ax, %bx   /* bh = al/10, bl = al mod 10 */
1188         movb %bh, %al
1189         aam             /* ah = bh/10, al = bh mod 10 */
1190         movb %al, %bh   /* bh = 10s digit, bl = 1s digit */
1191         movb %ah, %al   /* ah = al = 100s digit */
1192         testb %al, %al  /* is there a 100s digit? */
1193         jz send_tens    /* move to tens if not */
1194         orb $0x30, %al  /* al = ascii value of digit */
1195         call send_byte
1196 send_tens:
1197         orb %bh, %ah    /* bh = 10s, ah = 100s digits */
1198         jz send_ones    /* non-zero = must send tens */
1199         movb %bh, %al   /* al = bh = 10s digit */
1200         orb $0x30, %al  /* al = ascii value of digit */
1201         call send_byte
1202 send_ones:
1203         movb %bl, %al   /* al = bl = 1s digit */
1204         orb $0x30, %al  /* al = ascii value of digit */
1205         call send_byte
1206         popw %bx
1207         popw %ax
1208         ret
1209
1210 /*
1211  * send_crlf
1212  *
1213  * send CRLF to serial port
1214  * 
1215  * FIXME: used at vga init and for scrolling terminal
1216  * so position is not tracked.  Callers of this routine
1217  * predate the code that does smart tty/cursor output.
1218  *
1219  * Callers should probably be changed to use those
1220  * routines or send_crlf changed to use them and
1221  * terminal scrolling fixed to use linefeed only.
1222  *
1223  * all registers preserved except flags
1224  */
1225
1226 send_crlf:
1227         pushw %ax
1228         movb $0x0d, %al
1229         call send_byte
1230         movb $0x0a, %al
1231         call send_byte
1232         popw %ax
1233         ret
1234 /*
1235  * send_ansi_csi
1236  *
1237  * send ESCAPE [ to serial port
1238  * 
1239  * output is not tracked since these are control sequences
1240  *
1241  * all registers preserved except flags
1242  */
1243
1244 send_ansi_csi:                  /* transmit ESC [ */
1245         pushw %ax
1246         movb $0x1b, %al         /* escape */
1247         call send_byte
1248         movb $0x5b, %al         /* [ */
1249         call send_byte
1250         popw %ax
1251         ret
1252 /*
1253  * send_ansi_csi_2num
1254  *
1255  * send ESC [ %dh ; %dl to serial port
1256  * 
1257  * since both position and attribute updates generally have
1258  * two parameters, this function converts values in dx to
1259  * two ascii numbers.  It's expected that the caller will
1260  * output the final trailing H or m or whatever is required.
1261  *
1262  * output is not tracked since these are control sequences
1263  *
1264  * all registers preserved except flags
1265  */
1266
1267 send_ansi_csi_2num:
1268 /* send ESC [ %dh ; %dl */
1269         pushw %ax
1270         call send_ansi_csi      /* esc [ */
1271         movb %dh, %al
1272         call send_number
1273         movb $0x3b, %al         /* semicolon */
1274         call send_byte
1275         movb %dl, %al
1276         call send_number
1277         popw %ax
1278         ret
1279
1280 /*
1281  * send_ansi_cursor_pos
1282  *
1283  * send ESC [ %dh+1 ; %dl+1 to serial port to position
1284  * cursor
1285  * 
1286  * since both position and attribute updates generally have
1287  * two parameters, this function converts values in dx to
1288  * two ascii numbers, after adding 1 to both dh and dl.
1289  *
1290  * output is not tracked since this is a control sequence
1291  *
1292  * all registers preserved except flags
1293  */
1294
1295 send_ansi_cursor_pos:
1296         pushw %ax
1297         pushw %dx
1298         addw $0x0101, %dx       /* dh += 1, dl += 1 */
1299         call send_ansi_csi_2num /* send esc [ %dh+1;%dl+1 */
1300         movb $0x48, %al         /* H */
1301         call send_byte
1302         popw %dx
1303         popw %ax
1304         ret
1305
1306 /*
1307  * send_attribute
1308  *
1309  * send ansi attribute change ESC [ 4x ; 3y ; (1|22)m
1310  * if the attribute has changed since last sent (stored in bda)
1311  * 
1312  * output is not tracked since this is a control sequence
1313  *
1314  * all registers preserved except flags
1315  */
1316
1317 send_attribute:
1318         andb $0x7f, %bl         /* ansi has no bright bg */
1319         pushw %ds
1320         pushw %es
1321         pushw %ax
1322         pushw %bx
1323         pushw %dx
1324         pushw $BDA_SEG
1325         popw %es                /* es = 0x40 */
1326         pushw %cs
1327         popw %ds                /* ds = cs */
1328         cmpb %es:BDA_COLOR_VAL, %bl
1329         jz send_attribute_tail
1330         cmpb $0x07, %bl         /* is it white on black? */
1331         jnz send_attribute_color
1332         /* for white on black, send esc [ m */
1333         call send_ansi_csi
1334         jmp send_attribute_m    /* send the m, return */
1335 send_attribute_color:
1336         movb %bl, %ah           /* ah = attribute */
1337         movw $colortable, %bx
1338         movb %ah, %al
1339         andb $7, %al            /* al = fg attr */
1340         xlatb                   /* al = fg ansi num */
1341         movb %al, %dl           /* dl = fg ansi num */
1342         movb %ah, %al
1343         shrb $4, %al            /* al = bg attr */
1344         xlatb                   /* al = bg ansi num */
1345         movb %al, %dh           /* dh = bg ansi num */
1346         addw $0x281e, %dx       /* 3x=setfg, 4x=setbg */
1347         call send_ansi_csi_2num
1348         movb $0x3b, %al         /* semicolon */
1349         call send_byte
1350         shlb $4, %ah            /* bright text? */
1351         sets %al                /* if bit 7, al = 1 */
1352         js send_attribute_intensity
1353         movb $22, %al           /* 22 = normal intensity */
1354 send_attribute_intensity:
1355         call send_number        /* either 22 or 1 */
1356 send_attribute_m:
1357         movb $0x6d, %al         /* m */
1358         call send_byte
1359 send_attribute_tail:
1360         popw %dx
1361         popw %bx
1362         /* mark attribute in %bl the current one */
1363         movb %bl, %es:BDA_COLOR_VAL
1364         popw %ax
1365         popw %es
1366         popw %ds
1367         ret
1368
1369 /*
1370  * serial_get_input
1371  *
1372  * common code for both interrupt-driven and non-interrupt
1373  * driven serial input.   Called only when LSR bit 1 is set.
1374  *
1375  * No parameters, no return values
1376  *
1377  * Preserves all registers
1378  */
1379
1380 serial_get_input:
1381         pushf
1382         cli     /* be paranoid about int 9h happening during update */
1383         pushaw
1384         pushw %ds
1385         /* next char input buffer is at 0x40:0x1c */
1386         pushw $BDA_SEG
1387         popw %ds                /* es = 0x40 */
1388         call get_byte           /* next scancode/byte in %ax */
1389         cmpb $0x1b, %al         /* look for escape */
1390         jnz serial_gotkey       /* not escape, don't look for more bytes */
1391         call get_multibyte      /* look for any chars after escape */
1392 serial_gotkey:
1393         movw KBD_TAIL, %bx      /* bx = keyboard tail pointer */
1394         movw %ax, (%bx)         /* store key in buffer */
1395         addw $2, %bx            /* point to next location */
1396         cmpw $KBD_BUF_END, %bx  /* did the buffer wrap? */
1397         jb kbd_buf_no_wrap
1398         movw $KBD_BUF_START, %bx
1399 kbd_buf_no_wrap:
1400         movw %bx, KBD_TAIL      /* update tail pointer to show key */
1401         popw %ds
1402         popaw
1403         popf
1404         ret
1405
1406 /*
1407  * irq3_isr
1408  *
1409  * entry point for irq 3 / int 0x0b / exception 11
1410  *
1411  * Called when COM2 or COM4 have characters pending
1412  *
1413  * The segment not present exception should never happen
1414  * in real mode 16-bit code like this, but just to be safe,
1415  * if this interrupt is invoked and no characters are
1416  * pending on the port found in serial_port_base_address,
1417  * this routine will chain to the original handler.
1418  *
1419  * If characters are found pending, they will be processed
1420  * and control returned via iret.
1421  */
1422
1423 irq3_isr:
1424 #if 0
1425         pushw %ax
1426         pushw %dx
1427         /* placeholder, this shouldn't ever happen */
1428         /* no interrupts are configured outside COM1 */
1429         call get_serial_lsr     /* get serial lsr in %al */
1430         jz chain_irq3           /* no port present... */
1431         testb $1, %al   /* check bit 0 of LSR for data available */
1432         jz chain_irq3           /* no input waiting */
1433         call serial_get_input   /* get input and stuff kbd buffer */
1434         movb $0x20, %al
1435         outb %al, $0x20         /* send non-specific EOI */
1436         popw %dx
1437         popw %ax
1438         iret
1439 chain_irq3:
1440         popw %dx
1441         popw %ax
1442 #endif
1443         jmp do_old_irq3
1444
1445 /*
1446  * irq4_isr
1447  *
1448  * entry point for irq 4 / int 0x0c / exception 12
1449  *
1450  * Called when COM1 or COM3 have characters pending
1451  *
1452  * The stack fault exception may occur if code attempts to
1453  * read from sp:0xffff, so if this interrupt is invoked and
1454  * no characters are pending on the port found in
1455  * serial_port_base_address, this routine will chain to the
1456  * original handler.
1457  *
1458  * If characters are found pending, they will be processed
1459  * and control returned via iret.
1460  */
1461
1462 irq4_isr:
1463 #if 0
1464         pushw %ax
1465         pushw %dx
1466         call get_serial_lsr     /* get serial lsr in %al */
1467         jz chain_irq4           /* no port present... */
1468         testb $1, %al   /* check bit 0 of LSR for data available */
1469         jz chain_irq4           /* no input waiting */
1470         call serial_get_input   /* get input and stuff kbd buffer */
1471         movb $0x20, %al
1472         outb %al, $0x20         /* send non-specific EOI */
1473         popw %dx
1474         popw %ax
1475         iret
1476 chain_irq4:
1477         popw %dx
1478         popw %ax
1479 #endif
1480         jmp do_old_irq4
1481
1482 /*
1483  * int14h_isr
1484  *
1485  * entry point for int 14h
1486  *
1487  */
1488 int14h_isr:
1489         pushaw
1490         movw %sp, %bp
1491         addw $16, %bp           /* bp points to return address */
1492         orb %ah, %ah            /* fn 0x00, initialize port */
1493         jz int14h_init_port
1494         cmpb $0x04, %ah         /* fn 0x04, extended intialize */
1495         jnz chain_isr14h
1496 int14h_init_port:
1497         /* check for init port = current port */
1498         pushw %ds
1499         pushw $BDA_SEG
1500         popw %ds                /* ds = 0x40 */
1501         movw %dx, %bx           /* bx = port number */
1502         shlw $1, %bx            /* bx = port number * 2 */
1503         andw $7, %bx            /* bx = offset of serial io address in bda */
1504         movw (%bx), %cx         /* cx = io address of port to init */
1505         popw %ds                /* restore original ds */
1506         cmpw %cx, %cs:serial_port_base_address
1507         jnz chain_isr14h        /* if different, don't get in the way */
1508         /* init port == current port */
1509         pushw %ds
1510         /* LILO 22.6 HACK STARTS HERE */
1511         movw (%bp), %bx         /* return address for int 14h call */
1512         movw 2(%bp), %ds        /* return segment for int 14h call */
1513         cmpl $0x4f4c494c, 0x06  /* does segment have lilo signature? */
1514         jnz int14h_init_tail    /* not lilo, bail on hack */
1515         cmpw $0x0616, 0x0a      /* does version match lilo 22.6? */
1516         jnz int14h_init_tail    /* not known lilo release, bail on hack */
1517         movb $0, 0x12           /* set lilo com port = 0 */
1518         movl $0x90c3585a, (%bx) /* return code= pop dx;pop ax;ret;nop */
1519         /* now lilo 22.6's own serial out is permanently disabled */
1520         /* this prevents double-character output from int10h + serial */
1521         /* this also prevents lilo from stealing serial input chars */
1522         /* END LILO 22.6 HACK */
1523 int14h_init_tail:
1524         popw %ds
1525         popaw
1526         pushw %dx               /* get_serial_lsr trashes %dx */
1527         call get_serial_lsr     /* return serial status in %al */
1528         xorb %ah, %ah           /* return serial status in %ax */
1529         popw %dx                /* restore %dx */
1530         iret
1531 chain_isr14h:
1532         popaw
1533         jmp do_old_int14h
1534
1535 /*
1536  * int16h_isr
1537  *
1538  * entry point for int 16h
1539  *
1540  * keyboard characters are usually retrieved by calling
1541  * int 16h, generally placed in the keyboard buffer by
1542  * irq 1 (int 9h).  Poll serial port for new data before
1543  * chaining to int 16h to fake irq 1 behavior
1544  *
1545  * all registers preserved except flags (later iret will restore)
1546  * bda updated with a new keypress if available
1547  *
1548  * FIXME: handle multi-byte keypresses like cursor up/down
1549  * to send proper scancodes for navigating lilo menus
1550  */
1551
1552 int16h_isr:
1553         pushw %ax
1554         pushw %dx
1555         /* each time int 16h is invoked, fake an int 9h */
1556         /* except read the serial input buffer */
1557         /* then chain to the original int 16h for processing */
1558         call get_serial_lsr
1559         jz chain_isr16h         /* no port present... */
1560         testb $1, %al   /* check bit 0 of LSR for data available */
1561         jz chain_isr16h         /* no input waiting */
1562         call serial_get_input   /* get input and stuff kbd buffer */
1563         /* for now, leave remaining chars pending in serial fifo */
1564         /* int 16h callers only get one char at a time anyway */
1565 chain_isr16h:
1566         popw %dx
1567         popw %ax
1568         jmp do_old_int16h
1569
1570 /*
1571  * update serial_cursor
1572  *
1573  * figure out where the cursor was, and where it's going
1574  * use the minimal amount of serial output to get it there
1575  * input: vga cursor and serial cursor positions stored in BDA
1576  *
1577  * all registers preserved except flags
1578  * bda updated with new position for serial console cursor
1579  */
1580 update_serial_cursor:
1581         pushw %ax
1582         pushw %bx
1583         pushw %dx
1584         pushw %ds
1585         pushw $BDA_SEG
1586         popw %ds                        /* ds = 0x40 */
1587         call get_current_cursor         /* dh = row, dl = col */
1588         movw BDA_SERIAL_POS, %bx        /* bh = row, bl = col */
1589         subb %dl, %bl                   /* -col update */
1590         negb %bl                        /* col update */
1591         subb %dh, %bh                   /* -row update */
1592         negb %bh                        /* row update */
1593         /* handle a few special movement cases */
1594         /* cr, lf, bs, bs+bs, space, else send full ansi position */
1595         orb %dl, %dl                    /* column zero? */
1596         jnz update_serial_cursor_lf
1597         movb $0x0d, %al                 /* CR */
1598         call send_byte
1599         xorb %bl, %bl                   /* mark no diff in col */
1600 update_serial_cursor_lf:
1601         cmpb $1, %bh                    /* +1 row? */
1602         jnz update_serial_cursor_bs
1603         movb $0x0a, %al                 /* LF */
1604         call send_byte
1605         xorb %bh, %bh                   /* mark no diff in row */
1606 update_serial_cursor_bs:
1607         cmpb $-1, %bl                   /* one char back */
1608         jz update_serial_cursor_one_bs
1609         cmpb $-2, %bl                   /* two chars back */
1610         jnz update_serial_cursor_space  /* check for space */
1611         movb $0x08, %al                 /* BS */
1612         call send_byte
1613 update_serial_cursor_one_bs:
1614         movb $0x08, %al                 /* BS */
1615         call send_byte
1616         xorb %bl, %bl                   /* mark no diff in col */
1617 update_serial_cursor_space:
1618         cmpb $1, %bl                    /* one char forward */
1619         jnz update_serial_cursor_up
1620         movb $0x20, %al                 /* space */
1621         call send_byte
1622         xorb %bl, %bl                   /* mark no diff in col */
1623 update_serial_cursor_up:
1624         cmpb $-1, %bh                   /* -1 row? */
1625         jnz update_serial_cursor_full   /* do full ansi pos update */
1626         call send_ansi_csi              /* send ESC [ A (cursor up) */
1627         movb $0x41, %al                 /* A */
1628         call send_byte
1629         xorb %bh, %bh                   /* mark no diff in row */
1630 update_serial_cursor_full:
1631         orw %bx, %bx                    /* diff = 0? */
1632         jz update_serial_cursor_done
1633         call send_ansi_cursor_pos       /* set cursor pos from dh,dl */
1634 update_serial_cursor_done:
1635         movw %dx, BDA_SERIAL_POS
1636         popw %ds
1637         popw %dx
1638         popw %bx
1639         popw %ax
1640         ret
1641
1642 /*
1643  * write_teletype
1644  *
1645  * handle int 10h, function 0eh
1646  *
1647  * ah = 0x0e write teletype character
1648  * al = character ascii code
1649  * bh = display page number
1650  *
1651  * all registers except %al preserved
1652  * caller will restore all registers
1653  */
1654
1655 write_teletype:
1656         pushw %bx
1657         movb $0x07, %bl         /* black bg, white fg */
1658         call send_attribute
1659         popw %bx
1660         call send_char_tty
1661         ret
1662
1663 /*
1664  * write_attr_char
1665  *
1666  * handle int 10h, function 09h
1667  *
1668  * ah = 0x09 write attribute/character at current cursor position
1669  * al = character ascii code
1670  * bh = display page number
1671  * bl = character attribute
1672  * cx = repetition count
1673  *
1674  * does not update cursor position
1675  * all registers except %cx and %al preserved
1676  * caller will restore all registers
1677  */
1678
1679 write_attr_char:
1680         call send_attribute     /* send attribute in %bl */
1681         jmp write_char_common
1682
1683 /*
1684  * write_char
1685  *
1686  * handle int 10h, function 0ah
1687  *
1688  * ah = 0x0a write character at current cursor position
1689  * al = character ascii code
1690  * bh = display page number
1691  * cx = repetition count
1692  *
1693  * does not update cursor position
1694  * all registers except %cx and %al preserved
1695  * caller will restore all registers
1696  */
1697
1698 write_char:
1699         pushw %bx
1700         movb $0x07, %bl         /* black bg, white fg */
1701         call send_attribute
1702         popw %bx
1703 write_char_common:
1704         call get_current_cursor
1705         call send_char
1706         /* make cx=0 and cx=1 only output one char */
1707         cmpw $1, %cx
1708         jbe write_char_tail
1709         decw %cx
1710         jmp write_char
1711 write_char_tail:
1712         /* put cursor back where it was on entry */
1713         call set_current_cursor
1714         ret
1715
1716 /*
1717  * write_string
1718  *
1719  * handle int 10h, function 13h
1720  *
1721  * ah = 0x13 write character at current cursor position
1722  * al = 0, data = char, ..., no cursor update
1723  * al = 1, data = char, ..., cursor at end of string
1724  * al = 2, data = char+attr, ..., no cursor update
1725  * al = 3, data = char+attr, ..., cursor at end of string
1726  * bh = display page number
1727  * bl = character attribute for all chars (if al = 0 or 1)
1728  * cx = characters in string (attributes don't count)
1729  * dh = cursor row start 
1730  * dl = cursor column start
1731  * es:bp = pointer to source text string in memory
1732  *
1733  * all registers preserved except flags
1734  * caller will restore all registers
1735  */
1736 write_string:
1737         call set_cursor_position
1738         pushw %es
1739         pushw %es
1740         popw %ds                /* ds = es */
1741         movw %bp, %si           /* si = bp */
1742         testb $2, %al
1743         jnz write_attr_string
1744         call send_attribute     /* send attribute in %bl */
1745         call send_string        /* plaintext out */
1746         jmp write_string_update_cursor
1747 write_attr_string:
1748         call send_attr_string   /* text+attrib out */
1749 write_string_update_cursor:
1750         testb $1, %al           /* cursor update? */
1751         jnz write_string_tail   /* yes?  already happened */
1752         /* restore entry cursor position if no update */
1753         call set_cursor_position
1754 write_string_tail:
1755         popw %es
1756         ret
1757
1758 /*
1759  * set_cursor_position
1760  *
1761  * handle int 10h, function 02h
1762  *
1763  * ah = 0x02 set cursor position
1764  * bh = display page number
1765  * dh = cursor row
1766  * dl = cursor column
1767  *
1768  * update bda cursor position with value in %dx
1769  * serial console cursor only updated on text output
1770  * this routine also called by set_current_cursor
1771  * which won't bother setting ah = 2
1772  *
1773  * all registers preserved except flags
1774  */
1775
1776 set_cursor_position:
1777         pushw %ax
1778         pushw %ds
1779         pushw $BDA_SEG
1780         popw %ds                /* ds = 0x40 */
1781         movzbw %bh, %ax         /* ax = page number */
1782         andb $0x07, %al         /* prevent invalid page number */
1783         shlb $1, %al            /* calculate word offset */
1784         addb $BDA_CURSOR_BUF, %al       /* ax = cursor save offset */
1785         movw %ax, %bx           /* bx = cursor save offset */
1786         movw %dx, (%bx)         /* save new cursor value */
1787         popw %ds
1788         popw %ax
1789         ret
1790
1791 /*
1792  * set_current_cursor
1793  *
1794  * get current display page number and call set_cursor_positon
1795  * to store the row/column value in dx to the bda
1796  *
1797  * all registers preserved except flags
1798  */
1799
1800 set_current_cursor:
1801         pushw %ds
1802         pushw %bx
1803         pushw $BDA_SEG
1804         popw %ds                /* ds = 0x40 */
1805         movb BDA_ACTIVE_PAGE, %bh
1806         call set_cursor_position
1807         popw %bx
1808         popw %ds
1809         ret
1810
1811 /*
1812  * get_cursor_common
1813  *
1814  * read cursor position for page %bh from bda into %dx
1815  *
1816  * returns:
1817  * dh = cursor row
1818  * dl = cursor column
1819  * ch = cursor start scanline 
1820  * cl = cursor end scanline 
1821  *
1822  * all registers except %dx, %cx preserved
1823  */
1824 get_cursor_common:
1825         pushw %bx
1826         pushw %ds
1827         pushw $BDA_SEG
1828         popw %ds                /* ds = 0x40 */
1829         movzbw %bh, %bx         /* dx = current page */
1830         andb $7, %bl
1831         shlb $1, %bl
1832         addb $BDA_CURSOR_BUF, %bl
1833         movw (%bx), %dx         /* get cursor pos */
1834         movw BDA_CURSOR_SCAN, %cx
1835         popw %ds
1836         popw %bx
1837         ret
1838
1839 /*
1840  * get_current_cursor
1841  *
1842  * read cursor position for current page from bda into %dx
1843  *
1844  * returns:
1845  * dh = cursor row
1846  * dl = cursor column
1847  *
1848  * all registers except %dx preserved
1849  */
1850
1851 get_current_cursor:
1852         pushw %ds
1853         pushw %bx
1854         pushw %cx
1855         pushw $BDA_SEG
1856         popw %ds                /* ds = 0x40 */
1857         movb BDA_ACTIVE_PAGE, %bh
1858         call get_cursor_common
1859         popw %cx
1860         popw %bx
1861         popw %ds
1862         ret
1863
1864 /*
1865  * get_cursor_position
1866  *
1867  * handle int 10h, function 03h
1868  *
1869  * ah = 0x02 get cursor position
1870  * bh = display page number
1871  *
1872  * returns:
1873  * ax = 0
1874  * ch = cursor start scanline
1875  * ch = cursor end scanline
1876  * dh = cursor row
1877  * dl = cursor column
1878  *
1879  * all registers except %ax, %cx, %dx preserved
1880  */
1881
1882 get_cursor_position:
1883         call bail_if_vga_attached       /* does not return if vga attached */
1884         popw %ax        /* not chaining, pop fake return address */
1885         popw %dx        /* not chaining, pop saved cursor position */
1886         popaw   /* not chaining to old int 10h, pop saved state */
1887         call get_cursor_common
1888         xorw %ax, %ax
1889         iret
1890
1891 /*
1892  * return_current_video_state
1893  *
1894  * handle int 10h, function 0fh
1895  *
1896  * ah = 0x0f return current video state
1897  *
1898  * returns:
1899  * ah = number of columns on screen (from 40:4a)
1900  * al = current video mode setting (from 40:49)
1901  * bh = active display page number (from 40:62)
1902  *
1903  * all registers except %ax and %bh preserved
1904  */
1905
1906 read_current_video_state:
1907         call bail_if_vga_attached       /* does not return if vga attached */
1908         popw %ax        /* not chaining, pop fake return address */
1909         popw %dx        /* not chaining, pop saved cursor position */
1910         popaw   /* not chaining to old int 10h, pop saved state */
1911         pushw %ds
1912         pushw $BDA_SEG
1913         popw %ds                /* ds = 0x40 */
1914         movb BDA_COLS, %ah
1915         movb BDA_MODE_NUM, %al
1916         movb BDA_ACTIVE_PAGE, %bh
1917         popw %ds
1918         iret
1919
1920 /*
1921  * read_attr_char
1922  *
1923  * handle int 10h, function 08h
1924  *
1925  * ah = 0x08 read character/attribute from screen
1926  *
1927  * returns:
1928  * ah = attribute at current cursor position
1929  * al = character read from current cursor position
1930  *
1931  * all registers preserved except %ax and flags
1932  */
1933
1934 read_attr_char:
1935         call bail_if_vga_attached       /* does not return if vga attached */
1936         popw %ax        /* not chaining, pop fake return address */
1937         popw %dx        /* not chaining, pop saved cursor position */
1938         popaw   /* not chaining to old int 10h, pop saved state */
1939         pushw %ds
1940         pushw $BDA_SEG
1941         popw %ds                /* ds = 0x40 */
1942         movb BDA_COLOR_VAL, %ah /* return last color value */
1943         call sgabioslog_get_char
1944         popw %ds
1945         iret
1946
1947 /*
1948  * set_video_mode
1949  *
1950  * handle int 10h, function 00h
1951  *
1952  * ah = 0x00 set video mode
1953  * al = video mode
1954  *
1955  * unless bit 7 of al = 1, setting mode clears screen
1956  *
1957  * all registers preserved except %bh, %dx, flags
1958  */
1959
1960 set_video_mode:
1961         testb $0x80, %al        /* preserve screen flag? */
1962         jnz set_video_mode_tail
1963         call send_ansi_csi
1964         movb $0x32, %al         /* 2 */
1965         call send_byte
1966         movb $0x4a, %al         /* J */
1967         call send_byte
1968 set_video_mode_tail:
1969         movb $0x07, %bl         /* white on black text */
1970         call send_attribute     /* send attribute in %bl */
1971         /* set cursor position to 0,0 */
1972         xorb %bh, %bh           /* page 0 */
1973         xorw %dx, %dx
1974         jmp set_cursor_position
1975
1976 /*
1977  * scroll_page_up
1978  *
1979  * handle int 10h, function 06h
1980  *
1981  * ah = 0x06 scroll current page up
1982  * al = scroll distance in character rows (0 blanks entire area)
1983  * bh = attribute to used on blanked lines
1984  * ch = top row (upper left corner) of window
1985  * cl = left-most column (upper left corner) of window
1986  * dh = bottom row (lower right corner) of window
1987  * dl = right-most column (lower right corner) of window
1988  *
1989  * all registers preserved except flags
1990  */
1991
1992 scroll_page_up:
1993         pushw %si
1994         pushw %dx
1995         call get_current_cursor /* save current cursor */
1996         movw %dx, %si           /* si = vga cursor pos */
1997         popw %dx
1998         cmpb $0, %al            /* al = 0 = clear window */
1999         jz scroll_common_clear
2000         pushw %ax
2001         call send_ansi_csi      /* CSI [ %al S */
2002         call send_number
2003         movb $0x53, %al         /* S */
2004         call send_byte
2005         popw %dx
2006         popw %si
2007         ret
2008
2009 /*
2010  * scroll_common_clear
2011  *
2012  * common tail for up/down scrolls to clear window specified
2013  * in %cx and %dx.
2014  *
2015  * stack should contain saved copy of si
2016  * si = original vga cursor position on service entry
2017  * 
2018  * bh = attribute to used on blanked lines
2019  * ch = top row (upper left corner) of window
2020  * cl = left-most column (upper left corner) of window
2021  * dh = bottom row (lower right corner) of window
2022  * dl = right-most column (lower right corner) of window
2023  */
2024 scroll_common_clear:
2025         pushw %ax
2026         xchgb %bl, %bh          /* bl = attribute, bh = old bl */
2027         call send_attribute     /* send attribute in %bl */
2028         xchgb %bl, %bh          /* restore bx */
2029         pushw %ds
2030         pushw $BDA_SEG
2031         popw %ds                /* ds = 0x40 */
2032         /* check to see if region is full screen, and attribute default */
2033         orw %cx, %cx            /* is top left 0,0? */
2034         jnz scroll_common_window        /* no, handle window */
2035         cmpb $0x07, %bh         /* is attribute white on black? */
2036         jnz scroll_common_window        /* no, must write spaces */
2037 #ifdef LILO_CLEAR_WORKAROUND_NOT_REQUIRED
2038         cmpb %cs:term_cols, %dl /* is right less than cols ? */
2039         jc scroll_common_window /* if so, handle window */
2040         cmpb %cs:term_rows, %dh /* is bottom less than rows ? */
2041         jc scroll_common_window /* if so, handle window */
2042 #endif
2043         /* safe to send standard clear screen sequence */
2044         call send_ansi_csi      /* send ESC [ */
2045         movb $0x32, %al         /* 2 */
2046         call send_byte
2047         movb $0x4a, %al         /* J */
2048         call send_byte
2049         jmp scroll_common_tail
2050 scroll_common_window:
2051         pushw %dx
2052         movw %cx, %dx           /* dx = upper right */
2053         call set_current_cursor
2054         popw %dx
2055         pushw %cx
2056         /* setup cx with count of chars to clear per row */
2057         xorb %ch, %ch
2058         negb %cl
2059         addb %dl, %cl           /* cl = dl - cl */
2060         incb %cl                /* start = end col = clear 1 col */
2061         cmpb %cs:term_cols, %cl /* is count < cols? */
2062         jc scroll_common_row_ok /* if so then skip limit */
2063         movb %cs:term_cols, %cl /* limit count to cols */
2064 scroll_common_row_ok:
2065         jz scroll_common_row_done       /* count == 0 ? */
2066         movb $0x20, %al         /* space */
2067 scroll_common_space_loop:
2068         call send_char
2069         loop scroll_common_space_loop   /* send cx spaces */
2070 scroll_common_row_done:
2071         popw %cx
2072         incb %ch                /* top left now next row */
2073         cmpb %dh, %ch
2074         jbe scroll_common_window        /* do next row */
2075 scroll_common_tail:
2076         popw %ds
2077         popw %ax
2078         pushw %dx
2079         movw %si, %dx           /* dx = saved vga cursor pos */
2080         call set_current_cursor /* restore saved cursor */
2081         popw %dx
2082         popw %si
2083         ret
2084
2085 /*
2086  * scroll_page_down
2087  *
2088  * handle int 10h, function 07h
2089  *
2090  * ah = 0x07 scroll current page down
2091  * al = scroll distance in character rows (0 blanks entire area)
2092  * bh = attribute to used on blanked lines
2093  * ch = top row (upper left corner) of window
2094  * cl = left-most column (upper left corner) of window
2095  * dh = bottom row (lower right corner) of window
2096  * dl = right-most column (lower right corner) of window
2097  *
2098  * FIXME: this routine doesn't handle windowing, it currently
2099  * only handles one line screen scrolls and erasing entire screen
2100  *
2101  * all registers preserved except flags
2102  */
2103
2104 scroll_page_down:
2105         pushw %si
2106         pushw %dx
2107         call get_current_cursor /* save current cursor */
2108         movw %dx, %si           /* si = vga cursor pos */
2109         popw %dx
2110         cmpb $0, %al            /* al = 0 = clear window */
2111         jz scroll_common_clear
2112         pushw %ax
2113         call send_ansi_csi      /* CSI [ %al T */
2114         call send_number
2115         movb $0x54, %al         /* T */
2116         call send_byte
2117         popw %dx
2118         popw %si
2119         ret
2120
2121 /*
2122  * bail_if_vga_attached
2123  *
2124  * Check for vga installed, if not, return to caller.
2125  * If so, pop return address, return to chain_isr_10h
2126  *
2127  * expected that routine calling this one has chain_isr_10h
2128  * as the next item on the stack
2129  *
2130  * all registers except flags and sp preserved
2131  */
2132
2133 bail_if_vga_attached:
2134         cmpw $0xc000, %cs:old_int10h_seg        /* vga attached? */
2135         jnz bail_tail   /* if not, don't modify stack */
2136         addw $2, %sp    /* else drop first return address */
2137 bail_tail:
2138         ret             /* return to caller or chain_isr_10h */
2139
2140 /*
2141  * int10h_isr
2142  *
2143  * entry point for int 10h
2144  *
2145  * save all registers, force return to chain to previous int10h isr
2146  * decide which function in ah needs to be dispatched
2147  *
2148  * ah = 0x00 set mode
2149  * ah = 0x01 set cursor type
2150  * ah = 0x02 set cursor position
2151  * ah = 0x03 read cursor position
2152  * ah = 0x04 read light pen position
2153  * ah = 0x05 set active display page
2154  * ah = 0x06 scroll active page up
2155  * ah = 0x07 scroll active page down
2156  * ah = 0x08 read attribute/character at cursor
2157  * ah = 0x09 write attribute/character at cursor
2158  * ah = 0x0a write character at cursor position
2159  * ah = 0x0b set color palette
2160  * ah = 0x0c write pixel
2161  * ah = 0x0d read pixel
2162  * ah = 0x0e write teletype
2163  * ah = 0x0f read current video state
2164  * ah = 0x10 set individual palette registers
2165  * ah = 0x11 character generation (font control/info)
2166  * ah = 0x12 alternate select (video control/info)
2167  * ah = 0x13 write string
2168  * ah = 0x1a read/write display combination code
2169  * ah = 0x1b return functionality/state information
2170  * ah = 0x1c save/restore video state
2171  * ah = 0x4f vesa bios calls
2172  * all registers preserved except flags (later iret will restore)
2173  */
2174
2175 int10h_isr:
2176         pushaw
2177         call get_current_cursor
2178         pushw %dx               /* save current cursor */
2179         pushw %bp               /* need bp for indexing off stack */
2180         movw %sp, %bp           /* bp = sp */
2181         movw 14(%bp), %dx       /* restore dx value from earlier pushaw */
2182         popw %bp                /* restore old bp */
2183         pushw $chain_isr10h     /* force return to chain_isr10h */
2184         testb %ah, %ah
2185         jnz int10h_02
2186         jmp set_video_mode
2187 int10h_02:
2188         cmpb $0x02, %ah
2189         jnz int10h_03
2190         jmp set_cursor_position
2191 int10h_03:
2192         cmpb $0x03, %ah
2193         jnz int10h_06
2194         jmp get_cursor_position
2195 int10h_06:
2196         cmpb $0x06, %ah
2197         jnz int10h_07
2198         jmp scroll_page_up
2199 int10h_07:
2200         cmpb $0x07, %ah
2201         jnz int10h_08
2202         jmp scroll_page_down
2203 int10h_08:
2204         cmpb $0x08, %ah
2205         jnz int10h_09
2206         jmp read_attr_char
2207 int10h_09:
2208         cmpb $0x09, %ah
2209         jnz int10h_0a
2210         jmp write_attr_char
2211 int10h_0a:
2212         cmpb $0x0a, %ah
2213         jnz int10h_0e
2214         jmp write_char
2215 int10h_0e:
2216         cmpb $0x0e, %ah
2217         jnz int10h_0f
2218         jmp write_teletype
2219 int10h_0f:
2220         cmpb $0x0f, %ah
2221         jnz int10h_13
2222         jmp read_current_video_state
2223 int10h_13:
2224         cmpb $0x13, %ah
2225         jnz int10h_default
2226         jmp write_string
2227 int10h_default:
2228         popw %ax                /* pop chain_isr10h return address */
2229 chain_isr10h:
2230         popw %dx                /* pop saved cursor */
2231         cmpw $0xc000, %cs:old_int10h_seg        /* vga attached? */
2232         jnz chain_post_cursor   /* if not, don't restore the cursor */
2233         call set_current_cursor /* restore cursor if video card connected */
2234 chain_post_cursor:
2235         popaw
2236         jmp do_old_int10h
2237
2238 /*
2239  * pnp_sga_init
2240  *
2241  * handle PnP initialization of option rom
2242  *
2243  * es:di = pointer to PnP structure
2244  * ax = indication as to which vectors should be hooked
2245  *      by specifying th type of boot device this has
2246  *      been selected as
2247  *      bit 7..3= reserved(0)
2248  *      bit 2   = 1 = connect as IPL (int 13h)
2249  *      bit 1   = 1 = connect as primary video (int 10h)
2250  *      bit 0   = 1 = connect as primary input (int 9h)
2251  * bx = card select number (probably 0xffff)
2252  * dx = read data port address (probably 0xffff)
2253  *
2254  * return:
2255  * ax = initialization status
2256  * bit 8    = 1 = IPL device supports int 13h block dev format
2257  * bit 7    = 1 = Output device supports int 10h char output
2258  * bit 6    = 1 = Input device supports int 9h char input
2259  * bit 5..4 = 00 = no IPL device attached
2260  *            01 = unknown whether or not IPL device attached
2261  *            10 = IPL device attached (RPL devices have connection)
2262  *            11 = reserved
2263  * bit 3..2 = 00 = no display device attached
2264  *            01 = unknown whether or not display device attached
2265  *            10 = display device attached
2266  *            11 = reserved
2267  * bit 1..0 = 00 = no input device attached
2268  *            01 = unknown whether or not input device attached
2269  *            10 = input device attached
2270  *            11 = reserved
2271  *
2272  * all registers preserved except %ax
2273  */
2274
2275 pnp_sga_init:
2276         /* FIXME: this is *wrong* -- init only what bios says to init */
2277         movw $0xca, %ax /* 0xca = attached int 10h, 9h display, input */
2278
2279 /*
2280  * sga_init
2281  *
2282  * legacy option rom entry point
2283  *
2284  * all registers preserved
2285  */
2286
2287 sga_init:
2288         /* this is probably paranoid about register preservation */
2289         pushfw
2290         cli                     /* more paranoia */
2291         pushaw
2292         pushw %ds
2293         pushw %es
2294         pushw $0
2295         popw %es                /* es = 0 */
2296         pushw %cs
2297         popw %ds                /* ds = cs */
2298         /* get original ISR */
2299         movl %es:0x28, %eax     /* eax = old irq 3/int 0bh */
2300         movl %eax, old_irq3     /* save away old irq 4/int 0bh */
2301         movl %es:0x2c, %eax     /* eax = old irq 4/int 0ch */
2302         movl %eax, old_irq4     /* save away old irq 4/int 0ch */
2303         movl %es:0x40, %eax     /* eax = old int 10h */
2304         movl %eax, old_int10h   /* save away old int 10h */
2305         movl %es:0x50, %eax     /* eax = old int 14h */
2306         movl %eax, old_int14h   /* save away old int 14h */
2307         movl %es:0x58, %eax     /* eax = old int 16h */
2308         movl %eax, old_int16h   /* save away old int 16h */
2309         movw $irq3_isr, %es:0x28        /* new irq 3 offset */
2310         movw %cs, %es:0x2a              /* write new irq 3 seg */
2311         movw $irq4_isr, %es:0x2c        /* new irq 4 offset */
2312         movw %cs, %es:0x2e              /* write new irq 4 seg */
2313         movw $int10h_isr, %es:0x40      /* new int 10h offset */
2314         movw %cs, %es:0x42              /* write new int10h seg */
2315         movw $int14h_isr, %es:0x50      /* new int 14h offset */
2316         movw %cs, %es:0x52              /* write new int14h seg */
2317         movw $int16h_isr, %es:0x58      /* new int 16h offset */
2318         movw %cs, %es:0x5a              /* write new int16h seg */
2319         /* empty input buffer to prepare for terminal sizing */
2320         call init_serial_port
2321 input_clear_loop:
2322         call get_byte
2323         jnz input_clear_loop
2324         movw $term_init_string, %si
2325         call send_asciz_out
2326         push $BDA_SEG
2327         push $BDA_SEG
2328         popw %ds                /* ds = 0x40 */
2329         popw %es                /* es = 0x40 */
2330         movw $BDA_CURSOR_BUF, %di
2331 input_timeout_loop:
2332         /* get input from terminal until timeout found */
2333         /* store input at 40:50 - 40:5e (cursor pos) */
2334         call poll_byte
2335         jz input_timeout
2336         stosb                   /* es:di */
2337         cmpw $0x5f, %di         /* 14 characters max */
2338         jnz input_timeout_loop  /* good for more data */
2339 input_timeout:
2340         xorb %al, %al           /* nul terminate input */
2341         stosb
2342         cmpw $0x58, %di         /* less than 8 chars? */
2343         jc resize_end           /* too small to have valid data */
2344         movw $BDA_CURSOR_BUF, %si       /* point to start */
2345         lodsw                   /* ax = first 2 chars */
2346         cmpw $0x5b1b, %ax       /* was it "ESC[" ? */
2347         jnz resize_end          /* reply starts ESC[row;colR */
2348         xorb %bl, %bl           /* bl = ascii->int conversion */
2349 input_first_number:
2350         lodsb                   /* al = next char */
2351         cmpb $0x30, %al
2352         jc resize_end           /* char < 0x30 invalid */
2353         cmpb $0x3a, %al         /* is char < 0x3a */
2354         jnc input_semicolon
2355         andb $0x0f, %al         /* al = 0 - 9 */
2356         movb %bl, %ah           /* ah = last conversion */
2357         aad                     /* ax = (al + ah * 10) & 0xff */
2358         movb %al, %bl           /* bl = row ascii->int conversion */
2359         jmp input_first_number
2360 input_semicolon:
2361         /* at this point bl should contain rows, al = ; */
2362         /* sanity check, bail if invalid */
2363         cmpb $0x3b, %al
2364         jnz resize_end          /* invalid input found */
2365         cmpb $0x0a, %bl         /* less than 10 rows? */
2366         jc suspect_loopback     /* consider input invalid */
2367         xorb %bh, %bh           /* bh = col ascii->int conversion */
2368 input_second_number:
2369         lodsb                   /* al = next char */
2370         cmpb $0x30, %al
2371         jc resize_end           /* char < 0x30 invalid */
2372         cmpb $0x3a, %al         /* is char < 0x3a */
2373         jnc input_final_r
2374         andb $0x0f, %al         /* al = 0 - 9 */
2375         movb %bh, %ah           /* ah = last conversion */
2376         aad                     /* ax = (al + ah * 10) & 0xff */
2377         movb %al, %bh           /* bh = ascii->int conversion */
2378         jmp input_second_number
2379 input_final_r:
2380         cmpb $0x52, %al         /* is al = 'R' ? */
2381         jnz suspect_loopback    /* invalid input found */
2382         movb %bl, %cs:term_rows /* save away bl rows value */
2383         cmpw $0xc000, %cs:old_int10h_seg        /* vga attached? */
2384         jz resize_end           /* if so, leave term_cols at 80 */
2385         movb %bh, %cs:term_cols /* save away bh cols value */
2386         jmp resize_end
2387 suspect_loopback:
2388         /*
2389          * characters were received that look like what we sent out
2390          * at this point, assume that a loopback device was plugged in
2391          * and disable any future serial port reads or writes, by pointing
2392          * output to port 0x2e8 (COM4) instead of 0x3f8 -- it's expected
2393          * that this is safe since a real port responds correctly and a
2394          * missing port will respond with 0xff which will terminate the
2395          * loop that waits for the "right" status on the port.
2396          */
2397         movw $0x2e8, %cs:serial_port_base_address
2398 resize_end:
2399         /* clear (hopefully) overwritten cursor position buffer */
2400         xorb %al, %al
2401         movw $BDA_CURSOR_BUF, %di
2402         movw $0x10, %cx
2403         cld
2404         rep
2405         stosb                   /* fill 40:50 - 40:5f with 0 */
2406         pushw %cs
2407         popw %ds                /* ds = cs */
2408         call get_byte           /* flush any remaining "wrong" input */
2409         jnz resize_end
2410         call send_crlf          /* place cursor on start of last line */
2411         movw $mfg_string, %si
2412         call send_asciz_out
2413         call send_crlf
2414         movw $prod_string, %si
2415         call send_asciz_out
2416         call send_crlf
2417         movw $long_version, %si
2418         call send_asciz_out
2419         call send_crlf
2420         /* if vga attached, skip terminal message and bda setup... */
2421         cmpw $0xc000, %cs:old_int10h_seg        /* vga attached? */
2422         jz post_bda_init_tail   /* if so, don't modify BDA */
2423         /* show detected terminal size, or default if none detected */
2424         movw $term_info, %si
2425         call send_asciz_out
2426         pushw $BDA_SEG
2427         popw %ds                /* ds = 0x40 */
2428         movb %cs:term_cols, %al
2429         movb %al, BDA_COLS      /* 40:4a = number of character cols */
2430         movb $0, BDA_CURSOR_COL /* 40:51 = cursor0 col */
2431         call send_number
2432         movb $0x78, %al         /* x */
2433         call send_byte
2434         movb %cs:term_rows, %al
2435         movb %al, %ah
2436         decb %ah                /* ah = rows-1 */
2437         movb %ah, BDA_ROWS      /* 40:84 = number of character rows - 1 */
2438         movb %ah, BDA_CURSOR_ROW        /* 40:50 = cursor0 row */
2439         call send_number
2440         call send_crlf
2441         movb $3, BDA_MODE_NUM
2442         movb $0x29, BDA_MODE_SEL
2443         movw $VGA_IO_BASE, BDA_6845_ADDR
2444         movw $0x4000, BDA_PAGE_SIZE     /* 16KB per video page */
2445         /* to avoid ansi colors every character, store last attribute */
2446         movb $0x07, BDA_COLOR_VAL       /* 07 = black bg, white fg */
2447         movw %cs, %ax
2448         movw $_start, BDA_ROM_OFF
2449         movw %ax, BDA_ROM_SEG
2450 post_bda_init_tail:
2451         /* copy BDA rows/cols to sgabios location... */
2452         /* if vga card is installed, reuse those values... */
2453         /* if no vga card is installed, this shouldn't change anything */
2454         pushw $BDA_SEG
2455         popw %ds                /* ds = 0x40 */
2456         movb BDA_ROWS, %al
2457         incb %al                /* bda holds rows-1 */
2458         movb %al, %cs:term_rows /* sgabios rows */
2459         movb BDA_COLS, %ah
2460         movb %ah, %cs:term_cols /* sgabios cols */
2461         /* setup in-memory logging of console if desired... */
2462         call setup_memconsole
2463         /* setup logging of last 256 characters output, if ebda has room */
2464         call sgabioslog_setup_ebda
2465         movw $ebda_info, %si
2466         call send_asciz_out
2467         movw %cs:sgabios_ebda_logbuf_offset, %ax
2468         xchgb %ah, %al
2469         call send_number
2470         movb $0x20, %al
2471         call send_byte
2472         movb %ah, %al
2473         call send_number
2474         call send_crlf
2475         popw %es
2476         popw %ds
2477         popaw
2478         popf
2479         lret
2480
2481 _end_sgabios: