使用工具:
1、DOSBox 0.74,用于编译程序,测试程序主要功能
2、Bochs 2.6.9,用于模拟加载软盘,使用软盘或者硬盘启动系统
3、Linux虚拟机,用于虚拟软化读写,实现和bochs中的dos虚拟机进行数据交互
演示结果:
程序1效果:
程序2效果:
程序4效果:
程序3效果:
操作流程:
1、DOSBox编译程序生成kcsj2.exe
2、可以使用Bochs中的虚拟盘创建工具bximage.exe创建虚拟软盘a.img
3、Bochs中设置dos系统虚拟软盘(freedos.img)为A软盘挂载,同时挂载B软盘(a.img),系统虚拟软盘A为引导盘
# freedos.bxrc
###############################################################
# Configuration file for Bochs
###############################################################
# how much memory the emulated machine will have
megs: 32
# filename of ROM images
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
# what disk images will be used
floppya: 1_44=freedos.img, status=inserted
floppyb: 1_44=a.img, status=inserted
# hard disk
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
# !! Remember to change these if the hd img is changed:
# 1. include/sys/config.h::MINOR_BOOT
# 2. boot/include/load.inc::ROOT_BASE
# 3. Makefile::HD
# 4. commands/Makefile::HD
# ata0-master: type=disk, path="c.img", mode=flat, cylinders=162, heads=16, spt=63
# choose the boot disk.
# boot: floppy
boot: a
# where do we send log messages?
# log: bochsout.txt
# disable the mouse
mouse: enabled=0
# enable key mapping, using US layout as default.
keyboard: keymap=$BXSHARE/keymaps/x11-pc-us.map
# set up IPS value and clock sync
# cpu: ips=1000000
4、在freedos中格式化B软盘
5、使用Linux将生成的kcsj2.exe拷贝到B软盘(a.img)
sudo mount -o loop a.img /mnt/floppy
sudo cp kcsj2.exe /mnt/floppy -v
sudo ls -l /mnt/floppy
sudo umount /mnt/floppy
6、在freedos系统中运行kcsj2.exe程序,程序会将引导程序写入到B软盘
7、修改freedos的启动文件freedos.bxrc,设置a.img为A软盘,freedos.img为B软盘,可启动后使用kcsj2.exe中的引导程序启动
; [xyz]*:$
assume cs:code
data segment
db 64 dup (0)
data ends
code segment
start:
;jmp setup
mov ax, data
mov ss, ax
mov sp, 64
mov ax, cs
mov es, ax
mov bx, offset setup
mov dx, 0
mov ax, offset setupend - offset setup
mov cx, 512
div cx
cmp dx, 0
je setpre
inc ax
setpre:
mov si, offset disknum
mov es:[si], ax
mov ah, 1
mov al, 1 ; 写b
mov dx, 0
mov cx, ax
lwdisk:
call rwdisk
inc dx
add bx, 512
loop lwdisk
mov ax, 4c00h
int 21h
org 7c00h
setup:
jmp near ptr setup_start
nop
; bochs虚拟机需要识别软盘的文件系统格式才能启动系统,所以将写入软盘
; 第一个扇区引导程序模拟为FAT12的文件系统格式
; FAT12 磁盘的头
; ----------------------------------------------------------------------
BS_OEMName DB 'HuxBoot!' ; OEM String, 必须 8 个字节
BPB_BytsPerSec DW 512 ; 每扇区字节数
BPB_SecPerClus DB 1 ; 每簇多少扇区
BPB_RsvdSecCnt DW 1 ; Boot 记录占用多少扇区
BPB_NumFATs DB 2 ; 共有多少 FAT 表
BPB_RootEntCnt DW 224 ; 根目录文件数最大值
BPB_TotSec16 DW 2880 ; 逻辑扇区总数
BPB_Media DB 0f0h ; 媒体描述符
BPB_FATSz16 DW 9 ; 每FAT扇区数
BPB_SecPerTrk DW 18 ; 每磁道扇区数
BPB_NumHeads DW 2 ; 磁头数(面数)
BPB_HiddSec DD 0 ; 隐藏扇区数
BPB_TotSec32 DD 0 ; 如果 wTotalSectorCount 是 0 由这个值记录扇区数
BS_DrvNum DB 0 ; 中断 13 的驱动器号
BS_Reserved1 DB 0 ; 未使用
BS_BootSig DB 29h ; 扩展引导标记 (29h)
BS_VolID DD 0 ; 卷序列号
BS_VolLab DB 'HuxBootSect'; 卷标, 必须 11 个字节
BS_FileSysType DB 'FAT12 ' ; 文件系统类型, 必须 8个字节
;------------------------------------------------------------------------
disknum dw 0
stacktop: dw 64 dup (0)
stackend:
setup_start:
mov ax, offset stacktop
mov ss, ax
mov sp, offset stackend - offset stacktop
mov ax, cs
mov ds, ax
cmp disknum, 1
jb initscreen
; 加载大于512的数据
mov ax, 7c0h
mov es, ax
mov bx, 512
mov ah, 0
mov al, 0 ; 读a
mov dx, 1
mov cx, disknum
sub cx, 1
loaddisk:
call rwdisk
inc dx
add bx, 512
loop loaddisk
initscreen:
call choice_option
mov ax, 4c00h
int 21h
; =================================================================
; 读写磁盘;
; Para: (ah)=寄存器传递功能号,0表示读,1表示写;
; (dx)=寄存器传递要读写的扇区的逻辑扇区号;
; (al)=驱动器号 0:软驱A,1:软驱B,80h:硬盘C,81h:硬盘D
; es:bx指向存储读出的数据或写入数据的内存区;
rwdisk:
push ax
push bx
push cx
push dx
push si
push di
cmp ah, 1
ja rwdiskret
push bx
mov bl, al ;驱动器号
mov bh, ah ;读写标志
mov cx, 1440
mov ax, dx
mov dx, 0
div cx
mov di, dx
mov dx, 0
mov dh, al ;面号
mov cx, 0
mov cl, 18
mov ax, di
div cl
mov cl, ah
mov ch, al ;磁道号
add cl, 1 ;扇区号
mov dl, bl ;驱动器号
mov al, 1 ;读取扇区数
mov ah, 2 ;2表示读扇区,3表示写扇区
add ah, bh
pop bx
int 13h
rwdiskret:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
;----------------------------------------------------------------------------
sectorend:
db 510 - (offset sectorend - offset setup) dup (0)
dw 0aa55h ; 结束标志
; =================================================================
message:
s0 db 'Select options!', 0
s1 db '1) reset pc', 0
s2 db '2) start system', 0
s3 db '3) clock', 0
s4 db '4) set clock', 0
s5 db 'Input choice q or 1~4:', 0
s6 db 'Set clock format yy/mm/dd hh:mm:ss', 0
s7 db 'Input Esc back, F1 change color!', 0
cmoss1 db '/', '/', ' ', ':', ':', 0
cmoss2 db 9, 8, 7, 4, 2, 0
srow dw s0, s1, s2, s3, s4, s5, s6, s7
func dw fun1, fun2, fun3, fun4
dsdata db 64 dup (0)
; =================================================================
show_screen:
push ax
push bx
push cx
push dx
; 清屏
call cls
mov dh, 4
mov dl, 30
mov cx, 5
mov bx, 0
lshowms:
call show_message
inc dh
inc bx
loop lshowms
mov si, offset s5
mov dh, 9
mov dl, 0
mov ch, offset s6 - offset s5 - 1
call choice_option
pop si
pop ds
pop dx
pop cx
pop bx
pop ax
ret
; =================================================================
; 选择子程序
choice_option:
push ax
push bx
push cx
push dx
choice:
; 清屏
call cls
mov dh, 4
mov dl, 30
mov cx, 5
mov bx, 0
lcshowms:
call show_message
inc dh
inc bx
loop lcshowms
mov dl, 0
call show_message
add dl, offset s6 - offset s5 - 1
call setcursor
lreadkey:
; 读取键盘
mov ah, 0
int 16h
; 比较判断
cmp al, 'q'
je choiceret
cmp al, '4'
ja lreadkey
cmp al, '1'
jb lreadkey
; 显示输入
mov bl, 3
mov cl, 1
call showbyte
; 调用子函数
mov bx, 0
mov bl, al
sub bl, '1'
add bx, bx
call word ptr func[bx]
jmp short choice
choiceret:
pop dx
pop cx
pop bx
pop ax
ret
; =================================================================
; 输出选项信息
; Para: (dh)=光标起始行,(dl)=光标起始列
; (bx)=第几条消息
show_message:
push ax
push bx
push cx
push ds
push si
mov ax, cs
mov ds, ax
add bx, bx
mov si, srow[bx]
mov cl, 2
call show_str
pop si
pop ds
pop cx
pop bx
pop ax
ret
; =================================================================
fun1:
add sp, 2
mov ax, 0ffffh
push ax
mov ax, 0
push ax
retf
fun2:
;call start_old_system
mov bx, 0
mov es, bx
mov bx, 7c00h
;
; mov ah, 0
; mov al, 1h ;读b
; mov dx, 0
; call rwdisk
mov al,1
mov ch,0
mov cl,1
mov dl,1h ;0h代表a盘,80h代表C盘
mov dh,0
mov ah,2
int 13h
add sp, 2
mov bx, 0
push bx
mov bx, 7c00h
push bx
retf
fun3:
push ax
push bx
push cx
push dx
push ds
push es
push si
push di
; 设置int9
mov ax, cs
mov ds, ax
mov ax, 0
mov es, ax
mov si, offset int9
mov di, 206h
mov cx, offset int9end - offset int9
cld
rep movsb
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
mov word ptr es:[204h], 0
cli
mov word ptr es:[9*4], 206h
mov word ptr es:[9*4+2], 0
sti
call cls
mov dh, 7
mov dl, 25
mov bx, 7
call show_message
; 读取cmos时间
mov cx, cs
mov ds, cx
mov si, offset dsdata
mov dh, 8
mov dl, 30
mov cl, 2
lfun3s:
call readcmos
call show_str
mov bx, es:[204h]
cmp bx, 0
je lfun3s
mov word ptr es:[204h], 0
cmp bl, 01h ; 判断是否是ESC
je fun3ret
cmp bl, 3bh ; 判断是否是F1
jne lfun3s
inc cl
jmp lfun3s
fun3ret:
call clear_buff
; 恢复int9
cli
push es:[200h]
pop es:[9*4]
push es:[202h]
pop es:[9*4+2]
sti
pop di
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret
fun4:
push ax
push cx
push dx
push ds
push si
call cls
mov dh, 8
mov dl, 20
mov bx, 6
call show_message
mov dh, 9
mov dl, 30
call setcursor
call getstr
call writecmos
pop si
pop ds
pop dx
pop cx
pop ax
ret
start_old_system:
mov bx,0
mov es,bx
mov bx,7c00h
mov al,1
mov ch,0
mov cl,1
mov dl,1h ;0h代表a盘,80h代表C盘
mov dh,0
mov ah,2
int 13h
mov bx,0
push bx
mov bx,7c00h
push bx
retf
clear_buff:
mov ah, 1
int 16h
jz clearbuffret
mov ah, 0
int 16h
jmp clear_buff
clearbuffret:
ret
; 中断9
int9:
push ax
in al, 60h
mov cs:[204h], ax
pushf
call dword ptr cs:[200h]
int9ret:
pop ax
iret
int9end: nop
; 设置当前页的光标位置;
; Para: (dh)=行,(dl)=列
setcursor:
push ax
push bx
mov ah, 2
mov bh, 0
int 10h
pop bx
pop ax
ret
; 在光标位置显示字符;
; Para: (al)=字符,(bl)=颜色属性,(cx)=字符重复个数
showbyte:
push ax
push bx
mov ah, 9
mov bh, 0
int 10h
pop bx
pop ax
ret
; 在光标位置显示字符串;
; Para: ds:si指向字符串,需要用"$"作为结束符
showstr:
push ax
push dx
mov dx, si
mov ah, 9
int 21h
pop dx
pop ax
ret
; 清屏
cls:
push ax
push bx
push cx
push es
mov ax, 0b800h
mov es, ax
mov bx, 0
mov ah, ' '
;mov al, 0
mov cx, 2000
lcls:
mov byte ptr es:[bx], ah
add bx, 2
loop lcls
pop es
pop cx
pop bx
pop ax
ret
; Info: 在指定的位置,用指定的颜色,显示一个字符
; Para: (dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
; (cl)=颜色
; (al)=字符
; Ret: 无
show_byte:
push ax
push bx
push cx
push dx
push es
mov ch, cl
mov cl, al
mov ax, 0b800h
mov es, ax
mov bl, 0a0h
mov al, dh
mul bl
mov dh, 0
add dl, dl
add ax, dx
mov bx, ax
mov word ptr es:[bx], cx
pop es
pop dx
pop cx
pop bx
pop ax
ret
; Info: 在指定的位置,用指定的颜色,显示一个用0结束的字符串
; Para: (dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
; (cl)=颜色,ds:si指向字符串的首地址
; Ret: 无
show_str:
push ax
push bx
push cx
push dx
push es
push si
mov ax, 0b800h
mov es, ax
mov bl, 0a0h
mov al, dh
mul bl
mov bh, 0
mov bl, dl
add bl, bl
add ax, bx
mov bx, ax
mov al, cl
show_str_change:
mov ch, 0
mov cl, [si]
jcxz show_str_ok
mov ch, al
mov word ptr es:[bx], cx
add bx, 2
inc si
jmp short show_str_change
show_str_ok:
pop si
pop es
pop dx
pop cx
pop bx
pop ax
ret
;读取CMOS时间;
;Para: ds:si指向读取数据内存
readcmos:
push ax
push bx
push cx
push si
mov bx, 0
mov cx, 6
lcmos:
push cx
mov al, cmoss2[bx]
out 70h, al
in al, 71h
mov ah, al
mov cl, 4
shr al, cl
and ah, 00001111b
add al, 30h
add ah, 30h
mov [si], ax
add si, 2
mov al, cmoss1[bx]
mov [si], al
inc bx
inc si
pop cx
loop lcmos
;mov byte ptr [si], 0
pop si
pop cx
pop bx
pop ax
ret
;写入CMOS时间;
;Para: ds:si指向写入数据内存
writecmos:
push ax
push bx
push cx
push si
mov bx, 0
mov cx, 6
wlcmos:
push cx
mov ax, [si]
sub al, 30h
sub ah, 30h
mov cl, 4
shl al, cl
and ah, 00001111b
or ah, al
mov al, cmoss2[bx]
out 70h, al
mov al, ah
out 71h, al
inc bx
add si, 3
pop cx
loop wlcmos
pop si
pop cx
pop bx
pop ax
ret
; 字符栈的入栈、出栈和显示
; Para: (ah)=功能号,0表示入栈,1表示出栈,2表示显示,3当前数据长度;
; ds:si指向字符栈空间
; 0号功能:(al)=入栈字符;
; 1号功能:(al)=返回的字符;
; 2号功能:(cl)=字符颜色属性,(dh)、(dl)=字符串在屏幕上显示的行、列位置;
; 3号功能:(al)=返回的长度;
charstack:
jmp short charstart
chartable dw charpush, charpop, charshow, charlen
chartop dw 0
charstart:
push bx
push dx
push di
push es
cmp ah, 3
ja sret
mov bl, ah
mov bh, 0
add bx, bx
jmp word ptr chartable[bx]
charpush:
mov bx, chartop
mov [si][bx], al
inc chartop
jmp sret
charpop:
cmp chartop, 0
je sret
dec chartop
mov bx, chartop
mov al, [si][bx]
jmp sret
charshow:
mov bx, 0b800h
mov es, bx
mov al, 160
mov ah, 0
mul dh
mov di, ax
add dl, dl
add al, dl
mov dh, 0
add di, dx
mov bx, 0
charshows:
cmp bx, chartop
jne noempty
mov byte ptr es:[di], ' '
jmp sret
noempty:
mov al, [si][bx]
mov es:[di], al
mov es:[di+1], cl
mov byte ptr es:[di+2], ' '
inc bx
add di, 2
jmp charshows
charlen:
mov bx, chartop
mov al, bl
jmp sret
sret:
pop es
pop di
pop dx
pop bx
ret
; 获取字符并显示
; Para: ds:si指向字符栈空间
; (dh)、(dl)=字符串在屏幕上显示的行、列位置;
getstr:
push ax
push cx
push dx
mov cl, 4
getstrs:
mov ah, 0
int 16h
cmp al, 20h
jb nochar
mov ah, 0
call charstack
mov ah, 2
call charstack
mov ah, 3
call charstack
add dl, al
call setcursor
sub dl, al
jmp getstrs
nochar:
cmp ah, 0eh ;退格键扫描码
je getbackspace
cmp ah, 1ch ;Enter键扫描码
je getenter
jmp getstrs
getbackspace:
mov ah, 1
call charstack
mov ah, 2
call charstack
mov ah, 3
call charstack
add dl, al
call setcursor
sub dl, al
jmp getstrs
getenter:
mov al, 0
mov ah, 0
call charstack
mov ah, 2
call charstack
pop dx
pop cx
pop ax
ret
setupend: nop
code ends
end start
;end setup
注:
在实现子程序2时使用rwdisk子程序加载软驱数据,发现无法正常跳转到freedos操作系统,后只能使用中断程序加载扇区可正常跳转到dos系统。原因未知。