[comp.sys.amiga.programmer] Direct hardware drive access

bairds@eecs.cs.pdx.edu (Shawn L. Baird) (03/30/91)

With the recent flamage/discussion I've revitalized some of my interest
in trying my hand at direct disk reading. I've included a small snippet
of code and will say that I can't even get the drive light to come on.
The code enters with Disable() having been called and all DMA turned
off (I'm not using any disk DMA for stepping out to track 0, so this
shouldn't be a problem should it?). The goal is to step the disk out to
track 0.

		; step to the outside of the disk in drive 0 (I wish)
		and.b	#$7f,ciaaprb		; select motor
		and.b	#$f7,ciaaprb		; select drive 0
diskready:	btst.b	#5,ciaapra		; check DSKRDY
		beq.s	diskready
		or.b	#$02,ciaaprb		; set step direction
checktrack0:	move.l	#3000,d0
		jsr	wait
		btst.b	#4,ciaapra		; check DSKTRACK0
		bne.s	disktrack0
		and.b	#$fe,ciaaprb		; step out
		or.b	#$01,ciaaprb
		bra.s	checktrack0
disktrack0:	or.b	#$88,ciaaprb		; unselect drive 0

The subroutine wait uses the cia timer to wait d0 microseconds and works
as far as I can tell. Didn't someone around here claim to have worked on,
or developed entirely, the trackdisk.device for 1.3 and 2.0? If so, help!

---
 Shawn L. Baird, bairds@eecs.ee.pdx.edu, Wraith on DikuMUD
 The above message is not licensed by AT&T, or at least, not yet.

mykes@amiga0.SF-Bay.ORG (Mike Schwartz) (03/31/91)

In article <2147@pdxgate.UUCP> bairds@eecs.cs.pdx.edu (Shawn L. Baird) writes:
>With the recent flamage/discussion I've revitalized some of my interest
>in trying my hand at direct disk reading. I've included a small snippet
>of code and will say that I can't even get the drive light to come on.
>The code enters with Disable() having been called and all DMA turned
>off (I'm not using any disk DMA for stepping out to track 0, so this
>shouldn't be a problem should it?). The goal is to step the disk out to
>track 0.
>
>		; step to the outside of the disk in drive 0 (I wish)
>		and.b	#$7f,ciaaprb		; select motor
>		and.b	#$f7,ciaaprb		; select drive 0
>diskready:	btst.b	#5,ciaapra		; check DSKRDY
>		beq.s	diskready
>		or.b	#$02,ciaaprb		; set step direction
>checktrack0:	move.l	#3000,d0
>		jsr	wait
>		btst.b	#4,ciaapra		; check DSKTRACK0
>		bne.s	disktrack0
>		and.b	#$fe,ciaaprb		; step out
>		or.b	#$01,ciaaprb
>		bra.s	checktrack0
>disktrack0:	or.b	#$88,ciaaprb		; unselect drive 0
>
>The subroutine wait uses the cia timer to wait d0 microseconds and works
>as far as I can tell. Didn't someone around here claim to have worked on,
>or developed entirely, the trackdisk.device for 1.3 and 2.0? If so, help!
>
>---
> Shawn L. Baird, bairds@eecs.ee.pdx.edu, Wraith on DikuMUD
> The above message is not licensed by AT&T, or at least, not yet.


Try the following routines, instead.  Notice that 'structured' programming
techniques are just as doable in assembler as they are in 'C'.  Note that
when coding in assembler, your code is pretty optimal.  In particular, notice
that rather than saying:
	bsr	StepDelay
	rts
You can use
	bra	StepDelay
Which saves 34 clock cycles and 2 bytes.



tdError		dc.l	0		; result of last operation
tdDrive		dc.b	0		; selected drive
tdDriveBits	dc.b	0		; bits for CIA for selected drive
		EVEN

SetDrive0	clr.b	tdDrive
		move.b	#%00110000,tdDriveBits
		rts
SetDrive1	move.b	#1,tdDrive
		move.b	#%00101000,tdDriveBits
		rts
SetDrive2	move.b	#2,tdDrive
		move.b	#%00011000,tdDriveBits
		rts

; Turn Amiga drive motor on:
MotorOn		clr.l	tdError
		lea	CIAB,a0
		move.b	#%11111111,(a0)
		move.b	#%01111111,(a0)
		move.b	#%01000111,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
	; wait for disk to come up to speed
		move.l	d4,-(sp)
		move.w	#750/3,d4
.wait		bsr	StepDelay
		dbra	d4,.wait
		move.l	(sp)+,d4
		move.l	tdError,d0
		rts

; Turn Amiga drive motor off:
MotorOff	move.l	#CIAB,a0
		move.b	#%11111111,(a0)
		move.b	#%10000111,(a0)
		move.b	#%11111111,(a0)
		rts

; Use CIA A, Timer A, to delay at least 3Ms.
StepDelay	move.l	d0,-(sp)
	 	move.b	CRAA,d0
		and.b	#%11000000,d0
		or.b	#%00001000,d0
		move.b	d0,CRAA
		move.b	#%01111111,ICRA
		move.b	#(2148&255),TALOA
		move.b	#(2148>>8),TAHIA
.wait		btst	#0,ICRA
		beq.s	.wait
		move.l	(sp)+,d0
		rts

; Step toward track 80:
StepRight	move.l	#CIAB,a0
		move.b	#%01000111,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
		move.b	#%01000100,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
		move.b	#%01000101,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
		move.b	#%01000111,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
		bra	StepDelay

; Step toward track 00 (outermost physical track)
StepLeft	move.l	#CIAB,a0
		move.b	#%01000111,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
		move.b	#%01000110,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
		move.b	#%01000111,d0
		or.b	tdDriveBits,d0
		move.b	d0,(a0)
		bra	StepDelay

--
********************************************************
* Appendix A of the Amiga Hardware Manual tells you    *
* everything you need to know to take full advantage   *
* of the power of the Amiga.  And it is only 10 pages! *
********************************************************

jesup@cbmvax.commodore.com (Randell Jesup) (03/31/91)

In article <2147@pdxgate.UUCP> bairds@eecs.cs.pdx.edu (Shawn L. Baird) writes:
>With the recent flamage/discussion I've revitalized some of my interest
>in trying my hand at direct disk reading. I've included a small snippet
>of code and will say that I can't even get the drive light to come on.
>The code enters with Disable() having been called and all DMA turned
>off (I'm not using any disk DMA for stepping out to track 0, so this
>shouldn't be a problem should it?). The goal is to step the disk out to
>track 0.

	I assume you've taken over the machine, or you've called GetUnit()
to obtain control of the disk hardware.

>		; step to the outside of the disk in drive 0 (I wish)
>		and.b	#$7f,ciaaprb		; select motor

	I would use #~CIAF_DSKMOTOR, that way you don't have to remember
the value of 15 million hardware bits.

>		and.b	#$f7,ciaaprb		; select drive 0

	This should be #~(CIAF_DSKMOTOR!CIAF_DSKSEL0) - you're in a timing
race to see if dskmotor goes high before the disk notices it has been selected.

>diskready:	btst.b	#5,ciaapra		; check DSKRDY
>		beq.s	diskready
>		or.b	#$02,ciaaprb		; set step direction
>checktrack0:	move.l	#3000,d0

	For safety when doing calibrates, we usually use 4ms.

>		jsr	wait
>		btst.b	#4,ciaapra		; check DSKTRACK0
>		bne.s	disktrack0
>		and.b	#$fe,ciaaprb		; step out
>		or.b	#$01,ciaaprb
>		bra.s	checktrack0
>disktrack0:	or.b	#$88,ciaaprb		; unselect drive 0

	If track 0 is set when you go into calibration, you must step in until
the track-0 indication goes away, delay a settle time (15ms on top of the
4ms step), and then reverse direction and search for track 0).

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
Thus spake the Master Ninjei: "To program a million-line operating system
is easy, to change a man's temperament is more difficult."
(From "The Zen of Programming")  ;-)

jesup@cbmvax.commodore.com (Randell Jesup) (03/31/91)

In article <mykes.0840@amiga0.SF-Bay.ORG> mykes@amiga0.SF-Bay.ORG (Mike Schwartz) writes:
>	bsr	StepDelay
>	rts
>You can use
>	bra	StepDelay
>Which saves 34 clock cycles and 2 bytes.

	One of the oldest optimizations known to mankind (and compilers with
tail-recursion/etc optimizations will do this too - even a simple peephole
optimizer can catch this).

>; Turn Amiga drive motor on:
>MotorOn	clr.l	tdError
>		lea	CIAB,a0
>		move.b	#%11111111,(a0)
>		move.b	#%01111111,(a0)
>		move.b	#%01000111,d0
>		or.b	tdDriveBits,d0
>		move.b	d0,(a0)
>	; wait for disk to come up to speed
>		move.l	d4,-(sp)
>		move.w	#750/3,d4
>.wait		bsr	StepDelay
>		dbra	d4,.wait

	First, the persons code you replied to was far better than this.
You're waiting .75 seconds (750 ms) on every motor-on.  We spec that floppy
drives on the Amiga MUST be up to speed in .5 seconds.  Also, the disk hardware
as a ready signal, to tell you when it's up to speed.  Many drives come up to
speed far faster than .5 seconds, or may have just been turned off and are
still close to speed, etc, or may have been on already, etc.  The person who
you responded to looped checking dskrdy.  The best way to to wait .5 seconds,
checking dskrdy every so often (2.0 trackdisk checks it every .1 seconds).

>		move.l	(sp)+,d4
>		move.l	tdError,d0
>		rts
>
>; Turn Amiga drive motor off:
>MotorOff	move.l	#CIAB,a0
>		move.b	#%11111111,(a0)
>		move.b	#%10000111,(a0)
>		move.b	#%11111111,(a0)
		^^^^^^^^^^^^^^^^^^^^  not needed, doesn't hurt
BTW, you could have coded that routine 2 bytes shorter and 12 cycles shorter,
if you care so much about space/speed.

>		rts
...
>; Step toward track 80:
>StepRight	move.l	#CIAB,a0
>		move.b	#%01000111,d0
			   ^		Interesting, that's DSKSEL3
					Ever test this code with 4 drives?
			        ^	Interesting, that's outwards (toward 0)
					This stuff of CIAB is not needed.
					(though a different one is)
>		or.b	tdDriveBits,d0
>		move.b	d0,(a0)
>		move.b	#%01000100,d0
			   ^		Interesting, that's DSKSEL3
				^	Interesting, that's inwards (toward 80)
The hardware manual says DSKDIREC MUST be set correctly BEFORE step is set to
0.  You must be lucky not to have hit a drive that actually requires it yet.

>		or.b	tdDriveBits,d0
>		move.b	d0,(a0)
>		move.b	#%01000101,d0
			   ^		Interesting, that's DSKSEL3
>		or.b	tdDriveBits,d0
>		move.b	d0,(a0)
>		move.b	#%01000111,d0
			   ^		Interesting, that's DSKSEL3
				^	Interesting, that's outwards again
					This stuff of CIAB is not needed.
>		or.b	tdDriveBits,d0
>		move.b	d0,(a0)
>		bra	StepDelay

	And where is your settle routine?  (I suspect you merely didn't
include it - you need 15ms settle after finishing seeking (on top of the
step delay!)

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
Thus spake the Master Ninjei: "To program a million-line operating system
is easy, to change a man's temperament is more difficult."
(From "The Zen of Programming")  ;-)

bairds@eecs.cs.pdx.edu (Shawn L. Baird) (03/31/91)

Well, after examining the CIA specifications in appendix F of the Hardware
Reference Manual, I found a small misprint in Chapter 10, "Interface
Hardware", of the Hardware Reference Manual. On page 238 it says:

     Bits labeled "PB" are output bits located in CIAAPRB ($BFD100).
                                                  ^^^^^^^

This should be CIABPRB, for which the correct address is given. Upon
switching to this register and using tips provided by Randell Jesup, I
was able to get the motor to come on and the drive to spin (Hey, it's
more excitement than I've had in weeks!). Thanks both to Mike Schwartz
and Randell Jesup for their help.

Note: Even the top of this page says CIAAPRA/CIABPRB. In fact, everything
else says CIABPRB. Why I saw only the one little reference to CIAAPRA I
don't know, but I guess it seemed to make sense that the registers for
controlling and sensing the disk would be on the same CIA chip.

---
 Shawn L. Baird, bairds@eecs.ee.pdx.edu, Wraith on DikuMUD
 The above message is not licensed by AT&T, or at least, not yet.

bairds@eecs.cs.pdx.edu (Shawn L. Baird) (04/01/91)

Having finished in my attempts at stepping in and out tracks I have decided
to move on to the more difficult prospect of reading and (eventually)
writing tracks. With this move I have several questions:

   1. The documentation for DSKBYTR states that in read mode data is placed
      here one byte at a time. I assume this is before DMA is started? For
      the purposes of syncing or somesuch?

   2. DMAON is DSKBYTR does not refer to actual DMA activity, but rather
      that DMA is enabled?

   3. Is there any way to busy loop on the completion of disk DMA? (I
      realize that an interrupt is more elegant, but this is just a test
      program.)

   4. The hardware manual mentions bugs in both reading and writing data.
      Now, how am I expected to compensate for these bugs? For reading, I
      assume I request an extra word?

   5. I use DSKSYNC on $4489. Is this correct, or at least for most disks
      such as AmigaDOS disks? If $4489 isn't found does it keep searching
      forever?

   6. The ADKCON register mentions precomposition. Is there a value for
      precomposition that I should use with standard MFM data?

   7. Does anyone have examples of blitter routines which both encode and
      decode raw MFM tracks? The hardware manual mentions this as a
      possibility as have several people on Usenet. I suspect this
      involves some tricky operation with the line mode, with which I'm
      just not well enough adjusted to figure it out for myself.

If you are looking for a good article on just what MFM encoding itself is,
and GCR for that matter, take a look at _Transactor for the Amiga_ Vol. 3,
Issue 1, "Amiga Disk Encoding Schemes" by Betty Clay, pp. 70-73.

Oh, one last question. Has anybody taken a look at AC Tech? Is it very
technical? Are they planning on moving from quarterly to bi-monthly or
somesuch?

---
 Shawn L. Baird, bairds@eecs.ee.pdx.edu, Wraith on DikuMUD
 The above message is not licensed by AT&T, or at least, not yet.

mykes@amiga0.SF-Bay.ORG (Mike Schwartz) (04/01/91)

In article <20215@cbmvax.commodore.com> jesup@cbmvax.commodore.com (Randell Jesup) writes:
>In article <mykes.0840@amiga0.SF-Bay.ORG> mykes@amiga0.SF-Bay.ORG (Mike Schwartz) writes:
>>	bsr	StepDelay
>>	rts
>>You can use
>>	bra	StepDelay
>>Which saves 34 clock cycles and 2 bytes.
>
>	One of the oldest optimizations known to mankind (and compilers with
>tail-recursion/etc optimizations will do this too - even a simple peephole
>optimizer can catch this).
>

Some people who've never seen assembler language don't know about this.  They
*have* to rely upon their compilers to be good enough to do this for them.
I'd be interested to know exactly *what* compilers for the Amiga do this
optimization?

I'd also point out that sometimes debugging can be easier if you do
the bsr followed by RTS.  You lose the clock cycles and bytes, but
you gain the ability to "step over" the subroutine instead of having
to manually put a breakpoint at its RTS.

>>; Turn Amiga drive motor on:
>>MotorOn	clr.l	tdError
>>		lea	CIAB,a0
>>		move.b	#%11111111,(a0)
>>		move.b	#%01111111,(a0)
>>		move.b	#%01000111,d0
>>		or.b	tdDriveBits,d0
>>		move.b	d0,(a0)
>>	; wait for disk to come up to speed
>>		move.l	d4,-(sp)
>>		move.w	#750/3,d4
>>.wait		bsr	StepDelay
>>		dbra	d4,.wait
>
>	First, the persons code you replied to was far better than this.
>You're waiting .75 seconds (750 ms) on every motor-on.  We spec that floppy
>drives on the Amiga MUST be up to speed in .5 seconds.  Also, the disk hardware
>as a ready signal, to tell you when it's up to speed.  Many drives come up to
>speed far faster than .5 seconds, or may have just been turned off and are
>still close to speed, etc, or may have been on already, etc.  The person who
>you responded to looped checking dskrdy.  The best way to to wait .5 seconds,
>checking dskrdy every so often (2.0 trackdisk checks it every .1 seconds).
>

I check DSKRDY before reading/writing to the disk.  I waited a little extra time
deliberatly to help ensure that drives that are up to 50% out of spec work.  Better
safe than sorry.  And to help other developers who use AmigaMail:  The article
titled "Low Level Disk Access" by Bryce Nesbitt on page VIII-10/11 shows the following
routine:

motor_wait	btst.b	#5,CIAA_PRA	; check READY line
		bne.s	motor_wait	; busy wait until drive is ready
		rts

When I tried this on an A500 at EA, it didn't work at ALL.

>>		move.l	(sp)+,d4
>>		move.l	tdError,d0
>>		rts
>>
>>; Turn Amiga drive motor off:
>>MotorOff	move.l	#CIAB,a0
>>		move.b	#%11111111,(a0)
>>		move.b	#%10000111,(a0)
>>		move.b	#%11111111,(a0)
>		^^^^^^^^^^^^^^^^^^^^  not needed, doesn't hurt
>BTW, you could have coded that routine 2 bytes shorter and 12 cycles shorter,
>if you care so much about space/speed.
>
>>		rts
>...
>>; Step toward track 80:
>>StepRight	move.l	#CIAB,a0
>>		move.b	#%01000111,d0
>			   ^		Interesting, that's DSKSEL3
>					Ever test this code with 4 drives?

My mistake.  One of us (you, Bryce, and myself) should have caught this
one.  You saw these sources before we shipped.  I had cut these routines
from a set of MS-DOS/Atari ST format drivers someone else had written.

>			        ^	Interesting, that's outwards (toward 0)

Difference in terminology here, but Randall's usage is more correct.  It should
be IN instead of RIGHT, and OUT instead of LEFT.

>					This stuff of CIAB is not needed.
>					(though a different one is)

>>		or.b	tdDriveBits,d0
>>		move.b	d0,(a0)
>>		move.b	#%01000100,d0
>			   ^		Interesting, that's DSKSEL3
>				^	Interesting, that's inwards (toward 80)
>The hardware manual says DSKDIREC MUST be set correctly BEFORE step is set to
>0.  You must be lucky not to have hit a drive that actually requires it yet.
>

PEOPLE NOTE THIS!  There might not be a drive that requires it yet, but there
might be in the future.  EA has had ZERO returns or reports of any problems
with these routines, but it is always a good idea to do it right.

>>		or.b	tdDriveBits,d0
>>		move.b	d0,(a0)
>>		move.b	#%01000101,d0
>			   ^		Interesting, that's DSKSEL3
>>		or.b	tdDriveBits,d0
>>		move.b	d0,(a0)
>>		move.b	#%01000111,d0
>			   ^		Interesting, that's DSKSEL3
>				^	Interesting, that's outwards again
>					This stuff of CIAB is not needed.

Kinda picky about the lower 3 bits... is it WRONG?

>>		or.b	tdDriveBits,d0
>>		move.b	d0,(a0)
>>		bra	StepDelay
>
>	And where is your settle routine?  (I suspect you merely didn't
>include it - you need 15ms settle after finishing seeking (on top of the
>step delay!)
>

There is a routine called StepToTrack that calls these routines.  When
it has stepped to the desired track, it waits 15ms.

The original poster asked how to step the heads, not how to read/write
to the disk.

Another word of advice.  Do not use the book "Amiga Disk Drives: Inside
and Out" by Abacus.  I was told by Commodore when I was writing my routines
that the book is full of misinformation and was likely to cause people
to have disk driver problems.

>-- 
>Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
>{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
>Thus spake the Master Ninjei: "To program a million-line operating system
>is easy, to change a man's temperament is more difficult."
>(From "The Zen of Programming")  ;-)

--
********************************************************
* Appendix A of the Amiga Hardware Manual tells you    *
* everything you need to know to take full advantage   *
* of the power of the Amiga.  And it is only 10 pages! *
********************************************************

DXB132@psuvm.psu.edu (04/01/91)

In article <mykes.0972@amiga0.SF-Bay.ORG>, mykes@amiga0.SF-Bay.ORG (Mike
Schwartz) says:

>Another word of advice.  Do not use the book "Amiga Disk Drives: Inside
>and Out" by Abacus.  I was told by Commodore when I was writing my routines
>that the book is full of misinformation and was likely to cause people
>to have disk driver problems.

Correct. Although, I bought it because it DOES contain useful  information
...but read the hardware manual first and last!
(I really got a chuckle out of the example code that polled disk ready
after stepping the head or something. Real smart...)

-- Dan Babcock

jesup@cbmvax.commodore.com (Randell Jesup) (04/01/91)

In article <mykes.0972@amiga0.SF-Bay.ORG> mykes@amiga0.SF-Bay.ORG (Mike Schwartz) writes:
>I check DSKRDY before reading/writing to the disk.  I waited a little extra time
>deliberatly to help ensure that drives that are up to 50% out of spec work.  Better
>safe than sorry.  And to help other developers who use AmigaMail:  The article
>titled "Low Level Disk Access" by Bryce Nesbitt on page VIII-10/11 shows the following
>routine:
>
>motor_wait	btst.b	#5,CIAA_PRA	; check READY line
>		bne.s	motor_wait	; busy wait until drive is ready
>		rts
>
>When I tried this on an A500 at EA, it didn't work at ALL.

	Bug report???  It seems to work ok for us currently (2.0 trackdisk
checks ready every .1 seconds up to .5, a compromise).  What drives didn't
that code work on?  Did you tell bryce?

>>>; Step toward track 80:
>>>StepRight	move.l	#CIAB,a0
>>>		move.b	#%01000111,d0
>>			   ^		Interesting, that's DSKSEL3
>>					Ever test this code with 4 drives?
>
>My mistake.  One of us (you, Bryce, and myself) should have caught this
>one.  You saw these sources before we shipped.  I had cut these routines
>from a set of MS-DOS/Atari ST format drivers someone else had written.

	I never saw your routines, and Bryce is a busy person.  Things like
this are why we discourage the Abacus-style of programming (hard-coded
constants, no includes), and prefer and encourage use of symbolic references.
I would have written #(DSKSEL3!DSKSIDE!DSKDIREC!DSKSTEP), which makes it
obvious that DSKSEL3 has been selected, without having to look up or memorize
every bit in the hardware.  If he was using hardcoded constants, you should
have looked up the register definitions at least so you had some idea what
he was doing.

>>			        ^	Interesting, that's outwards (toward 0)
>
>Difference in terminology here, but Randall's usage is more correct.  It should
>be IN instead of RIGHT, and OUT instead of LEFT.

	I've never heard of left/right for disks.  It's always in/out.

>>>		move.b	#%01000100,d0
>>			   ^		Interesting, that's DSKSEL3
>>				^	Interesting, that's inwards (toward 80)
>>The hardware manual says DSKDIREC MUST be set correctly BEFORE step is set to
>>0.  You must be lucky not to have hit a drive that actually requires it yet.
>>
>
>PEOPLE NOTE THIS!  There might not be a drive that requires it yet, but there
>might be in the future.  EA has had ZERO returns or reports of any problems
>with these routines, but it is always a good idea to do it right.

	This sort of thing might not come to the attention of EA unless there
were a lot of them out there.  And as he says, there is no guarantee that
the code above will work with the drives that leave the factory tomorrow or
any other day.

>>>		move.b	#%01000111,d0
>>			   ^		Interesting, that's DSKSEL3
>>				^	Interesting, that's outwards again
>>					This stuff of CIAB is not needed.
>
>Kinda picky about the lower 3 bits... is it WRONG?

	It doesn't hurt anything, but it's spurious.  I thought you cared
about efficiency... :-)

>There is a routine called StepToTrack that calls these routines.  When
>it has stepped to the desired track, it waits 15ms.
>
>The original poster asked how to step the heads, not how to read/write
>to the disk.

	Actually he seemed to be recalibrating the heads to track 0.

>Another word of advice.  Do not use the book "Amiga Disk Drives: Inside
>and Out" by Abacus.  I was told by Commodore when I was writing my routines
>that the book is full of misinformation and was likely to cause people
>to have disk driver problems.

	Absolutely.  The code in the book doesn't even work with 1.3 roms,
unless they've revised it recently.  evil, evil, evil, IMHO.  NOTE: this
is my personal opinion, not an official opinion or statement of Commodore
(of course).

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
Thus spake the Master Ninjei: "To program a million-line operating system
is easy, to change a man's temperament is more difficult."
(From "The Zen of Programming")  ;-)

jesup@cbmvax.commodore.com (Randell Jesup) (04/01/91)

In article <2161@pdxgate.UUCP> bairds@eecs.cs.pdx.edu (Shawn L. Baird) writes:
>   1. The documentation for DSKBYTR states that in read mode data is placed
>      here one byte at a time. I assume this is before DMA is started? For
>      the purposes of syncing or somesuch?

	DIsk data is available in the register.  It's possible to read data
directly with the processor using this, but tricky and highly liable to
break on different processors/machines if it works at all.

>   2. DMAON is DSKBYTR does not refer to actual DMA activity, but rather
>      that DMA is enabled?

	Yup.

>   3. Is there any way to busy loop on the completion of disk DMA? (I
>      realize that an interrupt is more elegant, but this is just a test
>      program.)

	Sure:  just check for a DSKBLK interrupt in INTREQR.  A DSKBLK
interrupt that merely signals your task is trivial, though.

>   4. The hardware manual mentions bugs in both reading and writing data.
>      Now, how am I expected to compensate for these bugs? For reading, I
>      assume I request an extra word?

	Always request 1 more word than you know you need for read (not
a problem, make the maximum read 6814 instead of 6812).  On write, make
sure you have a word of $aaa8 OR $2aa8 (depending on the last bit of the
previous word).

>   5. I use DSKSYNC on $4489. Is this correct, or at least for most disks
>      such as AmigaDOS disks? If $4489 isn't found does it keep searching
>      forever?

	$4489 is the AmigaDos and MSDOS standard (it's an "illegal" mfm
value that is guaranteed by the hardware to be both readable and writable).

>   6. The ADKCON register mentions precomposition. Is there a value for
>      precomposition that I should use with standard MFM data?

	Look at the public unit structures.  Normal amiga disks use no
precomp on cylinders 0-39, 01 precomp on cylinders 40-79.  The values
are available from the public unit structure.

>   7. Does anyone have examples of blitter routines which both encode and
>      decode raw MFM tracks? The hardware manual mentions this as a
>      possibility as have several people on Usenet. I suspect this
>      involves some tricky operation with the line mode, with which I'm
>      just not well enough adjusted to figure it out for myself.

	It's rather tricky (decode is fairly simple, encode is 4-pass).
I advise playing with blitlab a bit.  

	Decode:
	((odd bits word & 0x5555) << 1) | (even bits word & 0x5555)
	This can all be done in one blitter pass.

	The encoding is done in four separate passes.  Two passes
	encode the odd bits, and two passes encode the even bits.  The
	first pass encodes the data bits and the clock bits.  The second
	pass corrects the clock bits of the first pass by unsetting the
	clock bit of a zero cell that follows a one cell.  In all four
	passes input C is a mask of $5555.  This mask will be used to
	"cookie cut" the input into clock and data bits.  In all cases
	C refers to data bits and C* to clock bits.

	Equations are:

		Pass 1: AC + B*C*  (A=odd data bits of the source aligned
to the odd data bits of the dest, B=source data bits aligned for the clock
bits of the dest.  C=clock/data cookie cut mask($5555)).

		Pass 2: A*BC* + BC (B=result of the previous pass, BC
preserves the data from the previous pass.  A=data source delayed for
two bits - results in off bits aligned over the next cells clock. A*BC*
will unset the clock bit of any cell that follows a cell with a data of 1.
C=clock/data cookie cut mask($5555)).

		Pass 3: like pass 1, just for even bits.

		Pass 4: like pass 2, but A is delayed 1 bit instead of 2, and
even bits of course.

>If you are looking for a good article on just what MFM encoding itself is,
>and GCR for that matter, take a look at _Transactor for the Amiga_ Vol. 3,
>Issue 1, "Amiga Disk Encoding Schemes" by Betty Clay, pp. 70-73.

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
Thus spake the Master Ninjei: "To program a million-line operating system
is easy, to change a man's temperament is more difficult."
(From "The Zen of Programming")  ;-)

mykes@amiga0.SF-Bay.ORG (Mike Schwartz) (04/01/91)

In article <2161@pdxgate.UUCP> bairds@eecs.cs.pdx.edu (Shawn L. Baird) writes:
>Having finished in my attempts at stepping in and out tracks I have decided
>to move on to the more difficult prospect of reading and (eventually)
>writing tracks. With this move I have several questions:

I don't want to give away all my source code, but this should help.

>   3. Is there any way to busy loop on the completion of disk DMA? (I
>      realize that an interrupt is more elegant, but this is just a test
>      program.)

You don't have to use interrupts for ANYTHING on the Amiga if you don't
want to :)

	; Wait until DMA write is finished.  We poll IRQ bits to find out.
		move.w	#2,INTREQW(a6)		; clear DMA done bit
		...
	>>>	set up and start your DMA  <<<
		moveq	#2,d1			; to make Randall happy!
l1		move.w	INTREQR(a6),d0
		and.w	d1,d0
		beq.s	l1			; 0, not finished.

Make sure you do the following, too.

	; Our work is done.  'dsklen'=$4000 prevents accidental writes,
	; leave IRQ bits and disk DMA off.
		move.w	#$10,DMACONW(a6) 	; disable disk dma.
		move.w	#$4000,DSKLENW(a6)	; prevent accidental writes
		move.w	#$1002,INTREQW(a6)	; DSKSYN+DSKBLCK (to make Randall Happy!)

>
>   4. The hardware manual mentions bugs in both reading and writing data.
>      Now, how am I expected to compensate for these bugs? For reading, I
>      assume I request an extra word?
>

	; After a disk write DMA has finished, a delay of POSTWRITE_TIME
	; miliseconds is required before any other operations (drive select,
	; step, head change, etc).  The type of disk drives used have a gap
	; between the erase head and the read-write head.  The disk drive keeps
	; the erase head enabled after the end of write gate to compensate for
	; the gap.  Failure to wait out the delay may result in writing over
	; innocent data on other tracks or sides.


>   5. I use DSKSYNC on $4489. Is this correct, or at least for most disks
>      such as AmigaDOS disks? If $4489 isn't found does it keep searching
>      forever?
>


Here is a fragment of code that will read your track.  Remember, before this
routine has been called, the motor is up to speed, the heads have been stepped
to the correct track, and 12ms of settle time has elapsed.  The first thing that
is done is DSKRDY is tested to make sure that the disk hasn't been ejected.

ReadTrack	btst	#DSKRDY,CIAA
		beq.s	TrackReader		; there is a disk in the drive
		move.l	#ERROR_NoDisk,tdError
		move.l	tdError,d0
		rts

TrackReader	clr.l	tdError
		move.w	#$4000,DSKLENW(a6)	; turn off DMA (to be safe)
		move.w	#$8400,ADKCONW(a6)	; turn on precomp (280ns)
		move.w	#$4489,DSKSYNW(a6)	; sync pattern for start of header
		move.l	#trackBuffer,DSKPTRW(a6) ; set DMA transfer address

		; clear pending interrupt requests.
		move.w	#$1002,INTREQW(a6) 
		move.w	#$8210,DMACONW(a6)	; enable disk dma
		move.w	#$8000+READSIZE,d0	; a number here would be more informative...

Randall can tell you what READSIZE should be.

		move.w	d0,DSKLENW(a6)		; set length (WORDS)
		move.w	d0,DSKLENW(a6)		; again! This starts DMA

; If the disk has not been formatted, the DSKSYN won't happen.  You have
; to use the timer to timeout in this case.

	; Wait until DMA write is finished.  We poll IRQ bits to find out.
.retry		move.w	#200,d1			; about 600 ms
	; This is similar to StepDelay, except it breaks out
	; of the busy wait loop (for DMA finished) even
	; if the 3ms time hasn't elapsed.
.waitsync	move.b	CRAA,d0
		and.b	#%11000000,d0
		or.b	#%00001000,d0
		move.b	d0,CRAA
		move.b	#%01111111,ICRA
		move.b	#(2148&255),TALOA
		move.b	#(2148>>8),TAHIA
.wait		move.w	INTREQR(a6),d0
		btst	#1,d0			; DMA done?
		bne.s	.done
		btst	#0,ICRA
		beq.s	.wait
		dbra	d1,.waitsync

; sync never happened, 
		move.w	#$4000,DSKLENW(a6)	; stop DMA
		move.w	#$10,DMACONW(a6)	; stop DMA
		move.l	#ERROR_NoSync,tdError 	; No Sync
		move.l	tdError,d0
		rts

; The read is complete.

.done		move.w	#$4000,DSKLENW(a6)
		move.w	#$10,DMACONW(a6)	; stop DMA
		bra	DecodeTrack		; use the blitter!


>   7. Does anyone have examples of blitter routines which both encode and
>      decode raw MFM tracks? The hardware manual mentions this as a
>      possibility as have several people on Usenet. I suspect this
>      involves some tricky operation with the line mode, with which I'm
>      just not well enough adjusted to figure it out for myself.
>

Maybe there should be a routine provided by trackdisk.device to do this.

The blitter code isn't as tricky as you'd think.  Here is how the CPU
does it:

DecodeLongWord	move.l	(a0)+,d0
		move.l	(a0)+,d1
		andi.l	#$55555555,d0
		andi.l	#$55555555,d1
		lsl.l	#1,d0
		or.l	d1,d0
		rts


>---
> Shawn L. Baird, bairds@eecs.ee.pdx.edu, Wraith on DikuMUD
> The above message is not licensed by AT&T, or at least, not yet.

--
********************************************************
* Appendix A of the Amiga Hardware Manual tells you    *
* everything you need to know to take full advantage   *
* of the power of the Amiga.  And it is only 10 pages! *
********************************************************

bairds@eecs.cs.pdx.edu (Shawn L. Baird) (04/01/91)

jesup@cbmvax.commodore.com (Randell Jesup) writes:

>In article <mykes.0972@amiga0.SF-Bay.ORG> mykes@amiga0.SF-Bay.ORG (Mike Schwartz) writes:
>>motor_wait	btst.b	#5,CIAA_PRA	; check READY line
>>		bne.s	motor_wait	; busy wait until drive is ready
>>		rts
>>
>>When I tried this on an A500 at EA, it didn't work at ALL.

>	Bug report???  It seems to work ok for us currently (2.0 trackdisk
>checks ready every .1 seconds up to .5, a compromise).  What drives didn't
>that code work on?  Did you tell bryce?

This code works perfectly fine on my A500. In fact, it's what I'm using
now. Of course, when taking over the machine it may not matter that this
is a busy loop. Checking every .1 seconds seems like a good compromise.
But you say it checks up to .5 seconds. Does it return an error if the
drive is not up to speed within .5 seconds?

I modified most of my routines to use constants as you mention. Like you
say, it's a good idea. I have my own include file that I have been adding
to since starting my assembler projects. I never quite figured out where
the common constants are listed so I just pick a suitable name and use it
from there on out. It is, of course, much easier than trying to memorize
bit numbers.

>	I've never heard of left/right for disks.  It's always in/out.

Left/right just doesn't seem to make any sense. On one side of the spindle
it's left and on the other it's right. In/out however, make perfect sense.

>>PEOPLE NOTE THIS!  There might not be a drive that requires it yet, but there
>>might be in the future.  EA has had ZERO returns or reports of any problems
>>with these routines, but it is always a good idea to do it right.

>	This sort of thing might not come to the attention of EA unless there
>were a lot of them out there.  And as he says, there is no guarantee that
>the code above will work with the drives that leave the factory tomorrow or
>any other day.

The Chinon timing diagrams in the Hardware Reference Manual explicitly show
that the direction should be set prior to stepping the drive. However, they
don't show how long, so I'm assuming just doing it in two different
instructions is the main thing. That is, using two different instructions
would still work on a 68030 machine?

>>The original poster asked how to step the heads, not how to read/write
>>to the disk.

>	Actually he seemed to be recalibrating the heads to track 0.

You are both correct in essence. I was trying to figure out how to step the
heads of the disk to get to track 0. It seems to be the only way to know
exactly what track you are on. I assume from then on that you keep the
value of the track and modify it every time you step in or out.

>>Another word of advice.  Do not use the book "Amiga Disk Drives: Inside
>>and Out" by Abacus.  I was told by Commodore when I was writing my routines
>>that the book is full of misinformation and was likely to cause people
>>to have disk driver problems.

Okay, what book should you use when accessing the drives at such a low
level? The Hardware Reference Manual is a good reference, but is somewhat
lacking in detail when it comes to such a low level of drive operation.

Oh, by the way Mr. Jesup, could you possibly send me some email with the
address to C.A.T.S. or somesuch?

One final thing. A third party manufacturer is producing a high density
3.5" drive for the Amiga. Does anyone know what ID string it uses? Was
this ID string assigned by Commodore? Also, will the high density 3.5"
drives in the 3000UX be compatible with this third party drive? Is
Commodore going to set a standard for high density 3.5" drives?

---
 Shawn L. Baird, bairds@eecs.ee.pdx.edu, Wraith on DikuMUD
 The above message is not licensed by AT&T, or at least, not yet.

dillon@overload.Berkeley.CA.US (Matthew Dillon) (04/02/91)

In article <mykes.0840@amiga0.SF-Bay.ORG> mykes@amiga0.SF-Bay.ORG (Mike Schwartz) writes:
>Try the following routines, instead.  Notice that 'structured' programming
>techniques are just as doable in assembler as they are in 'C'.  Note that
>when coding in assembler, your code is pretty optimal.  In particular, notice
>that rather than saying:
>	bsr	StepDelay
>	rts
>You can use
>	bra	StepDelay
>Which saves 34 clock cycles and 2 bytes.

    A good example of an optimization that will never make a bit of
    difference to the overall performance, because the step delay loop
    does a busy wait.  2 bytes verses how many for the routine?

    Now, of course, such a *simple* optimization I would probably do
    myself, but when all is said and done it seems to me that a BETTER
    optimization would be do something useful while you were waiting
    on the step... 3ms = 3000uS = many, many cycles.

>********************************************************
>* Appendix A of the Amiga Hardware Manual tells you	*
>* everything you need to know to take full advantage	*
>* of the power of the Amiga.  And it is only 10 pages! *
>********************************************************

			    -Matt

--

    Matthew Dillon	    dillon@Overload.Berkeley.CA.US
    891 Regal Rd.	    uunet.uu.net!overload!dillon
    Berkeley, Ca. 94708
    USA

jesup@cbmvax.commodore.com (Randell Jesup) (04/02/91)

In article <2171@pdxgate.UUCP> bairds@eecs.cs.pdx.edu (Shawn L. Baird) writes:
>jesup@cbmvax.commodore.com (Randell Jesup) writes:
>>In article <mykes.0972@amiga0.SF-Bay.ORG> mykes@amiga0.SF-Bay.ORG (Mike Schwartz) writes:
>>>motor_wait	btst.b	#5,CIAA_PRA	; check READY line
>>>		bne.s	motor_wait	; busy wait until drive is ready
>>>		rts

>This code works perfectly fine on my A500. In fact, it's what I'm using
>now. Of course, when taking over the machine it may not matter that this
>is a busy loop. Checking every .1 seconds seems like a good compromise.
>But you say it checks up to .5 seconds. Does it return an error if the
>drive is not up to speed within .5 seconds?

	We specify that drives must be up to speed in .5 seconds.  The .5
second max has two reasons: paranoia, since we hadn't been actively using
the signal in 1.3, and also performance, since dskrdy may not be active
for as much as .7 seconds after motor-on, even though the motor is up to
speed (it has to wait for the magnet to come by the sensor).

>I modified most of my routines to use constants as you mention. Like you
>say, it's a good idea. I have my own include file that I have been adding
>to since starting my assembler projects. I never quite figured out where
>the common constants are listed so I just pick a suitable name and use it
>from there on out. It is, of course, much easier than trying to memorize
>bit numbers.

	They aren't in an include file.  They're in the timing diagrams
in the 1.3 HW manual and in the text of the low-level disk access article
I wrote for AmigaMail a couple of years ago.

>The Chinon timing diagrams in the Hardware Reference Manual explicitly show
>that the direction should be set prior to stepping the drive. However, they
>don't show how long, so I'm assuming just doing it in two different
>instructions is the main thing. That is, using two different instructions
>would still work on a 68030 machine?

	The 1.3 HW manual says explicitly that they must be set by two
separate writes.  68030 has no affect on this.

>Okay, what book should you use when accessing the drives at such a low
>level? The Hardware Reference Manual is a good reference, but is somewhat
>lacking in detail when it comes to such a low level of drive operation.

	I'm afraid the HW manual is about it.  If you're really careful,
know what you're doing and what is legal and illegal by inspection, the
abacus book can be used a bit as a reference.  It requires a LOT of
separating the wheat from the chaff.

>One final thing. A third party manufacturer is producing a high density
>3.5" drive for the Amiga. Does anyone know what ID string it uses? Was
>this ID string assigned by Commodore? Also, will the high density 3.5"
>drives in the 3000UX be compatible with this third party drive? Is
>Commodore going to set a standard for high density 3.5" drives?

	I assign ID strings.  I assume you're talking about applied
engineering.  Normally there's no need for you to know other people's id
numbers, the same way we keep HW manufacturer numbers private.  If there
is some reason, let me know.  Since their interface to the HD modes is
funny, the ID number won't help you.

	The new HD drives for the tower/UX are just 150 rpm versions of the
current drives.  They change ID depending on the disk in them, and 2.0 
trackdisk reads the id on each disk insertion.  Other than that, you
merely read or write twice as much data.  If you have need for more
detailed info, contact CATS.

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
Thus spake the Master Ninjei: "To program a million-line operating system
is easy, to change a man's temperament is more difficult."
(From "The Zen of Programming")  ;-)

jesup@cbmvax.commodore.com (Randell Jesup) (04/02/91)

In article <mykes.1046@amiga0.SF-Bay.ORG> mykes@amiga0.SF-Bay.ORG (Mike Schwartz) writes:
>>   5. I use DSKSYNC on $4489. Is this correct, or at least for most disks
>>      such as AmigaDOS disks? If $4489 isn't found does it keep searching
>>      forever?
>
>Here is a fragment of code that will read your track.  Remember, before this
>routine has been called, the motor is up to speed, the heads have been stepped
>to the correct track, and 12ms of settle time has elapsed.  The first thing that
>is done is DSKRDY is tested to make sure that the disk hasn't been ejected.

	NAK!  You say wait 12ms (I assume you already waited the 3ms of step
time).  That's wrong.  You must wait a total of 18ms (3ms + 15ms) for the
heads to properly settle.  See the 1.3 HW manual timing diagrams, they show
the required 18ms total delay.

>ReadTrack	btst	#DSKRDY,CIAA
>		beq.s	TrackReader		; there is a disk in the drive
>		move.l	#ERROR_NoDisk,tdError
>		move.l	tdError,d0
>		rts

	Wrong, the proper bit to check is DSKCHANGE.  It goes low immediately
if a disk is removed, as the hardware manual states on page 238.  DSKRDY only
tells you that the motor is up to speed, not whether there's a disk in the
drive.  It can rotate even with no disk in the drive.

>TrackReader	clr.l	tdError
>		move.w	#$4000,DSKLENW(a6)	; turn off DMA (to be safe)
>		move.w	#$8400,ADKCONW(a6)	; turn on precomp (280ns)
>		move.w	#$4489,DSKSYNW(a6)	; sync pattern for start of header
>		move.l	#trackBuffer,DSKPTRW(a6) ; set DMA transfer address
>
>		; clear pending interrupt requests.
>		move.w	#$1002,INTREQW(a6) 
>		move.w	#$8210,DMACONW(a6)	; enable disk dma
>		move.w	#$8000+READSIZE,d0	; a number here would be more informative...
>
>Randall can tell you what READSIZE should be.

	It should be (6812+2)*2 bytes (6814 words) at least (a bit extra
doesn't hurt).  If you're not using DSKSYNC/WORDSYNC, you should add a full
raw sector to that to ensure getting all sectors.  The AmigaMail article 
goes into all this, and how the numbers are arrived at (it was also posted 
to BIX in amiga.dev at the time (quite a while ago)).

>; If the disk has not been formatted, the DSKSYN won't happen.  You have
>; to use the timer to timeout in this case.

	Yup.  Since we don't care about performance here, we use a 300000
microsecond wait, started AFTER dma, and you always check for dma completion
first if the timer goes off.  (A nominal rotation is 200000us, but we're
transferring more than a nominal track, and we want a safety margin.)

>Maybe there should be a routine provided by trackdisk.device to do this.
>
>The blitter code isn't as tricky as you'd think.  Here is how the CPU
>does it:
>
>DecodeLongWord	move.l	(a0)+,d0
>		move.l	(a0)+,d1
>		andi.l	#$55555555,d0
>		andi.l	#$55555555,d1
>		lsl.l	#1,d0
>		or.l	d1,d0
>		rts

	(BTW, that can be shorter and faster).  ;-)

	Note that applies only to the header/checksum words.  Data words
(and the 16 bytes of header info) are split, all the odd and even bits
together (odd bits first).  Also, when doing this you may want to accumulate
the checksum value as well (if doing the data area).  2.0 does it like that.

	Here's a table showing how the bits are split up:

*       |<- 16 bits->|                |<------ 16 bits ------>|
* data addr                   buffer addr
* ----- /------------\        -----   /-----------------------\
* +0    | word  0    |        +0      |  odd bits of word 0   |
*       |------------|                |-----------------------|
* +1    | word  1    |        +1      |  odd bits of word 1   |
*       |------------|                |-----------------------|
*       |      *     |                |           *           |
*       |      *     |                |           *           |
*       |      *     |                |           *           |
*       |------------|                |-----------------------|
* +n -2 | word  n -2 |        +n -2   |  odd bits of word n-2 |
*       |------------|                |-----------------------|
* +n -1 | word  n -1 |        +n -1   |  odd bits of word n-1 |
*       \------------/                |-----------------------|
* +n                          +n      | even bits of word 0   |
*                                     |-----------------------|
*                             +n +1   | even bits of word 1   |
*                                     |-----------------------|
*                                     |           *           |
*                                     |           *           |
*                                     |           *           |
*                                     |-----------------------|
*                             +2n -2  | even bits of word n-2 |
*                                     |-----------------------|
*                             +2n -1  | even bits of word n-1 |
*                                     \-----------------------/
*                             +2n
*

	Here's a piece of a routine to decode data set up like that, and
keep a checksum value in a register:  (it makes assumptions about certain
things, like d4 being $55555555, odd/even sources in a0/a2, destination in a1,
count in d1, and 0 in d0)

SumBufLoop:			; first we grab the odd bits
            MOVE.L  (A0)+,D2
	    MOVE.L  (A2)+,D3	; then the even bits
            EOR.L   D2,D0
	    EOR.L   D3,D0	; add into checksum
	    AND.L   D4,D2
	    AND.L   D4,D3	; mask off clock bits
	    ASL.L   #1,D2	; allign &
	    OR.L    D3,D2	;  combine them
	    MOVE.L  D2,(A1)+	; and store in dest
            DBRA    D1,SumBufLoop

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
Thus spake the Master Ninjei: "To program a million-line operating system
is easy, to change a man's temperament is more difficult."
(From "The Zen of Programming")  ;-)