[Back to MISC SWAG index] [Back to Main SWAG index] [Original]
{ Updated MISC.SWG on May 26, 1995 }
{
This unit has many many features, including, but not limited to:
o InterNational upper and lower casing functions.
o Easy reference to the keys, via consts
o Easy access to direct screen writes (written in Assembler of course)
o FAST wild card compare routine (thanks to Arne de Bruijn)
o Access to miscellanous video commands, like blinkon and blinkoff, cursor
on, and cursor off.
o Much more to improve in overall performance of your code.
{$A+,B-,D-,E+,F-,G-,I-,L+,N-,O-,R-,S-,V+,X+}
{$M 16384,0,655360}
Unit AsmMisc; { by John MD Stephenson }
{ Country specific case conversation and other info retrieval.
Donated to the public domain by Bj�rn Felten @ 2:203/208. }
{ Arne de.Bruijn wrote the WildComp function }
{ The UnCrunch routine comes from TheDraw - public domain }
{ All other code is written by myself, or Public Domain }
{ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿}
{³ } Interface { ³}
{ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ}
Uses Dos,Crt;
{ For easy reference to keys }
Const
_Home = #71;
_End = #79;
_Up = #72;
_Down = #80;
_Left = #75;
_Right = #77;
_PageUp = #73;
_PageDown = #81;
_Insert = #82;
_Delete = #83;
_CtrlPageUp = #132;
_CtrlPageDown = #118;
_CtrlHome = #119;
_CtrlEnd = #117;
_F1 = #59;
_F2 = #60;
_F3 = #61;
_F4 = #62;
_F5 = #63;
_F6 = #64;
_F7 = #65;
_F8 = #66;
_F9 = #67;
_F10 = #68;
{ First row }
_AltQ = #16;
_AltW = #17;
_AltE = #18;
_AltR = #19;
_AltT = #20;
_AltY = #21;
_AltU = #22;
_AltI = #23;
_AltO = #24;
_AltP = #25;
{ Second row }
_AltA = #30;
_AltS = #31;
_AltD = #32;
_AltF = #33;
_AltG = #34;
_AltH = #35;
_AltJ = #36;
_AltK = #37;
_AltL = #38;
{ Forth row }
_AltZ = #44;
_AltX = #45;
_AltC = #46;
_AltV = #47;
_AltB = #48;
_AltN = #49;
_AltM = #50;
{ Number row }
_Alt1 = #120;
_Alt2 = #121;
_Alt3 = #122;
_Alt4 = #123;
_Alt5 = #124;
_Alt6 = #125;
_Alt7 = #126;
_Alt8 = #127;
_Alt9 = #128;
_Alt0 = #129;
_Alt_Dash = #130;
_Alt_Equal= #131;
{ Variations }
_AltF1 = #104;
_AltF2 = #105;
_AltF3 = #106;
_AltF4 = #107;
_AltF5 = #108;
_AltF6 = #109;
_AltF7 = #110;
_AltF8 = #111;
_AltF10 = #112;
_ShiftF1 = #84;
_ShiftF2 = #85;
_ShiftF3 = #86;
_ShiftF4 = #87;
_ShiftF5 = #88;
_ShiftF6 = #89;
_ShiftF7 = #90;
_ShiftF8 = #91;
_ShiftF10 = #92;
type
DelimType = record
thousands,
decimal,
date,
time: array[0..1] of Char;
end;
CurrType = (leads, { symbol precedes value }
trails, { value precedes symbol }
leads_, { symbol, space, value }
_trails, { value, space, symbol }
replace); { replaced }
datefmt = (USA,Europe,Japan);
CountryType = record
DateFormat : Word; { 0: USA, 1: Europe, 2: Japan }
CurrSymbol : array[0..4] of Char;
Delimiter : DelimType; { Separators }
CurrFormat : CurrType; { Way currency is formatted }
CurrDigits : Byte; { Digits in currency }
Clock24hrs : Boolean; { True if 24-hour clock }
CaseMapCall : procedure; { Lookup table for ASCII > $80 }
DataListSep : array[0..1] of Char;
CID : word;
Reserved : array[0..7] of Char;
end;
CountryInfo = record
case InfoID: byte of
1: (IDSize : word;
CountryID : word;
CodePage : word;
TheInfo : CountryType);
2: (UpCaseTable: pointer);
end;
var
CountryOk : Boolean; { Could determine country code flag }
CountryRec : CountryInfo;
Maxwidth,maxheight : Byte;
ScreenSize : Word;
{ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ}
Procedure BlinkOff;
Procedure BlinkOn;
Procedure CLI; Inline($FA);
Procedure CursorOff;
Procedure CursorOn;
Procedure GetBorder(var color: byte);
Procedure SetBorder(color: byte);
Procedure PutAttrs(x,y: byte; times: word);
Procedure PutChars(x,y: byte; chr: char; times: word);
Procedure PutString(x,y: byte; s: string);
Procedure ReallocateMemory(P: Pointer);
Procedure Retrace;
Procedure StuffChar(c: char);
Procedure STI; Inline($FB);
Procedure UnCrunch(var Addr1,Addr2; BlkLen: Word);
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Function Execute(Name,tail: pathstr): Word;
Function FileExists(filename: PathStr): Boolean;
Function LoCase(c: Char) : Char;
Function LoCaseStr(s: String): String;
Function Upcase(c: Char) : Char;
Function UpCaseStr(s: String): String;
Function WildComp(NameStr,SearchStr: String): Boolean;
{ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ}
type
screentype = array[0..7999] of byte;
Var
Segment : word;
Screenaddr: ^screentype;
LoTable : array[0..127] of byte;
CRP, LTP : pointer;
{ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿}
{³ } Implementation { ³}
{ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ}
Procedure SetBorder(color : byte); assembler;
asm
mov ax, $1001
mov bh, color
int $10
End;
Procedure GetBorder(var color : byte); assembler;
asm
mov ax, $1008
int $10
les DI, color
mov [ES:DI], bh
end;
procedure CursorOff; assembler;
asm
mov ah, $01 { Cursor function }
mov cx, $FFFF { Set new cursor value }
int $10 { Video interrupt }
end;
procedure CursorOn; assembler;
asm
mov ah, $01 { Cursor function }
mov cx, 1543 { Set new cursor value }
int $10 { Video interrupt }
end;
procedure StuffChar(c : char); assembler;
asm
mov ah, $05
mov cl, c { cl = c }
xor ch, ch { ch = 0 }
int $16
end;
Procedure BlinkOff; assembler;
{ Note that the BL is the actual register, but BH _should_ also be set to 0 }
asm
mov ax, $1003
mov bx, $0000
int $10
end;
Procedure BlinkOn; assembler;
{ Note that the BL is the actual register, but BH _should_ also be set to 0 }
asm
mov ax, $1003
mov bx, $0001
int $10
end;
Procedure Retrace; assembler;
{ waits for a vertical retrace }
asm
mov dx, $03DA
@loop1:
in al, dx
test al, 8
jz @loop1
@loop2:
in al, dx
test al, 8
jnz @loop2
end;
procedure UnCrunch(var Addr1,Addr2; BlkLen:Word); assembler;
{ From TheDraw, not my procedure }
asm
PUSH DS { Save data segment.}
LDS SI, Addr1 { Source Address}
LES DI, Addr2 { Destination Addr}
MOV CX, BlkLen { Length of block}
JCXZ @Done
MOV DX,DI { Save X coordinate for later.}
XOR AX,AX { Set Current attributes.}
CLD
@LOOPA:
LODSB { Get next character.}
CMP AL,32 { If a control character, jump.}
JC @ForeGround
STOSW { Save letter on screen.}
@Next:
LOOP @LOOPA
JMP @Done
@ForeGround:
CMP AL,16 { If less than 16, then change the}
JNC @BackGround { foreground color. Otherwise jump.}
AND AH,0F0h { Strip off old foreground.}
OR AH,AL
JMP @Next
@BackGround:
CMP AL,24 { If less than 24, then change the}
JZ @NextLine { background color. If exactly 24,}
JNC @FlashBitToggle{ then jump down to next line.}
SUB AL,16 { Otherwise jump to multiple output}
ADD AL,AL { routines.}
ADD AL,AL
ADD AL,AL
ADD AL,AL
AND AH,8Fh { Strip off old background.}
OR AH,AL
JMP @Next
@NextLine:
ADD DX,160 { If equal to 24,}
MOV DI,DX { then jump down to}
JMP @Next { the next line.}
@FlashBitToggle:
CMP AL,27 { Does user want to toggle the blink}
JC @MultiOutput { attribute?}
JNZ @Next
XOR AH,128 { Done.}
JMP @Next
@MultiOutput:
CMP AL,25 { Set Z flag if multi-space output.}
MOV BX,CX { Save main counter.}
LODSB { Get count of number of times}
MOV CL,AL { to display character.}
MOV AL,32
JZ @StartOutput { Jump here if displaying spaces.}
LODSB { Otherwise get character to use.}
DEC BX { Adjust main counter.}
@StartOutput:
XOR CH,CH
INC CX
REP STOSW
MOV CX,BX
DEC CX { Adjust main counter.}
LOOPNZ @LOOPA { Loop if anything else to do...}
@Done:
POP DS { Restore data segment.}
end;
Procedure ReallocateMemory(P : Pointer); Assembler;
Asm
Mov AX, PrefixSeg
Mov ES, AX
Mov BX, word ptr P+2
Cmp Word ptr P,0
Je @OK
Inc BX
@OK:
Sub BX, AX
Mov AH, $4A
Int $21
Jc @Out
Les DI, P
Mov Word Ptr HeapEnd,DI
Mov Word Ptr HeapEnd+2,ES
@Out:
End;
Function Execute(Name, tail : pathstr) : Word; Assembler;
Asm
Push Word Ptr HeapEnd+2
Push Word Ptr HeapEnd
Push Word Ptr Name+2
Push Word Ptr Name
Push Word Ptr Tail+2
Push Word Ptr Tail
Push Word Ptr HeapPtr+2
Push Word Ptr HeapPtr
Call ReallocateMemory
Call SwapVectors
Call Dos.Exec
Call SwapVectors
Call ReallocateMemory
Mov AX, DosError
Or AX, AX
Jnz @Done
Mov AH, $4D
Int $21 { Return error in will be in AX (if any) }
@Done:
End;
Procedure Putchars(x, y : byte; chr : char; times : word);
{ Procedure to fill a count amount of characters from position x, y }
var offst: word;
begin
offst := (pred(y)*maxwidth+pred(x))*2;
asm
mov es, segment { Segment to start at }
mov di, offst { Offset to start at }
mov al, chr { Data to place }
mov ah, textattr { Colour to use }
mov cx, times { How many times }
cld { Forward in direction }
rep stosw { Store the word (cx times) }
end;
end;
Procedure PutAttrs(x,y: byte; times: word);
{ This procedure is to fill a certain amount of spaces with a colour }
{ (from cursor position) and doesn't move cursor position! }
var offst: word;
begin
offst := succ((pred(y)*maxwidth+pred(x))*2);
asm
mov es, segment
mov di, offst
mov cx, times
mov ah, 0
mov al, textattr
cld
@s1:
stosb
inc di { Increase another above what the stosb already loops }
loop @s1 { Loop until cx = 0 }
end;
end;
Procedure PutString(x, y: byte; s: string);
Begin
{ Does a direct video write -- extremely fast. }
asm
mov dh, y { move X and Y into DL and DH (DX) }
mov dl, x
xor al, al
mov ah, textattr { load color into AH }
push ax { PUSH color combo onto the stack }
mov ax, segment
push ax { PUSH video segment onto stack }
mov bx, 0040h { check 0040h:0049h to get number of screen columns }
mov es, bx
mov bx, 004ah
xor ch, ch
mov cl, es:[bx]
xor ah, ah { move Y into AL; decrement to convert Pascal coords }
mov al, dh
dec al
xor bh, bh { shift X over into BL; decrement again }
mov bl, dl
dec bl
cmp cl, $50 { see if we're in 80-column mode }
je @eighty_column
mul cx { multiply Y by the number of columns }
jmp @multiplied
@eighty_column: { 80-column mode: it may be faster to perform the }
mov cl, 4 { multiplication via shifts and adds: remember }
shl ax, cl { that 80d = 1010000b , so one can SHL 4, copy }
mov dx, ax { the result to DX, SHL 2, and add DX in. }
mov cl, 2
shl ax, cl
add ax, dx
@multiplied:
add ax, bx { add X in }
shl ax, 1 { multiply by 2 to get offset into video segment }
mov di, ax { video pointer is in DI }
lea si, s { string pointer is in SI }
SEGSS lodsb
cmp al, 00h { if zero-length string, jump to end }
je @done
mov cl, al
xor ch, ch { string length is in CX }
pop es { get video segment back from stack; put in ES }
pop ax { get color back from stack; put in AX (AH = color) }
@write_loop:
SEGSS lodsb { get character to write }
mov es:[di], ax { write AX to video memory }
inc di { increment video pointer }
inc di
loop @write_loop { if CX > 0, go back to top of loop }
@done: { end }
end;
end;
function WildComp(NameStr,SearchStr: String): Boolean; assembler;
{
Compare SearchStr with NameStr, and allow wildcards in SearchStr.
The following wildcards are allowed:
*ABC* matches everything which contains ABC
[A-C]* matches everything that starts with either A,B or C
[ADEF-JW-Z] matches A,D,E,F,G,H,I,J,W,V,X,Y or Z
ABC? matches ABC, ABC1, ABC2, ABCA, ABCB etc.
ABC[?] matches ABC1, ABC2, ABCA, ABCB etc. (but not ABC)
ABC* matches everything starting with ABC
(for using with DOS filenames like DOS (and 4DOS), you must split the
filename in the extention and the filename, and compare them seperately)
}
var
LastW: word;
asm
cld
push ds
lds si,SearchStr
les di,NameStr
xor ah,ah
lodsb
mov cx,ax
mov al,es:[di]
inc di
mov bx,ax
or cx,cx
jnz @ChkChr
or bx,bx
jz @ChrAOk
jmp @ChrNOk
xor dh,dh
@ChkChr:
lodsb
cmp al,'*'
jne @ChkQues
dec cx
jz @ChrAOk
mov dh,1
mov LastW,cx
jmp @ChkChr
@ChkQues:
cmp al,'?'
jnz @NormChr
inc di
or bx,bx
je @ChrOk
dec bx
jmp @ChrOk
@NormChr:
or bx,bx
je @ChrNOk
{From here to @No4DosChr is used for [0-9]/[?]/[!0-9] 4DOS wildcards...}
cmp al,'['
jne @No4DosChr
cmp word ptr [si],']?'
je @SkipRange
mov ah,byte ptr es:[di]
xor dl,dl
cmp byte ptr [si],'!'
jnz @ChkRange
inc si
dec cx
jz @ChrNOk
inc dx
@ChkRange:
lodsb
dec cx
jz @ChrNOk
cmp al,']'
je @NChrNOk
cmp ah,al
je @NChrOk
cmp byte ptr [si],'-'
jne @ChkRange
inc si
dec cx
jz @ChrNOk
cmp ah,al
jae @ChkR2
inc si {Throw a-Z < away}
dec cx
jz @ChrNOk
jmp @ChkRange
@ChkR2:
lodsb
dec cx
jz @ChrNOk
cmp ah,al
ja @ChkRange {= jbe @NChrOk; jmp @ChkRange}
@NChrOk:
or dl,dl
jnz @ChrNOk
inc dx
@NChrNOk:
or dl,dl
jz @ChrNOk
@NNChrOk:
cmp al,']'
je @NNNChrOk
@SkipRange:
lodsb
cmp al,']'
loopne @SkipRange
jne @ChrNOk
@NNNChrOk:
dec bx
inc di
jmp @ChrOk
@No4DosChr:
cmp es:[di],al
jne @ChrNOk
inc di
dec bx
@ChrOk:
xor dh,dh
dec cx
jnz @ChkChr { Can't use loop, distance >128 bytes }
or bx,bx
jnz @ChrNOk
@ChrAOk:
mov al,1
jmp @EndR
@ChrNOk:
or dh,dh
jz @IChrNOk
jcxz @IChrNOk
or bx,bx
jz @IChrNOk
inc di
dec bx
jz @IChrNOk
mov ax,[LastW]
sub ax,cx
add cx,ax
sub si,ax
dec si
jmp @ChkChr
@IChrNOk:
mov al,0
@EndR:
pop ds
end;
Function Upcasestr(S : String) : String; Assembler;
Asm
PUSH DS
LDS SI,S
LES DI,@Result
CLD
LODSB
STOSB
xor CH,CH
MOV CL,AL
JCXZ @OUT
@LOOP:
LODSB
xor ah, ah
push ax
call upcase
StoSb
Loop @Loop
@OUT:
POP DS
end;
Function Locasestr(S : String) : String; Assembler;
Asm
PUSH DS
LDS SI,S
LES DI,@Result
CLD
LODSB
STOSB
xor CH,CH
MOV CL,AL
JCXZ @OUT
@LOOP:
LODSB
xor ah, ah
push ax
call locase { So we're not duping a lot of instructions }
STOSB
LOOP @LOOP
@OUT:
POP DS
end;
{ Convert a character to upper case }
function UpCase; Assembler;
asm
mov al, c
cmp al, 'a'
jb @2
cmp al, 'z'
ja @1
sub al, ' '
jmp @2
@1:
cmp al, 80h
jb @2
sub al, 7eh
push ds
lds bx,CountryRec.UpCaseTable
xlat
pop ds
@2:
end; { UpCase }
{ Convert a character to lower case }
function LoCase; Assembler;
asm
mov al, c
cmp al, 'A'
jb @2
cmp al, 'Z'
ja @1
or al, ' '
jmp @2
@1:
cmp al, 80h
jb @2
sub al, 80h
mov bx,offset LoTable
xlat
@2:
end; { LoCase }
Function FileExists(filename: PathStr): Boolean; Assembler;
Asm
Push Ds
Lds Si, [filename] { Make ASCIIZ }
Xor Ah, Ah
Lodsb
XChg Ax, Bx
Mov Byte Ptr [Si+Bx], 0
Mov Dx, Si
Mov Ax, 4300h { Get file attributes }
Int 21h
Mov Al, False
Jc @1 { Fail? }
Inc Ax
@1:
Pop Ds
end; { FileExists }
Begin
{ Init the video addresses }
if lastmode = 7 then segment := $B000 else segment := $B800;
screenaddr := ptr(segment,$0000);
{ Init the video }
Maxwidth := succ(lo(windmax)); { Get maximum window positions, which are }
Maxheight := succ(hi(windmax)); { the maxwidth and maxheight to be precise! }
ScreenSize := maxheight*maxwidth*2; { For easy references to move commands. }
{ Init the tables for Upcasing }
Crp := @CountryRec;
Ltp := @LoTable;
asm
{ Exit if Dos version < 3.0 }
mov ah, 30h
int 21h
cmp al, 3
jb @1
{ Call Dos 'Get extended country information' function }
mov ax, 6501h
les di, CRP
mov bx,-1
mov dx,bx
mov cx,41
int 21h
jc @1
{ Call Dos 'Get country dependent information' function }
mov ax, 6502h
mov bx, CountryRec.CodePage
mov dx, CountryRec.CountryID
mov CountryRec.TheInfo.CID, dx
mov cx, 5
int 21h
jc @1
{ Build LoCase table }
les di, LTP
mov cx, 80h
mov ax, cx
cld
@3:
stosb
inc ax
loop @3
mov di, offset LoTable - 80h
mov cx, 80h
mov dx, cx
push ds
lds bx, CountryRec.UpCaseTable
sub bx, 7eh
@4:
mov ax, dx
xlat
cmp ax, 80h
jl @5
cmp dx, ax
je @5
xchg bx, ax
mov es:[bx+di], dl
xchg bx, ax
@5:
inc dx
loop @4
pop ds
mov [CountryOk], True
jmp @2
@1:
mov [CountryOk], False
@2:
end;
end.
[Back to MISC SWAG index] [Back to Main SWAG index] [Original]