;------------------------------------------------------------------------------ ; ; 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 (IEEE-488) 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 256 bytes ; (meaning you lose that space in the ROM) but I can't recall why other than ; the card didn't init unless I did. Others more familiar with the Apple II design ; will probably understand why. ; ; 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. Some code is unused (I took code I worded on separately ; and placed it into the cffa code so I wouldn't have to reinvent the wheel. ; Anything referencing ATA commands can probably be removed. ; .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 *