作为我操作系统的一部分,我编写了这个读取扇区函数。
从BIOS设备ID读取扇区地址。但是当我设置从扇区19(Head:0,Track:1,Sector 2)读取时,0x1000:0x0000的结果可能超过该扇区(我用十六进制查看器检查了几次)。
另外,当我读取多个扇区,以便包含扇区19时,在上面提到的地址,我可以读取扇区19,它被复制在0x1000:(512 * 19)而没有问题。
void __NOINLINE resetDisk(const int device_id) {
__asm__ __volatile__("" : : "d"(0x0000|device_id)); //set device id
__asm__ __volatile__("mov $0x0000,%ax"); //function 0x02
__asm__ __volatile__("int $0x13");
}
void __NOINLINE readDiskSector(const int sector, const int device_id) {
resetDisk(device_id);
int sector_count = 2880;
int heads = 2;
int tracks = 18;
int h = sector/(sector_count/heads);
int c = (sector-h*(sector_count/heads))/tracks;
int s = sector-c*tracks-h*(sector_count/heads)+1;
__asm__ __volatile__("push %es");
__asm__ __volatile__("" : : "a"(c));
__asm__ __volatile__("" : : "b"(s));
__asm__ __volatile__("mov %al,%ch");
__asm__ __volatile__("mov %bl,%cl");
__asm__ __volatile__("" : : "a"(h));
__asm__ __volatile__("" : : "b"(device_id));
__asm__ __volatile__("mov %al,%dh");
__asm__ __volatile__("mov %bl,%dl");
__asm__ __volatile__("mov $0x03,%si");
__asm__ __volatile__("try_again_reading:");
__asm__ __volatile__("cmp $0x00,%si");
__asm__ __volatile__("je stop_trying");
__asm__ __volatile__("mov $0x1000,%bx");
__asm__ __volatile__("mov %bx,%es");
__asm__ __volatile__("mov $0x0000,%bx");
__asm__ __volatile__("mov $0x02,%ah");
__asm__ __volatile__("mov $0x01,%al");
__asm__ __volatile__("int $0x13");
__asm__ __volatile__("dec %si");
__asm__ __volatile__("jc try_again_reading");
__asm__ __volatile__("stop_trying:");
__asm__ __volatile__("pop %es");
}
答案 0 :(得分:1)
从正确的GCC基本内联汇编和扩展内联汇编的角度来看,该代码存在一些严重问题,但是从根本上讲,计算中涉及访问逻辑块地址19(LBA)的问题。 LBA 19是 CHS (圆柱体,头部,扇区)=(0,1,2),其中OP表示它是(头部:0,轨迹:1 ,Sector 2)这是不正确的。
Int 13h/ah=2采用 CHS 值。您可以使用以下公式(或等效公式)将 LBA 转换为 CHS 值:
C = (LBA ÷ SPT) ÷ HPC H = (LBA ÷ SPT) mod HPC S = (LBA mod SPT) + 1 HPC = Heads per cylinder (aka Number of Heads) SPT = Sectors per Track, LBA = logical block address "mod" is the modulo operator (to get the remainder of a division)
我在将LBA转换为CHS 的其他Stackoverflow answer中已经写了关于 LBA 至 CHS 计算的更多信息。
一个观察结果是,最大扇区数根本不计入方程式。真正的问题是OP的公式不正确:
int sector_count = 2880;
int heads = 2; /* Head per cylinder */
int tracks = 18; /* Sectors per Track */
int h = sector/(sector_count/heads);
int c = (sector-h*(sector_count/heads))/tracks;
int s = sector-c*tracks-h*(sector_count/heads)+1;
方程式中唯一能产生正确结果(以一轮回合的方式)的部分是s
(扇区)。 c
(圆柱)和h
(头)的计算不正确。因此,这导致了问题和OP的后续回答中观察到的问题。为了了解由OP方程产生的值,我编写了一个程序,使用适当的公式将其值与正确的值进行比较:
LBA = 0: CHS = ( 0, 0, 1) | CHS = ( 0, 0, 1) LBA = 1: CHS = ( 0, 0, 2) | CHS = ( 0, 0, 2) LBA = 2: CHS = ( 0, 0, 3) | CHS = ( 0, 0, 3) LBA = 3: CHS = ( 0, 0, 4) | CHS = ( 0, 0, 4) LBA = 4: CHS = ( 0, 0, 5) | CHS = ( 0, 0, 5) LBA = 5: CHS = ( 0, 0, 6) | CHS = ( 0, 0, 6) LBA = 6: CHS = ( 0, 0, 7) | CHS = ( 0, 0, 7) LBA = 7: CHS = ( 0, 0, 8) | CHS = ( 0, 0, 8) LBA = 8: CHS = ( 0, 0, 9) | CHS = ( 0, 0, 9) LBA = 9: CHS = ( 0, 0, 10) | CHS = ( 0, 0, 10) LBA = 10: CHS = ( 0, 0, 11) | CHS = ( 0, 0, 11) LBA = 11: CHS = ( 0, 0, 12) | CHS = ( 0, 0, 12) LBA = 12: CHS = ( 0, 0, 13) | CHS = ( 0, 0, 13) LBA = 13: CHS = ( 0, 0, 14) | CHS = ( 0, 0, 14) LBA = 14: CHS = ( 0, 0, 15) | CHS = ( 0, 0, 15) LBA = 15: CHS = ( 0, 0, 16) | CHS = ( 0, 0, 16) LBA = 16: CHS = ( 0, 0, 17) | CHS = ( 0, 0, 17) LBA = 17: CHS = ( 0, 0, 18) | CHS = ( 0, 0, 18) LBA = 18: CHS = ( 1, 0, 1) | CHS = ( 0, 1, 1) LBA = 19: CHS = ( 1, 0, 2) | CHS = ( 0, 1, 2) LBA = 20: CHS = ( 1, 0, 3) | CHS = ( 0, 1, 3) LBA = 21: CHS = ( 1, 0, 4) | CHS = ( 0, 1, 4) LBA = 22: CHS = ( 1, 0, 5) | CHS = ( 0, 1, 5) LBA = 23: CHS = ( 1, 0, 6) | CHS = ( 0, 1, 6) LBA = 24: CHS = ( 1, 0, 7) | CHS = ( 0, 1, 7) LBA = 25: CHS = ( 1, 0, 8) | CHS = ( 0, 1, 8) LBA = 26: CHS = ( 1, 0, 9) | CHS = ( 0, 1, 9) LBA = 27: CHS = ( 1, 0, 10) | CHS = ( 0, 1, 10) LBA = 28: CHS = ( 1, 0, 11) | CHS = ( 0, 1, 11) LBA = 29: CHS = ( 1, 0, 12) | CHS = ( 0, 1, 12) LBA = 30: CHS = ( 1, 0, 13) | CHS = ( 0, 1, 13) LBA = 31: CHS = ( 1, 0, 14) | CHS = ( 0, 1, 14) LBA = 32: CHS = ( 1, 0, 15) | CHS = ( 0, 1, 15) LBA = 33: CHS = ( 1, 0, 16) | CHS = ( 0, 1, 16) LBA = 34: CHS = ( 1, 0, 17) | CHS = ( 0, 1, 17) LBA = 35: CHS = ( 1, 0, 18) | CHS = ( 0, 1, 18) LBA = 36: CHS = ( 2, 0, 1) | CHS = ( 1, 0, 1) LBA = 37: CHS = ( 2, 0, 2) | CHS = ( 1, 0, 2) LBA = 38: CHS = ( 2, 0, 3) | CHS = ( 1, 0, 3) LBA = 39: CHS = ( 2, 0, 4) | CHS = ( 1, 0, 4) LBA = 40: CHS = ( 2, 0, 5) | CHS = ( 1, 0, 5) LBA = 41: CHS = ( 2, 0, 6) | CHS = ( 1, 0, 6) LBA = 42: CHS = ( 2, 0, 7) | CHS = ( 1, 0, 7) LBA = 43: CHS = ( 2, 0, 8) | CHS = ( 1, 0, 8) LBA = 44: CHS = ( 2, 0, 9) | CHS = ( 1, 0, 9) LBA = 45: CHS = ( 2, 0, 10) | CHS = ( 1, 0, 10) LBA = 46: CHS = ( 2, 0, 11) | CHS = ( 1, 0, 11) LBA = 47: CHS = ( 2, 0, 12) | CHS = ( 1, 0, 12) LBA = 48: CHS = ( 2, 0, 13) | CHS = ( 1, 0, 13) LBA = 49: CHS = ( 2, 0, 14) | CHS = ( 1, 0, 14) LBA = 50: CHS = ( 2, 0, 15) | CHS = ( 1, 0, 15) LBA = 51: CHS = ( 2, 0, 16) | CHS = ( 1, 0, 16) LBA = 52: CHS = ( 2, 0, 17) | CHS = ( 1, 0, 17) LBA = 53: CHS = ( 2, 0, 18) | CHS = ( 1, 0, 18) LBA = 54: CHS = ( 3, 0, 1) | CHS = ( 1, 1, 1) LBA = 55: CHS = ( 3, 0, 2) | CHS = ( 1, 1, 2) LBA = 56: CHS = ( 3, 0, 3) | CHS = ( 1, 1, 3) LBA = 57: CHS = ( 3, 0, 4) | CHS = ( 1, 1, 4) LBA = 58: CHS = ( 3, 0, 5) | CHS = ( 1, 1, 5) LBA = 59: CHS = ( 3, 0, 6) | CHS = ( 1, 1, 6) LBA = 60: CHS = ( 3, 0, 7) | CHS = ( 1, 1, 7) LBA = 61: CHS = ( 3, 0, 8) | CHS = ( 1, 1, 8) LBA = 62: CHS = ( 3, 0, 9) | CHS = ( 1, 1, 9) LBA = 63: CHS = ( 3, 0, 10) | CHS = ( 1, 1, 10) LBA = 64: CHS = ( 3, 0, 11) | CHS = ( 1, 1, 11) LBA = 65: CHS = ( 3, 0, 12) | CHS = ( 1, 1, 12) LBA = 66: CHS = ( 3, 0, 13) | CHS = ( 1, 1, 13) LBA = 67: CHS = ( 3, 0, 14) | CHS = ( 1, 1, 14) LBA = 68: CHS = ( 3, 0, 15) | CHS = ( 1, 1, 15) LBA = 69: CHS = ( 3, 0, 16) | CHS = ( 1, 1, 16) LBA = 70: CHS = ( 3, 0, 17) | CHS = ( 1, 1, 17) LBA = 71: CHS = ( 3, 0, 18) | CHS = ( 1, 1, 18) LBA = 72: CHS = ( 4, 0, 1) | CHS = ( 2, 0, 1) LBA = 73: CHS = ( 4, 0, 2) | CHS = ( 2, 0, 2) LBA = 74: CHS = ( 4, 0, 3) | CHS = ( 2, 0, 3) LBA = 75: CHS = ( 4, 0, 4) | CHS = ( 2, 0, 4) LBA = 76: CHS = ( 4, 0, 5) | CHS = ( 2, 0, 5) LBA = 77: CHS = ( 4, 0, 6) | CHS = ( 2, 0, 6) LBA = 78: CHS = ( 4, 0, 7) | CHS = ( 2, 0, 7) LBA = 79: CHS = ( 4, 0, 8) | CHS = ( 2, 0, 8) ...
OP的结果在左侧,正确的结果在右侧。 LBA 0到 LBA 17是正确的。如果您开始读取 LBA 小于18的一个或多个扇区,那将是正确的。如果使用为 LBA 19计算的 CHS 值,则它们是错误的。
OP在他们的答案中建议,气缸和缸盖值的文档不正确,并且寄存器已颠倒。该文档是正确的:
AL = number of sectors to read (must be nonzero) CH = low eight bits of cylinder number CL = sector number 1-63 (bits 0-5) high two bits of cylinder (bits 6-7, hard disk only) DH = head number DL = drive number (bit 7 set for hard disk) ES:BX -> data buffer
OP的答案表明一种解决方法是更换磁头和磁头。实际上,这偶然使他的代码对于 LBA 0到 LBA 35起作用。 LBA > = 36是不正确的。
解决方法是在OP的代码中使用适当的计算:
c = (sector / tracks) / heads;
h = (sector / tracks) % heads;
s = (sector % tracks) + 1;
#include <stdio.h>
int main()
{
const int sector_count = 2880;
const int heads = 2;
const int tracks = 18; /* tracks per sector */
unsigned char h, h2;
unsigned char c, c2;
unsigned char s, s2;
int sector; /* LBA */
for (sector=0; sector < sector_count; sector++) {
/* Improper calculation */
h = sector/(sector_count/heads);
c = (sector-h*(sector_count/heads))/tracks;
s = sector-c*tracks-h*(sector_count/heads)+1;
/* Proper calculation */
c2 = (sector / tracks) / heads;
h2 = (sector / tracks) % heads;
s2 = (sector % tracks) + 1;
printf ("LBA = %4d: CHS = (%2d, %2d, %2d) | CHS = (%2d, %2d, %2d)\n",
sector, c, h, s, c2, h2, s2);
}
return 0;
}
biosdisk.h
#ifndef BIOSDISK_H
#define BIOSDISK_H
#include <stdint.h>
/* BIOS Parameter Block (BPB) on floppy media */
typedef struct __attribute__((packed)) {
char OEMname[8];
uint16_t bytesPerSector;
uint8_t sectPerCluster;
uint16_t reservedSectors;
uint8_t numFAT;
uint16_t numRootDirEntries;
uint16_t numSectors;
uint8_t mediaType;
uint16_t numFATsectors;
uint16_t sectorsPerTrack;
uint16_t numHeads;
uint32_t numHiddenSectors;
uint32_t numSectorsHuge;
uint8_t driveNum;
uint8_t reserved;
uint8_t signature;
uint32_t volumeID;
char volumeLabel[11];
char fileSysType[8];
} disk_bpb_s;
/* State information for CHS disk accesses */
typedef struct __attribute__((packed)) {
uint16_t segment;
uint16_t offset;
uint16_t status;
/* Drive geometry needed to compute CHS from LBA */
uint16_t sectorsPerTrack;
uint16_t numHeads;
/* Disk parameters */
uint16_t cylinder;
uint8_t head;
uint8_t sector;
uint8_t driveNum;
uint8_t numSectors; /* # of sectors to read */
/* Number of retries for disk operations */
uint8_t retries;
} disk_info_s;
extern fastcall uint8_t
reset_disk (disk_info_s *const disk_info);
extern fastcall uint8_t
read_sector_chs (disk_info_s *const disk_info);
/* Forced inline version of reset_sector */
static inline fastcall always_inline uint8_t
reset_disk_i (disk_info_s *const disk_info)
{
uint16_t temp_ax = 0x0000;
uint8_t carryf;
__asm__ __volatile__ (
"int $0x13\n\t"
#ifdef __GCC_ASM_FLAG_OUTPUTS__
: [cf]"=@ccc"(carryf),
#else
"setc %[cf]\n\t"
: [cf]"=qm"(carryf),
#endif
"+a"(temp_ax)
: "d"(disk_info->driveNum)
: "cc");
disk_info->status = temp_ax;
return (carryf);
}
/* Forced inline version of read_sector */
static inline fastcall always_inline uint8_t
read_sector_chs_i (disk_info_s *const disk_info)
{
uint16_t temp_ax;
uint16_t temp_dx;
uint8_t carryf = 0;
uint8_t retry_count = 0;
#ifndef BUGGY_BIOS_SUPPORT
temp_dx = (disk_info->head << 8) | disk_info->driveNum;
#endif
do {
/* Only reset disk if error detected previously */
if (carryf)
reset_disk_i (disk_info);
/* Need to reload AX during each iteration since a previous
* int 0x13 call will destroy its contents. There was a bug on
* earlier BIOSes where DX may have been clobbered.
*/
temp_ax = (0x02 << 8) | disk_info->numSectors;
#ifdef BUGGY_BIOS_SUPPORT
temp_dx = (disk_info->head << 8) | disk_info->driveNum;
#endif
__asm__ __volatile__ (
"push %%es\n\t"
"mov %w[seg], %%es\n\t"
#ifdef BUGGY_BIOS_SUPPORT
"stc\n\t" /* Some early bioses have CF bug */
"int $0x13\n\t"
"sti\n\t" /* Some early bioses don't re-enable interrupts */
#else
"int $0x13\n\t"
#endif
"pop %%es\n\t"
#ifdef __GCC_ASM_FLAG_OUTPUTS__
: [cf]"=@ccc"(carryf),
#else
"setc %[cf]\n\t"
: [cf]"=qm"(carryf),
#endif
#ifdef BUGGY_BIOS_SUPPORT
"+a"(temp_ax),
"+d"(temp_dx)
:
#else
"+a"(temp_ax)
:
"d"(temp_dx),
#endif
"c"(((disk_info->cylinder & 0xff) << 8) |
((disk_info->cylinder >> 2) & 0xC0) |
(disk_info->sector & 0x3f)),
"b"(disk_info->offset),
[seg]"r"(disk_info->segment)
: "memory", "cc");
} while (carryf && (++retry_count < disk_info->retries));
disk_info->status = temp_ax;
return (carryf);
}
/* Forced inline version of read_sector_lba */
static inline fastcall always_inline uint8_t
read_sector_lba_i (disk_info_s *const disk_info, const uint32_t lba)
{
disk_info->cylinder = lba / disk_info->sectorsPerTrack / disk_info->numHeads;
disk_info->head = (lba / disk_info->sectorsPerTrack) % disk_info->numHeads;
disk_info->sector = (lba % disk_info->sectorsPerTrack) + 1;
return read_sector_chs_i (disk_info);
}
#endif
biosdisk.c :
#include <stdint.h>
#include "biosdisk.h"
fastcall uint8_t
reset_disk (disk_info_s *const disk_info)
{
return reset_disk_i (disk_info);
}
fastcall uint8_t
read_sector_chs (disk_info_s *const disk_info)
{
return read_sector_chs_i (disk_info);
}
fastcall uint8_t
read_sector_lba (disk_info_s *const disk_info, const uint32_t lba)
{
return read_sector_lba_i (disk_info, lba);
}
x86helper.h :
#ifndef X86HELPER_H
#define X86HELPER_H
#define fastcall __attribute__((regparm(3)))
/* noreturn lets GCC know that a function that it may detect
won't exit is intentional */
#define noreturn __attribute__((noreturn))
#define always_inline __attribute__((always_inline))
#define used __attribute__((used))
#endif
可以找到一个小的概念证明项目,该项目可以在GCC中创建一个两阶段的引导加载程序,on my website
Int 13h/AH=0h重置磁盘系统。在诸如软盘之类的实际硬件上,此操作可能会花费大量时间,因为它还会重新校准驱动器磁头。仅在检测到错误之后且重试磁盘操作之前,才应重置磁盘。
使用GCC创建将在实模式下运行的代码充其量是有问题的。用-m16
生成的代码通常只能在80386或更高版本的处理器上运行。
编译器不能保证多个asm
语句按照它们在代码中出现的顺序发出。您应该将多个asm
语句合并为一个。 GCC documentation说:
即使在使用volatile限定符时,也不要期望 asm 语句序列在编译后会保持完美连续。如果某些指令需要在输出中保持连续,请将它们放在一条多指令 asm 语句中。
如果您在GCC的内联汇编中修改寄存器,您应该告诉编译器。使用GCC的扩展内联汇编,并在 clobber 列表中列出修改后的寄存器。
尝试将内联汇编最小化,并尽可能多地使用 C 代码。 David Wohlferd用reasons not to use inline assembly写了一篇不错的文章。如果您不了解内联汇编的细微差别,则可以考虑在单独的汇编语言模块中编写代码,并将其链接到您的 C 程序。
GCC没有实模式的20-bit segment offset addressing概念,这会使事情变得过于复杂和complex肿。除了使用GCC之外,还有其他选择可以在 C 中开发16位代码,例如Open Watcom C/C++或Alexey Frunze的Smaller C编译器。
答案 1 :(得分:0)
如果操作失败,ah
中的值将会更改。您的代码假定它不会被更改。