I've been running this implementation on my laptop for a while now, and it hasn't shown any signs of errors or corruption, so I believe its ready to be shared here.
This is a PIO driver, it polls the disk instead of dispatching to, say, the DMA controller. It works with blocks, 1024 kb chunks, at a time while the HDD supports sectors, 512 kb chunks, so I am reading/writing 2 sectors per operation.
First to enable PIO mode, I set the nIEN bit of the device control register (this will disable interrupts from the HDD):
2 3F6 out
I'm using an unrolled loop to run words 256 times, so I'll define it here:
:cell _i
: i _i @ >r ;
: 8i i i i i i i i i ;
: 40i 8i 8i 8i 8i 8i 8i 8i 8i ;
: 100i 40i 40i 40i 40i ;
: 100* r> _i ! jump 100i ;
Next is some interfacing words for the ATA registers:
: data 1F0 in2 ;
: data! 1F0 out2 ;
: ERROR 1F1 in ;
: double 2 1F2 out ;
: SECTOR! 1F3 out ;
: LOW! 1F4 out ;
: HIGH! 1F5 out ;
: STATUS 1F7 in ;
: ALT 3F6 in ;
Block selection:
:cell target
: BLOCK! dup target ! dup 2* SECTOR! 7 >> dup LOW! 8 >> HIGH! ;
ATA state machine words:
80 :const BSY
8 :const DRQ
: active? STATUS 88 && ;
: error target @ 1+ num ERROR num jump ;
: poll active? if> BSY && when jump poll else 52 emit jump error ;
: cycle ALT drop ;
: 400ns cycle cycle cycle cycle ;
: wait STATUS 40 && unless jump wait ;
: command wait 1F7 out 400ns ;
Block reading:
:cell addr
: addr! addr @ !+ addr ! ;
: read-all 100* data addr! ;
: read double 20 command poll read-all 400ns poll read-all 400ns STATUS drop ;
: block> addr ! BLOCK! read ;
And block writing:
: addr@ addr @ @+ addr ! ;
21 :const ERR
: poll STATUS ERR && if> 57 emit num jump error else STATUS BSY && when jump poll ;
: write-all 100* addr@ data! ;
: write double 30 command poll write-all 400ns poll write-all 400ns poll ;
: >block BLOCK! addr ! write E7 command poll ;
All in all I believe it does the job well, using 37 words in total, and has been functioning reliably for the past couple weeks.