📄 iterator.asm
字号:
.286 ;For PUSH imm instr.
.xlist
include stdlib.a
includelib stdlib.lib
.list
; Some "cute" equates:
Iterator textequ <proc>
endi textequ <endp>
wp textequ <word ptr>
; Yield is a macro which emits the necessary code for the YIELD operation.
Yield macro
mov dx, [bp+2] ;Place to yield back to.
push bp ;Save Iterator link
mov bp, [bp] ;Get ptr to caller's A.R.
call dx ;Push resume address and rtn.
pop bp ;Restore ptr to our A. R.
endm
; Necessary global variables:
dseg segment para public 'data'
; As per UCR StdLib instructions, InputStr must hold
; at least 128 characters.
InputStr byte 128 dup (?)
; Note that the following statement initializes the
; VowelCnt array to zeros, saving us from having to
; do this in the main program.
VowelCnt word 256 dup (0)
dseg ends
cseg segment para public 'code'
assume cs:cseg, ds:dseg
; GetVowel- This iterator searches for the next vowel in the
; input string and returns the index to the value
; as the iterator result. On entry, ES:DI points
; at the string to process. On yield, AX returns
; the zero-based index into the string of the
; current vowel.
;
; GVYield- Address to call when performing the yield.
; GVStrPtr- A local variable which points at our string.
GVYield textequ <word ptr [bp+2]>
GVStrPtr textequ <dword ptr [bp-4]>
GetVowel Iterator
push bp
mov bp, sp
; Create and initialize GVStrPtr. This is a pointer to the
; next character to process in the input string.
push es
push di
; Save original ES:DI values so we can restore them on YIELD
; and on termination.
push es
push di
; Okay, here's the main body of the iterator. Fetch each
; character until the end of the string and see if it is
; a vowel. If it is a vowel, yield the index to it. If
; it is not a vowel, move on to the next character.
GVLoop: les di, GVStrPtr ;Ptr to next char.
mov al, es:[di] ;Get this character.
cmp al, 0 ;End of string?
je GVDone
; The following statement will convert all lower case
; characters to upper case. It will also translate other
; characters to who knows what, but we don't care since
; we only look at A, E, I, O, U, W, and Y.
and al, 5fh
; See if this character is a vowel. This is a disgusting
; set membership operation. See Chapter Ten to learn how
; to do it right.
cmp al, 'A'
je IsAVowel
cmp al, 'E'
je IsAVowel
cmp al, 'I'
je IsAVowel
cmp al, 'O'
je IsAVowel
cmp al, 'U'
je IsAVowel
cmp al, 'W'
je IsAVowel
cmp al, 'Y'
jne NotAVowel
; If we've got a vowel we need to yield the index into
; the string to that vowel. To compute the index, we
; restore the original ES:DI values (which pointer at
; the beginning of the string) and subtract the current
; position (now in AX) from the first position. This
; produces a zero-based index into the string.
; This code must also increment the corresponding entry
; in the VowelCnt array so we can print the results
; later. Unlike the Pascal code, we've converted lower
; case to upper case so the count for upper and lower
; case characters will appear in the upper case slot.
IsAVowel: push bx ;Bump the vowel
mov ah, 0 ; count by one.
mov bx, ax
shl bx, 1
inc VowelCnt[bx]
pop bx
mov ax, di
pop di ;Restore original DI
sub ax, di ;Compute index.
pop es ;Restore original ES
yield
push es ;Save ES:DI again
push di
; Whether it was a vowel or not, we've now got to move
; on to the next character in the string. Increment
; our string pointer by one and repeat the process
; over again.
NotAVowel: inc wp GVStrPtr
jmp GVLoop
; If we've reached the end of the string, terminate
; the iterator here. We need to restore the original
; ES:DI values, remove local variables, pop the YIELD
; address, and then return to the termination address.
GVDone: pop di ;Restore ES:DI
pop es
mov sp, bp ;Remove locals
add sp, 4 ;Pop YIELD/S.L.
pop bp
ret
GetVowel endi
Main proc
mov ax, dseg
mov ds, ax
mov es, ax
print
byte "Enter a string: ",0
lesi InputStr
gets ;Read input line.
; The following is the foreach loop. Note that the label
; "FOREACH" is present for documentation purpose only.
; In fact, the foreach loop always begins with the first
; instruction after the call to GetVowel.
;
; One other note: this assembly language code uses
; zero-based indexes for the string. The Pascal version
; uses one-based indexes for strings. So the actual
; numbers printed will be different. If you want the
; values printed by both programs to be identical,
; uncomment the INC instruction below.
push offset ForDone ;Termination address.
push bp ;Static link.
call GetVowel ;Start iterator
FOREACH: mov bx, ax
print
byte "Vowel ",0
mov al, InputStr[bx]
putc
print
byte " at position ",0
mov ax, bx
; inc ax
puti
putcr
ret ;Iterator resume.
ForDone: printf
byte "# of A's: %d\n"
byte "# of E's: %d\n"
byte "# of I's: %d\n"
byte "# of O's: %d\n"
byte "# of U's: %d\n"
byte "# of W's: %d\n"
byte "# of Y's: %d\n",0
dword VowelCnt + ('A'*2)
dword VowelCnt + ('E'*2)
dword VowelCnt + ('I'*2)
dword VowelCnt + ('O'*2)
dword VowelCnt + ('U'*2)
dword VowelCnt + ('W'*2)
dword VowelCnt + ('Y'*2)
Quit: ExitPgm ;DOS macro to quit program.
Main endp
cseg ends
sseg segment para stack 'stack'
stk db 1024 dup ("stack ")
sseg ends
zzzzzzseg segment para public 'zzzzzz'
LastBytes db 16 dup (?)
zzzzzzseg ends
end Main
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -