《汇编语言·第三版》--王爽
1 任务
将实验7中的Power idea公司的数据按照图10.2所示的格式在屏幕上显示出来。
在这个程序中,要用到前面学到的几乎所有的知识,主要选择适当的寻址方式和相关子程序的设计和应用。
另外,要注意,因为程序显示的数据有些已经大于65535,应该编写一个新的数据到字符串转化的子程序,完成dword型数据到字符串的转化,说明如下。
名称:dtoc
功能:将dword型数转变为表示十进制数的字符串,字符串以0为结尾符。
参数:(ax) = dword型数据的低16位,(dx) = dword型数据的高16位,ds:si指向字符串的首地址
返回:无
在这个子程序中要注意除法溢出的问题,可以用我们在实验IV中设计的子程序divdw来解决。
2 程序设计
(1) 程序流程图
Figure1. 程序设计图
divdw():解决“收入/员工”除法溢出问题,并得出员工平均工资。
dtoc():数字转字符串。
show_str():将Buf内以0结束的字符串以指定属性显示在指定的行列。
(2) 程序流程图对应的程序框架
1. assume cs:main,ss:stack 2. 3. ;数据段:存储欲显示在显存之上的数据 4. data segment 5. db '1975','1976','1977','1978','1979','1980','1981','1982','1983' 6. db '1984','1985','1986','1987','1988','1989','1990','1991','1992' 7. db '1993','1994','1995' 8. ;以上是表示21年的21个字符串 9. 10. dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514 11. dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000 12. ;以上是表示21年公司总收入的21个dword型数据 13. 14. dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226 15. dw 11542,14430,15275,17800 16. ;以上是表示21年雇员人数的21个word型数据 17. data ends 18. 19. ;缓存区 20. buf segment 21. db 32 dup(0),0 22. buf ends 23. 24. ;堆栈段:(7 + 14 * 2)bytes足矣 25. stack segment 26. dw 20 dup(0) 27. stack ends 28. 29. 30. main segment 31. start: 32. main0: 33. call far ptr read_year ;调用read_year函数,将data段(si) + 1年份读到buf中,以0结束 34. call far ptr show_str ;调用show_str显示在buf中的字符串 35. 36. 37. call far ptr read_money ;调用read_money读si年的收入,将收入的低16位保存到ax中,收入的高16位保存到dx中 38. call far ptr dtoc ;调用dtoc将ax和dx共同保存的数据转换为对应的字符串到buf中 39. call far ptr show_str ;调用show_str显示在buf中的字符串 40. 41. 42. call far ptr read_staff ;调用read_staff读取data中的员工数目,(bx) =员工数目 43. call far ptr dtoc ;调用dtoc将ax和dx共同保存的数据转换为对应的字符串到buf中 44. call far ptr show_str ;调用show_str显示在buf中的字符串 45. 46. 47. call far ptr read_money ;调用read_money读si年的收入,将收入的低16位保存到ax中,收入的高16位保存到dx中 48. call far ptr read_staff ;调用read_staff读取data中si年的员工数目,(bx) =员工数目 49. call far ptr dtoc ;调用dtoc将ax和dx共同保存的数据转换为对应的字符串到buf中 50. call far ptr show_str ;调用show_str显示在buf中的字符串 51. loop main0 52. 53. mov ax, 4c00h 54. int 21h |
3 子函数
编写子函数时,各子函数要主要压栈保存在子函数内会修改的寄存器变量值。
(1) 数字转字符串
1. ;功能:将dword型数转变为表示十进制数的字符串,字符串以0为结尾符。 2. ;参数:(ax) = dword型数据的低16位,(dx) = dword型数据的高16位,ds:si指向字符串的首地址 3. ;返回:无 4. dtoc: 5. push ax 6. push cx 7. push dx 8. push si 9. 10. mov cx, 0 11. push cx ;先将0入栈,作为标记 12. 13. dtc_s0: 14. mov cx,ax 15. add cx,dx 16. jcxz dtc_s1 17. mov cx,10 18. call far ptr divdw 19. add cx,30h 20. push cx 21. loop dtc_s0 22. 23. dtc_s1: 24. pop cx 25. mov ds:[si],cl 26. jcxz dtc_s2 27. inc si 28. loop dtc_s1 ;将栈中数据取出 29. 30. dtc_s2: 31. pop si 32. pop dx 33. pop cx 34. pop ax 35. retf |
(2) 解决除法溢出问题
1. ;功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。 2. ;参数:(ax) = dword型数据的低16位,(dx) = dword型数据的高16位,(cx) = 除数 3. ;返回:(dx) = 结果的高16位,(ax) = 结果的低16位,(cx) = 余数 4. divdw: 5. push bx 6. push si 7. 8. mov bx,ax 9. mov ax, dx 10. mov dx, 0 11. 12. div cx ;ax = int(H/N), dx = rem(H/N) 13. mov si, ax ;si =int(H/N), (dx) = rem(H/N) * 65536 14. 15. mov ax, bx 16. div cx ;(rem(H/N) * 65536 + L) / N, ax保存的低16位商,(dx) = 余数 17. 18. mov cx, dx 19. mov dx, si 20. 21. pop si 22. pop bx 23. retf |
(3) 显示字符串
1. ;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串 2. ;参数:(dh) = 行号(取值范围 0 ~ 24),(dl) = 列号(取值范围0 ~ 79),(cl) = 颜色,ds:si指向字符串的首地址 3. ;返回:无 4. show_str: 5. push ax 6. push cx 7. push dx 8. push es 9. push bp 10. push di 11. push si 12. 13. mov ax, 0b800h 14. mov es, ax ;es段保存显存段地址 15. 16. mov al, cl ;字符前景色保存到al 17. 18. mov bp, 0 19. mov ch, 0 20. mov cl, dh 21. inc cl 22. str_s0: 23. jcxz str_s1 24. add bp,160 ;行号对应的显存地址 25. loop str_s0 26. str_s1: 27. mov di,0 28. mov ch,0 29. mov cl,dl 30. str_s2: 31. jcxz str_s3 32. add di,2 ;列号对应的显存地址 33. loop str_s2 ;es:[di + bp]为dh行dl列对应的显存地址 34. 35. str_s3: 36. mov cl,ds:[si] 37. jcxz str_s4 ;检测当前字符是否为0,如果为0则转移到s4处执行 38. mov es:[bp+ di],cl 39. inc di 40. mov es:[bp+di], al ;字符前景色 41. inc di 42. inc si 43. loop str_s3 44. 45. str_s4: 46. pop si 47. pop di 48. pop bp 49. pop es 50. pop dx 51. pop cx 52. pop ax 53. retf |
(4) 用来读(寻址)data段数据的子函数
1. ;功能:读取data段的(si)年份到buf中,以0结束 2. ;参数:无 3. ;返回:无 4. read_year: 5. push ax 6. push cx 7. push dx 8. push ds 9. push si 10. push bp 11. 12. mov ax, data 13. mov ds, ax 14. mov ax, 4 15. mul si 16. mov si, ax ;ds:[si]指向pop si,(si)年的年份 17. 18. mov ax, buf 19. mov es,ax 20. mov bp, 0 ;es:[bp]指向buf段的首个内存单元 21. 22. mov cx, 2 23. read_s0: 24. mov ax,ds:[si] 25. mov es:[bp],ax ;年份低字节 26. add si,2 27. add bp,2 28. loop read_s0 29. 30. mov al, 0 31. mov es:[bp],al ;字符串以0为结尾符 32. 33. pop bp 34. pop si 35. pop ds 36. pop dx 37. pop cx 38. pop ax 39. Retf 40. 41. 42. ;功能:读取data段的(si)年收入到(ax)和(dx)中 43. 参数:(si) = 第几年 44. ;返回:(ax) = dword低16位,(dx)=dword高16位 45. read_money: 46. push si 47. push ds 48. 49. mov ax, data 50. mov ds, ax 51. mov ax, 4 52. mul si 53. mov si, ax 54. add si, 84 ;ds:[si]指向pop si年的收入 55. 56. mov ax,ds:[si] 57. mov dx,ds:[si + 2] 58. 59. pop ds 60. pop si 61. Retf 62. 63. 64. 65. ;功能:读取data段的(si)年员工数到(ax)中 66. ;参数:(si) = 第几年 67. ;返回:(ax) = 员工数 68. read_staff: 69. push ax 70. push dx ;乘法会改变dx的值 71. push si 72. push ds 73. 74. mov ax, data 75. mov ds, ax 76. mov ax, 2 77. mul si 78. mov si, ax 79. add si, 168 ;ds:[si]指向pop si年的员工 80. 81. mov bx,ds:[si] 82. 83. pop ds 84. pop si 85. pop dx 86. pop ax 87. retf |
4 主程序
1. main segment 2. start: mov ax, stack 3. mov ss, ax 4. mov sp, 40 ;ss:sp指向栈底 5. 6. mov cx, 21 ;每次读一年数据,一共21年的数据,需要读21次 7. mov si, 0 ;用来表示将要读取第(si) + 1年的数据 8. mov dx, 02h ;第一年数据第一个数据在屏幕上显示的位置 9. 10. main_s1_3: 11. ;---------------显示年份-------------------------------------------- 12. call far ptr read_year ;调用read_year函数,将data段(si) + 1年份读到buf中,以0结束 13. 14. push cx 15. push si ;需要压栈保存寄存器的值 16. mov ax, buf 17. mov ds, ax 18. mov si, 0 19. mov cl, 02 20. call far ptr show_str ;调用show_str显示在buf中的字符串 21. ;---------------显示年份-------------------------------------------- 22. 23. ;---------------显示收入-------------------------------------------- 24. pop si ;需要出栈使用寄存器的值 25. push si 26. push dx 27. call far ptr read_money ;调用read_money读si年的收入,将收入的低16位保存到ax中,收入的高16位保存到dx中 28. mov bx, buf 29. mov ds, bx 30. mov si, 0 31. call far ptr dtoc ;调用dtoc将ax和dx共同保存的数据转换为对应的字符串到buf中 32. 33. pop dx 34. push dx 35. mov ax, buf 36. mov ds, ax 37. mov si, 0 38. add dl, 12 39. mov cl, 2 40. call far ptr show_str ;调用show_str显示在buf中的字符串 41. pop dx 42. ;---------------显示收入-------------------------------------------- 43. 44. ;---------------显示员工-------------------------------------------- 45. pop si 46. push si 47. push dx 48. call far ptr read_staff ;调用read_staff读取data中的员工数目,(bx) =员工数目 49. mov ax, bx 50. mov dx, 0 51. mov bx, buf 52. mov ds, bx 53. mov si, 0 54. call far ptr dtoc ;调用dtoc将ax和dx共同保存的数据转换为对应的字符串到buf中 55. 56. pop dx 57. 58. push dx 59. mov ax, buf 60. mov ds, ax 61. mov si, 0 62. add dl, 24 63. call far ptr show_str ;调用show_str显示在buf中的字符串 64. pop dx 65. pop si 66. 67. 68. inc si ;下一年数据 69. inc dh ;显示在下一行 70. 71. pop cx 72. loop main_s1_3 73. ;---------------显示员工-------------------------------------------- 74. 75. ;---------------显示员工平均工资-------------------------------------------- 76. 77. mov cx, 21 ;每次读一年数据,一共21年的数据,需要读21次 78. mov si, 0 ;用来表示将要读取第(si) + 1年的数据 79. mov dx, 02h ;第一年数据第一个数据在屏幕上显示的位置 80. main_s4: 81. push cx 82. push dx 83. call far ptr read_money ;调用read_money读si年的收入,将收入的低16位保存到ax中,收入的高16位保存到dx中 84. call far ptr read_staff ;调用read_staff读取data中si年的员工数目,(bx) =员工数目 85. mov cx, bx 86. call far ptr divdw 87. 88. push si 89. mov bx, buf 90. mov ds, bx 91. mov si, 0 92. call far ptr dtoc ;调用dtoc将ax和dx共同保存的数据转换为对应的字符串到buf中 93. pop si 94. 95. pop dx 96. push si 97. push dx 98. mov ax, buf 99. mov ds, ax 100. mov si, 0 101. add dl, 34 102. mov cl, 2 103. call far ptr show_str ;调用show_str显示在buf中的字符串 104. ;---------------显示员工平均工资-------------------------------------------- 105. pop dx 106. pop si 107. 108. inc si ;下一年数据 109. inc dh ;显示在下一行 110. 111. pop cx 112. loop main_s4 113. 114. mov ax, 4c00h 115. int 21h 116. 117. ;子函数定义区 118. 119. main ends 120. end start |