目录

前言

1.问题背景

2.小结

3.本文概述

一、下载、安装VMware虚拟机软件

1.下载

(1)访问官网

(2)找到产品搜索框

(3)搜索workstation软件 

(4)下载workstation player 

2.安装VMware虚拟机

二、下载dos操作系统的安装文件

1.关于dos

2.下载FreeDos安装文件

(1)访问FreeDos

(2)版本选择

(3)关于软盘安装

三、创建一个运行dos系统的虚拟机

1.创建新的虚拟机

2. 选择创建方式

3.选择操作系统

4.给虚拟机起个名字

5.指定磁盘容量

6.添加一个软驱

7.设置软驱

8.虚拟机开机,开始安装FreeDos

9.切换软盘

四、在dos虚拟机中运行汇编程序

1.写一段测试程序

2.关于虚拟机访问物理机的文件

3.下载img镜像文件制作工具

(1)访问WinImage官网

(2)新建一个镜像文件

(3)保存镜像文件

 4.在dos中运行软盘中的程序

(1)为虚拟机添加软驱

(2)访问新软盘

(3)编译、连接

 5.编程读取软盘数据

五、一些可能有用的代码

1.访问硬盘端口读写数据

2.保存、复现屏幕内容

总结


前言

1.问题背景

(1)学习王爽老师《汇编语言》(第四版) 第十七章时,需要编程来读写磁盘。因为书中讲解的软盘如今已很少被使用,如果买软盘及软驱,我不确定是否真的能运行,且学完这节课后就基本无用,实属资源浪费。起初我打算使用同样属于磁盘的U盘来作为替代。这些程序要使用BIOS的13H号中断例程,但是它们在我使用的(64位win10系统)DosBox(虚拟dos环境的软件)中无法正确运行。

(2)我又将读写U盘改为读写笔记本里的固态硬盘,仍然无法读取,返回值一直是FF。

(3)后来我尝试了使用01F0H-0HF7H硬盘端口读取硬盘数据,同样无法运行。

(4)再后来,尝试了在虚拟机中运行dos系统,然后在实模式下运行读写磁盘的程序,结果是仍然不行。

(5)最终,我无功而返。唯一能做的就是将这耗费整整五天得来的探索经验总结下来,后人可以少走弯路。

2.小结

这几天的探索经验总结如下。

(1)无论是在64位win10系统下的DosBox中,还是在dos系统虚拟机中运行,都无法通过 01f0H-01f7H端口 读写硬盘(包括U盘)的数据。

(2)无论是在64位win10系统下的DosBox中,还是在dos系统虚拟机中运行,都可以通过BIOS的13H中断例程 读取磁盘的数据,但不能写入数据(原因不明)。

(3)学会了如何在64位win10系统下,使用虚拟机运行dos系统,并运行汇编程序。(接下来详细介绍)

在虚拟机软件与DOS系统版本的选择方面,我认为比较好的方案是:

①.使用VMware公司的虚拟机软件,因为它提供了官方的免费正版可供使用,比较稳定可靠;

②.使用FreeDos这个开源dos操作系统,因为它开源且与微软的MS-DOS完全兼容;

3.本文概述

本文按照操作顺序,详细介绍:

1.如何下载安装VMware虚拟机软件;

2.如何下载DOS操作系统的安装包;

3.如何创建虚拟机并安装Dos操作系统;

4.如何在dos虚拟机中运行汇编程序。


一、下载、安装VMware虚拟机软件

1.下载

(1)访问官网

打开 VMware官网 后界面如图。

(2)找到产品搜索框

 点击顶部菜单栏中的“应用 平台”,之后将右侧的滚动条(注意!是橙色圈出来的那个!)下拉到底部。效果如图。

然后点击图中的“所有产品”。页面跳转后,将页面下拉,会在左边看到搜索框,如图。

(3)搜索workstation软件 

 在搜索框中搜索“workstation”,回车,如图。

(4)下载workstation player 

① 选择“workstation player”产品

点击“workstation player”,页面跳转后,点击download。(player是免费版,Pro是收费版)

 ②选择版本号

如下页面,点击“下载”。

 ③ 选择对应操作系统的版本

点击下载,如图。(我使用的是64位windows系统)

 文件大小584MB,下载速度Ok,几分钟之内下载完成。

2.安装VMware虚拟机

双击运行安装程序,一路“下一步”。

在这一步可以选择安装位置,如图。

这里的“增强型键盘驱动程序”,我查了一下,似乎是这样的:它的作用是为了增强安全性,但普通使用可以不安装,以后需要用了可以随时安装。于是我决定不勾选。

之后正常安装,需要等待几分钟,即可安装完成。桌面图标如下。


二、下载dos操作系统的安装文件

1.关于dos

虚拟机也像真实的物理机一样,需要操作系统。我想要在虚拟机上运行dos系统,于是现在需要一个dos系统的安装文件。

dos系统是微软开发的,不过早就已经停止更新了。而dos系统在一些领域还有实际作用(比如运行经典dos程序等),因而一些开源社区还在持续维护dos系统。微软的MS-Dos我没有找到开源的安装镜像文件。FreeDos是一个开源的dos系统,完全兼容微软的MS-Dos,正好方便学习。

FreeDos这个网站上有开源的dos系统安装包,可以下载使用(目前最新版是2022年2月发布的)。

2.下载FreeDos安装文件

(1)访问FreeDos

进入 FreeDos 网站首页,如图。然后页面下拉,点击下载,跳转页面。

(2)版本选择

因为是学习汇编语言使用,只需要最核心的系统功能(而不需要各种扩展功能及附带的游戏等),可以选择 FreeDos 1.3 Floppy Edition 版本(这是一个软盘安装版本),压缩包只有20.6MB。

(3)关于软盘安装

 下载后打开压缩包,发现其中有三个文件夹:120m 、 144m、720k 。这个名称是指每一张软盘的容量大小。这个软盘版本的安装程序,自然就是使用软盘来安装操作系统。如果是在真实物理机上使用软盘安装操作系统,就需要一张一张地按顺序插入软盘(插入到软驱中)。现在我们是要使用VMware创建虚拟机,然后在虚拟机上模拟这个过程,因此也会需要一张一张地按照顺序插入软盘,只不过在虚拟机上只需要用鼠标点而不需要操作真实的软盘。

为了操作方便(减少换软盘的次数),这里就选择容量最大的软盘,也就是“144m”这个文件夹,记住它的存放地址,一会儿要用到。

现在可以进行下一步了。


三、创建一个运行dos系统的虚拟机

1.创建新的虚拟机

双击运行workstation player,选择“创建新虚拟机”。如图。

2. 选择创建方式

选择创建一个空白的硬盘,稍后安装操作系统。

这是因为我们下载的是软盘版本的安装文件(img格式),如果下载的是ISO格式的安装文件,那么这一步就应该选择“安装光盘映像文件”。

3.选择操作系统

操作系统的版本选择:其它、MS-DOS。

4.给虚拟机起个名字

5.指定磁盘容量

默认的2GB已经很够用了,然后选择 单个文件 。

6.添加一个软驱

自定义硬件,添加一个软驱。

7.设置软驱

设置软驱,勾选启动时连接软驱。

软盘映像文件的地址,选择FreeDos的地址中“144m”文件夹的“x86Boot.img”文件。

这一步是将“x86Boot.img”这张软盘插入软驱,并且设置在虚拟机开机后自动读取软盘内容,开始安装操作系统。 

8.虚拟机开机,开始安装FreeDos

现在,将虚拟机开机,就会自动从软驱中开始执行FreeDos的安装程序。

一路输入Y(表示Yes)即可,当出现下面这行提示的时候,表示需要插入另一张虚拟软盘。

9.切换软盘

FreeDos的“144m”文件夹中,一共有6张软盘,如图。现在已经放在虚拟软驱中的是第一张(x86BOOT.img)。

接下来,另外五张要依次插入虚拟软驱中,软盘切换的方法如下:

① Ctrl+alt将鼠标切换回windows界面中,菜单栏player-管理-虚拟机设置。

② 在虚拟机的设置窗口右侧,将软盘镜像文件换成下一个img文件,然后回到dos系统界面按任意键,dos就会读取这张软盘的内容。

③ 接下来重复操作,直到“X86DSK05.img”软盘也读取完毕,此时会有一行提示说这张软盘“not bootable floppy”。这时需要再重新换回文件夹中的第一张软盘(x86Boot.img),再次读取完成后,FreeDos系统就安装完成了。 界面如下。

现在dos系统已经安装完成,关机以后,就可以删除软驱中的镜像文件了。不删除也没事。


四、在dos虚拟机中运行汇编程序

现在已经有了能运行dos操作系统的虚拟机,接下来准备运行一个测试程序。

那么,就先写一个简单的测试程序吧。

1.写一段测试程序

程序很简单,就是在屏幕上显示一个字符串。代码如下。

assume cs:code
data segment
    db 'Hello,Dos!',0
data ends
code segment
start:
    mov ax,data     ;ds:si指向字符串源地址
    mov ds,ax
    mov si,0
    mov ax,0B800H   ;es:di指向目标地址
    mov es,ax
    mov di,160*3+32*2
    s:              ;显示字符串
    mov cl,ds:[si]
    jcxz ok
    movsb
    mov es:[di],07H
    inc di
    jmp s

    ok:             ;程序返回
    mov ax,4c00H
    int 21H
code ends
end start

2.关于虚拟机访问物理机的文件

以上这个测试程序是在windows系统中编写及保存的,现在要让虚拟机能够访问这些程序(exe文件或者是asm文件)。

我探索了很久也无法实现在dos系统中读取物理机中的文件(试过了网上的各种方法,包括以管理员身份运行、共享文件夹、磁盘映射等等,均无果),于是决定另辟蹊径。既然dos虚拟机可以读取软盘,那么我们就可以将写好的程序打包成软盘镜像文件,然后在虚拟机的软驱中加载就可以了。

具体的设置方法如下。

3.下载img镜像文件制作工具

(1)访问WinImage官网

访问 WinImage官网 下载30天试用版,安装。

(2)新建一个镜像文件

① 新建一个软盘镜像文件,大小就选择1.44MB。

② 然后,将要测试的asm文件拖拽到WinImage界面的软盘镜像文件中,然后将masm.exe 和 link.exe这两个工具也拖拽进去。这样就可以在dos系统中编译、运行。当然也可以在windows系统中先编译、链接为.exe文件,再直接将exe文件拖拽到软盘镜像文件中。

③ 为了方便调试,记得将debug32.exe也拖拽到软盘镜像文件中。

注意,平时在win10系统下的DosBox中,我使用debug.exe工具进行调试,但是在FreeDos中,就要使用debug32.exe工具进行调试。如果使用debug.exe工具,在FreeDos中运行它时系统会提示说不是正确的dos版本。debug32.exe也是开源工具,可以在“debug.exe”所在的目录中找一下,如果没有,就百度一个吧。

(3)保存镜像文件

这里注意,FreeDos能识别的软盘镜像文件只有img和flp两个格式,因此保存镜像的时候,文件名的后缀要写flp,下边的文件类型选“vfd/flp那个”,如图。这个文件的保存地址下一步要用到。

 4.在dos中运行软盘中的程序

(1)为虚拟机添加软驱

运行VMware,在虚拟机关机状态,进入设置界面,添加一个软驱,勾选“启动时连接”,软盘映像文件的地址就设置为上一步保存的那个flp文件的地址。

注意,要运行的程序所在的软盘文件,对应的软驱是“软盘2”,也就是会在盘符B下。

(2)访问新软盘

将虚拟机开机,进入后默认在盘符A下。这个A盘就是启动盘,对应着第一个软驱。(如果安装完FreeDos之后,已经将安装盘从软驱中清空的话,那么这里的默认盘符就是C。)而上一步中新建的软驱,对应盘符B。因此在命令行输入 b:回车,就进入了B盘下。注意,要在英文状态下输入冒号!

(3)编译、连接

现在在命令行输入 dir回车(这表示列出当前目录下的文件及文件夹),可以看到刚才打包的文件。

 接着,就像之前在DosBox中一样,编译、链接。然后就可以运行了!

(假设文件名为“1.asm”,则编译命令为 masm 1; 回车,链接命令为 link 1; 回车,运行命令为  1.exe 回车))

运行效果如下。

(4)debug32调试

如果要使用debug32工具调试程序,那么首先进入到debug32.exe工具所在的目录,然后输入 debug32 回车 ,即可开始调试,如图。

 

 5.编程读取软盘数据

在dos虚拟机中,可以在程序中读取软盘的数据。只要在另一个软驱中(最多配置2个软驱)放置一个空的flp镜像文件,那么就相当于插入了一张空白软盘,然后就可以读取数据了。只能读软盘,不能读硬盘,软盘也是只能读不能写,我也很想知道为什么。

附上一段读取软盘数据的程序。

assume cs:code  ;测试:编程读取软盘数据
data segment
    db 512 dup(41H)
data ends
stack segment
    db 16 dup(0)
stack ends
code segment
start:
    mov ax,stack    ;初始化 设置栈顶
    mov ss,ax
    mov sp,20H

    mov ax,data     ;es:bx指向接收磁盘数据的区域
    mov es,ax
    mov bx,0

    call test_read

    mov ax,4C00H
    int 21H

test_read:  ;功能:读取磁盘数据到内存es:bx处

    mov al,1    ;读取的扇区个数
    mov ch,0    ;磁道号
    mov cl,1    ;扇区号
    mov dl,0    ;驱动器号 0代表软盘A
    mov dh,1    ;磁头号,即面号
    mov ah,02H   ;读命令
    int 13H

    ret

五、一些可能有用的代码

在探索过程中整理了一些代码,也许读到本文的人能用到吧。

1.访问硬盘端口读写数据

在使用13H读写磁盘失败后,我搜索到了另外一种读写硬盘数据的方法,就是通过访问 01F0H-01F7H端口来读写硬盘(共有CHS LBA24 LBA48三种模式)。虽然这个方法我的电脑也不能运行成功,不过还是将代码整理在这里吧。

test3:  ;功能:LBA48方式读取硬盘数据
        ;参数:ds:bx指向接收硬盘数据的内存首地址

    push ax
    push dx
    push cx

    ;1.写两次0x1f1端口: 0
    mov al,0
    mov dx,01F1H
    out dx,al
    out dx,al

    ;2.写两次0x1f2端口: 第一次要读的扇区数的高8位,第二次低8位
    mov al,0H
    mov dx,01F2H
    out dx,al
    mov al,01H
    out dx,al

    ;3.写0x1f3: LBA参数的24~31位
    mov al,0
    mov dx,01F3H
    out dx,al

    ;4.写0x1f3: LBA参数的0~7位
    mov al,1
    mov dx,01F3H
    out dx,al

    ;5.写0x1f4: LBA参数的32~39位
    mov al,0
    mov dx,01F4H
    out dx,al

    ;6.写0x1f4: LBA参数的8~15位
    mov al,0
    mov dx,01F4H
    out dx,al

    ;7.写0x1f5: LBA参数的40~47位
    mov al,0
    mov dx,01F5H
    out dx,al

    ;8.写0x1f5: LBA参数的16~23位
    mov al,0
    mov dx,01F5H
    out dx,al

    ;9.写0x1f6: 7~5位,010,第4位0表示主盘,1表示从盘,3~0位,0
    mov al,01000000B
    mov dx,01F6H
    out dx,al

    ;10.写0x1f7: 0x24为读, 0x34为写
    mov al,24H
    mov dx,01F7H
    out dx,al

    ;11.读取数据
    mov dx,01F0H
    in ax,dx
    mov ds:[bx],ax

    pop cx
    pop dx
    pop ax

    ret
;==============================================
test2:  ;chs方式访问硬盘数据

    push ax
    push dx
    push cx

    ;1.向01F1H 写入0
    mov al,0
    mov dx,01F1H
    out dx,al
    out dx,al

    ;2.向01F2H写入要访问的扇区个数
    mov al,1
    mov dx,01F2H
    out dx,al

    ;3.写入扇区号
    mov al,0
    mov dx,01F3H
    out dx,al

    ;4.写入柱面低8位
    mov al,0
    mov dx,01F4H
    out dx,al

    ;5.写入柱面高8位
    mov al,0
    mov dx,01F5H
    out dx,al

    ;6.写入 7~5位,101,第4位0表示主盘,1表示从盘,3~0位,磁头号
    mov al,10110000B
    mov dx,01F6H
    out dx,al

    ;7.传送读写命令
    mov al,20H
    mov dx,01F7H
    out dx,al

    ;8.等待读写完成
    mov dx,01F7H
    s_test2:
    in al,dx
    and al,00010000B
    cmp al,10H
    jne s_test2

    ;9.读取数据
    mov dx,01F0H
    mov cx,256
    s2_test2:
    in ax,dx
    mov ds:[bx],ax
    add bx,2
    loop s2_test2

    pop cx
    pop dx
    pop ax

    ret
;==============================================

test1:  ;功能:LBA24方式读取U盘中1个扇区的数据,并存入ds:bx处
    ;参数:ds:bx指向接收硬盘数据的内存首地址

    ;1.将访问的扇区个数送入端口
    mov al,01H        ;参数:要访问的扇区个数
    mov dx,01F2H
    out dx,al

    ;2.将访问的起始扇区的逻辑扇区号送入端口
    mov al,02H      ;参数:逻辑扇区号0-7位
    mov dx,01F3H
    out dx,al
    mov al,00H      ;参数:逻辑扇区号8-15位
    mov dx,01F4H
    out dx,al
    mov al,00H      ;参数:逻辑扇区号16-23位
    mov dx,01F5H
    out dx,al
    mov al,0F0H     ;参数:高4位 1 读写模式(1位LBA) 1 硬盘选择(0为主硬盘) ;低4位 逻辑扇区号24-27位
    mov dx,01F6H
    out dx,al

    ;3.将读/写指令送入端口
    mov al,20H      ;参数:20H是读入数据;30H是写出数据
    mov dx,01F7H
    out dx,al

    ;4.读取硬盘已就绪的状态字节
    mov dx,01F7H
    ready:
    in al,dx
    and al,88H
    cmp al,08H
    jne ready

    ;5.从端口读取/写入数据
    mov cx,256
    mov dx,01F0H
    s_test1:
    in ax,dx
    mov es:[bx],ax
    add bx,2
    loop s_test1

    ret     

2.保存、复现屏幕内容

这是书上的内容,以后如果换了运行环境,可以重新拿来做个测试。代码如下。

saveScreen:     ;功能:将当前屏幕上的内容保存在磁盘上
        ;参数:ah 功能号(3表示写扇区);al写入的扇区个数
        ;       ch 磁道号;cl 扇区号(从1开始);
        ;       dh 磁头号(面);dl 驱动器号(81H为硬盘D盘)
        ;       es:bx指向要写入磁盘的数据首地址
        ;返回:操作成功: ah=0,al=写入的扇区个数;
        ;       操作失败:ah=出错代码

    push ax
    push es
    push bx
    push cx
    push dx

    mov ax,0B800H   ;参数:es:bx指向要写入磁盘的数据首地址
    mov es,ax
    mov bx,0
    mov al,8        ;参数:写入的扇区个数(一屏幕4000字节,一个扇区512字节,所以需要8个扇区)
    mov ch,0        ;参数:磁道号
    mov cl,1        ;参数:扇区号,从1开始
    mov dh,1        ;参数:磁头号(面)
    mov dl,81H      ;参数:驱动器号(D盘为81H)
    mov ah,3        ;参数:功能号 3表示写扇区
    int 13H         ;调用int 13H中断例程

    pop dx
    pop cx
    pop bx
    pop es
    pop ax

    ret
;===================================================

readScreen:     ;功能:将磁盘中的内容显示在屏幕上
        ;参数:ah 功能号(2表示读扇区);al 读取的扇区个数;
        ;       ch 磁道号; cl 扇区号(从1开始);
        ;       dh 磁头号(面);dl 驱动器号(81H为D盘)
        ;       es:bx指向用于接收磁盘读入的数据的内存区
        ;返回:操作成功:ah=0 al=读入的扇区数;
        ;       操作失败:ah=出错代码

    push ax
    push bx
    push cx
    push dx
    push es

    mov ax,0B800H   ;参数:es:bx指向用于接收磁盘读入数据的内存区
    mov es,ax
    mov bx,0
    mov al,8        ;参数:读取的扇区个数(一屏幕4000字节,需要8个扇区)
    mov ch,0        ;参数:磁道号
    mov cl,1        ;参数:扇区号,从1开始
    mov dh,1        ;参数:磁头号(面)
    mov dl,81H      ;参数:驱动器号,81H为D盘
    mov ah,2        ;参数:功能号,2表示读扇区
    int 13H         ;调用int 13H中断例程

    pop es
    pop dx
    pop cx
    pop bx
    pop ax

    ret


总结

这可以说是学习汇编语言以来花费时间最长的一篇博文了。耗时耗力的主要原因,是书籍内容与时代脱节,导致我不得不进行了许多摸索(网上的资料我找过了),绕了很大一个弯,浪费了许多宝贵时间,才最终发现无法走通。一路学习到这最后一章,我对王爽老师的独特教学方法很佩服、很信任。但是这一次的尝试摸索,不禁使我对这本2019年12月才修订出版过第四版的教材产生了一些失望。

由于第十七章的学习无法完成,后面的课程设计也无法完成了。原本还想继续写几篇关于汇编语言的博文,但现在兴趣索然,故这一篇大概会是这一系列关于汇编语言的博文的最后一篇了。2022.11

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐