;* ------------------------ * ;* 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