assembly 无法理解为什么程序集不工作输出不显示正确的结果,即使值从值寄存器正确复制

kuarbcqp  于 2023-11-19  发布在  其他
关注(0)|答案(1)|浏览(145)

我试图建立一个程序在emu8086这将是作为输入1 8位二进制数,然后在输出中显示它的十六进制形式。
我的代码是这样的:

  1. data segment
  2. ends
  3. stack segment
  4. dw 128 dup(0)
  5. ends
  6. code segment
  7. start:
  8. mov ax,data
  9. mov ds,ax
  10. mov cx,0
  11. mov bl,8
  12. input:
  13. mov ah,07h
  14. int 21h
  15. cmp al,46
  16. sub al,30h
  17. cmp al,0
  18. je valid
  19. cmp al,1
  20. je valid
  21. jmp input
  22. valid:
  23. cmp bl,0
  24. je exit
  25. sub bl,1
  26. shl cl,1
  27. add cl,al
  28. jmp input
  29. exit:
  30. mov bl,15
  31. and bl,cl
  32. cmp bl,0
  33. je printzero
  34. cmp bl,1
  35. je printone
  36. cmp bl,2
  37. je printtwo
  38. cmp bl,3
  39. je printthree
  40. cmp bl,4
  41. je printfour
  42. cmp bl,5
  43. je printfive
  44. cmp bl,6
  45. je printsix
  46. cmp bl,7
  47. je printseven
  48. cmp bl,8
  49. je printeight
  50. cmp bl,9
  51. je printnine
  52. cmp bl,10
  53. je printa
  54. cmp bl,11
  55. je printb
  56. cmp bl,12
  57. je printc
  58. cmp bl,13
  59. je printd
  60. cmp bl,14
  61. je printe
  62. cmp bl,15
  63. je printf
  64. printzero:
  65. mov dl,'0'
  66. mov ah,02h
  67. int 21h
  68. jmp exit2
  69. printone:
  70. mov dl,'1'
  71. mov ah,02h
  72. int 21h
  73. jmp exit2
  74. printtwo:
  75. mov dl,'2'
  76. mov ah,02h
  77. int 21h
  78. jmp exit2
  79. printthree:
  80. mov dl,'3'
  81. mov ah,02h
  82. int 21h
  83. jmp exit2
  84. printfour:
  85. mov dl,'4'
  86. mov ah,02h
  87. int 21h
  88. jmp exit2
  89. printfive:
  90. mov dl,'5'
  91. mov ah,02h
  92. int 21h
  93. jmp exit2
  94. printsix:
  95. mov dl,'6'
  96. mov ah,02h
  97. int 21h
  98. jmp exit2
  99. printseven:
  100. mov dl,'7'
  101. mov ah,02h
  102. int 21h
  103. jmp exit2
  104. printeight:
  105. mov dl,'8'
  106. mov ah,02h
  107. int 21h
  108. jmp exit2
  109. printnine:
  110. mov dl,'9'
  111. mov ah,02h
  112. int 21h
  113. jmp exit2
  114. printa:
  115. mov dl,'A'
  116. mov ah,02h
  117. int 21h
  118. jmp exit2
  119. printb:
  120. mov dl,'B'
  121. mov ah,02h
  122. int 21h
  123. jmp exit2
  124. printc:
  125. mov dl,'C'
  126. mov ah,02h
  127. int 21h
  128. jmp exit2
  129. printd:
  130. mov dl,'D'
  131. mov ah,02h
  132. int 21h
  133. jmp exit2
  134. printe:
  135. mov dl,'E'
  136. mov ah,02h
  137. int 21h
  138. jmp exit2
  139. printf:
  140. mov dl,'F'
  141. mov ah,02h
  142. int 21h
  143. jmp exit2
  144. exit2:
  145. mov bl,240
  146. and bl,cl
  147. cmp bl,0
  148. je printzero2
  149. cmp bl,1
  150. je printone2
  151. cmp bl,2
  152. je printtwo2
  153. cmp bl,3
  154. je printthree2
  155. cmp bl,4
  156. je printfour2
  157. cmp bl,5
  158. je printfive2
  159. cmp bl,6
  160. je printsix2
  161. cmp bl,7
  162. je printseven2
  163. cmp bl,8
  164. je printeight2
  165. cmp bl,9
  166. je printnine2
  167. cmp bl,10
  168. je printa2
  169. cmp bl,11
  170. je printb2
  171. cmp bl,12
  172. je printc2
  173. cmp bl,13
  174. je printd2
  175. cmp bl,14
  176. je printe2
  177. cmp bl,15
  178. je printf2
  179. printzero2:
  180. mov dl,'0'
  181. mov ah,02h
  182. int 21h
  183. jmp exit3
  184. printone2:
  185. mov dl,'1'
  186. mov ah,02h
  187. int 21h
  188. jmp exit3
  189. printtwo2:
  190. mov dl,'2'
  191. mov ah,02h
  192. int 21h
  193. jmp exit3
  194. printthree2:
  195. mov dl,'3'
  196. mov ah,02h
  197. int 21h
  198. jmp exit3
  199. printfour2:
  200. mov dl,'4'
  201. mov ah,02h
  202. int 21h
  203. jmp exit3
  204. printfive2:
  205. mov dl,'5'
  206. mov ah,02h
  207. int 21h
  208. jmp exit3
  209. printsix2:
  210. mov dl,'6'
  211. mov ah,02h
  212. int 21h
  213. jmp exit3
  214. printseven2:
  215. mov dl,'7'
  216. mov ah,02h
  217. int 21h
  218. jmp exit3
  219. printeight2:
  220. mov dl,'8'
  221. mov ah,02h
  222. int 21h
  223. jmp exit3
  224. printnine2:
  225. mov dl,'9'
  226. mov ah,02h
  227. int 21h
  228. jmp exit3
  229. printa2:
  230. mov dl,'A'
  231. mov ah,02h
  232. int 21h
  233. jmp exit3
  234. printb2:
  235. mov dl,'B'
  236. mov ah,02h
  237. int 21h
  238. jmp exit3
  239. printc2:
  240. mov dl,'C'
  241. mov ah,02h
  242. int 21h
  243. jmp exit3
  244. printd2:
  245. mov dl,'D'
  246. mov ah,02h
  247. int 21h
  248. jmp exit3
  249. printe2:
  250. mov dl,'E'
  251. mov ah,02h
  252. int 21h
  253. jmp exit3
  254. printf2:
  255. mov dl,'F'
  256. mov ah,02h
  257. int 21h
  258. jmp exit3
  259. exit3:
  260. mov ax, 4c00h
  261. int 21h
  262. ends
  263. end start

字符串
我很快意识到,你不需要把每个输入都存储在一个数组中,你可以把存储数字的寄存器的值左移1,然后加上输入(1或0)。好吧,我希望我的代码能工作,但我遇到了一个问题:
存储我的数字值的寄存器是CL。如果我按此顺序键入11110000,则CL的值将变为F0,这就是我想要的。我将其通过Fh的AND掩码提取4 LSB,然后通过F0h的AND掩码提取4 MSB,但在输出中我得到00.老实说,我不知道我错在哪里。BL寄存器的值变得与CL寄存器的值相同,但它仍然不起作用。

qybjjes1

qybjjes11#

必要的更正

  1. mov bl,8
  2. input:
  3. mov ah,07h
  4. int 21h
  5. cmp al,46
  6. sub al,30h
  7. cmp al,0
  8. je valid
  9. cmp al,1
  10. je valid
  11. jmp input
  12. valid:
  13. cmp bl,0
  14. je exit
  15. sub bl,1
  16. shl cl,1
  17. add cl,al
  18. jmp input
  19. exit:

字符串

  • cmp al,46指令没有任何用处。它是以前编辑时留下的:只需删除它。
  • 输入循环运行1次太多!幸运的是,你没有把第九个输入加到CL寄存器。原因是你在错误的地方检查BL变成0。见下文。

您可以简化检查和循环:

  1. mov bl, 8
  2. input:
  3. mov ah, 07h ; DOS.DirectSTDINInput
  4. int 21h ; -> AL
  5. sub al, 30h ; Convert from character to number [0,9]
  6. cmp al, 1 ; Everything other than [0,1] is ABOVE 1 ...
  7. ja input ; and so `JA input` will go repeat the input
  8. shl cl, 1 ; Make room in CL
  9. add cl, al ; Add the new digit
  10. dec bl ; `DEC` already sets the flags
  11. jnz input ; Repeating 8 times means we jump back 7 times
  12. exit:
  1. exit:
  2. mov bl,15
  3. and bl,cl
  4. cmp bl,0
  5. je printzero
  6. cmp bl,1
  7. je printone
  8. ...
  9. exit2:
  10. mov bl,240
  11. and bl,cl
  12. cmp bl,0
  13. je printzero2
  14. cmp bl,1
  15. je printone2
  16. ...

的数据

  • 您需要先显示最高有效位!因此在 exit 使用掩码240来检索高半字节,在 * exit 2 * 使用掩码15来检索低半字节。
  • 您没有真正检索到高半字节!240的and所做的是从BL寄存器中删除低半字节,但高半字节仍然保留在原来的位置。这意味着BL中的值为{0,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240}。

然后,* 快速修复 * 是替换cmp bl,?指令系列:

  1. exit:
  2. mov bl, 240
  3. and bl, cl
  4. cmp bl, 0
  5. je printzero2
  6. cmp bl, 16
  7. je printone2
  8. cmp bl, 32
  9. je printtwo2
  10. ...
  11. exit2:
  12. mov bl, 15
  13. and bl, cl
  14. cmp bl, 0
  15. je printzero
  16. cmp bl, 1
  17. je printone
  18. cmp bl, 2
  19. je printtwo
  20. ...

改进的第一步

目前你的程序包含很多重复的指令。更好的解决方案存在,我相信@PeterCordes提供的链接会揭示这些。
尽管如此,除了自己完成整个任务之外,我建议你学习下面的代码,你会发现这已经大大简化了程序,并且它演示了如何编写可读的代码。请注意,我并不是把它作为 * 最佳 * 解决方案:

  1. mov bl, 8
  2. input:
  3. mov ah, 07h ; DOS.DirectSTDINInput
  4. int 21h ; -> AL
  5. sub al, '0' ; Convert from character to number [0,9]
  6. cmp al, 1 ; Everything other than [0,1] is ABOVE 1 ...
  7. ja input ; and so `JA input` will go repeat the input
  8. shl cl, 1 ; Make room in CL
  9. add cl, al ; Add the new digit
  10. dec bl ; `DEC` already sets the flags
  11. jnz input ; Repeating 8 times means we jump back 7 times
  12. ; ---------------
  13. mov bl, 0F0h ; High Nibble mask
  14. and bl, cl ; BL = {0, 16, 32, 48, ... , 208, 224, 240}
  15. mov dl, '0'
  16. cmp bl, 0
  17. je printHigh
  18. mov dl, '1'
  19. cmp bl, 16
  20. je printHigh
  21. ...
  22. mov dl, 'E'
  23. cmp bl, 224
  24. je printHigh
  25. mov dl, 'F' ; No need for `CMP BL, 240`. It's 240 for sure
  26. printHigh:
  27. mov ah, 02h
  28. int 21h
  29. ; ---------------
  30. mov bl, 0Fh ; Low Nibble mask
  31. and bl, cl ; BL = {0, 1, 2, 3, ... , 13, 14, 15}
  32. mov dl, '0'
  33. cmp bl, 0
  34. je printLow
  35. mov dl, '1'
  36. cmp bl, 1
  37. je printLow
  38. ...
  39. mov dl, 'E'
  40. cmp bl, 14
  41. je printLow
  42. mov dl, 'F' ; No need for `CMP BL, 15`. It's 15 for sure
  43. printLow:
  44. mov ah, 02h
  45. int 21h
  46. ; ---------------
  47. mov ax, 4C00h
  48. int 21h

最终改进步骤

因为你的程序包含8个从键盘输入的字符和2个输出到屏幕的字符,所以优化的版本应该关注代码大小而不是执行时间,这10个DOS函数所花费的时间几乎占了执行时间的100%!
由于所有的重复指令,你的程序目前包含471字节。我的“第一步”解决方案已经将这个数字降低到255字节,但我们肯定可以做得更好吗?
让我们看看这个程序是怎么做的,你输入八个二进制数字,合并把它们组合成一个字节大小的值,然后立即把这个字节分解成两个半字节。
通常,(相当大的)速度增益或大小减小来自于我们使用的方法的根本改变。我建议的是不必将一个字节分解为两个半字节。下面的片段重复两次,只有四个二进制数字的输入被转换为一个十六进制字符。

  • 第一个代码片段使用了一个包含十六进制字符的查找表。这个解决方案的长度为55字节:
  1. ORG 256
  2. call @f ; -> DH (AX BX)
  3. mov dl, dh
  4. call @f ; -> DH (AX BX)
  5. mov ah, 02h ; DOS.DisplayOutput
  6. int 21h ; -> (AL)
  7. mov dl, dh
  8. int 21h ; -> (AL)
  9. ret ; Terminate in .COM program
  10. @@: mov bx, 0010h ; Initialize BX with a sentinel-bit set at index 4
  11. mov ah, 07h ; DOS.DirectSTDINInput
  12. @@: int 21h ; -> AL
  13. sub al, '0' ; Convert from ["0","1"] to [0,1]
  14. shr al, 1 ; Bring digit [0,1] into the carry flag
  15. jnz @b ; Every invalid key repeats the loop
  16. rcl bl, 1 ; Bring new digit into BL
  17. jnc @b ; Repeat while sentinel-bit was not shifted out
  18. mov dh, [list + bx] ; Fetch a hex character
  19. ret
  20. lst: db '0123456789ABCDEF'

  • 第二个代码片段使用了一个非常古老而又非常聪明的计算方法将[0,15]转换为十六进制字符。这个解决方案有39字节:
  1. ORG 256
  2. call @f ; -> AL (AX BX)
  3. xchg dx, ax ; Shorter code than `mov dl, al`
  4. call @f ; -> AL (AX BX)
  5. push ax
  6. mov ah, 02h ; DOS.DisplayOutput
  7. int 21h ; -> (AL)
  8. pop dx
  9. int 21h ; -> (AL)
  10. ret ; Terminate in .COM program
  11. @@: mov bl, 00010000b ; Initialize BL with a sentinel-bit set at index 4
  12. mov ah, 07h ; DOS.DirectSTDINInput
  13. @@: int 21h ; -> AL
  14. sub al, '0' ; Convert from ["0","1"] to [0,1]
  15. shr al, 1 ; Bring digit [0,1] into the carry flag
  16. jnz @b ; Every invalid key repeats the loop
  17. rcl bl, 1 ; Bring new digit into BL
  18. jnc @b ; Repeat while sentinel-bit was not shifted out
  19. xchg ax, bx ; Shorter code than `mov al, bl`
  20. cmp al, 10 ; \.
  21. sbb al, 69h ; |. From [0,15] to {["0","9"],["A","F"]}
  22. das ; /.
  23. ret ; AL now has a hex character


这些优化的片段是使用FASM组装的。

展开查看全部

相关问题