home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frostbyte's 1980s DOS Shareware Collection
/
floppyshareware.zip
/
floppyshareware
/
GLEN
/
STUFF30.ZIP
/
STUFFIT.ASM
next >
Wrap
Assembly Source File
|
1990-09-06
|
24KB
|
1,222 lines
PAGE 80,132
TITLE "StuffIt, Delayed keyboard stuffer. (C) Terje Mathisen 1989-90"
Version EQU 300h
VerStr EQU '3.00'
LOCALS
TicksPrDay EQU 1573041
TicksPrHour EQU 65543
BIOS SEGMENT AT 40h
ORG 1Ah
BufferHead dw ?
BufferTail dw ?
BufferStart dw 16 dup (?)
BufferEnd LABEL word
ORG 49h
VideoMode db ?
CrtWidth dw ?
ORG 4Eh
CurrStart dw ?
Cursor dw ?
ORG 6Ch
BIOS_Timer dw 2 dup (?)
BIOS ENDS
BOOT SEGMENT AT 0F000h
ORG 0FFF0h
RebootLocation LABEL FAR
BOOT ENDS
REBOOT_CODE EQU 0
ATTIME_CODE EQU 1
DELTATIME_CODE EQU 2
FIND_CODE EQU 3
PROMPT_CODE EQU 4
EXTENDED_CODE EQU 255
GETWORD_CODE EQU 254
CODE SEGMENT PARA PUBLIC 'code'
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING
ORG 0
PspStart label byte
ORG 5Ch
ResidentSize dw ?
;LowStart label byte
ORG 80h
CommandLen db ?
CommandLine LABEL BYTE
ORG 100h
start:
jmp init
Semafor equ 'ST' ; Short for STuffit
LowStart label byte
HighStart label byte
MoveDown EQU HighStart - LowStart
Int2F proc far
cmp ax,0E000h
jne @@chain
cmp dx,Semafor ; Be safe, insist on semafor in DX
je @@We_Are_Here
@@chain:
; jmp [OldInt2F]
db 0EAh
OldInt2F dd ?
@@We_Are_Here:
mov al,0FFh
mov dx,cs
mov bx,Version
iret
Int2F endp
PUSH_AX_OPCODE EQU 50h
IRET_OPCODE EQU 0CFh
MyTimer PROC FAR
pushf
; call [OldTimer]
db 09Ah
OldTimer dw ?,?
SelfModify label byte ; This will be IRET when idle
push ax ; PUSH AX = 50h, IRET = 0CFh
inc byte ptr [cs:active-MoveDown] ; INC from -1 to 0
jnz Already_Active
STI
CLD
push bx
push cx
push dx
push si
push di
push ds
push es
push cs
pop ds
ASSUME DS:CODE
mov es,[BiosSeg-MoveDown]
ASSUME ES:BIOS
call word ptr [StuffMode-MoveDown]
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING
Already_Active:
CLI
dec byte ptr [cs:active-MoveDown]
pop ax
iret
MyTimer ENDP
ASSUME CS:CODE,DS:CODE,ES:BIOS
StuffFinished:
mov [SelfModify-MoveDown],IRET_OPCODE
ret
NextKey proc near
mov si,[StuffPtr-MoveDown]
GetNext:
cmp si,[StuffEnd-MoveDown]
jae StuffFinished
lodsb
cmp al,GETWORD_CODE
ja Extended ; Extended function
je @@GetBoth ; 254 => char, scan follows
mov ah,2 ; Simulate scan = 2 for normal chars
cmp al,224 ; Character for Enh.Kbd new keys
je @@GetScan
or al,al
jne stuff
@@GetScan:
mov ah,[si]
inc si
jmp short stuff
@@GetBoth:
lodsw
Stuff:
CLI
mov di,[BufferTail]
stos word ptr [BIOS:di]
cmp di, OFFSET BufferEnd
jb @@1
mov di, OFFSET BufferStart
@@1:
cmp di,[BufferHead]
je @@Overflow
mov [BufferTail],di
STI
StuffOK:
mov [StuffPtr-MoveDown],si
jmp GetNext
@@OverFlow:
STI
ret
NextKey ENDP
ExtendedTable label word
dw Reboot - MoveDown
dw AbsTime - MoveDown
dw DeltaTime - MoveDown
dw StartFind - MoveDown
dw StartPrompt - MoveDown
Extended PROC near
lodsb ; Get function code!
cmp al,4
ja StuffFinished ; Program Error!
cbw
mov bx,ax
shl bx,1
jmp ExtendedTable[bx - MoveDown]
Extended endp
Reboot proc near
mov word ptr [BIOS: 72h],1234h
jmp RebootLocation
Reboot endp
AbsTime: ; Both Abs time & Delta time land here
DeltaTime: ; ---- " ----
GetTime proc near
cmp al, DELTATIME_CODE
lodsw ; Next 3 bytes is # of ticks
mov dl,[si]
jz @@TimeOk ; Delta time, so wait # of ticks
; Wait until time equal: Calculate remaining ticks
sub ax,[Bios_Timer]
sbb dl,BYTE PTR [Bios_Timer+2]
jae @@TimeOk
add ax,TicksPrDay AND 0FFFFh
adc dl,TicksPrDay Shr 16
@@TimeOK:
inc si
mov [StuffPtr-MoveDown],si ; Point to next byte
sub dh,dh ; Fill top of DX with 0
or ax,dx
jz @@WaitZero ; Special case, wait for empty kbd
mov [CountLow-MoveDown],ax
mov [CountHigh-MoveDown],dl
mov [StuffMode-MoveDown], OFFSET CountDown - MoveDown
ret
@@WaitZero:
mov [StuffMode-MoveDown], OFFSET WaitEmpty - Movedown
ret
GetTime endp
CountDown proc near
DEC [CountLow-MoveDown]
jnz NoChange
SUB [CountHigh-MoveDown],1
jae NoChange
StartNextKey:
mov [StuffMode-MoveDown], OFFSET NextKey - MoveDown
NoChange:
ret
CountDown endp
WaitEmpty proc near
mov ax,[BufferHead]
cmp ax,[BufferTail]
je StartNextKey
ret
WaitEmpty endp
StartPrompt:
mov [StuffMode-MoveDown], offset ScanPrompt - MoveDown
mov [StuffPtr-MoveDown],si
; si -> dx:BYTE, dy:BYTE, count:WORD, len:BYTE, attr:BYTE, st:BYTE * len
ScanPrompt: ; Find start posn'n
mov si,[StuffPtr-MoveDown]
lodsw ; AL = dx, AH = dy
mov cx,[Cursor] ; CL = X, CH = y
sub cl,al
jae @@1
sub cl,cl
@@1:
sub ch,ah
jae @@2
sub ch,ch
@@2:
mov al,byte ptr [CrtWidth]
mul ch ; AX = Y-offset
sub ch,ch
add ax,cx
shl ax,1 ; AX = Start of scan
jmp short Scan1
StartFind:
mov [StuffMode-MoveDown], offset ScanText - MoveDown
mov [StuffPtr-MoveDown],si
; si -> start:WORD, count:WORD, len:BYTE, attr:BYTE, st:BYTE * len
ScanText proc near
mov si,[StuffPtr-MoveDown]
lodsw ; starting offset in screen
Scan1:
mov di,ax
lodsw
mov cx, ax ; # of char cells to search
lodsw ; AL = len, AH = attr
mov dl,al
xor dh,dh ; Text len
dec dx ; Skip first char in length
mov bx, 0B000h
cmp [VideoMode],7
je @@1
cmp [VideoMode],3
ja @@done
mov bh,0B8h
@@1:
mov es,bx ; ES -> video segment
ASSUME ES:NOTHING
inc si ; Skip first char
cmp ah,255 ; ATTR = 255 -> no attr
je @@FindChar
@@FindCharAttr:
mov al,[si-1] ; First char to match
repne scasw
jne @@done
or dx,dx ; Remaining length = 0
jz @@found
push cx
push si
push di
mov cx,dx
@@l2:
lodsb
scasw
loope @@l2
pop di
pop si
pop cx
jne @@FindCharAttr
je @@found
@@FindChar:
mov al,[si-1]
dec di
@@l3:
inc di
scasb
loopne @@l3
jne @@done
or dx,dx
jz @@found
push cx
push si
push di
mov cx,dx
@@l4:
inc di
cmpsb
loope @@l4
pop di
pop si
pop cx
jne @@l3
@@found:
add si,dx ; Point after text to search for
mov [StuffMode-MoveDown], offset NextKey - MoveDown
mov [StuffPtr-MoveDown],si
@@done:
ret
ScanText Endp
ALIGN 2
BiosSeg dw 40h
FirstByteToCopy label byte
StuffPtr dw OFFSET StuffBuffer - MoveDown
StuffEnd dw ?
StuffMode dw OFFSET NextKey - MoveDown
CountLow dw ?
CountHigh db ? ; Use 24 bits for tick counter
active db -1
ResidentEnd EQU $
StuffBuffer LABEL byte
StartMsg db 'StuffIt V',VerStr,' (C) Terje Mathisen 1989-90',13,10,'$'
SyntaxMsg label byte
db 'Syntax: Stuffit <commands>',13,10
db ' Valid commands:',13,10
db ' +|=[[hh:]mm:]ss | Delay for(+) or until(=) a specified time.',13,10
db ' +45 will wait for 45 seconds.',13,10
db ' =14:00:00 will wait until 2pm.',13,10
db ' +0 will wait until the kbd buffer is empty.',13,10
db ' <character code> | Stuff a given character code.',13,10
db ' 27 = <Esc>, 13 = <CR> etc.',13,10
db ' @<scan code> | Stuff a given scan code (char=0).',13,10
db ' @68 = F10, @73 = PgUp etc.',13,10
db ' <char>:<scan> | Specify both character and scan.',13,10
db ' 43:74 = <Num+>',13,10
db " 'TEXT'",' or "TEXT" | Stuff all the characters in TEXT',13,10
db ' F<x>,<y>,<n>[,attr],"STRING" | Find "STRING" in an area starting at (X,Y),',13,10
db " and beeing (N) char's long.",13,10
db ' Ignore text attributes, unless <attr> is specified.',13,10
db ' P<dx>,<dy>,<n>[,attr],"STRING" | Find Prompt "STRING", starting at',13,10
db " (CursorX-DX,CursorY-DY).",13,10
db ' ! | Reboot. (=0 ! will reboot at midnight.)',13,10
db ' /F:FileName | Read commands from <FileName>.',13,10
db ' /B:nnnn | Allocate room for <nnnn> bytes in TSR(512 default).',13,10
db 'StuffIt /R will Remove (Unload) StuffIt from RAM.',13,10,'$'
UpdateMsg db 'Resident copy updated!',13,10,'$'
StayResMsg db 'Resident code loaded!',13,10,'$'
RemovedMsg db 'Resident code removed from RAM!',13,10,'$'
WrongVerMsg db 'A different version of StuffIt is already resident!',13,10,'$'
NotRemovedMsg label byte
db 'Resident code cannot be removed, as another '
db 'program is using the',13,10
db 'Timer (Int 8) and/or the Multiplex (Int 2F) '
db 'vector. Please remove all',13,10
db 'programs loaded after StuffIt, '
db 'and retry the operation.',13,10,'$'
RamErrMsg label byte
db 'Not enough RAM! (Need at least 128 kB to initialize program.)',13,10,'$'
FileErrMsg label byte
db 'Error reading input file!',13,10,'$'
ResidentToSmallMsg label byte
db 'Resident buffer to small! Try to remove it (/R) and reload.',13,10,'$'
;FirstBlock dw ?
;SecondBlock dw ?
JUMPS ; Allow inefficient code here!
Init proc near
ASSUME DS:CODE, ES:CODE
mov ax,OFFSET StuffBuffer - MoveDown + 512 + 15
and ax,0FFF0h
mov [ResidentSize], ax
; Start by relocating the program into a second segment
; mov [FirstBlock],CS ; Save segment addr
mov ah,4Ah
mov bx,2000h ; Realloc to 128 kB
; mov es,[FirstBlock]
int 21h
mov dx, offset RamErrMsg
jc ErrorMessage
; mov ah,48h
; mov bx,1000h
; int 21h ; Alloc second 64kB block
; mov dx, offset RamErrMsg
; jc ErrorMessage
mov ax,cs
add ax,1000h ; Point after 1st 64kB
mov es,ax
; mov [SecondBlock],es
sub si,si
mov di,si
mov cx,(OFFSET ProgramEnd - OFFSET PspStart + 1) Shr 1
cld
rep movsw
push es
mov ax, OFFSET Continue
push ax
retf
; pop cs ; Jump into second copy of program!
Continue:
push ds
push cs
pop ds
pop es ; DS=CS = SecondBlock, ES=FirstBlock
; Move resident part of code as low as possible:
mov si, OFFSET HighStart
mov di, OFFSET LowStart
mov cx, OFFSET ResidentEnd - OFFSET HighStart
rep movsb
mov dx, OFFSET StartMsg
mov ah,9
int 21h
call Parse
cmp di, OFFSET StuffBuffer - MoveDown
je Syntax ; No parameter
push es
pop ds ; DS,ES,SS = FirstBlock
mov [StuffEnd-MoveDown],di
; mov [StuffMode-MoveDown], OFFSET NextKey - MoveDown
; mov [StuffPtr-MoveDown], OFFSET StuffBuffer - MoveDown
; mov [active-MoveDown],-1 ; Initialize [active] flag
; mov [BiosSeg-MoveDown],40h ; Fast load of ES: when resident
call TestSecond ; Don't return if second copy!
; Get old int 2F interrupt
mov ax,352Fh
int 21h
mov WORD PTR [OldInt2F-MoveDown],BX
mov WORD PTR [OldInt2F+2-MoveDown],ES
; Get old timer interrupt
mov ax,3508h
int 21h
mov WORD PTR [OldTimer-MoveDown],BX
mov Word Ptr [OldTimer+2-Movedown],ES
; Enter our routine first
mov ax,252Fh
mov dx, OFFSET Int2F - MoveDown
int 21h
mov ax,2508h
mov dx, OFFSET MyTimer - MoveDown
int 21h
mov ES, [DS:2Ch]
mov ah,49h
int 21h
mov word ptr [DS:2Ch],0 ; Signal no environment!
push ds
push cs
pop ds
mov dx, OFFSET StayResMsg
mov ah,9
int 21h
pop ds
mov cx,5
@@CloseLoop:
mov bx,cx
dec bx
mov ah,3Eh
int 21h
loop @@CloseLoop
push ss
pop ds
mov dx, [DS:ResidentSize]
cmp dx, [DS:Stuffend-MoveDown]
ja @@OK
mov dx, [DS:StuffEnd-MoveDown] ; DX = MAX(ResidentSize, StuffEnd)
mov [DS:ResidentSize],dx
@@OK:
add dx,15
mov cl,4
shr dx,cl
mov ax,3100h
int 21h ; Go TSR with first block
Init Endp
FindFirst proc near
mov dx,Semafor
mov ax,0E000h
xor bx,bx
int 2Fh
cmp al,0FFh
jne @@done
cmp bx, Version
je @@done
mov dx, offset WrongVerMsg
jmp ErrorMessage
@@done:
ret ; Return ZERO if found
FindFirst endp
TestSecond proc near
call FindFirst
jne @@NotFound
mov es,dx ; Save segment
; This is the second copy! ES -> to first copy
; Test if enough room in resident program:
mov ax,[StuffEnd-MoveDown]
cmp ax,[ES:ResidentSize]
mov dx, OFFSET ResidentToSmallMsg
ja ErrorMessage
; Will now move all data into first copy, including pointers and StuffMode
mov [ES:SelfModify-MoveDown], IRET_OPCODE ; Stop resident program
mov [DS:SelfModify-MoveDown], IRET_OPCODE ; Stop this version!
mov si, offset FirstByteToCopy - MoveDown
mov di, si
mov cx, ax ; [StuffEnd]
sub cx,si
rep movsb
mov [ES:SelfModify-MoveDown], PUSH_AX_OPCODE ; Restart resident version
mov dx, OFFSET UpdateMsg
mov ah,9
int 21h
mov ax,4C00h
int 21h
@@NotFound:
ParseFinish:
ret
TestSecond ENDP
LocalSyntax:
jmp Syntax
EOF EQU 26
Parse Proc near
cld
mov si, OFFSET CommandLine
mov bl,[si-1]
sub bh,bh
mov byte ptr [si+bx],EOF
RestartParse:
mov di, OFFSET StuffBuffer - MoveDown
ParseNextChar:
lodsb
cmp al,EOF
je ParseFinish
cmp al,' '
jbe ParseNextChar
cmp al,'0'
jb @@NotDigit
cmp al,'9'
ja @@NotDigit
@@Digit:
dec si
mov bx,255
call GetNumber
cmp al,':'
mov al,bl
mov ah,2
jne @@NotTwo
push ax
mov bx,255
call GetNumber
cmp al,':'
je LocalSyntax
pop ax
mov ah,bl
@@NotTwo:
Call SaveChar
jmp ParseNextChar
@@NotDigit:
cmp al,'/'
je ParseOption
cmp al,'@'
jne @@NotFunc
mov bx,255
call GetNumber
mov ah,bl
xor al,al
stosw
jmp ParseNextChar
@@NotFunc:
cmp al,"'"
je @@Quote
cmp al,'"'
jne @@NotQuote
@@Quote:
mov bl,al ; Save starting quote char
@@2:
lodsb
cmp al,13 ; Missing last quote
je Syntax
cmp al,bl ; Ending quote?
je ParseNextChar ; Yes, restart
mov ah,2 ; Assume scan = 2
call SaveChar
jmp @@2
@@NotQuote:
cmp al,'+'
jne @@3
jmp DeTime
@@3:
cmp al,'='
jne @@4
jmp AtTime
@@4:
; Use ! to signal Reboot
cmp al,'!'
je SignalReboot
cmp al,'f'
je FindNear
cmp al,'F'
je FindNear
cmp al,'p'
je PromptNear
cmp al,'P'
je PromptNear
; Fall into syntax error!
Parse endp
Syntax Proc near
mov dx, OFFSET SyntaxMsg
ErrorMessage:
push cs
pop ds
mov ah,9
int 21h
mov ax,4C01h
int 21h
Syntax Endp
FindNear:
jmp FindText
PromptNear:
jmp PromptText
SignalReBoot:
mov ax,EXTENDED_CODE + (REBOOT_CODE * 256)
stosw
jmp ParseNextChar
ReadFile proc near
cmp byte ptr [si],':'
jne @@Skip
inc si
@@Skip:
mov dx,si
@@Next:
lodsb
cmp al,' '
ja @@Next
dec si
mov ax,3D00h ; Open file for Read_Only
mov byte ptr [si],al ; Make ASCIIZ filename
int 21h
mov dx, OFFSET FileErrMsg
jc ErrorMessage
mov bx,ax
mov ah,3Fh ; Read File
mov dx, OFFSET ProgramEnd
mov si,dx
mov cx, - (OFFSET ProgramEnd) ; Max Size in segment
int 21h
mov dx, OFFSET FileErrMsg
jc ErrorMessage
add si, ax
mov byte ptr [si],EOF
sub si, ax ; Point back to start of filebuffer
mov ah,3Eh
int 21h ; Close this file
jmp RestartParse ; Parse file buffer!
ReadFile endp
SetBufferSize proc near
cmp byte ptr [si],':'
jne @@Skip
inc si
@@Skip:
mov bx,-( OFFSET StuffBuffer - MoveDown) + 32; Max text buffer
call GetNumber
add bx,OFFSET StuffBuffer - MoveDown + 15
and bx,0FFF0h
mov [ES:ResidentSize],bx
jmp ParseNextChar
SetBufferSize endp
ParseOption Proc near
lodsb
cmp al,'a'
jb @@Upper
cmp al,'z'
ja @@Upper
sub al,'a'-'A'
@@Upper:
cmp al,'B'
je SetBufferSize
cmp al,'F'
je ReadFile
cmp al,'R'
jne Syntax
@@Remove:
call FindFirst
jnz Syntax ; First copy, nothing to remove!
mov ax,3508h
int 21h
cmp bx, OFFSET MyTimer - MoveDown
jne @@CannotRemove
mov ax, es
cmp ax, dx
jne @@CannotRemove ; Other TSR has timer vector!
mov ax,352Fh
int 21h
cmp bx, OFFSET Int2F - MoveDown
jne @@CannotRemove
mov ax, es
cmp ax, dx
jne @@CannotRemove ; Other TSR has Int 2F vector!
; OK to remove previous copy from RAM
; First, restore timer vector
push ds
mov dx,[word ptr ES:OldTimer-MoveDown]
mov ds,[word ptr ES:OldTimer+2-MoveDown]
mov ax,2508h
int 21h
; Then, retore Int 2F vector
mov dx,[word ptr ES:OldInt2F-MoveDown]
mov ds,[word ptr ES:OldInt2F+2-MoveDown]
mov ax,252Fh
int 21h
pop ds
; Next, release the memory segment
; ES -> to previos copy!
mov ah,49h
int 21h
mov dx, OFFSET RemovedMsg
mov ah,9
int 21h
mov ax,4C00h
int 21h
@@CannotRemove:
mov dx, OFFSET NotRemovedMsg
mov ah,9
int 21h
mov ax,4C01h
int 21h
ParseOption Endp
hour dw ?
min dw ?
sec dw ?
DeTime Proc near
mov ax,EXTENDED_CODE + (DELTATIME_CODE * 256)
jmp short ParseTime
AtTime:
mov ax,EXTENDED_CODE + (ATTIME_CODE * 256)
ParseTime:
stosw ; Save marker for time
; FIX BUG found by davidgb. HOUR and Min MUST be initialized to ZERO!
xor ax,ax
mov [hour],ax
mov [min],ax
; END OF bug-fix
mov bx,59 ; Max value
call GetNumber
cmp al,':'
jne @@SaveSec
mov [min],bx
mov bx,59
call GetNumber
cmp al,':'
jne @@SaveSec
xchg bx,[min]
mov [hour],bx
mov bx,59
call GetNumber
cmp al,':'
je NearError
@@SaveSec:
mov [sec],bx
; Now convert hour:min:sec into Timer ticks:
; Ticks= (hour*TicksPrHour) + (((min*60)+sec) * 34829 + 956) DIV 1913
mov ax,(TicksPrHour - 65536)
mul [hour]
add dx,[hour] ; DX:AX = hour*TicksPrHour
push ax ; Save DX:AX
push dx ; --- " ---
mov al,60
mul [byte ptr min]
add ax,[sec]
mov dx,34829
mul dx
add ax,1913 Shr 1
adc dx,0
mov bx,ax
mov ax,dx
xor dx,dx
mov cx,1913
div cx
xchg ax,bx
div cx ; BX:AX = Ticks in (min*60+sec)
pop dx
pop cx ; DX:CX = Ticks in hours
add ax,cx
adc dx,bx ; DX:AX = Total Ticks
stosw ; Save Low word of count
mov al,dl
stosb ; Save high byte of count
jmp ParseNextChar
DeTime Endp
PromptText:
mov ax,EXTENDED_CODE + (PROMPT_CODE * 256)
stosw
mov bx,127
call GetNumber
cmp al,','
jne NearError
mov al,bl ; Save X value
stosb
mov bx,60 ; 0<y<=60
call GetNumber
cmp al,','
jne NearError
mov al,bl
stosb ; Save Y value
jmp short Text1
NearError:
jmp syntax
FindText proc near
mov ax,EXTENDED_CODE + (FIND_CODE * 256) ; 255 + 3 -> flag for Find Text
stosw
mov bx,132 ; 0<x<=132
call GetNumber
cmp al,','
jne GetError
sub bl,1
jb GetError
mov cx,bx ; Save X value
mov bx,60 ; 0<y<=60
call GetNumber
cmp al,','
jne GetError
sub bl,1
jb GetError
mov al,80
mul bl
add ax, cx
shl ax, 1
stosw ; Save X,Y as starting offset
Text1:
mov bx,(132*60) ; Get length to search in
call GetNumber
cmp al,','
jne NearError
or bx,bx
jz NearError ; Count must be > 0
mov ax,bx
stosw ; Save buffer length
mov bx,255
cmp byte ptr [si],'"'
je @@SkipAttr
cmp byte ptr [si],"'"
je @@SkipAttr
call GetNumber
cmp al,','
jne NearError
@@SkipAttr:
mov ah,bl
lodsb
cmp al,'"'
je @@1
cmp al,"'"
jne GetError
@@1:
mov bx,di ; Save current pos for len
stosw ; Store len + attr
mov ah,al
xor cx,cx
@@2:
lodsb
cmp al,EOF
je GetError
cmp al,13
je GetError
cmp al,ah
je @@3
inc cx ; INC len
stosb ; Save Text
jmp @@2
@@3:
mov [ES:bx],cl ; Save actual length!
jmp ParseNextChar
FindText endp
GetError:
jmp syntax
GetNumber proc near
; input: SI -> first char to convert, BX = max value
push cx ; Use as temp buffer
push dx ; For mul
push di ; For sign flag
sub cx,cx
mov ah,ch
mov di,cx ; Zero DI -> Positive
cmp byte ptr [si],'-'
jne @@GetLoop
dec di
inc si
@@GetLoop:
lodsb
cmp al,' '
jbe @@GetEnd
cmp al,':'
je @@GetEnd
cmp al,','
je @@GetEnd
sub al,'0'
jb GetError
cmp al,9
ja GetError
; Valid decimal digit!
xchg ax,cx
mov dx,10
mul dx
add cx,ax
jmp @@GetLoop
@@GetEnd:
cmp al,EOF
jne @@1
dec si ; Prepare to reload AL
@@1:
cmp cx,bx ; Valid value?
ja GetError
mov bx,cx
or di,di
jz @@done
neg bx ; return -BX
@@done:
pop di
pop dx
pop cx
ret
GetNumber endp
SaveChar proc near
; AL, AH = char, scan to save in StuffBuffer
or al,al
je @@Save2
cmp al,224
je @@Save2
cmp al,254
jae @@Save3
cmp ah,2 ; Scan for normal chars
jne @@Save3
mov ah,14
cmp al,8
je @@Save3
mov ah,15
cmp al,9
je @@Save3
mov ah,28
cmp al,13
je @@Save3
mov ah,1
cmp al,27
je @@Save3
; Normal character, store just the char itself
stosb
ret
@@Save2:
stosw
ret
@@Save3:
mov byte ptr [es:di],254
inc di
stosw
ret
SaveChar endp
ProgramEnd label byte
CODE ENDS
END start