summaryrefslogtreecommitdiff
path: root/drive/+bios/drive.ha
blob: da337401fc1d7abd813734e592dc40eea3f9f32f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use bios;

def sector_size: u32 = 512;

// 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;
	};
	
};