
;* 			------------------------			*
;* 			1571  BURST  SUBROUTINES			*
;* 			------------------------			*
;* 
;*     The following burst subroutines have been designed for use in    *
;*  BASIC and machine language programs.                                *
;* 
;*     If you are programming in assembly language, you can use these   *
;*  routines as is, or you can use them as a framework to build upon.   *
;* 
;*     If you are programming in C128 BASIC, you should follow the      *
;*  procedure shown below.  (Note: you cannot use the burst commands    *
;*  with the C64.)                                                      *
;* 
;*    BLOAD the binary file containing the burst routines.              *
;*    The file name is "1571 BURST.BIN".                                *
;* 
;*    Assign a logical file number to the command channel to the 1571.  *
;* 
;*    Open the command channel within your BASIC program.               *
;* 
;*    Execute the BANK 0 command.  This will tell BASIC to PEEK and     *
;*    POKE to the RAM under the BASIC ROM.                              *
;* 
;*    POKE the logical file number to LF.                               *
;* 
;*    POKE the appropriate parameters into the proper variable          *
;*    locations, and SYS to the desired routine.                        *
;* 
;*    The BURST protocol and handshaking will be done automatically.    *
;*    The BASIC program can then PEEK any values returned.              *
;* 
;* If you are using a RAM buffer, be sure that the buffer uses only the *
;* memory between the end of BASIC text and $C000.  The KERNEL and I/O  *
;* will need the space after $C000.  BASIC programs normally start at   *
;* $1C00. If you enable bit-map graphics, then your program will start  *
;* at $4000.  The binary file containing these BURST routines loads at  *
;* $1300, so that they are in a safe place below the BASIC text area.   *
;* 
;*  If you want to make your program intelligent, the pointers to the   *
;* exact beginning and end of the BASIC program are in locations $002D  *
;* and $1210 respectively. For the most part, however, there is no need *
;* to look at those values.  As a general rule, if you use memory       *
;* by working backwards from $C000, you'll be OK.  When you PEEK and    *
;* POKE this memory from BASIC, be sure to execute the BANK 0 command.  *
;* This tells BASIC to PEEK and POKE to the RAM under the BASIC ROM.    *
;* 
;* NOTE:  There is no BURST FORMAT routine provided here.  If you need  *
;*        do formatting then use BASIC.   The following BASIC command   *
;*        will format physical tracks 10 through 20 of the disk with 5  *
;*        1024 byte MFM sectors per side (sectors 1-5).                 *
;*
;*        OPEN 1,8,15                                                   *
;*        PRINT#1,"U0"CHR$(38)CHR$(129)CHR$(0)CHR$(3)CHR$(10)CHR$(5)    *
;*                 CHR$(10)CHR$(10);					*
;*
;*
;*
;* Note the use of the semicolon (;) at the end of the statement.  This *
;* is very important!  If there was no semicolon, the C128 would        *
;* send a carriage return after the last parameter.  Since the 1571     *
;* counts the number of bytes sent to determine the number of optional  *
;* parameters that are being sent, it would misinterpret the carriage   *
;* return as the next optional parameter.  In this case, it would be    *
;* fill byte. 								*
;*									*

;*********************************************************************
;*									*
;*                     VARIABLE DECLARATIONS				*
;*									*
;* These variables are parameters passed between a BURST routine and    *
;* its calling program.                                                 *
;*                                                                      *
;************************************************************************


	*=$1300

STATUS 	.byte 0		; status byte
DEV	.byte 8		; device number
LF      .byte 8		; logical file number
TRACK	*=*+1		; track 
SECTOR  *=*+1		; sector
NUMSEC	*=*+1		; Number of sectors.
BUFLOC	*=*+2		; Page # of buffer to get/put data.
SECSIZE	*=*+1		; Sector size (1=256, 2=512, 4=1024)
SIDE	*=*+1		; Physical side of the disk (0 or 1).
MINSEC 	*=*+1		; Minimum logical sector found in QUERY.
MAXSEC	*=*+1		; Maximum logical sector found in QUERY.
INTLV	*=*+1		; Physical interleave found in QUERY.
FLAG    *=*+1		; Empty track flag.
                        ; This flag is used to indicate that the
;  track or data just read contains all 0's.  This is handy in some cases, 
;  such as during a disk copy program.  When a disk is formatted, the 
;  sectors are filled with 0's.  If a sector to be copied contains
;  all 0's, then we don't bother to write it to the destination disk


;******************************************************************
;*								  *
;* Here are other variables used internally by the BURST routines.*
;*								  *
;******************************************************************


cmdline
        .byte 'u0'		
	*=*+10		; Parameter space for burst command.
cmdlen	*=*+1		; Length of the command string (# of bytes).
oldclk  *=*+1		; Status of clock line.
temp	*=*+1


;************************************************************************
;*									*
;*                         JUMP TABLE					*
;*									*
;* This jump table gives the locations of each of the BURST routines.   *
;* The jump table positions will never change, even if the routines     *
;* below are modified.  Always SYS to this table from BASIC, or JSR or  *
;* JMP to this table from assembly language.                            *
;*									*
;************************************************************************

	*=$1340

J_INQUIRE_FORMAT	
	jmp INQUIRE_FORMAT		
J_PHYSICAL_READ
	jmp PREAD
J_PHYSICAL_WRITE
	jmp PWRITE
J_QUERY_FORMAT
	jmp QUERY_FORMAT
J_COMPARE_MEMORY
	jmp COMPARE_MEMORY
J_SENDCMD
	jmp SENDCMD
J_OPEN_CHANNEL
	jmp OPEN_CHANNEL

;************************************************
;* 						*
;*		KERNAL EQUATES			*
;*						*
;************************************************

chkout=$ffc9		; kernel channel output routine
clrchn=$ffcc		; kernel clear channel routine
setlfs=$ffba		; kernel set logical file # routine
setnam=$ffbd		; kernel set file name routine
bsout =$ffd2		; kernel basic i/o routine
open =$ffc0		; kernel open logical file for I/O routine
spin_out=$ff47		; Set up fast serial for input or output
			;  SEC for output, CLC for input.

;************************************************************************
;*									*
;* These addresses are memory locations used by the BURST subroutines.	*
;*									*
;************************************************************************

d2pra =$dd00		; C128 serial port location
clkout=$10		; slow serial clock output bit mask
clkin =$40		; slow serial clock input bit mask
d1icr =$dc0d		; 6526 CIA interrupt control register
d1sdr =$dc0c		; 6526 CIA serial data register
buffer=$fa		; zero page pointer variable
buffer2=$fc		; zero page pointer variable


;***************************************************************
;*								*
;* 		BURST COMMAND PRIMITIVES			*
;*								*
;* These are the BURST commands as the 1571 sees them.		*
;*								*
;***************************************************************


PBURSTRD =$00		; Burst read.
PBURSTWR =$02		; Burst write.
BURST_INQUIRE  =$04	; Burst inquire.
BURST_QUERY =$86	; Query disk format.


;****************************************************************
;*								*
;*		     THE BURST ROUTINES				*
;*								*
;****************************************************************


PREAD	;This BURST routine reads sectors from device LF.
	; 
	; Required Parameters:
	;	LF	Logical file number to read from.
	;	TRACK	Track to be read from.
	;	SECTOR  Sector to be read from.
	;	BUFLOC	Pointer to the starting location of the
	;		buffer in RAM bank 0.  As the characters
	;		are read, they are put in this buffer.
	;	SECSIZE	Physical sector size (1=256, 2=512, 4=1024)
	;	NUMSEC	The number of sectors to be read.
	;	SIDE	Physical side of the disk to read from,  
	;		0 or 1.  SIDE is ignored if the disk is GCR.
	;
	; Returned Parameters:
	;	STATUS	Status byte returned after read is attempted.


	lda #PBURSTRD   ;Physical burst read command.
	ldx SIDE	; Check which side to read from.
	beq 1$
	ora #$10	; If side 1, then set bit in the command byte.
1$	sta  cmdline+2

READ

	lda $ff00	;Save old MMU setup.
	pha

	lda #$0e	;Set MMU for RAM0,KERNEL,I/O.
	sta $ff00

	jsr SETU0	;Put "U0" at start of command string.


	lda TRACK	
	sta CMDLINE+3	; track
	lda SECTOR
	sta CMDLINE+4	; sector
	lda NUMSEC
	sta CMDLINE+5	; Number of sectors to read.

	lda #$06	; Length of command string.
	sta CMDLEN
	jsr  sendcmd	; send cmd string

	lda  BUFLOC	; Set up zero page indirect pointer.
	sta  BUFFER
	lda  BUFLOC+1
	sta  BUFFER+1

	ldy  #0		; clear the 'empty sector(s)' flag.
	sty  flag

	sei		; No irq's allowed during handshake.

	bit  d1icr	; clear pending

	jsr CLK_CHNG	;Change state of clock.

1$	ldx  SECSIZE	; Sector size gives # of pages per sector.

	jsr WAIT	;Wait for fast byte (1st is status).
	lda  d1sdr	;Get status byte.
	sta STATUS

	and  #15	;Was there an error?
	cmp  #2		; 
	bcs  5$		; branch if error occured.

	jsr CLK_CHNG	;Change clock so next byte is sent.

3$	jsr WAIT	;Wait for the next byte.
	
	jsr CLK_CHNG	;Change state of clock so next byte is sent.
	lda  D1SDR	;Get the data byte
	sta  (buffer),y	; and save it 
			;  while next byte is being transmitted.
	ora  flag
	sta  flag	;Update 'zero' sector flag.

	iny		;Any more in this page?
	bne  3$

	inc BUFFER+1
	dex		;Loop for the # of pages per sector.
	bne 3$

	dec CMDLINE+5	;Loop for the number of sectors.
	bne 1$


5$	cli

	pla		;Restore MMU to old configuration.
	sta $ff00

	rts

;*************************************************************

PWRITE		; This BURST subroutine writes physical sectors to device LF.
		;
		; Required Parameters:
		;	LF	Logical file number to write to.
		;	TRACK	Track to be written to.
		;	SECTOR	Sector to be written to.
		;	BUFLOC	Location of the beginning of the C128 buffer
		;		where the characters are to be read from.
		;	SECSIZE	Physical sector size (1=256, 2=512, 4=1024).
		;	NUMSEC	The number of sectors to be written.
		;	SIDE	Physical side of the disk to write to,
		;		0 or 1.  SIDE is ignored if the disk is GCR.
		;
		; Returned Parameters:
		;	STATUS	Status byte returned after write is attempted.

	lda #PBURSTWR   ;Physical burst write command.
	ldx SIDE	; Check which side to write to.
	beq 1$
	ora #$10	; If side 1, then set bit in the command byte.
1$	sta  cmdline+2


WRITE		
	lda $ff00	;Save old MMU setup.
	pha

	lda #$0e	;Set MMU for RAM0,KERNEL,I/O.
	sta $ff00

	jsr SETU0	;Put "U0" at start of command string.
	lda TRACK
	sta CMDLINE+3	; track
	lda SECTOR
	sta CMDLINE+4	; sector
	lda NUMSEC
	sta CMDLINE+5	; Number of sectors to write.

	lda #$06
	sta CMDLEN	;Command length.
	jsr  sendcmd	; send cmd string

	lda  BUFLOC	; Set up zero page indirect pointer.
	sta  BUFFER
	lda BUFLOC+1
	sta BUFFER+1


	lda  #clkin	;Initial clock status.
	sta  oldclk

	ldy  #0
	sei		; no irq's during burst handshake

1$	ldx  SECSIZE	; Sector size gives # of pages per sector.
	sec		;Turn fast serial to output mode.
	jsr  spin_out

2$	lda  d2pra	;Wait for state change.
	eor  oldclk
	and  #clkin
	beq  2$

	eor  oldclk	;Change status of OLDCLK.
	sta  oldclk

	lda  (buffer),y	; get data 
	sta  d1sdr	;  & send it

	jsr WAIT	;Wait for the byte to be transmitted.

	iny
	bne  2$		;Any more left in this page?

	inc  buffer+1
	dex  		;Loop for the # of pages per sector.
	bne  2$

	clc		;Turn around to input mode to get STATUS.
	jsr  spin_out
	bit  d1icr	; clear pending

	jsr  clklo	; set clock low when ready for status

	jsr WAIT	;Wait for the byte to be shifted in.
	lda  d1sdr	;Get the status byte.
	sta STATUS	;Save it.
	pha
	jsr  clkhi	;Release the slow clock line.
	pla

	and  #15	;Check for any error.
	cmp  #2	
	bcs  7$		;  branch if there was an error.
	
	dec  CMDLINE+5	;Loop for the number of sectors.
	bne  1$

7$	cli

	pla		;Restore old memory configuration.
	sta $ff00
	rts


;**************************************************************

INQUIRE_FORMAT	; This BURST subroutine sends an INQUIRE DISK command to
		; drive indicated by LF.
		;
		; Required Parameters:
		;	LF	Logical file number of device to inquire.
		;	SIDE	Physical side of the disk to inquire about,
		;		0 or 1.  SIDE is ignored if the disk is GCR.
		;
		; Returned Parameters:
		;	STATUS	Status byte returned.

	jsr SETU0		;Put "U0" at start of command string.
        lda  #BURST_INQUIRE	; inquire burst command
	sta  cmdline+2

	ldx SIDE		; Check which side to check.
	beq 1$
	ora #$10		; If side 1, then set bit in the command byte.

1$	lda #$03		; length of command.
	sta CMDLEN
	jsr  sendcmd		; send cmd string
	sei			;Disable interrupts during handshake.
	bit D1ICR		;Clear any byte ready that's pending.

	jsr CLK_CHNG		;Change clock so 1571 sends next.
	jsr WAIT		;Wait for the byte to be shifted in.
	lda D1SDR		;Get the status byte.
	sta STATUS		;Save it off.

	cli
	rts
									
;*********************************************************

QUERY_FORMAT	; This BURST subroutine sends a QUERY DISK FORMAT 
		; command to drive indicated by LF.
	;
	; Required Parameters:
	;	LF	Logical file number of device to query.
	;	TRACK	Physical track number to query.
	;	SIDE	Physical side of the disk to query,
	;		0 or 1.  SIDE is ignored if the disk is GCR.
	;	
	; Returned Parameters:
	;	NUMSEC	Number of sectors found on the track.
	;	TRACK	Logical track number found in the sector headers.
	;	MINSEC	Minimum logical sector no. found in sector headers. 
        ;	MAXSEC	Maximum logical sector no. found in sector headers.
	;	INTLV	Physical interleave between sectors.
	;	STATUS	This is the byte that QUERY_FORMAT returns.
	;		If an error is encountered, then none of the
	;		returned parameters are valid except STATUS.


	jsr SETU0	  ;Put "U0" at start of command string.

        lda #BURST_QUERY  ;QUERY DISK burst command

	ldx SIDE	  ; Set the side bit accordingly.
	bne 4$
	ora #$10

4$	sta cmdline+2

	lda TRACK	; Physical track offset.
	sta cmdline+3
	lda #$04	; length of command.
	sta CMDLEN
	jsr  sendcmd	; send cmd string

	sei		;Disable interrupts during handshake.
	bit D1ICR	;Clear any byte ready that's pending.

	jsr CLK_CHNG	;Change state of clock so 1571 sends next.
	jsr WAIT	;Wait for the first status byte.
	lda D1SDR	;Get the status byte (status of track 0).
	sta STATUS	;Save it off.

	and #$0f	;Was there an error?
	cmp #2
	bcs 5$		; branch if there was an error.

	lda STATUS	;Is the format GCR (if so no bytes follow)?
	bpl 5$

	jsr CLK_CHNG	;Change state of clock, so 1571 sends next.
	jsr WAIT	;Wait for next status byte to be ready.
	lda D1SDR	;Get it (status of track TRACK).
	sta STATUS	;Save it.

	and #$0f	;Was there an error in compiling MFM info?
	cmp #2	
	bcs 5$		; branch if an error.

	jsr CLK_CHNG	;Change state of clock, so 1571 sends next.
	jsr WAIT	;Wait for 'number of sectors byte' to be ready
	lda D1SDR	;Get it.
	sta NUMSEC	;Save it.

	jsr CLK_CHNG	;Change state of clock, so 1571 sends next.
	jsr WAIT	;Wait for 'logical track #' byte to be ready.
	lda D1SDR	;Get it.
	sta TRACK	;Save it.

	jsr CLK_CHNG	;Change state of clock, so 1571 sends next.
	jsr WAIT	;Wait for 'minimum sector #' byte to be ready.
	lda D1SDR	;Get it.
	sta MINSEC	;Save it.


	jsr CLK_CHNG	;Change state of clock, so 1571 sends next.
	jsr WAIT	;Wait for 'maximum sector #' byte to be ready.
	lda D1SDR	;Get it.
	sta MAXSEC	;Save it.

	jsr CLK_CHNG	;Change state of clock, so 1571 sends next.
	jsr WAIT	;Wait for 'interleave' byte to be ready.
	lda D1SDR	;Get it.
	sta INTLV	;Save it.

5$	cli
	rts
									
;*********************************************************

COMPARE_MEMORY	; This subroutine compares memory blocks in C128 memory.
		;
		; Required Parameters:
		;	.A	Number of pages to compare
		;	.X	First page of first memory block
		;	.Y	First page of second memory block
		;
		; Returned Parameters:
		;	STATUS	This is the byte that COMPARE_MEMORY
		;		returns, 0 if the two blocks are equal.

	sta temp

	lda $ff00	;Save old MMU setup.
	pha

	lda #$0e	;Set MMU for RAM0,KERNEL,I/O.
	sta $ff00

	stx buffer+1	;Set up MSB of 1st memory pointer.
	sty buffer2+1	;Set up MSB of 2nd memory pointer.
	ldx temp	;Number of pages to compare.
	
	lda #0		;Set up LSB's of memory pointers.
	sta buffer
	sta buffer2

	sta STATUS	;Initialize STATUS.

	ldy #0
2$	lda (buffer),y
	cmp (buffer2),y
	beq 1$

	lda #$ff	;Not equal!  Load STATUS with nonzero.
	sta STATUS
	bne 99$		;  (branch always)

1$	iny
	bne 2$		;More in this page?


	inc buffer+1
	inc buffer2+1
	dex		;# of pages counter.
	bne 2$
		
99$	pla		;Restore old memory configuration.
	sta $ff00

	rts
									
;*********************************************************

SENDCMD		; This BURST subroutine sends a command to logical file LF.
		;
		; Required Parameters:
		;	LF	Logical file number to send command to.
		;	CMDLINE	Command string to send.
		;	CMDLEN	Length of command string.
		;	
		; No parameter is returned.

	ldx LF
	jsr  chkout	; channel output (pointed to by .X)
	ldx  #0
	ldy  cmdlen	; send cmd 
1$	lda  cmdline,x
	jsr  bsout
	inx
	dey
	bne  1$

	jsr  clrchn	; send buffered char & eoi
	rts

;*********************************************************

OPEN_CHANNEL	; This BURST subroutine opens up a channel to device .X
		; and assigns it a logical file number with an optional
		; secondary address.  This subroutine performs the same
		; function as the BASIC open statement.
		;	
		; Required Parameters:
		;	.X	Device number
		;	.A	Logical file number
		;	.Y	This is the secondary address.  This
		;		number should be set to $FF if no
		;		secondary address is desired.
		;
		; Returned Parameters:
		;	.A	This is the byte that OPEN_CHANNEL returns
		;		1 if the routine was successful,
		;		0 otherwise.  If an error was encountered,
		;		then OPEN_CHANNEL will also set the CARRY
		;		bit.
		;


	jsr SETLFS	;Setup the logical file.
	
	lda #0		;No name/command string for OPEN (length=0).
	jsr SETNAM	;Setup the filename/command string for OPEN.

	jsr OPEN	;Open the logical file.

	rts

;*********************************************************

SETU0	lda #85		;'U'
	sta CMDLINE
	lda #48		;'0'
	sta CMDLINE+1
	rts


CLKLO		  	; set clock low
	pha
	lda  d2pra		
	ora  #clkout
	sta  d2pra
	pla
	rts

CLKHI			; set clock high
	lda  d2pra
	and  #$ff-clkout
	sta  d2pra
	rts

CLK_CHNG		;  change the state of the clock line output.
	lda D2PRA
	eor #clkout
	sta D2PRA
	rts

WAIT			;  wait for the shift register to be full or empty.
1$	lda #8
	bit D1ICR
	beq 1$
	rts


	.end
