Well... When I was a younger lad, one of my favorite jokes to play on folks was to install a program called 'Drip'. This was an 'invisible' TSR that would act at random (but decreasing) time intervals, selecting a character from the screen-map and 'dripping' it down the screen until just before it contacted another character. Of course, this didn't hurt anything, but certainly caused some humorous reactions (e.g. "EEEK! My screen is melting!!").
;a86 morasf7.a86 morasf7.com ;---------------------------------------------------------------------; ; COS 230 Final - Problem 7 Sean P. O. MacCath Moran ; ; ; ; Assignment: ; ; Weird Screen TSR. Do something weird with the screen. ; ; ; ; Description: ; ; Well... When I was a younger lad, one of my favorite jokes to ; ; play on folks was to install a program called 'Drip'. ; ; This was an 'invisible' TSR that would act at random (but ; ; decreasing) time intervals, selecting a character from the ; ; screen-map and 'dripping' it down the screen until just before it ; ; contacted another character. Of course, this didn't hurt ; ; anything, but certainly caused some humorous reactions ; ; (i.e. "EEEK! My screen is melting!!"). ; ; ; ; (The following documentation does not apply. I could not figure ; ; out how to get the command line arguments to save with the ; ; program when it was moved to memory...) ; ; ; ; This prog will accept two command line arguments. The first one ; ; specifies the number of ticks to wait before starting the screen ; ; melt. If ommited, the prog will wait five minutes (5460 ticks). ; ; After each drip, the program reduces the number of ticks to wait ; ; by 10. ; ; ; ; The second argument specifies the number of lines on the screen. ; ; If ommitted, 25 lines are assumed. ; ; ; ; WARNING: No error checking is performed against the arguments ; ;---------------------------------------------------------------------; jmp install MaxCol DW 0 ;Maximum number of columns displayed on the screen MaxColReal DW 0 ;Maximum number bytes to display row MaxRow DW 25d ;Maximum number of rows on the screen LastRow DW 0 ;Byte offset position of start of last row CurRow DW 0 ;Temp storage of current row CurCol DW 0 ;Temp storage of current column CurChar DW 0 ;Char offset being manipulated ScrnOffset DW 08000h TICKS DW 15 ;5460 ; time delay in ticks (1092/min) (5460/5min) ; can set low for debugging and testing add8 dd ? ; old interrupt 8 vector count dw 0 ; count to activity in ticks (3276=3 min) busy db 0 ; indicates TSR currently active, to ; prevent re-entrant execution initDone db 0 ; indicates if initialization has been called args DB 0FFh DUP (?) ;Provide 255 chars to store all args. argsLen DB 0FFh argsCount DB 0 argSto DB 064h DUP (?) ; storage for numeric results hexSto DW 0 argStoLen DW 064h ;---------------------------------------------------------------------; ; Interrupt handler for INT 8. With every tick, do the following: ; ; -Check kflag to see if key pressed since last tick ; ; NO: decrement the countdown byte. ; ; if 0, dim the VGA palette registers and set pflag to 1 ; ; YES: Check to see if screen dimmed (pflag=1) ; ; and restore the screen if so. ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; Interrupt handler for INT 8. With every tick, do the following: ; ; ; ; Input Parameters: ; ; None ; ; ; ; Returns: ; ; None ; ; ; ;---------------------------------------------------------------------; vec8: cmp CS:busy, 0 ; check the busy flag je vec81 ; if not active continue jmp far CS:add8 ; otherwise quit to old int 8 handler vec81: mov CS:busy, 1 ; indicate TSR active now pushf ; prepare to execute old int 8 call far CS:add8 ; which expects flags, CS, IP on stack sti ; enable interrupts dec CS:count ; decrement tick count jz vec82 ; if time has lapsed then cause a drip vec811: mov CS:busy, 0 ; otherwise, IRET ; we're done vec82: pusha ; save all registers push es push ds ; cli ; disable interrupts during drip mov es, 0B000h ; point the extra seg to the screen's text buffer cmp CS:initDone, 0 jne Drip10 mov CS:initDone, 1 ; indicate TSR active now call RandomInit Drip10: call GetNewRowCol ; get the next random column/row offset mov bx, CS:CurChar ; get the character at the new position mov ax, es:[bx] ; grab the character at the current position cmp al, 020h ; and test to see if a space was selected je Done ; jump out if selected char is a space mov dx, CS:MaxColReal ; add the bytes necessary to get to next row Drip50: add bx, dx ; to the current position. mov di, CS:LastRow ; if we have exceeded the capacity of the screen cmp bx, di ; then get out jg Done mov cx, es:[bx] ; test character beneath selected one cmp cl, 020h ; if char below is not a space jne done ; then jump out mov es:[bx], al ; swap the two chars so that the target char appears sub bx, dx mov es:[bx], cl ; to drip down the screen one space add bx, dx mov ax, es:[bx] ; grab the character at the current position jmp Drip50 Done: mov ax, TICKS mov cs:count, ax ; reset counter pop ds ; restore save regs pop es popa sti ; enable interrupts mov CS:busy, 0 ; and that we're no longer busy IRET mov ax, 4C00h ; DOS Terminate program function int 21H ;---------------------------------------------------------------------; ; GetNewRowCol Call GetNewCol and GetNewRow to get a new character. ; ; Make 10 attempts to find a position with a character ; ; other than a space and then give up... ; ; ; ; Input Parameters: ; ; None ; ; ; ; Returns: ; ; CurCol and CurRow are populated with new column and row ; ; ; ;---------------------------------------------------------------------; GetNewRowCol: push ax push bx push cx push dx mov cx, 0Ah ; make 10 attempts at finding a non-space char. GetNewRowCol10: push offset CurCol call GetCol ; get the next random column offset push offset CurRow call GetRow ; get the next random row offset mov bx, CS:CurRow ; point bx to the next random column add bx, CS:CurCol ; add the random column value to this mov al, es:[bx] ; get the char at the random position cmp al, 020h ; test to see if it is a space jne GetNewRowCol20 ; exit loop if it is loop GetNewRowCol10 GetNewRowCol20: mov CS:CurChar, bx GetNewRowColTest: pop dx pop cx pop bx pop ax ret ;---------------------------------------------------------------------; ; END GetNewRowCol ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; GetCol Use Random function to select next column and store ; ; results in CurCol. ; ; ; ; Input Parameters: ; ; 1st: Storage address for the new column value ; ; ; ; Returns: ; ; Address specified by 1st has new random column value ; ; ; ;---------------------------------------------------------------------; DestCol EQU word ptr [bp+04h] GetCol: push bp ; save callers bp mov bp, sp ; point current bp to the stack frame push ax push bx push cx push dx push di mov di, [DestCol] push 0 ; lower range of random number push CS:MaxCol ; upper range of random number push di ; storage for random number call Random mov ax, 02h ; mult by 2 because each column in memory is two bytes mov cx, CS:[di] mul cx mov CS:[di], ax ; and store the column value again GetColTest: pop di pop dx pop cx pop bx pop ax pop bp ret 2 ;---------------------------------------------------------------------; ; END GetCol ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; GetRow Use Random function to select next row and store ; ; results in CurRow ; ; ; ; Input Parameters: ; ; 1st: Storage address for the new row value ; ; ; ; Returns: ; ; Address specified by 1st has new row row value ; ; ; ;---------------------------------------------------------------------; DestRow EQU word ptr [bp+04h] GetRow: push bp ; save callers bp mov bp, sp ; point current bp to the stack frame push ax push bx push cx push dx push di mov di, [DestRow] push 0 ; lower range of random number push CS:MaxRow ; upper range of random number push di ; storage for random number call Random mov ax, CS:MaxColReal ; mult by 160 because each row in memory mov cx, CS:[di] ; begins in 160 byte increments mul cx add ax, CS:ScrnOffset ; add the beginning address info (B000:8000) mov CS:[di], ax ; and store the column value again pop di pop dx pop cx pop bx pop ax pop bp ret 2 ;---------------------------------------------------------------------; ; END GetRow ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; Random Generate a random number within a given range. ; ; This code is a modified version of an implementation by Wilson Seto; ; REF: http://www.cs.nyu.edu/courses/fall98/V22.0201-002/ranasm.html ; ; ; ; Input Parameters: ; ; 1st: The lower bound of the random number range ; ; 2nd: The upper bound of the random number range ; ; 3rd: The memory address for the memory address to store the word ; ; ; ; Returns: ; ; Carry Flag indicates success or failure ; ; All other regs preserved ; ; ; ;---------------------------------------------------------------------; RandLow EQU word ptr [bp+08h] Randhigh EQU word ptr [bp+06h] RandSto EQU word ptr [bp+04h] constant dw 8405h ;multiplier value seed1 dw ? seed2 dw ? ;random number seeds ;---------------------------------------------------------------------; ; Randomization and Screen Info Initialization Routine ; ;---------------------------------------------------------------------; RandomInit: push ax push bx push cx push dx mov ah,2ch ;get current time int 21h mov CS:seed1,cx mov CS:seed2,dx ;save as initial seeds mov ah, 0Fh ; Get the current video mode int 10h ; ah = chars per line, al = vid mode, bh = page mov al, ah ; convert ah to a word in ax sub ah, ah mov CS:MaxCol, ax ; sto ax (chars per line as a word) mov cx, 02h ; multiply by a factor of two to retrieve the mul cx ; number of bytes necessary to store a row mov CS:MaxColReal, ax ; and store that value for later use mov cx, CS:MaxRow ; multiply the maximum number of rows allowed on the screen mul cx ; by the bytes per row add ax, 08000h ; and add the 8000 bytes up to the first row's offset mov CS:LastRow, ax ; to get the starting position of the last row pop dx pop cx pop bx pop ax ret ;---------------------------------------------------------------------; ; Randomization Routine ; ;---------------------------------------------------------------------; Random: push bp ; save callers bp mov bp, sp ; point current bp to the stack frame push ax push bx push cx push dx push di mov ax, [RandHigh] ; subtract the low value sub ax, [RandLow] ; from the high value push ax ;save the range value mov ax, CS:seed1 mov bx, CS:seed2 ;load seeds mov cx,ax ;save seed mul CS:constant ;(dx,ax) = ax * constant shl cx,1 shl cx,1 shl cx,1 add ch,cl add dx,cx add dx,bx shl bx,1 ;begin scramble algorithm shl bx,1 add dx,bx add dh,bl mov cl,5 shl bx,cl add ax,1 adc dx,0 mov CS:seed1, ax mov CS:seed2, dx ;save results as the new seeds pop bx ;get back range value xor ax,ax ;clear register xchg ax,dx ;adjust ordering div bx ;ax = trunc((dx,ax) / bx), dx = (r) xchg ax, dx ;return remainder as the random number mov di, [RandSto] mov CS:[di], ax pop di pop dx pop cx pop bx pop ax pop bp ; restore callers bp ret 6 ; clear stack params ;---------------------------------------------------------------------; ; END Random ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; StdOut Copies the contents of a buffer into another, ; ; converting it to all capital letters. ; ; ; ; Input Parameters: ; ; CX Number of bytes to write ; ; DS:DX start of output buffer ; ; ; ; Returns: ; ; All regs except flags preserved ; ; ; ;---------------------------------------------------------------------; ;StdOut: ; push ax ; push bx ; ; mov bx, 1 ; mov ah, 40h ; set up for int 21h ; int 21h ; ; pop bx ; pop ax ; ret ;---------------------------------------------------------------------; ; END StdOut ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; TESTING Outputs 'TEST' to StdOut for debugging use ; ;---------------------------------------------------------------------; TestText DB 'TEST' TestTextLen EQU $-TestText TESTING: push ax push bx push cx push dx mov cx, CS:TestTextLen ; setup stdout call mov dx, offset TestText ; to print the word TEST mov bx, 1 mov ah, 40h ; set up for int 21h int 21h ; call StdOut ; used for testing purposes pop dx pop cx pop bx pop ax ret ;---------------------------------------------------------------------; ; END TESTING ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; Install Installs prog as a TSR. All functions following ; ; this routine will not be installed with the program. ; ; ; ; Input Parameters: ; ; None ; ; ; ; Returns: ; ; None ; ; ; ;---------------------------------------------------------------------; Install: mov bx, offset argsLen ; args will store the command line arguments push bx mov bx, offset argsCount push bx call StoreArgs ; populate args with the command line values mov bl, argsCount cmp bl, 2 jne Arg210 push 02h push offset argsLen push offset argSto push offset argStoLen call ReturnArg push offset argSto push offset hexSto call DecStr2BinHex mov MaxRow, hexSto Arg210: cmp bl, 1 jne Arg110 push 01h push offset argsLen push offset argSto push offset argStoLen call ReturnArg push offset argSto push offset hexSto call DecStr2BinHex mov TICKS, hexSto Arg110: mov bx, TICKS ; set the count to the default value... mov count, bx mov ax, 3508h ; get old interrupt 8 address int 21h mov word ptr add8, bx ; save it mov word ptr add8+2, es mov ax, 2508h ; hook interrupt 8 mov dx, offset vec8 int 21h mov es, es:[2ch] ; release environment block mov ax, 4900h int 21h mov dx, offset install ; prepare to go resident shr dx, 4 ; find paragraphs occupied by resident part inc dx ; for odd bytes left over mov ax, 3100h int 21h ;---------------------------------------------------------------------; ; END Install ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; StoreArgs Retrieve the command line args are store them in a ; ; variable length array. ; ; ; ; Input Parameters: ; ; DS: Points to the base address of the storage area ; ; ES: Points to the base address of PSP ; ; 1st: Points to the start of the word that has the length of ; ; the storage area. The actually storage area precedes this ; ; word and is assumed to be stored by byte. ; ; 2nd: Points to the byte storage location for the number of args ; ; found ; ; ; ; Returns: ; ; Carry Flag indicates success or failure ; ; All other regs preserved ; ; ; ;---------------------------------------------------------------------; StoLoc EQU word ptr [bp+06h] ArgsFound EQU word ptr [bp+04h] ArgLen EQU word ptr [bp-0Ch] StoLen EQU word ptr [bp-0Eh] TotLen EQU word ptr [bp-010h] StoreArgs: push bp ; save callers bp mov bp, sp ; point current bp to the stack frame push ax push bx push cx push dx push di push si sub sp, 06h ; make room for ArgLen, StoLen, and TotLen sub ax, ax sub cx, cx mov bx, [ArgsFound] ; grab the memory address of the args found counter mov [bx], al ; and zero it out mov di, [StoLoc] ; retrieve the location of the storage area length mov cx, [di] ; and then the actual storage area length mov [StoLen], cx ; and then store it in StoLen dec di ; and then bring it back to the last available space mov cl,ES:[080h] ; get tail length into cl jcxz NotFound ; if CX=0 there are no cmd tails args mov si,81h ; set si to first byte of command tail ... add si,cx ; ... then move to the last byte of the command tail ; skip leading blanks before arg skipBlanks: call CharScan ; scan for the next non space, non 0D char jnz FoundArg ; if non-blank then copy this argument into the destination buffer jmp GetArgExit ; if blank we ran out of tail ; copy found argument into dest buffer FoundArg: mov [ArgLen], 0h ; zero out the argument length accumulator dec di ; adjust di down one to make room for the arg length val later CopyArg10: jcxz GetArgDone ; if out of chars then we're done mov al, es:[si] ; copy char from PSP cmd tail... cmp al, ' ' ; if this char is a space then je GetArgDone ; we are at the end of this arg inc [ArgLen] ; increase argument length accumulator call CopyOne ; copy one char over and increment regs jmp CopyArg10 GetArgDone: mov ax, [ArgLen] ; move the arg lenth into ax for temp storage inc [ArgLen] ; increase lengh of arg (used as pointer relocation value) add di, [ArgLen] ; move destination pointer one past end of arg's text mov [di],al ; insert the length of string at end of arg text sub di, [ArgLen] ; move the destination string pointer back to the start of the arg ... inc byte ptr [bx] ; incrememnt the number of args found (bx is set above) jcxz GetArgExit ; if out of chars then we're done jmp skipBlanks ; get next argument into the buffer NotFound: add sp, 06h ; restore local storage stc ; no arg found so set carry flag jmp RestoreRegs GetArgExit: add sp, 06h ; restore local storage clc ; clear carry to indicate success RestoreRegs: pop si ; restore callers regs pop di pop dx pop cx pop bx pop ax pop bp ; restore callers bp ret 4 ; clear stack params ;---------------------------------------------------------------------; ; END StoreArgs ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; CharScan Scans ES:SI, going UP the string, stopping at the first ; ; occurance of a char that is neither a space or an OD. ; ; I wrote this because repe scasb is a pain in the ; ; rectal cavity. ; ; ; ; Input Parameters: ; ; SI: Points to the char to start search from ; ; CX: MAX number of characters to search ; ; ; ; Returns: ; ; si point to the non-space, non-0D character ; ; CX is decremented the number of times characters moved ; ; All other regs except flags preserved ; ; ; ;---------------------------------------------------------------------; CharScan: push ax ;preserve AX sub ax, ax ; zero out ax ChSc10: mov al, es:[si] ; get the letter at si cmp al, ' ' ; if this is a space je ChSc20 ; then keep searching cmp al, 0dh ; if this is an enter je ChSc20 ; then keep searching jmp ChSc30 ; otherwise, be done with it ChSc20: dec si ; point to the next item to search dec cx ; decrement the max char counter jcxz ChSc30 ; and exit if CX reaches zero jmp ChSc10 ; ChSc30: pop ax ret ;---------------------------------------------------------------------; ; END CharScan ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; CopyOne Copies on char from source to dest and increments all ; ; registers as appropriate. For use with the StoreArgs ; ; process. ; ; ; ;---------------------------------------------------------------------; CopyOne: mov [di], al ; copy char to buffer at [di] dec di ; adjust pointers dec si dec cx ; decrement length counter ret ;---------------------------------------------------------------------; ; END CopyOne ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; ReturnArg Retrieve a command line argument from a string ; ; created by StoreArg ; ; ; ; Input Parameters: ; ; DS: Points to the base address of the storage area ; ; 1st (was AL): Parameter number to retrive ; ; 2nd: Points to the start of the word that has the length of ; ; the storage area. The actual storage area precedes this ; ; word and is assumed to be stored by byte. ; ; 3rd (was SI): Start address of the argument string storage area ; ; 4th (was BX): Max Length of argument string storage area ; ; ; ; Returns: ; ; Carry Flag indicates success or failure ; ; All regs except flags preserved ; ; ; ;---------------------------------------------------------------------; ParamNum EQU word ptr [bp+0Ah] SrceSto EQU word ptr [bp+08h] ; dest sto and source sto both point to the end DestSto EQU word ptr [bp+06h] ; of the string where the string len is stored Alignment EQU word ptr [bp+04h] ; 0 = left, 1 = right ArgLenR EQU word ptr [bp-0Fh] StoLenR EQU word ptr [bp-010h] DestLen EQU word ptr [bp-012h] ReturnArg: push bp ; save callers bp mov bp, sp ; point current bp to the stack frame push ax push bx push dx push si push di sub sp, 06h ; make room for ArgLenR and StoLenR sub ax, ax sub bx, bx sub cx, cx sub dx, dx mov dx, [Alignment] ; move the value in alignment to dx for later reference mov si, [SrceSto] ; retrieve the location of the storage area length mov cl, [si] ; and then the actual storage area length mov [StoLenR], cx ; and then store it in StoLenR dec si ; and then bring it back to the last available space mov di, [DestSto] ; place the destination address into DI mov cx, [di] ; get the max allowable length of destination mov [DestLen], cx ; and store it for later reference cmp dx, 00h ; if Alignment is not set to 0 jnz Align10 ; then setup DI for right align sub di, [DestLen] ; otherwise, move di to the begining of the string for left align jmp Align20 Align10: dec di ; move to the last available storage location of destination Align20: sub cx, cx ; prep registers for use sub bx, bx mov ax, [ParamNum] ; retrieve the parameter number to be returned ; Move to the start of the arguments available in the string mov bl,[si] ; load message length into bl cmp bl, 0h ; check to see if any arguments are available jz NotFoundR ; and get out of dodge if not MoveToFirstArg: sub si, bx ; move si to start of string dec si ; move si to previos arg length mov bl,[si] ; load message length into bl inc cx ; increment the count of num of aguments up cmp bl, 0h ; check to see if we are at end of available args jnz MoveToFirstArg ; ... and continue on up if not inc si ; set si on to first available character cmp ax, cx ; test if greater number of parameters than availabe jg NotFoundR ; and fail process if so. mov si, [SrceSto] ; retrieve end position back into SI ;move to the beginning of the requested argument sub bx, bx ; zero out bx for use sub cx, ax ; subtract the desired position from the number available inc cx ; increment up by one. FindArg: sub si, bx ; move si start start of string dec si ; point si at the next available message len mov bl,[si] ; load message length into bl dec cx ; decrease argument counter jnz FindArg ; if not at desired arg then move to next one mov cx, bx ; cx is returned with the len of returned arg cmp dx, 00h ; if Alignment is not set to 0 jnz Align30 ; then setup SI for right align copying sub si, bx ; otherwise, move si to the begining of the string for left align jmp Align40 Align30: dec si ; move to the last available storage location of destination Align40: CopyArg: mov al, [si] ; copy char from arg source mov [di], al ; to buffer at [si] cmp dx, 00h ; Check alignment jnz Align50 ; inc si ; if performing a left aligned copy then inc di ; adjust pointers down the string jmp Align60 Align50: dec si ; if performing a right aligned copy then dec di ; adjust pointers up the string Align60: dec bx ; decrement the number of positions left jnz CopyArg ; and continue loop if more are left mov byte ptr [di],0 ; make copied arg an ASCIIZ string jmp GetArgExitR ; ok, all done now, bye bye NotFoundR: sub cx, cx ; zero out cx - no param so no length add sp, 06h ; restore local storage stc ; no arg found set carry flag jmp RestoreRegsR GetArgExitR: add sp, 06h ; restore local storage clc ; clear carry to indicate success RestoreRegsR: pop di pop si ; restore regs pop dx pop bx pop ax pop bp ; restore callers bp ret 8 ; clear stack params ;---------------------------------------------------------------------; ; END ReturnArg ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; DecStr2BinHex Converts an ASCII string of decimal numbers to ; ; a binary byte (DB). ; ; ; ; Input Parameters on Stack: ; ; 1st: Address of ACCIIZ decimal text to convert ; ; 2nd: Address to store byte (DB) ; ; ; ; Returns: ; ; All regs including flags preserved ; ; Stack is cleaned up on return ; ; ; ;---------------------------------------------------------------------; D2BDecStr EQU [bp+6] D2BOutput EQU [bp+4] D2BIsNeg DB 0h ; flag tells if dealing with negative # DecStr2BinHex: push bp ; save caller's bp mov bp, sp ; get frame pointer push ax ; save registers used push bx push cx push dx push si push di pushf ; save the flags mov si, [D2BDecStr] ; retrieve the address of the hex string push si ; and push it onto the stack as a param for call StrLen ; call to StrLen. Len returned in AX. mov cx, ax mov di, 0Ah ; will divide by 10 mov D2BIsNeg, 0 ; assume we have a positive number sub ax, ax ; beginning result = 0 cmp byte ptr [si], '-' ; is first char a minus sign? jne dec2bin10 mov D2BIsNeg, 1 ; set flag to indicate YES inc si ; and adjust buffer pointer dec cx ; and char count accordingly dec2bin10: mul di ; multiply result so far by 10 mov dl, [si] ; get next digit sub dh, dh sub dl, 30h ; convert ASCII to binary add ax, dx ; add to result so far inc si loop dec2bin10 cmp D2BIsNeg, 1 ; if the number was negative jne dec2bin20 neg ax dec2bin20: mov bx, [D2BOutput] mov [bx], al popf ; restore the flags pop di ; restore registers pop si pop dx pop cx pop bx pop ax pop bp ret 4 ; return with a clearing of the stack parameter ;---------------------------------------------------------------------; ; END DecStr2BinHex ; ;---------------------------------------------------------------------; ;---------------------------------------------------------------------; ; StrLen returns in AX the length of the ASCIIZ string whose ; ; address is at BP + 4. ; ; ; ; Input Parameters on Stack: ; ; 1st: Start of an ASCIIZ string to retrieve length from ; ; ; ; Returns: ; ; AX = length of the string ; ; All other regs including flags preserved ; ; Stack is cleaned up on return ; ; ; ;---------------------------------------------------------------------; SLStr EQU [bp+4] StrLen: push bp ; save caller's bp mov bp, sp ; get frame pointer push cx ; save registers used push di pushf ; save the flags mov di, [SLStr] ; get pointer placed on stack for this function push di ; and save it for length calculation later mov cx, 0ffffh ; move the max number of allowable chars to cx sub ax, ax ; clear out al so scan will look for 00h cld ; clear direction flag - string searches go up repnz scasb ; scan for value in al until it is found jnz L10 dec di ; point back to 0 byte L10: mov ax, di ; move the address that scan left us at into ax pop di ; restore di, which points to the start of string sub ax, di ; set ax to the length of the ASCIIZ string popf ; restore the flags pop di ; restore registers pop cx pop bp ret 2 ; return with a clearing of the stack parameter ;---------------------------------------------------------------------; ; END StrLen ; ;---------------------------------------------------------------------;
Comments
Post new comment