!************************************************************************** !* !* Boot-ROM-Code to load an operating system across a TCP/IP network. !* !* Module: ansi.S !* Purpose: Display ANSI control sequences onto the console !* Entries: prnansi !* !************************************************************************** !* !* Copyright (C) 1995-2003 Gero Kuhlmann !* !* 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; either version 2 of the License, or !* any later version. !* !* This program is distributed in the hope that it will be useful, !* but WITHOUT ANY WARRANTY; without even the implied warranty of !* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !* GNU General Public License for more details. !* !* You should have received a copy of the GNU General Public License !* along with this program; if not, write to the Free Software !* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. !* !* $Id: ansi.S,v 1.4 2003/01/25 23:29:41 gkminix Exp $ !* !************************************************************************** !* ! !************************************************************************** ! ! This module provides ANSI control sequence interpretation to the ! bootrom kernel. Its output routine will only be called by printf. ! ! Supported ANSI control sequences: ! ! Display attributes ! ! Code Effect ! [0m normal text ! [1m high-intensity on ! [21m high-intensity off ! [5m blinking on ! [25m blinking off ! [7m reverse video on ! [27m reverse video off ! [3xm set foreground color: ! [4xm set background color. x can be: ! ! 0 - black 4 - blue ! 1 - red 5 - magenta ! 2 - green 6 - cyan ! 3 - yellow 7 - white ! ! ! Cursor control ! ! Code Effect ! [r;cH move cursor to row r col c (r and c are both numbers) ! [rA move cursor up r rows ! [rB move cursor down r rows ! [cC move cursor right c columns ! [cD move cursor left c columns ! [?7l turn off line wrap ! [?$7 turn on line wrap ! [J clear screen and home cursor ! [s save the cursor position ! [u return cursor to saved position ! !************************************************************************** ! ! Include assembler macros: ! #include #include "libpriv.inc" ! !************************************************************************** ! ! Miscellaneous defines: ! ARGMAX equ 2 ! maximum number of arguments MAXLINES equ 25 ! default number of screen lines FLAG_NOWRAP equ %00000001 ! flag indicating not to wrap FLAG_INVERS equ %00000010 ! flag for invers video FLAG_BLINK equ %00000100 ! flag for blinking FLAG_INTENSE equ %00001000 ! flag for high intensity FLAG_NORMAL equ %11110001 ! mask to set flags to normal mode NORMAL_STATE equ 0 ! normal display state ESC_STATE equ 1 ! found escape in input stream BRACKET_STATE equ 2 ! found bracket in input stream NUMBER_STATE equ 3 ! found numbers in input stream ! !************************************************************************** ! ! BSS segment: ! .bss .lcomm scnmode,1 ! current screen mode .lcomm scnpage,1 ! current screen page .lcomm scncolumns,1 ! number of screen columns .lcomm curpos,2 ! current cursor position .lcomm savedpos,2 ! saved cursor position .lcomm flags,1 ! display flags .lcomm dispstate,1 ! current display state .lcomm argnum,1 ! current argument number .lcomm argbuf,ARGMAX+1 ! argument number buffer ! !************************************************************************** ! ! Data segment: ! .data scnattr: .byte $07 ! attributes for characters to print fg_color: .byte $07 ! foreground color bg_color: .byte 0 ! background color ! !************************************************************************** ! ! Start code segment. ! .text public prnansi ! define entry points ! !************************************************************************** ! ! Command table ! macro tab .byte '?1 ! put letter into table' .word cmd_?2?1 ! put offset to cmd routine mend cmdtab: ! Define commands for uppercase letters tab (A,u) tab (B,u) tab (C,u) tab (D,u) tab (H,u) tab (J,u) ! Define commands for lowercase letters tab (h,l) tab (l,l) tab (m,l) tab (s,l) tab (u,l) ! Mark end of table .byte 0 TABSIZE equ 3 ! size of each table entry ! !************************************************************************** ! ! Print character using ANSI control sequences ! Input: AL - character to display ! Output: none ! Registers used: AX ! prnansi: push bx push cx push dx ! Check if we have been initialized already. In that case, the number ! of screen columns is non-zero. Otherwise determine the current screen ! mode, size and page. Also determine the current cursor position. push ax test byte ptr [scncolumns],#$FF jnz ansi0 mov ah,#$0F ! determine current screen mode int $10 mov [scncolumns],ah ! set number of columns on screen mov [scnpage],bh ! set screen page mov al,bh mov ah,#$05 ! select screen page int $10 ansi0: mov ah,#$03 mov bh,[scnpage] ! determine current cursor position int $10 mov [curpos],dx pop ax ! State switcher. Depending on the character received with this routine ! the state will be switched and the corresponding action executed. mov ah,[dispstate] if NORMAL_STATE = 0 or ah,ah ! first handle normal state else .error NORMAL_STATE has to be zero endif jnz ansi2 cmp al,#CHR_ESC ! is it an escape character? jne ansi1 mov byte ptr [dispstate],#ESC_STATE ! yes, just switch the state and jmp ansiF ! terminate ansi1: call putchar ! no, we have to print the jmp ansi3 ! character ansi2: cmp ah,#ESC_STATE jne ansi4 ! are we in escape state? cmp al,#CHR_BRACKET jne ansi3 ! a bracket has to follow mov byte ptr [dispstate],#BRACKET_STATE ! simply switch the state jmp ansiF ! and terminate ansi3: mov byte ptr [dispstate],#NORMAL_STATE ! if no bracket, this is an ansiF: jmp ansi9 ! error, so switch back to ! normal mode ansi4: cmp ah,#BRACKET_STATE ! check for bracket state jne ansi5 cmp al,#CHR_QUEST ! skip any question marks je ansiF cmp al,#CHR_EQUAL ! skip any equal signs je ansiF mov byte ptr [dispstate],#NUMBER_STATE ! continue with number state mov byte ptr [argnum],#0 if ARGMAX = 2 mov word ptr [argbuf],#0 ! zero out argument buffer else mov bx,#ARGMAX ansiD: mov byte ptr argbuf-1[bx],#0 ! zero out argument buffer dec bx jnz ansiD endif jmp ansi6 ansi5: cmp ah,#NUMBER_STATE ! if we are not in number state its an jne ansi3 ! error, as we dont have anymore states ansi6: cmp al,#CHR_COLON jne ansi7 ! a semicolon sign advances the argument mov al,[argnum] ! counter inc al cmp al,#ARGMAX ! no more than ARGMAX arguments allowed, jae ansi9 ! simply skip the rest mov [argnum],al jmp ansi9 ansi7: cmp al,#$30 ! check if we got a number here jb ansi8 cmp al,#$39 ja ansi8 sub al,#$30 ! convert character into number mov cl,al mov bl,[argnum] xor bh,bh mov al,argbuf[bx] ! multiply the number in the buffer mov ah,#10 ! by 10 mul ah add al,cl ! and add the new value mov argbuf[bx],al ! set new value in argument buffer jmp ansi9 ! thats it ! Now execute the command associated with the last character of the escape ! sequence. ansi8: xor bx,bx ansiA: seg cs mov ah,cmdtab[bx] or ah,ah ! end of table reached je ansiC cmp al,ah ! found character je ansiB add bx,#TABSIZE ! continue with next table entry jmp ansiA ansiB: mov dx,[curpos] seg cs call word ptr cmdtab+1[bx] ! call command handling routine ansiC: mov byte ptr [dispstate],#NORMAL_STATE ! restore state counter ! Terminate the routine by restoring all registers ansi9: pop dx pop cx pop bx ret ! !************************************************************************** ! ! Actually display a character onto the console ! Input: AL - character to display ! DX - current cursor position ! Output: none ! Registers used: AX, BX, CX, DX ! putchar: mov bh,[scnpage] ! used throughout this routine ! The bell character is best handled by the BIOS cmp al,#CHR_BELL jne putch2 putch1: mov ah,#$0E ! print special character using the int $10 ! BIOS jmp putch9 ! TABs are not handled by the BIOS teletype driver, so we have to do it ! ourselves. putch2: cmp al,#CHR_TAB ! check for tab character jne putch4 mov cl,dl ! compute number of spaces to print or cl,#$F8 neg cl xor ch,ch putch3: push cx mov al,#CHR_SPACE ! print spaces call putchar ! recursive call pop cx loop putch3 jmp putch9 ! Carriage return simply involves setting the cursor position to zero putch4: cmp al,#CHR_CR ! check for carriage return jne putch5 xor dl,dl ! move cursor into column zero jmp putcursor ! Linefeed involves incrementing the line number and scrolling the screen if ! necessary putch5: cmp al,#CHR_LF ! check for linefeed je putch8 ! the linefeed code is below ! Backspace simply involves decrementing the current cursor position cmp al,#CHR_BS ! check for backspace jne putch6 or dl,dl ! dont decrement below zero jz putcursor dec dl ! decrement column number jmp putcursor ! All other characters are printed at the cursor position putch6: mov ah,#$09 mov bl,[scnattr] ! print the character using BIOS mov cx,#1 int $10 inc dl ! increment cursor position cmp dl,[scncolumns] ! check if at right end of line jb putcursor dec dl ! restore cursor to rightmost test byte ptr [flags],#FLAG_NOWRAP ! column and check if we should jnz putcursor ! wrap at the end of the line xor dl,dl ! put cursor into column 0 putch8: inc dh cmp dh,#MAXLINES ! check if at bottom of screen jb putcursor dec dh ! restore cursor to lowest screen line push dx mov ax,#$0601 xor cx,cx ! define upper left corner mov dl,[scncolumns] ! define lower right corner dec dl mov bh,[scnattr] int $10 ! use the BIOS to scroll the screen pop dx ! Put cursor to position indicated by the curpos variable putcursor: mov [curpos],dx ! save new cursor position mov ah,#$02 mov bh,[scnpage] ! set cursor using BIOS int $10 putch9: ret ! !************************************************************************** ! ! Handle escape sequence 'A': move cursor up ! Input: DX - current cursor position ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_uA: sub dh,[argbuf+0] ! decrement line number jae putcursor ! set new cursor position ret ! !************************************************************************** ! ! Handle escape sequence 'B': move cursor down ! Input: DX - current cursor position ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_uB: add dh,[argbuf+0] ! increment line number cmp dh,#MAXLINES jb putcursor ! set new cursor position ret ! !************************************************************************** ! ! Handle escape sequence 'C': move cursor right ! Input: DX - current cursor position ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_uC: add dl,[argbuf+0] ! increment column number cmp dl,[scncolumns] jb putcursor ! set new cursor position ret ! !************************************************************************** ! ! Handle escape sequence 'D': move cursor left ! Input: DX - current cursor position ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_uD: sub dh,[argbuf+0] ! decrement column number jae putcursor ! set new cursor position ret ! !************************************************************************** ! ! Handle escape sequence 'H': move cursor to specified position ! Input: none ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_uH: mov dx,[argbuf+0] xchg dh,dl ! get new cursor position jmp putcursor ! !************************************************************************** ! ! Handle escape sequence 'J': clear screen and move cursor into upper ! left corner of the screen ! Input: none ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_uJ: mov ax,#$0600 mov bh,[scnattr] xor cx,cx mov dh,#MAXLINES-1 ! use the BIOS scrolling function mov dl,[scncolumns] ! to clear the screen dec dl int $10 xor dx,dx ! put cursor into upper left corner jmp putcursor ! !************************************************************************** ! ! Handle escape sequence 'h': set display option, currently only option 7 ! is supported, which turns line wrapping on. ! Input: none ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_lh: mov al,[argbuf+0] cmp al,#7 ! check for option 7 jne cmdlh9 and byte ptr [flags],#~FLAG_NOWRAP ! turn wrapping off cmdlh9: ret ! !************************************************************************** ! ! Handle escape sequence 'l': reset display option, currently only option 7 ! is supported, which turns line wrapping off. ! Input: none ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_ll: mov al,[argbuf+0] cmp al,#7 ! check for option 7 jne cmdll9 or byte ptr [flags],#FLAG_NOWRAP ! turn wrapping off cmdll9: ret ! !************************************************************************** ! ! Handle escape sequence 'm': set screen attributes according to the ! following table: ! ! [0m normal text ! [1m high-intensity on ! [21m high-intensity off ! [5m blinking on ! [25m blinking off ! [7m reverse video on ! [27m reverse video off ! [3xm set foreground color ! [4xm set background color ! ! Input: none ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_lm: ! First set the global variables according to the first parameter of the ! escape sequence mov ah,[flags] mov al,[argbuf+0] or al,al jnz cmdlm1 and ah,#FLAG_NORMAL ! set normal mode jmp cmdlm9 cmdlm1: mov cl,al cmp cl,#20 ! check for reset command jb cmdlm2 sub cl,#20 cmdlm2: cmp cl,#1 jne cmdlm3 mov ch,#FLAG_INTENSE ! set high-intensity mode jmp cmdlm5 cmdlm3: cmp cl,#5 jne cmdlm4 mov ch,#FLAG_BLINK ! set blinking mode jmp cmdlm5 cmdlm4: cmp cl,#7 jne cmdlm7 mov ch,#FLAG_INVERS ! set inverse mode cmdlm5: cmp al,#20 jae cmdlm6 or ah,ch ! set the flag jmp cmdlm9 cmdlm6: not ch and ah,ch ! reset the flag jmp cmdlm9 cmdlm7: sub al,#30 jb cmdlmF ! check for foreground color cmp al,#7 ja cmdlm8 mov [fg_color],al jmp cmdlm9 cmdlm8: sub al,#10 jb cmdlmF ! check for background color cmp al,#7 ja cmdlmF mov [bg_color],al ! Now set the actual attribute value according to the flags set previously cmdlm9: mov [flags],ah xor ch,ch test ah,#FLAG_BLINK jz cmdlmA or ch,#%10000000 ! set blink attribute cmdlmA: test ah,#FLAG_INTENSE jz cmdlmB or ch,#%00001000 ! set high-intensity attribute cmdlmB: mov cl,#4 mov bl,[fg_color] mov bh,[bg_color] ! set colors test ah,#FLAG_INVERS jz cmdlmC shl bl,cl ! shift foreground color into background jmp cmdlmD ! for inverse display cmdlmC: shl bh,cl ! shift background color cmdlmD: or bl,bh and bl,#%01110111 ! compute color attribute value or ch,bl ! and merge it into the final value mov [scnattr],ch ! save final screen attributes cmdlmF: ret ! !************************************************************************** ! ! Handle escape sequence 's': save current cursor position ! Input: none ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_ls: mov ax,[curpos] mov [savedpos],ax ! save the current cursor position ret ! !************************************************************************** ! ! Handle escape sequence 'u': restore cursor position ! Input: none ! Output: none ! Registers used: AX, BX, CX, DX ! cmd_lu: mov dx,[savedpos] jmp near putcursor ! place cursor to new position ! !************************************************************************** ! end