コードには、適切な GCC の基本インライン アセンブリと拡張インライン アセンブリの観点からいくつかの重大な問題がありますが、基本的に論理ブロック アドレス 19 (LBA) へのアクセスに関する問題は計算にあります。LBA 19 はCHS (Cylinder, Head, Sector) = (0, 1, 2) であり、OP はそれが(Head: 0, Track: 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への計算については、セクションLBA から CHS への変換 のこの他のStackoverflow の回答で詳しく説明しています。
観測結果の 1 つは、最大セクター数が方程式にまったく考慮されていないことです。ここでの本当の問題は、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 未満の 1 つまたは複数のセクターの読み取りを開始すると、正しくなります。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;
LBA から CHS への方程式をテストするコード
#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;
}
インライン アセンブリを使用してディスク読み取りを行うサンプル GCC コード
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 で 2 段階のブートローダーを作成する小さな概念実証プロジェクトは、私の Web サイトにあります。
ノート
Int 13h/AH=0hは、ディスク システムをリセットします。この操作は、ドライブ ヘッドの再調整も行うため、フロッピー ディスクなどの実際のハードウェアではかなりの時間がかかる場合があります。エラーが検出された後、ディスク操作を再試行する前にのみ、ディスクをリセットする必要があります。
GCC を使用してリアルモードで実行されるコードを作成することは、せいぜい問題です。で生成されたコードは、-m16
通常、80386 以降のプロセッサでのみ実行できます。
コンパイラは、複数asm
のステートメントがコードに表示される順序で発行されることを保証しません。複数のasm
ステートメントを 1 つに結合する必要があります。GCC のドキュメントには次のように書かれています。
volatile 修飾子を使用している場合でも、一連のasmステートメントがコンパイル後に完全に連続しているとは思わないでください。出力で特定の命令を連続させておく必要がある場合は、それらを単一の複数命令asmステートメントに入れます。