Mercurial > hg > Members > kono > nitros9-code
view level1/modules/llcocosdc.asm @ 3036:42861a1dd59e
Low level driver update to CoCoSDC provided by Darren.
author | David Ladd <drencor-xeen@users.sf.net> |
---|---|
date | Fri, 09 Jan 2015 12:32:56 -0600 |
parents | 3afecdae6f53 |
children | c6eafb074443 |
line wrap: on
line source
******************************************************************** * llcocosdc - CoCo SDC Low-level driver * * $Id$ * * Edt/Rev YYYY/MM/DD Modified by * Comment * ------------------------------------------------------------------ * 2013/05/?? Boisy G. Pitre * Created. * 2014/11/27 tim lindner * Changed read and write routines to enter and leave command mode. * * 2014/12/22 Darren Atkinson * Total re-write. Provides MPI slot selection to permit co-existence * with a real floppy controller. Sends command to controller during * Init which locks out the floppy emulation capability. Adds GetStat * functions for extended commands. Uses TFM to retrieve data blocks * when a 6309 CPU is present. Supports the SS.DSize GetStat function * to query controller for the disk size. * * 2015/01/08 Darren Atkinson * Fixed bug in getSize which caused boot to fail when controller was * installed in slot 2, 3 or 4 of a Multi-Pak Interface. * NAM llcocosdc TTL CoCo SDC Low-level driver USE defsfile USE rbsuper.d USE cocosdc.d MPIREG equ $FF7F tylg SET Sbrtn+Objct atrv SET ReEnt+rev rev SET 0 MOD eom,name,tylg,atrv,start,0 * Low-level driver static memory area inside that of rbsuper ORG V.LLMem V.SDCMPI rmb 1 MPI slot containing CoCo SDC V.MaskIRQs rmb 1 contains $50 if IRQ/FIRQ should be masked name FCS /llcocosdc/ start lbra ll_init bra ll_read nop bra ll_write nop bra ll_getstat nop bra ll_setstat nop *** lbra ll_term *** clrb rts *-------------------------------------------------------------------------- * ll_getstat * * Entry: * Y = address of path descriptor * U = address of device memory area * * Exit: * CC = carry set on error * B = error code * ll_getstat ldx PD.RGS,y point X at stacked registers lda R$B,x get function code cmpa #SS.DSize beq getSize branch if "get disk size" tfr a,b andb #$F0 keep hi nibble cmpb #CMDEXD beq exCmd branch if extended command with data anda #$FF-4 make sure "TFM" bit is cleared cmpb #CMDEX beq exCmd branch if ext command without data *** Fall Thru *** *-------------------------------------------------------------------------- * ll_setstat * * Entry: * Y = address of path descriptor * U = address of device memory area * * Exit: * CC = carry set on error * B = error code * ll_setstat comb set carry ldb #E$UnkSvc return error rts *-------------------------------------------------------------------------- * SS.DSize - Return size information about a device * * Exit: * Carry cleared for no error. * A = Sector Size (1=256, 2=512, 4=1024, 8=2048) * * IF B = 0 * X = Number of Sectors (bits 31-16) * Y = Number of Sectors (Bits 15-0) * * IF B != 0 * B = Number of Logical Sides * X = Number of Logical Cylinders * Y = Number of Logical Sectors/Track * getSize lda #CMDEX primary command code ora PD.DRV,y combine drive num with command ldb #'Q "Query Size" sub-command leay ,u point Y at device mem ldu #$FFFF I/O buffer = none pshs x save frame ptr bsr CommSDC send query to controller puls x restore frame ptr ldb #E$NotRdy error code bcs sizeRet return if error clra bits 31-24 of size always zero ldb -1,y bits 23-16 of size std R$X,x return bits 31-16 in X ldd ,y bits 15-0 of size std R$Y,x return in Y inca A = 1: 256 byte sectors clrb B = 0: LBA device std R$D,x return in D sizeRet rts *-------------------------------------------------------------------------- * SDC Extended Device Commands * * Entered with SDC command code in A. * * Parameters are passed indirectly via the register frame: * X = address of 256 byte I/O buffer or $FFFF if none * U.L = optional param byte 1 * Y = optional param bytes 2 (hi) and 3 (low) * * Response values are returned indirectly via the register frame: * A = controller status * U.H = 0 * U.L = response byte 1 * Y = response bytes 2 (hi) and 3 (low) * exCmd pshs u,y,x preserve registers leay ,u Y = device memory area ldu R$X,x U = I/O buffer address ldb R$U+1,x B = param byte 1 ldx R$Y,x X = param bytes 2 and 3 bsr CommSDC do command protocol puls x restore register frame pointer stb R$A,x [A] = controller status lda #0 will clear top half of U ldb -1,y get response byte 1 (FF49) std R$U,x [U] = 1st response byte ldd ,y get response bytes 2,3 (FF4A,B) std R$Y,x [Y] = 2nd and 3rd response bytes ldb #E$NotRdy error code bcs exOut skip next if error clrb no error exOut puls y,u,pc return *-------------------------------------------------------------------------- * ll_write * * Write one sector. * * Entry: * Y = address of path descriptor * U = address of device memory area * * ll_write lda #CMDWRITE command code for Write bsr sectorIO common sector I/O bcc wrRet return if success ldb #E$Write error code for write wrRet rts *-------------------------------------------------------------------------- * ll_read * * Read one sector. * * Entry: * Y = address of path descriptor * U = address of device memory area * ll_read lda #CMDREAD+4 command code for 6309 Read (TFM) fcb $11 next instr is "LDE" on 6309 lda #CMDREAD command code for 6809 Read bsr sectorIO common sector I/O bcc rdRet return if success ldb #E$Read error code for read rdRet rts *-------------------------------------------------------------------------- * Funnel code for sector read and write * * Entry: * A = read/write command code * Y = address of path descriptor * U = address of device memory area * sectorIO ldb #1 highest drive number allowed subb PD.DRV,y range check drive number bcc drvOK branch if valid ldb #E$Unit error code for bad drive number puls x,pc pop top address then return drvOK ora PD.DRV,y combine drive num with command leay ,u point Y at device mem ldb V.PhysSect,u B = hi byte of LSN ldx V.PhysSect+1,u X = lo word of LSN ldu V.CchPSpot,u U = buffer address *** Fall Thru *** *-------------------------------------------------------------------------- * CommSDC * * This is the core routine used for all * transactions with the SDC controller. * * Entry: * A = Command code * B = LSN hi byte / First parameter byte * X = LSN lo word / 2nd and third param bytes * Y = Address of driver device memory area * U = Address of I/O buffer ($FFFF = none) * * Exit: * Carry set on error * B = controller status code * Y = address of SDC Data Register A (FF4A) * CommSDC * Save current MPI setting and activate the SDC slot pshs cc preserve CC (interrupt masks) lsr ,s shift carry out of saved CC pshs a,b save cmd code, alloc byte for saving MPI tfr cc,a get copy of current CC ora V.MaskIRQs,y set I and F masks if FDC present tfr a,cc update I and F in CC lda MPIREG get current MPI slot sta 1,s save on stack where B was pushed anda #$30 mask out current SCS nibble ora V.SDCMPI,y insert SCS nibble for SDC slot ldy #DATREGA setup Y for hardware addressing tsta was an SDC controller found? bmi sdcNone exit if no sta MPIREG activate MPI slot * Put controller in Command mode lda #CMDMODE the magic number sta -10,y send to control latch (FF40) puls a pull saved command code back into A * Put input parameters into the registers. * It does no harm to put random data in the * registers for commands which dont use them. stb -1,y high byte to param reg 1 stx ,y low word to param regs 2 and 3 * Wait for Not Busy. lbsr waitForIt run polling loop bcs cmdExit exit if error or timed out * Send command to controller sta -2,y to command register (FF48) * Determine if a data block should be sent. * The code for any command which requires * a data block will have bit 5 set. bita #$20 test the "send block" command bit beq rxBlock branch if no block to send * Wait for Ready to send bsr waitForIt run polling loop bcs cmdExit exit if error or timed out leax ,u move data address to X IFGT Level-1 bita #$40 extended command or sector write? lbne txCmd branch if extended command ENDIF * Send 256 bytes of data ldd #32*256+8 32 chunks of 8 bytes txChunk ldu ,x send one chunk... stu ,y ldu 2,x stu ,y ldu 4,x stu ,y ldu 6,x stu ,y abx point X at next chunk deca decrement chunk counter bne txChunk loop until all 256 bytes sent * Wait for command to complete txCompl lda #5 timeout retries txWait bsr waitForIt run polling loop bitb #BUSY test BUSY bit beq cmdExit exit if completed deca decrement retry counter bne txWait repeat if until 0 coma set carry for timeout error bra cmdExit exit * Set error condition and exit when no SDC hardware found sdcNone leas 1,s pop saved command code comb set carry ldb #E$Unit error number for missing hardware bra cmdExit exit * For commands which return a 256 byte response block the * controller will set the READY bit in the Status register * when it has the data ready for transfer. For commands * which do not return a response block the BUSY bit will * be cleared to indicate that the command has completed. * rxBlock bsr longWait run long status polling loop bls cmdExit exit if error, time out or completed leax 1,u test the provided buffer address beq cmdExit exit if "no buffer" ($FFFF) bita #$04 test the "TFM" command bit bne rx6309 branch if set leax ,u move data address to X IFGT Level-1 bita #$40 extended command or sector read? bne rxCmd branch if extended command ENDIF * 6809 read transfer loop into current task space ldd #32*256+8 32 chunks of 8 bytes rxChunk ldu ,y read one chunk... stu ,x ldu ,y stu 2,x ldu ,y stu 4,x ldu ,y stu 6,x abx update X for next chunk deca decrement chunk counter bne rxChunk loop until all 256 bytes transferred bra cmdOK exit with SUCCESS * 6309 read transfer using TFM into current task space rx6309 fcb $10,$86,$01,$00 [ldw #256] block size = 256 orcc #$50 ensure interrupts are masked leax 1,y point X at Data Register B (FF4B) fcb $11,$3B,$13 [tfm x,u+] read block cmdOK clrb status code for SUCCESS, clear carry * Exit cmdExit puls a pull saved MPI settings rol ,s rotate carry into saved CC on stack clr -10,y end command mode sta MPIREG restore saved MPI settings puls cc,pc restore irq masks, update carry and return *-------------------------------------------------------------------------- * Wait for controller status to indicate either "Not Busy" or "Ready". * Will time out if neither condition satisfied within a suitable period. * * Exit: * CC.C set on error or time out. * CC.Z set on "Not Busy" status (if carry cleared). * B = status * A, Y and U are preserved. * longWait bsr waitForIt enter here for doubled timeout bcc waitRet return if cleared in 1st pass waitForIt ldx #0 setup timeout counter waitLp comb set carry for assumed FAIL ldb -2,y read status bmi waitRet return if FAILED lsrb BUSY --> Carry bcc waitDone branch if not busy bitb #READY/2 test READY (shifted) bne waitRdy branch if ready for transfer bsr waitRet consume some time ldb #$81 status = timeout leax ,-x decrement timeout counter beq waitRet return if timed out bra waitLp try again waitDone clrb Not Busy: status = 0, set Z waitRdy rolb On Ready: clear C and Z waitRet rts return *-------------------------------------------------------------------------- * Transfers of command and response data require special handling * in Level 2 due to separate address space for System/User tasks. * IFGT Level-1 * Send command data from User task space txCmd bsr tskPrep B = task#, push word counter txWord os9 F$LDABX get byte from user space buffer sta ,y send 1st half of word leax 1,x increment buffer address os9 F$LDABX get byte from user space buffer sta 1,y send 2nd half of word leax 1,x increment buffer address dec ,s decrement word counter bne txWord loop if more to send leas 1,s pop word counter lbra txCompl go wait for command completion tskPrep ldu D.Proc get current process ptr ldb P$Task,u get task # for current process lda #128 number of words to transfer (256 bytes) ldu ,s+ half-pop return address into U sta ,s leave word counter on the stack tfr u,pc return * Read response data into User task space rxCmd bsr tskPrep B = task#, push word counter rxWord lda ,y read 1st half of word os9 F$STABX store in user space buffer leax 1,x increment buffer address lda 1,y read 2nd half of word os9 F$STABX store in user space buffer leax 1,x increment buffer address dec ,s decrement word counter bne rxWord loop if more to read leas 1,s pop word counter bra cmdOK exit with SUCCESS ENDIF *-------------------------------------------------------------------------- * ll_init * * Entry: * Y = address of device descriptor * U = address of device memory area * * Exit: * CC = carry set on error * B = error code * ll_init pshs cc save irq masks orcc #$50 mask irqs ldd #$8080 prepare "not found" values pshs dp,b,a alloc variables on stack ldx #CMDREG point X at FDC command reg ldb MPIREG get current MPI slot andb #$33 mask out the unused bits stb 2,s save on stack (in DP position) orb #$03 start SCS scan at slot 4 chkSlot stb MPIREG activate slot being scanned lda ,s have we already found CoCo SDC ? bpl chkFDC branch if yes lda #$64 test pattern sta -6,x store at Flash Data Reg address lda -5,x get value from Flash Ctrl Reg clr -6,x clear Flash Data Reg eora -5,x get changed bits from Ctrl Reg suba #$60 did expected bits change? bne chkFDC branch if not an SDC stb ,s record the SDC slot bra nxtSlot go scan next slot chkFDC lda 1,s have we already found an FDC ? bpl nxtSlot branch if yes bsr fdcTest test if FDC present bne nxtSlot branch if not present stb 1,s record the FDC slot nxtSlot decb decrement SCS slot number bitb #$08 have we scanned all slots? beq chkSlot loop if more to scan lda ,s get slot with CoCo SDC bmi saveSDC branch if none cmpa 2,s same as original slot selection? bne saveSDC branch if no eora #$01 change original slot to.. sta 2,s ..be something else eora #$01 back to the true SDC slot saveSDC anda #$83 keep SDC slot number or "not found" sta V.SDCMPI,u store SDC slot info in device mem lda #$50 irq masks will be needed if FDC present ldb 1,s was an FDC found? bpl useSlot branch if yes clra irq masks not needed ldb 2,s get original MPI slot selection useSlot stb MPIREG activate default MPI slot sta V.MaskIRQs,u store irq masks in device mem lda $FF22 reset any latched CART interrupt leas 3,s pop variables off the stack puls cc restore saved irq masks * Disable Floppy Emulation capability in SDC controller lda #$C0 primary command code ldb #'g secondary command to "Set Global Flags" ldx #$FF80 mask/flag bytes leay ,u point Y at static variables ldu #$FFFF "no buffer" address lbsr CommSDC send command to controller clrb no error rts return * Test for presence of FDC in active slot fdcTest lda #$D0 FORCE INTERRUPT command sta ,x send to FDC command register bsr fdcDelay wait awhile lda ,x read FDC status lda 3,x read FDC data register coma invert all bits sta 3,x put inverted data back bsr fdcDelay wait awhile suba 3,x test if read matches write fdcDelay pshs y,x,d,cc push regs mul delay cycles puls cc,d,x,y,pc restore regs and return *-------------------------------------------------------------------------- EMOD eom EQU * END