mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-24 17:32:59 +00:00
1180 lines
43 KiB
Plaintext
1180 lines
43 KiB
Plaintext
;
|
|
; File: ADBDriver.aii
|
|
;
|
|
; Contains: IOP code for IOP Based ADB driver
|
|
;
|
|
; Written by: Gary G. Davidian 10-Jul-88
|
|
;
|
|
; Copyright: © 1988-1990 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <2> 1/2/90 GGD NEEDED FOR ZONE-5: General source cleanup, no code changes.
|
|
; <1.5> 11/19/89 GGD NEEDED FOR ZONE-5 Added conditional assembly option
|
|
; 'ForceWorstCase' to cause ADB to use more processor time, to
|
|
; stress test the worst case paths of the SWIM Driver. Added
|
|
; facility to control auto-polling on a device address basis so
|
|
; that the ADB Manager can select to only poll addresses that have
|
|
; device table entries.
|
|
; <1.4> 11/2/89 GGD NEEDED FOR ZONE-5 Changed the ADB Polling rate from 13.5ms to
|
|
; 11.0ms to be closer to a floppy disk sector time, and to be
|
|
; consistent with the Mac II ADB Micro poll rate.
|
|
; <1.3> 9/21/89 GGD Added ADBActivity indicator so that the disk driver can give ADB
|
|
; more priority if there was recent activity. Corrected timing
|
|
; calculation in Attn pulse (off by 5µs).
|
|
; <1.2> 7/8/89 CCH No changes. Testing in-file comments for ".aii" suffix files.
|
|
; <1.1> 6/15/89 GGD Updated to use equates for the latest rev of the IOP chip,
|
|
; re-formated tab stops in source.
|
|
; <1.0> 11/9/88 CCH Adding to EASE.
|
|
; 7/10/88 GGD Created today.
|
|
;
|
|
; To Do:
|
|
;
|
|
eject
|
|
title 'IOP ADB Driver'
|
|
; File: ADBDriver.aii - IOP code for IOP Based ADB driver.
|
|
;
|
|
;_______________________________________________________________________
|
|
;
|
|
; Copyright © Apple Computer, Inc. 1988-1989
|
|
;
|
|
; written by Gary G. Davidian 10-Jul-88
|
|
;
|
|
; This module implements the IOP side of the IOP based Apple Desktop Bus
|
|
; Driver. It performs the low level ADB transactions as well as automatic
|
|
; polling of the last device, and automatic service request polling (in
|
|
; Most Recently Used order). The Host CPU will only be interrupted, with
|
|
; a single interrupt per transaction, when there is some device input to
|
|
; be processed.
|
|
;
|
|
; This driver co-exists on the IOP that also has the SWIM Driver, and the
|
|
; SWIM chip is used to drive and receive the ADB bus.
|
|
;
|
|
; NOTE: This code was optimized for space, and also contains many cycle
|
|
; counted timing dependent routines. This may detract from its
|
|
; readability at times, but keep in mind that this code replaces a
|
|
; considerable amount of hardware and saves $$$ (profit sharing).
|
|
;
|
|
; MODIFICATION HISTORY:
|
|
;
|
|
; <10Jul88> GGD Created today.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
machine m65C02
|
|
codechk off
|
|
datachk off
|
|
longa off
|
|
longi off
|
|
print nomdir,nogen
|
|
print off
|
|
include 'IOPDefs.aii'
|
|
include 'SWIMDefs.aii'
|
|
print on
|
|
|
|
hasAltADB equ 1 ; include alternate SWIM based ADB also
|
|
ForceWorstCase equ 0 ; don't simulate worst case data length <1.5>
|
|
|
|
; Polling rate definitions
|
|
|
|
; These two time constants are used to control auto polling, and service request
|
|
; polling. Explicit commands are serviced immediatly, which may mean that if they
|
|
; are issued at a very high frequency, they may utilize more than 50% of the bus
|
|
; bandwith, and may prevent the processors in the devices from having time to
|
|
; service their device (this is the way that it is done on other machines).
|
|
; During normal AutoPolling, this situation will not occur. But it can occur during
|
|
; Service Request polling.
|
|
;
|
|
; AutoPollRate is the MINIMUM time from the START of a command to the START of
|
|
; the next command when auto polling.
|
|
;
|
|
; SRQPollDelay is the MINIMUM time from the END of a command to the START of
|
|
; the next command. This is used when polling for a device that issued a service
|
|
; request.
|
|
|
|
AutoPollRate equ (11000*TMPB1second)/1000000 ; 11.000 MS auto poll interval <1.4>
|
|
SRQPollDelay equ (00500*TMPB1second)/1000000 ; 0.500 MS delay before SRQ polls
|
|
|
|
|
|
; ADB transaction related definitions
|
|
|
|
NumADBAddrs equ 16 ; 16 addresses, 0..15
|
|
|
|
ADBCmdMask equ $0F ; mask for command/reg portion of ADB command
|
|
ADBAddrMask equ $F0 ; mask for address portion of ADB command
|
|
|
|
ResetCmd equ $00 ; ADB Bus Reset command
|
|
TalkR0Cmd equ $0C ; ADB command Talk Reg 0
|
|
|
|
MinDataLen equ 2 ; Data length can be 0, 2..8 bytes
|
|
MaxDataLen equ 8 ; Data length can be 0, 2..8 bytes
|
|
|
|
|
|
; Message Format
|
|
|
|
ADBMsg record 0,increment ; format CPU <-> IOP messages
|
|
Flags ds.b 1 ; modifier/status flags (see below)
|
|
DataCount ds.b 1 ; number of bytes of ADBData (0..8) (cmd not included)
|
|
ADBCmd ds.b 1 ; ADB command to send / command that replied
|
|
ADBData ds.b MaxDataLen ; ADB Data to be sent with command, or data received
|
|
ds.b 1 ; input data buffer (needs 1 extra byte)
|
|
endr
|
|
|
|
; ADBMsg.Flags bit definitions
|
|
|
|
ExplicitCmd equ %10000000 ; message contains an explicit commmand
|
|
AutoPollEnable equ %01000000 ; auto polling and srq polling enabled
|
|
SetPollEnables equ %00100000 ; Update auto-enable bit mask from ADBData field
|
|
ServiceRequest equ %00000100 ; service request detected
|
|
TimedOut equ %00000010 ; addressed device did not return data
|
|
|
|
|
|
; Low level message passing state and data buffer assignments
|
|
|
|
ADBXmtMsgState equ XMTMsg3State
|
|
ADBRcvMsgState equ RCVMsg3State
|
|
|
|
ADBXmtMsgData equ XMTMsg3Data
|
|
ADBRcvMsgData equ RCVMsg3Data
|
|
|
|
|
|
; Page zero variables
|
|
|
|
seg 'zdata'
|
|
ADBVarsZ record
|
|
export ADBActivity ; export activity status for disk driver
|
|
ADBActivity ds.b 1 ; status of last 8 transactions
|
|
ADBFlags ds.b 1 ; status information
|
|
ByteCount ds.b 1 ; number of bytes to send / bytes received
|
|
PollingTMPB ds.b 1 ; index of Polling Timer Param Block
|
|
MRUAddr ds.b 1 ; ADB address most recently received from
|
|
LRUAddr ds.b 1 ; ADB address least recently received from
|
|
AutoPollAddr ds.b 1 ; ADB address to auto poll
|
|
MsgPtr ds.w 1 ; pointer to xmt/rcv message buffer
|
|
Temp ds.b 1 ; general purpose temporary
|
|
SRQcount ds.b 1 ; number of devices searched during SRQ poll
|
|
endr
|
|
|
|
|
|
; Other Driver variables
|
|
|
|
seg 'data'
|
|
ADBVars record
|
|
XmtCmdAndData ds.b 1 ; command byte (must precede SendBuffer)
|
|
ds.b MaxDataLen ; output data buffer
|
|
MRUList ds.b NumADBAddrs ; list of ADB addrs in MRU order
|
|
DisableFlags ds.b NumADBAddrs ; if <> 0 then don't auto-poll this address
|
|
endr
|
|
ReceiveBuffer equ ADBXmtMsgData+ADBMsg.ADBData ; rcv directly into msg buffer
|
|
|
|
|
|
|
|
seg 'code'
|
|
ADBDrvr proc export
|
|
|
|
export HandleRCVMsg3 ; ADB driver requests use MSG 3
|
|
export HandleXMTMsg3 ; ADB driver requests use MSG 3
|
|
export InitADBDrvr ; ADB driver initialization
|
|
import GetTMPB
|
|
import CancelTMPBTimer
|
|
import StartTMPBTimer
|
|
import WaitTMPBDone
|
|
import CheckTMPBdone
|
|
import TMPBtimeH
|
|
import TMPBtimeL
|
|
|
|
with ADBVarsZ,ADBVars ; globals used throughout driver
|
|
title 'IOP ADB Driver - Initialization'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr InitADBDrvr
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, z, c
|
|
; Calls: GetTMPB
|
|
; Called by: SWIMIOP driver initialization
|
|
;
|
|
; Function: Initializes the IOP based ADB Driver variables.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
InitADBDrvr
|
|
if hasAltADB then
|
|
jsr CheckForAltADB ; patch in SWIM based ADB if present
|
|
endif
|
|
jsr GetTMPB ; get a timer parameter block pointer
|
|
sty <PollingTMPB ; save the TMPB pointer
|
|
|
|
* stz <ADBFlags ; initialize the status flags
|
|
|
|
assert (ADBXmtMsgData**$FF)=(ADBRcvMsgData**$FF)
|
|
lda #<(ADBXmtMsgData\
|
|
+ADBMsg.ADBCmd) ; get low byte of msg pointer
|
|
sta <MsgPtr ; init low byte of message pointer
|
|
|
|
; Initialize the MRU information
|
|
|
|
* stz <MRUAddr ; first entry is default MRU
|
|
* stz <AutoPollAddr ; first entry is default Poll device
|
|
ldx #NumADBAddrs-1 ; index/counter
|
|
stx <LRUAddr ; last entry is default LRU
|
|
@MRUInitLoop txa ; prepare to store count
|
|
sta MRUList-1,x ; MRUList[x] := x+1
|
|
dex ; point to prev list
|
|
bne @MRUInitLoop ; init the entire list
|
|
* stz MRUList\
|
|
+NumADBAddrs-1 ; last entry points to first
|
|
rts
|
|
title 'IOP ADB Driver - Host Message Request Handling'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr HandleRCVMsg3
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: CopyMsg, ADBAction (branched to)
|
|
; Called by: IOPKernel.HandleHSTInt
|
|
;
|
|
; Function: Handles receiving new messages from the host, by copying
|
|
; the request information, and notifying the host that the
|
|
; message request has been accepted. ADBAction is then
|
|
; called to actually process the command.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
HandleRCVMsg3 ldx #>ADBRcvMsgData ; get high byte of message pointer
|
|
ldy ADBRcvMsgData\
|
|
+ADBMsg.DataCount ; get the xmt byte count
|
|
lda ADBRcvMsgData\
|
|
+ADBMsg.Flags ; get the new flags
|
|
jsr CopyMsg ; copy the message
|
|
lda #MessageCompleted ; message completed state
|
|
sta ADBRcvMsgState ; indicate that it is done
|
|
lda #MsgCompletedINT ; get the interrupt bit
|
|
sta HostControl ; interrupt the main CPU
|
|
bra ADBAction ; go run a command
|
|
|
|
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr HandleXMTMsg3
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: CopyMsg, ADBAction (branched to)
|
|
; Called by: IOPKernel.HandleHSTInt
|
|
;
|
|
; Function: Handles receiving messages replies from the host, by copying
|
|
; the request information, and updating the message state to
|
|
; indicate that the message reply has been accepted.
|
|
; ADBAction is then called to actually process the command.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
HandleXMTMsg3 ldx #>ADBXmtMsgData ; get high byte of message pointer
|
|
ldy ADBXmtMsgData\
|
|
+ADBMsg.DataCount ; get the xmt byte count
|
|
lda ADBXmtMsgData\
|
|
+ADBMsg.Flags ; get the new flags
|
|
jsr CopyMsg ; copy the message
|
|
assert Idle=0
|
|
* lda #Idle ; Indicate that response was received
|
|
stz ADBXmtMsgState ; set the new message state
|
|
bra ADBAction ; go run a command
|
|
title 'IOP ADB Driver - Polling Request Handling'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp StartSRQPoll
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, n, z, c
|
|
; Calls: CancelPollTimer, StartPolling (branched to)
|
|
; Called by: ADBAction
|
|
;
|
|
; Function: Handles service request polling, by setting up a Talk R0
|
|
; command for the next device in MRU (Most Recently Used) order.
|
|
; It will delay for a short time before issuing the request
|
|
; so that devices on the ADB bus, and the IOP itself will not be
|
|
; saturated by this polling.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
StartSRQPoll ldx <AutoPollAddr ; get address of last device polled
|
|
@disabled lda MRUList,x ; get the next address to poll for service request
|
|
tax ; save the poll address
|
|
inc <SRQcount ; update the SRQ poll count
|
|
lda <SRQcount ; get the updated count
|
|
cpa #NumADBAddrs ; see if one pass through table completed
|
|
bge @updatePollAddr ; if all enabled addrs searched, search disabled ones
|
|
bit DisableFlags,x ; see if this address is enabled
|
|
bmi @disabled ; if disabled, skip it the first time
|
|
@updatePollAddr stx <AutoPollAddr ; change poll device to next most recently used
|
|
jsr CancelPollTimer ; cancel the autopoll timer
|
|
lda #<SRQPollDelay ; get low byte of poll delay
|
|
* (fall into) bra StartPolling ; poll the next device after a short delay
|
|
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr StartPolling
|
|
; Inputs: A - low byte of poll timer delay (high byte assumed zero)
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: ADBAction (falls into)
|
|
; Called by: ADBAction, StartSRQPoll (branched to)
|
|
;
|
|
; Function: Starts running the polling timer, and returns.
|
|
;
|
|
; When the timer goes off, ADBAction will be called.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
StartPolling ldy <PollingTMPB ; get the TMPB index
|
|
sta TMPBtimeL,y ; store low into the TMPB
|
|
assert (SRQPollDelay>>8)=(AutoPollRate>>8)
|
|
lda #>AutoPollRate ; get high byte of poll rate
|
|
sta TMPBtimeH,y ; store high into the TMPB
|
|
jsr StartTMPBTimer ; start the timer
|
|
ldy <PollingTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; wait for it to time out, return to caller
|
|
|
|
; Timer interrupt resumes us here
|
|
* (fall into) bra ADBAction ; when timer goes off, perform some ADB Action
|
|
title 'IOP ADB Driver - Perform ADB Action'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp ADBAction
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: StartPolling, CheckTMPBdone, ADBRequest, StartSRQPoll (branched to)
|
|
; Called by: HandleRCVMsg3, HandleXMTMsg3, StartPolling (falls into)
|
|
;
|
|
; Function: Handles ADB request processing, by deciding what if anything
|
|
; should be done. While the IOP to Host transmit message is in
|
|
; use, no ADB action will be performed, since there is no way
|
|
; to notify the Host that it has completed. If an Explicit
|
|
; request is pending, it will be sent, otherwise, auto/SRQ
|
|
; polling will be done if enabled.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
ADBAction lda ADBXmtMsgState ; get the xmt message state
|
|
assert Idle=0
|
|
bne @Done ; if xmt msg in use, wait until its free
|
|
|
|
ldy <PollingTMPB ; get the TMPB index
|
|
jsr CheckTMPBdone ; see if auto poll timer timed out
|
|
bne @Done ; if not, wait until auto poll interval
|
|
lda #<AutoPollRate ; get low byte of auto poll rate
|
|
jsr StartPolling ; setup timer to wake us up later
|
|
|
|
; Figure out what to do.
|
|
|
|
assert ExplicitCmd=$80 ; n bit
|
|
assert AutoPollEnable=$40 ; v bit
|
|
bit <ADBFlags ; test explicit and auto-poll bits
|
|
bmi @SendIt ; if explicit command, go send it
|
|
bvc @Done ; if implicit, and no auto polling, do nothing
|
|
|
|
; If implicit, and polling enabled, create the auto-poll command
|
|
|
|
lda <AutoPollAddr ; get the device address to auto poll
|
|
asl a ; shift left 4 bits to position device address
|
|
asl a
|
|
asl a
|
|
asl a
|
|
ora #TalkR0Cmd ; make it a talk R0 command
|
|
sta XmtCmdAndData ; update the command
|
|
stz <ByteCount ; no data to send, now send the command
|
|
|
|
@SendIt jsr ADBRequest ; perform the ADB transaction
|
|
|
|
; Check the result of the transaction
|
|
|
|
lsr <ADBActivity ; shift in zero, assume no activity
|
|
|
|
lda <ADBFlags ; get flags
|
|
sta ADBXmtMsgData\
|
|
+ADBMsg.Flags ; return the new flags
|
|
and #ExplicitCmd\
|
|
+AutoPollEnable\
|
|
+TimedOut ; isolate just the flags that we want to check
|
|
|
|
cpa #AutoPollEnable\
|
|
+TimedOut ; test for implicit, auto, no reply
|
|
beq @CheckSRQ ; if so, don't notify host, keep on polling
|
|
|
|
; If explicit command, or reply received, Send ADB transaction result back to host
|
|
|
|
lda #$80 ; mask to set activity indication
|
|
tsb <ADBActivity ; indicate explicit or reply received activity
|
|
|
|
lda <ByteCount ; get the number of bytes received
|
|
sta ADBXmtMsgData\
|
|
+ADBMsg.DataCount ; get the xmt byte count
|
|
assert (Idle+1)=NewMessageSent
|
|
inc ADBXmtMsgState ; change message state Idle -> NewMessageSent
|
|
lda #NewMsgSentINT ; prepare to interrupt the host
|
|
sta HostControl ; interrupt the host
|
|
|
|
lda #ExplicitCmd ; prepare to clear explicit bit
|
|
trb <ADBFlags ; indicate no longer explicit
|
|
bne @Done ; if was explicit, don't check SRQ
|
|
stz <SRQcount ; if reply received, reset the SRQ count
|
|
|
|
; Check for service request, and if so, start SRQ polling
|
|
|
|
|
|
@CheckSRQ lda #ServiceRequest ; prepare to test for service request
|
|
bit <ADBFlags ; see if service request was set
|
|
bne StartSRQPoll ; if so, start/continue SRQ polling, and return
|
|
@Done rts ; otherwise, request is complete
|
|
title 'IOP ADB Driver - Utility Routines'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr CopyMsg
|
|
; Inputs: A - new flags from ADBMsg.Flags
|
|
; X - page number of message buffer
|
|
; Y - data count from ADBMsg.DataCount
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: CancelPollTimer (falls into)
|
|
; Called by: HandleRCVMsg3, HandleXMTMsg3
|
|
;
|
|
; Function: If an explicit command is not already accepted, it will
|
|
; copy the flags, byte count, command and data, from the
|
|
; message whose page number was passed in, to prepare for
|
|
; starting an ADB transaction. If the new command is an
|
|
; explicit command, the poll timer is canceled, so that
|
|
; the command can run immediatly.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
CopyMsg bit <ADBFlags ; see if already have an explicit cmd
|
|
assert ExplicitCmd=$80 ; n bit
|
|
bmi @CopyDone ; if explicit pending, don't overwrite it
|
|
stx <MsgPtr+1 ; setup high byte of message pointer
|
|
sta <ADBFlags ; set new flags
|
|
stz <ByteCount ; send count is zero when implicit
|
|
and #ExplicitCmd\
|
|
+SetPollEnables ; check for explicit / SetPollEnables
|
|
assert ExplicitCmd=$80 ; n bit
|
|
bmi @Explicit ; if explicit then copy cmd and data
|
|
beq @CopyDone ; if not SetPollEnables, just return
|
|
|
|
; Process a SetPollEnables request (ignored if ExplicitCmd is also set)
|
|
trb <ADBFlags ; reset the SetPollEnables flag
|
|
ldy #2 ; 2 bytes of data (16 bit mask)
|
|
lda (MsgPtr),y ; get low byte of mask
|
|
sta <Temp ; save it
|
|
dey ; point to high byte
|
|
lda (MsgPtr),y ; get high byte of mask
|
|
ldx #NumADBAddrs-1 ; loop counter, index into DisableFlags
|
|
@EnablesLoop rol <Temp ; shift the low byte
|
|
rol a ; shift the high byte (highest bit into carry)
|
|
stz DisableFlags,x ; assume polling enabled
|
|
bcs @NextEnable ; if enable bit set, DisableFlags := $00
|
|
dec DisableFlags,x ; if enable bit clear, DisableFlags := $FF
|
|
@NextEnable dex ; update loop counter and index
|
|
bpl @EnablesLoop ; loop through all device addresses
|
|
|
|
@CopyDone rts ; all done
|
|
|
|
@Explicit cpy #MaxDataLen+1 ; see if count is reasonable
|
|
blt @CopyStart ; if in range start copying
|
|
ldy #MaxDataLen ; otherwise, peg it to the Max
|
|
@CopyStart sty <ByteCount ; init the send count
|
|
@CopyLoop lda (MsgPtr),y ; get a byte from the message
|
|
sta XmtCmdAndData,y ; copy it into the send buffer
|
|
dey ; dec index/count
|
|
bpl @CopyLoop ; copy the command and data
|
|
* (fall into) bra CancelPollTimer ; cancel the poll timer and return
|
|
|
|
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr CancelPollTimer
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: CancelTMPBTimer
|
|
; Called by: StartSRQPoll, CopyMsg (falls into)
|
|
;
|
|
; Function: Cancels to Auto Polling interval timer.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
CancelPollTimer ldy <PollingTMPB ; get the TMPB index
|
|
jmp CancelTMPBTimer ; cancel the timer and return
|
|
title 'IOP ADB Driver - Bus Timing'
|
|
|
|
;_______________________________________________________________________
|
|
; Apple Desktop Bus timing definitions from ADB spec #062-0267 (Rev E)
|
|
; Clock counts based upon IOP Clock = 15.6672 MHZ / 8 = 1.9584 MHZ
|
|
;_______________________________________________________________________
|
|
|
|
;symbol clocks true clocks time meaning
|
|
Tcyc equ 196 ; 195.840 100µs bit cell time
|
|
T0 equ 127 ; 127.296 65µs "0" low time
|
|
T1 equ 69 ; 68.544 35µs "1" low time
|
|
Tattn equ 1567 ; 1566.720 800µs attention signal
|
|
Tsynch equ 127 ; 127.296 65µs synch pulse width
|
|
Tlt equ 392 ; 391.680 200µs stop to start time
|
|
Tstop equ 137 ; 137.088 70µs stop pulse width
|
|
Tsrq equ 588 ; 587.520 300µs service request
|
|
*Tres equ 5875 ; 5875.200 3ms reset signal (3ms min)
|
|
|
|
; Additionally we define the following
|
|
Tres equ 7834 ; 7833.600 4ms reset signal (40*Tcyc in Rev C)
|
|
*TsrqMin equ Tsrq-Tsrq*30/100 ; 210µs Minimum SRQ width is Tsrq - 30%
|
|
TsrqMin equ Tsrq-Tsrq*40/100 ; 180µs Minimum SRQ width is Tsrq - 40% (mouse violates spec)
|
|
TsrqMax equ Tsrq*130/100 ; 390µs Maximum SRQ width is Tsrq + 30%
|
|
TltMax equ Tlt*130/100 ; 260µs Maximum stop to start time is Tlt + 30%
|
|
T0Max equ Tcyc*130/100*65/100*105/100 ; 88.725µs Max time between data bit transitions
|
|
Tlow equ T1 ; 68.544 35µs initial low phase of bit cell
|
|
Tdata equ T0-T1 ; 58.752 30µs middle low or high phase of bit cell
|
|
Thigh equ Tcyc-T0 ; 68.544 35µs final high phase of bit cell
|
|
|
|
assert Tcyc=(T0+T1)
|
|
assert Tcyc=(Tlow+Tdata+Thigh)
|
|
|
|
; The ADB bus is normally high, a transaction is sent as follows.
|
|
|
|
; Attention signal starts the transaction
|
|
;High Bus is high when not in use
|
|
;Low Tattn Attention signal
|
|
;High Tsynch synch pulse
|
|
|
|
; The 8 bits of the command are sent, MSB first, LSB last.
|
|
;Low Tlow low phase of data bit
|
|
;L/H Tdata low if zero, high if one. Data phase of bit
|
|
;High Thigh high phase of data bit
|
|
|
|
; Stop bit follows command
|
|
;Low Tstop command stop bit
|
|
|
|
; Service requests from other devices are indicated here by pulling bus low.
|
|
;High Tlt stop bit to start bit
|
|
|
|
; data if any is as follows (otherwise bus is left high)
|
|
|
|
;Low Tlow beginning of start bit for data
|
|
;High Tdata data phase of start bit
|
|
;High Thigh high phase of start bit
|
|
|
|
; 2..8 bytes are sent, MSB first, LSB last.
|
|
;Low Tlow low phase of data bit
|
|
;L/H Tdata low if zero, high if one. Data phase of bit
|
|
;High Thigh high phase of data bit
|
|
|
|
; Stop bit follows command
|
|
;Low Tstop command stop bit
|
|
;High bus left high at end of transaction
|
|
title 'IOP ADB Driver - Bus Manipulation'
|
|
|
|
;_______________________________________________________________________
|
|
; Macros and constants used to read and write the ADB Bus. By using
|
|
; these definitions, this code can be easily modified if the polarity,
|
|
; bit position, or register addresss ever change. This would allow easy
|
|
; migration to an IOP that had direct access to the ADB bus, instead of
|
|
; accessing it through the SWIM chip.
|
|
;_______________________________________________________________________
|
|
|
|
; ADB output bit
|
|
|
|
if hasAltADB then
|
|
rAltADBoutReg equ rSetup ; SWIM register for ADB output state
|
|
wAltADBoutReg equ wSetup ; SWIM register for ADB output state
|
|
AltADBoutMask equ Select35 ; bit mask for output bit
|
|
endif
|
|
rADBoutReg equ TimerDPLLctl ; SWIM register for ADB output state
|
|
wADBoutReg equ TimerDPLLctl ; SWIM register for ADB output state
|
|
ADBoutMask equ GPOUT0 ; bit mask for output bit
|
|
|
|
; macros for modifing state of ADBoutReg when it is the Accumulator
|
|
|
|
macro
|
|
ADBoutLow ; change state to cause bus to go Low
|
|
ora #ADBoutMask ; output bit is active low
|
|
endm
|
|
|
|
macro
|
|
ADBoutHigh ; change state to cause bus to go High
|
|
and #≈ADBoutMask ; output bit is active low
|
|
endm
|
|
|
|
; ADB input bit
|
|
|
|
if hasAltADB then
|
|
AltADBinReg equ rHandshake ; SWIM register for ADB input state
|
|
AltADBinMask equ DriveSense ; bit mask for input bit
|
|
endif
|
|
ADBinReg equ TimerDPLLctl ; SWIM register for ADB input state
|
|
ADBinMask equ GPIN0 ; bit mask for input bit
|
|
|
|
; macros for branching on the state of ADBinReg after a BIT instruction
|
|
|
|
macro
|
|
brADBlo &Target ; branch if bus is low
|
|
beq &Target ; input bit is active high
|
|
endm
|
|
|
|
macro
|
|
brADBhi &Target ; branch if bus is high
|
|
bne &Target ; input bit is active high
|
|
endm
|
|
title 'IOP ADB Driver - Set Bus / Set Bus After'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr SetBus
|
|
; Inputs: c - Value to set the ADB bus to.
|
|
; Outputs: z - setup for brADBlo/brADBhi testing
|
|
; Destroys: A, n, v, z
|
|
; Calls: none
|
|
; Called by: ADBRequest
|
|
;
|
|
; Function: Sets the ADB bus output to the state specified by the
|
|
; carry bit. Carry reset means bus low, Carry set means
|
|
; bus high. After setting the bus, the bus input is read,
|
|
; and tested, ready for branching with brADBlo/brADBhi.
|
|
; This routine returns to the caller 12 clocks after the
|
|
; bus is set.
|
|
;
|
|
; The Macro SetBusAfter, is provided to allow an exact number
|
|
; of clocks to be counted before setting the bus.
|
|
; The acceptable clock counts are 27, 29..1301, 1303.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
|
|
macro
|
|
SetBusAfter &Count ; set bus to carry after "count" clocks
|
|
CallWaiting &Count-22,SetBusPlus
|
|
endm
|
|
|
|
macro
|
|
CallWaiting &Count,&Routine
|
|
lcla &q,&r
|
|
&q seta &eval(&Count)/5
|
|
&r seta &eval(&Count)//5
|
|
if &r=1 then
|
|
&q seta &q-1
|
|
&r seta &r+5
|
|
endif
|
|
if &q<1 then
|
|
aerror 'Wait value too small'
|
|
elseif &q>255 then
|
|
aerror 'Wait value too large'
|
|
else
|
|
lda #&q
|
|
jsr &Routine&r
|
|
endif
|
|
endm
|
|
|
|
if hasAltADB then
|
|
PatchBase
|
|
endif
|
|
SetBusPlus6 nop ; add 6 clocks before loop
|
|
SetBusPlus4 nop ; add 4 clocks before loop
|
|
SetBusPlus2 nop ; add 2 clocks before loop
|
|
* lda #1 ; ( 2)
|
|
* jsr SetBusHighPlus0 ; ( 8)
|
|
SetBusPlus0 dea ; (10) decrement loop counter
|
|
SetBusPlus3 bne SetBusPlus0 ; (12) loop until A=0
|
|
|
|
SetBus lda rADBoutReg ; (16) read output register
|
|
bcs @SetHigh ; (18) (19)
|
|
ADBoutLow ; (20) modify to set bus low
|
|
bra @SetBusOutput ; (23)
|
|
|
|
@SetHigh ADBoutHigh ; (21) modify to set bus high
|
|
nop ; (23)
|
|
|
|
@SetBusOutput sta wADBoutReg ; (27) set the bus high or low
|
|
lda #ADBinMask ; ( 2) prepare check bus fighting
|
|
bit ADBinReg ; ( 6) test the bus
|
|
rts ; (12) all done
|
|
if hasAltADB then
|
|
|
|
gbla &FixrADBoutReg1,&FixOutLowOp1,&FixOutLowData1,&FixOutHighOp1
|
|
gbla &FixOutHighData1,&FixwADBoutReg1,&FixADBinMask1,&FixADBinReg1
|
|
&FixrADBoutReg1 seta SetBus+1-PatchBase
|
|
&FixOutLowOp1 seta SetBus+5-PatchBase
|
|
&FixOutLowData1 seta SetBus+6-PatchBase
|
|
&FixOutHighOp1 seta @SetHigh+0-PatchBase
|
|
&FixOutHighData1 seta @SetHigh+1-PatchBase
|
|
&FixwADBoutReg1 seta @SetBusOutput+1-PatchBase
|
|
&FixADBinMask1 seta @SetBusOutput+4-PatchBase
|
|
&FixADBinReg1 seta @SetBusOutput+6-PatchBase
|
|
endif
|
|
title 'IOP ADB Driver - ADB Request Transaction'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr ADBrequest
|
|
; Inputs: XmtCmdAndData - Command byte, followed by data, if any
|
|
; ByteCount - number of data bytes to send (0..8)
|
|
; Outputs: ReceiveBuffer - data received from device
|
|
; ByteCount - number of data bytes received (0..8)
|
|
; ADBFlags.ServiceRequest - set if SRQ detected, else 0
|
|
; ADBFlags.TimedOut - set if no reply data received, else 0
|
|
; Destroys: A, X, Y, n, v, z. c
|
|
; Calls: SetBus, SetBusAfter, SendBytes, @ReceiveReply (branched to)
|
|
; Called by: ADBAction
|
|
;
|
|
; Function: Performs the low level ADB transmission, sending attention,
|
|
; sync, command byte, and stop bit. Detects and records
|
|
; service requests after command byte's stop bit. Data is
|
|
; sent if ByteCount is non-zero, preceded by a start bit,
|
|
; and followed by a stop bit.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
ADBrequest
|
|
if ForceWorstCase then ; simulate worst case data length <1.5>
|
|
ldy #10*8 ; 10 byte times @ 8 bits per byte
|
|
@delay sec ; leave bus high
|
|
setbusafter (Tcyc*130/100) ; worst case bit time
|
|
dey ; update bits remaining count
|
|
bne @delay ; loop through all bits
|
|
endif ; <1.5>
|
|
clc ; prepare to set bus low for attention pulse
|
|
jsr SetBus ; ( 12) attn is low for Tattn
|
|
lda <ADBFlags ; ( 15) Setup default result flags
|
|
and #≈ServiceRequest ; ( 17) Assume no service request
|
|
ora #TimedOut ; ( 19) Assume timeout (no receive data)
|
|
sta <ADBFlags ; ( 22)
|
|
|
|
; initialize the receive buffer while attention is being sent.
|
|
|
|
ldx #MaxDataLen ; ( 24) buffer index
|
|
lda #$01 ; ( 26) initialize receive buffer to $01 for shift loop
|
|
@RcvBufInitLoop sta ReceiveBuffer,x ; ( 31) ( 101)
|
|
dex ; ( 33) ( 103)
|
|
bne @RcvBufInitLoop ; ( 36) ( 105)
|
|
stz ReceiveBuffer ; ( 109) first byte is zero, to allow for start bit
|
|
|
|
; check for specially handled reset command
|
|
|
|
lda XmtCmdAndData ; ( 113) Get the command byte
|
|
sta ADBXmtMsgData\
|
|
+ADBMsg.ADBCmd ; ( 117) return the command
|
|
and #ADBCmdMask ; ( 119) isolate the command
|
|
assert ResetCmd=$00
|
|
* eor #ResetCmd ; test for reset command
|
|
beq @SendReset ; ( 121) ( 122) if reset command, send it
|
|
|
|
; kill some extra time because Tattn is too long for SetBusAfter range.
|
|
|
|
* ldx #0 ; X is still zero from above
|
|
@AttnLowWait dex ; ( 123) (1398)
|
|
bne @AttnLowWait ; ( 126) (1400)
|
|
|
|
; Send Sync before command byte
|
|
|
|
ldy #1 ; (1402) byte count is 1, index is 0. ADBcmd
|
|
assert Tsynch=(Tcyc-T1) ; sync is same as high phase of a 1 bit (start bit)
|
|
SetBusAfter Tattn-Tlow-1402 ; (Tattn-Tlow) (12) beginning of low phase of start bit
|
|
jsr SendBytes ; (Tlow) (18) send start bit, ADBcmd, and Stop bit
|
|
bcc @BusFite ; (20) report error if fighting (before stop bit)
|
|
brADBhi @NoSRQ ; (22) (23) if no fight then no SRQ
|
|
|
|
; An SRQ is defined as TsrqMin .. TsrqMax of low from the beginning of the stop
|
|
; bit, which was Tstop ago. So the min would be TsrqMin - Tstop.
|
|
; And the max would be TsrqMax - Tstop.
|
|
|
|
lda #ADBinMask ; (24) prepare to read the bus
|
|
ldx #(TsrqMin\ ; get min srq poll time
|
|
-Tstop\ ; Tsrq is defined from start of stop bit
|
|
-26\ ; which started its high phase 26 clocks ago
|
|
+10)/11 ; (26) Round up, Load min loop counter
|
|
@ChkSRQmin bit ADBinReg ; ( 4) test to see if SRQ went away
|
|
brADBhi @NoSRQ ; ( 6) Branch if SRQ too short
|
|
dex ; ( 8) decrement timeout count
|
|
bne @ChkSRQmin ; (11) loop
|
|
|
|
; if the SRQ was low long enough, make sure it goes back high soon enough
|
|
|
|
ldx #(TsrqMax\ ; get remaining max srq poll time
|
|
-(TsrqMin+2)\ ; adjust for min srq time already waited
|
|
+10)/11+1 ; (TsrqMin+2) Load max loop counter (1 extra due to early dec)
|
|
@ChkSRQmax dex ; ( 2) decrement timeout count
|
|
beq @BusFite ; ( 4) if timed out, SRQ too long
|
|
bit ADBinReg ; ( 8) test to see if SRQ is done
|
|
brADBlo @ChkSRQmax ; (11) loop until it goes high or times out
|
|
|
|
lda #ServiceRequest ; Flag valid SRQ detected
|
|
tsb <ADBFlags ; update status
|
|
|
|
; After the stop bit (and possibly SRQ) we wait 200µs (391 clocks) before
|
|
; the start bit that indicates the beginning of the data bytes.
|
|
|
|
@NoSRQ ldx #1 ; (25) initialize buffer index
|
|
ldy <ByteCount ; (28) get send byte count
|
|
beq @ReceiveReply ; (30) (31) if no data to send, look for a reply
|
|
|
|
; Send Start bit before data bytes
|
|
clc ; (32) prepare to set bus low
|
|
SetBusAfter Tlt-32 ; (Tlt) (12) begin start bit before sending data
|
|
jsr SendBytes ; send the transmit buffer
|
|
* (fall into) brADBhi @Done ; all done if no bus fight
|
|
|
|
@BusFite
|
|
@DataError
|
|
@TimeOut
|
|
@Done stz <ByteCount ; indicate no bytes in reply
|
|
rts ; Done
|
|
if hasAltADB then
|
|
|
|
gbla &FixADBinMask2,&FixADBinReg2,&FixADBinReg3
|
|
&FixADBinMask2 seta @ChkSRQmin-3-PatchBase
|
|
&FixADBinReg2 seta @ChkSRQmin+1-PatchBase
|
|
&FixADBinReg3 seta @ChkSRQmax+4-PatchBase
|
|
endif
|
|
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Reset is a special case command, the command byte is not sent out
|
|
; on the bus, instead, the bus is held low for Tres.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
@ResetLoopCount equ (Tres-(132-15)+1299)/1300
|
|
@ResetWait equ (Tres-(132-15))/@ResetLoopCount-(4+15)
|
|
|
|
@SendReset
|
|
* ldx #0 ; X is still zero from above
|
|
@ResetLoop inx ; ( 2) increment loop count
|
|
cpx #@ResetLoopCount ; ( 4) carry := 1 if limit reached, otherwise 0.
|
|
SetBusAfter @ResetWait ; (12) either leave the bus zero, or set it high
|
|
bcc @ResetLoop ; (15) loop until last time
|
|
rts ; all done
|
|
title 'IOP ADB Driver - Receive Reply'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr @ReceiveReply
|
|
; Inputs: ByteCount - initialized to zero
|
|
; ReceiveBuffer[0] - initialized to zero
|
|
; ReceiveBuffer[1..8] - initialized to $01
|
|
; Outputs: ReceiveBuffer - data received from device
|
|
; ByteCount - number of data bytes received (0..8)
|
|
; ADBFlags.TimedOut - set if no reply data received, else 0
|
|
; Destroys: A, X, Y, n, v, z. c
|
|
; Calls: @UpdateMRU (falls into)
|
|
; Called by: ADBRequest
|
|
;
|
|
; Function: Performs the low level ADB data reception, sending attention,
|
|
; sync, command byte, and stop bit. Detects and records
|
|
; service requests after command byte's stop bit.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
@ReceiveReply lda #ADBinMask ; ( 33) get the bit mask for testing the bus
|
|
; start bit should happen by TltMax after stop bit
|
|
ldx #-((T0Max\ ; ( 35) setup low counter/timeout
|
|
-0\ ; no extra time to reomve
|
|
+10)/11+1) ; Round up, adjust for early inc
|
|
ldy #(TltMax\ ; setup start bit timeout
|
|
-(37-7)\ ; Adjust for time before/after
|
|
+10)/11 ; ( 37) round up
|
|
@StartSearch bit ADBinReg ; ( 4) sample the bus
|
|
brADBlo @LowLoop ; ( 6) loop until it goes low
|
|
dey ; ( 8) check for timeout
|
|
bne @StartSearch ; (11) loop until start bit or timeout
|
|
rts ; no reply data if start bit timeout
|
|
|
|
|
|
@GetNextBit beq @DataError ; (20) if Shift Data is zero, start bit was zero
|
|
ldx #-((T0Max\ ; (22) setup low counter/timeout
|
|
-22\ ; adjust for cycles already consumed
|
|
+10)/11+1) ; Round up, adjust for early inc
|
|
|
|
; count number of samples when bus is low
|
|
|
|
@LowLoop inx ; ( 2) update low count/timeout
|
|
beq @TimeOut ; ( 4) if low too long
|
|
bit ADBinReg ; ( 8) sample the bus
|
|
brADBlo @LowLoop ; (11) loop until it goes high
|
|
|
|
; count number of samples when bus is high
|
|
|
|
ldy #-((T0Max\ ; ( 2) setup high counter/timeout
|
|
-2\ ; adjust for cycles already consumed
|
|
+10)/11+1) ; Round up, adjust for early inc
|
|
@HighLoop iny ; ( 2) update high count/timeout
|
|
beq @CheckEnd ; ( 4) if high too long (maybe end of data)
|
|
bit ADBinReg ; ( 8) sample the bus
|
|
brADBhi @HighLoop ; (11) loop until it goes low
|
|
|
|
; data bit is based on the higher counter
|
|
|
|
stx <Temp ; ( 3) setup for compare
|
|
cpy <Temp ; ( 6) carry := 1 if high >= low
|
|
ldx <ByteCount ; ( 9) get count/buffer index
|
|
rol ReceiveBuffer,x ; (15) shift the bit in
|
|
bcc @GetNextBit ; (17) (18) loop until entire byte is received
|
|
|
|
cpx #MaxDataLen ; (19) see if end of buffer reached
|
|
bge @DataError ; (21) report error if too many bytes received
|
|
inc <ByteCount ; (26) update count/buffer index
|
|
ldx #-((T0Max\ ; (28) setup timeout counter
|
|
-31\ ; adjust for cycles already consumed
|
|
+10)/11+1) ; Round up, adjust for early inc
|
|
bra @LowLoop ; (31) get next bit
|
|
|
|
@CheckEnd ldx <ByteCount ; get count/buffer index
|
|
dec ReceiveBuffer,x ; test for Shift Data = $01 (no bits shifted in)
|
|
bne @TimeOut ; if partial byte received, then this is a timeout
|
|
cpx #MinDataLen ; see if the 2 byte minimum was reached
|
|
blt @DataError ; if not, then the report the error
|
|
|
|
lda #TimedOut ; indicate that there is a valid reply
|
|
trb <ADBFlags ; update the flags
|
|
* (fall into) bra @UpdateMRU ; mark the device as most recent
|
|
if hasAltADB then
|
|
|
|
gbla &FixADBinMask3,&FixADBinReg4,&FixADBinReg5,&FixADBinReg6
|
|
&FixADBinMask3 seta @ReceiveReply+1-PatchBase
|
|
&FixADBinReg4 seta @StartSearch+1-PatchBase
|
|
&FixADBinReg5 seta @LowLoop+4-PatchBase
|
|
&FixADBinReg6 seta @HighLoop+4-PatchBase
|
|
endif
|
|
|
|
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr @UpdateMRU
|
|
; Inputs: XmtCmdAndData - command whose device is to be made MRU
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z. c
|
|
; Calls: none
|
|
; Called by: @ReceiveReply (falls into)
|
|
;
|
|
; Function: Updates the MRU chain by making the specified device the
|
|
; most recently used, by removing it from the chain, and
|
|
; inserting it before the prior MRU entry, and marking it
|
|
; as the head of the MRU chain.
|
|
;
|
|
; When a Service Request is pending, don't update the MRU,
|
|
; because it is about to change again, and we don't want
|
|
; the MRU list to change during SRQ polling because if more
|
|
; that two devices were sending constant data, only the first
|
|
; two would get serviced.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
|
|
@UpdateMRU lda #ServiceRequest ; prepare to test service request
|
|
bit <ADBFlags ; see if it is set
|
|
bne @MRUok ; don't update MRU if SRQ pending
|
|
|
|
lda XmtCmdAndData ; get the last command sent
|
|
lsr a ; shift right 4 bits to get device address
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
cpa <MRUAddr ; see if already MRU
|
|
beq @MRUok ; if so, no need to change anything
|
|
|
|
tax ; load last address into index reg
|
|
bit DisableFlags,x ; see if polling is disabled for this address
|
|
bmi @MRUok ; if disabled, don't update the AutoPollAddr
|
|
|
|
ldy #-1 ; loop counter/index
|
|
@MRUloop iny ; inc counter/index
|
|
cpa MRUList,y ; look for entry that points to new MRU
|
|
bne @MRUloop ; loop until it is found
|
|
|
|
cpa <LRUAddr ; see if LRU is becoming MRU
|
|
bne @DoUpdate ; if not, do normal stuff
|
|
sty <LRUAddr ; new LRU is pointer to old LRU
|
|
bra @MRUdone ; all done
|
|
|
|
@DoUpdate lda MRUList,x ; get successor of new MRU
|
|
sta MRUList,y ; remove new MRU entry from chain
|
|
ldy <LRUAddr ; get pointer to LRU entry
|
|
txa ;
|
|
sta MRUList,y ; LRU entry points to new MRU entry
|
|
lda <MRUAddr ; get pointer to old MRU entry
|
|
sta MRUList,x ; new MRU entry points to old MRU entry
|
|
@MRUdone stx <MRUAddr ; mark new entry as MRU
|
|
stx <AutoPollAddr ; mark new entry as auto poll device
|
|
|
|
@MRUok rts ; return, with valid reply
|
|
title 'IOP ADB Driver - Send Bytes'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr SendBytes
|
|
; Inputs: X - index to first byte to send from XmtCmdAndData
|
|
; Y - number of bytes to send
|
|
; Outputs: c - 0 means send aborted due to bus fighting
|
|
; 1 means send was successfull
|
|
; z - setup for brADBlo/brADBhi testing
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetBusAfter, brADBlo
|
|
; Called by: ADBRequest
|
|
;
|
|
; Function: Sends a start bit, the requested number of bytes, and a
|
|
; stop bit. The send will be aborted, and indicated in
|
|
; carry bit, if a bus fight occurs on any bit other than the
|
|
; stop bit. The fight status of the stop bit is returned in
|
|
; the z bit. It is assumed the SetBusAfter was called to
|
|
; set the bus low to begin the start bit right before calling
|
|
; SendBytes.
|
|
;
|
|
; This routine returns to the caller 18 clocks after setting
|
|
; the stop bit high.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
SendBytes
|
|
|
|
; Send Start bit before data bytes
|
|
|
|
stz <Temp ; (21) setup shift data for 1 bit (start bit)
|
|
sec ; (23) start bit is a one
|
|
|
|
; Send the data bytes
|
|
|
|
@NextBit SetBusAfter Tlow-23 ; (Tlow) (12) wait for end of low phase
|
|
bra @SendLoop ; (15) join common code (waste 3 clocks)
|
|
|
|
@SendLoop sec ; (17)
|
|
SetBusAfter Tdata-17 ; (Tdata) (12) set the bus high for Thigh
|
|
clc ; (14) prepare to set bus low / flag fight error
|
|
brADBlo @Error ; (16) report error if fighting
|
|
SetBusAfter Thigh-16 ; (Thigh) (12) set the bus low for Tlow
|
|
bit <Temp ; (15) just waste 3 clocks
|
|
asl <Temp ; (20) Shift the next bit into carry
|
|
bne @NextBit ; (22) (23) loop if not final bit
|
|
|
|
; drop 1 clock every byte to adjust for 195.84 vs 196.00
|
|
; missing clock ; (23)
|
|
lda XmtCmdAndData,x ; (27) get the next data byte
|
|
sec ; (29) shift in a one, so non-zero until done
|
|
rol a ; (31) Shift first bit into carry, 1 into low bit
|
|
sta <Temp ; (34) setup shift data
|
|
inx ; (36) update the buffer index
|
|
dey ; (38) update byte count
|
|
bmi @SendStop ; (40) (41) send the entire buffer
|
|
SetBusAfter Tlow-40 ; (Tlow) (12) wait for end of low phase
|
|
bra @SendLoop ; (15) join common code
|
|
|
|
; Send Stop bit after data bytes
|
|
@SendStop sec ; ( 43)
|
|
SetBusAfter Tstop-43 ; (Tstop) (12) stop bit is low for Tstop
|
|
@Error rts ; (18) then leave bus high
|
|
if hasAltADB then
|
|
title 'IOP ADB Driver - CheckForAltADB'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr CheckForAltADB
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, z, c
|
|
; Calls: none
|
|
; Called by: InitADBDrvr
|
|
;
|
|
; Function: If running on an old IOP chip, patch in the SWIM support.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
CheckForAltADB ldx #-1 ; PatchTable index, pre-decremented
|
|
lda #ADBoutMask ; get the output bit mask
|
|
tsb wADBoutReg ; set the output bit
|
|
bit rADBoutReg ; see if the bit got set
|
|
beq @patchStart ; if clear, running on an old IOP chip
|
|
rts ; if set, new IOP, code is correct
|
|
|
|
; If this is an old IOP chip, we will patch our code to access the SWIM chip
|
|
; registers instead.
|
|
|
|
@addrLoop sta PatchBase,y ; change the code to use the new byte
|
|
@addrStart inx ; advance to next entry in the PatchTable
|
|
ldy PatchTable,x ; get the code offset to be changed
|
|
bne @addrLoop ; loop until end of list
|
|
|
|
@patchStart inx ; advance to next entry in the PatchTable
|
|
lda PatchTable,x ; get the new code byte to be used
|
|
bne @addrStart ; loop until end of list
|
|
rts ; code now patched for old IOP
|
|
|
|
|
|
FixrADBoutReg1 equ &FixrADBoutReg1
|
|
FixwADBoutReg1 equ &FixwADBoutReg1
|
|
FixOutLowOp1 equ &FixOutLowOp1
|
|
FixOutLowData1 equ &FixOutLowData1
|
|
FixOutHighOp1 equ &FixOutHighOp1
|
|
FixOutHighData1 equ &FixOutHighData1
|
|
FixADBinMask1 equ &FixADBinMask1
|
|
FixADBinMask2 equ &FixADBinMask2
|
|
FixADBinMask3 equ &FixADBinMask3
|
|
FixADBinReg1 equ &FixADBinReg1
|
|
FixADBinReg2 equ &FixADBinReg2
|
|
FixADBinReg3 equ &FixADBinReg3
|
|
FixADBinReg4 equ &FixADBinReg4
|
|
FixADBinReg5 equ &FixADBinReg5
|
|
FixADBinReg6 equ &FixADBinReg6
|
|
|
|
PatchTable
|
|
dc.b rAltADBoutReg**$FF ; change reads to the ADBoutReg
|
|
dc.b FixrADBoutReg1
|
|
dc.b 0
|
|
|
|
dc.b wAltADBoutReg**$FF ; change writes to the ADBoutReg
|
|
dc.b FixwADBoutReg1
|
|
dc.b 0
|
|
|
|
dc.b $29 ; change the 'ora' to an 'and'
|
|
dc.b FixOutLowOp1
|
|
dc.b 0
|
|
|
|
dc.b ≈AltADBoutMask ; active high output data
|
|
dc.b FixOutLowData1
|
|
dc.b 0
|
|
|
|
dc.b $09 ; change the 'and' to an 'ora'
|
|
dc.b FixOutHighOp1
|
|
dc.b 0
|
|
|
|
dc.b AltADBoutMask ; active high output data
|
|
dc.b FixOutHighData1
|
|
dc.b 0
|
|
|
|
dc.b AltADBinMask ; change loads of the ADBinMask
|
|
dc.b FixADBinMask1
|
|
dc.b FixADBinMask2
|
|
dc.b FixADBinMask3
|
|
dc.b 0
|
|
|
|
dc.b AltADBinReg**$FF ; change reads to the ADBinReg
|
|
dc.b FixADBinReg1
|
|
dc.b FixADBinReg2
|
|
dc.b FixADBinReg3
|
|
dc.b FixADBinReg4
|
|
dc.b FixADBinReg5
|
|
dc.b FixADBinReg6
|
|
dc.b 0
|
|
|
|
dc.b 0 ; end of table
|
|
endif
|
|
endwith
|
|
endproc
|
|
|
|
end
|
|
|