什么是函数调用约定

一言概之:函数调用约定就是调用函数时,如何传参入栈,调用后如何恢复栈的协定。我们实现的过程中使用的是cdecl协定,基本的原则是:

  1. 参数按从右到左的顺序入栈
  2. 由调用者清理栈空间

其中第二点带来了一个很明显的好处,那就是可以传入不定长的参数。

实现打印函数

复习下汇编的知识:

in [num], [port] ; 从port端口读出num,in的主体是内存
out [port], [num]   ; 向port端口写入num,out的主体是内存

实现字符打印

然后开始写打印函数:

TI_GDT equ 0
RPL0 equ 0
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0[bits 32]
section .text
;------------ put char ----------------
global put_char
put_char:pushad ; 备份所有寄存器mov ax, SELECTOR_VIDEOmov gs, ax
; 获取光标位置mov dx, 0x03d4  ; 索引号端口mov al, 0x0e ; 高8位寄存器out dx, almov dx, 0x03d5    ; 数据端口in al, dx ; 读取高八位到almov ah, al; 低八位同理mov dx, 0x03d4mov al, 0x0fout dx, almov dx, 0x03d5in al, dxmov bx, ax    ; 将光标存入bxmov ecx, [esp + 36]   ; 获取待打印的字符,其中0-4位是返回地址,4-36是寄存器cmp cl, 0xdjz .is_carriage_returncmp cl, 0xajz .is_line_feed   ; 二者都看成换行+回车cmp cl, 0x8    ; 退格jz .is_backspacejmp .put_other.is_backspace:    ; 退格并将退后的字符设置为空cmp bx, 0je .set_cursor  dec bxshl bx, 1 ; 每次读写光标必须乘2,因为每个位置用2字节表示,1字节代表ascii,1字节代表状态mov word [gs:bx], 0x0720shr bx, 1jmp .set_cursor.put_other:shl bx, 1mov [gs:bx], clinc bxmov byte [gs:bx], 0x07inc bxshr bx, 1cmp bx, 2000jl .set_cursor.is_line_feed:
.is_carriage_return:xor dx, dxmov ax, bxmov si, 80div sisub bx, dx.is_carriage_return_end:add bx, 80cmp bx, 2000
.is_line_feed_end:jl .set_cursor; 1. 将1-24行搬到0-23行
.roll_screen:cldmov ecx, 960    ; 960 = (2000 - 80) * 2 / 4mov esi, 0xc00b80a0mov edi, 0xc00b8000rep movsd
; 2. 将最后一行填充为空
mov ebx, 3840
mov ecx, 80.cls:mov word [gs:ebx], 0x0720add ebx, 2loop .clsmov bx, 1920    ; 将光标重置为最后一行行首; 将当前光标写回显存
.set_cursor:; 高八位mov dx, 0x03d4mov al, 0x0eout dx, almov dx, 0x03d5mov al, bhout dx, al; 低八位mov dx, 0x03d4mov al, 0x0fout dx, almov dx, 0x03d5mov al, blout dx, al
.put_char_done:popadret

文章看似字数不多,但我在这里卡了快一天了,终于通过一次次调试得出了理想的结果:

哈哈不容易啊不容易……

打印字符串

接下来写打印字符串,尝试自己手撸:

;------------ put str -----------------
global put_str
put_str:push ebxpush ecxmov ebx, [esp + 12]
.print_loop:mov ecx, [ebx]cmp cl, 0je .print_endpush ecxcall put_charpop ecxadd ebx, 1jmp .print_loop.print_end:pop ecxpop ebxret

检查一下,成功!

打印整数

这个是我们大二上的考试题哈,继续手撸(代码后面有汇总):

到此为止,所有三个打印函数已经全部实现!
给出完美版本的print.S:

TI_GDT equ 0
RPL0 equ 0
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0section .data
buffer dq 0[bits 32]
section .text
;------------ put str -----------------
global put_str
put_str:push ebxpush ecxmov ebx, [esp + 12]
.print_loop:mov ecx, [ebx]cmp cl, 0je .print_endpush ecxcall put_charpop ecxadd ebx, 1jmp .print_loop.print_end:pop ecxpop ebxret;------------ put char ----------------
global put_char
put_char:pushad ; 备份所有寄存器mov ax, SELECTOR_VIDEOmov gs, ax
; 获取光标位置mov dx, 0x03d4  ; 索引号端口mov al, 0x0e ; 高8位寄存器out dx, almov dx, 0x03d5    ; 数据端口in al, dx ; 读取高八位到almov ah, al; 低八位同理mov dx, 0x03d4mov al, 0x0fout dx, almov dx, 0x03d5in al, dxmov bx, ax    ; 将光标存入bxmov ecx, [esp + 36]   ; 获取待打印的字符,其中0-4位是返回地址,4-36是寄存器cmp cl, 0xdjz .is_carriage_returncmp cl, 0xajz .is_line_feed   ; 二者都看成换行+回车cmp cl, 0x8    ; 退格jz .is_backspacejmp .put_other.is_backspace:    ; 退格并将退后的字符设置为空cmp bx, 0je .set_cursor  dec bxshl bx, 1 ; 每次读写光标必须乘2,因为每个位置用2字节表示,1字节代表ascii,1字节代表状态mov word [gs:bx], 0x0720shr bx, 1jmp .set_cursor.put_other:shl bx, 1mov [gs:bx], clinc bxmov byte [gs:bx], 0x07inc bxshr bx, 1cmp bx, 2000jl .set_cursor.is_line_feed:
.is_carriage_return:xor dx, dxmov ax, bxmov si, 80div sisub bx, dx.is_carriage_return_end:add bx, 80cmp bx, 2000
.is_line_feed_end:jl .set_cursor; 1. 将1-24行搬到0-23行
.roll_screen:cldmov ecx, 960    ; 960 = (2000 - 80) * 2 / 4mov esi, 0xc00b80a0mov edi, 0xc00b8000rep movsd
; 2. 将最后一行填充为空
mov ebx, 3840
mov ecx, 80.cls:mov word [gs:ebx], 0x0720add ebx, 2loop .clsmov bx, 1920    ; 将光标重置为最后一行行首; 将当前光标写回显存
.set_cursor:; 高八位mov dx, 0x03d4mov al, 0x0eout dx, almov dx, 0x03d5mov al, bhout dx, al; 低八位mov dx, 0x03d4mov al, 0x0fout dx, almov dx, 0x03d5mov al, blout dx, al
.put_char_done:popadret;------------put_int------------
global put_int
put_int:pushadmov ebp, espadd ebp, 36mov eax, [ebp] ; point to the nummov edi, bufferadd edi, 7 ; move to the last locationmov ecx, 8   ; num of loop
deal_with_num:mov ebx, eaxand ebx, 0x0000000fshr eax, 4cmp bl, 0x9  ; judge numjg A2F   ; if greater than 9, go to A2Fadd bl, '0' ; else add '0'jmp store   ; cross A2F
A2F:sub bl, 10add bl, 'A'
store:mov byte [edi], bl    ; store itdec edi   ; point to the next locationloop deal_with_nummov ecx, 8xor edx, edxmov edi, buffer
check_if_all_0:mov bl, [edi + edx]cmp bl, '0'jne do_print_numinc edx ; count num of '0'loop check_if_all_0cmp edx, 8jne do_print_num
print_all_0:mov ecx, '0'push ecxcall put_charpop ecxjmp put_int_end
do_print_num:cmp edx, 8je put_int_endmov ecx, [edi + edx]push ecxcall put_charpop ecxinc edxjmp do_print_num
put_int_end:popadret

什么是内联汇编

一句话,在C中嵌入汇编,提升代码的“战斗力”。
总共花了30分钟左右把最后一章过了一遍,第6章正式结束。

结语

原来第五章埋的雷在这一章爆了,导致调试调了快一整天,最后看到bochs中如我心意地打出字符和数字时,真的很开心,原来最简单的一句printf(不完全的),如果全部手写,都需要我花一整天啊。
总的来说,感觉自己对于汇编的知识提升又高了一些,但是还有很长的路要走啊,预计接下来两天把第六章——中断给吃完吧。

操作系统真象还原——第6章 完善内核相关推荐

  1. 操作系统真相还原——第6章 完善内核

    函数底层调用约定 cdecl:函数参数由栈进行传递,从右向左顺序入栈,栈空间由调用者清理,函数的返回值存储在EAX 寄存器. syscall:参数从右到左入校.参数列袤的大小被放置在AL 寄存器中 o ...

  2. 《操作系统真象还原》第九章

    <操作系统真象还原>第九章 本篇对应书籍第九章的内容 本篇内容介绍了线程是什么,多线程的原理,多线程用到的核心数据结构,以及通过代码实现了内核多线程的调度 线程是什么? 执行流 过去,计算 ...

  3. 《操作系统真象还原》第二章

    <操作系统真象还原>第二章 编写MBR主引导记录 载入内存 过程: (1)程序被加载器(软件或硬件)加载到内存某个区域. (2)CPU的cs:ip寄存器被指向这个程序的起始地址. 从按下主 ...

  4. 《操作系统真象还原》1-3章 学习记录

    文章目录 前言 一.开始实验前的一些基本问题解答? section的含义? vstart的含义? $ 和 $$区别? 实模式的特点? CPU如何和硬盘进行交互? CPU和IO设备交互方式? 程序载入内 ...

  5. 操作系统真象还原——第5章 从保护模式到内核

    目录 前言 5.1 获取物理内存容量 5.1.1 学习Linux获取内存的方法 5.1.2 实战内存容量检测 5.2 内存分页 为什么需要分页? 一级页表 二级页表 如何设计一个页表 分页机制的代码实 ...

  6. 操作系统真象还原第3章:完善MBR

    前言 这次我出现了一些BUG,导致我忙活了一阵子,还好的是解决了 老规矩还是把整个流程过一遍,当MBR忙活完后,就需要把自己手中的棒交给loader了,MBR的作用就是从硬盘中的内核加载器移动到内存中 ...

  7. 《操作系统真象还原》第九章 ---- 终进入线程动斧开刀 豁然开朗拨云见日 还需解决同步机制才能长舒气

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 进程 线程的自我小理解 线程 进程的状态 内核级线程 & 用户级线程 初步实现内核级线程 浪费两三个小时调试的辛酸史 编写thread ...

  8. 操作系统真象还原第2章:编写MBR主引导记录

    前言 这章的内容挺少的,也很简单,如果环境没配置错的话是没啥问题的.但是这章也很精彩,把引导的过程给说了出来,我也是看了几遍把这个过程给大致看懂了. 首先计算机一开机这个时cpu会自动把cs:ip指针 ...

  9. 《操作系统真象还原》第二章 ---- 编写MBR主引导记录 初尝编写的快乐 雏形已显!

    文章目录 专栏博客链接 前引 相关术语 理清操作系统启动程序运行流程(部分) 编写MBR引导内容 编译并检验mbr.bin Linux dd 磁盘操作指令与参数 模拟操作试一试 结束语 专栏博客链接 ...

最新文章

  1. byte数组转字符串_leetcode刷题844比较含退格的字符串(带代码解析,带知识点回顾)...
  2. 在大促中什么影响了数据库性能
  3. RESTful 架构详解
  4. 写第一个spark程序(wordcount)
  5. word之八大文本替换技巧
  6. 【AD用户设置系列一】让IT省心省力的漫游配置文件
  7. java计算加速减速_java – 使用JOCL / OPENCL计算强度的加速总和
  8. 查看电脑boot mode方式
  9. 前端-requests-flask对应关系 HTTPTokenAuth
  10. Flutter基础布局组件及实现
  11. 用户模块 之 完成查询所有用户
  12. Atitit. IE8.0 显示本地图片预览解决方案 img.src=本地图片路径无效的解决方案
  13. Android ASM字节码插桩
  14. 显著性检验:P值和置信度
  15. Why “the CUDA Samples are not meant for performance measurements”?
  16. tp6 的unique验证
  17. ONGene:基于文献检索的肿瘤基因数据库
  18. linux查看服务器用户名密码,怎么查看linux服务器的配置?
  19. Unsupervised Degradation Representation Learning for Blind Super-Resolution(基于无监督退化表示学习的盲超分辨率处理)
  20. 揭秘你所看不见的技术原理 - 游戏世界服

热门文章

  1. 云米涉嫌专利侵权是小米生态链的整体隐患
  2. [課程筆記] 強化學習(李弘毅) L1. Policy Gradient
  3. 基于qt和opencv3人脸检测
  4. 乐鑫M5GO自制睡眠小助手!新手。。。轻打脸
  5. iOS iphone5屏幕适配 autosizing
  6. C++ operator 重载
  7. 蓝桥训练赛 (14点--18点 19点--21点20)
  8. java 数组内元素相乘,java数组元素如何进行加减乘除,请大侠咪赐教!!
  9. 饥荒机器人怎么解锁_饥荒机器人怎么玩 饥荒机器人详细玩法
  10. python线程间通信方法之Event