📄 cmdmacro.asm
字号:
; CMDMACRO.ASM
; (c) 1989, 1990 Ashok P. Nadkarni
;
; Module implementing macro and symbol feature for CMDEDIT.
;
; Symbols :
; CMDEDIT symbols can be defined either from the command line or read
; from a file during initialization. The syntax is given by
; defs symbolname expansion
; When the defined symbol appears as the first word in the line, it is
; replaced by its expansion. The rest of the line is unchanged. The
; following line defines a symbol called 'ed' that runs my editor.
;
; defs ed c:\util\editor
;
; Now if you type
;
; ed newfile
;
; the command
;
; c:\util\editor newfile
;
; will be executed.
;
; Symbols are expanded recursively.
;
; Macros :
;
; CMDEDIT macros can be defined either from the command line or read
; from a macro file when CMDEDIT is installed. In both cases, macros
; are defined using the same syntax. Macros may expand into multiple
; lines. In the latter case, each line of the expansion in passed to
; the calling application one at a time.
;
; Macros are defined using the CMDEDIT command 'defm' followed by the
; macro name. The macro name is separated from the 'defm' keyword by one
; or more spaces/tabs. Any characters after the name of the macro are ignored.
; Each line of the macro expansion is defined on a separate line. The
; expansion may contain any number of lines (limited by buffer space)
; and is terminated by a line that begins with the keyword 'endm'. For
; example, the following lines define a macro that will change the
; current directory from any disk:
; defm gotc
; c:
; cd \TURBOC
; endm
; Macro keywords are case-insensitive.
;
; Macro Parameters:
; Similar to batch files, macros can be passed parameters. (Read
; your DOS manual to find out about parameters). Although the
; concept is similar to DOS batch files, CMDEDIT parameters behave a
; little differently. Upto 9 parameters can be defined. These are
; indicated in macro definitions as '%n' where n is a digit from 1 to 9.
; A parameter can appear anywhere in the definition and need
; not be surrounded by whitespace. Also, the character % itself can be
; placed anywhere in the definition as long as it is not followed by a
; digit. If you do want a '%n' sequence in the expansion, indicate the '%'
; character as '%%'.
; For example, consider
; defm bf
; copy %1 a:\%1\%%1
; endm
; Then, when you type
; "bf myfile"
; the macro will expand to
; "copy myfile a:\myfile\%1"
; Note how %1 has been replaced by 'myfile' in two places but not the third.
;
; A macro cannot call another macro except if the call is the last
; line in the macro. Macros anywhere else are not expanded and the line
; is passed to the calling application without modification.
;
; Note that the macro name can be null as well. In this case, hitting
; carraige return on an blank line will result in that macro being run.
INCLUDE common.inc
INCLUDE general.inc
INCLUDE ascii.inc
INCLUDE dos.inc
PUBLIC macro_init
PUBLIC symbol_init
PUBLIC execute_defm
PUBLIC execute_defs
PUBLIC execute_delm
PUBLIC execute_dels
PUBLIC execute_rstsym
PUBLIC execute_rstmac
PUBLIC execute_cmdstat
PUBLIC get_macro_line
PUBLIC expand_macro
PUBLIC expand_symbol
PUBLIC expand_var
PUBLIC get_symbol
PUBLIC mac_stk
PUBLIC sym_stk
PUBLIC ismacsym
PLACEHOLDER equ PERCENT ;Placeholder character
INCLUDE buffers.inc
CSEG SEGMENT PARA PUBLIC 'CODE'
DGROUP GROUP CSEG
EXTRN endm_cmd:BYTE
EXTRN defm:BYTE
EXTRN defs:BYTE
EXTRN get_kbd_line:ABS
EXTRN source:WORD
EXTRN macro_level:WORD
EXTRN macro_ignore_char:BYTE
EXTRN lastchar:WORD
EXTRN linebuf:BYTE
EXTRN dot:WORD
EXTRN LINEBUF_END:ABS
EXTRN cur_macro:BYTE
EXTRN cur_macro_len:WORD
mac_stk $string_stack <> ;Descriptor for macro buffer
sym_stk $string_stack <> ;Descriptor for sym buffer
separator db 13 ;Separator string between macros
sep_len equ $-separator ;Length of separator string
macro_noroom_msg db 'Table full. Definition ignored.',CR,LF,DOLLAR
macro_prompt db CR,LF,'DEFM>',DOLLAR
EXTRN push_word:PROC
EXTRN push_string:PROC
EXTRN get_next_line:PROC
EXTRN reset_line:PROC
EXTRN abort_processing:PROC
EXTRN getargs:PROC
EXTRN stre_cmp:PROC
EXTRN set_disp_marks:PROC
EXTRN isspace:PROC
EXTRN isdelim:PROC
EXTRN skip_whitespace:PROC
EXTRN skip_nonwhite:PROC
EXTRN skip_nondelim:PROC
EXTRN makeroom:PROC
EXTRN remove_chars:PROC
EXTRN insert_chars:PROC
EXTRN output_counted_string:PROC
EXTRN output_newline:PROC
EXTRN locate_dosenv:PROC
ASSUME CS:DGROUP,DS:DGROUP,ES:DGROUP,SS:DGROUP
;+
; FUNCTION : macro_init,symbol_init
;
; Initializes the various data structures associated with the
; macro /symbol buffer. CALLER MUST ENSURE PASSED ADDRESSES ARE VALID
; AND BUFFER IS LARGE ENOUGH.
;
; Parameters:
; AX - length of buffer in bytes
; BX - address of buffer
;
; Returns:
; Nothing.
; Registers destroyed :
; BX
;-
macro_init proc near
push bx
mov bx,offset DGROUP:mac_stk ;bx := address of buffer descriptors
jmp short @macsym_init
symbol_init LABEL near
push bx
mov bx,offset DGROUP:sym_stk ;bx := address of buffer descriptors
@macsym_init:
xchg ax,cx ;CX = buffer size
pop ax ;AX = Buffer address
;BX points to appropriate descriptor
call near ptr strstk_init ;Initialize buffer and descriptor
;Store a separator into the macro buffer
call near ptr separate
ret
macro_init endp
;+
; FUNCTION : execute_rstmac, execute_rstsym
;
; Resets the various data structures associated with the
; macro /symbol buffer.
;
; Parameters:
; None.
;
; Returns:
; Nothing.
;
; Registers destroyed :
; AX,CX,BX,DX
;-
rst_macsym proc near
execute_rstmac LABEL near
; mov macro_level,0
mov bx,offset DGROUP:mac_stk ;bx := address of buffer descriptors
jmp short @rst_macsym
execute_rstsym LABEL near
mov bx,offset DGROUP:sym_stk ;bx := address of buffer descriptors
@rst_macsym:
call near ptr strstk_reset ;Re-initialize buffer and descriptor
;Store a separator into the macro buffer
call near ptr separate
ret
rst_macsym endp
;+
; FUNCTION : ismacsym
;
; Called to check if the passed string is a valid symbol or macro
; name. If found, the symbol/macro string stack pointer is set to the
; first line of the expansion of the symbol/macro.
;
; Parameters:
; AX - length of symbol name to be checked
; BX - address of the string stack descriptor (symbol or macro)
; SI - pointer to the symbol to be checked.
;
; Returns:
; CF - 1 if symbol not found, the string stack current pointer
; is undefined
; 0 if symbol found. The string stack pointer points to the
; first line of the expansion of the found symbol/macro.
; Register(s) destroyed:
; AX,CX,DX
;-
ismacsym proc near
@save si,di
mov di,ax ;DI<-length of symbol
call near ptr strstk_settop ;Reset macro stack pointer
@ismacsym_20:
; Loop start, DI = num chars in word, SI->start of word
mov ax,si ;AX->word
mov cx,di ;CX<-length of word
call near ptr strstk_bck_find ;Look backward for string
; Params AX,BX,CX
; BX unchanged
jc @ismacsym_98 ;Not found so return with CF set
; The name matched. Now make sure it is a macro name by ensuring
; previous string is a separator.
xor cx,cx ;cx<-length of pattern
call near ptr strstk_bck_match ;Set current to previous string
; BX unchanged
jc @ismacsym_98 ;start of buffer so return
; with CF set
push bx
call near ptr check_separator
pop bx
je @ismacsym_50 ;This is the one
; Wasn't a separator. Move back over it to start the hunt again.
xor cx,cx
call near ptr strstk_fwd_match
jmp short @ismacsym_20
@ismacsym_50:
; The macro/symbol has been found.
xor cx,cx ;CX<-match length
call near ptr strstk_fwd_match ;Skip over separator
xor cx,cx ;CX<-match length
call near ptr strstk_fwd_match ;Skip over macro/symbol name
clc ;Clear return flag
@ismacsym_98:
@restore
ret
ismacsym endp
;+
; FUNCTION : execute_dels, execute_delm
;
; execute_dels and execute_delm respectively delete symbols and
; macros from the appropriate stack. All symbols/macros listed on the
; line are deleted. If a particular symbol/macro is not defined, no
; error is generated.
;
; 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
del_macsym proc near
execute_dels LABEL near
mov bx,offset DGROUP:sym_stk
jmp short @del_macsym_5
execute_delm LABEL near
cmp macro_level,1
jne @execute_delm_1
mov ax,E_NESTED_DELM
jmp near ptr abort_processing
@execute_delm_1:
mov bx,offset DGROUP:mac_stk
@del_macsym_5:
@save si,di
push bp
mov bp,sp
sub sp,2
num_remain equ <word ptr [bp-2]>
;
@del_macsym_10:
; At this point, SI->remaining chars in line, CX is number of chars
jcxz @del_macsym_99 ;No more chars in line
; Find first word
push bx ;Save buffer descriptor addr
call near ptr skip_whitespace ;SI->first non blank
mov di,si ;DI->start of word
call near ptr skip_nondelim ;SI->char after word
mov ax,si ;
xchg si,di ;SI->word, DI->rest of line
sub ax,si ;AX<-num chars in word
pop bx ;BX->buffer descriptor
push cx ;Save remaining count
call near ptr ismacsym ;Is it in macro/symbol stack ?
pop cx ;Restore remaining count
mov si,di ;SI->rest of line
jc @del_macsym_10 ;Not found so go onto next
; symbol
; The macro or symbol has been found. The current stack pointer is the
; first line of the expansion. Move it down to the separator just before
; the macro or symbol name itself. Then keep deleting strings from the
; stack until we hit another separator.
push cx ;Save remaining count
xor cx,cx
call near ptr strstk_bck_match ;Point to name
xor cx,cx
call near ptr strstk_bck_match ;Point to separator
@del_macsym_20:
call near ptr strstk_kill ;Delete the string
; Check if separator
call near ptr check_separator ;Is new 'current' a
; separator ?
jne @del_macsym_20 ;No, so keep deleting
; Reached a separator.
pop cx ;Restore count
jmp short @del_macsym_10 ;Keep looping for rest of line
@del_macsym_99:
mov sp,bp
pop bp
@restore
ret
del_macsym endp
;+
; FUNCTION : expand_macro, expand_symbol
;
; expand_macro and expand_symbol attempt expand the first word in
; linebuf as a macro name and symbol respectively. If no macro
; expansion is going on, the expand_macro routine will attempt to
; expand the first word in the line buffer as a macro (leading
; whitespace is ignored). If an expansion is found, it is stored
; in the line buffer with the parameters (if any) filled in. If
; there is already a macro expansion going on or no macro is
; found, the line buffer is unchanged.
;
; In contrast, the expand_symbol routine always tries to
; expand the first word as a symbol.
;
; If the first character of the line is a macro_ignore_char, the
; character is removed, the rest of the line moved up and no
; expansions are done.
;
; Parameters:
;
; Returns:
; CF = 0 if line buffer changed
; 1 otherwise (no macro/symbol or ongoing macro expansion)
;
; Register(s) destroyed:
; AX,BX,CX,DX
;-
expand_macro proc near
mov bx,offset DGROUP:mac_stk ;BX->macro descriptor
jmp short @expand
expand_symbol LABEL near
mov bx,offset DGROUP:sym_stk ;BX->symbol descriptor
@expand:
push si
push di
push bp
mov bp,sp
sub sp,2
num_remain equ <word ptr [bp-2]>
cmp bx,offset DGROUP:mac_stk
jne @expand_10 ;If symbol, don't worry about
; whether any macro expansions
; are ongoing
cmp macro_level,0 ;Already expanding a macro?
je @expand_10 ;No macro expansion currently ongoing
stc ;set CF to indicate no expansion
jmp @expand_98 ;Yes, exit with carry flag set
@expand_10:
; Look back through the macro/symbol stack buffer for a macro definition.
; Find first word of line.
push bx ;Save buffer descriptor addr
mov cx,lastchar ;End of line
mov si,offset DGROUP:linebuf ;SI->line buffer
sub cx,si ;CX<-length of line
call near ptr skip_whitespace ;SI->first non blank
mov di,si ;DI->start of word
call near ptr skip_nondelim ;SI->char after word
mov num_remain,cx ;Remember num chars remaining
; in line
mov ax,si ;
mov si,di ;SI->word
sub ax,si ;AX<-num chars in word
pop bx ;BX->buffer descriptor
call near ptr ismacsym ;Is it in macro/symbol stack ?
jc @expand_98 ;Not found so return with CF set
; The macro or symbol has been found.
; CHeck if it is the macro/symbol string stack
cmp bx,offset DGROUP:mac_stk ;Expanding a macro ?
jne @expand_50 ;No, expanding symbol
mov macro_level,1 ;Indicate expansion going on
; Copy linebuf into cur_macro (to remember arguments)
mov di,offset DGROUP:cur_macro
mov si,offset DGROUP:linebuf
mov cx,lastchar
sub cx,si ;CX<-length of linebuf
mov cur_macro_len,cx ;Store length of invocation line
rep movsb ;Remember macro invocation line
; The current stack string is the first expansion line of the macro
mov sp,bp ;clean up stack
pop bp ;restore registers
pop di
pop si
jmp short get_macro_line ;Yes, copy it into the linebuf buffer
; get_macro_line will return to caller with the appropriate value
; of the carry flag.
@expand_50:
; We are expanding a symbol.
; num_remain = num chars in linebuf after symbol
; First we move these characters to the end of the buffer
mov cx,num_remain ;CX<-number of chars to move back
call near ptr makeroom ;Make room in buffer
mov si,di ;SI->chars copied to the back
; Now copy the string
mov ax,offset DGROUP:linebuf ;AX->line buffer
mov cx,LINEBUF_SIZE ;CX<-size of line buffer
call near ptr strstk_copy ;Copy line. Never mind if end
; string overwritten.
; AX<-length of expanded string
mov di,offset DGROUP:linebuf
add di,ax ;DI->location after expansion
mov cx,num_remain ;CX<-num chars to copy after expansion
add ax,cx ;ax<-total length of line
cmp ax,LINEBUF_SIZE ;End chars overwritten ?
jbe @expand_60
mov ax,E_TRUNCATE ;Truncation error
jmp near ptr abort_processing
@expand_60:
; Copy trailing chars to end of expansion
rep movsb
mov lastchar,di
mov dot,di
clc ;Indicate expansion took place
@expand_98:
; MUST NOT CHANGE CARRY FLAG AFTER THIS POINT.
mov sp,bp ;clean up stack
pop bp ;restore registers
pop di
pop si
ret
expand_macro endp
;+
; FUNCTION : get_macro_line
;
; Copies a line from the macro stack to linebuf. All parameters
; codes (%1, %2 etc.) are replaced with their corresponding
; parameters. If this line is the last in the macro definition,
; the macro_level flag is reset. This allows the last line in a
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -