一、搭建框架
vim Makefile
build:
nasm boot.asm
dd if=/dev/zero of=a.img bs=512 count=2880 #创建一个1.44M的空软盘
dd if=boot of=a.img bs=512 count=1 conv=notrunc #把引导程序写到0面0磁道1扇区
bochs:
bochs -q -f bochsrc #用bochsrc配置文件启动模拟器
clean:
rm boot a.img
根据Makefile文件,我们需要bochsrc文件和boot.asm文件。
二、bochsrc文件
运行bochs程序,选择4.Save options to...,输入文件名bochsrc。然后vim bochsrc
display_library: x, options="gui_debug" ;启用图形调试界面
boot: floppy
floppya: 1_44M, image="a.img", status=inserted ;加载软盘启动映像文件a.img
magic_break: enabled=1 ;启用xchg bx, bx魔术断点
三、编写boot.asm
[org 0x7c00]
[section]
[bits 16]
global _start ;相当与c中的全局变量:也叫导出符号--相当于一个内存地址让其他程序能看到
_start:
mov ax, 0
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov sp, 0x1000
mov ax, 3 ; 清屏的功能号
int 0x10 ; 这两句的作用是调用0x10号中断清屏
mov si, msg ; msg所在地址赋值给si,这个位置是要显示的信息
call print
jmp $ ; $ 指的是当前指令所在的地址,此句的含义就是在此死循环
print:
mov ah, 0x0e ;逐字节打印的功能号
mov bh, 0
mov bl, 1
.loop: ;nasm中.loop之类的可以看成是程序里面的循环,实质也是符号地址。
mov al, [si]
cmp al, 0
jz .done
int 0x10
inc si
jmp .loop
.done:
ret
; 还有一种是往显存映射地址0xb8000处直接写内容
print1:
mov gs, 0xb800
xor di, di
mov ah, 0x73 ;高位表示颜色
.loop:
mov al, [si] ;低位表示字符
cmp al, 0
jz .done
mov word[gs:di], ax ;一个显示位置用16位表示,高位字节表示颜色,低位字节表示字符
inc si
inc di
inc di
jmp .loop
.done:
ret
msg:
db "Hello world!", 10, 13, 0 ;10 13分别是回车(LF)和换行(CR),0表示字符串结束
times 510-($-$$) db 0 ;$$ 表示本section开始的位置,此句含义就是除程序外的内存地址直到510
;字节处,内容填充为0
db 0x55, 0xaa ;511字节填充为0x55,512字节处填充为0xaa,这两个字节是表示这个扇区是
;启动扇区的规定。
四、玩懂编程主要玩转内存
1、内存是顺序连接的一个一个格子(单元),最小的格子是bit,可以按需要组装成大格子(byte、word、dword等)。每个8个比特组成一个byte,发一个门牌(地址:地址总线),要往格子里面放东西就首先要知道他家门牌。C语言可以用指针(指哪打哪)找,汇编用标号。
2、存数据的时候是从内存的低地址向高地址存。用栈这种数据结构的时候由于栈是从高地址向低地址增长,所以算不好老是容易迷。16位系统为例,栈帧是16位(2个字节两个地址),所以push的时候sp要先减2,在往里面放值。
3、push、pop原则是压谁还给谁。如果还错了,就会出问题,所以c编译成汇编会有平栈一说。因为有时压入栈的是1234这样的参数,没有地方还。