[bits 32]
FD_COMMAND_SEEK = 0Fh
FD_COMMAND_WRITESECTOR = 45h
FD_COMMAND_READSECTOR = 46h
FD_COMMAND_READID = 4Ah
FD_COMMAND_FORMATTRACK = 4Dh
// Reset controller
// DESTROY: ax, dx
h_fd_p_reset:
mov al, 0
mov dx, 3F2h
out dx, al
ret
// Start motor
// IN: al = fdnumber
// DESTROY: ax, cx, dx
h_fd_p_start_motor:
and al, 3
mov cl, al
mov al, 10h
shl al, cl
or al, 8 + 4
or al, cl
mov dx, 3F2h
out dx, al
ret
// Stop motor
// DESTROY: ax, dx
h_fd_p_stop_motor:
mov al, 8 + 4
mov dx, 3F2h
out dx, al
ret
// Wait0
// DESTROY: ax, dx
h_fd_p_wait0:
mov dx, 3F4h
l_h_fd_p_wait0_1:
//...
in al, dx
and al, 0C0h
cmp al, 80h
jnz l_h_fd_p_wait0_1
ret
// Wait1
// DESTROY: ax, dx
h_fd_p_wait1:
mov dx, 3F4h
l_h_fd_p_wait1_1:
//...
in al, dx
and al, 0C0h
cmp al, 0C0h
jnz l_h_fd_p_wait1_1
ret
// BEFORE: wait0, motor started
// IN: fdt_diskn, bh = head, cl = cylinder
// DESTROY: ax, dx
h_fd_p_seek:
mov dx, 3F5h
mov al, FD_COMMAND_SEEK
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov ah, fdt_diskn
mov al, bh
and al, 1
shl al, 2
or al, ah
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, cl
out dx, al
ret
// BEFORE: wait0, motor started
// IN: al = command, cl = cylinder, bh = head, bl = sector, fdt_diskn, fd_tracklen, fd_gap3
// OUT: al = 0 | errorcode
// DESTROY: ax, cx, dx, esi
h_fd_p_iocommand:
mov dx, 3F5h
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov ah, fdt_diskn
mov al, bh
and al, 1
shl al, 2
or al, ah
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, cl
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, bh
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, bl
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, 2
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, fd_tracklen
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, fd_gap3
out dx, al
call h_fd_p_wait0
mov dx, 3F5h
mov al, 0FFh
out dx, al
// Wait, read result
call h_fd_p_wait1
mov dx, 3F5h
in al, dx
mov ah, al
call h_fd_p_wait1
mov dx, 3F5h
in al, dx
mov cl, al
call h_fd_p_wait1
mov dx, 3F5h
in al, dx
mov ch, al
call h_fd_p_wait1
mov dx, 3F5h
in al, dx
call h_fd_p_wait1
mov dx, 3F5h
in al, dx
call h_fd_p_wait1
mov dx, 3F5h
in al, dx
call h_fd_p_wait1
mov dx, 3F5h
in al, dx
//... Project ah, cl, ch -> al
mov al, 0
ret
// BEFORE: wait0, motor started
// IN: fdt_diskn, cl = cylinder, bh = head, bl = sector, edi = target DMA, fd_*
// OUT: al = 0 | errorcode
// DESTROY: ax, cx, dx, esi
h_fd_p_read0:
// Prepare DMA
push ebx
push ecx
mov al, 2
mov ah, DMA_MODE_SINGLE | DMA_MODE_READ
mov ebx, edi
mov ecx, 512
call h_dma_read
pop ecx
pop ebx
test al, al
jz l_h_fd_p_read0_3
ret
l_h_fd_p_read0_3:
// Read sector
mov al, FD_COMMAND_READSECTOR
jmp h_fd_p_iocommand
// BEFORE: wait0, motor started
// IN: fdt_diskn, cl = cylinder, bh = head, bl = sector, edi = source DMA, fd_*
// OUT: al = 0 | errorcode
// DESTROY: ax, cx, dx, esi
h_fd_p_write0:
// Prepare DMA
push ebx
push ecx
mov al, 2
mov ah, DMA_MODE_SINGLE | DMA_MODE_WRITE
mov ebx, edi
mov ecx, 512
call h_dma_write
pop ecx
pop ebx
test al, al
jz l_h_fd_p_write0_3
ret
l_h_fd_p_write0_3:
// Write sector
mov al, FD_COMMAND_WRITESECTOR
jmp h_fd_p_iocommand
HD_PORT_DATA = 0x1F0
HD_PORT_FEATURES = 0x1F1
HD_PORT_ERROR = 0x1F1
HD_PORT_COUNT = 0x1F2
HD_PORT_SECTOR = 0x1F3
HD_PORT_CYLLOW = 0x1F4
HD_PORT_CYLHIGH = 0x1F5
HD_PORT_DRIVEHEAD = 0x1F6
HD_PORT_COMMAND = 0x1F7
HD_PORT_STATUS = 0x1F7
HD_STATUS_BUSY = 0x80
HD_STATUS_DATAREADY = 8
HD_STATUS_ERROR = 1
HD_STATUS_FAULT = 0x20
ATA_CMD_READ = 0x20
ATA_CMD_WRITE = 0x30
ATA_CMD_EXT_READ = 0x24
ATA_CMD_EXT_WRITE = 0x34
ATA_CMD_DMA_EXT_READ = 0x25
ATA_CMD_DMA_EXT_WRITE = 0x35
ATA_CMD_FLUSH = 0xE7
ATA_CMD_IDENTIFY = 0xEC
ERC_HARDWARE = 2
uns1 hdt_slave_bit
uns2 hdt_port_base
// IN: al = physical disk number (0..3)
// OUT: hdt_slave_bit, hdt_port_base
h_hd_prepare_port_slave:
push eax
and al, 1
shl al, 4
mov hdt_slave_bit, al // hdt_slave_bit = (pdn & 1) << 4
pop eax
test al, 2
jz l_h_hd_prepare_port_slave_2
mov hdt_port_base, 170h
jmp short l_h_hd_prepare_port_slave_3
l_h_hd_prepare_port_slave_2:
mov hdt_port_base, 1F0h
l_h_hd_prepare_port_slave_3:
ret
// IN: edx:ebx = sector, ecx = count, hdt_slave_bit, hdt_port_base
// OUT: al = command_base, dx = port_base, hdt_count_remain
// DESTROY: eax, ecx, edx, hdt_high
h_hd_p_mklba:
test edx, edx
jnz l_h_hd_p_mklba_48
cmp ebx, 0x10000000
jnb l_h_hd_p_mklba_48
// 28
mov eax, ebx
shr eax, 24
and al, 15
or al, hdt_slave_bit
or al, 0xE0
mov dx, hdt_port_base
or dl, 6 // dx = HD_PORT_DRIVEHEAD (6)
out dx, al
mov eax, ecx
cmp eax, 256
jb l_h_hd_p_mklba_281
mov eax, 255
l_h_hd_p_mklba_281:
sub ecx, eax
mov hdt_count_remain, ecx
mov ecx, eax
sub dl, 4 // dx = HD_PORT_COUNT (2)
out dx, al
mov al, bl
inc dl // dx = HD_PORT_SECTOR (3) / LBA low
out dx, al
mov al, bh
inc dl // dx = HD_PORT_CYLLOW (4) / LBA mid
out dx, al
mov eax, ebx
shr eax, 16
inc dl // dx = HD_PORT_CYLHIGH (5) / LBA high
out dx, al
sub dl, 5 // dx = PORT_BASE (0)
mov al, 0
ret
// 48
l_h_hd_p_mklba_48:
mov eax, ebx
shr eax, 24
shl edx, 8
or eax, edx
mov hdt_high, eax
mov al, 0xE0
or al, hdt_slave_bit
mov dx, hdt_port_base
or dl, 6 // dx = HD_PORT_DRIVEHEAD (6)
out dx, al
mov eax, ecx
cmp eax, 65536
jb l_h_hd_p_mklba_481
mov eax, 65535
l_h_hd_p_mklba_481:
sub ecx, eax
mov hdt_count_remain, ecx
mov ecx, eax
mov al, ah
sub dl, 4 // dx = HD_PORT_COUNT (2)
out dx, al
mov eax, hdt_high
inc dl // dx = HD_PORT_SECTOR (3) / LBA low
out dx, al
shr eax, 8
inc dl // dx = HD_PORT_CYLLOW (4) / LBA mid
out dx, al
mov al, ah
inc dl // dx = HD_PORT_CYLHIGH (5) / LBA high
out dx, al
mov al, cl
sub dl, 3 // dx = HD_PORT_COUNT (2)
out dx, al
mov eax, ebx
inc dl // dx = HD_PORT_SECTOR (3)
out dx, al
shr eax, 8
inc dl // dx = HD_PORT_CYLLOW (4)
out dx, al
mov al, ah
inc dl // dx = HD_PORT_CYLHIGH (5)
out dx, al
sub dl, 5 // dx = PORT_BASE (0)
mov al, 4
ret
// IN: edx:ebx = sector, ecx = count, es:edi -> buffer, hdt_slave_bit,
hdt_port_base
h_hd_read:
call h_hd_p_mklba
or al, ATA_CMD_READ
or dl, 7 // dx = HD_PORT_COMMAND (7)
out dx, al
l_h_hd_read5:
or dl, 7 // dx = HD_PORT_STATUS (7)
in al, dx
test al, HD_STATUS_BUSY
jz l_h_hd_read6
//... noop
jmp l_h_hd_read5
l_h_hd_read6:
test al, HD_STATUS_ERROR | HD_STATUS_FAULT
jnz l_h_hd_error
push ecx
and dl, 0F8h // dx = HD_PORT_DATA (0)
mov ecx, 512/4
cld
rep insd
pop ecx
dec ecx
test ecx, ecx
jnz l_h_hd_read5
xor eax, eax
ret
l_h_hd_error:
mov dx, hdt_port_base
or dl, 1 // dx = HD_PORT_ERROR (1)
in al, dx
mov ah, al
mov al, ERC_HARDWARE
ret
// IN: edx:ebx = sector, ecx = count, es:edi -> buffer, hdt_slave_bit,
hdt_port_base
h_hd_write:
call h_hd_p_mklba
or al, ATA_CMD_WRITE
or dl, 7 // dx = HD_PORT_COMMAND (7)
out dx, al
push ds
push esi
mov ax, es
mov esi, edi
mov ds, ax
l_h_hd_write5:
or dl, 7 // dx = HD_PORT_STATUS (7)
in al, dx
test al, HD_STATUS_BUSY
jz l_h_hd_write6
//... noop
jmp l_h_hd_write5
l_h_hd_write6:
test al, HD_STATUS_ERROR | HD_STATUS_FAULT
jnz l_h_hd_write_error
push ecx
and dl, 0F8h // dx = HD_PORT_DATA (0)
mov ecx, 512/4
cld
l_h_hd_write71:
outsd
jmp short l_h_hd_write76
l_h_hd_write76:
loop l_h_hd_write71
pop ecx
dec ecx
test ecx, ecx
jnz l_h_hd_write5
mov edi, esi
pop esi
pop ds
xor eax, eax
ret
l_h_hd_write_error:
pop esi
pop ds
jmp l_h_hd_error
ATA_CMD_PACKET = 0xA0
SCSI_COMMAND_READ10 = 28h
byte cd_packet[12], cd_buffer[2048]
// IN: ebx = sector number, hdt_port_base, hdt_slave_bit
// hdt_slave_bit: 0 (master) || 10h (slave)
// hdt_port_base: 1F0h (primary) || 170h (secondary)
// KEEP: ebx, ecx, edi
h_cd_p_read:
mov word ptr cd_packet[0], SCSI_COMMAND_READ10
mov eax, ebx
shr eax, 16
mov byte ptr cd_packet[2], ah
mov byte ptr cd_packet[3], al
mov byte ptr cd_packet[4], bh
mov byte ptr cd_packet[5], bl
mov byte ptr cd_packet[6], 0
mov byte ptr cd_packet[7], 1 / 256
mov byte ptr cd_packet[8], 1 & 255
mov byte ptr cd_packet[9], 0
mov word ptr cd_packet[10], 0
push ebx
push ecx
mov dx, hdt_port_base
mov al, hdt_slave_bit
or dl, 6 // dx = HD_PORT_DRIVEHEAD (6)
out dx, al
sub dl, 5 // dx = 1
mov al, 0 // 0: PIO, 1:DMA
out dx, al
add dl, 3 // dx = 4
mov al, 2048 & 255
out dx, al
inc dl // dx = 5
mov al, 2048 >> 8
out dx, al
mov al, ATA_CMD_PACKET
or dl, 7 // dx = HD_PORT_COMMAND (7)
out dx, al
l_h_cd_p_read_5:
or dl, 7 // dx = HD_PORT_STATUS (7)
in al, dx
test al, HD_STATUS_BUSY
jz l_h_cd_p_read_6
//... noop
jmp l_h_cd_p_read_5
l_h_cd_p_read_6:
test al, HD_STATUS_ERROR | HD_STATUS_FAULT
jnz l_h_cd_p_read_error
// write packet
and dl, 0F8h // dx = HD_PORT_DATA (0)
mov esi, offset cd_packet
mov ecx, 6
cld
rep outsw
// wait
l_h_cd_p_read_7:
or dl, 7 // dx = HD_PORT_STATUS (7)
in al, dx
test al, HD_STATUS_BUSY
jz l_h_cd_p_read_8
//... noop
jmp l_h_cd_p_read_7
l_h_cd_p_read_8:
test al, HD_STATUS_ERROR | HD_STATUS_FAULT
jnz l_h_cd_p_read_error
// read byte count
and dl, 0F8h
add dl, 4 // dx = HD_PORT_CYLLOW (4) / LBA mid
in al, dx
mov cl, al
inc dl // dx = HD_PORT_CYLHIGH (5) / LBA high
in al, dx
mov ch, al
movzx ecx, cx // assert ecx = 2048
cmp ecx, 800h
jz l_h_cd_p_read_85
mov al, ERC_UNKNOWN_GEOMETRY
pop ecx
pop ebx
jmp short l_h_cd_p_read_9
l_h_cd_p_read_85:
// read data
push edi
and dl, 0F8h // dx = HD_PORT_DATA (0)
mov edi, offset cd_buffer // es:edi
shr ecx, 2
rep insd
pop edi
pop ecx
pop ebx
mov al, 0
l_h_cd_p_read_9:
ret
l_h_cd_p_read_error:
pop ecx
pop ebx
jmp l_h_hd_error