📄 cmdmacro.asm
字号:
; macro definition to be treated as a macro itself. In case of
; any errors( eg. expansion too long), the macro expansion is
; aborted and the input is directed to the keyboard.
;
; Parameters:
; None.
;
; Returns:
; CF = 0 if a line copied to linebuf
; 1 if no more lines in expansion or no ongoing expansion
; Register(s) destroyed:
; AX,BX,CX,DX
;-
get_macro_line proc near
cmp macro_level,0 ;Expanding a macro ?
jne @get_macro_line_1 ;Yes
stc ;CF = 1 for no expansion
ret
@get_macro_line_1:
push si
push di
push bp
mov bp,sp
sub sp,LINEBUF_SIZE
tempbuf equ <byte ptr [bp-LINEBUF_SIZE]>
; Copy the current string from the macro buffer into temp buffer.
mov bx,offset DGROUP:mac_stk ;BX->address of buffer descriptors
lea ax,tempbuf ;AX->destination address
mov cx,LINEBUF_SIZE ;CX<-size of buffer
call near ptr strstk_copy ;Copy macro into temp buffer
;AX<-length of expansion
; No error possible
; Replace any placeholders in the macro line by the corresponding paramters.
; copy all chars until first placeholder. Copy argument.
; Repeat for whole expansion. If at any time there is no place in
; buffer, then abort.
xchg cx,ax ;CX<-length of expansion
lea si,tempbuf ;SI->tempbuf (macro expansion)
mov di,offset DGROUP:linebuf ;DI->linebuf
@get_macro_line_38:
jcxz @get_macro_line_60 ;Jump if no more expansion
@get_macro_line_40:
; At the start of the loop, the following hold :
; DI->next empty location in the linebuf
; SI->next char of macro expansion to be examined (in tempbuf)
; CX = remaining number of chars in expansion (> 0)
mov al,PLACEHOLDER ;Going to search for placeholder
mov dx,cx ;DX<-length of remaining expansion
push di ;Save DI
mov di,si ;DI->start point for
; placeholder scan
repne scasb ;Look for placeholder
;assumes! (ES == SS)
; CX is number of chars after placeholder
sub dx,cx ;DX<-num chars to be copied
xchg dx,cx ;CX<-num chars to be copied
;DX<-num chars after placeholder
pop di ;DI->destination in linebuf
rep movsb ;Move chars from tempbuf to linebuf
;assumes! (ES == SS)
; Note we don't care if the characters at the end of the linebuf
; are overwritten.
mov cx,dx ;CX<-remaining number of chars
jcxz @get_macro_line_60 ;All chars copied (placeholder
; not found or last char in line)
; We have found a placeholder character in tempbuf. SI points to the
; character AFTER the placeholder. Based on the JCXZ above, there
; is at least one character after the placeholder. If it is a
; char between '0' and '9' then it is a genuine placeholder. If
; it is another placeholder character, then a single placeholder
; is stored. Else both the placeholder as well as the character
; will be stored into linebuf.
mov al,[si] ;AL<-char after placeholder
cmp al,PLACEHOLDER ;Is it a placeholder ?
jne @get_macro_line_45 ;No
; Skip over second placeholder
inc si
loop @get_macro_line_40
jmp short @get_macro_line_60 ;No more chars in expansion
@get_macro_line_45:
cmp al,'9'
ja @get_macro_line_40 ;Not a digit
sub al,'0'-1 ;Compaer with '0'. At the same
; time translate '0'->1,'1'->2
; ..and so on to '9'->10 since
; getargs counts from 1, not 0.
jbe @get_macro_line_40 ;Not a digit
dec di ;Cancel stored PLACEHOLDER char
push cx ;Save CX
mov bx,di ;BX->destination for argument
mov dx,LINEBUF_END
sub dx,bx ;DX<-remaining space in buffer
mov di,si ;Save SI in DI
mov si,offset DGROUP:cur_macro ;SI->current macro being expanded
xor ah,ah ;AX<-arg number (AL already
; holds actual arg number)
mov cx,cur_macro_len ;Length of original macro string
call near ptr getargs ;Get the argument into linebuf
;AX<-num chars copied, BX unchanged
;CF indicates error condition
pop cx ;Restore CX (num remaining
; chars in macro expansion)
mov si,di ;Restore SI
; (SI->char AFTER placeholder char)
mov di,bx ;Start of copied characters
jc @get_macro_line_101 ;Jump if getargs returned error
@get_macro_line_50:
add di,ax ;DI->next destination char in linebuf
inc si ;SI->next char of macro expansion
loop @get_macro_line_40 ;Decrement remaing characters
@get_macro_line_60:
mov lastchar,di ;Update end of line
mov dot,di ;Update cursor
; Set the lastchar and display end pointers
IF 0
Currently no need to set display pointers since macro lines are
not displayed
mov ax,di ;AX->Potential disp_end
mov dx,offset DGROUP:linebuf ;Potential disp_begin
call near ptr set_disp_marks ;Set the marks
ENDIF
; Finally check to see if this line is the last in the macro expansion.
mov bx,offset DGROUP:mac_stk ;BX->macro stack descriptor
xor cx,cx ;Move to next string in stack
call near ptr strstk_fwd_match
call near ptr check_separator ;Is this the last line of
; expansion ?
jne @get_macro_line_90 ;No
; This was the last line in the macro expansion. Reset macro flag
mov macro_level,0 ;Reset flag
@get_macro_line_90:
; @unlink
mov sp,bp ;clean up stack
pop bp ;restore registers
pop di
pop si
clc ;Return macro expanded
ret
@get_macro_line_101:
; truncation error
mov ax,E_TRUNCATE ;Indicate truncation of line
jmp near ptr abort_processing
get_macro_line endp
;+
; FUNCTION : check_separator
;
; Checks to see if the current macro/symbol buffer line is a
; separator.
;
; Parameters:
; BX = address of macro/symbol stack descriptor
;
; Returns:
; ZF = 1 if current buffer line is the separator string
; 0 otherwise.
;
; Register(s) destroyed:
; AX,CX
;-
check_separator proc near
mov ax,offset DGROUP:separator
mov cx,sep_len
call near ptr strstk_compare ;Is it the separator ?
; strstk_comapre sets ZF.
ret
check_separator endp
;+
; FUNCTION : execute_defs
;
; Called to define a symbol.
;
; Parameters:
; SI -> first char in linebuf following this command
; CX == remaining num chars in the line
;
; Returns:
; Nothing.
;
; Register(s) destroyed:
; AX,BX,CX,DX
;-
execute_defs proc near
mov bx,offset DGROUP:sym_stk ;BX->stack descriptor
; Push macro name
call near ptr push_word ;Push first word onto
; the stack. Params :
; SI->string
; CX=num chars
; Returns:
; AX<-status code
; SI->char after word
; CX<-num remaining chars
cmp ax,0 ;Check status
jg @execute_defs_99 ;No word on line,
; ignore command
jl @execute_defs_109 ;No room in stack
; Now push the rest of the string as is except that leading whitespace
; is compressed.
call near ptr skip_whitespace ;Skip leading whitespace
;Params SI, CX
;Returns SI->first
; non-white space char
; CX<-remaining chars
; (maybe 0)
call near ptr push_string ;Push string onto stack
; (maybe null string)
jc @execute_defs_109 ;No room in stack
call near ptr cmdsym_separate ;Push separator onto stack
jc @execute_defs_109 ;No room in stack
@execute_defs_99:
ret
@execute_defs_109:
; Error. No room in macro stack.
call near ptr cmdsym_cleanup ;Clear out partial definition
call near ptr disp_noroom ;Display error
ret
execute_defs endp
;+
; FUNCTION : execute_defm
;
; Called to define a multiple line macro. This function will
; keep reading from the current input source and storing it in
; the macro buffer until an `endm' is seen. The ENDM directive
; can be followed by any characters (eg. macro name)
;
; Parameters:
; SI -> first char in linebuf following this command
; CX == remaining num chars in the line
;
; Returns:
; Nothing.
;
; Register(s) destroyed:
; AX,BX,CX,DX
;-
execute_defm proc near
mov ax,E_NESTED_MACRO ;Assume error
cmp macro_level,0 ;Expanding a macro ?
je @execute_defm_1 ;No, jump
jmp near ptr abort_processing ;Yes, nested macro error
@execute_defm_1:
@save si,di
push bp
mov bp,sp
sub sp,2
err_flag equ <word ptr [bp-2]>
mov bx,offset DGROUP:mac_stk ;BX->stack descriptor
mov err_flag,0 ;Initially no errors
; If expanding a macro, ignore all lines until an endm
cmp macro_level,0
jnz @execute_defm_5 ;Go set error flag
; Push macro name
call near ptr push_word ;Push first word onto
; the stack. Params :
; SI->string
; CX=num chars
; Returns:
; AX<-status code
; SI->char after word
; CX<-num remaining chars
cmp ax,0 ;Check status
jb @execute_defm_5 ;No room in stack
je @execute_defm_40 ;No errors
; No name for macro. Push a 0 length macro name. Macro can be called by
; a blank line
xor cx,cx ;CX<-0 (length of string)
call near ptr push_string ;Push onto stack
jnc @execute_defm_40 ;No errors
@execute_defm_5:
mov err_flag,1 ;Indicate error
call near ptr cmdmacro_cleanup ;Cleanup macro fragments
@execute_defm_40:
; Keep reading lines from the input and store in macro buffer unless an error
; has been previously seen.
; Prompt is displayed only if reading from the keyboard.
cmp source,offset DGROUP:get_kbd_line
jne @execute_defm_42
@DispStr macro_prompt
call near ptr reset_line
@execute_defm_42:
call near ptr get_next_line
mov si,offset DGROUP:linebuf ;SI->String to push
mov cx,lastchar
sub cx,si ;CX<-length of line
push si ;Remember SI and CX
push cx
call near ptr skip_whitespace ;SI->first non-white
;CX<-num remaining chars
jcxz @execute_defm_50 ;Blank line so go store it
mov di,si ;DI->start of word
call near ptr skip_nonwhite
mov cx,si
sub cx,di ;CX<-length of word
xor ah,ah
mov si,offset DGROUP:endm_cmd
lodsb ;AX<-length of endm command
cmp cx,ax
jne @execute_defm_50 ;Not ENDM
call near ptr stre_cmp ;Compare strings
jnz @execute_defm_50
; ENDM seen.
cmp err_flag,1 ;Had we seen an error ?
jne @execute_defm_45 ;No
call near ptr disp_noroom ;Display error
jmp short @execute_defm_99
@execute_defm_45:
; End of macro seen. Store macro separator
call near ptr cmdmacro_separate
jmp short @execute_defm_99
@execute_defm_50:
; Store line in macro buffer
pop cx ;CX<-length of line
pop si ;SI->linebuf
mov bx,offset DGROUP:mac_stk ;BX->stack descriptor
call near ptr push_string
jnc @execute_defm_40 ;No error
jmp short @execute_defm_5 ;Indicate error
@execute_defm_99:
mov sp,bp
pop bp
@restore
ret
execute_defm endp
;+
; FUNCTION : cmdmacro_separate,cmdsym_separate,separate
;
; Pushes a separator string onto the stack.
;
; Parameters:
; None for cmdmacro_separate and cmdsym_separate
; BX->buffer descriptor for routine separate
; Returns:
; CF = 1 if no room in stack
; 0 otherwise
;
; Register(s) destroyed:
; AX,BX,CX,DX
;-
cmdmacro_separate proc near
mov bx,offset DGROUP:mac_stk ;BX->stack descriptor
jmp short separate
cmdsym_separate LABEL near
mov bx,offset DGROUP:sym_stk ;BX->stack descriptor
separate LABEL near
mov ax,sep_len ;AX<-length of separaot string
mov dx,offset DGROUP:separator ;DX->separator string
mov cx,1 ;Force push onto stack
call near ptr strstk_push ;Returns status in CF
ret
cmdmacro_separate endp
;+
; FUNCTION : cmdmacro_cleanup,cmdsym_cleanup
;
; This function is called to clean up the top of the macro or symbol
; stacks when a complete definition cannot be pushed onto the
; stack due to lack of space. The routine keeps deleting strings
; from the top of the macro stack until it finds a separator string.
;
; Parameters:
; None.
;
; Returns:
; Nothing.
;
; Register(s) destroyed:
; AX,BX,CX,DX
;-
cmdmacro_cleanup proc near
mov bx,offset DGROUP:mac_stk ;BX->stack descriptor
jmp short @cleanup_5
cmdsym_cleanup LABEL near
mov bx,offset DGROUP:sym_stk ;BX->stack descriptor
@cleanup_5:
call near ptr strstk_settop ;Reset cur pointer
; BX unchanged
; While the top of stack is not a separator, keep killing strings.
@cleanup_10:
call near ptr check_separator
; mov ax,offset DGROUP:separator ;AX->separator string
; mov cx,sep_len ;CX<-length of separator
; call near ptr strstk_compare ;Is this a separator ?
je @cleanup_99 ;Yes, then all done
call near ptr strstk_kill ;Kill string
jmp short @cleanup_10 ;Keep going
@cleanup_99:
ret
cmdmacro_cleanup endp
;+
; FUNCTION : disp_noroom
;
; Displays a message saying there is no room in the macro buffer.
;
; Parameters:
; None.
;
; Returns:
; Nothing.
; Register(s) destroyed:
; AX,DX
;-
disp_noroom proc near
@DispStr macro_noroom_msg
ret
disp_noroom endp
;+
; FUNCTION : search_variable
;
; Called to search the passed string for a variable. A variable is a
; sequence of characters starting with the VAR_MARKER character
; followed by another VAR_MARKER character. Two marker characters in
; succession are left untouched. (This is so that this routine can be
; called again to check for more variables.)
;
; Parameters:
; SI - points to the string.
; AX - length of the string.
;
; Returns:
; SI - Points to first marker character of variable if AX is not 0
; else points to end of string.
; AX - length of variable (including marker chars) if
; variable is present, else 0
; Register(s) destroyed:
; CX
;-
search_variable proc near
@save di
mov cx,ax
@search_variable_10:
mov di,si ;DI->string to search
; CX is number of chars
; Note we are OK if length of line is already 0.
mov al,VAR_MARKER
repne scasb ;Search for marker
; If marker not found or found in last position, CX will be 0.
jcxz @search_variable_99
; DI->char after first marker, CX is remaining number of characters
mov si,di
@search_variable_20:
lodsb ;AL<-next char
dec cx
jcxz @search_variable_99 ;If there was only one char
; after the marker, variable
; not possible
cmp al,VAR_MARKER ;Is it a marker ?
je @search_variable_10 ;Yes, found a marker, just
; ignore the pair and keep
; looking
; We have found the start of a variable. Look for its end.
inc di ;Move past first char of var
mov al,VAR_MARKER
repne scasb ;Look for it
jne @search_variable_99 ;Not found (CX is 0)
; Found a variable
dec si
dec si ;SI->start marker of var
mov cx,di
sub cx,si ;CX<-length of var
@search_variable_99:
xchg ax,cx ;AX<-length of var
@restore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -