| ;; $Id$ |
| ;; ----------------------------------------------------------------------- |
| ;; |
| ;; Copyright 1994-2004 H. Peter Anvin - All Rights Reserved |
| ;; |
| ;; This program is free software; you can redistribute it and/or modify |
| ;; it under the terms of the GNU General Public License as published by |
| ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
| ;; Boston MA 02111-1307, USA; either version 2 of the License, or |
| ;; (at your option) any later version; incorporated herein by reference. |
| ;; |
| ;; ----------------------------------------------------------------------- |
| |
| ;; |
| ;; conio.inc |
| ;; |
| ;; Console I/O code, except: |
| ;; writechr, writestr - module-dependent |
| ;; cwritestr, crlf - writestr.inc |
| ;; writehex* - writehex.inc |
| ;; |
| |
| ; |
| ; loadkeys: Load a LILO-style keymap; SI and DX:AX set by searchdir |
| ; |
| section .text |
| |
| loadkeys: |
| and dx,dx ; Should be 256 bytes exactly |
| jne loadkeys_ret |
| cmp ax,256 |
| jne loadkeys_ret |
| |
| mov bx,trackbuf |
| mov cx,1 ; 1 cluster should be >= 256 bytes |
| call getfssec |
| |
| mov si,trackbuf |
| mov di,KbdMap |
| mov cx,256 >> 2 |
| rep movsd |
| |
| loadkeys_ret: ret |
| |
| ; |
| ; get_msg_file: Load a text file and write its contents to the screen, |
| ; interpreting color codes. Is called with SI and DX:AX |
| ; set by routine searchdir |
| ; |
| get_msg_file: |
| push es |
| shl edx,16 ; EDX <- DX:AX (length of file) |
| mov dx,ax |
| mov ax,xfer_buf_seg ; Use for temporary storage |
| mov es,ax |
| |
| mov byte [TextAttribute],07h ; Default grey on white |
| mov byte [DisplayMask],07h ; Display text in all modes |
| call msg_initvars |
| |
| get_msg_chunk: push edx ; EDX = length of file |
| xor bx,bx ; == xbs_textbuf |
| mov cx,[BufSafe] |
| call getfssec |
| pop edx |
| push si ; Save current cluster |
| xor si,si ; == xbs_textbuf |
| mov cx,[BufSafeBytes] ; Number of bytes left in chunk |
| print_msg_file: |
| push cx |
| push edx |
| es lodsb |
| cmp al,1Ah ; DOS EOF? |
| je msg_done_pop |
| push si |
| mov cl,[UsingVGA] |
| inc cl ; 01h = text mode, 02h = graphics |
| call [NextCharJump] ; Do what shall be done |
| pop si |
| pop edx |
| pop cx |
| dec edx |
| jz msg_done |
| loop print_msg_file |
| pop si |
| jmp short get_msg_chunk |
| msg_done_pop: |
| add sp,byte 6 ; Drop pushed EDX, CX |
| msg_done: |
| pop si |
| pop es |
| ret |
| msg_putchar: ; Normal character |
| cmp al,0Fh ; ^O = color code follows |
| je msg_ctrl_o |
| cmp al,0Dh ; Ignore <CR> |
| je msg_ignore |
| cmp al,0Ah ; <LF> = newline |
| je msg_newline |
| cmp al,0Ch ; <FF> = clear screen |
| je msg_formfeed |
| cmp al,19h ; <EM> = return to text mode |
| je msg_novga |
| cmp al,18h ; <CAN> = VGA filename follows |
| je msg_vga |
| jnb .not_modectl |
| cmp al,10h ; 10h to 17h are mode controls |
| jae msg_modectl |
| .not_modectl: |
| |
| msg_normal: call write_serial_displaymask ; Write to serial port |
| test [DisplayMask],cl |
| jz msg_ignore ; Not screen |
| mov bl,[TextAttribute] |
| mov bh,[BIOS_page] |
| mov ah,09h ; Write character/attribute |
| mov cx,1 ; One character only |
| int 10h ; Write to screen |
| mov al,[CursorCol] |
| inc ax |
| cmp al,[VidCols] |
| ja msg_line_wrap ; Screen wraparound |
| mov [CursorCol],al |
| |
| msg_gotoxy: mov bh,[BIOS_page] |
| mov dx,[CursorDX] |
| mov ah,02h ; Set cursor position |
| int 10h |
| msg_ignore: ret |
| msg_ctrl_o: ; ^O = color code follows |
| mov word [NextCharJump],msg_setbg |
| ret |
| msg_newline: ; Newline char or end of line |
| mov si,crlf_msg |
| call write_serial_str_displaymask |
| msg_line_wrap: ; Screen wraparound |
| test [DisplayMask],cl |
| jz msg_ignore |
| mov byte [CursorCol],0 |
| mov al,[CursorRow] |
| inc ax |
| cmp al,[VidRows] |
| ja msg_scroll |
| mov [CursorRow],al |
| jmp short msg_gotoxy |
| msg_scroll: xor cx,cx ; Upper left hand corner |
| mov dx,[ScreenSize] |
| mov [CursorRow],dh ; New cursor at the bottom |
| mov bh,[ScrollAttribute] |
| mov ax,0601h ; Scroll up one line |
| int 10h |
| jmp short msg_gotoxy |
| msg_formfeed: ; Form feed character |
| mov si,crff_msg |
| call write_serial_str_displaymask |
| test [DisplayMask],cl |
| jz msg_ignore |
| xor cx,cx |
| mov [CursorDX],cx ; Upper lefthand corner |
| mov dx,[ScreenSize] |
| mov bh,[TextAttribute] |
| mov ax,0600h ; Clear screen region |
| int 10h |
| jmp msg_gotoxy |
| msg_setbg: ; Color background character |
| call unhexchar |
| jc msg_color_bad |
| shl al,4 |
| test [DisplayMask],cl |
| jz .dontset |
| mov [TextAttribute],al |
| .dontset: |
| mov word [NextCharJump],msg_setfg |
| ret |
| msg_setfg: ; Color foreground character |
| call unhexchar |
| jc msg_color_bad |
| test [DisplayMask],cl |
| jz .dontset |
| or [TextAttribute],al ; setbg set foreground to 0 |
| .dontset: |
| jmp short msg_putcharnext |
| msg_vga: |
| mov word [NextCharJump],msg_filename |
| mov di, VGAFileBuf |
| jmp short msg_setvgafileptr |
| |
| msg_color_bad: |
| mov byte [TextAttribute],07h ; Default attribute |
| msg_putcharnext: |
| mov word [NextCharJump],msg_putchar |
| ret |
| |
| msg_filename: ; Getting VGA filename |
| cmp al,0Ah ; <LF> = end of filename |
| je msg_viewimage |
| cmp al,' ' |
| jbe msg_ret ; Ignore space/control char |
| mov di,[VGAFilePtr] |
| cmp di,VGAFileBufEnd |
| jnb msg_ret |
| mov [di],al ; Can't use stosb (DS:) |
| inc di |
| msg_setvgafileptr: |
| mov [VGAFilePtr],di |
| msg_ret: ret |
| |
| msg_novga: |
| call vgaclearmode |
| jmp short msg_initvars |
| |
| msg_viewimage: |
| push es |
| push ds |
| pop es ; ES <- DS |
| mov si,VGAFileBuf |
| mov di,VGAFileMBuf |
| push di |
| call mangle_name |
| pop di |
| call searchdir |
| pop es |
| jz msg_putcharnext ; Not there |
| call vgadisplayfile |
| ; Fall through |
| |
| ; Subroutine to initialize variables, also needed |
| ; after loading a graphics file |
| msg_initvars: |
| pusha |
| mov bh,[BIOS_page] |
| mov ah,03h ; Read cursor position |
| int 10h |
| mov [CursorDX],dx |
| popa |
| jmp short msg_putcharnext ; Initialize state machine |
| |
| msg_modectl: |
| and al,07h |
| mov [DisplayMask],al |
| jmp short msg_putcharnext |
| |
| ; |
| ; write_serial: If serial output is enabled, write character on serial port |
| ; write_serial_displaymask: d:o, but ignore if DisplayMask & 04h == 0 |
| ; |
| write_serial_displaymask: |
| test byte [DisplayMask], 04h |
| jz write_serial.end |
| write_serial: |
| pushfd |
| pushad |
| mov bx,[SerialPort] |
| and bx,bx |
| je .noserial |
| push ax |
| mov ah,[FlowInput] |
| .waitspace: |
| ; Wait for space in transmit register |
| lea dx,[bx+5] ; DX -> LSR |
| in al,dx |
| test al,20h |
| jz .waitspace |
| |
| ; Wait for input flow control |
| inc dx ; DX -> MSR |
| in al,dx |
| and al,ah |
| cmp al,ah |
| jne .waitspace |
| .no_flow: |
| |
| xchg dx,bx ; DX -> THR |
| pop ax |
| call slow_out ; Send data |
| .noserial: popad |
| popfd |
| .end: ret |
| |
| ; |
| ; write_serial_str: write_serial for strings |
| ; write_serial_str_displaymask: d:o, but ignore if DisplayMask & 04h == 0 |
| ; |
| write_serial_str_displaymask: |
| test byte [DisplayMask], 04h |
| jz write_serial_str.end |
| |
| write_serial_str: |
| .loop lodsb |
| and al,al |
| jz .end |
| call write_serial |
| jmp short .loop |
| .end: ret |
| |
| ; |
| ; pollchar: check if we have an input character pending (ZF = 0) |
| ; |
| pollchar: |
| pushad |
| mov ah,11h ; Poll keyboard |
| int 16h |
| jnz .done ; Keyboard response |
| mov dx,[SerialPort] |
| and dx,dx |
| jz .done ; No serial port -> no input |
| add dx,byte 5 ; DX -> LSR |
| in al,dx |
| test al,1 ; ZF = 0 if data pending |
| jz .done |
| inc dx ; DX -> MSR |
| mov ah,[FlowIgnore] ; Required status bits |
| in al,dx |
| and al,ah |
| cmp al,ah |
| setne al |
| dec al ; Set ZF = 0 if equal |
| .done: popad |
| ret |
| |
| ; |
| ; getchar: Read a character from keyboard or serial port |
| ; |
| getchar: |
| RESET_IDLE |
| .again: |
| DO_IDLE |
| mov ah,11h ; Poll keyboard |
| int 16h |
| jnz .kbd ; Keyboard input? |
| mov bx,[SerialPort] |
| and bx,bx |
| jz .again |
| lea dx,[bx+5] ; DX -> LSR |
| in al,dx |
| test al,1 |
| jz .again |
| inc dx ; DX -> MSR |
| mov ah,[FlowIgnore] |
| in al,dx |
| and al,ah |
| cmp al,ah |
| jne .again |
| .serial: xor ah,ah ; Avoid confusion |
| xchg dx,bx ; Data port |
| in al,dx |
| ret |
| .kbd: mov ah,10h ; Get keyboard input |
| int 16h |
| cmp al,0E0h |
| jnz .not_ext |
| xor al,al |
| .not_ext: |
| and al,al |
| jz .func_key |
| mov bx,KbdMap ; Convert character sets |
| xlatb |
| .func_key: ret |
| |
| %ifdef DEBUG_TRACERS |
| ; |
| ; debug hack to print a character with minimal code impact |
| ; |
| debug_tracer: pushad |
| pushfd |
| mov bp,sp |
| mov bx,[bp+9*4] ; Get return address |
| mov al,[cs:bx] ; Get data byte |
| inc word [bp+9*4] ; Return to after data byte |
| call writechr |
| popfd |
| popad |
| ret |
| %endif ; DEBUG_TRACERS |
| |
| section .data |
| ScrollAttribute db 07h ; Grey on white (normal text color) |
| |
| section .bss |
| alignb 2 |
| NextCharJump resw 1 ; Routine to interpret next print char |
| CursorDX equ $ |
| CursorCol resb 1 ; Cursor column for message file |
| CursorRow resb 1 ; Cursor row for message file |
| ScreenSize equ $ |
| VidCols resb 1 ; Columns on screen-1 |
| VidRows resb 1 ; Rows on screen-1 |
| |
| ; Serial console stuff... |
| BaudDivisor resw 1 ; Baud rate divisor |
| FlowControl equ $ |
| FlowOutput resb 1 ; Outputs to assert for serial flow |
| FlowInput resb 1 ; Input bits for serial flow |
| FlowIgnore resb 1 ; Ignore input unless these bits set |
| |
| TextAttribute resb 1 ; Text attribute for message file |
| DisplayMask resb 1 ; Display modes mask |