boot3/OS/IoPrimitives/ImmgPrimitives.a
Elliot Nunn 5b0f0cc134 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 10:02:57 +08:00

1955 lines
69 KiB
Plaintext

;__________________________________________________________________________________________________
;
; File: ImmgPrimitives.a
;
; Contains: Primitive tables and init routines for the internal modem manager
;
; Written by: Steven Swenson
;
; Copyright © 1994-1994 by Apple Computer, Inc. All rights reserved.
;
; Change History (most recent first):
;
; <SM3> 1/26/94 rab Removed padForOverpatch stuff from the end of this file
; (SuperMario does not use itÉ).
; <SM2> 12/21/93 ged Changed rate11_025khz label to newer rate11025hz definition.
; <H13> 6/1/93 SES Made modifications as per SPI review on 6/1/93. The only real
; change was to vector the command count and receive count table
; pointers through global storage rather than lea's inside the
; mdmPrime routine (prevents someone who wants to patch the count
; tables from having to patch the entire mdmPrime routine). The
; other changes were to add comments, and rearrange a few of the
; EQU's into a more logical grouping.
; <H12> 5/23/93 SES Made changes from code review on 5/19/93.
; <H11> 5/21/93 SES Made the Slot9Handler routine check the status register
; interrupt level mask. If the mask is greater than 2, the
; routine sets the mask down to 2 and then restores it to whatever
; it was. This is to fix a problem that occurs because the Elan
; ethernet driver interrupt handling routine sometimes leaves the
; interrupt mask in the status register at 4 or 6, which causes
; problems on Optimus (when the slot 9 interrupt is really handled
; by the level 3 interrupt handler - if the level 3 handler cannot
; execute, the slot 9 interrupt is not cleared, and an infinite
; loop results).
; <H10> 5/17/93 SES Changed modem sound control and set up primitives. Sound control
; keeps track of when it has been called on and off so that it
; does not do on or off more than once in a row. The set up
; routine initializes the saved input source to none and the saved
; playthru volume to zero.
; <H9> 5/11/93 SES Screwed up! One minor mod needed to happen to make the previous
; fix work, the moveq instruction does not work with that large an
; operand!
; <H8> 5/11/93 SES Made mdmWakeup3615 primitive for Optimus return
; immgInvalidSelector error rather than no error, since the
; routine does not do anything (and in the future, some other
; routine might actually do something).
; <H7> 5/10/93 SES In order to alleviate a possible bus float situation, wrote data
; to the modem ID register instead of the ROMBase, since the IO
; data bus needed to be cleared, not the processor data bus.
; <H6> 5/6/93 SES Altered sound control so that the play through volume is set to
; equal the slider volume during modem sound transactions.
; <H5> 5/4/93 SES Removed machine primitives and replaced them with the dispatch
; functions in the IntModemMgr.a file.
; <H4> 4/27/93 SES Enhanced modem manager and adding primitives as per code review
; on 4/22/93. Moved dispatcher to IntModemMgr.a. Split
; primitives into machine primitives and modem primitives.
; Significantly reduced size of the command and reply lookup
; tables.
; <H3> 4/24/93 SES Played around with how sound is handled on Optimus for the
; modem.
; <H2> 4/15/93 SES Changed Optimus primitives so that machine does not hang when
; powered off. This means that the power control primitive
; disables interrupts from the modem when the modem is "turned
; off." Also implemented the sound control primitive for Optimus
; (hits sound control bit in sound control register. Finally,
; altered the structure of the primitives table to have an offset
; to the next modem table, effectively creating a linked list of
; modem tables so that they do not have to be physically
; contiguous in memory.
; <1> 4/8/93 SES first checked in
; <H1> 4/8/93 SES First checked in
;__________________________________________________________________________________________________
LOAD 'StandardEqu.d'
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'UniversalEqu.a'
INCLUDE 'ROMEqu.a'
INCLUDE 'IOPrimitiveEqu.a'
INCLUDE 'IntModemMgrEqu.a'
INCLUDE 'IntModemMgrPrivEqu.a'
INCLUDE 'PowerPrivEqu.a'
INCLUDE 'Sound.a'
MACHINE MC68020 ; <H3>
STRING ASIS
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
; BEGIN OPTIMUS MODEM PRIMITIVES
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
;----------------------------------------------------------------------------------------
; Equates for Optimus modem primitives
;----------------------------------------------------------------------------------------
OptMdmBase equ $50F09000
; Optimus modem address offsets
OptSPIMdmCtl EQU $000
OptSndCtl EQU $400
OptSPISftReg EQU $800
OptLmpSftCtl EQU $C00
OptMdmIDReg EQU $FFC
; Optimus OptSPIMdmCtl bit definitions
OptSPIMdmId EQU 0 ; (1 = SPI modem connected)
OptSPIAck EQU 2 ; (1 = ack idle)
OptSPIReq EQU 3 ; (1 = req idle)
OptSPIIrq EQU 4 ; (1 = interrupting)
OptRingDet EQU 5 ; ring detect bit
; Optimus OptLmpSftCtl bit definitions
OptLmpSPIDir EQU 4 ; shift direction control bit
; Optimus OptSndCtl bit definitions
bOptIntMask EQU 0
bOptSndMask EQU 1
; Bit definitions for the power manager status bits for modem
bPMMdmPower EQU 0 ; Modem power
bPMRingWakeUp EQU 2 ; Ring wake up enable
bPMMdmInstall EQU 3 ; Modem installed
bPMRingDet EQU 4 ; Ring detect
; equ for Optimus slot interrupt hack
OptHackSlotNum EQU 9 ; slot number for interrupt handler
; equ's for dummy slot 9 interrupt handler hack fix of Elan ethernet interrupt handler bug
statMaskBits EQU $0700 ; mask for status bits in SR
level3Bits EQU $0300 ; bits for checking level 3 and above mask in SR
level2Bits EQU $0200 ; bits for setting mask in SR to level 2
;----------------------------------------------------------------------------------------
; Equates for Baby Rock modem primitives
;----------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------
; Private storage data structures for 3615 primitives
;----------------------------------------------------------------------------------------
immg3615Rec RECORD 0
ModemType DS.L 1 ; ID of installed modem
ModemBase DS.L 1 ; base address of the modem
Slot9Addr DS.L 1 ; pointer to slot 9 handler queue element
SndInputSrc DS.B 1 ; saved sound input source ; <H3>
PlayThruVol DS.B 1 ; saved sound playthrough volume ; <H3>
PreviousCmd DS.W 1 ; the last command executed through the call; <H10>
SendTable DS.L 1 ; pointer to modem prime send table
RecvTable DS.L 1 ; pointer to modem prime receive table
size EQU *-immg3615Rec ; size of record
ENDR
immg3615SlotQEl RECORD 0
sqLink DS.L 1 ; link address
sqType DS.W 1 ; type of queue element
sqPrio DS.W 1 ; priority
sqAddr DS.L 1 ; address of handler
sqParm DS.L 1 ; optional A1 pointer
sqInt9Count DS.L 1 ; number of times in slot 9 interrupt
sqInt3Count DS.L 1 ; number of times in level 3 interrupt
size EQU *-immg3615SlotQEl ; size of record
ENDR
;----------------------------------------------------------------------------------------
; Private storage data structures for Baby Rock primitives
;----------------------------------------------------------------------------------------
immgBabyRockRec RECORD 0
ModemType DS.L 1 ; ID of installed modem
ModemBase DS.L 1 ; base address of the modem
SndMonState DS.W 1 ; current modem sound monitoring state
SndMonVolume DS.W 1 ; current modem sound volume level
size EQU *-immgBabyRockRec ; size of record
ENDR
ImmgPrim PROC
EXPORT ImmgBabyRock
ImmgBabyRock
;----------------------------------------------------------------------------------------
; ROM root table structure for modem manager initialization for BabyRock Modem in Blackbird.
; See IntModemMgrPrivEqu.a for the structure that is built below
;----------------------------------------------------------------------------------------
immgMdmListHeadBabyRock
DC.L mdmPrimsBabyRock ; offset to the BabyRock primitives
DC.L 0 ; no further modems supported
EndImmgBabyRock
mdmPrimsBabyRock
DC.W 0 ; flags
DC.W 0 ; number of primitives (filled in at init time)
DC.L mdmTypeBabyRock-mdmPrimsBabyRock ; offset to modem type routine
DC.L mdmPowerBabyRock-mdmPrimsBabyRock ; offset to modem power routine
DC.L mdmWakeupBabyRock-mdmPrimsBabyRock ; offset to modem wakeup on ring control routine
DC.L mdmStatusBabyRock-mdmPrimsBabyRock ; offset to modem status routine
DC.L mdmPrimeBabyRock-mdmPrimsBabyRock ; offset to modem prime routine
DC.L mdmSndCtlBabyRock-mdmPrimsBabyRock ; offset to modem sound control routine
DC.L mdmExistsBabyRock-mdmPrimsBabyRock ; offset to modem exists routine
DC.L mdmSetUpBabyRock-mdmPrimsBabyRock ; offset to modem set up routine
DC.L mdmTearDownBabyRock-mdmPrimsBabyRock; offset to modem tear down routine
DC.L mdmNameBabyRock-mdmPrimsBabyRock ; offset to modem name routine
DC.L mdmSndVolBabyRock-mdmPrimsBabyRock ; offset to modem sound volume routine
DC.L mdmSndHWBabyRock-mdmPrimsBabyRock ; offset to system sound arbitration routine
;----------------------------------------------------------------------------------------
; mdmTypeBabyRock
;
; Entry: a1 = modem primitive global pointer
;
; Exit: d0 = result code
; a0 = modem type
;
; Trashes: none
;
; Returns the modem type stored in global storage.
;----------------------------------------------------------------------------------------
mdmTypeBabyRock
WITH PowerDispRec,PMgrHookDispRec
MOVE.L #(PModemType<<16)|\
(PMgrHookDisp<<0),D0 ; get the modem type
_PowerDispatch
MOVEA.L D0,A0 ; return modem type
MOVEQ #noErr,D0 ; never any errors
RTS
ENDWITH
;----------------------------------------------------------------------------------------
; mdmPowerBabyRock
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = no error
;
; Trashes: none
;
;----------------------------------------------------------------------------------------
mdmPowerBabyRock
WITH immgParamBlock
MOVE.B #%00001000,D0 ; select power cmd on channel C
TST.W immgCommand(a0) ; IF Power Off Modem THEN
BNE.S @CallSerPwr ; .
ORI.B #(1<<7),D0 ; Set Power Off Bit
@CallSerPwr _SerialPower ; Call Serial Power
RTS
ENDWITH
;----------------------------------------------------------------------------------------
; mdmWakeupBabyRock
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Returns immgInvalidSelector error, while we wait for PG&E to É
; support wake up on ring. There is no software to take advantage of the wake up on ring
; feature at the present time. This routine returns an error so that when software
; becomes available that can take advantage of wake up on ring, it can check the return
; result from this function to determine if the desired behavior has taken place.
;----------------------------------------------------------------------------------------
mdmWakeupBabyRock
MOVE.W #immgInvalidSelector,D0 ; no good
RTS
;----------------------------------------------------------------------------------------
; mdmStatusBabyRock
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Constructs status byte from various sources.
;----------------------------------------------------------------------------------------
mdmStatusBabyRock
BRA DoPmgrCmd ; run through the Pmgr
;----------------------------------------------------------------------------------------
; mdmPrimeBabyRock
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
;
; Sends and receives bytes to/from the modem.
;
; Processor interrupts are masked during this command. The SCC data is polled during the
; call and the data is stored on the stack. Great pains are taken to keep the stack clear
; for the SCC data. At the end of the call, the PollProc is called to remove the SCC data
; from the stack.
;
; Interrupts can be disabled by this routine for an indeterminate amount of time. In
; practice, the call takes no more than about 4ms. The time out value for the communication
; of one byte of data to or from the modem is 32ms. It is therefore theoretically possible
; that in a worst case scenario, say some hardware problem where the communication to and
; from the modem takes 31ms, and there are 100 bytes to receive from the modem, that
; interrupts would be disabled for 3.1sec! This problem is left to future authors of the
; primitive.
;
; SPI stands for Serial Port Interface, and is a standard interface specification for
; the Motorola 6805 microcontroller.
;
; There is no attempt by this primitive to recover from any errors that may be encountered
; during the sending or receiving of bytes to/from the modem. If an error does occur, it
; is reported, but the hardware may not be in the expected state.
;
; Pointers to the send and receive count tables are stored in the mdmSetUp routine above.
; This is done so that patching the tables is easy.
;----------------------------------------------------------------------------------------
mdmPrimeBabyRock
DoPmgrCmd
_PMgrOp
RTS
;------------------------------------------------------------------------------------------------
; mdmSndCtlBabyRock
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Control modem sound for the BabyRock modem. The caller can either enable or disable the modem
; sound channel. Note that this channel is a software channel that sends DMA'ed (is that an
; adjective?) sound samples from the modem to the sound manager. The relative volume of that
; channel is controlled via the mdmSndVolBabyRock primitive.
;------------------------------------------------------------------------------------------------
mdmSndCtlBabyRock
WITH immgParamBlock,immgBabyRockRec
@savedRegs REG d1
movem.l @savedRegs,-(sp) ; save registers
move.w immgCommand(a0),d1 ; D1 -> sound command to process
@chkOnOff cmpi.w #immgSndOff,d1 ; check for sound monitoring on/off
beq.s @valid
cmpi.w #immgSndOn,d1
beq.s @valid
@undefined bra.s @done ; sound command unrecognized, leave
@valid cmp.w SndMonState(a1),d1 ; is modem sound already in this state?
beq.s @done ; if so, then we're doneÉ
cmpi.w #immgSndOn,d1
bne.s @turnOff
@turnOn ; Add Codec programming, not done by primitive code
; Set codec interface to TCM320AC36
move.b #2,HardRockCodecICR
; Set sample rate to SCLK * 256 (i.e. singer clock)
move.b #1,HardRockCodecSRCR
; Enable codec
move.b #1,HardRockCodecEnReg
; Call interface code
move.l #immgSndOn,-(sp)
bsr ModemSoundCntl
addq #4,sp
bne.s @noGood
bra.s @logIt
@turnOff move.l #immgSndOff,-(sp)
bsr ModemSoundCntl
addq #4,sp
bne.s @noGood
@logIt move.w d1,SndMonState(a1) ; save state for next time
@noGood ;not much we can do since the interface does not allow an error return, at least
;we don't set the SndMonState field incorrectly - so future calls have a shot at
;working (of course this shouldn't happen, butÉ)
@done moveq #noErr,d0
movem.l (sp)+,@savedRegs ; restore registers
@exit rts
ENDWITH
;___________________________________________________________________________________________
;
; Modem Hardware Info Structure
;
; Used to store pass information about the modem sound hardware until we get the
; Internal Modem Manager on-line (so that we can handle various modem hardware during
; development).
;___________________________________________________________________________________________
ModemHWInfo RECORD 0
Flags DS.B 1 ; bit flags specifying various modem HW modes
reserved1 DS.B 1 ; reserved for future use
mSrcSampleRate DS.L 1 ; modem sound source sample rate (unsigned fixed)
reserved2 DS.L 1 ; reserved for future use
reserved3 DS.L 1 ; reserved for future use
reserved4 DS.L 1 ; reserved for future use
ModemHWInfoSize EQU * ; size of ModemHWInfo
ENDR
; =====================================================================
;
; void ModemSoundCntl(Boolean enable);
;
; calls the custom sound primitive to enable/disable modem sound
;
; NOTE: The sample rate passed in the ModemHWInfo structure field must
; be in unsigned fixed format. This format is the same as that
; described in IM1 except the msb is not interpreted as a
; sign bit (e.g. AC440000 is 44,100 and NOT -21,436).
;
; NOTE2: Make sure the CODEC interface has been set up correctly
; before calling this routine (we don't touch it).
;
; =====================================================================
HrClockSrcBit EQU 0 ; bit is one if using external clock, zero if using Singer clk
ModemSoundCntl
ModemSndFrame RECORD {A6Link},DECR
enable DS.L 1
return DS.L 1
A6Link DS.L 1
reserved DS.L 1
LocalSize EQU *
ENDR
@savedRegs reg d0-d3/a0-a1
WITH ModemHWInfo,ModemSndFrame,ExpandMemRec
link a6,#LocalSize ; set up stack frame
movem.l @savedRegs,-(sp)
@gotIt moveq #ModemHWInfoSize,d0 ; allocate *TEMPORARY* pass-in structure
_NewPtr ,SYS,CLEAR
bne.s @done
movea.l a0,a1
;********************* SET HARDWARE CALL MONITORING ATTRIBUTES HERE *********************
;bset #HrClockSrcBit,Flags(a1) ; flag external clock for HardRock CODEC
bclr #HrClockSrcBit,Flags(a1) ; flag Singer clock for HardRock CODEC
move.l #rate11025hz,mSrcSampleRate(a1) ; set sample rate in unsigned fixed format
moveq #sndModemSndOff,d0 ; assume modem sound off
tst.l enable(a6) ; see what the caller wants
beq.s @execute ; we guessed right, so execute primitive
moveq #sndModemSndOn,d0 ; no, select modem sound on
@execute movea.l ([ExpandMem],emSndPrimitives),a0 ; get the address of the sound primitive table
move.l sndModemSound*4(a0),d1 ; is there a handler?
beq.s @leave ; no, just exit
move.l d1,a0
jsr (a0) ; call the ioprimitive
bne.s @primFailed
@primOK clr.w d3 ; save passed value for later check
bra.s @leave
@primFailed moveq #-1,d3 ; save error value for later check
@leave movea.l a1,a0
_DisposPtr ; get rid of *TEMPORARY* pass-in structure
@done movem.l (sp)+,@savedRegs
unlk a6
rts
ENDWITH
; ===================================================================== ;
;
; Boolean Error ModemHWCntl(Boolean enable);
;
; calls the custom sound primitive to Grab/Release modem sound hardware
;
; ===================================================================== ;
ModemHWCntl
ModemHWCFrame RECORD {A6Link},DECR
enable DS.L 1
return DS.L 1
A6Link DS.L 1
reserved DS.L 1
LocalSize EQU *
ENDR
@savedRegs reg d0-d2/a0-a1
WITH ModemHWCFrame,ExpandMemRec
link a6,#LocalSize ; set up stack frame
movem.l @savedRegs,-(sp)
moveq #sndReleaseHW,d0 ; assume modem sound release
tst.l enable(a6) ; see what the caller wants
beq.s @execute ; we guessed right, so execute primitive
moveq #sndGrabHW,d0 ; no, select modem sound grab
@execute movea.l ([ExpandMem],emSndPrimitives),a0 ; get the address of the sound primitive table
move.l sndSoundHWCntl*4(a0),d1 ; is there a handler?
beq.s @done ; no, just exit
move.l d1,a0
jsr (a0) ; call the ioprimitive
bne.s @problem
moveq #0,d0 ; flag that call was successful
bra.s @done
@problem moveq #1,d0 ; flag that call did not succeed
@done movem.l (sp)+,@savedRegs
unlk a6
rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmExistsBabyRock
;
; Entry: none
;
; Exit: d0 = result code
;
; Trashes: none
;
; Check if a BabyRock modem is installed in the box.
;----------------------------------------------------------------------------------------
mdmExistsBabyRock
WITH PowerDispRec,PMgrHookDispRec
MOVE.L #(PModemType<<16)|\
(PMgrHookDisp<<0),D0 ; get the modem type
_PowerDispatch
TST.W D0 ;
BEQ.S @noModem
MOVEQ #noErr,D0 ; indicate modem exists
RTS
@noModem
MOVE.W #immgNoBoardErr,d0 ; indicate no board
RTS
ENDWITH
;----------------------------------------------------------------------------------------
; mdmSetUpBabyRock
;
; Entry: none
;
; Exit: d0 = result code
; a0 = pointer to modem primitives globals
;
; Trashes: none
;
; Set up the primitives for the 3615 modem.
;----------------------------------------------------------------------------------------
mdmSetUpBabyRock
@savedRegs REG a1-a3
movem.l @savedRegs,-(sp) ; save registers
WITH immgBabyRockRec
; allocate private storage for modem primitives
move.l #immgBabyRockRec.size,d0 ; get size of private storage
_NewPtr ,SYS,CLEAR ; get a pointer in the system heap ; <H10>
tst.w d0 ; check for errors
bne.w @error1 ; if no memory, cannot continue
movea.l a0,a3 ; a3 = pointer to private storage
movea.l #HardRockSlotBase,a2 ; get base address of modem
move.l a2,ModemBase(a3) ; store base address
movea.l #FIFOMEM_BASE,a0 ; set up the HardRock FIFO bases (for DMA)
IMPORT GetRealProc
bsr.l GetRealProc
movea.l a0,a1
adda.l #FIFO_0_OFFSET,a1
move.l a1,HardRockFifoBase0
movea.l a0,a1
adda.l #FIFO_1_OFFSET,a1
move.l a1,HardRockFifoBase1
movea.l a0,a1
adda.l #FIFO_2_OFFSET,a1
move.l a1,HardRockFifoBase2
WITH PowerDispRec,PMgrHookDispRec
MOVE.L #(PModemType<<16)|\
(PMgrHookDisp<<0),D0 ; get the modem type
_PowerDispatch
ENDWITH
move.l d0,ModemType(a3) ; store the modem type in private storage
move.w #immgSndOff,SndMonState(a3) ; initialize state variables to track modem sound
move.w #$FF,SndMonVolume(a3) ; initialize modem sound volume (to full)
movea.l a3,a0 ; return pointer to primitives globals
moveq #noErr,d0 ; no error
@exit movem.l (sp)+,@savedRegs ; restore registers
rts
; indicate initialization error
@error1 move.w #immgInitError,d0 ; bad...
bra.s @exit
ENDWITH
;----------------------------------------------------------------------------------------
; mdmTearDownBabyRock
;
; Entry: a1 = pointer to modem primitives globals
;
; Exit: d0 = no error
;
; Trashes: none
;
; Deallocates everything that the set up routine allocated and sets PrimeTime so that
; no modem interrupts are generated.
;----------------------------------------------------------------------------------------
mdmTearDownBabyRock
WITH immgBabyRockRec
; deallocate private storage for modem primitives
movea.l a1,a0 ; A0 -> pointer to private storage
_DisposPtr
moveq #noErr,d0 ; no error
@exit rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmNameBabyRock
;
; Entry: a0 = pointer to string buffer
; a1 = pointer to modem primitives globals
;
; Exit: d0 = no error
; a0 = pointer to null terminated string
;
; Trashes: A1
;
; Returns the name of the modem.
;----------------------------------------------------------------------------------------
mdmNameBabyRock
MOVE.L A0,-(sp) ; save pointer
MOVEA.l A0,A1 ; destination
LEA @modemName,A0 ; source (skip over length byte)
@loop MOVE.B (A0)+,(A1)+ ; copy one byte
BNE.S @loop ; keep copying until null terminal byte is copied
MOVEA.L (SP)+,A0 ; return modem name in A0
MOVEQ #noErr,D0 ; no error
@exit RTS
@modemName DC.B 'Baby Rock'
DC.B 0
ALIGN 2
;----------------------------------------------------------------------------------------
; mdmSndVolBabyRock
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Controls the volume of the modem sound channel relative to the overall hardware sound
; level.
;----------------------------------------------------------------------------------------
mdmSndVolBabyRock
WITH immgParamBlock,immgBabyRockRec,ExpandMemRec
@savedRegs REG a0/d1
movem.l @savedRegs,-(sp) ; save registers
move.w immgCommand(a0),d1 ; D1 -> sound command to process
@chkCmd cmpi.w #immgVolSet,d1 ; dispatch to appropriate codeÉ
beq.s @setVol
cmpi.w #immgVolRead,d1
beq.s @readVol
@undefined bra.s @badCmd ; sound command unrecognized, leave
@setVol cmpi.w #2,immgLength(a0) ; verify the volume parameter format
bne.s @formatErr
move.w ([immgSBuffer,a0]),d0 ; get volume level to set
movea.l ([ExpandMem],emSndPrimitives),a0 ; get the address of the sound primitive table
move.l sndModemSndVol*4(a0),d1 ; is there a handler?
beq.s @problemo ; no, so bail
move.l d1,a0
jsr (a0) ; call the snd primitive
bne.s @problemo ; bail if the primitive returns an error
move.w d0,SndMonVolume(a1) ; save current modem sound level (for later read calls)
bra.s @normal
@readVol move.w #2,immgLength(a0) ; set up for volume parameter return
tst.l immgRBuffer(a0) ; be paranoid and make sure a return buff ptr is present
beq.s @formatErr
move.w SndMonVolume(a1),([immgRBuffer,a0])
bra.s @normal
@problemo move.w #immgCommandError,d0 ; signals a generic problem with command execution
bra.s @exit
@formatErr move.w #immgSendEndErr,d0 ; simulate a data transfer error
bra.s @exit
@badCmd move.w #immgInvalidCommand,d0
bra.s @exit
@normal moveq #noErr,d0
@exit movem.l (sp)+,@savedRegs ; restore registers
rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmSndHWBabyRock
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Allows the caller to negotiate/release exclusive control of the sound hardware.
;----------------------------------------------------------------------------------------
mdmSndHWBabyRock
WITH immgParamBlock,immgBabyRockRec,ExpandMemRec
@savedRegs REG a0-a1/d1
movem.l @savedRegs,-(sp) ; save registers
move.w immgCommand(a0),d1 ; D1 -> sound command to process
@chkCmd cmpi.w #immgSndRelease,d1 ; dispatch to appropriate codeÉ
beq.s @valid
cmpi.w #immgSndTakeover,d1
beq.s @valid
@undefined bra.s @badCmd ; sound command unrecognized, leave
@valid movea.l ([ExpandMem],emSndPrimitives),a1 ; get the address of the sound primitive table
move.l sndSoundHWState*4(a1),d0 ; is there a handler?
beq.s @problemo ; no, so bail
move.l d0,a1
jsr (a1) ; call the snd primitive
bne.s @problemo ; bail if the primitive returns an error
cmpi.w #immgSndRelease,d1 ; see if command makes sense given current HW state
bne.s @isGrabOK
@isRlseOK cmpi.b #sndReleaseHW,d0
beq.s @problemo ; (bail if command is release and we are already released)
bra.s @tryRelease
@isGrabOK cmpi.b #sndReleaseHW,d0
bne.s @problemo ; (bail if command is grab and HW is already "grabbed")
@tryGrab move.w #sndGrabHW,d0
bra.s @arbitrate
@tryRelease move.w #sndReleaseHW,d0
@arbitrate movea.l ([ExpandMem],emSndPrimitives),a1 ; get the address of the sound primitive table
move.l sndSoundHWCntl*4(a1),d1 ; is there a handler?
beq.s @problemo ; no, so bail
move.l d1,a1
jsr (a1) ; call the snd primitive
bne.s @arbFailed
@arbGood move.w #immgArbOK,d1
bra.s @normal
@arbFailed move.w #immgArbFailed,d1
@normal moveq #noErr,d0
bra.s @report
@problemo move.w #immgCommandError,d0 ; signals a generic problem with command execution
move.w #immgArbFailed,d1
bra.s @report
@badCmd move.w #immgInvalidCommand,d0
move.w #immgArbFailed,d1
@report move.w #2,immgLength(a0)
tst.l immgRBuffer(a0) ; be paranoid and check for receive buffer ptr
beq.s @formatErr
move.w d1,([immgRBuffer,a0])
bra.s @exit
@formatErr move.w #immgRecvEndErr,d0 ; simulate a data transfer error
@exit movem.l (sp)+,@savedRegs ; restore registers
rts
ENDWITH
EXPORT ImmgOptimus
ImmgOptimus
;----------------------------------------------------------------------------------------
; ROM root table structure for modem manager initialization for Optimus.
; See IntModemMgrPrivEqu.a for the structure that is built below
;----------------------------------------------------------------------------------------
immgMdmListHeadOpt
DC.L mdmPrims3615-immgMdmListHeadOpt ; offset to 3615 primitives
DC.L 0 ; no further modems supported
EndImmgOptimus
mdmPrims3615
DC.W 0 ; flags
DC.W 0 ; number of primitives (filled in at init time)
DC.L mdmType3615-mdmPrims3615 ; offset to modem type routine
DC.L mdmPower3615-mdmPrims3615 ; offset to modem power routine
DC.L mdmWakeup3615-mdmPrims3615 ; offset to modem wakeup on ring control routine
DC.L mdmStatus3615-mdmPrims3615 ; offset to modem status routine
DC.L mdmPrime3615-mdmPrims3615 ; offset to modem prime routine
DC.L mdmSndCtl3615-mdmPrims3615 ; offset to modem sound control routine
DC.L mdmExists3615-mdmPrims3615 ; offset to modem exists routine
DC.L mdmSetUp3615-mdmPrims3615 ; offset to modem set up routine
DC.L mdmTearDown3615-mdmPrims3615 ; offset to modem tear down routine
DC.L mdmName3615-mdmPrims3615 ; offset to modem name routine
DC.L mdmSndVol3615-mdmPrims3615 ; offset to modem sound volume routine
DC.L mdmSndHW3615-mdmPrims3615 ; offset to system HW arbitration routine
;----------------------------------------------------------------------------------------
; mdmType3615
;
; Entry: a1 = modem primitive global pointer
;
; Exit: d0 = result code
; a0 = modem type
;
; Trashes: none
;
; Returns the modem type stored in global storage.
;----------------------------------------------------------------------------------------
mdmType3615
WITH immg3615Rec
move.l ModemType(a1),a0 ; A0 -> modem type from private storage
moveq #noErr,d0 ; no error
rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmPower3615
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = no error
;
; Trashes: none
;
; The express modem on Optimus cannot be turned on and off like the express modem on the
; portables. Thus this routine disables interrupts from the modem to simulate the modem
; power being turned off, and enables interrupts from the modem to simulate the modem
; power being turned on.
;----------------------------------------------------------------------------------------
mdmPower3615
WITH immg3615Rec, immgParamBlock
move.l a2,-(sp) ; save a2
movea.l ModemBase(a1),a2 ; base address of modem
move.b OptSndCtl(a2),d0 ; get the current status
bclr #bOptIntMask,d0 ; assume power on, unmask interrupts
tst.w immgCommand(a0) ; check power on or off ; <H10>
bne.s @commonExit
bset #bOptIntMask,d0 ; mask interrupts
@commonExit move.b d0,OptSndCtl(a2) ; set the status
move.l (sp)+,a2 ; restore a2
moveq #noErr,d0
rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmWakeup3615
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Returns immgInvalidSelector error, since the express modem on Optimus does not currently
; support wake up on ring. There is no software to take advantage of the wake up on ring
; feature at the present time. This routine returns an error so that when software
; becomes available that can take advantage of wake up on ring, it can check the return
; result from this function to determine if the desired behavior has taken place.
;----------------------------------------------------------------------------------------
mdmWakeup3615
WITH immg3615Rec
move.w #immgInvalidSelector,d0 ; <H8>
rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmStatus3615
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Constructs status byte from various sources.
;----------------------------------------------------------------------------------------
mdmStatus3615
WITH immgParamBlock
moveq #(1<<bPMMdmPower) | \ ; modem is on
(1<<bPMRingWakeUp) | \ ; ring wakeup is enabled
(1<<bPMMdmInstall),d0 ; modem is installed
btst.b #OptRingDet,OptMdmBase+OptSPIMdmCtl ; test if ring detect is set
beq.s @noRingDet
moveq #(1<<bPMMdmPower) | \ ; modem is on
(1<<bPMRingWakeUp) | \ ; ring wakeup is enabled
(1<<bPMMdmInstall) | \ ; modem is installed
(1<<bPMRingDet),d0 ; ring detect is set
@noRingDet move.w #1,immgLength(a0) ; set the length to one byte
move.l immgRBuffer(a0),a0 ; A0 -> pointer to receive buffer
move.b d0,(a0) ; put the status into the return buffer
move.w #noErr,d0 ; set error code
rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmPrime3615
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Uses:
; D0: scratch register, result code
; D1: high word: scratch; low word: command number
; D2: scratch (by lower level routines)
; D3: scratch
; D4: scratch
; D5: scratch
; D6: high word: contents of status register; low word: scratch
; D7: stack base for poll stack
;
; A0: modem manager parameter block pointer
; A1: scratch, VIA 1 base
; A2: modem base address
; A3: scratch (by lower level routines, return address)
; A4: scratch (by lower level routines, return address)
; A5: pointer to immg3615Rec, not valid after UnloadPollStack
; A6: pointer to SCC channel A data register
; A7: stack pointer, pollstack pointer
;
; Sends and receives bytes to/from the modem.
;
; Processor interrupts are masked during this command. The SCC data is polled during the
; call and the data is stored on the stack. Great pains are taken to keep the stack clear
; for the SCC data. At the end of the call, the PollProc is called to remove the SCC data
; from the stack.
;
; Interrupts can be disabled by this routine for an indeterminate amount of time. In
; practice, the call takes no more than about 4ms. The time out value for the communication
; of one byte of data to or from the modem is 32ms. It is therefore theoretically possible
; that in a worst case scenario, say some hardware problem where the communication to and
; from the modem takes 31ms, and there are 100 bytes to receive from the modem, that
; interrupts would be disabled for 3.1sec! This problem is left to future authors of the
; primitive.
;
; SPI stands for Serial Port Interface, and is a standard interface specification for
; the Motorola 6805 microcontroller.
;
; There is no attempt by this primitive to recover from any errors that may be encountered
; during the sending or receiving of bytes to/from the modem. If an error does occur, it
; is reported, but the hardware may not be in the expected state.
;
; Pointers to the send and receive count tables are stored in the mdmSetUp routine above.
; This is done so that patching the tables is easy.
;----------------------------------------------------------------------------------------
mdmPrime3615
immgrPollRegs REG D3/D4/D5/D6/A0/A1/A2/A4 ; registers to save across pollproc call
@numPollRegs EQU 4+4 ; number of registers to save across pollproc call
@savedRegs REG D1-D7/A0-A6 ; registers saved across this routine
movem.l @savedRegs,-(SP) ; save working registers
move.w sr,d6 ; save the entry SR in the upper half of D6
swap d6
WITH immgParamBlock
movea.l immg3615Rec.ModemBase(a1),a2 ; point to modem base address
movea.l a1,a5 ; save pointer to immg3615Rec
; disable interrupts during the time of this call
ori #HiIntMask,sr ; no interrupts
; set up for SCC polling...
lea -4*@numPollRegs(sp),sp ; allocate space for registers saved if we call PollProc
move.l sp,d7 ; point to the stack top for our SCC poll stack
movea.l SccRd,a6 ; point to the SCC channel A's data register <H26>
addq.w #aData,a6 ; <H26>
tst.l PollProc ; is a poll proc installed? <H52>
bne.s @WillPoll ; -> yes <H52>
; the offset +sccData is added to the address below because the routines that read the
; scc data out of the address subtract sccData from the base of the scc when reading data.
; See WaitSPIAckHi and WaitSPIAckLo below.
lea @NoPollSCC+sccData,a6 ; no, avoid collecting bytes no one will use <H52>
@WillPoll
; ----- send command and count ----
move.w immgCommand(a0),d3
move.w d3,d1
bsr SendSPI ; send command byte
bne.s @ImmgOpExit ; exit if error returned <H24>
movea.l immg3615Rec.SendTable(a5),a1 ; and get the send count table <H21>
move.w immgLength(a0),d5 ; pop the count into d5
bsr GetLenCount ; D0 -> number of bytes to send, -1 for dynamic
bpl.s @noCount ; if positive, no count
move.w d5,d1
bsr SendSPI ; send count byte
bne.s @ImmgOpExit ; exit if error returned <H24>
; ----- send data ----
@noCount movea.l immgSBuffer(a0),a1 ; get the pointer to the command's data bytes
moveq #0,d1 ; (set CCR for BEQ so DBNE below won't fall thru)
bra.s @StartSend
@SendData move.b (a1)+,d1 ; get the next data byte
bsr SendSPI ; and send it
@StartSend dbne d5,@SendData ; -> more bytes to send
bne.s @ImmgOpExit ; -> error
; ----- receive data -----
movea.l immg3615Rec.RecvTable(a5),a1 ; and get the receive count table <H21>
bsr GetLenCount ; D0 -> number of bytes to send, -1 for dynamic
move.b d1,d3
bmi.s @readReplyCount ; (<0)
cmp.b #1,d3 ; test against 1
ble.s @readData ; if ( =0 or =1 ) go to read
subq #1,d3 ; (>1) correct count
bra.s @readData ; if 0 or 1 go to read
@readReplyCount
bsr ReceiveSPI ; read first byte for receive count
bne.s @ImmgOpExit ; exit if error returned <H24>
move.b d1,d3 ; move count into d3
@readData ; d4 has receive byte count
movea.l immgRBuffer(a0),a1 ; a1 new points
move.w d3,immgLength(a0) ;
bra.s @StartReceive ; start receiving data
@ReceiveByte
bsr ReceiveSPI ; read a byte into d1
bne.s @ImmgOpExit ; -> error
move.b d1,(a1)+ ; move data byte into buffer
@StartReceive
dbra d3,@ReceiveByte ; -> more bytes to send
@ImmgOpExit
; check if any SCC bytes were collected so we can send them to the serial driver...
move.l d0,d4 ; save return result
movea.l VIA,a1 ; point to VIA1
bsr UnloadPollstack ; unload any SCC data stashed while interrupts were disabled
lea 4*@numPollRegs(sp),sp ; de-allocate PollProc saved register frame
move.l d4,d0 ; restore return result
; clean everything up...
swap d6
move.w d6,sr
tst.l d0 ; to set condition codes
@exit movem.l (sp)+,@savedRegs ; restore working registers
rts
ENDWITH
@NoPollSCC dc.w 0 ; A6 points here if no PollProc to avoid stack overflow <H52>
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ <H24>
; Routine: WaitSPIAckHi
;
; Input: a2.l - pointer to modem base
; a6 - pointer to serialpoll data area
;
; Destroys: d2,d1,a3
;
; Returns d0.b - 0 = ok, non-zero = error
;
; Function: wait for SPI ack high
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ <H24>
WaitSPIAckHi
move.l (sp)+,a3 ; pop return address into a3 <37>
move.w #32,d2 ; loop to max 32 msec
@nextmsec
move.w timedbra,d1 ; 1 msec count
@waitAckhi
btst.b #RxCA,-sccData(A6) ; SCC data available? <37>
beq.s @WaitLoop ; <37>
move.b (A6),-(SP) ; yes, push it on the stack <37>
@WaitLoop ; <37>
btst.b #OptSPIAck,OptSPIMdmCtl(a2) ; test ack
dbne d1,@waitAckhi ; loop for upto 1 msec
dbne d2,@nextmsec ; loop for d2 msec
seq d0 ; set result (d0 <> 0 = error), bit lo
tst.b d0 ; set the condition codes <H26>
jmp (a3) ; return through a3 <37>
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ <H24>
; Routine: WaitSPIAckLo
;
; Input: a2 - pointer to modem base
; a6 - pointer to serialpoll data area
;
; Destroys: d2,d1,a3
;
; Returns d0 - 0 = ok, non-zero = error
;
; Function: wait for SPI ack lo
;
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ <H24>
WaitSPIAckLo
move.l (sp)+,a3 ; pop return address into a3 <37>
move.w #32,d2 ; loop to max 32 msec
@nextmsec
move.w timedbra,d1 ; 1 msec count
@waitAckhi
btst.b #RxCA,-sccData(A6) ; SCC data available? <37>
beq.s @WaitLoop ; <37>
move.b (A6),-(SP) ; yes, push it on the stack <37>
@WaitLoop ; <37>
btst.b #OptSPIAck,OptSPIMdmCtl(a2) ; test ack
dbeq d1,@waitAckhi ; loop for upto 1 msec
dbeq d2,@nextmsec ; loop for d2 msec
sne d0 ; set result (d0 <> 0 = error), bit hi
tst.b d0 ; set the condition codes <H26>
jmp (a3) ; return through a3 <37>
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; Routine: SendSPI
;
; Input: a2.l - pointer to modem base
; a6 - pointer to serialpoll data area
; d1.b - byte to send
;
; Destroys: a4
; Destroys: d2,d1,a3 (by lower level routines)
;
; Returns d0.w - 0 = ok, non-zero = error
;
; Function: send a byte thru the SPI
;
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
SendSPI
move.l (sp)+,a4 ; pop return address into a4 <37>
swap d1 ; save the data byte in the hi work of d0
bsr.s WaitSPIAckHi ; (1) wait for pmgr idle <H24>
bne.s @error ; if bit low, error <H24>
;begin transaction
swap d1 ; get the data byte back
bset.b #OptLmpSPIDir,OptLmpSftCtl(a2) ; (2) set direction to output
move.b d1,OptSPISftReg(a2) ; (3) write data
bclr.b #OptSPIReq,OptSPIMdmCtl(a2) ; (4) assert data valid
bsr.s WaitSPIAckLo ; (5) --> modem shift data <H24>
; (6) wait for data accepted <H24>
bne.s @error ; if bit low, error <H24>
bset.b #OptSPIReq,OptSPIMdmCtl(a2) ; (7) clear data valid
move.w #noErr,d0 ; report no error
jmp (a4)
@error ; <H24>
bset.b #OptSPIReq,OptSPIMdmCtl(a2) ; (7) clear data valid <28>
move.w #immgSendStartErr,d0 ; report send error <H24>
jmp (a4)
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; Routine: ReceiveSPI
;
; Input: a2.l - pointer to modem base
; a6 - pointer to serialpoll data area
; d1.b - byte to send
;
; Destroys: a4
; Destroys: d2,a3 (by lower level routines)
;
; Returns d0.w - 0 = ok, non-zero = error
; d1.b - data byte read
;
; Function: read a byte from the spi
;
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
ReceiveSPI
move.l (sp)+,a4 ; pop return address into a4 <37>
bsr.s WaitSPIAckHi ; (1) wait for pmgr idle <H24>
bne.s @error ; if bit low, error <H24>
;begin transaction
bclr.b #OptLmpSPIDir,OptLmpSftCtl(a2) ; (2) set direction to input
bclr.b #OptSPIReq,OptSPIMdmCtl(a2) ; (3) (RFD) ready for data
bsr.s WaitSPIAckLo ; (4) acknowledge req <H24>
; (5) <-- modem shifting <H24>
bne.s @error ; if bit low, error <H24>
bset.b #OptSPIReq,OptSPIMdmCtl(a2) ; (6) acknowledge ack
bsr.s WaitSPIAckHi ; (7) wait (DAV) <H24>
bne.s @error ; if bit low, error <H24>
move.b OptSPISftReg(a2),d1 ; (8) read data
move.w #noErr,d0 ; report no error
jmp (a4)
@error
bset.b #OptSPIReq,OptSPIMdmCtl(a2) ; (7) clear data valid <28>
move.w #immgRecvStartErr,d0 ; mark as recieve error <H24>
jmp (a4)
;_______________________________________________________________________
;
; Routine: UnloadPollstack
;
; Inputs: D7 -- pointer to top of poll stack
; A1 -- pointer to VIA1 base
; A6 -- pointer to SCC channel A data register
; SP -- pointer to bottom of poll stack
;
; Outputs: D7 -- pointer to top of poll stack
; A1 -- pointer to VIA1 base
; A6 -- pointer to SCC channel A data register
; SP -- pointer to top of poll stack
;
; Trashes: D0,D1,D2,A3,A4,A5
;
; Function: calls the poll proc to unload any bytes stashed from SCC
; port A while interrupts were disabled
;_______________________________________________________________________
UnloadPollstack
movea.l (sp)+,A4 ; pop the return address
cmpa.l d7,sp ; is there any poll data?
beq.s @NoSCCData ; -> no
move.l PollProc,d0 ; is there a poll proc?
beq.s @NoPollProc ; -> no
move.l d7,PollStack ; stuff the PollStack
movea.l d7,a3
movem.l immgrPollRegs,(a3) ; save regs while calling poll proc W/O USING STACK
lea vBufA(a1),a5 ; point to the register with the SCC WR/REQ bit in VIA 1<H26>
movea.l d0,a3
jsr (a3) ; run the poll proc
movea.l d7,a3
movem.l (a3),immgrPollRegs ; restore our registers
@NoPollProc movea.l d7,sp ; toss any bytes left on the pollstack <H26>
@NoSCCData jmp (a4)
;----------------------------------------------------------------------------------------
; mdmSndCtl3615
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Hit the bit in the modem sound control register to turn sound on or off. Use sound
; primitives to set the sound input source and the play thru volume.
;----------------------------------------------------------------------------------------
mdmSndCtl3615
sndInvalidSrc EQU $ff ; invalid sound input source
WITH immg3615Rec, immgParamBlock, ExpandMemRec, SoundIOHeader
@savedRegs REG d1 ; registers to save over call
movem.l @savedRegs,-(sp) ; save registers
move.w immgCommand(a0),d1 ; D1 -> sound on or off command ; <H10>
cmp.w PreviousCmd(a1),d1 ; make sure that we are doing the opposite of what has been done ; <H10>
beq.s @done ; if the same, that means sound on request ; <H10>
; with sound on already, or sound off request; <H10>
; with sound off already : <H10>
move.w d1,PreviousCmd(a1) ; save the command being executed ; <H10>
move.b OptMdmBase+OptSndCtl,d0 ; pick up the current state
bclr #bOptSndMask,d0 ; assume modem sound off
cmp.w #immgSndOn,d1 ; check if sound should go on
bne.s @continue ; if not, continue
bset #bOptSndMask,d0 ; turn modem sound on
@continue move.b d0,OptMdmBase+OptSndCtl ; make it so!
cmp.w #immgSndOn,d1 ; check if sound should go on ; <H3>
beq.s @saveIt ; if sound on, save the current state ; <H3>
move.b SndInputSrc(a1),d0 ; no, restore sound input
move.b PlayThruVol(a1),d1 ; D1 -> saved playthrough volume
exg d0,d1 ; D0 -> playthrough volume
bsr SetPlayThru ; reset playthrough volume first
exg d0,d1 ; D0 -> sound input source
bsr SetInputSrc ; reset input source last
bra.s @done ; continue to just set sound input ; <H3>
@saveIt movea.l ([ExpandMem],emSndPrimitives),a0 ; <H3>
move.b UserVolume(a0),PlayThruVol(a1) ; save playthrough volume setting
move.b SDVolume,d1 ; D1 -> playthrough volume
tst.l sndInputSource*4(a0) ; is there a handler? ; <H3>
beq.s @done ; no, just exit ; <H3>
move.l sndInputSource*4(a0),a0 ; <H3>
jsr (a0) ; call the ioprimitive ; <H3>
; check that the source that was returned is not invalid. The primitives for Optimus
; currently return sndInvalidSrc if the sound input source is undefined (not yet been
; set). Unfortunately, if we try to restore the sound input source to the value
; sndInvalidSrc, DFAC II hangs.
cmp.b #sndInvalidSrc,d0 ; check for garbage ; <H3>
bne.s @okToSave ; if not garbage, save it ; <H3>
moveq #0,d0 ; assume no input ; <H3>
@okToSave move.b d0,SndInputSrc(a1) ; save previous input source ; <H3>
moveq #sndAuxiliary,d0 ; set modem sound on ; <H3>
bsr SetInputSrc ; set input source first
exg d0,d1 ; D0 -> playthrough volume
bsr SetPlayThru ; set playthrough last
@done move.w #noErr,d0
movem.l (sp)+,@savedRegs ; restore registers
@exit rts
SetPlayThru movea.l ([ExpandMem],emSndPrimitives),a0 ; <H3>
tst.l sndPlayThruVol*4(a0) ; is there a handler? ; <H3>
beq.s @done ; no, just exit ; <H3>
move.l sndPlayThruVol*4(a0),a0
jsr (a0) ; call the ioprimitive
@done rts
SetInputSrc movea.l ([ExpandMem],emSndPrimitives),a0 ; <H3>
tst.l sndInputSelect*4(a0) ; is there a handler? ; <H3>
beq.s @done ; no, just exit ; <H3>
move.l sndInputSelect*4(a0),a0 ; <H3>
jsr (a0) ; call the ioprimitive ; <H3>
@done rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmExists3615
;
; Entry: none
;
; Exit: d0 = result code
;
; Trashes: none
;
; Check if a 3615 modem is installed in the box.
;----------------------------------------------------------------------------------------
mdmExists3615
@savedRegs REG d1/a2 ; registers to save over call
movem.l @savedRegs,-(sp) ; save registers
; now, how to tell if a modem is installed? Unfortunately, we do NOT get a
; bus error reading the modem register space (since this is ethernet prom space).
; This means that we must write a register in the modem and read it back to determine
; if there is anything at that address that will latch the data we send.
move.b BIOS_Config2+BIOSAddr,d0 ; get BIOS config 2 reg contents
bset #bBIOSIOCSTime,d0 ; set IO_CS_Time to be asserted on writes
move.b d0,BIOS_Config2+BIOSAddr ; put byte into configuration reg
movea.l #OptMdmBase,a2 ; get base address of modem
move.b OptSndCtl(a2),d0 ; contents of interrupt req reg
not.b d0 ; invert bits
bset #bOptIntMask,d0 ; never unmask interrupts unless handler is installed
move.b d0,OptSndCtl(a2) ; write invert out to int req reg
not.b d0 ; put back original
bset #bOptIntMask,d0 ; never unmask interrupts unless handler is installed
; Do a write to the modem ID reg so that the bus does not float to the thing just
; written to the register. MdmID is a safe place to write arbitrary data. If the
; byte written to MdmID floats on the next read, we will conclude that there is no
; modem installed (due to the eor).
move.b d0,OptMdmBase+OptMdmIDReg ; Place bad data on the bus ; <H7>
move.b OptSndCtl(a2),d1 ; read back contents
eor.b d1,d0 ; to test if bit for int mask latched
btst.l #bOptSndMask,d0 ; check int mask bit
beq.w @noModem ; no modem!
move.w #noErr,d0 ; indicate modem exists
@exit move.b BIOS_Config2+BIOSAddr,d1 ; get BIOS config 2 reg contents
bclr #bBIOSIOCSTime,d1 ; set IO_CS_Time to not be asserted on writes
move.b d1,BIOS_Config2+BIOSAddr ; put byte into configuration reg
movem.l (sp)+,@savedRegs ; restore registers
rts
@noModem move.w #immgNoBoardErr,d0 ; indicate no board
bra.s @exit
;----------------------------------------------------------------------------------------
; mdmSetUp3615
;
; Entry: none
;
; Exit: d0 = result code
; a0 = pointer to modem primitives globals
;
; Trashes: none
;
; Set up the primitives for the 3615 modem.
;----------------------------------------------------------------------------------------
mdmSetUp3615
@savedRegs REG a2-a3
movem.l @savedRegs,-(sp) ; save registers
WITH immg3615Rec, immg3615SlotQEl
; allocate private storage for modem primitives
move.l #immg3615Rec.size,d0 ; get size of private storage
_NewPtr ,SYS,CLEAR ; get a pointer in the system heap ; <H10>
tst.w d0 ; check for errors
bne.w @error1 ; if no memory, cannot continue
movea.l a0,a3 ; a3 = pointer to private storage
movea.l #OptMdmBase,a2 ; get base address of modem
move.l a2,ModemBase(a3) ; store base address
lea cmdCounts,a0 ; A0 -> pointer to command send count table for mdmPrime
move.l a0,SendTable(a3) ; store command send count table
lea replyCounts,a0 ; A0 -> pointer to receive count table
move.l a0,RecvTable(a3) ; store receive count table
moveq #0,d0 ; clear D0
move.b OptMdmIDReg(a2),d0 ; get modem ID
move.l d0,ModemType(a3) ; store the modem type in private storage
move.b #(1<<bOptIntMask),OptSndCtl(a2) ; set modem interrupt bit, clear speaker sound bit ; <H10>
; The bit in the via2 is set for a slot 9 interrupt whenever the modem interrupts, even
; though the modem interrupt comes in at level 3. If the process of a level 2 interrupt
; has already begun, the slot 9 interrupt will attempt to be handled, which causes a
; problem. Is there anything that we should be aware of that might bite us in the butt
; later since we are installing a minimal slot 9 interrupt handler? How can the slot 9
; handler actually tell that the modem caused the interrupt and the interrupt is not
; actually a spurious one?
move.l #immg3615SlotQEl.size,d0 ; size of 3615 slot queue element (modified)
_NewPtr ,SYS ; get a slot queue element in the system heap
tst.w d0 ; check for valid pointer
bne.s @error2 ; cannot go on like this...
move.l a0,Slot9Addr(a3) ; save pointer to slot 9 handler
move.w #0,sqPrio(a0) ; set priority to 0, the lowest priority
move.w #sIQType,sqType(a0) ; set type to slot interrupt type
lea sqInt9Count(a0),a2 ; get pointer to count buffer
move.l #0,(a2) ; initialize counter
move.l a2,sqParm(a0) ; set up optional a1 pointer to the counter space
lea Slot9Handler,a2 ; get the address of the slot interrupt handler
move.l a2,sqAddr(a0) ; put pointer into slot queue element routine address
move.l #OptHackSlotNum,d0 ; install the handler in slot 9
_SIntInstall
tst.w d0 ; any error?
bne.s @error3 ; we just can't go on...
move.b BIOS_Config2+BIOSAddr,d0 ; get BIOS config 2 reg contents
ori.b #(1<<bBIOSENETIRQLVL) | \ ; set irq level to level 3
(1<<bBIOSIOCSTime),d0 ; set IO_CS_Time to be asserted on writes
move.b d0,BIOS_Config2+BIOSAddr ; put byte into configuration reg
move.l ModemBase(a3),a2 ; A2 -> modem base address ; <H10>
move.b #(1<<bOptIntMask),OptSndCtl(a2) ; set modem interrupt bit, clear speaker sound bit ; <H10>
movea.l a3,a0 ; return pointer to primitives globals
moveq #noErr,d0 ; no error
@exit movem.l (sp)+,@savedRegs ; restore registers
rts
; have allocated a slot queue element (in globals)
@error3 movea.l Slot9Addr(a3),a0 ; A0 -> pointer to slot queue element
_DisposPtr ; take out the trash
; have allocated pointer to globals in A3
@error2 movea.l a3,a0 ; gotta have it
_DisposPtr ; gotta get rid of it
; indicate initialization error
@error1 move.w #immgInitError,d0 ; bad...
bra.s @exit
ENDWITH
;_______________________________________________________________________
;
; Routine: Slot 9 handler
;
; Inputs: a1 = pointer to a count buffer
;
; Outputs: d0 = 0 if interrupt not handled, 1 if interrupt handled
;
; Trashes: a0
;
; Function: a simple slot 9 interrupt handler. The reason this routine
; is necessary is twofold:
; 1. There is a bug in PrimeTime that sets the VIA2 port A data register
; bit that signifies a slot 9 interrupt has occurred when the modem
; signals an interrupt on level 3.
; 2. There is a bug in the interrupt handler for the Elan card that leaves
; the interrupt mask bits in the processor status register (SR) set
; to mask level 4 or 6, which, in combination with above bug, causes
; the slot manager slot interrupt code to go into an infinite loop under
; the following circumstance:
; The processor is handling a level 2 interrupt for the Elan card,
; and while it is in the Elan interrupt service routine, a modem
; interrupt comes in, setting the VIA2 port A data register bit.
; The Elan interrupt routine exits, but leaves the SR mask set to
; 4 (or 6). The slot manager, wanting to make sure that all slot
; interrupts have been handled, reads the VIA2 port A data register,
; which indicates that a slot 9 interrupt is requested. The slot 9
; interrupt handler is entered. If the handler for slot 9 does not
; lower the SR mask to 2 (to allow the real level 3 interrupt to occur
; and be serviced), an infinite loop results.
;_______________________________________________________________________
ALIGN 2
Slot9Handler
add.l #1,(a1) ; bump counter of how many times in this routine
move sr,d0 ; get the status register to check for interrupt mask
andi.w #statMaskBits,d0 ; mask off interrupt mask information
cmpi.w #level3Bits,d0 ; check for int mask > 2
blo.s @noProblem ; no problem with interrupt mask
move sr,d0 ; save the status register
swap d0 ; put saved SR in high word
move sr,d0 ; get SR to lower mask
andi.w #~statMaskBits,d0 ; clear all status mask bits
ori.w #level2Bits,d0 ; make interrupt mask level 2
move d0,sr ; lower priority mask temporarily
swap d0 ; get the saved sr -- the level 3 interrupt should occur here!
move d0,sr ; restore interrupt mask level
@noProblem move.l VIA2RBVOSS,a0 ; get via2 base address
move.b vBufA(a0),d0 ; read via 2 data register
andi.b #(1<<v2IRQ1),d0 ; mask off slot 9 interrupt bit (will be zero if interrupt not handled, 1 if handled)
rts
ALIGN 2
;----------------------------------------------------------------------------------------
; mdmTearDown3615
;
; Entry: a1 = pointer to modem primitives globals
;
; Exit: d0 = no error
;
; Trashes: none
;
; Deallocates everything that the set up routine allocated and sets PrimeTime so that
; no modem interrupts are generated.
;----------------------------------------------------------------------------------------
mdmTearDown3615
WITH immg3615Rec
; make sure BIOS does not generate level three interrupts
move.b BIOS_Config2+BIOSAddr,d0 ; get BIOS config 2 reg contents
bclr.l #bBIOSENETIRQLVL,d0 ; set irq level to level 2
bclr.l #bBIOSIOCSTime,d0 ; set IO_CS_Time to not be asserted on writes
move.b d0,BIOS_Config2+BIOSAddr ; put byte into configuration reg
; remove slot 9 interrupt handler
move.l Slot9Addr(a1),a0 ; A0 -> pointer to slot queue element
move.l #OptHackSlotNum,d0 ; remove the handler from slot 9
_SIntRemove ; get rid of it if it is there
; deallocate slot queue element
move.l Slot9Addr(a1),a0 ; A0 -> pointer to slot queue element
_DisposPtr
; deallocate private storage for modem primitives
movea.l a1,a0 ; A0 -> pointer to private storage
_DisposPtr
moveq #noErr,d0 ; no error
@exit rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmName3615
;
; Entry: a0 = pointer to string buffer
; a1 = pointer to modem primitives globals
;
; Exit: d0 = no error
; a0 = pointer to null terminated string
;
; Trashes: A1
;
; Returns the name of the modem.
;----------------------------------------------------------------------------------------
mdmName3615
move.l a0,-(sp) ; save pointer
movea.l a0,a1 ; destination
lea @modemName,a0 ; source (skip over length byte)
@loop move.b (a0)+,(a1)+ ; copy one byte
bne.s @loop ; keep copying until null terminal byte is copied
movea.l (sp)+,a0 ; return modem name in A0
moveq #noErr,d0 ; no error
@exit rts
@modemName dc.b 'Express Modem'
dc.b 0
ALIGN 2
;----------------------------------------------------------------------------------------
; mdmSndVol3615
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Returns immgInvalidSelector error, since the express modem on Optimus does not currently
; support relative sound volume control. This routine returns an error so that when software
; becomes available that can take advantage of sound volume, it can check the return
; result from this function to determine if the desired behavior has taken place.
;----------------------------------------------------------------------------------------
mdmSndVol3615
WITH immg3615Rec
move.w #immgInvalidSelector,d0
rts
ENDWITH
;----------------------------------------------------------------------------------------
; mdmSndHW3615
;
; Entry: a0 = modem manager param block pointer
; a1 = modem primitives global pointer
;
; Exit: d0 = result code
;
; Trashes: none
;
; Returns immgInvalidSelector error, since the express modem on Optimus does not currently
; support system sound hardware arbitration. This routine returns an error so that if software
; becomes available that arbitrates for the hardware, it can check the return
; result from this function to determine if the desired behavior has taken place.
;----------------------------------------------------------------------------------------
mdmSndHW3615
WITH immg3615Rec
move.w #immgInvalidSelector,d0
rts
ENDWITH
;----------------------------------------------------------------------------------------
; GetLenCount
;
; Entry: d3.w = command for count
; a1 = pointer to count table
;
; Exit: d1.b = count
; condition codes set on d1.b
;
; Trashes: a1
;
; Looks up count for command in d3 in table.
;----------------------------------------------------------------------------------------
GetLenCount
@saveRegs REG d3/a0 ; registers to save
movem.l @saveRegs,-(sp) ; save registers
clr.w d1 ; clear out the low word
lea @RangeTable,a0 ; A0 -> pointer to range table
@checkLoop move.b (a0)+,d1 ; D1 -> normalizing byte
beq.s @noCount ; no more ranges, no count...
sub.b d1,d3 ; normalize d3 to range
bmi.s @noCount ; if less than, no count
move.b (a0)+,d1 ; D1 -> range size
cmp.b d1,d3 ; check if d3 is in range
blt.s @lookup ; yep, go look it up
adda.w d1,a1 ; increment table pointer to next range
bra.s @checkLoop ; nope, continue checking...
@lookup move.b (a1,d3.w),d1 ; D1 -> count
@exit movem.l (sp)+,@saveRegs ; restore registers
rts
@noCount move.b #$ff,d1 ; D1 -> no count
bra.s @exit
@RangeTable
dc.b $50,$10 ; cmd range $50-$5f
dc.b $20,$10 ; cmd range $70-$7f
dc.b $30,$10 ; cmd range $a0-$af
dc.w 0
;_______________________________________________________________________________________
;
; This table is used to determine if the 680x0 needs to send a count byte to the modem.
; A positive value means that the count is known by both sides, and so it is not sent.
; A negative value means that the command can send variable amounts of data, so the
; count byte specified in the modem manager param block needs to be sent.
;
; Unused commands will be marked as expecting the count to be sent so that commands
; may be added without having to change the ROM.
cmdCounts DC.B 1 ; [$50] Set Internal Modem Control Bits
DC.B 0 ; [$51] Clear FIFOs
DC.B 2 ; [$52] Set FIFO Interrupt Marks
DC.B 2 ; [$53] Set FIFO Sizes
DC.B -1 ; [$54] Write Data to Modem
DC.B 1 ; [$55] Set Data Mode
DC.B 3 ; [$56] Set Flow Control Mode <H55>
DC.B 1 ; [$57] Set DAA control lines <H54>
DC.B 0 ; [$58] Read Internal Modem Status
DC.B 1 ; [$59] Get DAA Identification <H55>
DC.B 0 ; [$5A] Get FIFO Counts
DC.B 0 ; [$5B] Get Maximum FIFO Sizes
DC.B 0 ; [$5C] Read Data From Modem
DC.B -1 ; [$5D] General Purpose modem command (modem dependent) <H58>
DC.B -1 ; [$5E] -
DC.B -1 ; [$5F] -
DC.B 1 ; [$70] Set One-Second Interrupt
DC.B 1 ; [$71] Modem Interrupt Control
DC.B 1 ; [$72] Set Modem Interrupt
DC.B -1 ; [$73] -
DC.B -1 ; [$74] -
DC.B -1 ; [$75] -
DC.B -1 ; [$76] -
DC.B -1 ; [$77] -
DC.B 0 ; [$78] Read Interrupt Flag Register.
DC.B 0 ; [$79] Read Modem Interrupt Data
DC.B -1 ; [$7A] -
DC.B -1 ; [$7B] -
DC.B -1 ; [$7C] -
DC.B -1 ; [$7D] -
DC.B 4 ; [$7E] Enter Shutdown Mode
DC.B 4 ; [$7F] Enter Sleep Mode
DC.B 2 ; [$A0] Write Modem Register
DC.B 2 ; [$A1] Clear Modem Register Bits
DC.B 2 ; [$A2] Set Modem Register Bits
DC.B 4 ; [$A3] Write DSP RAM
DC.B -1 ; [$A4] Set Filter Coefficients
DC.B 0 ; [$A5] Reset Modem
DC.B -1 ; [$A6] -
DC.B -1 ; [$A7] -
DC.B 1 ; [$A8] Read Modem Register
DC.B 1 ; [$A9] Send Break
DC.B 3 ; [$AA] Dial Digit
DC.B 2 ; [$AB] Read DSP RAM
DC.B -1 ; [$AC] -
DC.B -1 ; [$AD] -
DC.B -1 ; [$AE] -
DC.B -1 ; [$AF] -
; This table is used to determine how the 680x0 needs to handle the reply:
;
; =0: no reply should be expected.
; =1: only a reply byte will be sent (this is a special case for a couple of commands)
; <0: a reply is expected and the modem will send a count byte.
; >1: a reply is expected and the modem will not send a count byte,
; but the count to return in the modem manager param block will be (value-1).
;
; Unused commands in the range $x8 to $xF will be marked as expecting a reply (with count)
; so that commands may be added without having to change the ROM.
replyCounts DC.B 0 ; [$50] Set Internal Modem Control Bits
DC.B 0 ; [$51] Clear FIFOs
DC.B 0 ; [$52] Set FIFO Interrupt Marks
DC.B 0 ; [$53] Set FIFO Sizes
DC.B 0 ; [$54] Write Data to Modem
DC.B 0 ; [$55] Set Data Mode
DC.B 0 ; [$56] Set Flow Control Mode
DC.B 0 ; [$57] Set DAA control lines <H54>
DC.B 1+1 ; [$58] Read Internal Modem Status
DC.B 0 ; [$59] Get DAA Identification <H55>
DC.B 2+1 ; [$5A] Get FIFO Counts
DC.B 2+1 ; [$5B] Get Maximum FIFO Sizes
DC.B -1 ; [$5C] Read Data From Modem
DC.B -1 ; [$5D] General Purpose modem command (modem dependent) <H58>
DC.B -1 ; [$5E] -
DC.B -1 ; [$5F] -
DC.B 0 ; [$70] Set One-Second Interrupt
DC.B 0 ; [$71] Modem Interrupt Control
DC.B 0 ; [$72] Set Modem Interrupt
DC.B 0 ; [$73] -
DC.B 0 ; [$74] -
DC.B 0 ; [$75] -
DC.B 0 ; [$76] -
DC.B 0 ; [$77] -
DC.B -1 ; [$78] Read Interrupt Flag Register.
DC.B -1 ; [$79] Read Modem Interrupt Data
DC.B -1 ; [$7A] -
DC.B -1 ; [$7B] -
DC.B -1 ; [$7C] -
DC.B -1 ; [$7D] -
DC.B 0+1 ; [$7E] Enter Shutdown Mode
DC.B 0+1 ; [$7F] Enter Sleep Mode
DC.B 0 ; [$A0] Write Modem Register
DC.B 0 ; [$A1] Clear Modem Register Bits
DC.B 0 ; [$A2] Set Modem Register Bits
DC.B 0 ; [$A3] Write DSP RAM
DC.B 0 ; [$A4] Set Filter Coefficients
DC.B 0 ; [$A5] Reset Modem
DC.B 0 ; [$A6] -
DC.B 0 ; [$A7] -
DC.B 1+1 ; [$A8] Read Modem Register
DC.B 0 ; [$A9] Send Break
DC.B 0 ; [$AA] Dial Digit
DC.B 0 ; [$AB] Read DSP RAM
DC.B -1 ; [$AC] -
DC.B -1 ; [$AD] -
DC.B -1 ; [$AE] -
DC.B -1 ; [$AF] -
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
; END OPTIMUS MODEM PRIMITIVES
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
;¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
End