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