;__________________________________________________________________________________________________ ; ; 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): ; ; 1/26/94 rab Removed padForOverpatch stuff from the end of this file ; (SuperMario does not use itΙ). ; 12/21/93 ged Changed rate11_025khz label to newer rate11025hz definition. ; 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. ; 5/23/93 SES Made changes from code review on 5/19/93. ; 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). ; 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. ; 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! ; 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). ; 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. ;
5/6/93 SES Altered sound control so that the play through volume is set to ; equal the slider volume during modem sound transactions. ;
5/4/93 SES Removed machine primitives and replaced them with the dispatch ; functions in the IntModemMgr.a file. ;

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. ;

4/24/93 SES Played around with how sound is handled on Optimus for the ; modem. ;

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 ;

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 ;

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 ;

PlayThruVol DS.B 1 ; saved sound playthrough volume ;

PreviousCmd DS.W 1 ; the last command executed through the call; 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 ; 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 ; 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 ; 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< 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 addq.w #aData,a6 ; tst.l PollProc ; is a poll proc installed? bne.s @WillPoll ; -> yes ; 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 @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 movea.l immg3615Rec.SendTable(a5),a1 ; and get the send count table 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 ; ----- 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 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 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 ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; 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 ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 jmp (a3) ; return through a3 <37> ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; 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 ; ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 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 bne.s @error ; if bit low, error ;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 ; (6) wait for data accepted bne.s @error ; if bit low, error bset.b #OptSPIReq,OptSPIMdmCtl(a2) ; (7) clear data valid move.w #noErr,d0 ; report no error jmp (a4) @error ; bset.b #OptSPIReq,OptSPIMdmCtl(a2) ; (7) clear data valid <28> move.w #immgSendStartErr,d0 ; report send error 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 bne.s @error ; if bit low, error ;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 ; (5) <-- modem shifting bne.s @error ; if bit low, error bset.b #OptSPIReq,OptSPIMdmCtl(a2) ; (6) acknowledge ack bsr.s WaitSPIAckHi ; (7) wait (DAV) bne.s @error ; if bit low, error 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 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 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 @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 ; cmp.w PreviousCmd(a1),d1 ; make sure that we are doing the opposite of what has been done ; beq.s @done ; if the same, that means sound on request ; ; with sound on already, or sound off request; ; with sound off already : move.w d1,PreviousCmd(a1) ; save the command being executed ; 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 ;

beq.s @saveIt ; if sound on, save the current state ;

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 ;

@saveIt movea.l ([ExpandMem],emSndPrimitives),a0 ;

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? ;

beq.s @done ; no, just exit ;

move.l sndInputSource*4(a0),a0 ;

jsr (a0) ; call the ioprimitive ;

; 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 ;

bne.s @okToSave ; if not garbage, save it ;

moveq #0,d0 ; assume no input ;

@okToSave move.b d0,SndInputSrc(a1) ; save previous input source ;

moveq #sndAuxiliary,d0 ; set modem sound on ;

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 ;

tst.l sndPlayThruVol*4(a0) ; is there a handler? ;

beq.s @done ; no, just exit ;

move.l sndPlayThruVol*4(a0),a0 jsr (a0) ; call the ioprimitive @done rts SetInputSrc movea.l ([ExpandMem],emSndPrimitives),a0 ;

tst.l sndInputSelect*4(a0) ; is there a handler? ;

beq.s @done ; no, just exit ;

move.l sndInputSelect*4(a0),a0 ;

jsr (a0) ; call the ioprimitive ;

@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 ; 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 ; 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< ; 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< modem base address ; move.b #(1< 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< 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 DC.B 1 ; [$57] Set DAA control lines DC.B 0 ; [$58] Read Internal Modem Status DC.B 1 ; [$59] Get DAA Identification 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) 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 DC.B 1+1 ; [$58] Read Internal Modem Status DC.B 0 ; [$59] Get DAA Identification 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) 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