mac-rom/Drivers/IOP/ADBDriver.aii
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

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