Assembly 在用户输入中查找第一个和最后一个大写字母
输入取自a-z或a-z,输入以星号*结束 我们需要输入字符的第一个和最后一个大写字母作为输出。此外,我们还应显示每次所做的输入。注意:我们一个字符接一个字符地接受输入,而不是字符串 测试用例1:输入:aAbCcP*输出:AP 测试用例2:输入:ZabCBc*输出:ZB 我在下面编写了这段代码,它满足测试用例1,但不满足测试用例2:Assembly 在用户输入中查找第一个和最后一个大写字母,assembly,emu8086,Assembly,Emu8086,输入取自a-z或a-z,输入以星号*结束 我们需要输入字符的第一个和最后一个大写字母作为输出。此外,我们还应显示每次所做的输入。注意:我们一个字符接一个字符地接受输入,而不是字符串 测试用例1:输入:aAbCcP*输出:AP 测试用例2:输入:ZabCBc*输出:ZB 我在下面编写了这段代码,它满足测试用例1,但不满足测试用例2: .MODEL .STACK 100H .DATA STR DB 'Enter letters:$' .CODE MAIN PROC MOV AX, @DAT
.MODEL
.STACK 100H
.DATA
STR DB 'Enter letters:$'
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
LEA DX, STR
MOV AH, 9
INT 21H
cycle:
MOV AH, 1
INT 21H
CMP AL, '*'
JZ output
CMP AL, 'Z'
JA save
head:
CMP BL, 1
JZ save
MOV BL, 1
MOV BH, AL
clear:
XOR AL, AL
save:
MOV CH, AL
LOOP cycle
output:
MOV AH, 2
MOV DL, BH
INT 21H
MOV AH, 2
MOV DL, CH
INT 21H
MAIN ENDP
END MAIN
首先问自己以下问题:
什么是首都?
如果我们不考虑重音字符,那么大写字母是ASCII码的字符,范围从65到90。
我可以信任用户只输入a-z或a-z中的字符吗?
不,你不能。你无法控制用户在键盘上做什么,这就是为什么你的程序应该采取防御性的方法,用比单个cmp al“Z”更好的东西来测试大写字母
如果输入不包含单个资本,结果会是什么?
您可以选择打印两个空格,或者一条描述性消息,或者像我一样什么也不显示
如果输入只包含一个资本,结果会是什么?
你可以选择打印一个大写字母,或者像我一样显示两次,因为如果你想一想,这个大写字母同时是大写字母的第一次出现,也是大写字母的最后一次出现
我将使用哪些输入/输出函数?
对于单字符输入,您可以在DOS功能01h、06h、07h、08h、0Ch和3Fh之间进行选择。
对于单字符输出,您可以在DOS函数02h、06h和40h之间进行选择。
如果您对组装还不熟悉,请坚持使用更简单的,并使用函数01h和02h。在使用任何DOS功能之前,请务必咨询。当然,请使用emu8086检查它是否完全支持该功能
为了完成这项任务,你需要决定以上所有事项。重要的是,对于你做出的每一个选择,你都可以捍卫自己的选择
下面是我对这项任务的看法。为了简单起见,我使用了微型程序模型。请参阅顶部的ORG 256指令?该程序模型的主要优点是,所有段寄存器都平等地指向您的程序CS=DS=ES=SS
程序运行2个循环。第一个循环运行,直到收到资金。不言而喻,如果输入包含星号,它会提前停止。因为资本同时是资本的第一次出现,也是资本的最后一次出现,所以我在DL和DH中保存了两次
第二个循环运行,直到收到星号。每次出现新的大写字母时,它都会替换DH中所写的字母。当这个循环最终结束时,DL和DH都显示在屏幕上,当然是按这个顺序显示的
程序使用首选DOS功能4Ch退出,以终止程序
我已经写了一些重要的注释,避免添加多余的注释,并在程序中为标签使用描述性名称。请注意,这是一个很好的表格布局。对于可读性来说,这是关键
ORG 256
Loop1: mov ah, 01h ; DOS.GetKeyboardCharacter
int 21h ; -> AL
cmp al, "*" ; Found end of input marker ?
je Done
cmp al, "A"
jb Loop1
cmp al, "Z"
ja Loop1
mov dl, al ; For now it's the first
mov dh, al ; AND the last capital
Loop2: mov ah, 01h ; DOS.GetKeyboardCharacter
int 21h ; -> AL
cmp al, "*" ; Found end of input marker ?
je Show
cmp al, "A"
jb Loop2
cmp al, "Z"
ja Loop2
mov dh, al ; This is the latest capital
jmp Loop2
Show: mov ah, 02h ; DOS.DisplayCharacter
int 21h ; -> (AL)
mov dl, dh
mov ah, 02h ; DOS.DisplayCharacter
int 21h ; -> (AL)
Done: mov ax, 4C00h ; DOS.TerminateWithReturnCode
int 21h
例如:
阿泽蒂*
aZeRTy*ZT
如果你简单地复制/粘贴我的代码,那将是非常令人失望的。我试着详细解释,希望你能从中学到很多东西
对于这项任务,我的解决方案当然不是唯一好的解决方案。例如,您可以先输入所有字符并将其存储在内存中的某个位置,然后再从内存中处理这些字符,类似于我的处理方式。
请尝试编写一个工作版本,以这种替代方式完成。你只能变得更聪明!快乐编程。您的代码被破坏了,因为每次迭代都要保存:MOV CH,AL,所以只有当最后一个大写字母也是整个输入的最后一个字符时,它才能工作 用一个调试器对ABc*这样的简单输入进行单步调试,看看它是如何出错的 此外,您还可以使用类似于dec-cx/jnz的循环。这毫无意义,因为不存在基于计数器的终止条件,并且如果CL为零,可能会损坏CH。你甚至不先初始化CX!循环指令不是循环的唯一方式;这只是一个代码大小的窥视孔优化,当您方便地将CX用作循环计数器时,可以使用它。否则不要使用它 这是Sep实现的简化版本,利用了输入保证为字母的事实,因此我们确实可以像检查c一样轻松地检查大写字母,您的代码在案例2中打印了什么?这不是一个好主意。还要用英语注释您的代码和/或描述您的算法应该是什么,哪些寄存器应该包含什么。我不明白为什么你有一个明确的标签:当没有跳转的时候。你只会从头上掉下去。此外,在循环开始之前,您似乎在读取BL时从未将BL或BX归零。您的代码似乎不正确
依靠BL!=1。此外,循环会根据CX提前结束,或者循环指令会损坏CH。我认为您需要jmp,因为没有基于计数的循环终止条件。循环基本上是dec cx/jnz,但没有更新标志,因此显然您不希望这样。无论如何,最明显的问题是,每次循环都会更新CH。仅当最后一个大写字符也是最后一个全局字符时,它才能工作。您可以编写两个循环:一个循环找到第一个大写字母并将其保存在一个寄存器中,然后另一个循环跟踪最后一个大写字母。OP已经在使用cmp al“Z”。我不确定你的第一个要点是什么。他们可以假定他们的输入是字母,所以c>'Z'表示小写,否则表示大写。您的版本使用额外的分支来拒绝非字母字符,从而使其复杂化。但是,是的,两个单独的循环似乎是最好的选择,而不是每次您找到新大写字母时都在一个标志上分支。@PeterCordes我的答案是根据OP得出的。我错误地将其发布在这里。尽管如此,我认为这仍然是一个有效的答案。是的,这个问题应该被删除或关闭,因为这是一个重复。也许可以简化第一个要点,因为OP已经知道了这一点。或者强调排除非字母字符,使代码更加复杂,但能够处理输入,如12ABcd7_,如果您对代码大小进行优化,您也可以删除:mov ax,4C00h;DOS.TerminateWithReturnCode int 21h以Done:ret为受益人。COM程序可以通过简单的ret终止。DOS将在堆栈顶部推送0000h,然后再将控制权转移到CS:100h。CS:0000h是PSP的开始,包含int 20h指令,该指令又返回DOS。@MichaelPetch:heh,我没有考虑优化算法本身之外的内容,但我猜在.com文件中没有任何其他开销如此确定。int 20h是否通过ret设置退出状态=0?我在google上找到的所有东西都称之为无退出状态,除非DOS退出状态类似于标志+值或可以编码非a状态而不是数字的东西,否则没有意义。
ORG 100h ; DOS .com is loaded with IP=100h, with CS=DS=ES=SS
; we don't actually do any absolute addressing so no real effect.
mov ah, 01h ; DOS.GetKeyboardCharacter
; AH=01 / int 21h doesn't modify AH so we only need this once
find_first_cap:
int 21h ; stdin -> AL
cmp al, '*' ; Found end of input marker ?
je Done ; if (c=='*') return; without print anything, we haven't found a capital yet
cmp al, 'Z'
ja find_first_cap
; fall through: AL <= 'Z' and we can assume it's a capital letter, not a digit or something.
mov dl, al ; For now it's the first
;mov dh, al ; AND the last capital
;mov ah, 01h ; DOS.GetKeyboardCharacter AH still = 01
;jmp loop2_entry ; we can let the first iteration set DH
Loop2: ; do {
cmp al, 'Z' ; assume all c <= 'Z' is a capital alphabetic character
ja loop2_entry
mov dh, al ; This is the latest capital
loop2_entry:
int 21h ; stdin -> AL
cmp al, '*'
jne Loop2 ; }while(c != '*');
Show: mov ah, 02h ; DOS.DisplayCharacter
int 21h ; AL -> stdout
mov dl, dh
; mov ah, 02h ; DOS.DisplayCharacter
int 21h ; AL -> stdout
Done: mov ax, 4C00h ; DOS.TerminateWithReturnCode
int 21h