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