nasm : 屏幕打印语句的实现
2015-09-23 16:29
337 查看
就像应用层编程一样,如果不借助第三方工具和调试器,要知道程序哪里出了问题,需要记录日志.
bochs和真机还是有点区别的, 前几天搞出了打印语句, 拖到今天,实验环境搞定后,才在真机上验证通过.
现在实验用的这1个扇区,是MBR加载到内存的, 验证函数级别的问题,应该是可以了.
现在一个扇区被写的差不多满了,下一步要考虑,用BOOT代码加载更多的扇区,然后调到加载后新的扇区, 执行更多的代码才有空间保障.
打印语句如下:
* 清屏
* 设置光标位置
* 打印带背景的文本, 可以是重复的字符,也可以是一段固定的话
* 打印现有显示属性(当前前景,背景)下的文本, 可以是重复的字符,也可以是一段固定的话
* 需要完善的地方 :考虑打印寄存器,内存,这样活动的内容.
效果图:(和真机运行效果相同,已经验证过了)
nasm代码实现
bochs和真机还是有点区别的, 前几天搞出了打印语句, 拖到今天,实验环境搞定后,才在真机上验证通过.
现在实验用的这1个扇区,是MBR加载到内存的, 验证函数级别的问题,应该是可以了.
现在一个扇区被写的差不多满了,下一步要考虑,用BOOT代码加载更多的扇区,然后调到加载后新的扇区, 执行更多的代码才有空间保障.
打印语句如下:
* 清屏
* 设置光标位置
* 打印带背景的文本, 可以是重复的字符,也可以是一段固定的话
* 打印现有显示属性(当前前景,背景)下的文本, 可以是重复的字符,也可以是一段固定的话
* 需要完善的地方 :考虑打印寄存器,内存,这样活动的内容.
效果图:(和真机运行效果相同,已经验证过了)
nasm代码实现
; @file boot_print_debug.asm ; @brief 启动后显示一些信息 ; @note 编译命令行 ; cd D:\prj\nasm_prj\boot\boot_dispmsg ; d: ; C:\nasm\nasm.exe boot_print_debug.asm -d UBOOT -o boot_print_debug.bin -l boot_print_debug.list ; @note 将boot_print_debug.bin写U盘0扇区 ; 实验点 ; * 用栈来传递出参 ; * 现在有了屏幕日志, 不用完全依赖bochs调试器来调试程序. ; 屏幕日志还需要完善, e.g. 打印一个寄存器或内存的值 ; 颜色 %define COLOR_BG_RED_FG_WHITE 47h ; ///< 红底白字 ; 函数内临时变量 - 以函数入口处的bp为基准 ;Stack address size 2 ; | STACK 0xffc6 [0x7ce7] ///< 函数内临时变量2 ; | STACK 0xffc8 [0x0000] ///< 函数内临时变量1 ; | STACK 0xffca [0xffcc] ///< 入口处的sp值 ; | STACK 0xffcc [0x0000] ///< 入口处的bp值 ; | STACK 0xffce [0x7c1e] ///< 函数返回地址, 入口处的bp值已经指到了这里 %define FUNCTION_TEMP_VAR_1 [bp - 6] %define FUNCTION_TEMP_VAR_2 [bp - 8] %define FUNCTION_TEMP_VAR_3 [bp - 12] %define FUNCTION_TEMP_VAR_4 [bp - 14] %define FUNCTION_TEMP_VAR_5 [bp - 16] %define FUNCTION_TEMP_VAR_6 [bp - 18] %define FUNCTION_TEMP_VAR_7 [bp - 20] %define FUNCTION_TEMP_VAR_8 [bp - 22] %define FUNCTION_TEMP_VAR_9 [bp - 24] ; 栈入参 - 以函数入口处的bp为基准 %define STACK_IN_PARAM_1 [bp + 2] %define STACK_IN_PARAM_2 [bp + 4] %define STACK_IN_PARAM_3 [bp + 6] %define STACK_IN_PARAM_4 [bp + 8] %define STACK_IN_PARAM_5 [bp + 10] %define STACK_IN_PARAM_6 [bp + 12] %define STACK_IN_PARAM_7 [bp + 14] %define STACK_IN_PARAM_8 [bp + 16] %define STACK_IN_PARAM_9 [bp + 18] ; 栈出参 - 以函数入口处的sp为基准, 函数出口处的sp必须和入口处相同 ; 必须先执行 mov si, sp 因为没有 mov ax, [sp + 4] 这样的指令 ; 用完 STACK_OUT_PARAM_X 之后, 执行 pop bp %define STACK_OUT_PARAM_1 [si + 0] %define STACK_OUT_PARAM_2 [si + 2] %define STACK_OUT_PARAM_3 [si + 4] %define STACK_OUT_PARAM_4 [si + 6] %define STACK_OUT_PARAM_5 [si + 8] %define STACK_OUT_PARAM_6 [si + 10] %define STACK_OUT_PARAM_7 [si + 12] %define STACK_OUT_PARAM_8 [si + 14] %define STACK_OUT_PARAM_9 [si + 16] %define BOOT_CODE_ENTRY_POINT 07c00h ; ///< boot 代码被BIOS加载后的位置 ; /// 这句没用的, 自己用WinHex烧到想要的扇区 org BOOT_CODE_ENTRY_POINT ; 程序加载到0x7c00 ; -------------------------------------------------------------------------------- ; /// @todo ls for debug ; -------------------------------------------------------------------------------- ; jmp $ ; 死循环 ; /// 在真机上, 读取和设置段,均会引起异常. 跑飞了 mov ax, cs mov ds, ax mov es, ax ; /// ss 和 sp 依赖与bios启动时设置的值,还是不改了 ; mov ss, ax ; mov sp, BOOT_CODE_ENTRY_POINT ; /// 清屏 call clear_screen ; -------------------------------------------------------------------------------- ; 显示一行分隔线, 开始位置(20,1), 长度78 ; 在21,22,23,24行显示调试信息 ; -------------------------------------------------------------------------------- ; /// 设置光标(20,1) push 20 ; ///< y push 1 ; ///< x call set_cursor ; ///< set_cursor(x, y) add sp, 4 ; ///< 堆栈平衡 ; /// 显示78个- push COLOR_BG_RED_FG_WHITE ; ///< 颜色 : 红底白字 push 78 push '=' call disp_n_char_by_attributes ; ///< disp_repeat_char(char cDispContent, int iDispCount, int iColor) add sp, 6 ; ///< 堆栈平衡 ; -------------------------------------------------------------------------------- ; 在(21,1)显示信息 str_boot_msg ; -------------------------------------------------------------------------------- push 21 push 1 call set_cursor add sp, 4 ; ///< 堆栈平衡 mov si, str_boot_msg mov cx, len_str_boot_msg call disp_message_by_default_attributes ; /// disp_message_by_default_attributes 是靠寄存器传参, 不需要堆栈平衡 ; -------------------------------------------------------------------------------- ; 在(22,1)写一行分隔符, 使用现有属性, 长度40 ; -------------------------------------------------------------------------------- push 22 push 1 call set_cursor add sp, 4 ; ///< 堆栈平衡 push 40 push '-' call disp_n_char_by_default_attributes add sp, 4 ; ///< 堆栈平衡 ; -------------------------------------------------------------------------------- ; 在(23,1)提示信息 str_debug_tip ; -------------------------------------------------------------------------------- push 23 push 1 call set_cursor add sp, 4 ; ///< 堆栈平衡 mov si, str_debug_tip mov cx, len_str_debug_tip call disp_message_by_default_attributes ; -------------------------------------------------------------------------------- ; 在(24,1)提示其他信息 ; -------------------------------------------------------------------------------- ; /// 空间不够了, 先屏蔽掉这一段 ;main_read_kb: ; push 24 ; push 1 ; call set_cursor ; add sp, 4 ; ///< 堆栈平衡 ; call getchar ; mov si, str_1_dot ; mov cx, len_str_1_dot ; call disp_message_by_default_attributes ; jmp main_read_kb ; -------------------------------------------------------------------------------- ; /// @todo ls for debug ; -------------------------------------------------------------------------------- jmp $ ; 死循环 disp_str: mov bp, sp push bp mov bh, 0 ; no.0 page mov bl, 47h ; char color mov cx, 6 ; char count is 1 mov al, '6' ; char content is '6' mov ah, 9 ; bios 10# function = write char to UI int 10h pop bp mov sp, bp ret ; /// @fn char getchar(), al是键值 getchar: ; /// 保护现场 bp和sp push bp push sp ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax getchar_clear_buffer: mov ah, 1 int 16h jz getchar_clear_read ; ///< 键盘缓冲区都空了, 可以转"读键盘输入" mov ah, 0 int 16h jmp getchar_clear_buffer ; ///< 继续清键盘缓冲区 getchar_clear_read: mov ah, 1 int 16h jz getchar_clear_read ; ///< 如果没有键盘输入,继续死等键盘输入 mov ah, 0 ; ///< al是键盘输入 int 16h ; /// 恢复现场 bp和sp pop sp pop bp ret ; /// @fn disp_n_char_by_default_attributes(char* pcMsg, int iLenMsg) ; si = pcMsg ; cx = iLenMsg disp_message_by_default_attributes: ; /// 保护现场 bp和sp push bp push sp ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax push cx ; ///< 重复执行次数 push bx mov bx, 0 disp_message_by_default_attributes_begin: cmp cx, 0 jbe disp_message_by_default_attributes_end ; ///< 执行次数 <= 0, break dec cx mov ax, [si + bx] ; ///< 需要显示的字符 inc bx push ax ; /// disp_one_char_by_default_attributes 必须保护 bx,cx,si call disp_one_char_by_default_attributes add sp, 2 ; ///< 堆栈平衡 jmp disp_message_by_default_attributes_begin disp_message_by_default_attributes_end: pop bx pop cx ; /// 恢复现场 bp和sp pop sp pop bp ret ; /// @fn disp_n_char_by_default_attributes(char cDispContent, int iDispCnt) disp_n_char_by_default_attributes: ; /// 保护现场 bp和sp push bp push sp ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax mov cx, STACK_IN_PARAM_2 ; ///< 重复执行次数 disp_n_char_by_default_attributes_begin: cmp cx, 0 jbe disp_n_char_by_default_attributes_end ; ///< 执行次数 <= 0, break dec cx mov ax, STACK_IN_PARAM_1 ; ///< 需要显示的字符 push ax call disp_one_char_by_default_attributes add sp, 2 ; ///< 堆栈平衡 jmp disp_n_char_by_default_attributes_begin disp_n_char_by_default_attributes_end: ; /// 恢复现场 bp和sp pop sp pop bp ret ; /// @fn disp_one_char_by_default_attributes(char cDispContent) disp_one_char_by_default_attributes: ; /// 保护现场 bp和sp push bp push sp ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax push bx push cx push si xor bh, bh mov ax, STACK_IN_PARAM_1 mov ah, 0x0e int 0x10 pop si pop cx pop bx ; /// 恢复现场 bp和sp pop sp pop bp ret ; /// @fn disp_repeat_char(char cDispContent, int iDispCount, int iColor) ; /// @brief 先写显示内容,再移动光标 disp_n_char_by_attributes: ; /// 保护现场 bp和sp push bp push sp ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax ; /// 开辟一个临时变量区 ; /// STACK_IN_PARAM_1 保存光标位置 ; /// STACK_IN_PARAM_2 保存 iDispCount sub sp, 4 ; /// 初始化临时变量, 使调试时,容易分辨 mov ax, 0xffff mov FUNCTION_TEMP_VAR_1, ax ; ///< 临时变量1 mov ax, 0xffff mov FUNCTION_TEMP_VAR_2, ax ; ///< 临时变量2 mov bh, 0 ; no.0 page mov ax, STACK_IN_PARAM_3 ; ///< int iColor mov bl, al ; char color ; /// 做个标记, 有利于在bochs下下断点到这里 add ax, 0x112 mov ax, STACK_IN_PARAM_2 ; ///< int iDispCount mov FUNCTION_TEMP_VAR_2, ax ; ///< 保存字符个数, 用于设置新的光标位置 mov cx, ax ; char count mov ax, STACK_IN_PARAM_1 ; ///< char cDispContent mov ah, 9 ; functin sn : 按照设定的属性显示重复的字符 int 10h ; /// 移动光标到末尾 ; /// 先得到光标位置(x,y) ; /// 得到光标 push 0 call get_cursor mov si, sp add sp, 2 ; ///< 堆栈平衡; mov ax, STACK_OUT_PARAM_1 ; ///< 将得到的光标位置给ax 出参 ah = y, al = x mov FUNCTION_TEMP_VAR_1, ax ; ///< 保存当前光标位置,用于设置新的光标位置 ; /// 再设置光标位置为(x+ STACK_PARAM2, y) xor ax, ax mov cx, FUNCTION_TEMP_VAR_1 mov al, ch; ///< y push ax ; ///< y xor ax, ax mov cx, FUNCTION_TEMP_VAR_1 mov al, cl add ax, FUNCTION_TEMP_VAR_2 push ax ; ///< x call set_cursor ; ///< set_cursor(x, y) add sp, 4 ; ///< 堆栈平衡 add sp, 4 ; ///< 临时变量的堆栈平衡 ; /// 恢复现场 bp和sp pop sp pop bp ret ; /// 清屏 clear_screen: ; /// 保护现场 bp和sp mov ax, bp push ax mov ax, sp push ax ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax ; (7)、功能 06H 和 07H ; 功能描述:初始化屏幕或滚屏 ; 入口参数:AH=06H——向上滚屏,07H——向下滚屏 ; AL=滚动行数(0——清窗口) ; BH=空白区域的缺省属性 ; (CH、CL)=窗口的左上角位置(Y 坐标,X 坐标) ; (DH、DL)=窗口的右下角位置(Y 坐标,X 坐标) ; 出口参数:无 mov ah, 6h mov al, 0 mov bh, 17h ; ///< 蓝底白字,光标闪烁 mov cl, 0 mov ch, 0 mov dl, 79 mov dh, 24 int 10h ; -------------------------------------------------------------------------------- ; /// @todo ls for debug ; -------------------------------------------------------------------------------- ; jmp $ ; 死循环 ; /// 恢复现场 bp和sp pop sp pop bp ret ; /// 设置光标位置 ; /// set_cursor(x, y) set_cursor: ; /// 保护现场 bp和sp push bp push sp ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax ; push CURSOR_Y_0 ; ///< STACK_PARAM2 ; push CURSOR_X_0 ; ///< STACK_PARAM1 mov ax, 0 mov bh, al ; ///< display page number mov ax, STACK_IN_PARAM_1 mov dl, al ; ///< cursor column, CURSOR_X_N mov ax, STACK_IN_PARAM_2 mov dh, al ; ///< cursor row, CURSOR_Y_N mov ah, 2 mov al, 0 int 10h ; /// 恢复现场 bp和sp pop sp pop bp ret ; /// 得到光标位置 ; /// get_cursor(x, y) ; 调用示例 ; push 0 ///< y, STACK_OUT_PARAM_2 ; push 0 ///< x, STACK_OUT_PARAM_1 ; call get_cursor get_cursor: ; /// 保护现场 bp和sp push bp push sp ; /// 使bp回到函数入口处的值 mov ax, sp add ax, 4 mov bp, ax ; push CURSOR_Y_0 ; ///< STACK_PARAM2 ; push CURSOR_X_0 ; ///< STACK_PARAM1 ; (4)、功能 03H ; 功能描述:在文本坐标下,读取光标各种信息 ; 入口参数:AH=03H ; BH=显示页码 ; 出口参数:CH=光标的起始行 ; CL=光标的终止行 ; DH=行(Y 坐标) ; DL=列(X 坐标) xor ax, ax mov ah, 3h mov bh, 0 int 10h mov ax, dx mov STACK_IN_PARAM_1, ax ; /// 恢复现场 bp和sp pop sp pop bp ret str_boot_msg: db "the boot code was witten by NASM assembler", 0 len_str_boot_msg equ ($ - str_boot_msg) str_debug_tip: db "press any key to go next watch point", 0 len_str_debug_tip equ ($ - str_debug_tip) ; /// 空间不够了, 还有11个字节满512, 等下次再玩 ; str_1_dot: db ".", 0 ; len_str_1_dot equ ($ - str_1_dot) ; str_build_time: db "2015_0923_1532" times 510-($-$$) db 0 ; 用0填充剩余空间,使该段二进制代码正好为512字节 dw 0aa55h ; 结束标记
相关文章推荐
- Java EE : 一、图解Http协议
- systemd系统服务管理详解
- mesos和Elasticsearch设计(一)
- 微信和百度地图都不支持iOS6了
- 【Qt】窗体间传递数据(跨控件跨类),三种情况与处理方法
- Linux-CentOS 安装oh-my-zsh
- 常见函数的导数
- cocos2d-x3.6获取csb文件里的按钮控件
- org.apache.jasper.JasperException: /WEB-INF/jsp/add.jsp(40,24) quote symbol expected
- Spring中的DI(依赖注入)--给对象的属性赋值
- javascript省市联动
- PAT(甲级)1012
- 计算机辅助几何设计(CAGD)与非均匀有理B样条的几个思考题
- swift 学习 3
- css的hack问题-测试记录
- (date.getYear()+" "+date.getMonth() + " " + date.getDate()+" "+date.getDay()
- Linux启动流程
- Highcharts标题Title(二)
- layer-list实现只有左、右和下边框的圆角矩形
- bzoj3993[SDOI2015]星际战争