【问题标题】:Assembly Language Shift into Carry Flags汇编语言转换为进位标志
【发布时间】:2016-04-28 02:27:35
【问题描述】:

我目前有一个任务,我必须用汇编语言编写代码,你需要用户输入来获得一个 4 位十六进制值并将其转换为二进制,然后在你获得二进制值后,你必须将其转换为一个月和年份,其中前 7 位数字是年份,接下来的 4 位是月份,最后 5 位是日期。

我已将所有内容转换为二进制,并且知道如何将其从二进制转换为年、月和日的正常整数值。当我运行我的代码时,输​​出是0/0/0。我不确定这是我在换档时搞砸的地方还是其他什么地方。你们能看一下并给我一个关于在哪里更正的意见吗?在我粘贴的代码中,我只输入了 calcYear 并认为我可以解决这个问题,然后从那里开始处理其余部分。

我的代码:

firstLine:

    call crlf
    mov si, offset programOne
    mov cx, programOneLen
    call putStrng       ;displays 'Program by Joe Remaklus'

    call crlf
    call crlf

    call inputVal   ;prompt for hex input
    call putBin ;display the value in AX as binary
    call crlf
    call calcYear   ;display the year of the first 7 binary digits.
    mov si, offset slash
    mov cx, slashLen
    call putStrng
    call calcMonth  ;display the month of the next 4 binary digits.
    mov si, offset slash
    mov cx, slashLen
    call putStrng
    call calcDay    ;display the day of the next 5 binary digits.
    call crlf

    call inputVal
    call putBin






    mov ah,04c
    int 021




prompt db 'Enter a 4-digit hex value'

lenPrompt = $-prompt


inputVal:

    push si, cx

    mov  si, offset prompt

    mov  cx, lenPrompt

    call putStrng

    call crlf

    call getHex

    call crlf

    pop  cx, si

    ret
;---------------------------------------------------------------

putBin:

    push ax, cx, dx

    mov  cx, 16     ;number of bits to display

  putBinLoopTop:

    mov  dl, '0'        ;assume bit to display is zero

    shl  ax, 1      ;shift bit to display into Carry Flag

    jnc  putBinSkipInc  ;if the top bit was zero skip the inc

    inc  dl         ;else inc DL to '1'

  putBinSkipInc:

    call putChar        ;display the character in DL

    loop putBinLoopTop  ;continue until 16 bits are displayed

    pop  dx, cx, ax

    ret
;---------------------------------------------------------------

calcYear:

    mov year, 0

    mov si, 0

    shl ax, 1
    adc si, 0
    iMul onetwoeight
    add year, si
    mov si, 0 

    shl ax, 1
    adc si, 0
    iMul sixfour
    add year, si
    mov si, 0

    shl ax, 1
    adc si, 0
    iMul threetwo
    add year, si
    mov si, 0

    shl ax, 1
    adc si, 0
    iMul sixteen
    add year, si
    mov si, 0

    shl ax, 1
    adc si, 0
    iMul eight
    add year, si
    mov si, 0


    shl ax, 1
    adc si, 0
    iMul four
    add year, si
    mov si, 0


    shl ax, 1
    adc si, 0
    iMul two
    add year, si
    mov si, 0


    shl ax, 1
    adc si, 0
    iMul one
    add year, si
    mov si, year
    add si, 1980
    call putPos
ret

【问题讨论】:

  • 您是否在调试器中单步执行此操作以确保 regs 中的值符合您的预期?你说得对,shl ax/adc si, 0 相当于在 ax 中测试一点,然后使用setcc。 (例如xor dx,dx/bt ax, 3/setc dl,除非这不会修改ax)我完全不相信您的imuls 有意义。也许这是一种非常迂回的做事方式,但我还没弄清楚是什么。你知道乘以 2 的幂和移位是一样的,对吧?此外,您可以一直将year 保留在si 中,而不是一直使用内存目标。
  • 由于代码中缺少 cmets 或对确切年份 calc 应该实现的算法的描述而被否决。对我来说,添加这些位看起来有点奇怪(popcnt)。也因为没有一个最小的例子来说明你遇到的问题。这仍然是很多代码。如果您改进了问题,请回复,我可能会删除我的反对票。
  • 没有。使用调试器来确定您的代码哪里出错了,如果您看不到为什么会发生这种特定行为,请再次询问更具体的内容,而不是“...输入要纠正的地方”。

标签: assembly x86 shift carryflag irvine16


【解决方案1】:

如果我理解正确,在输入 calcYear 时,您的“ax”在二进制文件中如下所示: yyyy yyym mmmd dddd

并且您想将值提取回普通数字。 我想我可以理解你对“calcYear”的想法,但很抱歉没有费心去尝试完全理解并修复它。如果您仍然好奇,只需使用调试器单步执行每条指令,看看它往哪里走。

我将向您展示如何以不同的方式思考这个问题。

让我们尝试挖掘并理解这一点:

    ; ax is encoded as this: yyyy yyym mmmd dddd
    push ax
    push ax ; store the encoded value at stack twice
    ; extract "day" value
    and ax,01Fh  ;only "d" bits will survive
    mov [day],ax
    ; extract "month" value
    pop ax ; restore encoded input
    shr ax,5 ; shift ax by number of "d" bits
    and ax,0Fh ; only shifted "m" bits
    mov [month],ax
    ; extract "year" value
    pop ax ; restore encoded input
    shr ax,5+4 ; shift ax by number of "d" and "m" bits
    ; no need to "and", as "shr" did fill upper bits by zeroes
    add ax,1980 ; so "0" encoded year is 1980? (deducted from OP source)
    mov [year],ax
    ret

我希望这会给你一些新的想法,如何使用特定的位数。请记住,and/or 可以很好地掩盖您感兴趣的事物,shr/sar/sal/shl/ror/rol/rcr/... 可以很好地将其放在所需的位置。 xor 可用于对结果进行一些修补(如果需要),test 就像 and 但只更新标志寄存器,然后还有一些面向位的 x386+(或 486?)指令,这有点“高级”,可以为您节省我之前提到的那些基础知识的一些 2-3 指令组合。所以你可以放心地忽略它们,只要你完全理解那些基本的,并且可以随心所欲地使用它们。

【讨论】:

  • 如果你好奇我是如何考虑这个问题的......不幸的是我只向你展示了一半的魔法,这是第一行:; ax is encoded as this: yyyy yyym mmmd dddd = 1) 确保你明白什么是您的输入数据。隐藏的后半部分魔法是 2) 充分了解所需的输出数据,在这种情况下,我想得到 year = 0yyy yyyy, month = 0000 mmmm, day = 000d dddd ...然后我开始思考如何移动这些位并通过掩码提取它们以获得所需的输出。确保您完全了解目标,然后找到算法来完成它。
  • 最后注...如果您是学生,您可能想知道为什么and ax,01F(为什么我使用 31 的十六进制方式)...因为经过多年的 ASM 编程,我几乎可以看到这些位在十六进制数字中,例如 AA 是很好的“1010 1010”模式......请记住,每 4 位(有时称为“半字节”)形成一个十六进制数字,因此从长二进制数计算十六进制值对于人类来说实际上比尝试计算十进制值更容易,这涉及到大量的乘法和加法。所以当我写“and reg, MASK”时,我知道 MASK 的二进制值,我只是从头开始用十六进制写。
  • 由于您对push ax 的疯狂建议两次而未投票。推一次,然后加载而不是poping 怎么样?或者更好的是,将其复制到另一个寄存器。否则,很好的答案,但这些代码转储问题不会为未来的读者增加太多的价值。
  • 我的主要反对意见是你推送两次,而不是push / mov ax, [bp-?] / pop ax。哦,我猜你避免在堆栈帧上浪费指令,而 16 位不能使用 [sp]... 不过,不喜欢避免使用暂存寄存器的编码风格。了解函数可用于暂存空间的调用保留寄存器与调用破坏寄存器是很有价值的。我不得不承认,你额外的push 也比到处使用内存目的地要好。
  • 我认为这个问题根本不应该回答。这不是 OP 所遇到问题的最小示例,而且我认为它对未来的 SO 读者不会有太大的未来价值,所以它只是占用空间。回答不好的问题只会鼓励人们继续问他们。我还必须承认,在投票和投票结束之前,我确实倾向于给 cmets 一些小的帮助。但是对于某些问题来说,完整的工作解决方案肯定太多了。无论如何,请为不浪费指令制作堆栈帧投赞成票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
  • 2014-06-07
  • 1970-01-01
  • 2016-12-22
  • 2021-08-22
相关资源
最近更新 更多