汇编语言程序设计--03--16位汇编实模式环境搭建
到了这个点了,看来很有必要搞个中文写作ai, 不然老是修修改改的,这也不是一个事。
汇编从实模式到保护模式,linux0.11系统也是如此,而它的实现语言从as86实模式汇编到gnu汇编再到c语言,其中c跟gnu汇编是其核心实现语言。那么为了更好的理解linux0.11或者说手动去改写一些东西,那么as86是必须要深入了解的。
可as86这种实模式下的汇编,其编译运行环境也是非常有讲究的,它并不像学校里边那种在dos模拟器运行的masm汇编,下载个模拟器就可以直接上手了。
当然模拟器这类思路是可以的,不过16位实模式汇编就需要手动去做。个人实力不够,搭建不了类似dos模拟器。这里的思路,我借用linux0.11源码tools/build.c文件,接着借助makefile功能,在makefile文件里边实现build.c 将as86 ld86编译链接的执行代码写入一个名为RunImage的文件,最后由bochs模拟器去运行makefile模块生成的代码。这样就实现了16位汇编编译运行。同样的思路,我也可以采用bochs的gdb来调试16位汇编代码。
目前我也不是学的很深,暂时就编译运行环境就可以了。我就那几句代码,要啥自行车。
我去看了linux5.15启动代码,额… as86似乎没必要学,nasm也没必要,gnu汇编就可以解决,可路得一步步走,先把linux0.11搞明白,再去搞那些。人生的路很长,没必要过度努力混个重症住院。
复制相关文件
bochs是虚拟机,一般虚拟机有磁盘。一台裸机的首次启动逻辑,从启动盘再到磁盘。这里的build.c就是将生成的汇编编译链接后的执行文件,最后以某种格式写入到RunImage。
下边是bochs运行配置文件,软盘为启动盘,其目录为$OSLAB_PATH/src/RunImage,那么build.c就是为了生成
这么一个文件。
romimage: file=$OSLAB_PATH/bochs/BIOS-bochs-latest
megs: 16
vgaromimage: file=$OSLAB_PATH/bochs/vgabios.bin
floppya: 1_44="$OSLAB_PATH/src/RunImage", status=inserted
ata0-master: type=disk, path="$OSLAB_PATH/hdc-0.11.img", mode=flat, cylinders=204, heads=16, spt=38
boot: a
log: $OSLAB_PATH/bochsout.txt
#parport1: enable=0
#vga_update_interval: 300000
#keyboard_serial_delay: 200
#keyboard_paste_delay: 100000
#floppy_command_delay: 50000
cpu: count=1, ips=4000000
mouse: enabled=0
private_colormap: enabled=0
fullscreen: enabled=0
screenmode: name="sample"
#i440fxsupport: enabled=0
#display_library: sdl
编写运行相关代码
demo.S
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
BOOTSEG = 0x07C0
entry _start
_start:
mov ax,#BOOTSEG !将ds 段寄存器置为0×7C0
mov ds,ax
mov es,ax
mov ah,#0x03 ! read cursor pos
xor bh,bh ! 首先读光标位置。返回光标位置值在dx中
int 0x10 ! dh - 行(0--24); dl - 列(0--79),移动光标
mov cx,#36 ! 共显示36个字符
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg ! es:bp 指向要显示的字符串
mov ax,#0x1301 ! write string, move cursor
int 0x10 ! 写字符串并移动光标到串结尾处
loop0: ! 屏幕暂停到这里
jmp loop0
msg:
.ascii "Loading system..."
.byte 13,10 ! 换行
.org 510
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss:
makefile编译文件
# 定义RAM盘,若定义了则需要指定参数DRAMDISK块大小
RAMDISK = #-DRAMDISK=512
# 8086汇编语言的汇编器与链接器
# -0表示生成8086目标程序
AS86 =as86 -0 -a
LD86 =ld86 -0
# GNU编译器与链接器
AS =as
LD =ld
# GNU链接器LD的参数
# -m elf_i386表示以elf格式输出i386的32位代码
# -Ttext 0表示代码在0处加载/对齐
# -e startup_32表示指定startup_32为入口点
LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32
# 指定gcc-3.4编译时使用-i386的指令集
CC =gcc-3.4 -march=i386 $(RAMDISK)
# -m32表示在64位编译器上生成32位的代码
# -g表示生成调试文件(用于GDB调试)
# -Wall表示打印警告信息
# -O2表示使用二级优化
# -fomit-frame-pointer表示对无需帧指针的函数不把帧指针保留在寄存器中
CFLAGS =-m32 -g -Wall -O2 -fomit-frame-pointer
# 指定头文件的搜索目录为当前目录下的include(即不使用系统/usr/include目录下的头文件)
CPP =cpp -nostdinc -Iinclude
# 指定在build程序创建内核映像文件(Image)时所使用的默认根文件系统所在的设备(未指定时,使用默认值/dev/hd6)
ROOT_DEV= #FLOPPY
# 定义3条了make的隐式规则
# 当源文件是.c文件,目标文件是.s文件,则使用第1条规则
# 当源文件是.s文件,目标文件是.o文件,则使用第2条规则
# 当源文件是.c文件,目标文件是.o文件,则使用第3条规则
.c.s:
$(CC) $(CFLAGS) \
-nostdinc -Iinclude -S -o $*.s $<
.s.o:
$(AS) -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-nostdinc -Iinclude -c -o $*.o $<
# all为当前MAkefile文件的最终目标,其依赖于Image(make基本知识)
all: RunImage
# 用于生成Image映像文件,其依赖于1个目标文件
RunImage: demo tools/build
tools/build demo $(ROOT_DEV) > RunImage
sync
# 编译链接build.c程序
tools/build: tools/build.c
gcc $(CFLAGS) -o tools/build tools/build.c
# 使用86的汇编器和链接器编译生成setup文件
demo: demo.S
$(AS86) -o demo.o demo.S
$(LD86) -s -o demo demo.o
# 用于清除make执行过程中生成的文件
clean:
rm -f RunImage demo demo.o tools/build
run脚本
#!/bin/sh
export OSLAB_PATH=$(dirname `which $0`)
bochs -q -f $OSLAB_PATH/bochs/bochsrc.bxrc
运行结果
利用codeRunner实现一键编译运行
code Runner 的josn配置setting.json( “code-runner.executorMapByFileExtension”)
".S": "cd /usr/linux-os/assembly/as86/src && make clean && cp $dir$fileName /usr/linux-os/assembly/as86/src/demo.S && make all && make cleanMiddle && cd /usr/linux-os/assembly/as86 && ./run",
上边不够好,借助code runner预定义变量,再改动下:
".S": "cd $workspaceRoot/assembly/as86/src && make clean && cp $fullFileName $workspaceRoot/assembly/as86/src/demo.S && make all && make cleanMiddle && cd $workspaceRoot/assembly/as86 && ./run",
code runner vscode图形界面配置:
需要勾选上。
Code-runner: Run In Terminal
√ Whether to run code in Integrated Terminal.
修改makefile
# 定义RAM盘,若定义了则需要指定参数DRAMDISK块大小
RAMDISK = #-DRAMDISK=512
# 8086汇编语言的汇编器与链接器
# -0表示生成8086目标程序
AS86 =as86 -0 -a
LD86 =ld86 -0
# GNU编译器与链接器
AS =as
LD =ld
# GNU链接器LD的参数
# -m elf_i386表示以elf格式输出i386的32位代码
# -Ttext 0表示代码在0处加载/对齐
# -e startup_32表示指定startup_32为入口点
LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32
# 指定gcc-3.4编译时使用-i386的指令集
CC =gcc-3.4 -march=i386 $(RAMDISK)
# -m32表示在64位编译器上生成32位的代码
# -g表示生成调试文件(用于GDB调试)
# -Wall表示打印警告信息
# -O2表示使用二级优化
# -fomit-frame-pointer表示对无需帧指针的函数不把帧指针保留在寄存器中
CFLAGS =-m32 -g -Wall -O2 -fomit-frame-pointer
# 指定头文件的搜索目录为当前目录下的include(即不使用系统/usr/include目录下的头文件)
CPP =cpp -nostdinc -Iinclude
# 指定在build程序创建内核映像文件(Image)时所使用的默认根文件系统所在的设备(未指定时,使用默认值/dev/hd6)
ROOT_DEV= #FLOPPY
# 定义3条了make的隐式规则
# 当源文件是.c文件,目标文件是.s文件,则使用第1条规则
# 当源文件是.s文件,目标文件是.o文件,则使用第2条规则
# 当源文件是.c文件,目标文件是.o文件,则使用第3条规则
.c.s:
$(CC) $(CFLAGS) \
-nostdinc -Iinclude -S -o $*.s $<
.s.o:
$(AS) -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-nostdinc -Iinclude -c -o $*.o $<
# all为当前MAkefile文件的最终目标,其依赖于Image(make基本知识)
all: RunImage
# 用于生成Image映像文件,其依赖于1个目标文件
RunImage: demo tools/build
tools/build demo $(ROOT_DEV) > RunImage
sync
# 编译链接build.c程序
tools/build: tools/build.c
gcc $(CFLAGS) -o tools/build tools/build.c
# 使用86的汇编器和链接器编译生成setup文件
demo: demo.S
$(AS86) -o demo.o demo.S
$(LD86) -s -o demo demo.o
# 用于清除make执行过程中生成的所有文件
clean:
rm -f RunImage demo demo.o tools/build
# 用于清除make执行过程中生成的中间文件
cleanMiddle:
rm -f demo.S demo demo.o tools/build
运行结果
附
vscode预定义全局变量
以官网文档为准 : https://code.visualstudio.com/docs/editor/variables-reference
${userHome} - 用户主文件夹的路径
${workspaceFolder} - 在 VS Code 中打开的文件夹的路径
${workspaceFolderBasename} - 在 VS Code 中打开的文件夹的名称,不带任何斜杠 (/)
${file} - 当前打开的文件
${fileWorkspaceFolder} - 当前打开的文件的工作区文件夹
${relativeFile} - 当前打开的文件,相对于workspaceFolder
${relativeFileDirname} - 当前打开的文件的目录名称相对于workspaceFolder
${fileBasename} - 当前打开的文件的基名称
${fileBasenameNoExtension} - 当前打开的文件的基本名称,没有文件扩展名
${fileExtname} - 当前打开的文件的扩展名
${fileDirname} - 当前打开的文件的文件夹路径
${fileDirnameBasename} - 当前打开的文件的文件夹名称
${cwd} - VS Code 启动时任务运行程序的当前工作目录
${lineNumber} - 活动文件中当前选定的行号
${selectedText} - 活动文件中当前选定的文本
${execPath} - 正在运行的 VS Code 可执行文件的路径
${defaultBuildTask} - 默认构建任务的名称
${pathSeparator} - 操作系统用来分隔文件路径中组件的字符
${/}- ${pathSeparator} 的简写
Code Runner预定义变量
$workspaceRoot vscode打开的当前文件夹路径
$dir 当前文件所在文件夹目录,后边带 / 字符
$dirWithoutTrailingSlash 当前文件所在的文件夹路径,后边不带 / 字符
$fullFileName 当前文件地址,等于 $dir$fileName
$fileName 当前文件名称
$fileNameWithoutExt 不带后缀的当前文件名称