1571-7.TXT rev 1a 96-11-06
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     THIS DOCUMENT IS COPYRIGHT (C) 1988, 1996 BY HERNE DATA
     SYSTEMS LTD.  THE MATERIAL CONTAINED HEREIN MAY BE FREELY
     USED FOR PERSONAL INFORMATION ONLY.  IF YOU REPRODUCE IT,
     THIS COPYRIGHT NOTICE MUST NOT BE REMOVED.  THIS MATERIAL
     MAY NOT BE EXPLOITED FOR COMMERCIAL PURPOSES.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Herne Data Systems Ltd., 
PO Box 250, Tiverton, ON N0G 2T0 CANADA.  
Voice/fax 519-366-2732, 
e-mail herne@herne.com, 
internet: http://www.herne.com

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 
                       Burst Read Protocol 
 
 
 
Burst mode reading can be very fast, up to 4000 bytes per second
or more.  This is considerably faster than the normal 1571 "fast"
mode (via the KERNAL) of about 1600 bytes per second and the 1541
(or 1571 slow mode) rate of about 350 bytes per second.  (When
you take into account the "overhead" such as disk initialization,
track to track jump time and sector seeking time, the effective
burst mode speed is about 2200 bytes per second while the average
value for 1571 fast mode is about 1100 bytes per second.)  Only
the C-128 has the necessary hardware for burst mode data
transfer.  Therefore, the procedures described in this chapter, 
apply to the C-128 only.  For other computers, data can be
transferred using direct memory reads and writes.

During normal data transfers (e.g. those using GET#'s, LOAD, etc)
between a C-128 and a 1571 or 1541 drive, a significant fraction
of time is taken up by the convoluted path through the computer's
KERNAL ROM and drive's DOS ROM that must be followed for each
byte to be sent.  Burst mode eliminates much of this inefficiency
by sending data based on a much simpler, and therefore much
faster, hardware handshake.   Data are exchanged directly between
the data register of the complex interface adaptor (CIA) of the
1571 and the data register of the C-128 CIA#1 based on a simple
signal from the normal C-128 serial bus controller (CIA#2).  (It
should be noted that 1571 "fast" mode is actually based on burst
mode but is accessed via the KERNAL.  The tortuous KERNAL path
results in slower access speeds because of the software
overhead.) 
 
There are five simple steps to performing a burst mode read
operation.  These are: 

(a)  open the command channel and log in the disk (if
     required).
 
(b)  send the appropriate command string to access a burst
     mode read; 
 
(c)  initialize the C-128 CIA's; 
 
(d)  read the data; and 
 
(e)  restore the default I/O devices. 

Burst Read Setup
The first step can be performed by either machine language or
BASIC statements.  If you are reading a given disk for the first
time using burst SECTOR READ or FAST LOAD, you must first "log
in" the disk using either the "INQUIRE DISK", "INQUIRE STATUS" or
"QUERY DISK FORMAT" commands.  This sets up the 1571 electronics
to read the particular format, whether it be GCR or an MFM
format.  A typical BASIC statement may be as follows: 
 
      OPEN 15,8,15,"U0"+CHR$(10) 
 
This particular example will OPEN the disk command channel, then
ask the 1571 to use the QUERY DISK FORMAT command to analyze the
format of the first track on side 0 of a disk.

The equivalent in simplified assembly language might look like: 
  
        LDX #$00                   ; GO TO BANK 15
        STX #FF00 
        LDA #$0F                   ; FILE NUMBER
        LDX #$08                   ; DEVICE NUMBER
        LDY #$0F                   ; CHANNEL NUMBER
        JSR $FFBA                  ; (KERNAL SETLFS ROUTINE) 
        LDA #$00                   ; NO FILE NAME
        JSR $FFBD                  ; (KERNAL SETNAM ROUTINE) 
        JSR $FFC0                  ; (KERNAL OPEN ROUTINE) 
        LDX #0F                    ; FILE NUMBER
        JSR $FFC9                  ; (KERNAL CHKOUT ROUTINE) 
        LDA #$55                   ; ("U") 
        JSR $FFD2                  ; (KERNAL BSOUT ROUTINE) 
        LDA #$30                   ; ("0") 
        JSR $FFD2 
        LDA $#0A                   ; (CHR$(10)) 
        JSR $FFD2 
        JSR $FFCC                  ; (KERNAL CLRCHN ROUTINE) 
 
The first two instructions are the machine language equivalent of
BASIC's BANK 15 statement.  This procedure should be used in
machine langauge whenever you want to call KERNAL routines
because they are all located in BANK 15.  Attempting to call one
while in another BANK will probably cause a crash.  All of the
C-128 KERNAL addresses are the same as for other Commodore
computers.  (The C-128 also has several new KERNAL routines, one
of which is of interest for burst mode.  The SPIN/SPOUT routine,
which is used for burst writes, will be discussed in Chpater 11.) 
The BANK switching is handled automatically by BASIC before using
the OPEN statement (or any other BASIC command).  The remainder
of the routine performs standard KERNAL type serial port output. 

If the command channel is already OPEN, you can use the following
in BASIC, or its machine language equivalent: 
 
    PRINT #15,"U0"+CHR$(10) 
 
If there is a possibility that the disk drive connected to the
C-128 is not a 1571 or that the 1571 has been set to 1541 mode,
you should test bit 6 of the FAST SERIAL flag (RAM location
$0a1c, decimal 2588) before proceeding with the actual burst mode
operations.  If this bit is set after an OPEN operation (either
BASIC or KERNAL) then the drive is a FAST device (i.e. a 1571 or
1581 in FAST mode) capable of handling burst mode.  The value of
the FAST SERIAL flag should be greater than or equal to $40
(decimal 64) for burst mode transfers.  With current ROM
versions, it will be equal to this value.  In BASIC, the test
could be similar to: 
 
100 IF PEEK(2588) AND 64 <> 64 THEN PRINT"NOT A 1571 DRIVE":END 
 
The equivalent in machine language might look like: 
 
    LDA $0A1C            ; FAST FLAG
    AND #$40             ; MASK BIT 6
    BEQ (TO ERROR AND EXIT ROUTINE) 
    (CONTINUE WITH CODE) 
 
This test should be done to prevent a lock up or crash if burst
mode is attempted on a slow device such as a 1541.  Note that
"FAST" and "SLOW" used in this context refer to the serial bus
speed and not to BASIC 7.0's FAST and SLOW commands.

If you are interested in the values returned by the QUERY DISK
FORMAT or INQUIRE DISK commands, you will have to read them via
burst mode.  If you are not interested in any of the parameter
values (i.e. they are already known to you), you can continue
without reading them.  The command will be cancelled with the
next command that you send.

Sending the Command String

After the disk has been logged in, you can send one of the burst
mode SECTOR READ or FAST LOAD commands, along with its
parameters, to the drive using a standard BASIC PRINT# or KERNAL
CHROUT (BSOUT) type routine.  It should be noted that you must
not try to read the disk drive error channel between sending a
burst mode read command and actually reading the data.  If you do
so, you will cancel the read command and probably lock up the
computer because it will wait for burst mode data from the drive
which will never come.

Reading the Data
The first step of the read sequence is to initialize the interupt
register of the C-128's CIA#1 and tell the serial bus that you
are ready to receive data.  This should be done immediately after
sending the burst mode command and only using machine language. 
(A short machine language program can easily be poked into RAM
from BASIC and called with a "SYS" statement.)  The machine
language instructions are as follows: 
 
          SEI            ; DISABLE INTERUPTS
          BIT $DCOD      ; WAIT FOR CIA 
          LDA $DD00      ; GET "CLOCK" VALUE
          EOR #$10       ; FLIP IT
          STA $DD00      ; AND SET IT
 
If this procedure is being called as a subroutine from either
BASIC or machine language, then you will need to add an "RTS"
after the last instruction to return to the calling program.  The
first instruction disables the normal processor interupts such as
keyboard scanning, etc.  This has the effect of increasing the
amount of time that the hardware can dedicate to data transfer
and eliminating the trapping of interupts which may cause errors
during data transfers.  The "BIT" instruction is used to reset
the interupt control register (ICR) of CIA#1.  Alternatively, a 
       LDA $DC0D 
 
can also be used to clear the ICR.  The final three instructions
toggle the state of the acknowledge and ready for data (ARFD)
line which is used as a clock (or handshake signal) during the
burst transfer.  This is a signal to the 1571 that we are ready
to recieve data. 
 
The next step is to read the actual burst data.  Again, this can
only be done in machine language, both for speed and simplicity. 
The subroutine for reading burst data bytes is quite simple: 
 
         LDA #$08   ; BIT 3 VALUE
WAIT     BIT $DCOD  ; WAIT LOOP
         BEQ WAIT   ; TILL BIT SET
 
         LDA $DD00  ; TOGGLE CLOCK
         EOR #$10 
         STA $DD00 
 
         LDA $DC0C  ; GET DATA
         RTS 
 
The first three instructions create a wait loop until bit 3 of
CIA#1's ICR is turned on.  This indicates that a byte has been
received.  The next three instructions toggle the state of the
ARFD line causing the next data byte to be transferred (or to
signal that the final byte has been received).  The final two
instructions read the data byte from the CIA#1 data register and
return it to the calling program in the "A" register.  
 
To store the returned byte, an indexed "STA" instruction similar
to: 
 
          STA ($FA),Y 
 
is normally used (assuming zero page locations $FA and $FB
contain the low and high bytes of the data buffer address and the
Y register is used as an index).  In order to use the I/O block
and KERNAL routines, the C-128 must be set for BANK 15. 
Unfortunately, this also limits the maximum size of a data buffer
to 8 k bytes (BANK 0 RAM below $4000 is visible in BANK 15 also.) 
This can be overcome by "playing with" the memory management unit
(MMU) configuration register ($FF00 - all BANKs) to switch
between BANK 0 and BANK 15 "on the fly".  Your machine code must
be in an area visible to both BANKs (i.e. below $4000 such as the
cassette buffer) for this to work.  In this case, the indexed
"STA" instruction mentioned above should be replaced with: 
 
          LDX #$3F       ; MMU CONFIGURATION FOR BANK 0
          STX $FF00      ; SET BANK 0
 
          STA ($FA),Y    ; STORE DATA
 
          LDX #$00       ; BACK TO BANK 15
          STX $FF00 
 
The first two instructions set the C-128 to BANK 0.  The data
byte is then stored in the correct BANK 0 location.  The last two
instructions switch back to BANK 15.  This simple technique
allows you to use up to about 60 k bytes of BANK 0 as a data
buffer (all of it except for the overhead such as screen RAM,
system use areas, etc. and your BASIC or machine language
program).  There is no need to protect the unused RAM in BANK 0
from being overwritten by BASIC variables (they are in BANK 1)
but don't forget to start your buffer above any machine language
or BASIC program that may be occupying BANK 0 (including the
hi-res graphics screen if used). 
 
The read subroutine is often called from an indexed loop,
especially when reading blocks of data.  It is important that you
keep track of the number of bytes transferred and that your
indexing method can handle the number of bytes involved. 
(Remember that the number of bytes transferred for a sector read
is 1 + the number of bytes per sector.  Commodore 1571 type GCR
disks have 256 bytes per sector, while MFM format disks may have
sector sizes of 128, 256, 512, or 1024 bytes per sector.  FAST
LOAD GCR sectors have 254 bytes per sector.  The number of bytes
transferred for other burst mode commands depends on the command. 
In addition, the number of sectors that can be transferred with a
multi sector read (except for FAST LOAD) should be restricted to
one track's worth.  Although the multi sector read command can
handle a larger number than one track worth, the specified number
of sectors will be read from the same track.  That is, the read
head will re-read sectors on the same track until the specified
number of sectors has been read.  This is normally a waste of
time and buffer space. 
 
A summary of annotated assembly language routines needed to read
each of the burst mode commands is given in TABLE 10-1.  These
routines can be entered directly on the C-128 with its built in
MONITOR command by replacing the labels (wait, next, etc.) with
absolute addresses and removing the comments.  The listed
routines can be easily converted for use with most assemblers as
well.  The most convenient location for the ml is the cassette
and RS-232 buffers beginning at $0B00 (dec 2816).  The combined
buffer space gives you 768 bytes for machine language.  An
alternate location is the unused area of BANK 0 RAM from $1300 to
$1BFF (dec 4864 to 7167).     

End of Transmission
The final step after all data have been transferred, processed,
stored etc. is to close the disk channel and restore the default
input and output devices.  In machine language, this is done
with: 
 
          CLI 
          JSR $FFCC              ; (KERNAL CLRCHN ROUTINE) 
          LDX #$0F               ; COMMAND CHANNEL
          JSR $FFC3              ; CLOSE IT  
 
It is very important to include the "CLI" instruction.  This
re-enables the processor interupts which were turned off by the
initial "SEI" instruction.  In BASIC, the file can be closed with
a: 
 
   DCLOSE #{file#}  
 
or equivalent type of statement.  This automatically will do all
of the required operations.
 
That in a nutshell is how to read data in burst mode.  

  
 
TABLE 10-1: SUMMARY OF ASSEMBLY LANGUAGE BURST MODE READ ROUTINES

General read-a-burst-byte routine (used by all subroutines below)

     READ1  LDA #$08 
     WAIT  BIT $DC0D 
           BEQ WAIT   ;WAIT FOR BIT 3 OF CIA#1 ICR 
 
    READ2  LDA $DD00 
           EOR #$10   ;TOGGLE CLOCK 
           STA $DD00 
           LDA $DC0C  ;GET DATA BYTE 
           RTS 
 
 
 
NOTE:     Before using any of the following routines, you must
          load zero page locations $fa and $fb with the low and
          high bytes of the start of your data buffer and call
          the appropriate burst mode command. 
 
Single Byte Read: 
 
(used for INQUIRE DISK, INQUIRE STATUS and  read SECTOR
INTERLEAVE) 
 
   LDY #$00    ;RESET POINTER 
   SEI         ;DISABLE INTERUPTS 
   BIT $DCOD   ;CLEAR CIA#1 ICR 
   JSR READ2   ;SIGNAL WHEN READY 
   JSR READ1   ;READ BYTE 
   STA ($FA),Y ;STORE BYTE 
   CLI         ;RESTORE INTERUPTS 
   JSR $FFCC   ;CLEAR I/O CHANNELS  
   RTS 
       
 
Multi byte read: 
(used for QUERY DISK FORMAT) 
 
       LDY #$00     ; RESET INDEX
       SEI 
       BIT $DC0D    ; LETS GO
       JSR READ2         
       JSR READ1         
       JSR STORE         
       CMP #$02    ; CHECK DISK TYPE     
       BCC EXIT    ; GCR DISK       
       AND #$0E         
       CMP #$00    ; CHECK ERRORS
       BNE EXIT    ; MFM ERROR 
       JSR READ1   ; READ ANOTHER STATUS BYTE 
       JSR STORE 
       AND #$0E  
       CMP #$00   ; CHECK ERRORS FOR SECOND BYTE
       BNE EXIT   ; MFM ERROR 
       JSR READ1  ; # SECTORS/TRACK 
       JSR STORE 
       JSR READ1  ; # LOGICAL TRACK FOUND 
       JSR STORE  
       JSR READ1  ; MINIMUM SECTOR # FOUND 
       JSR STORE 
       JSR READ1  ; MAXIMUM SECTOR # FOUND  
       JSR STORE 
       JSR READ1  ; CP/M HARD INTERLEAVE 
       JSR STORE          
 EXIT  CLI        ; EXIT
       JSR $FFCC 
       RTS 
 
STORE  STA ($FA),Y ; STORE BYTE SUBROUTINE
       INY         ;INCREMENT POINTER 
       RTS  
 
Read N sectors of data: 
;     128 BYTE MFM SECTORS:  
      LDX #NUMBER_OF_SECTORS ; SET COUNTER
      STX $FC                ; AND SAVE IT   
      LDX #00                ; #SECTORS ACTUALLY READ 
      STX $FD                ; AND SAVE IT
      SEI 
      BIT $DC0D              ; LETS GO
      JSR READ2              
 
NEXT2  LDY #$00 
      JSR READ1              ; READ STATUS BYTE 
      AND #$0E               ; MASK STATUS BITS
      CMP #$00 
      BNE END                ; END IF ERROR 
 NEXT1 JSR READ1             ; ELSE READ DATA BYTES
      LDX #$3F               ; GOTO BANK 0 
      STX $FF00  
      STA ($FA),Y            ; SAVE DATA
      LDX #$00               ; GOTO BANK 15 
      STX $FF00  
      INY                    ; NEXT BYTE
      CMP #$80               ; END OF SECTOR? 
      BNE NEXT1              ; GET NEXT BYTE  
      LDX $FD                ; NEXT SECTOR
      INX 
      CPX $FC                ; LAST SECTOR?  
      BEQ END 
      STX $FD  
      TYA                     ; SET FOR NEXT 128 BYTES
      CLC  
      ADC $FA                 ; INC PNTR 128 BYTES 
      BCC NEXT2               ; READ NEXT SECTOR 
      INC $FB  
      JMP NEXT2  
  END CLI                     ; EXIT
      JSR $FFCC  
      RTS  
 
 
; 256 BYTE GCR OR 256*N BYTE MFM SECTORS: 
 
      LDX #NUMBER_OF_SECTORS       ; #SECTORS TO READ
      STX $FC 
      LDX #$00 
      STX $FD 
      LDX #SECTOR_SIZE/256         ; NUMBER OF PAGES IN SECTOR
      STX $FE                      ; SAVE TWICE
      STX $FF 
      SEI                          ; RESET AND GO
      LDY #$00 
      BIT $DC0D 
      JSR READ2 
 NEXT2  JSR READ1                  ; CHECK STATUS
      AND #$0E 
      CMP #$00 
      BNE END                      ; END IF ERROR  
NEXT1  JSR READ1                   ; READ DATA
      LDX #$3F 
      STX $FF00 
      STA ($FA),Y 
      LDX #$00 
      STX $FF00 
      INY 
      CPY #00 
      BNE NEXT1 
      LDX $FE                      ; CHECK FOR ALL PAGES READ
      DEX 
      STX $FE                      ; NEXT PAGE
      INC $FB 
      CPX #00                      ; END OF SECTOR? 
      LDX $FF                      ; RESET PAGE COUNT
      STX $FE 
      BNE NEXT1 
      LDX $FD                      ; CHECK SECTORS READ
      INX 
      CPX $FC                      ; LAST SECTOR? 
      BNE NEXT2 
 END  CLI 
      JSR $FFCC 
      RTS 
 
; FAST LOAD ENTIRE FILE (254 BYTE GCR SECTORS): 
      SEI                ; RESET AND GO
      BIT $DC0C  
      JSR READ2  
NEXT2  JSR READ1         ; READ STATUS
      STA $FC 
      CMP #$02 
      BCS LAST           ; LAST SECTOR IN FILE?   
NEXT  JSR READ1          ; READ DATA
      LDX #$3F 
      STX $FF00 
      STA ($FA),Y 
      LDX #$00  
      STX $FF00 
      INY  
      CPY #$FE           ; ONLY 254 DATA BYTES               
      TYA  
      CLC  
      ADC $FA            ; SET POINTERS
      STA $FA 
      BCC NEXT2          ; CARRY SET?
      INC $FB            ; INCREMENT PAGE
      JMP NEXT2 
LAST  JSR READ1          ; GET # BYTES IN LAST SECTOR             
     LDY #$00 
NEXT3 JSR READ1          ; READ LAST SECTOR
      LDX #$3F 
      STX $FF00 
      STA ($FA),Y 
      LDX #$00 
      STX $FF00 
      INY 
      CPY $FC            ; LAST BYTE? 
      BNE NEXT3 
      CLI 
      JSR $FFCC 
      RTS 
 
 
 
