diff options
Diffstat (limited to 'bios/drive/drive.ha')
| -rw-r--r-- | bios/drive/drive.ha | 74 |
1 files changed, 70 insertions, 4 deletions
diff --git a/bios/drive/drive.ha b/bios/drive/drive.ha index 35cccca..a2992b8 100644 --- a/bios/drive/drive.ha +++ b/bios/drive/drive.ha @@ -1,5 +1,71 @@ -export let @symbol("drive_no") drive_no: u8; -export let @symbol("drive_spt") drive_spt: u8; -export let @symbol("drive_heads") drive_heads: u8; +use bios; -export fn read() void; +def sector_size: u32 = 512; + +export type error = !void; + +// Converts lba to the CHS format +fn lbatochs(lba: u32) (u16, u16, u16) = { + const temp = lba / bios::drive_sectors_per_track; + const sector = (lba % bios::drive_sectors_per_track) + 1; + const head = temp % bios::drive_heads; + const cylinder = temp / bios::drive_heads; + + return (cylinder: u16, head: u16, sector: u16); +}; + +// Reads the sector at lba and place it into the workspace buffer +fn bios_read(lba: u32) (void | error) = { + const chs = lbatochs(lba); + const readcount = len(bios::ws): u32 / sector_size; + + // AH=0x2 (read disk) AL=1 (read 1 sector) + bios::regs.eax = 0x2 << 8 | readcount; + + // CH=cylinder CL=sector + bios::regs.ecx = chs.0 << 8 | chs.2; + + // DH=head DL=driveno + bios::regs.edx = chs.1 << 8 | bios::drive_number; + + let ws = (&bios::ws): uintptr; + bios::regs.es = (ws / 16): u16; + bios::regs.ebx = (ws % 16): u32; + + bios::call(0x13); + + if (bios::regs.eax != readcount) { + return error; + }; +}; + +export fn read(addr: u32, count: size, dest: *[*]u8) (void | error) = { + // the amount of sectors fitting in the workspace (cannot be make a global constant yet, an assertion fails) + const readcount = len(bios::ws): u32 / sector_size; + + // the starting lba + let lba = addr / sector_size; + // the reading head (for starting to read bytes not aligned with the sector) + let head = addr % len(bios::ws); + + let cursor = 0z; + + for (true) { + bios_read(lba)?; + + for (head < len(bios::ws)) { + // XXX: use memmove + dest[cursor] = bios::ws[head]; + + head += 1; + cursor += 1; + if (cursor >= count) { + return; + }; + }; + + head = 0; + lba += readcount; + }; + +}; |
