为啥读取磁盘信息BIOS int 13h, ah=08h总是有错?

hxfjb 2010-09-02 09:27:19
环境:DOS 7.1的Vmware虚拟机;
编译:masm和link
问题:调用int 13,获取磁盘的扇区数等信息,调用int 13后,总是获取不到需要的数据
代码如下:
提问:为啥啊,这个代码实在是非常简单了,问题出在哪里?

DATAS SEGMENT
myMess db 'nihaoaaaaaaaaaaaaaaa', '$'
DATAS ENDS

STACKS SEGMENT
;此处输入堆栈段代码
STACKS ENDS

CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:DATAS
START:
;调用int 13,获取磁盘的扇区数等信息,调用int 13后,总是获取不到需要的数据
mov dl, 80h
mov ah, 08h
int 13h

MOV AH,4CH
INT 21H
CODES ENDS
END START


...全文
302 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
辰岡墨竹 2010-09-04
  • 打赏
  • 举报
回复
代码没什么错误,但是你不显示么?有完整代码么。
火雲邪神 2010-09-03
  • 打赏
  • 举报
回复
peter-bochs可能追蹤到你的OS存取到那些I/O口
goodider 2010-09-02
  • 打赏
  • 举报
回复
读扇区是ah, 02H吧。写扇区03H
软,硬磁盘复位用00H

INT 13H,AH=02H 读扇区说明:
  调用此功能将从磁盘上把一个或更多的扇区内容读进存贮器。因为这是一个
低级功能,在一个操作中读取的全部扇区必须在同一条磁道上(磁头号和磁道号
相同)。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始,因此用户必须
把跨多条磁道的读操作分为若干条单磁道读操作。
入口参数:
  AH=02H  指明调用读扇区功能。
  AL    置要读的扇区数目,不允许使用读磁道末端以外的数值,也不允许
使该寄存器为0。
  DL    需要进行读操作的驱动器号。
  DH    所读磁盘的磁头号。
  CH    磁道号的低8位数。
  CL    低5位放入所读起始扇区号,位7-6表示磁道号的高2位。
  ES:BX   读出数据的缓冲区地址。
返回参数:
  如果CF=1,AX中存放出错状态。读出后的数据在ES:BX区域依次排列。
  详情请参见磁盘错误状态返回码一文。
示例:
  C_SEG  SEGMENT PUBLIC
       ASSUME CS:C_SEG,DS:C_SEG
       ORG 100H
  START:  JMP READ
  BUFFER  DB  512 DUP(0)
  READ:  PUSH CS
       POP ES
       MOV BX, OFFSET BUFFER
       MOV AX, 0201H
       MOV CX, 0001H
       MOV DX, 0000H
       INT 13H
       ;读软盘A, 0面0道1扇区
       ;读出后数据在BUFFER中
       JC ERROR
       ……
  ERROR:  ……
  C_SEG  ENDS
       END START
看过这个你应该知道问题在那里了吧。
hxfjb 2010-09-02
  • 打赏
  • 举报
回复
to:BAYNPU
我决定这么简单的代码可能出问题的可能不大,是不是其他的什么地方有问题。
BAYNPU 2010-09-02
  • 打赏
  • 举报
回复
hxfjb 2010-09-02
  • 打赏
  • 举报
回复
to goodider:
我是要读取磁盘的信息,不是读取某个扇区的数据。按照int 13h的说明,此时的ah=08h。
我还不清楚问题出在了哪里。
功能06H 功能描述:设置闹钟 入口参数:AH=06H CH=BCD码格式的小时 CL=BCD码格式的分钟 DH=BCD码格式的秒 出口参数:CF=0——操作成功,否则,闹钟已设置或时钟已停止 (8)、功能07H 功能描述:闹钟复位 入口参数:AH=07H 出口参数:无 (9)、功能0AH 功能描述:读取天数计数,仅在PS/2有效,在此从略 (10)、功能0BH 功能描述:设置天数计数,仅在PS/2有效,在此从略 (11)、功能80H 功能描述:设置声音源信息 入口参数:AH=80H AL=声音源 =00H——8253可编程计时器,通道2 =01H——盒式磁带输入 =02H——I/O通道上的"Audio In" =03H——声音产生芯片 出口参数:无 8、直接系统服务(Direct System Service) INT 00H —“0”作除数 INT 01H —单步中断 INT 02H —非屏蔽中断(NMI) INT 03H —断点中断 INT 04H —算术溢出错误 INT 05H —打印屏幕和BOUND越界 INT 06H —非法指令错误 INT 07H —处理器扩展无效 INT 08H —时钟中断 INT 09H —键盘输入 INT 0BH —通信口(COM2:) INT 0CH —通信口(COM1:) INT 0EH —磁盘驱动器输入/输出 INT 11H —读取设备配置 INT 12H —读取常规内存大小(返回值AX为内存容量,以K为单位) INT 18H —ROM BASIC INT 19H —重启动系统 INT 1BH —CTRL+BREAK处理程序 INT 1CH —用户时钟服务 INT 1DH —指向显示器参数表指针 INT 1EH —指向磁盘驱动器参数表指针 INT 1FH —指向图形字符模式表指针
很有用的,大家都来吧;有编辑功能:Backspace键用于删除字符;上下左右键可以任意角度方向的移动 ;光标;Esc键用于退出本软件,同时提示是否保存,若输入Y,则软件将自动把已发送 ;的字符保存在指定文件夹中,初始值指定为'c:\masm\L.txt'中; ;聊天功能:发送字符串后,发送端自动换行;接收端有‘Rec:’字样提示收到了 ;字符;接收字符串基于中断 ;;作者:xuehui869^_^电子邮箱:xuehui869@163.com^_^时间 :2008年3月27日23:35:11 DATA SEGMENT MEMORYBUFFER DB 1000 DUP(?) FILE DB 'c:\masm\L.txt' , 0 ;创建文件的文件名 ERROR_MESSAGE DB 0AH , 'error !' , '$' ;出错时的提示 MESSAGE DB 0AH , 'ok !$' ;操作成功后的提示 HANDLE DW ? ;保存文件号 SENDBUFFER DB 1000 DUP(?) DATA ENDS CODE SEGMENT ASSUME CS: CODE , DS:DATA START: ;主程序部分 MOV AX , DATA MOV DS , AX MOV SI,OFFSET SENDBUFFER MOV DI,OFFSET MEMORYBUFFER ;用于存储接收和已发送的数据 MOV BP,OFFSET SENDBUFFER ;用于判断发送区是否为空,以利于判断DEL键是否进行删除 ;MOV AH,0 ;这些背景色必须加在声明语句中,在主循环中不行 ;MOV AL,11000011B ;MOV AL,42H ;INT 10H MOV AH,0BH ;加边框 MOV BH,0 MOV BL ,200 INT 10H MOV AH , 35H ; MOV AL , 0CH ; INT 21H ;读取原0CH号中断服务程序的地址 PUSH BX PUSH ES ;以上地址入栈保存(以便在程序退出时再恢复中断矢量表中的内容) MOV AX , CS ; MOV DS , AX ; MOV DX , OFFSET RECINTERRUPT ; MOV AH , 25H ; MOV AL , 0CH ; INT 21H ;修改中断矢量表,将NET_INT位置登记为0CH号中断服务程序的地址 ;以下初始化8250 MOV DX , 3FBH ;指向线路控制寄存器 MOV AL , 80H ;位7置1 OUT DX , AL ;发送此字节 MOV DX , 3F9H ;指向波特率除数的高字节 MOV AL , 0 ; 1200 BPS对应的高字节 OUT DX , AL ;发送此字节 DEC DX ;指向波特率除数的低字节 MOV AL , 60H ; 1200 BPS对应的低字节 OUT DX , AL ;发送此字节 ;MOV AL , 1BH ;数据长度为8位 , 1个奇偶校验位 , 偶校验 , 1个停止位 MOV AL , 13H ;数据长度为8位 , 无奇偶校验位 , 1个停止位 MOV DX , 3FBH ;指向线路控制寄存器 OUT DX , AL ;发送此字节 MOV DX , 3FCH ;设置MODEM控制寄存器 MOV AL , 0BH ;允许8250发中断 OUT DX , AL MOV DX , 3F9H ;指向中断允许寄存器 MOV AL , 1 ;允许数据接收准备好中断 OUT DX , AL ;发送此字节 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;这一段很关键,但往往被遗忘 CLI ;关中断,以便对中断控制器8259A进行操作 IN AL , 21H ;读取中断屏蔽寄存器的内容 AND AL , 0EFH ;将COM1中断(即IRQ4引脚)的对应位置0,允许中断。 OUT 21H , AL ;写入中断控制器8259A中 STI ;开中断 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;超级死循环 ,主要是发送程序部分 ,主程序循环,等待收到字符的中断发生。; MOV AX , DATA MOV DS , AX WAITSENDSTRING: MOV AH,1 ; 键盘缓冲区是否有字符? INT 16H ; JZ WAITSENDSTRING ; 无,继续查询 MOV AH,0 ;从键盘缓冲区取字符到AL ,当读了之后,缓冲区清空;不然,键盘缓冲去一直有数据 INT 16H ; CMP AL,00011011B ;是否为退出键Esc JNZ A ;不是,转A继续判断 JMP EXIT ;是,转EXIT,进行程序退出处理 A: CMP AH,48H ;是否为“上”键 JZ KEY_UP ;是,转上处理程序 CMP AH,50H ;下 JZ KEY_DOWN CMP AH,4BH ;左 JZ KEY_LEFT CMP AH,4DH ;右 JZ KEY_RIGHT CMP AL,08H ; 接收到的数据是Del键吗 JNZ OK ; 不是,则转OK CMP BP,SI ;现在没有字符吗 ;本步的功能在于容错处理 JZ OK ; 是,则转OK,不进行删除 MOV AH,02H ;;不是,执行删除字符程序 MOV DL,08H ; DL ='Del'的ASCII码 INT 21H ; MOV DL,00H ; DL ='NULL'的ASCII码 INT 21H ; MOV DL,08H ; DL ='Del'的ASCII码 INT 21H ; DEC SI ;删除存储区内的一个字符 JMP WAITSENDSTRING KEY_UP: MOV AH,03H ;使光标上移 MOV BH ,00H INT 10H DEC DH MOV AH,02H INT 10H JMP WAITSENDSTRING KEY_DOWN: MOV AH,03H ;使光标下移 MOV BH ,00H INT 10H INC DH MOV AH,02H INT 10H JMP WAITSENDSTRING KEY_LEFT: MOV AH,03H ;使光标左移 MOV BH ,00H INT 10H DEC DL MOV AH,02H INT 10H JMP WAITSENDSTRING KEY_RIGHT: MOV AH,03H ;使光标右移 MOV BH ,00H INT 10H INC DL MOV AH,02H INT 10H B: JMP WAITSENDSTRING ;;;;;;;;;;;;;;;;;;;;;;;;;;;;向标准输出外设(如显示器)输出单个字符 OK: MOV AH,02H ; MOV DL,AL ; DL =输出字符的ASCII码 INT 21H ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;将从键盘缓冲区接收到字符串保存到Receivebuffer ;;;;;;;;;;;;;;;;;;数组中,当遇到回车键时将该数组中的字符串通过串口发送出去 MOV BYTE PTR[DI],AL ;将接收到的字符保存到存储区 INC DI MOV BYTE PTR[SI],AL ;将接收到的字符保存到发送缓冲区 INC SI CMP AL,0DH ; 接收到的数据是回车键吗? JNZ B ; 不是,则转WAITSENDSTRING;但是只有JMP指令才能实现far_label跳转,所以 ;采用此迂回办法 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;BIOS中断此处还不能用,莫名其妙的出错 ;MOV AH,01H ;MOV AL,'A' ;MOV DX,0 ;INT 14H ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 是,开始发送 ;由于在sendbuffer帧头加Receive字样,发送会出错,也只能采取这种迂回的办法了 MOV AL,00001010B ;;要使接收端换行使用回车键不行,使用LF换行 MOV DX,3F8H ; 串口发送数据端口地址 OUT DX,AL ; 把该字符发送到输出数据缓冲器 MOV AL,01010010B ;;R键 MOV DX,3F8H ; 串口发送数据端口地址 OUT DX,AL ; 把该字符发送到输出数据缓冲器 MOV AL,01100101B ;;e键 MOV DX,3F8H ; 串口发送数据端口地址 OUT DX,AL ; 把该字符发送到输出数据缓冲器 MOV AL,01100011B ;;c键 MOV DX,3F8H ; 串口发送数据端口地址 OUT DX,AL ; 把该字符发送到输出数据缓冲器 MOV AL,00111010B ;;回车键 MOV DX,3F8H ; 串口发送数据端口地址 OUT DX,AL ; 把该字符发送到输出数据缓冲器 MOV AL,00001010B ;; MOV DX,3F8H ; 串口发送数据端口地址 OUT DX,AL ; 把该字符发送到输出数据缓冲器 MOV SI,OFFSET SENDBUFFER ;恢复 SI初始值 SEND: MOV AL,BYTE PTR[SI] INC SI MOV DX,3F8H ; 串口发送数据端口地址 OUT DX,AL ; 把该字符发送到输出数据缓冲器 CMP AL,0DH ; 接收到的数据是回车键吗? JNZ SEND ; 不是,则转SEND,继续发送 MOV SI,OFFSET SENDBUFFER ;一次发送结束后,重新置位si MOV DL,0AH ; 换行 MOV AH,02H INT 21H JMP WAITSENDSTRING ;发送完,重新开始接收 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;接收中断处理程序 RECINTERRUPT: CLI MOV AX , CS ; MOV DS , AX ; PUSH BX PUSH AX ; 暂存数据 PUSH DX MOV AH,41 ; MOV DL,AL ; DL =输出字符的ASCII码 INT 21H ; MOV DX,3F8H ; 端口接收数据端口地址 IN AL,DX ; 读取数据 AND AL,7FH ; 有效数据位为7位 MOV AH,02H ; 向标准输出外设(如显示器)输出单个字符 MOV DL,AL ; DL =输出字符的ASCII码 INT 21H ; CMP AL,0AH ; 接收到的数据是回车键吗? JNZ L2 ; 不是,则转L2 MOV DL,0AH ; 是,输出换行符换行 MOV AH,02H INT 21H L2: MOV AL , 20H ;发中断结束命令(EOI) OUT 20H , AL ; POP DX ; POP AX POP BX STI ;CPU开中断 IRET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;退出程序 EXIT: ;保存处理 MOV AH,02H ; MOV DL,01010011B ; DL =输出字符的ASCII S INT 21H ; MOV AH,02H ; MOV DL,01100001B ; DL =输出字符的ASCII a INT 21H ; MOV AH,02H ; MOV DL,01110110B ; DL =输出字符的ASCII码 v INT 21H ; MOV AH,02H ; MOV DL,01100101B ; DL =输出字符的ASCII码 e INT 21H ; MOV AH,02H ; MOV DL,00111111B ; DL =输出字符的ASCII码 ? INT 21H ; MOV AH,02H ; MOV DL,01011001B ; DL =输出字符的ASCII码 Y INT 21H ; MOV AH,02H ; MOV DL,00H ; DL =输出字符的ASCII码 INT 21H ; MOV AH,02H ; MOV DL,01001111B ; DL =输出字符的ASCII码 o INT 21H ; MOV AH,02H ; MOV DL,01010010B ; DL =输出字符的ASCII码 r INT 21H ; MOV AH,02H ; MOV DL,00H ; DL =输出字符的ASCII码 INT 21H ; MOV AH,02H ; MOV DL,01001110B ; DL =输出字符的ASCII码 n INT 21H ; WAITSAVE: MOV AH,1 ; 键盘缓冲区是否有字符? INT 16H ; JZ WAITSAVE ; 无,继续查询 MOV AH,0 ;从键盘缓冲区取字符到AL ,当读了之后,缓冲区清空;不然,键盘缓冲去一直有数据 INT 16H ; CMP AL,01011001B ; 接收到的数据是Y键吗? JNZ EXITNOW ; 不是,则转OK JMP SAVE ;退出处理 EXITNOW: MOV AH , 25H ; MOV AL , 0CH ; POP ES ; POP BX ; INT 21H ;恢复中断矢量表中0CH号中断服务程序的原地址。 MOV AH , 4CH ; INT 21H ;返回DOS SAVE: ;MOV AX , DATA ;MOV DS , AX MOV DX , OFFSET FILE MOV CX , 0 MOV AH , 3CH INT 21H ;创建文件,若磁盘上原有此文件,则覆盖 JC ERROR ;创建出错,转error处 MOV HANDLE , AX ;保存文件号 MOV BX , AX MOV BP , OFFSET MEMORYBUFFER SUB DI,BP MOV CX ,DI MOV DX , OFFSET MEMORYBUFFER ;用于计算发送的数目 ,同时是存储的原址 MOV AH , 40H INT 21H ;向文件中写入16个字节内容 JC ERROR ;写出错,转error处 MOV DL,AH MOV AH,02H INT 21H MOV DL,AL MOV AH,02H INT 21H MOV BX , HANDLE MOV AH , 3EH INT 21H ;关闭文件 JC ERROR ;关闭文件出错,转error处 MOV DX , OFFSET MESSAGE MOV AH , 9 INT 21H ;操作成功后显示提示 JMP END1 ERROR: MOV DX , OFFSET ERROR_MESSAGE MOV AH , 9 INT 21H ;显示错误提示` END1: MOV AH , 4CH INT 21H JMP EXITNOW CODE ENDS ; END START
! ! SYS_SIZE is the number of clicks (16 bytes) to be loaded. ! 0x3000 is 0x30000 bytes = 196kB, more than enough for current ! versions of linux ! SYS_SIZE 是要加载的节数(16 字节为1 节)。0x3000 共为 1 2 3 4 5 6 0x7c00 0x0000 0x90000 0x10000 0xA0000 system 模块 代码执行位置线路 0x90200 ! 0x30000 字节=192 kB(上面Linus 估算错了),对于当前的版本空间已足够了。 ! SYSSIZE = 0x3000 ! 指编译连接后system 模块的大小。参见列表1.2 中第92 的说明。 ! 这里给出了一个最大默认值。 ! ! bootsect.s (C) 1991 Linus Torvalds ! ! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves ! iself out of the way to address 0x90000, and jumps there. ! ! It then loads 'setup' directly after itself (0x90200), and the system ! at 0x10000, using BIOS interrupts. ! ! NOTE! currently system is at most 8*65536 bytes long. This should be no ! problem, even in the future. I want to keep it simple. This 512 kB ! kernel size should be enough, especially as this doesn't contain the ! buffer cache as in minix ! ! The loader has been made as simple as possible, and continuos ! read errors will result in a unbreakable loop. Reboot by hand. It ! loads pretty fast by getting whole sectors at a time whenever possible. ! ! 以下是前面这些文字的翻译: ! bootsect.s (C) 1991 Linus Torvalds 版权所有 ! ! bootsect.s 被bios-启动子程序加载至0x7c00 (31k)处,并将自己 ! 移到了地址0x90000 (576k)处,并跳转至那里。 ! ! 它然后使用BIOS 中断将'setup'直接加载到自己的后面(0x90200)(576.5k), ! 并将system 加载到地址0x10000 处。 ! ! 注意! 目前的内核系统最大长度限制为(8*65536)(512k)字节,即使是在 ! 将来这也应该没有问题的。我想让它保持简单明了。这样512k 的最大内核长度应该 ! 足够了,尤其是这里没有象minix 中一样包含缓冲区高速缓冲。 ! ! 加载程序已经做的够简单了,所以持续的读出错将导致死循环。只能手工重启。 ! 只要可能,通过一次取取所有的扇区,加载过程可以做的很快的。 .globl begtext, begdata, begbss, endtext, enddata, endbss ! 定义了6 个全局标识符; .text ! 文本段; begtext: .data ! 数据段; begdata: .bss ! 堆栈段; begbss: .text ! 文本段; SETUPLEN = 4 ! nr of setup-sectors ! setup 程序的扇区数(setup-sectors)值; BOOTSEG = 0x07c0 ! original address of boot-sector ! bootsect 的原始地址(是段地址,以下同); INITSEG = 0x9000 ! we move boot here - out of the way ! 将bootsect 移到这里 -- 避开; SETUPSEG = 0x9020 ! setup starts here ! setup 程序从这里开始; SYSSEG = 0x1000 ! system loaded at 0x10000 (65536). ! system 模块加载到0x10000(64 kB)处; ENDSEG = SYSSEG + SYSSIZE ! where to stop loading ! 停止加载的段地址; ! ROOT_DEV: 0x000 - same type of floppy as boot. ! 根文件系统设备使用与引导时同样的软驱设备; ! 0x301 - first partition on first drive etc ! 根文件系统设备在第一个硬盘的第一个分区上,等等; ROOT_DEV = 0x306 ! 指定根文件系统设备是第2 个硬盘的第1 个分区。这是Linux 老式的硬盘命名 ! 方式,具体值的含义如下: ! 设备号=主设备号*256 + 次设备号(也即dev_no = (major<>512 ! 由于代码段移动过了,所以要重新设置堆栈段的位置。 ! sp 只要指向远大于512 偏移(即地址0x90200)处 ! 都可以。因为从0x90200 地址开始处还要放置setup 程序, ! 而此时setup 程序大约为4 个扇区,因此sp 要指向大 ! 于(0x200 + 0x200 * 4 + 堆栈大小)处。 ! load the setup-sectors directly after the bootblock. ! Note that 'es' is already set up. ! 在bootsect 程序块后紧根着加载setup 模块的代码数据。 ! 注意es 已经设置好了。(在移动代码时es 已经指向目的段地址处0x9000)。 load_setup: ! 68--77 行的用途是利用BIOS 中断INT 0x13 将setup 模块从磁盘第2 个扇区 ! 开始读到0x90200 开始处,共读4 个扇区。如果读出错,则复位驱动器,并 ! 重试,没有退路。INT 0x13 的使用方法如下: ! 读扇区: ! ah = 0x02 - 读磁盘扇区到内存;al = 需要读出的扇区数量; ! ch = 磁道(柱面)号的低8 位; cl = 开始扇区(0-5 位),磁道号高2 位(6-7); ! dh = 磁头号; dl = 驱动器号(如果是硬盘则要置位7); ! es:bx ??指向数据缓冲区; 如果出错则CF 标志置位。 mov dx,#0x0000 ! drive 0, head 0 mov cx,#0x0002 ! sector 2, track 0 mov bx,#0x0200 ! address = 512, in INITSEG mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors int 0x13 ! read it jnc ok_load_setup ! ok - continue mov dx,#0x0000 mov ax,#0x0000 ! reset the diskette int 0x13 j load_setup ok_load_setup: ! Get disk drive parameters, specifically nr of sectors/track ! 取磁盘驱动器的参数,特别是每道的扇区数量。 ! 取磁盘驱动器参数INT 0x13 调用格式和返回信息如下: ! ah = 0x08 dl = 驱动器号(如果是硬盘则要置位7 为1)。 ! 返回信息: ! 如果出错则CF 置位,并且ah = 状态码。 ! ah = 0, al = 0, bl = 驱动器类型(AT/PS2) ! ch = 最大磁道号的低8 位,cl = 每磁道最大扇区数(位0-5),最大磁道号高2 位(位6-7) ! dh = 最大磁头数, dl = 驱动器数量, ! es:di -?? 软驱磁盘参数表。 mov dl,#0x00 mov ax,#0x0800 ! AH=8 is get drive parameters int 0x13 mov ch,#0x00 seg cs ! 表示下一条语句的操作数在cs 段寄存器所指的段中。 mov sectors,cx ! 保存每磁道扇区数。 mov ax,#INITSEG mov es,ax ! 因为上面取磁盘参数中断改掉了es 的值,这里重新改回。 ! Print some inane message ! 在显示一些信息('Loading system ...'回车换行,共24 个字符)。 mov ah,#0x03 ! read cursor pos xor bh,bh ! 读光标位置。 int 0x10 mov cx,#24 ! 共24 个字符。 mov bx,#0x0007 ! page 0, attribute 7 (normal) mov bp,#msg1 ! 指向要显示的字符串。 mov ax,#0x1301 ! write string, move cursor int 0x10 ! 写字符串并移动光标。 ! ok, we've written the message, now ! we want to load the system (at 0x10000) ! 现在开始将system 模块加载到0x10000(64k)处。 mov ax,#SYSSEG mov es,ax ! segment of 0x010000 ! es = 存放system 的段地址。 call read_it ! 读磁盘上system 模块,es 为输入参数。 call kill_motor ! 关闭驱动器马达,这样就可以知道驱动器的状态了。 ! After that we check which root-device to use. If the device is ! defined (!= 0), nothing is done and the given device is used. ! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending ! on the number of sectors that the BIOS reports currently. ! 此后,我们检查要使用哪个根文件系统设备(简称根设备)。如果已经指定了设备(!=0) ! 就直接使用给定的设备。否则就需要根据BIOS 报告的每磁道扇区数来 ! 确定到底使用/dev/PS0 (2,28) 还是 /dev/at0 (2,8)。 ! 上面一行中两个设备文件的含义: ! 在Linux 中软驱的主设备号是2(参见第43 行的注释),次设备号 = type*4 + nr,其中 ! nr 为0-3 分别对应软驱A、B、C 或D;type 是软驱的类型(2??1.2M 或7??1.44M 等)。 ! 因为7*4 + 0 = 28,所以 /dev/PS0 (2,28)指的是1.44M A 驱动器,其设备号是0x021c ! 同理 /dev/at0 (2,8)指的是1.2M A 驱动器,其设备号是0x0208。 seg cs mov ax,root_dev ! 将根设备号 cmp ax,#0 jne root_defined seg cs mov bx,sectors ! 取上面第88 行保存的每磁道扇区数。如果sectors=15 ! 则说明是1.2Mb 的驱动器;如果sectors=18,则说明是 ! 1.44Mb 软驱。因为是可引导的驱动器,所以肯定是A 驱。 mov ax,#0x0208 ! /dev/ps0 - 1.2Mb cmp bx,#15 ! 判断每磁道扇区数是否=15 je root_defined ! 如果等于,则ax 中就是引导驱动器的设备号。 mov ax,#0x021c ! /dev/PS0 - 1.44Mb cmp bx,#18 je root_defined undef_root: ! 如果都不一样,则死循环(死机)。 jmp undef_root root_defined: seg cs mov root_dev,ax ! 将检查过的设备号保存起来。 ! after that (everyting loaded), we jump to ! the setup-routine loaded directly after ! the bootblock: ! 到此,所有程序都加载完毕,我们就跳转到被 ! 加载在bootsect 后面的setup 程序去。 jmpi 0,SETUPSEG ! 跳转到0x9020:0000(setup.s 程序的开始处)。 !!!! 本程序到此就结束了。!!!! ! 下面是两个子程序。 ! This routine loads the system at address 0x10000, making sure ! no 64kB boundaries are crossed. We try to load it as fast as ! possible, loading whole tracks whenever we can. ! ! in: es - starting address segment (normally 0x1000) ! ! 该子程序将系统模块加载到内存地址0x10000 处,并确定没有跨越64KB 的内存边界。我们试图尽快 ! 地进行加载,只要可能,就每次加载整条磁道的数据。 ! 输入:es – 开始内存地址段值(通常是0x1000) sread: .word 1+SETUPLEN ! sectors read of current track ! 当前磁道中已读的扇区数。开始时已经读进1 扇区的引导扇区 ! bootsect 和setup 程序所占的扇区数SETUPLEN。 head: .word 0 ! current head !当前磁头号。 track: .word 0 ! current track !当前磁道号。 read_it: ! 测试输入的段值。必须位于内存地址64KB 边界处,否则进入死循环。清bx 寄存器,用于表示当前段内 ! 存放数据的开始位置。 mov ax,es test ax,#0x0fff die: jne die ! es must be at 64kB boundary ! es 值必须位于64KB 地址边界! xor bx,bx ! bx is starting address within segment ! bx 为段内偏移位置。 rp_read: ! 判断是否已经读入全部数据。比较当前所读段是否就是系统数据末端所处的段(#ENDSEG),如果不是就 ! 跳转至下面ok1_read 标号处继续读数据。否则退出子程序返回。 mov ax,es cmp ax,#ENDSEG ! have we loaded all yet? ! 是否已经加载了全部数据? jb ok1_read ret ok1_read: ! 计算和验证当前磁道需要读取的扇区数,放在ax 寄存器中。 ! 根据当前磁道还未读取的扇区数以及段内数据字节开始偏移位置,计算如果全部读取这些未读扇区,所 ! 读总字节数是否会超过64KB 段长度的限制。若会超过,则根据此次最多能读入的字节数(64KB – 段内 ! 偏移位置),反算出此次需要读取的扇区数。 seg cs mov ax,sectors ! 取每磁道扇区数。 sub ax,sread ! 减去当前磁道已读扇区数。 mov cx,ax ! cx = ax = 当前磁道未读扇区数。 shl cx,#9 ! cx = cx * 512 字节。 add cx,bx ! cx = cx + 段内当前偏移值(bx) ! = 此次读操作后,段内共读入的字节数。 jnc ok2_read ! 若没有超过64KB 字节,则跳转至ok2_read 处执行。 je ok2_read xor ax,ax ! 若加上此次将读磁道上所有未读扇区时会超过64KB,则计算 sub ax,bx ! 此时最多能读入的字节数(64KB – 段内读偏移位置),再转换 shr ax,#9 ! 成需要读取的扇区数。 ok2_read: call read_track mov cx,ax ! cx = 该次操作已读取的扇区数。 add ax,sread ! 当前磁道上已经读取的扇区数。 seg cs cmp ax,sectors ! 如果当前磁道上的还有扇区未读,则跳转到ok3_read 处。 jne ok3_read ! 读该磁道的下一磁头面(1 号磁头)上的数据。如果已经完成,则去读下一磁道。 mov ax,#1 sub ax,head ! 判断当前磁头号。 jne ok4_read ! 如果是0 磁头,则再去读1 磁头面上的扇区数据。 inc track ! 否则去读下一磁道。 ok4_read: mov head,ax ! 保存当前磁头号。 xor ax,ax ! 清当前磁道已读扇区数。 ok3_read: mov sread,ax ! 保存当前磁道已读扇区数。 shl cx,#9 ! 上次已读扇区数*512 字节。 add bx,cx ! 调整当前段内数据开始位置。 jnc rp_read ! 若小于64KB 边界值,则跳转到rp_read(156 行)处,继续读数据。 ! 否则调整当前段,为读下一段数据作准备。 mov ax,es add ax,#0x1000 ! 将段基址调整为指向下一个64KB 段内存。 mov es,ax xor bx,bx ! 清段内数据开始偏移值。 jmp rp_read ! 跳转至rp_read(156 行)处,继续读数据。 ! 读当前磁道上指定开始扇区和需读扇区数的数据到es:bx 开始处。参见第67 行下对BIOS 磁盘读中断 ! int 0x13,ah=2 的说明。 ! al – 需读扇区数;es:bx – 缓冲区开始位置。 read_track: push ax push bx push cx push dx mov dx,track ! 取当前磁道号。 mov cx,sread ! 取当前磁道上已读扇区数。 inc cx ! cl = 开始读扇区。 mov ch,dl ! ch = 当前磁道号。 mov dx,head ! 取当前磁头号。 mov dh,dl ! dh = 磁头号。 mov dl,#0 ! dl = 驱动器号(为0 表示当前驱动器)。 and dx,#0x0100 ! 磁头号不大于1。 mov ah,#2 ! ah = 2,读磁盘扇区功能号。 int 0x13 jc bad_rt ! 若出错,则跳转至bad_rt。 pop dx pop cx pop bx pop ax ret ! 执行驱动器复位操作(磁盘中断功能号0),再跳转到read_track 处重试。 bad_rt: mov ax,#0 mov dx,#0 int 0x13 pop dx pop cx pop bx pop ax jmp read_track /* * This procedure turns off the floppy drive motor, so * that we enter the kernel in a known state, and * don't have to worry about it later. */ ! 这个子程序用于关闭软驱的马达,这样我们进入内核后它处于已知状态,以后也就无须担心它了。 kill_motor: push dx mov dx,#0x3f2 ! 软驱控制卡的驱动端口,只写。 mov al,#0 ! A 驱动器,关闭FDC,禁止DMA 和中断请求,关闭马达。 outb ! 将al 中的内容输出到dx 指定的端口去。 pop dx ret sectors: .word 0 ! 存放当前启动软盘每磁道的扇区数。 msg1: .byte 13,10 ! 回车、换行的ASCII 码。 .ascii "Loading system ..." .byte 13,10,13,10 ! 共24 个ASCII 码字符。 .org 508 ! 表示下面语句从地址508(0x1FC)开始,所以root_dev ! 在启动扇区的第508 开始的2 个字节中。 root_dev: .word ROOT_DEV ! 这里存放根文件系统所在的设备号(init/main.c 中会用)。 boot_flag: .word 0xAA55 ! 硬盘有效标识。 .text endtext: .data enddata: .bss endbss:

21,459

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧