Metropoli BBS
VIEWER: crt.pas MODE: TEXT (ASCII)
{---------- 32 bit CRT unit for use with DPMI ---- V 0.01 - first release  H -}
{                                                                             }
{This code is Copyright (c)1996 Michael Tippach. You may use it free of charge}
{and you may redistribute verbatim copies too.You may not redistribute altered}
{versions of this code and you are not allowed to charge for it.              }
{                                                                             }
{However, this code is to be considered "work in progress" and it may not only}
{have, it HAS bugs. It is distributed "AS IS" without any warranty.           }
{                                                                             }
{In no event shall I, the author be liable for any kind of damage or losses   }
{arising out of the use, abuse or inability to use the code.                  }
{                                                                             }
{Note that all routines here are written to get the job done, not to gain     }
{maximum CRT performance. As soon as I have more time I'll probably add       }
{direct video support. Most of the boolean publics (checkBreak...) are        }
{ignored. As of now, there are lots of urgent 2-do's and priority scope       }
{is different among people...                                                 }
{compile with DCC32 Delphi 2 command line compiler "DCC32 CRT.PAS"            }
{                                                                             }
{Comments and suggestions to: Michael Tippach <tippachm@dialin.deh.de>        }
{                                             <tippach@metronet.de>           }
{                                                                             }
{                                             ... enjoy!                      }
{                                                                             }
{Note: This version has been hacked by me to make the huge selector public    }
{-----------------------------------------------------------------------------}

unit	crt;
interface
const
   Black        = 0;
   Blue         = 1;
   Green        = 2;
   Cyan         = 3;
   Red          = 4;
   Magenta      = 5;
   Brown        = 6;
   LightGray    = 7;
   DarkGray     = 8;
   LightBlue    = 9;
   LightGreen   = 10;
   LightCyan    = 11;
   LightRed     = 12;
   LightMagenta = 13;
   Yellow       = 14;
   White        = 15;
   Blink        = 128;

   BW40    = 0;{40x25 B/W on CGA}
   CO40    = 1;{40x25 Color on CGA}
   BW80    = 2;{80x25 B/W on CGA}
   CO80    = 3;{80x25 Color on CGA}
   Mono    = 7;{80x25 B/W on MDA or HGC}
   Font8x8 = 256;{43-/50-line mode EGA/VGA}

   C40  = CO40;
   C80  = CO80;


var	checkbreak,checksnow,checkeof,directvideo:boolean;
	lastmode,windmin,windmax:word;
	textattr:byte;
	huge_selector:	word;	{4G data selector with base 0}

procedure AssignCrt(var f:text);
procedure ClrEol;
procedure ClrScr;
procedure Delay(MS:word);
procedure DelLine;
procedure Gotoxy(X,Y:byte);
procedure HighVideo;
procedure InsLine;
function  Keypressed:boolean;
procedure LowVideo;
procedure NormVideo;
procedure NoSound;
function  ReadKey:char;
procedure Sound(Hz:word);
procedure TextBackground(Color:byte);
procedure TextColor(Color:byte);
procedure TextMode(Mode:integer);
function  WhereX:byte;
function  WhereY:byte;
procedure Window(X1,Y1,X2,Y2:byte);

implementation

var	
        isega:		boolean;
        current_page:	byte;
        normattr:	byte;

{******************** private functions and procedures ***********************}

function max_x:byte;
var	x:byte;
begin
  asm
    push ds
    mov ds,huge_selector
    mov al,ds:[44ah]
    pop ds
    dec al
    mov x,al
  end;
  max_x:=x;
end;

function max_y:byte;
var	y:byte;
begin
  asm
    push ds
    mov ds,huge_selector
    mov al,ds:[484h]
    pop ds
    mov y,al
  end;
  max_y:=y;
end;

procedure put_one(c:byte);
begin
  asm
    push ebx
    mov bl,textattr
    mov bh,current_page
    mov ah,9
    mov al,c
    mov cx,1
    int 10h
{update cursor position}
    mov ah,3
    int 10h
{start with X}
    inc dl
    cmp dl,byte ptr [offset windmax]
    jna @@1
    inc dh
    mov dl,byte ptr [offset windmin]

@@1:
{now check Y}
    cmp dh,byte ptr [offset windmax+1]
    jna @@2
{have to scroll the window}
    dec dh
    push edx
    mov cx,windmin
    mov dx,windmax
    mov bh,textattr
    mov ax,0601h
    push ds
    push ebp
    int 10h
    pop ebp
    pop ds
    pop edx

@@2:
{set new cursor pos.}
    mov bh,current_page
    mov ah,2
    int 10h
    pop ebx
  end;
end;

procedure nuke_one;
begin
  if whereX > 1 then begin
    asm
      push ebx
      mov bh,current_page
      mov ah,3
      int 10h
      dec dl
      mov ah,2
      int 10h
      mov ax,920h
      mov bl,textattr
      mov cx,1
      int 10h
      pop ebx
    end;
  end;
end;

procedure put_cr;
begin
  asm
    push ebx
    mov bh,current_page
    mov ah,3
    int 10h
    mov dl,byte ptr [offset windmin]
    mov ah,2
    int 10h
    pop ebx
  end;
end;

procedure put_lf;
begin
  asm
    push ebx
    mov bh,current_page
    mov ah,3
    int 10h
    inc dh
    cmp dh,byte ptr [offset windmax+1]
    jna @@1
{have to scroll the window}
    dec dh
    push edx
    mov cx,windmin
    mov dx,windmax
    mov bh,textattr
    mov ax,0601h
    push ds
    push ebp
    int 10h
    pop ebp
    pop ds
    pop edx

@@1:
{set new cursor pos.}
    mov bh,current_page
    mov ah,2
    int 10h
    pop ebx
  end;
end;

{********************* public functions and procedures ***********************}

procedure AssignCrt(var f:text);
begin
  assign(f,'CON'); {Is this the answer? Who actually uses it, BTW?}
end;

procedure ClrEol;
var i,x,y:byte;
begin
  x:=whereX;
  y:=whereY;
  for i:= (x+lo(windmin)) to (lo(windmax)+1) do put_one($20);
  gotoxy(x,y);
end;

procedure ClrScr;
begin
  asm
    push ebx
    mov ax,600h
    mov bh,textattr
    mov cx,windmin
    mov dx,windmax
    push ds	{there're some bugs...          }
    push ebp	{see Ralf Brown's Interrupt list}
    int 10h
    pop ebp
    pop ds
    mov bh,current_page
    mov dx,windmin
    mov ah,2
    int 10h
    pop ebx
  end;
end;

procedure Delay(MS:word);
begin
  asm
    sub ecx,ecx
    movzx edx,MS
    shl edx,10
    shld ecx,edx,16
    mov ah,86h
    int 15h {for some strange reason this does not work under NT, however in}
            {TP5.5 it behaves the same way under NT so we are compatible :) }
  end;
end;

procedure DelLine;
begin
  asm
    push ebx
    mov bh,current_page
    mov ah,3
    int 10h
    mov ax,601h
    mov bh,textattr
    mov cx,windmin
    mov ch,dh
    mov dx,windmax
    push ds	{there're some bugs...          }
    push ebp	{see Ralf Brown's Interrupt list}
    int 10h
    pop ebp
    pop ds
    pop ebx
  end;
end;

procedure Gotoxy(X,Y:byte);
begin
{check if in current window, ignore if not}
  asm
    push ebx
    mov ax,windmin
    mov dl,X
    mov dh,Y
    dec dl
    dec dh
    add dl,al
    add dh,ah
    mov ax,windmax
    cmp dl,al
    ja @@1
    cmp dh,ah
    ja @@1
    mov bh,current_page
    mov ah,2
    int 10h
@@1:
    pop ebx
  end;
end;

procedure HighVideo;
begin
  textattr:=(textattr or 8);
end;

procedure InsLine;
begin
  asm
    push ebx
    mov bh,current_page
    mov ah,3
    int 10h
    mov ax,701h
    mov bh,textattr
    mov cx,windmin
    mov ch,dh
    mov dx,windmax
    push ds	{there're some bugs...          }
    push ebp	{see Ralf Brown's Interrupt list}
    int 10h
    pop ebp
    pop ds
    pop ebx
  end;
end;

function  Keypressed:boolean;
var k:boolean;
begin
  asm
    mov ah,1
    mov k,false
    int 16h
    jz @@1
    mov k,true
@@1:
  end;
  Keypressed:=k;
end;

procedure LowVideo;
begin
  textattr:=(textattr and $F7);
end;

procedure NormVideo;
begin
  textattr:=normattr;
end;

procedure NoSound;
begin
  asm
    in al,61h
    and al,0fch
    out 61h,al
  end;
end;

function  ReadKey:char;
var 	k:char;
begin
  asm
    sub ah,ah
    int 16h
    mov k,al
  end;
  ReadKey:=k;
end;

procedure Sound(Hz:word);
begin
  if Hz > 18 then begin
    asm
      movzx ecx,Hz
      mov eax,1193180
      sub edx,edx
      div ecx
      mov ecx,eax
{set timer #3}
      mov al,182
      out 43h,al
      mov al,cl
      out 42h,al
      mov al,ch
      out 42h,al
{enable speaker}
      in al,61h
      or al,3
      out 61h,al
    end;
  end;
end;

procedure TextBackground(Color:byte);
begin
  asm
    mov al,textattr
    mov ah,color
    and al,08fh
    and ah,7
    shl ah,4
    or al,ah
    mov textattr,al
  end;
end;

procedure TextColor(Color:byte);
begin
  asm
    mov al,textattr
    mov ah,color
    and al,070h
    and ah,08fh
    or al,ah
    mov textattr,al
  end;
end;

procedure TextMode(Mode:integer);
begin
  asm
    push ebx
    mov eax,mode {integer now is 32 bit}
    test ah,ah
    jz @@1
    cmp isega,true
    jnz @@4

@@1:
    cmp al,4
    jc @@2
    cmp al,7
    jnz @@4

@@2:
    mov ecx,eax
    sub ah,ah
    int 10h
    mov ah,0fh
    int 10h
    cmp cl,al
    jnz @@4
    mov current_page,bh
    mov eax,mode
    mov lastmode,ax
    test ah,ah
    jz @@3
    mov ax,1112h
    sub bl,bl
    int 10h

@@3:
{update screen window size}
    mov windmin,0
    push ds
    mov ds,huge_selector
    mov al,ds:[44ah]
    mov ah,ds:[484h]
    pop ds
    mov windmax,ax
{update textattr}
    mov bh,current_page
    mov ah,8
    int 10h
    mov textattr,ah
{    mov normattr,ah}

@@4:
    pop ebx
  end;
end;


function  WhereX:byte;
var	X:byte;
begin
  asm
    push ebx
    mov bh,current_page
    mov ah,3
    int 10h
    sub dl,byte ptr [offset windmin]
    inc dl  
    mov X,dl
    pop ebx
  end;
  WhereX:=X;
end;

function  WhereY:byte;
var	Y:byte;
begin
  asm
    push ebx
    mov bh,current_page
    mov ah,3
    int 10h
    sub dh,byte ptr [offset windmin + 1]
    inc dh  
    mov Y,dh
    pop ebx
  end;
  WhereY:=Y;
end;

procedure Window(X1,Y1,X2,Y2:byte);
begin
  if (((X1-1) < X2) and ((Y1-1) < Y2) and (X2<=(max_x+1)) and (Y2<=(max_y+1))) then begin
    windmin:=256*(Y1-1) + X1 - 1;
    windmax:=256*(Y2-1) + X2 - 1;
  end;
end;

{*************************** init for unit CRT *******************************}

begin
  asm
{get a zero base selector}
    push ebx
    sub eax,eax
    mov cx,1
    int 31h
    mov ebx,eax
    mov ax,8
    stc
    sbb ecx,ecx
    sbb edx,edx
    int 31h
    jnc @@01

@@00:
    mov ax,4cffh
    int 21h

@@01:
    lar cx,bx
    mov cl,ch
    and cl,60h
    or cl,92h
    mov ch,0cfh
    mov ax,9
    int 31h
    jc @@00

    sub ecx,ecx
    sub edx,edx
    mov ax,7
    int 31h
    jc @@00

    mov huge_selector,bx

{get videomode info, check for supported videomode}
    mov ah,0fh
    int 10h
    cmp al,4
    jc @@1
    cmp al,Mono
    jz @@1
    mov ax,3
    int 10h
    mov ah,0fh
    int 10h
    cmp al,CO80
    jz @@1
    mov ax,7
    int 10h
    mov ah,0fh
    int 10h
    cmp al,Mono
    jnz @@00
{abort if no suitable videomode, this one is for the paranoid}

@@1:
    mov current_page,bh
    push eax
    mov ah,12h
    mov bx,0ff10h
    int 10h
    inc bh
    mov isega,true
    jnz @@2
{no ega}
    mov isega,false

@@2:
    push ds
    mov ds,huge_selector
    mov bl,ds:[484h]
    pop ds
    cmp bl,18h
    seta bh
    pop eax
    cmp isega,true
    jz @@3
    sub bh,bh

@@3:
    xchg ah,bh
    mov lastmode,ax

    mov checksnow,true
    cmp al,1
    jz @@4
    cmp al,3
    jz @@4
    mov checksnow,false

@@4:
    xchg bl,bh
    mov windmax,bx
    mov windmin,0

{read attribute at cursor position}
    mov bh,current_page
    mov ah,8
    int 10h
    mov textattr,ah
    mov normattr,ah
{set defaults}
    mov directvideo,true
    mov checkbreak,true
    mov checkeof,false

{hook into int 21 to trap console reads/writes                            }
{I'd like to apologize to everyone for that mess, especially if you're not}
{an all- day ASM coder. It works, anyway.                                 }

{get old interrupt vector}
    mov ax,0204h
    mov bl,21h
    int 31h
    jc @@00
    mov dword ptr ds:[offset @@21+1],edx
    mov word ptr ds:[offset @@21+5],cx

{set new one}

    mov cx,cs
    mov edx,offset @@hook
    inc eax
    int 31h
    jc @@00

    jmp @@5

@@hook:
    cmp ah,3fh
    jz @@tread
    cmp ah,40h
    jz @@twrite

@@21:
    db  0eah {jmp far}
    dd	0
    dw  0

@@tread:

    test bx,bx 
    jnz @@21
    sub	eax,eax
    pushad


@@r00:
    sub ah,ah
    int 16h

    cmp al,20h
    jnc @@r04

{is control char}
    cmp al,8
    jnz @@r01

    call nuke_one
    cmp dword ptr [esp+28],0
    jz @@r00    
    dec dword ptr [esp+28]    
    jmp @@r00

@@r01:
    cmp al,13
    jz @@r03
    cmp al,27
    jnz @@r00


@@r02:
    cmp dword ptr [esp+28],0
    jz @@r00
    call nuke_one
    dec dword ptr [esp+28]
    jmp @@r02

@@r04:
    mov ebx,[esp+28]
    inc ebx
    inc ebx
    cmp ebx,[esp+24]
    jnc @@r00
    dec ebx
    dec ebx
    inc dword ptr [esp+28]
    add ebx,[esp+20]
    mov [ebx],al
    call put_one
    jmp @@r00

@@r03:
{all done}
    mov ebx,[esp+28]
    add dword ptr [esp+28],2
    add ebx,[esp+20]
    mov word ptr [ebx],0a0dh
    call put_cr
    call put_lf
    popad
    and byte ptr [esp+8],0feh
    iretd

@@twrite:
    cmp bx,1
    jnz @@21

    push ebx
    test ecx,ecx
    jz @@w01
    push edx
    push ecx

@@w00:
    mov al,[edx]
    inc edx
    push edx
    push ecx
    call @@onscreen
    pop ecx
    pop edx
    loop @@w00

    pop ecx
    pop edx

@@w01:
    mov eax,ecx
    pop ebx
    and byte ptr [esp+8],0feh
    iretd

@@onscreen:
    cmp al,7
    jnz @@o1
    mov ah,0eh
    int 10h
    retn

@@o1:
    cmp al,8
    jnz @@o2
    call nuke_one
    retn

@@o2:
    cmp al,10
    jnz @@o3
    call put_lf
    retn

@@o3:
    cmp al,13
    jnz @@o4
    call put_cr
    retn

@@o4:

    call put_one
    retn

@@5:
    pop ebx
  end;
end.
[ RETURN TO DIRECTORY ]