程序集x8086将字符变成'z'或'Z'并打印小字符,大字符,数字

时间:2018-12-27 21:26:38

标签: assembly tasm emu8086

我有问题:

  1. 从用户获取输入直到输入“ z”或“ Z”的程序 该程序将检查字符,并打印 (新行)小字符
    (新行)大字符
    (新行)数字
    [没有'z'或'Z']
    不能使用变量。
    只有堆栈。
    例如: 输入:ASdf154sdgdf123vcvbz 小字符:dfsdgdfvcvb 大字符:AS 编号:154123

  2. 具有字符串大小的程序定义N,程序将从用户处获取输入,并且仅从输入中获取数字并将其放入具有N大小的字符串中,输入大小为N。并打印带有字符串的消息(必须只输入数字)和数量。
    示例(字符串大小10)
    输入:1adr1t23g7
    字符串:11237
    打印输出:
    数字字符串是:11237,总数字是:5

被问题1困扰,对主题不了解,无法继续问题2。 感谢所有可以帮助我的人

这是问题1的代码:

STA SEGMENT STACK
    DB 100H DUP (0)
STA ENDS
CODE SEGMENT
    ASSUME CS:CODE,SS:STA
MAIN:
    MOV BP, SP
    MOV BX, BP
    SUB BP, 2
    MOV CX, 0
INPUT:
    MOV AH, 01H
    INT 21H
    INC CX
    CMP AL, 'z'
    JE TOPRINT
    CMP AL, 'Z'
    JE TOPRINT
    MOV AH, 0
    PUSH AX
    MOV AX, 0
    JNE INPUT
    MOV DI, CX
TOPRINT:
    POP DX
    CMP DX, 'a'
    JL NEXT
    CMP DX, 'z'
    JG NEXT
    MOV AH, 02H
    INT 21H
    CMP DX, 'A'
    JL NEXT
    CMP DX, 'Z'
    JG NEXT
    MOV AH, 02H
    INT 21H
    CMP DX, '0'
    JL NEXT
    CMP DX, '9'
    JG NEXT
    MOV AH, 02H
    INT 21H
NEXT:
    SUB BP, 2
    DEC CX
    CMP CX, 0
    JMP TOPRINT
    MOV AX, 4C00H
    INT 21H
CODE ENDS
    END MAIN

1 个答案:

答案 0 :(得分:0)

好吧,似乎该任务旨在锻炼堆栈使用情况,并发表您的评论:

  

不了解主题

似乎是准确的。但是,然后您要人们将本书的简短章节写成简单的SO答案……嗯,通常我不喜欢这样做,因为整本书是完整的书,简短的答案将不得不省略一些细节,但是让我们尝试是否有减少的东西可以作为答案:

16b实模式下的堆栈内存是普通的计算机内存,但是寄存器对ss:sp指向“堆栈顶部”,并且有多个指令隐式地使用这两个寄存器来访问内存,例如{{ 1}}确实“将存储器地址pop dx上的字值读入寄存器ss:sp,然后将2加到dx(以使其指向存储器中的下一个字)”(意义为“ 16位信息”,而不是“文本”字。

记下关于push / pop指令的说明(请查看指令指南,无论是Intel的官方文档,还是https://www.felixcloutier.com/x86/之类的基础知识),sp确实会从push中减去,即堆栈从高内存地址向低内存地址“增长”,并且当您sp时,pop返回高内存地址。 “堆栈顶部”是最后一个被推入的堆栈,位于当前地址sp上。推入堆栈的上一个项目位于ss:sp(在16b模式下),等等...因此,如果您要使用ss:sp+2进行寻址,并将bp的值复制到{{ 1}}在最后sp将项目存储到堆栈之后,则可以使用bp寻址来访问您的项目(注意:push默认情况下与[bp+0], [bp+2], [bp+4], ...段相关,因此{ {1}}是隐式bp(从堆栈段中加载值),而ss默认是隐式mov ax,[bp](从数据段中加载)..除非您明确指定其他段覆盖源代码)。

还要注意指令mov ax,ss:[bp]mov ax,[bx],它们也隐式地使用堆栈,因此您会开始使用mov ax,ds:[bx] / call指令来使用子例程吗?在子程序内部时,堆栈数据的结构还将包含返回地址(进入子程序时位于“堆栈顶部”)。

在emu8086中的代码中,您通过在代码的开头定义堆栈段来保留堆栈的内存区域,保留256个字节(128个字),对于像这样的重堆栈任务,这是非常小的堆栈,例如代码将以“每输入一个字符一个字”的速度吃掉堆栈,您最多可以输入128个字符...这似乎是合理的,直到您了解到DOS中断也使用了实际的用户堆栈,这有两个主要结果:

  • 这128个字中的一些被中断使用(不确定多少,比如说30个字),如果您输入100个字符,则中断将已经位于保留内存以下,开始覆盖您未使用的部分内存。 t为其保留(“堆栈溢出”问题)
  • 当前 ret下的内存会被DOS中断处理程序定期覆盖

您的任务听起来像您可以做到的:

  • 在循环中读取用户输入,直到输入z / Z,将每一项放入堆栈中(并且计数=您现在已经有了类似的东西,一旦您将call固定为正确的时间),或者空堆栈的“结束”指针)
  • 浏览所有输入的值,并且仅输出小字符
  • 输出换行符
  • 遍历所有输入值,仅输出大写字母
  • 输出换行符
  • 浏览所有输入值,仅输出数字
  • 输出换行符
  • 退出

现在,您可能会很想在第一个“遍历所有输入值”中进行ret的操作,但这意味着您可以将ss:sp向上移至原始的“空堆栈” ...并且如果然后从pop中减去2 *个项目,您可能会认为您现在可以再次从内存中pop进行相同的字符了,通过ss:sp修改作弊。但是,如果同时发生某些中断,则sp以下的原始字符会被销毁,因此前两个循环不要pop

宁可使用sp来访问数据,即在每个输出循环之前访问ss:sp,然后循环执行项-多次执行pop bp,但保持{{ 1}}不变(并将项目副本的数量保留在某处,因此您可以在第二和第三循环中使用它……或在开始时将“空” mov bp,sp复制到类似mov dl,[bp]的地方,以进行{{1 }}检查输出循环(如果您已仔细阅读所有项目)。

如果您不太熟悉汇编程序设计主题,并且对我的大部分文本感到迷惑,请先阅读一些书或教程。有关寄存器/内存/等的基础知识通常可以从此摘要中{{3 }}的目标是32位模式,但是我不了解16b的简短介绍(我想您的讲师会给您一些建议,学习什么),或者使用google ...请记住16b模式更加棘手比32b模式更困难,因为您还必须了解内存段,并且内存寻址受到更多限制(add bp,2在16b模式下不存在,而ss:sp在32b模式下是合法的)。

如果您仅对某些特定部分有疑问,请提出评论。

然后看一下代码,学习使用调试器(这是绝对必要的,将stackoverflow web用作调试服务效率较低,并且许多人(包括我在内)都认为这是粗鲁和不良的行为...如果可以证明您已经调试了代码,并且可以很好地描述令人困惑的行为(正在发生的事情以及您想要/期望的事情),将会有更多的人愿意为您写答案,为什么会发生以及思维过程在哪里出错),并尝试使用上述信息将其重写。