;* ------------------ * ;* ---------------------- BURST SUBROUTINES -- (rev 2) ----------- * ;* ------------------ * ;* * ;* These assembly language sub-routines are designed for use in your * ;* BASIC and machine code programs. A BASIC program needs simply to * ;* POKE the appropriate values into the variable locations shown below, * ;* and then SYS to the desired routine. All of the BURST protocol and * ;* handshaking is done for you. The BASIC program can then PEEK any * ;* values returned. * ;* ;* The routines in this listing that require data buffer storage areas * ;* are passed the location of the buffer in BUFLOC. BUFLOC points to * ;* RAM location in RAM bank 0 of the start of the buffer. Since the * ;* KERNEL and I/O are needed, you must put BUFLOC below $C000. But be * ;* sure to put it in RAM above your BASIC text. As a general rule, * ;* work your way back from $C000 and you'll be OK. * ;* ;* To use these routines, your BASIC program must first BLOAD the file * ;* 'BURST SUBS.BIN'. The routines load at $1300, so they are in a * ;* safe place below BASIC text area. * ;* ;* There is no BURST FORMAT routine provided. The following BASIC * ;* commands will format physical tracks 10 through 20 of the disk with * ;* 5 1024 byte sectors: * ;* ;* OPEN 1,8,15 * ;* PRINT#1,"U0";CHR$(3);CHR$(20);CHR$(5);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 1581 * ;* 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. Any formatting errors can be checked via the command * ;* channel. * ;* ;* Since the BURST commands make use of the command channel to the * ;* drive, the command channel must first be OPENed in your BASIC * ;* program. The logical file number which you assigned to the command * ;* channel should be poked to LF before calling any of these routines. * ;* * ;******************************************************************** ; Variables - Values from BASIC can be POKEd, PEEKed to these areas. ;******************************************************************** *=$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 ;**************************************************** ; Other variables used in the following routines.... ;**************************************************** cmdline .byte 'u0' ; Burst prefix. *=*+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 of available burst routines. SYS to these locations ; from BASIC. The BURST routines themselves can then be modified ; or customized without affecting the SYS locations from BASIC. ;****************************************************************** *=$1340 J_INQUIRE_FORMAT jmp INQUIRE_FORMAT J_PHYSICAL_READ jmp PREAD J_LOGICAL_READ jmp LREAD J_PHYSICAL_WRITE jmp PWRITE J_LOGICAL_WRITE jmp LWRITE J_MEMORY_READ jmp MEMORY_READ J_MEMORY_WRITE jmp MEMORY_WRITE J_DUMP_CACHE jmp DUMP_CACHE J_QUERY_FORMAT jmp QUERY_FORMAT J_COMPARE_MEMORY jmp COMPARE_MEMORY ;*************************************** ; Locations of important C128 stuff... ;*************************************** chkout=$ffc9 ; kernel channel output clrchn=$ffcc ; kernel clear channel setlfs=$ffba ; kernel set logical file number setnam=$ffbd ; kernel set filename bsout =$ffd2 ; kernel basic input/output spin_out=$ff47 ; Set up fast serial for input or output. ; SEC for output, CLC for input. 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 ;****************************** PBURSTRD =$00 ; Physical burst read. PBURSTWR =$02 ; Physical burst write. LBURSTRD =$80 ; Logical burst read LBURSTWR =$82 ; Logical burst write. BURST_INQUIRE =$04 ; Burst inquire. DUMPCACHE =$9D ; Dump track cache ('force' bit set) BURST_QUERY =$8A ; Query disk format. ;********************************************************* ;* --------------- BURST ROUTINES -------------------- * ;********************************************************* LREAD ;Logical sector read from the device indicated by LF. ;The track and sector are in TRACK, SECTOR. The location ;Status byte from drive is returned in STATUS. lda #lburstrd ;logical burst read command sta cmdline+2 lda #$01 sta SECSIZE ;Logical sector size is always 256 bytes. jmp READ PREAD ;Physical sector read from device indicated by LF. ;The track and sector are in TRACK, SECTOR. The location ;of start of the C128 buffer to put the read data in BUFLOC. ;The physical sector size in SECSIZE (1=256,2=512,4=1024). ;Number of sectors in NUMSEC. ;Physical side of the disk in SIDE (0 or 1). ;Status byte from drive is returned in STATUS. 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 ;Update 'zero' sector flag. sta 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 ;**************************************************************** LWRITE ;Logical sector write to the device indicated by LF. ;The track and sector are in TRACK, SECTOR. The location ;of start of C128 buffer containing write data in BUFLOC. ;Status byte from drive is returned in STATUS. lda #lburstwr sta cmdline+2 ; burst write lda #$01 sta SECSIZE ; Logical sector size is always 256 bytes. jmp WRITE PWRITE ;Physical sector write to the device indicated by LF. ;The track and sector are in TRACK, SECTOR. The location ;of start of C128 buffer containing write data in BUFLOC. ;The physical sector size in SECSIZE (1=256,2=512,4=1024). ;Number of sectors in NUMSEC. ;Physical side in SIDE. ;Status byte from drive is returned in STATUS. 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 ;Sends an INQUIRE DISK command to the drive indicate by ;the logical file (LF). Status is returned in STATUS. jsr SETU0 ;Put "U0" at start of command string. lda #BURST_INQUIRE ; inquire burst command sta cmdline+2 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 1581 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 ;Sends a QUERY DISK FORMAT command to the drive indicate by ;the logical file (LF). Physical track number to query ;should be provided in TRACK. Physical side should ;be provided in SIDE. Status is returned in STATUS. ;Number of sectors found on the track returned in NUMSEC. ;Logical track number found in the sector headers returned ;in TRACK. Minimum logical sector number found in the ;sector headers is returned in MINSEC. The maximium ;logical sector is returned MAXSEC. Physical interleave ;is returned in INTLV. ;If an error is encountered in compiling this information ;(as indicated by STATUS), then none of the return values ;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 1581 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. jsr CLK_CHNG ;Change state of clock, so 1581 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? cmp #2 bcs 5$ ; branch if an error. jsr CLK_CHNG ;Change state of clock, so 1581 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 1581 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 1581 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 1581 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 1581 sends next. jsr WAIT ;Wait for 'interleave' byte to be ready. lda D1SDR ;Get it. sta INTLV ;Save it. 5$ cli rts ;*************************************************************** MEMORY_READ ;Burst memory read of the 1581. Page in 1581 memory to ;start reading at in .X, number of pages to read in .Y, ;location to store data in C128 memory in BUFLOC. ;Logical file to be read from in LF. 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 #$3E ;('>') 'burst memory read' command string. sta CMDLINE+2 ; ( "U0>MR" ) lda #$4D ;('M') sta CMDLINE+3 lda #$52 ;('R') sta CMDLINE+4 stx CMDLINE+5 ; 1581 page to start reading from. sty CMDLINE+6 ; # of pages to read. lda #$07 ; 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 lda #0 ; clear the 'empty sector(s)' flag. sta flag sei ; No irq's allowed during handshake. bit d1icr ; clear pending jsr CLK_CHNG ;Change state of clock. ldy #0 3$ jsr WAIT ;Wait for the byte to be shifted in. jsr CLK_CHNG ;Change clock so next byte is sent. lda d1sdr ; get data sta (buffer),y ; and save it while next byte is transmitted. ora flag ; Update 'zero' flag. sta flag iny bne 3$ ;Any more in this page? inc BUFFER+1 dec CMDLINE+6 ;Any more pages to do? bne 3$ 5$ cli pla ;Restore old memory configuration. sta $ff00 rts ;************************************************************ MEMORY_WRITE ;Burst memory write to the 1581's memory. The ;location in C128 memory to send data from in BUFLOC. ;The page in 1581 memory to start writing to in .X. ;The number of pages to write in .Y. ;Logical file to be written to in LF. 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 #$3E ;('>') 'burst memory write' command string. sta CMDLINE+2 ; ( "U0>MW" ) lda #$4D ;('M') sta CMDLINE+3 lda #$57 ;('W') sta CMDLINE+4 stx CMDLINE+5 ; 1581 page to start writing to. sty CMDLINE+6 ; # of pages to write. lda #$07 ; 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 lda #clkin ;Initial clock status. sta oldclk ldy #0 sei ;No IRQ's allowed during handshake. sec ; Set to output mode. jsr SPIN_OUT 2$ lda d2pra ;Wait for state (slow clock line) change. eor OLDCLK and #CLKIN beq 2$ eor OLDCLK ;Change status of OLDCLK variable. sta OLDCLK lda (BUFFER),y ;Get data to write. sta D1SDR ;Send it. jsr WAIT ;Wait for the byte to be sent. iny bne 2$ ;More in this page? inc BUFFER+1 dec CMDLINE+6 bne 2$ ;Any more pages to send? cli pla ;Restore old memory configuration. sta $ff00 rts ;********************************************************** DUMP_CACHE ;Dumps the track cache at 1581 $0C00 to the physical track ;specified in TRACK, on the side specified in SIDE. The ;'force' bit is set, so it is written whether dirty or not. jsr SETU0 lda #DUMPCACHE ;Dump track cache command. ldx SIDE ; Set SIDE bit accordingly. beq 1$ ora #$40 1$ sta CMDLINE+2 ; Put the command byte into command string. lda TRACK sta CMDLINE+3 ; Physical track to dump cache to. lda #4 sta CMDLEN ;Length of the command string. jsr SENDCMD sei ;Disable interrupts during handshake. bit D1ICR ;Clear any byte ready that's pending. jsr CLK_CHNG ;Change clock so 1581 will send status byte. jsr WAIT ;Wait for byte to be shifted in. lda D1SDR ;Get the status byte. sta STATUS ;Save it off. cli rts ;************************************************************* COMPARE_MEMORY ;Compares memory blocks in the C128 memory. ;Number of pages to compare in .A. ;First page of first memory block in .X. ;First page of second memory block in .Y. ;If they are equal, then STATUS=0. 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 ;Sends the command in CMDLINE to the logical file ;indicated by LF. Length of the command should be in CMDLEN. 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 ;*********************************************************** 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