Date : Sun, 30 Oct 2005 00:55:57 +0100
From : Tom Seddon <tom@...>
Subject: Re: BBC using 3.5 high density format
Greg Cook wrote:
> Supposing the HD controller board only raises NMI on a data request (by
> not connecting INTRQ), all the service routine need do is fetch the
> value, store it and increment the address. To do a disc operation we
> issue a single sector command then poll the status register and the
> ESCAPE flag until the operation is terminated. The concept has been
> demonstrated in EDOS and has worked for me flawlessly.
>
> For more speed, we can rewrite the EDOS polling loop to disable
> interrupts and use only the Y register, then the NMI routine can use
> indexed addressing without saving A or X. (The accumulator is needed
> as there is no indexed, absolute STX or STY instruction.) This does
> however mean providing a 128 byte lookup table to replace ROR.
>
>
>>That's not many 6502 instructions, and half as many at HD,
>
>
> The nominal data rate at high density is 32 cycles per byte at 2 MHz; a
> minimal NMI routine will keep up with such a controller, or at least
> stay re-entrant under worst case timings. The routine for reading
> could be, for instance:
>
> .nmir LDA fdc_data
> .nmira STA target,X \target MOD 256 = 0
> INX
> BEQ nmirb
> RTI
> .nmirb INC nmira+2
> RTI
I have an idea.
You could save off the pre-NMI stack pointer, and replace the RTI with
TXS. This would be a saving of 5 cycles over RTI. The end of the NMI
routine would then be a series of NOPs, followed by a JMP back to where
you came from in the ROM. For every byte other than the last, the NMI
routine would then be re-entered before it got there. The return address
on the stack would generally be bogus, but you don't care, because you
don't use it -- and the TXS has anyway restored the stack pointer to the
value it had before any NMIs happened at all.
According to my calculations, you could then relax the page-alignment
requirement, so you can if possible read a whole track directly into its
final location. For reading less than one track, though, you'll have to
read sector-by-sector, and you'll need an intermediate buffer if reading
less than a whole sector, for there's no time to cancel the operation
once it's started :-|
The NMI routine would go a bit like this:
\ +7 (7) -- NMI overhead
&D00 LDA FDC_DATA \ +6 (13)
STA (&C0),Y \ +6 (19)
INY \ +2 (21)
BNE NO_BUMP:INC &C1 \ +7 (worst case) 28 <-- point A
.NO_BUMP TXS \ +2 (30)
NOP \ +2 (32)
NOP:NOP:NOP \ +6 (cater for best-case timings)
NOP \ the lucky NOP
JMP READ_END
(At point A, you'd otherwise have no time for the RTI, because you'd be
3 cycles out by the time it finished. This would eventually throw the
timings out of whack.)
This gives you enough time -- just -- to cope with writing data into the
1MHz I/O area. That's the rationale behind the extra NOPs: whilst I've
listed the worst-case timings (writing a byte into the I/O area on the
"wrong" cycle, combined with bumping the current page), in the best case
you've got these extra 6 cycles.
Due to the interrupts' having being cleared, you should be guaranteed
that ROMSEL is preserved during the operation, so there's no need to
restore it before returning to the ROM routine.
Note also the extra 2 cycles for the initial LDA; I can't remember
whether reads incur cycle stretching when fetching bytes from the FDC,
so I added in 2 anyway. That's what my lucky NOP is for :)
The DFS code would then be something like:
SEI
\ copy in NMI code
\ store destination address in &C0
LDY #0
TSX
LDA #XX:STA FDC_CTRL \ hand-wavy "start operation" bit
.WAIT JMP WAIT
.READ_END
CLI
\ determine whether we're here because of an error,
\ or because the operation completed successfully
With apologies for the hand-waving details about how you start the
actual operation off... I can't remember that bit...
--Tom
P.S. I'm pretty sure that the timings would hold up for writing to disc
too. In fact, in the absence of a page crossing, I think you'd be 1
cycle better off... better add another lucky NOP ;)