diff --git a/amigodos.s b/amigodos.s new file mode 100644 index 0000000..8dde35b --- /dev/null +++ b/amigodos.s @@ -0,0 +1,2531 @@ +;------------------------------------------------------------------------------ +; +; Originally coded in 2009-2011 by Matthew A. Hudson hexsane +; at gmail.com +; Code style & format borrowed from CFFA v1 source code +; use ca65 to assemble +; +; HP-IB AMIGO protocol ROM replacement for genuine Apple II GP-IB controller. +; +; Was assembled and working for floppy drive in HP9133xv +; It will work with the hard drive but you will need to change the +; geometry and re assemble. +; +; Note about assembly: I had to offset the code by either 128 or 256 bytes +; (meaning you lose that space in the ROM) but I can't recall why. You may +; need to fiddle around with options to get it in the correct location to +; make the card happy. I've forgotten most of the Apple II I/O layout at this +; point in time. +; +; There is probably code in here from other sources but I couldn't recall from +; who at this point. If you see something that needs credit please let me know +; and I will happily do so. +; + +.define EQU = +.define TRUE 1 +.define FALSE 0 + +;.segment "EXEHDR" ; just to keep the linker happy +;.segment "STARTUP" ; just to keep the linker happy + +; +; Firmware Version Information +; +FIRMWARE_VER EQU $12 ;Version 1.2 (Version of this code) +SPDRIVERVERSION EQU $1200 ;SmartPort version 1.2 +GSOS_DRIVER EQU $02 ;GS/OS driver will check this byte to see if it + ;is still compatible with this firmware. + ;Increment by one, when something changes that + ;would require a change in the GS/OS driver. + ;Otherwise only change the FIRMWARE_VER for all + ;other changes. + ;01 = ProDOS Driver supporting 2 drives + ;02 = SmartPort Driver supporting 4 drives + ;03 = SmartPort Driver supporting 8 drives +; +; Firmware Configuration Settings: +; +SMARTPORT EQU FALSE ; TRUE +BLOCKOFFSET EQU 0 ;0..255: LBA of first block of first partition +PARTITIONS32MB EQU 4 ;Number of 32MB Partitions supported + ;Remember, ProDOS only supports 13 total + ;volumes for all devices, floppies, SCSI drives, + ;RAM drives, etc. + + +;------------------------------------------------------------------------------ +; To enable debug output, set DEBUG = TRUE, only if you have a Apple Super +; Serial card in slot 2. This will output one line of text for each request +; made to the firmware, which can be seen on a computer or terminal attached to +; the SSC. +; +; NOTE: If you use DEBUG=TRUE and you don't have an Apple Super Serial card in +; slot 2, your computer might hang in the routine DSChar, waiting for +; the SSC status bit to say it is okay to write to the 6551 UART. +; +; Set your terminal (software) at the remote end as follows: +; BaudRate: 19200 +; Data Bits: 8 +; Parity: None +; Stop Bits: 1 +; +; Example debug output at terminal from CAT command in ProDOS 8. Card is in +; slot 6. ProDOS makes a ProDOS 8 call to the firmware to read block 2 from +; unit: 60 into buffer memory at $DC00. +; +; P8: Rd B:0002 U:60 A$DC00 Chk$6711 +; +; Rd = ProDOS Read ($01). Also could be Wr = write ($02), St = Status ($00) +; U:60 = ProDOS 8 unit number $60 Slot 6, drive 1 +; A$DC00 = ProDOS buffer address +; Chk$6711 = Simple block checksum used to visually check data integrity +; +; NOTE: When DEBUG is true, some zero-page locations are used. The data at +; these locations are saved and restored and should not impact other programs. + +;DEBUG = FALSE +DEBUG = TRUE + +;------------------------------------------------------------------------------ +; Driver constant definitions +; +INITDONESIG EQU $A5 ;Device init is done signature value +CR EQU $0D +BELL EQU $07 + +; ProDOS request Constants +PRODOS_STATUS EQU $00 +PRODOS_READ EQU $01 +PRODOS_WRITE EQU $02 +PRODOS_FORMAT EQU $03 + +;ProDOS Return Codes +PRODOS_NO_ERROR EQU $00 ;No error +PRODOS_BADCMD EQU $01 ;Bad Command (not implemented) +PRODOS_IO_ERROR EQU $27 ;I/O error +PRODOS_NO_DEVICE EQU $28 ;No Device Connected +PRODOS_WRITE_PROTECT EQU $2B ;Write Protected +PRODOS_BADBLOCK EQU $2D ;Invalid block number requested +PRODOS_OFFLINE EQU $2F ;Device off-line + +;SmartPort return codes +BAD_UNIT_NUMBER EQU $11 + +; HP-IB AMIGO Commands +AMIGO_FORMAT EQU 12 +AMIGO_STATUS EQU 8 +AMIGO_DSJ EQU 16 +AMIGO_SEEK EQU 8 +AMIGO_SEEK_SEC EQU 2 +AMIGO_BUF_WRITE EQU 9 +AMIGO_BUF_WRITE_SEC EQU 8 + +;9914 bits +BIM EQU $20 ;Byte in mask +BOM EQU $10 ;Byte out mask +EOIMK EQU $08 ;EOI mask +RESET EQU $80 ;Software chip reset +RSTCLR EQU $00 ;Stop software reset +HDFA EQU $83 ;Holdoff all data +HDACLR EQU $03 ;Holdoff clear +LON EQU $89 ;Listen only +TON EQU $8A ;Talk only +SIC EQU $8F ;Send interface clear +SICLR EQU $0F ;Clear SIC +RHDF EQU $02 ;Release RFD Holdoff +FEOI EQU $08 ;Send EOI with next byte +GTS EQU $0B ;Go to standby +TCA EQU $0C ;Take control asynchronously +TCS EQU $0D ;Take control synchronouly +PPOL_ON EQU %10001110 +PPOL_OFF EQU %00001110 + + +; ATA Commands Codes +ATACRead EQU $20 +ATACWrite EQU $30 +ATAIdentify EQU $EC + +;Constants for Wait +; Constant = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 + +WAIT_100ms EQU 197 +WAIT_40ms EQU 124 +WAIT_100us EQU 4 +;------------------------------------------------------------------------------ +; Slot I/O definitions +; +mslot = $7F8 ;Apple defined location for the last active slot + +IOBase = $C080 +; TMS9914 I/O Registers +INT0 = IOBase+0 +INTM0 = IOBase+0 +INTM1 = IOBase+1 +AUXCMD = IOBase+3 +BUSSTAT = IOBase+3 +CMD_PT = IOBase+6 +GPIB_DIN = IOBase+7 +GPIB_DOUT = IOBase+7 + + +ATADataHigh = IOBase+0 +SetCSMask = IOBase+1 ;Two special strobe locations to set and clear + ; MASK bit that is used to disable CS0 line to + ; the CompactFlash during the CPU read cycles +ClearCSMask = IOBase+2 ; that occur before every CPU write cycle. + ; The normally inoccuous read cycles were + ; causing the SanDisk CF to double increment + ; during sector writes commands. + +ATADevCtrl = IOBase+6 ;when writing +ATAAltStatus = IOBase+6 ;when reading +ATADataLow = IOBase+8 +ATAError = IOBase+9 +ATASectorCnt = IOBase+10 +ATASector = IOBase+11 +ATACylinder = IOBase+12 +ATACylinderH = IOBase+13 +ATAHead = IOBase+14 +ATACommand = IOBase+15 ; when writing +ATAStatus = IOBase+15 ; when reading + +; Scratchpad RAM base addresses. Access using the Y register containg the slot # +; +;DriveResetDone = $478 ;remember the device has been software reset +;DriveNumber = $4f8 ;normally 0 to 3 for four 32MB partitions +;SerialInitDone = $578 ;For debug: if $A5 then serial init is complete +;DrvBlkCount0 = $5f8 ;low byte of usable block count + ; (excluding first BLOCKOFFSET blocks) +;DrvBlkCount1 = $678 ;bits 8..15 of usable block count +;DrvBlkCount2 = $6f8 ;bits 16..23 of usable block count +;DrvMiscFlags = $778 ;bit 7 = raw LBA block access +;Available2 = $7f8 ;not currently used + +; Scratchpad RAM base address. Access using the Y register containg the slot # +; Remapped for HPIB +DriveResetDone = $478 ;remember the device has been software reset +DriveNumber = $4f8 ;normally 0 to 3 for four 32MB partitions +SerialInitDone = $578 ;For debug: if $A5 then serial init is complete +DrvBlkCount0 = $5f8 ;low byte of usable block count + ; (excluding first BLOCKOFFSET blocks (FIXME?)) +DrvBlkCount1 = $678 ;bits 8..15 of usable block count +DrvBlkCount2 = $6f8 ;bits 16..23 of usable block count +DrvBlkCount3 = $7f8 +DrvMiscFlags = $778 ;bit 7 = raw LBA block access + +Spare = $778 +;Available2 = $7f8 ;not currently used + + + +;------------------------------------------------------------------------------ +; Zero-page RAM memory usage + +.IF DEBUG ;data at these locations saved and restored +MsgPointerLow = $EB +MsgPointerHi = $EC +CheckSumLow = $ED +CheckSumHigh = $EE +.ENDIF + +zpt1 = $EF ;data at this location is saved/restored + +StackBase = $100 + +; ProDOS block interface locations +pdCommandCode = $42 +pdUnitNumber = $43 +pdIOBuffer = $44 +pdIOBufferH = $45 +pdBlockNumber = $46 +pdBlockNumberH = $47 + +; Arbitrary locations for Smartport data, +; these locations are saved/restored before exit. +spCommandCode = pdCommandCode + +spParamList = $48 ;2 bytes +spCmdList = $4A ;2 bytes +spCSCode = $4C +spSlot = $4D +SLOT = $4D +spSlotX16 = $4E +SLOTx16 = $4E +spLastZP = spSlotX16 + +spZeroPgArea = pdCommandCode ; $42 +spZeroPgSize = spLastZP-spZeroPgArea+1 + +DBC0 = $48 ;2 bytes +DBC1 = $49 +DBC2 = $4A ;2 bytes +DBC3 = $4B +Carry = $4C +modSpare = $4D + + +;------------------------------------------------------------------------------ +; Apple II ROM entry points +; +; We can use these at boot time, but not while handling a call through +; our ProDOS or SmartPort entry points, as the ROM may not be available. +; +SetVID = $FE89 +SetKBD = $FE93 +COUT = $FDED +INIT = $FB2F +HOME = $FC58 +ROMWAIT = $FCA8 +AppleSoft = $E000 +MONRTS = $FF58 + +;------------------------------------------------------------------------------ +; Start of Peripheral Card ROM Space $Cn00 to $CnFF +; A macro is used here so that this code can be easily duplicated for each slot +; instead of by hand using the EPROM programmer. This is possible done because +; the hardware does not overlay the C1xx address space at C2xx, C3xx, etc. +; automatically. Instead the base address for the EPROM is $C000 but is enabled +; only when a valid address in the range of $C100 to $CEFF is on the bus. This +; allows for the development of slot specific behaviors in firmware, if desired. +; Currently that is not being done, instead the same slot code is repeated for +; every slot ROM space. Addresses $C000 to $C0FF are not decoded by the +; hardware as it is Apple's internal I/O. Any access of $CF00 to $CFFF is +; decoded by the card to reset the Expansion ROM flip-flop, but remember to +; use always address $CFFF for that task. + +;.macro CnXX SLOTADDR, SLOTx16, SLOT +.ORG $C600 +; .local P8DriverEntry +; .local P8Driver +.IF SMARTPORT + .local SmartPortEntry + .local SPDriver +.ENDIF +; .local Boot +; .local Error +; .local NotScanning +; .local wasteTime +; .local ErrorMsgDisplay +; .local msgLoop +; .local msgDone +; .local ErrorMessage + + lda #$20 ;$20 is a signature for a drive to ProDOS + ldx #$00 ;$00 " + lda #$03 ;$03 " + +.IF SMARTPORT + lda #$00 +.ELSE + lda #$3c ;$3c " +.ENDIF + ;clc + ;bcc Boot + bra Boot + +;------------------- Non-boot P8 driver entry point --------------------- +; The EPROM holding this code is decoded and mapped into $C100 to $CEFF, +; it is not nessecary to dynamically determine which slot we are in, as is +; common in card firmware. This code is in a MACRO and is located absolutely +; per slot. Any code in this MACRO that is not relocatable will have to be +; based on the MACROs parameters SLOTADDR, SLOTx16, SLOT. + +P8DriverEntry: + nop + bra P8DMid + ; FIXME These jumps MUST be closer <128 bytes currently 184 +; jmp P8Driver ;located at $Cn0A for best compatibility + jmp P8Driver ;FIXME +.IF SMARTPORT +SmartPortEntry: ;By definition, SmartPort entry is 3 bytes + ; after ProDOS entry + jmp SPDriver +.ENDIF + +Boot: + jsr MONRTS ;Get this location on stack + tsx ;Find page byte on stack + lda StackBase,x + sta mslot + and #$0f + tay + asl a + asl a + asl a + asl a + tax + lda SLOT + pha + lda SLOTx16 + pha + sty SLOT + stx SLOTx16 + + ;ldy #SLOT ;Y reg now has $0n for accessing scratchpad RAM + ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O +; lda #>SLOTADDR ;loads A with slot# we are in:$Cn +; sta mslot ;Apple defined location reserved for last slot + ; active. MSLOT needs the form of $Cn + bit $cfff ;turn off expansion ROMs + +;; +;; Initialize the 9914 to known state +;; +; jsr Init9914 +;; +;; Call DSJ (Device Specified Jump) for status code and reset DSJ to 0. +;; +; jsr DSJ +;; +;; Get drive status +;; +; jsr STAT +; + +; jsr DSString +; .byte " St",0 +; jsr DisplayParms +; +; Need to wait here (before CheckDevice) in case the CFFA RESET jumper +; is enabled, or a Delkin Devices CF card never becomes ready. +; +; ldy #5 +;wasteTime: +; lda #WAIT_100ms +; jsr ROMWAIT +; dey +; bne wasteTime +; ldy SLOT + +; jsr CheckDevice +; bcs Error + + lda #PRODOS_READ ;Request: READ block + sta pdCommandCode + stz pdIOBuffer ;Into Location $800 + stz pdBlockNumber ;ProDOS block $0000 (the bootloader block) + stz pdBlockNumberH + lda #$08 + sta pdIOBufferH + stx pdUnitNumber ;From unit number: $n0 (where n=slot#), + ; so drive bit is always 0 + + + ;jsr P8Driver ;Read bootloader from device's block 0 into + ; location $800 + + clv + jsr $FF58 + bvc P8Drts + + bcs Error ;Check for error during bootblock read + + lda $800 ;Check the first byte of boot loader code. + cmp #$01 ;If bootload code is there, this byte = $01 + bne Error + lda $801 ;If second byte is a 0, it's invalid + ; (we'd JMP to a BRK) + beq Error + + ldx pdUnitNumber ;X should contain the unit number when jumping + ; to the bootloader + jmp $801 ;No errors, jump to bootloader just read. + +P8DMid: + bra P8Driver + +; If any error occured, like drive not present, check to see if we are in a +; boot scan, if so re-enter scan routine, else drop to Applesoft, aborting boot. +Error: + lda $00 + bne NotScanning + lda $01 + cmp mslot + bne NotScanning + jmp $FABA ;Re-enter Monitor's Autoscan Routine + +;The boot code must have been called manually because we are not in a slot scan. +NotScanning: + jsr SetVID + jsr SetKBD +; +;Display error message +; + jsr INIT ;text mode, full screen, page 1 + jsr HOME + ldy #0 +msgLoop: + lda ErrorMessage,y + beq msgDone + ora #$80 + jsr COUT + iny + bra msgLoop +msgDone: + jmp AppleSoft + +ErrorMessage: + .byte CR,CR,CR + .byte "Dev missing, not formatted,",CR + .byte "or incompatible.",CR +; "vX.Y" built automatically: + .byte "Ver:",$30+(FIRMWARE_VER/16),".",$30+(FIRMWARE_VER & $F) +; Beep, then end-of-message: + .byte BELL,$00 + + +;------------------- Non-boot entry point for driver code ----------------- +; +; Handle a ProDOS call +; +; Setup MSLOT, X and Y registers. +; This must be done every time we enter this driver. +; +P8Drts: + tsx + dex + dex + txs +P8Driver: + jsr MONRTS ;Get this location on stack + tsx ;Find page byte on stack + lda StackBase,x + sta mslot + and #$0f + tay + asl a + asl a + asl a + asl a + tax + ;sty SLOT + ;stx SLOTx16 + + ;ldy SLOT ;Y reg now has $0n for accessing scratchpad RAM + ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O + ;lda #>SLOTADDR ;loads A with slot# we are in:$Cn(where n=slot#) + ;sta mslot ;Apple defined location reserved for last slot + ; active. MSLOT needs the form of $Cn + bit $cfff ;turn off other ROM that might be on + + jsr P8AuxROM + bit $ff58 + rts + +.IF SMARTPORT +; NO SMARTPORT DRIVER BUILT IN +.ENDIF + + .RES $C6F5-* ;skip to $CnF5, where n is the slot# + + ;.org $C6F5 + + .byte GSOS_DRIVER ;GS/OS driver compatibility byte. GS/OS driver + ; checks this byte to see if it is compatible + ; with this version of firmware. This way, + ; changes to firware versions, that do not + ; affect the GS/OS driver will not prevent the + ; GS/OS driver from loading and running. This + ; byte should be incremented each time a change + ; is made that would prevent the GS/OS driver + ; from working correctly. I.e. Partition layout + ; or something similar. + + .byte "CFFA", FIRMWARE_VER ;$CnF6..CnFA: Card Hardware ID, + ; non-standard scheme + + .byte $0 ;$CnFB: SmartPort status byte + ; Not Extended; not SCSI; not RAM card + ; Even if not supporting SmartPort, we need a + ; zero at $CnFB so Apple's RAMCard driver + ; doesn't mistake us for a "Slinky" memory + ; card. + +; Data table for ProDOS drive scan +; $CnFC/FD = disk capacity, if zero use status command to determine +; $CnFE = status bits (BAP p7-14) +; 7 = medium is removable +; 6 = device is interruptable +; 5-4 = number of volumes (0..3 means 1..4) +; 3 = device supports Format call +; 2 = device can be written to +; 1 = device can be read from (must be 1) +; 0 = device status can be read (must be 1) +; +; $CnFF = LSB of block driver + .word $0000 ;$CnFC-D: A zero here will cause prodos to + ; rely on the status command to determine + ; volume size + + .byte $9F ; $CnFE: support 2 ProDOS drives + .byte drive # then StatusSize = $FFFF +; If DrvBlkCount2 = drive # then StatusSize = DrvBlkCount1,DrvBlkCount0 +; If DrvBlkCount2 < drive # then StatusSize = 0 +; +; This scheme has a special case which must be handled because ProDOS +; partitions are not quite 32 meg in size but are only FFFF blocks in size. +; If a device is exactly: 32meg or 10000h blocks in size, it would appear +; as one drive of size FFFF and another drive of size 0000. To handle this +; case, we check for an exact size of 0000 and fall into the NoDrive code. +; +; FIXME: Hmmm. Floppy + HD combo. How to handle? + ;lda DriveNumber,y + ;cmp DrvBlkCount2,y + ;beq ExactSize + bra ExactSize + bcc FullSize + +NoDrive: + ldx #0 + ldy #0 + lda #PRODOS_OFFLINE + sec + bra sExit + +ExactSize: ;If equal, the DrvBlkCount1,DrvBlkCount0 is the + ; drive's exact size + lda DrvBlkCount0,y + ora DrvBlkCount1,y + beq NoDrive ;can't have a 0-block device + + lda DrvBlkCount0,y + tax + lda DrvBlkCount1,y + tay + lda #0 + clc ;no errors + bra sExit + +FullSize: + ldx #$FF ;X gets low byte of size + ldy #$FF ;Y gets high byte of size + lda #0 + clc ;no errors + +sExit: +.IF DEBUG + php ;save the carry's state + pha + jsr DSString + .byte "Rt:",0 + tya + jsr DSByte + txa + jsr DSByteCRLF + pla + plp ;recover the carry +.ENDIF + + rts + +;------------------------------------------------------------------------------ +; ReadBlock - Read a block from device into memory +; +; Input: +; pd Command Block Data $42 - $47 +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; Output: +; A = ProDOS read return code +; Carry flag: 0 = Okay, 1 = Error +; +; ZeroPage Usage: +; $EF +; w/DEBUG enabled: $EB, $EC, $ED, $EE +; Note: location $EF is saved and restored before driver exits +; +ReadBlock: +.IF DEBUG + jsr DSString + .byte " Rd",0 + ;jsr DisplayParms +.ENDIF + + lda pdIOBufferH + pha + lda zpt1 ; $EF save + pha + +.IF 0 ;DEBUG + lda CheckSumHigh + pha + lda CheckSumLow + pha +.ENDIF + + jsr ReadBlockCore + +.IF 0 ;DEBUG + ply + sty CheckSumLow + ply + sty CheckSumHigh +.ENDIF + + ply + sty zpt1 + ply + sty pdIOBufferH + rts + +ReadBlockCore: +.IF 0 ;DEBUG + stz CheckSumLow + stz CheckSumHigh +.ENDIF + +.IF DEBUG + jsr DSString + .byte " Sk",0 + ;jsr DisplayParms +.ENDIF + + jsr Block2Sector + + jsr HPIBSeek + + ; based on ProDOS address + +.IF DEBUG + jsr DSString + .byte " ORd",0 + ;jsr DisplayParms +.ENDIF + + ;lda #0 + ;sta ATADataHigh,x + + ;lda #ATACRead + ;sta ATACommand,x ;Issue the read command to the drive + ;jsr IDEWaitReady ;Wait for BUSY flag to clear + + jsr GPIBReadOpen + + bcs rShort ;any errors? + +.IF DEBUG + jsr DSString + .byte " GD",0 + ;jsr DisplayParms +.ENDIF + + bra rCommandOK + +.IF DEBUG +;warning this debug code trashes the Acc register + jsr DSString + .byte " Er",0 + lda ATAError,x + jsr DSByteCRLF +.ENDIF + +; +; The drive has returned an error code. Just return I/O error code to PRODOS +; + lda #PRODOS_IO_ERROR + sec + rts +; +; Sector is ready to read +; +rCommandOK: + phy + ldy #2 ;2 itterations + sty zpt1 + ldy #0 ; of 256 cycles Need seek? + +rLoop: +; lda ATAStatus,x ;Note: not using IDEWaitReady, using inline code +; bmi rLoop ;Wait for BUSY (bit 7) to be zero +; and #$08 ;get DRQ status bit +; beq rShort ;if off, didn't get enough data + + jsr GetGPIB + bcc @continue + + ply + phy + + ; Close this sector and open the next + jsr GPIBReadClose + inc pdIOBufferH + dec zpt1 + beq @done + + ; Not done so start reading the next sector + jsr GPIBReadOpen + jsr GetGPIB + + ldy #0 + +@continue: + sta (pdIOBuffer),y + iny + +.IF 0; DEBUG + jsr DSByte +.ENDIF + +.IF 0; DEBUG + clc + adc CheckSumLow + sta CheckSumLow +.ENDIF + +; iny +; bne rLoop + bra rLoop +@done: +; inc pdIOBufferH +; dec zpt1 +; bne rLoop + +.IF 0; DEBUG + jsr DSString + .byte " Chk$",0 + + lda CheckSumHigh + jsr DSByte + lda CheckSumLow +.ENDIF +.IF DEBUG + jsr DSCRLF +.ENDIF + + ply + lda #0 + clc + rts +; +; The Block was short, return I/O error code to PRODOS +; +rShort: +.IF DEBUG + jsr DSString + .byte "RSh", 0 + + lda zpt1 + jsr DSByte + tya + jsr DSByteCRLF +.ENDIF + + lda #PRODOS_IO_ERROR + sec + rts + +;------------------------------------------------------------------------ +; WriteBlock - Write a block in memory to device +; +; Input: +; pd Command Block Data $42 - $47 +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; Output: +; A = ProDOS write return code +; Carry flag: 0 = Okay, 1 = Error +; +; ZeroPage Usage: +; $EF +; w/DEBUG enabled: $EB, $EC, $ED, $EE +; Note: location $EF is saved and restored before driver exits +; +WriteBlock: +.IF DEBUG + jsr DSString + .byte " W",0 + ;jsr DisplayParms +.ENDIF + + lda pdIOBufferH + pha + lda zpt1 + pha + +.IF 0 ; DEBUG + lda CheckSumHigh + pha + lda CheckSumLow + pha +.ENDIF + + jsr WriteBlockCore + +.IF 0 ; DEBUG + ply + sty CheckSumLow + ply + sty CheckSumHigh +.ENDIF + + ply + sty zpt1 + ply + sty pdIOBufferH + rts + +WriteBlockCore: +.IF 0 ; DEBUG + stz CheckSumLow + stz CheckSumHigh +.ENDIF + + jsr Block2Sector + + jsr HPIBSeek + + ; Check Error on Seek? +; lda ATAStatus,x ;Check for error response from writing command +; and #$09 +; cmp #$01 ;if DRQ=0 and ERR=1 an error occured +; bne wCommandOK + bra wCommandOK + +.IF DEBUG +;warning this debug code trashes the Acc register + jsr DSString + .byte " Er",0 + lda ATAError,x + jsr DSByteCRLF +.ENDIF + +; The drive has returned an error code. Just return I/O error code to PRODOS +; + lda #PRODOS_IO_ERROR + sec + rts +; +; Sector is ready to write +; +wCommandOK: + phy + ldy #2 + sty zpt1 + +wNextSector: + ply + phy + jsr GPIBWriteOpen + clc + ldy #0 +wLoop: + +.IF 0 ;DEBUG + clc + adc CheckSumLow + sta CheckSumLow +.ENDIF + + clc + lda (pdIOBuffer),y + iny + bne @continue + sec ; last byte +@continue: + jsr SendGPIB + +.IF 0 ;DEBUG + adc CheckSumHigh + sta CheckSumHigh +.ENDIF + + cpy #0 + bne wLoop + ply + phy + jsr GPIBWriteClose + inc pdIOBufferH + dec zpt1 + bne wNextSector + + ply + +.IF 0 ;DEBUG +; Display the Checksum +; warning this debug code trashes the Acc register + jsr DSString + .byte " Chk$",0 + lda CheckSumHigh + jsr DSByte + lda CheckSumLow + jsr DSByteCRLF +.ENDIF + + lda #0 + clc + rts +; +; The Block was short, return I/O error code to PRODOS +; +wShort: +.IF DEBUG +; Display "W:Short" + jsr DSString + .byte " WSh", 0 + + lda zpt1 + jsr DSByte + tya + jsr DSByteCRLF +.ENDIF + + jsr GPIBWriteClose + + lda #PRODOS_IO_ERROR + sec + rts + +;------------------------------------------------------------------------------ +; +; Block2Sector - Translates ProDOS blocks into head, cylinder, sector +; +; Input: +; pd Command Block Data $42 - $47 +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Y = $0n (n = slot#) for accessing scratchpad RAM; +; +; Ouput: +; None +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: A, P +; +Block2Sector: + ; FIXME Will need identify table for these values. + ;lda #2 + ;;sta MXHEAD + ;lda #16 ; Need to / 2 (8 blocks = 16 256byte sectors) + ;;sta MXSECTOR + ;lda #35 + ;;sta MXCYLL + ;lda #0 + ;;sta MXCYLH + jsr DSCRLF + lda pdBlockNumberH + pha +.IF 0 ;DEBUG + jsr DSByte +.ENDIF + lda pdBlockNumber + pha +.IF 0 ;DEBUG + jsr DSByteCRLF + pla + pha +.ENDIF + + ; 2 heads + clc + + ;ror pdBlockNumberH ; Only for / 2 need something else for more than 2 heads + ;ror pdBlockNumber + ;lda #0 + ;rol a + ;pha ;push the head + + lda #0 + sta DrvBlkCount1,y +.IF 0;DEBUG + jsr DSByte +.ENDIF + clc + lda #16 ; Divsor = sectors per cylinder + ror a ; 512 byte per block (256 bytes per sector) + sta DrvBlkCount0,y +.IF 0;DEBUG + jsr DSByteCRLF +.ENDIF + + jsr Mod + + ; Block + clc + lda DrvBlkCount2,y + rol a ; Convert Block to Starting Sector (currently always < 256) + sta DrvBlkCount2,y + + jsr DSByte + + ; DrvBlkCount2 = SECTOR + + ;pla ; Pop head + lda #0 + sta DrvBlkCount3,y ; Store Head +.IF 0;DEBUG + jsr DSByteCRLF + ; Cylinder (16 bit) + lda DrvBlkCount1,y + jsr DSByte + lda DrvBlkCount0,y + jsr DSByteCRLF +.ENDIF + + pla + sta pdBlockNumber + pla + sta pdBlockNumberH + + rts + +; FIXME? +; Add BLOCKOFFSET to the ProDOS block number to offset the first drive block we +; use. This keeps the device's first BLOCKOFFSET blocks free, which usually +; includes a MBR at block 0. +; rawread (FIXME?) +; + +;------------------------------------------------------------------------------ +; +; HPIBSeek - Seek drive head to D#,CYLH,CYLL,HEAD,SECTOR +; +; Must call Block2Sector to load values (or manually load them) +; +HPIBSeek: + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$68 ;Secondary Listen + sta GPIB_DOUT,x ;inline the data out for GTS + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$02 ; OpCode (SEEK) + jsr SendGPIB + + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda DrvBlkCount1,y ;CylH + jsr SendGPIB + + lda DrvBlkCount0,y ;CylL + jsr SendGPIB + + lda DrvBlkCount3,y ;Head + jsr SendGPIB + + sec ; Last byte + lda DrvBlkCount2,y ;Sector + jsr SendGPIB + + lda #$3F ;Unlisten (Seek is unusual in that it wants the this + jsr SendGPIB ; before the Poll). + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + rts + +; FIXME are we sending EOI??? +; Error checking (no read) done via DSJ = 1 (Bad Data) +;------------------------------------------------------------------------------ +; +GPIBReadOpen: + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$6A ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$05 ; OpCode (Buffered Read) + jsr SendGPIB + + sec ; Last byte + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + jsr DSJ ;Any errors? + bcs ROError + + ; Actually read the data + lda #$40 ; TALK Address (MTA) + ora DriveNumber,y + jsr SendGPIB + lda #$60 ; Secondary TALK (MSA) + sta GPIB_DOUT,x + +.IF DEBUG + jsr DSByte +.ENDIF + + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + +ROError: + + rts + +;------------------------------------------------------------------------------ +; +GPIBReadClose: + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + lda #TON + sta AUXCMD,x + + lda #$5f ;UNTALK + jsr SendGPIB + +.IF DEBUG + jsr DSCRLF +.ENDIF + rts + +;------------------------------------------------------------------------------ +; +GPIBWriteOpen: + ;lda #$14 ; Universal Clear + ;jsr SendGPIB + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$69 ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$08 ; OpCode (Buffered Write) + jsr SendGPIB + + sec ; Last byte + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$60 ;Secondary Listen + ;jsr SendGPIB + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + jsr DSCRLF + + rts + + ;Send Data to write + +;GPIBWriteLoop: + +; jsr SendGPIB +; rts + + +;------------------------------------------------------------------------------ +; +GPIBWriteClose: + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr DSCRLF + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + rts + +;------------------------------------------------------------------------------ +; +; HP Identify Command. (HP-IB weirdness and completely non-standard). +; +HPIBId: + lda #$5f ;UNTALK + clc + jsr SendGPIB + lda DriveNumber,y + ora #$60 + sta GPIB_DOUT,x + + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + +@More: + jsr GetGPIB +.IF DEBUG + php + jsr DSByte + plp +.ENDIF + bcc @More + + lda #TON + sta AUXCMD,x + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + +.IF DEBUG + jsr DSCRLF +.ENDIF + + rts + + +;------------------------------------------------------------------------------ +; +; AMIGO Format command. CAUTION: LOW LEVEL FORMATS MEDIA. +; +AMIGOFormat: + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$6C ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$18 ; OpCode (FORMAT) + jsr SendGPIB + + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda #$80 + jsr SendGPIB + + lda #9 + jsr SendGPIB ; Interleave (not used) + sec ; Last byte + lda #$00 + jsr SendGPIB ; Data Byte (not used) + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + lda #0 ; No PRODOS error to report + clc + rts + + + +;------------------------------------------------------------------------------ +; +; HPIBPPoll: Parallel poll the drive waiting for a response. Drives that are +; busy are 'held off' by having parrallel poll disabled. In essence if it +; doesn't respond to a poll then its busy or offline. +; +; FIXME: Need to set a limit for the number of polls or the amount of time. +; HP says 60 seconds max response time. +; +HPIBPPoll: + phy + +@repoll: + lda #PPOL_ON + sta AUXCMD,x + + lda #WAIT_100ms ; Give drive time to respond to the poll (HP recommends 50us) + jsr Wait + + ply + phy + + lda #$08 + sec + sbc DriveNumber,y + tay + lda #0 + sec +@B: + rol a + dey + bne @B ;HP-IB devices respond to poll on DIO# = 8 - device ID + and CMD_PT,x + php + + pha + lda #PPOL_OFF + sta AUXCMD,x + pla + plp + + beq @repoll + + ply + + clc + rts + +;------------------------------------------------------------------------------ +; +; Find ZP locations to speed this up (use pha loop?) +; +; kudos to Garth Wilson for this function from www.6502.org +Mod: + phy + phx + tya + tax + +; save ZP, fetch cmd + pointer from (PC) + ldy #spZeroPgSize-1 +@save: + lda spZeroPgArea,y + pha + dey + bpl @save + + txa + tay + + lda DrvBlkCount1,y + sta DBC1 + lda DrvBlkCount0,y + sta DBC0 + + ; Will only ever use 16 bit values (Optimize?) + stz DBC3 + stz DBC2 + + ;pdBlockNumber(H) Already adjusted and saved at this point. + + phy + + + SEC ; Detect overflow or /0 condition. + LDA DBC2 ; Divisor must be more than high cell of dividend. To + SBC DBC0 ; find out, subtract divisor from high cell of dividend; + LDA DBC3 ; if carry flag is still set at the end, the divisor was + SBC DBC1 ; not big enough to avoid overflow. This also takes care + BCS @oflo ; of any /0 condition. Branch if overflow or /0 error. + ; We will loop 16 times; but since we shift the dividend + LDX #$11 ; over at the same time as shifting the answer in, the + ; operation must start AND finish with a shift of the + ; low cell of the dividend (which ends up holding the + ; quotient), so we start with 17 (11H) in X. +@loop: + ROL pdBlockNumber ; Move low cell of dividend left one bit, also shifting + ROL pdBlockNumberH ; answer in. The 1st rotation brings in a 0, which later + ; gets pushed off the other end in the last rotation. + DEX + BEQ @end ; Branch to the end if finished. + + ROL DBC2 ; Shift high cell of dividend left one bit, also + ROL DBC3 ; shifting next bit in from high bit of low cell. + STZ Carry ; Zero old bits of CARRY so subtraction works right. + ROL Carry ; Store old high bit of dividend in CARRY. (For STZ + ; one line up, NMOS 6502 will need LDA #0, STA CARRY.) + SEC ; See if divisor will fit into high 17 bits of dividend + LDA DBC2 ; by subtracting and then looking at carry flag. + SBC DBC0 ; First do low byte. + STA modSpare ; Save difference low byte until we know if we need it. + LDA DBC3 ; + SBC DBC1 ; Then do high byte. + TAY ; Save difference high byte until we know if we need it. + LDA Carry ; Bit 0 of CARRY serves as 17th bit. + SBC #0 ; Complete the subtraction by doing the 17th bit before + BCC @loop ; determining if the divisor fit into the high 17 bits + ; of the dividend. If so, the carry flag remains set. + LDA modSpare ; If divisor fit into dividend high 17 bits, update + STA DBC2 ; dividend high cell to what it would be after + STY DBC3 ; subtraction. + BRA @loop + +@oflo: + LDA #$FF ; If overflow occurred, put FF + STA DBC2 ; in remainder low byte + STA DBC3 ; and high byte, + STA pdBlockNumber ; and in quotient low byte + STA pdBlockNumberH ; and high byte. + +@end: + + ply + + lda DBC2 + sta DrvBlkCount2,y + lda DBC3 + sta DrvBlkCount3,y + + lda pdBlockNumberH + sta DrvBlkCount1,y + lda pdBlockNumber + sta DrvBlkCount0,y + + +; Restore zero page + ldy #0 +restore: + pla + sta spZeroPgArea,y + iny + cpy #spZeroPgSize + bcc restore + + plx + ply + + RTS + +;------------------------------------------------------------------------------ +; IDEWaitReady - Waits for BUSY flag to clear, and returns DRQ bit status +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Ouput: +; Carry flag = DRQ status bit +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: A, P +; +IDEWaitReady: +; lda ATAStatus,x +; bmi IDEWaitReady ;Wait for BUSY (bit 7) to be zero +; ror ;shift DRQ status bit into the Carry bit +; ror +; ror +; ror +; rts + +;------------------------------------------------------------------------------ +; CheckDevice - Check to see if a device is attached to the interface. +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Output: +; Carry flag: 0 = Device Present, 1 = Device Missing +; +; CPU Registers changed: A, P +; +; Checks to see if the drive status register is readable and equal to $50 +; If so, return with the Carry clear, otherwise return with the carry set. +; Waits up to 10sec on a standard 1Mhz Apple II for drive to become ready +; +CheckDevice: +; + jsr DSJ + ; If Carry set DSJ <> 0 force status + bcc DeviceFound + + jsr STAT + bcc DeviceFound + +; sec ;set c = 1 if drive is not attached +; ply + rts + +DeviceFound: +; ply + rts + + +;------------------------------------------------------------------------------ +; Status - Gets status of +; +; Input: None +; +; CPU Registers changed: None +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; DSJ - Device Specified Jump (Internal Subroutine) +;; +DSJ: + ;jsr DSString + ;.byte " Dsj:",0 + + clc + lda #$40 ; TALK Address (MTA) + ora DriveNumber,y + jsr SendGPIB + + lda #$60 ; Secondary TALK (MSA) + ora #$10 ; Secondary Command + sta GPIB_DOUT,x + + lda #HDFA + sta AUXCMD,x + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + + ; Single byte returned from DSJ. +@More: + jsr GetGPIB + ; sta ??? +.IF 0 ;DEBUG + php + jsr DSByte + plp +.ENDIF + bcc @More + + and #$03 + bne @uc + + lda #TCS + sta AUXCMD,x + jsr WaitOut9914;IO + + lda #RHDF + sta AUXCMD,x + + lda #HDACLR + sta AUXCMD,x + + lda #TON + sta AUXCMD,x + + lda #$5f ;UNTALK + clc + jsr SendGPIB + +.IF DEBUG + jsr DSCRLF +.ENDIF + + clc + rts + +@uc: + lda #TCS + sta AUXCMD,x + jsr WaitOut9914;0 + + lda #RHDF + sta AUXCMD,x + + lda #HDACLR + sta AUXCMD,x + + lda #TON + sta AUXCMD,x + + clc + lda #$5f ;UNTALK + jsr SendGPIB +.IF DEBUG + jsr DSCRLF +.ENDIF + +; clc +; lda #$14 ; Universal Clear +; jsr SendGPIB + +; jsr HPIBPPoll + +; Its hanging on new unformatted disk read. Was working when status / universal clear was being sent each time. +; Reads already formatted ProDOS disk (check again). + + sec ;Set Carry to force Status + rts + +;------------------------------------------------------------------------------ +; STAT - Get Drive Status Information. +; +STAT: + lda zpt1 + pha + jsr DSString + .byte "T:",0 + + clc + lda #$20 ;My address (Apple GPIB address) + jsr SendGPIB + + lda #$3F ;Unlisten + jsr SendGPIB + + lda #$20 + ora DriveNumber,y + jsr SendGPIB + + lda #$68 ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$03 ; OpCode + jsr SendGPIB + + lda #$00 ; Disk Unit Number (0) + sec + jsr SendGPIB + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + +jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + ;------------------------------------ + ; Get the response from stat request + clc + lda #$40 ; TALK Address (MTA) + ora DriveNumber,y + jsr SendGPIB + + lda #$60 ; Secondary TALK (MSA) + ora #$08 ; Secondary Command + ;jsr SendGPIB + sta GPIB_DOUT,x +.IF 0;DEBUG + jsr DSByte + clc +.ENDIF + lda #HDFA + sta AUXCMD,x + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + ;jsr SendGPIB + + ; Multiplie bytes returned. FIXME ignore for now + phy + ldy #0 +@More: + jsr GetGPIB + php + + cpy #2 + bne @chk2 + lsr a + and #$0f + sta zpt1 +; bra @chk2 +;@chk1: +; cpy #3 +; bne @chk2 +; and #$03 + + +@chk2: + +.IF 0;DEBUG + jsr DSByte +.ENDIF + + plp + iny + bcc @More + ply + clc + + lda #TCS + sta AUXCMD,x + jsr WaitOut9914;IO + + lda #RHDF + sta AUXCMD,x + + lda #HDACLR + sta AUXCMD,x + + lda #TON + sta AUXCMD,x + + lda #$5f ;UNTALK + jsr SendGPIB + +.IF 0;DEBUG + jsr DSCRLF +.ENDIF + + lda zpt1 + beq @bad ;use bmi? + cmp #$05 + bne @good + +@bad: + sec + pla + sta zpt1 + lda #PRODOS_IO_ERROR + rts + +@good: + clc + pla + sta zpt1 + lda #0 + rts + +;------------------------------------------------------------------------------ +; Init9914 - Routine to initialize the TMS9914 GPIBA +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; 9914 State on exit: +; B0 set +; All interrupts masked off +; Talk Only (TON) on +; Not listener active (LA) +; No holdoffs enabled or active +; 9914 Polled for status +; +; CPU Registers changed: A, P +; +Init9914: + ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O + lda #RESET + sta AUXCMD,x + lda #RSTCLR + sta INTM0,x + sta INTM1,x + sta AUXCMD,x + lda #SIC + sta AUXCMD,x + + txa + pha + tya + pha + + ldx #4 ;5.12 usec delay (FIXME) + ldy #0 +@init1: + dey + bne @init1 + dex + bne @init1 + + pla + tay + pla + tax + + ;Set my address + lda #$00 + sta IOBase+4,x + + ;ldx #SLOTx16 + lda #SICLR + sta AUXCMD,x + lda #$11 + sta AUXCMD,x + lda #$05 + sta AUXCMD,x + lda #TON + sta AUXCMD,x + jsr WaitOut9914 + + rts + +;------------------------------------------------------------------------------ +; SendGPIB - Output data then wait for Byte Out stat change. +; +; Input: +; A = Data Out +; P = Carry Set = Last byte (Assert EOI) +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; CPU Registers changed: A, P +; +SendGPIB: + bcc @NotLast + clc + pha + jsr DSString + .byte "e",0 + lda #$08 + sta AUXCMD,x + pla +@NotLast: + sta GPIB_DOUT,x +.IF 0;DEBUG + jsr DSByte + clc +.ENDIF + ;FALL THROUGH + +;------------------------------------------------------------------------------ +; WaitOut9914 - Wait for Byte Out stat change. +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; CPU Registers changed: A, P +; +; lda BUSSTAT,x +; jsr DSByte +WaitOut9914: +; phy +; ldy #10 ;10 timeouts (~1 second) +@reload: +; lda #WAIT_100ms +; jsr TimeSet +@again: +; jsr TimeCheck +; bcs @continue +; jsr TimeClear +; dey +; beq @timeout +; bra @reload +@continue: + lda INT0,x + and #BOM + beq @again +; jsr TimeClear +; clc +; ply + rts +@timeout: +; jsr TimeClear +.IF DEBUG +; jsr DSString +; .byte "TO",0 +; jsr DSCRLF +.ENDIF +; sec +; ply + rts + +;------------------------------------------------------------------------------ +; WaitIn9914 - Wait for Byte In AND/OR EOI stat change. +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Output: +; A = Byte received +; C = Set if last byte cleared otherwise +; +; CPU Registers changed: A, P +; +; Timeout without actually waiting??? +GetGPIB: +; phy +; ldy #10 ;10 tries (1 second) +@again: +; lda #WAIT_100ms +; jsr Wait +; dey +; beq @timeout + + lda INT0,x + and #EOIMK+BIM + beq @again + + clc + and #EOIMK + bne @LastByte ; Got EOI Get final byte + lda GPIB_DIN,x + pha + lda #RHDF + sta AUXCMD,x + pla + ; FIXME Release Holdoffs ??? + rts + +@LastByte: + lda GPIB_DIN,x + sec ;Set carry this is the last byte + ; FIXME Need TCS (Take Control Sync) ??? + ; Release HOLDOFFS? + rts + +;------------------------------------------------------------------------------ +; Wait - Copy of Apple's wait routine. Can't use ROM based routine in case +; ROM is not active when we need it. +; +; Input: +; A = desired delay time, where Delay(us) = .5(5A^2 + 27A + 26) +; or more usefully: A = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 +; +; CPU Registers changed: A, P +; +Wait: + sec + +Wait2: + pha + +Wait3: + sbc #1 + bne Wait3 + pla + sbc #1 + bne Wait2 + rts + +;------------------------------------------------------------------------------ +; Timeout Routines +; - TimeSet - A = Wait time (not 100% accurate) +; - TimeCheck - clc = timeout +; - TimeClear - Clears the stack +; +; ZP may be more suited for this task. +; +; CPU Registers changed: A, P +; +TimeSet: + pha + rts + +;------------------------------------- +TimeCheck: + pla + pha + sec + sbc #1 + beq @stilltime + pla + sbc #1 + beq @stilltime2 + ;carry clear = timeout +@stilltime2: + pha +@stilltime: + rts + +;------------------------------------- +TimeClear: + pla + rts + +.IF DEBUG + +;------------------------------------------------------------------------------ +; DisplayParms - Display the parameters of the ProDOS request +; Input: +; None +; Ouput: +; None +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: A, P +; +DisplayParms: +; jsr DSString +; .byte " B:",0 + +; lda pdBlockNumberH +; jsr DSByte +; lda pdBlockNumber +; jsr DSByte + +; jsr DSString +; .byte " U:",0 +; lda pdUnitNumber +; jsr DSByte + +; jsr DSString +; .byte " A$",0 + +; lda pdIOBufferH +; jsr DSByte +; +; lda pdIOBuffer + bra DSByte + + +;------------------------------------------------------------------------------ +; DSString - Sends a String to the Super Serial Card in Slot 2 +; Input: +; string must immediately follow the JSR to this function +; and be terminated with zero byte. +; Ouput: +; None +; +; ZeroPage Usage: +; MsgPointerLow, MsgPointerHi +; +; CPU Registers changed: A, P +; +DSString: + phx ;save the X reg + tsx ;put the stack pointer in X + lda MsgPointerLow + pha ;push zero page location on stack + lda MsgPointerHi + pha ;push zero page location on stack + + lda StackBase+2,x ;determine the location of message to display + clc + adc #$01 ;add 1 because JSR pushes the last byte of its + sta MsgPointerLow ; destination address on the stack + + lda StackBase+3,x + adc #0 + sta MsgPointerHi + +dss1: + lda (MsgPointerLow) + beq dssend + jsr DSChar ;display message + inc MsgPointerLow + bne dss1 + inc MsgPointerHi + bra dss1 + +dssend: + + lda MsgPointerHi + sta StackBase+3,x + lda MsgPointerLow + sta StackBase+2,x ;fix up the return address on the stack. + + pla + sta MsgPointerHi ;restore zero page location + pla + sta MsgPointerLow ;restore zero page location + plx + clc + rts ;return to location after string's null. + +;------------------------------------------------------------------------------ +; DSByteCRLF - Sends a Hex byte followed by a CR LF to the Super Serial +; Card in Slot 2 +; +; Input: +; A = Hex number to display +; Ouput: +; None +; +; CPU Registers changed: A, P +; +DSByteCRLF: + jsr DSByte +DSCRLF: + lda #$0D + jsr DSChar + lda #$0A + bra DSChar + +;------------------------------------------------------------------------------ +; DSByte - Sends a Hex byte to the Super Serial Card in Slot 2 +; Input: +; A = Hex number to display +; Ouput: +; None +; +; CPU Registers changed: A, P +; +DSByte: + pha + lsr a + lsr a + lsr a + lsr a + jsr DSNibble + pla +DSNibble: + and #$0F + ora #$30 + cmp #$30+10 + bcc digit + adc #6 +digit: + bra DSChar + +;------------------------------------------------------------------------------ +; DSChar - Sends a char to the Super Serial Card in Slot 2 +; Input: +; A = Character to Send +; Ouput: +; (data out serial port) +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: P +; +DSBlank: + lda #$20 +DSChar: + pha + phy + lda mslot + and #$0f + tay ;Y reg now has $0n for accessing scratchpad RAM + lda SerialInitDone,y + cmp #$A5 + beq dsc0 + +; Init the serial port if sig byte is not $A5. +; Set up serial port on the Apple Super Serial card. Always assume slot 2. + lda #$1f + sta $c0ab ;control register + lda #$0b + sta $c0aa ;format + lda $c0a9 ;clear status + lda #$A5 + sta SerialInitDone,y + +dsc0: + lda $c0a9 + and #%00010000 ;Transmit register empty? + beq dsc0 ;If not, wait + + ply + pla ;get byte back + sta $c0a8 ;send it + clc + rts + +.ENDIF + + +.WARNING .SPRINTF("%04X",*) +.IF * > $cfff +.WARNING "* TOO BIG!!!" +.ENDIF +.word *