mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2025-01-03 09:31:04 +00:00
4325cdcc78
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.
2287 lines
58 KiB
Plaintext
2287 lines
58 KiB
Plaintext
;
|
|
; File: SCCIOP.aii
|
|
;
|
|
; Contains: xxx put contents here (or delete the whole line) xxx
|
|
;
|
|
; Written by: xxx put name of writer here (or delete the whole line) xxx
|
|
;
|
|
; Copyright: © 1988-1990 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <3> 1/18/90 SWC WTO/Fix bugs in InstallTask and KillTask.
|
|
; <1.4> 12/8/89 WTO Fixed bug in version message to kernel. Range check on Driver ID
|
|
; was returning error. Needed to add compare to $00.
|
|
; <1.3> 7/8/89 CCH Added EASE comments to file.
|
|
; <1.2> 6/27/89 BTO Needed for AURORA padding
|
|
; <¥1.1> 2/8/89 SGS Initial release
|
|
; <1.0> 11/9/88 CCH Adding to EASE.
|
|
;
|
|
; To Do:
|
|
;
|
|
|
|
|
|
*
|
|
* File SCCIop.aii
|
|
*
|
|
* Copyright 1988 (c) Apple Computer Inc.
|
|
*
|
|
* Main routines to handle IOP. The Kernel is downloaded into the IOP
|
|
* during the IOPMgr initialization process. The IOPMgr then reads
|
|
* PRAM to set the ByPass mode to the appropriate state.
|
|
*
|
|
* Written by Bill O'Connor 2/19/88
|
|
* with ideas and code from Ron Hocksprung
|
|
* Gary Davidian
|
|
*
|
|
* Edit History
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Machine M65CX02
|
|
Longa off
|
|
Longi off
|
|
blanks on
|
|
print nopage, nomdir
|
|
|
|
include 'SCCIOPSysEqu.aii' ; IOP Sytem includes
|
|
include 'SCCIOPEqu.aii' ; IOP Kernel includes
|
|
|
|
Seg 'IOPOS'
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Vectored interrupt table
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Seg 'Vectors'
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Interrupt vector address table.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Vect Proc
|
|
|
|
Import NonMaskable, \
|
|
Reset, \
|
|
IRQ :Code
|
|
|
|
DC.W NonMaskable
|
|
DC.W Reset
|
|
DC.W IRQ
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Main entry into PIC kernel
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Seg 'Main'
|
|
|
|
Export NonMaskable, \
|
|
Reset, \
|
|
IRQ
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* NonMaskable interrupt handler.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
NonMaskable Proc
|
|
|
|
Lda #NonMask ; Let us know we are in NonMaskable Loop
|
|
Bra NonMaskable ; Should never get one
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* IRQ - save registers and jsr to ISR dispatcher.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
IRQ Proc
|
|
|
|
Import ISR_Dsptch : Code
|
|
|
|
Pha ; Save registers.
|
|
Phy
|
|
Phx
|
|
|
|
Jsr ISR_Dsptch ; Interrupt handler
|
|
|
|
Plx ; Restore registers
|
|
Ply
|
|
Pla
|
|
Rti
|
|
|
|
endproc
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Reset handler.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Reset Proc
|
|
|
|
Import Cmd_Table, \
|
|
VerInfo : Data
|
|
Import Kern_Task, \
|
|
Cmd_JmpTable, \
|
|
RxMsg_Signal, \
|
|
SCC_Dsptch, \
|
|
Timer_ISR, \
|
|
Init_Timer, \
|
|
Msg_Scan : Code
|
|
|
|
Sei ; Interrupts off
|
|
|
|
|
|
Stz Int_Host
|
|
Lda #SCC_Port|ByPass_Bit|PClk39
|
|
Sta SCC_Cntl ; Initialize SCC control reg
|
|
Lda #IO_Cntl_init ; Get initial value
|
|
Sta IO_Cntl ; Initialize I/O control reg
|
|
Lda #Host_Msk ; Enable host interrupts
|
|
Sta Int_Mask
|
|
Lda SCC_ACntl ; Sync SCC for writes
|
|
|
|
|
|
*
|
|
* Clear page zero
|
|
*
|
|
|
|
Ldx #$00 ; Index
|
|
@ClearLoop
|
|
Stz $00,x ; Zero out a byte
|
|
Dex ; Point to next byte
|
|
Bne @ClearLoop ; Clear the entire zero page
|
|
|
|
Stz DMA1_Cntl ; Init DMA1
|
|
Stz DMA2_Cntl ; Init DMA2
|
|
Stz TD_Cntl ; Init Timer DPLL control
|
|
Stz Int_Host ; Init host interrupt register
|
|
|
|
*
|
|
* Transfer the kernel command addresses to the zero page table
|
|
*
|
|
|
|
Ldx #KCmd_Table_size ; Begin transfer of command table
|
|
|
|
@Loop Lda Cmd_Table,x
|
|
Sta Krn_CmdBase,x
|
|
Dex
|
|
Bpl @Loop
|
|
|
|
|
|
*
|
|
* Initialize Zero Page variables
|
|
*
|
|
*
|
|
* IMPORTANT !!!!!
|
|
*
|
|
* Because of the manor in which the 6502 does a rts the address
|
|
* used must be the actual address minus 1
|
|
*
|
|
|
|
DvrA_Addr Set DvrA_Base-1
|
|
DvrB_Addr Set DvrB_Base-1
|
|
|
|
Lda #DvrA_ID ; Initialize the driver task ID table
|
|
Sta Dvr_TaskID
|
|
Lda #DvrB_ID
|
|
Sta Dvr_TaskID+1
|
|
|
|
Lda #>DvrA_Addr ; Install the Initialization vector for driver A
|
|
Sta Init_Vec+1
|
|
Lda #<DvrA_Addr
|
|
Sta Init_Vec
|
|
|
|
Lda #>DvrB_Addr ; Install the Initialization vector for driver B
|
|
Sta Init_Vec+3
|
|
Lda #<DvrB_Addr
|
|
Sta Init_Vec+2
|
|
|
|
Lda #>DvrA_Close ; Install the Close vector for driver A
|
|
Sta Close_Vec+1
|
|
Lda #<DvrA_Close
|
|
Sta Close_Vec
|
|
|
|
Lda #>DvrB_Close ; Install the Close vector for driver B
|
|
Sta Close_Vec+3
|
|
Lda #<DvrB_Close
|
|
Sta Close_Vec+2
|
|
|
|
Lda #$ff
|
|
Sta Bypass_Flag ; Set ByPass_Flag to on.
|
|
|
|
Jsr Init_Timer
|
|
|
|
_Reg_Ver Kern, VerInfo
|
|
_Inst_ISR HOST, Msg_Scan ; Install message scan for host interrupt
|
|
_Inst_ISR SCC, SCC_Dsptch ; Install the SCC interrupt dispatcher
|
|
_Inst_ISR TIMER, Timer_ISR
|
|
|
|
_Inst_RxMsgSgn 1, RxMsg_Signal ; Install the kernel receive message signaller
|
|
|
|
Lda #MaxRxMsg
|
|
Sta RxMsgCnt ; PIC Kernel uses 7 messages
|
|
Sta TxMsgCnt ; Both in and out
|
|
|
|
Ldx #$ff ; Stack base is $01FF
|
|
Stx Int_Reg ; Clear the interrupt register
|
|
Stx IOPAlive ; Tell IOPMgr we are Alive
|
|
Txs ; Initialize stack
|
|
|
|
Jmp Kern_Task ; Interrupts will be cleared when
|
|
; Event Wait is called.
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* ISR_Dsptch - determine interrupt source and dispatch to ISR.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
ISR_Dsptch Proc
|
|
|
|
Import ISR_table, \
|
|
ISR_Decode :Data
|
|
|
|
Ldx Int_reg ; Get the interrupt latch
|
|
Lda ISR_Decode,x
|
|
Tax
|
|
Jmp (ISR_table,x) ; Dispatch the interrupt
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* SCC_Dsptch - determine source and dispatch SCC interrupts.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
SCC_Dsptch Proc
|
|
|
|
Import SCC_table, \
|
|
UnKnown_Int :Code
|
|
|
|
Lda #Rd_Reg2 ; Read the SCC interrupt latch
|
|
Sta SCC_BCntl ; Interrupt latch is register 2 port B
|
|
Ldx SCC_BStat ; Read the status V1 V2 V3
|
|
Jmp (SCC_table,x) ; Dispatch the interrupt
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Unknown_SCCInt - unknown SCC interrupt handler.
|
|
*
|
|
* This routine handles interrupts that occur but do
|
|
* not have an SCCISR installed.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
UnKnown_SCCInt Proc
|
|
|
|
Brk
|
|
|
|
endproc
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Unknown_Int - unknown interrupt handler.
|
|
*
|
|
* This routine handles interrupts that occur but do
|
|
* not have an ISR installed.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Unknown_Int Proc
|
|
|
|
Tsx ; Get the stack pointer
|
|
Lda StackPage+6,x ; Get the psw from the stack
|
|
Bit #pswB ; Was it a break that caused the interrupt
|
|
Bne BRK_Handler ; Yes
|
|
Rts ; No, just return
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* BRK_Handler - handle a software interrupt
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BRK_Handler Tsx ; Get the stack pointer
|
|
|
|
*
|
|
* Cycle through stack so that we can see it on the logic analyzer
|
|
*
|
|
|
|
Do_Again Inx
|
|
Beq BRK_Handler
|
|
Lda StackPage,x
|
|
Bra Do_Again
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* Entry Points
|
|
*
|
|
* Rel_Task - release a task for one task cycle.
|
|
*
|
|
* Ev_Wait - wait for an event to happen.
|
|
*
|
|
* The return address should already be on the stack.
|
|
* Ev_Wait falls through into Ev_Dsptch.
|
|
*
|
|
* Register A - event mask.
|
|
*
|
|
* On exit register A contains the tEvent for the Task.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Rel_Task Proc
|
|
|
|
Export Ev_Wait
|
|
|
|
Import tSp, \
|
|
tNext, \
|
|
tEvent, \
|
|
tWait : Data
|
|
|
|
Lda #$80 ; This event is always set
|
|
; therefore the task will execute the
|
|
; next task cylce
|
|
|
|
Ev_Wait Ldy Cur_Task ; Get the Task ID
|
|
Sta tWait,y ; Store the event mask
|
|
|
|
*
|
|
* Ev_Dsptch - entry to task dispatcher.
|
|
*
|
|
|
|
Sei
|
|
Ldy Cur_Task ; Get the current task ID
|
|
Tsx
|
|
Txa
|
|
Sta tSp,y ; Save the task's stack pointer
|
|
Cli
|
|
|
|
Ldx #$FF
|
|
|
|
@Scan Lda tNext,y ; Get the next task ID
|
|
Tay
|
|
Lda tEvent,y ; Check the flags to see if this one is ready
|
|
And tWait,y
|
|
Stx IOPAlive ; This lets the Host know we're still alive
|
|
Beq @Scan
|
|
|
|
Sei
|
|
Sty Cur_Task ; Switch Tasks
|
|
Ldx tSp,y ; Get this task's stack pointer
|
|
Txs
|
|
Cli
|
|
|
|
Rts ; go to the task
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Ev_Signal - signal a task that an event has occurred.
|
|
*
|
|
* Events are flagged by setting bits in the tEvent byte of the
|
|
* TCB of the task to receive the signal.
|
|
*
|
|
* Register X - ID of task to signal.
|
|
* Register A - event flag.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Ev_Signal Proc
|
|
|
|
Import tEvent : Data
|
|
|
|
Php
|
|
Sei
|
|
Ora tEvent,x ; Set the event bit
|
|
Sta tEvent,x ; Save it in the task's TCB
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Ev_Reset - reset an event flag.
|
|
*
|
|
* Register A - complement of event flag.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Ev_Reset Proc
|
|
|
|
Import tEvent : Data
|
|
|
|
Php
|
|
Sei
|
|
Ldx Cur_task ; We can only reset our own task
|
|
And tEvent,x ; Reset the event bit
|
|
Sta tEvent,x ; Save it in the task's TCB
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Msg_Scan - handle interrupts from host CPU. Must be installed
|
|
* when processor is reset.
|
|
*
|
|
* Scan messages for New Message Sent and Message Complete. Each
|
|
* task installs message handlers that signal the task when new messages
|
|
* are received or messages are completed. Each task must install a
|
|
* handler for each message box it intends to use.
|
|
*
|
|
* The only message the kernel will accept when in ByPassMode
|
|
* is ByPassMode(Off).
|
|
*
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Msg_Scan Proc
|
|
|
|
Import TxMsgSignal, \
|
|
RxMsgSignal : Code
|
|
|
|
Import MsgNumTimes2 : Data
|
|
|
|
Lda #Host_Msk
|
|
Sta Int_Reg ; Clear the host interrupt
|
|
Trb Int_Mask ; Disable host interrupts
|
|
Cli ; Renable interrupts of higher priority
|
|
|
|
Lda ByPass_Flag ; We don't do anything in bypass mode
|
|
Bne Do_ByPass ; except receive on message one
|
|
|
|
*
|
|
* Service Completed Transmit Messages first
|
|
*
|
|
|
|
Ldy TxMsgCnt ; Get highest Tx message number
|
|
Lda #MsgCmplt ; Get the desired message state
|
|
|
|
@TxMsgLoop Cpa TxMsgState,y ; See if the message completed
|
|
Bne @TxMsgNext ; If no service needed goto next msg
|
|
|
|
Phy ; Preserve loop counter
|
|
Ldx MsgNumTimes2,y ; Convert message number to table index
|
|
Jsr TxHandler ; Call the handler
|
|
Ply ; Restore the message number
|
|
|
|
Lda #Idle
|
|
Sta TxMsgState,y ; Change message state to Idle
|
|
Lda #MsgCmplt ; Restore desired message state
|
|
|
|
@TxMsgNext Dey ; Point to previous message
|
|
Bne @TxMsgLoop ; Loop through all messages
|
|
|
|
Bra RxMsgStart
|
|
|
|
*
|
|
* Service Receive New Message Sent last
|
|
*
|
|
|
|
Do_ByPass Ldy #1 ; In ByPass mode we only receive message 1
|
|
Bra ByPassSkip ; for the kernel
|
|
|
|
RxMsgStart Ldy RxMsgCnt ; Get highest Rx message number
|
|
|
|
ByPassSkip Lda #NewMsgSent ; Get the desired message state
|
|
|
|
@RxMsgLoop Cpa RxMsgState,y ; See if message just arrived
|
|
Bne @RxMsgNext ; If no service needed
|
|
|
|
Lda #MsgRcv
|
|
Sta RxMsgState,y ; Change message state to Message Received
|
|
Phy ; Preserve loop counter
|
|
Ldx MsgNumTimes2,y ; Convert message number to table index
|
|
Jsr RxHandler ; Call the handler
|
|
Ply ; Restore the message number
|
|
Lda #NewMsgSent ; Restore desired message state
|
|
|
|
@RxMsgNext Dey ; Point to previous message
|
|
Bne @RxMsgLoop ; Loop through all messages
|
|
|
|
Sei
|
|
Lda #Host_Msk ; Get the host interrupt bit
|
|
Tsb Int_Mask ; Re-enable host interrupts
|
|
Rts ; All done
|
|
|
|
TxHandler Jmp (TxMsgSignal-2,x) ; Go to handler - msg's start at 1
|
|
|
|
RxHandler Jmp (RxMsgSignal-2,x) ; Go to handler - msg's start at 1
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* RxMsg_Signal - signal kernel that a message has arrived.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
RxMsg_Signal Proc
|
|
|
|
Import Cmd_JmpTable : Code
|
|
|
|
Ldx #Kern_ID ; ID of task we wish to signal
|
|
Lda #RxMsg ; Signal we want to send
|
|
Jmp Cmd_JmpTable+(SignalTask*3) ; Signal Task Command has an RTS
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Kern_Task - main kernel task routine and event dispatcher
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Kern_Task Proc
|
|
|
|
Import Dsptch_KRxMsg : Code
|
|
Import Cmd_JmpTable : Code
|
|
|
|
_Wait_Event RxMsg ; Wait until we get a receive message event
|
|
|
|
Asl A ; tEvent is already in register A
|
|
Tax
|
|
Jmp (Kern_Disp-2,x)
|
|
|
|
Kern_Disp DC.W Dsptch_KRxMsg ; Dispatch Kernel message is the only
|
|
; event we look for.
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Dsptch_KRxMsg - Decode and dispatch messages to Kernel
|
|
*
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Dsptch_KRxMsg Proc
|
|
|
|
Import MsgNumTimes32 : Data
|
|
|
|
Import Kern_Task, \
|
|
Alloc_Driver, \
|
|
DeAlloc_Driver, \
|
|
ByPass_Mode, \
|
|
Init_Driver, \
|
|
Version, \
|
|
SCC_Control, \
|
|
Cmd_JmpTable : Code
|
|
|
|
_Reset_Event RxMsg ; Reset the event
|
|
|
|
Ldy #$01 ; Kernel has only one message (msg1)
|
|
Ldx MsgNumTimes32,y ; Get the Receive message page offset
|
|
|
|
Lda ByPass_Flag ; Check for ByPass mode
|
|
Bne Do_ByPass
|
|
|
|
Procede Lda RxMsgPage,x ; Get the command byte
|
|
Beq UnKnown_Msg ; Zero is not a command
|
|
|
|
Cmp #Max_KMsg+1 ; If > the max msg #
|
|
Bge Unknown_Msg ; branch to unknown message routine
|
|
|
|
Stz RxMsgPage,x ; Clear the message byte
|
|
|
|
Asl A ; Make it a word offset
|
|
Phx
|
|
Tax
|
|
Jmp (Msg_Table-2,x) ; Dispatch the command
|
|
|
|
Do_ByPass Lda RxMsgPage,x
|
|
Cmp #ByPass ; Is it a ByPass Call
|
|
Beq Procede ; Yes, procede
|
|
|
|
Cmp #SCCCntl ; Allow the SCC Control register call.
|
|
Beq Procede ; Yes, procede with message
|
|
|
|
Stz RxMsgPage+2,x
|
|
Lda ByPass_Flag
|
|
Sta RxMsgPage+1,x ; Return ByPass client ID
|
|
Lda #InByPass
|
|
Bra Complete
|
|
|
|
UnKnown_Msg Stz RxMsgPage+2,x
|
|
Stz RxMsgPage+1,x
|
|
Lda #UnKnwnMsg ;
|
|
|
|
Complete Sta RxMsgPage,x ; Return the error
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
Msg_Table DC.W Alloc_Driver
|
|
DC.W DeAlloc_Driver
|
|
DC.W Init_Driver
|
|
DC.W ByPass_Mode
|
|
DC.W Version
|
|
DC.W SCC_Control
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Alloc_Driver
|
|
*
|
|
* Passed
|
|
*
|
|
* struct AllocMsgType {
|
|
* byte msgNum; /* Alloc_Channel msg = $01 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* byte ClientID; /* Port use ID > $00 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct AllocErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* DvrInUse = -4
|
|
* InBypass = -5
|
|
* */
|
|
* byte ClientID; /* ID > 0 if port in use */
|
|
* int Addr; /* Driver load address - 1 */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Alloc_Driver Proc
|
|
|
|
Import Kern_Task : Code
|
|
|
|
Plx ; Restore X - msg number * 32
|
|
Phy
|
|
Ldy RxMsgPage+1,x ; Get the channel byte (DvrA or DvrB)
|
|
Cpy #MaxDvr+1 ; Are we trying to open a nonexistant driver
|
|
Bge ERR
|
|
Cpy #DvrA
|
|
Blt ERR
|
|
Stz RxMsgPage+1,x ; Clear the Driver byte
|
|
Lda Dvr_Use,y ; See if the driver is in use
|
|
Bne InUse
|
|
|
|
Lda RxMsgPage+2,x ; Get the ClientID byte
|
|
Sta Dvr_Use,y ; Store the ID if the driver is free
|
|
Stz RxMsgPage+2,x ; Clear the ClientID
|
|
Tya ; Make the driver ID a word offset
|
|
Asl A
|
|
Tay
|
|
Lda Init_Vec,y ; Return the driver starting address
|
|
Sta RxMsgPage+3,x
|
|
Lda Init_Vec+1,y
|
|
Sta RxMsgPage+2,x
|
|
Bra Complete ; NoErr is reported because we've been
|
|
; clearing bytes as we go!!!
|
|
|
|
InUse Stz RxMsgPage+2,x ; Clear the ClientID of the message
|
|
Sta RxMsgPage+1,x ; Return the ClientID of the client using the Driver
|
|
Lda #DvrInUse
|
|
Sta RxMsgPage,x ; Return the error Driver In Use
|
|
Bra Complete
|
|
|
|
ERR Stz RxMsgPage+2,x ; Clear ClinetID
|
|
Stz RxMsgPage+1,x ; Clear driver byte
|
|
Lda #Error ; We made a boo boo
|
|
Sta RxMsgPage,x ; Return the code
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* DeAlloc_Driver
|
|
*
|
|
* Passed
|
|
*
|
|
* struct DeAllocMsgType {
|
|
* byte msgNum; /* DeAlloc_Channel msg = $02 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct DeAllocErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* InByPass = -5
|
|
* */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
DeAlloc_Driver Proc
|
|
|
|
Import Kern_Task : Code
|
|
|
|
Plx ; Restore X - msg number * 32
|
|
Phy
|
|
Ldy RxMsgPage+1,x ; Get the driver byte (DvrA or DvrB)
|
|
Cpy #MaxDvr+1 ; Are we trying to close a nonexistant driver
|
|
Bge ERR
|
|
Cpy #DvrA
|
|
Blt ERR
|
|
Stz RxMsgPage+1,x ; clear the channel byte
|
|
|
|
Lda #00
|
|
Sta Dvr_Use,y ; Disable driver use byte
|
|
|
|
Tya
|
|
Asl A ; Make the channel number a word offset
|
|
Tax
|
|
Jsr FakeIndirX
|
|
Bra Complete
|
|
|
|
ERR Lda #Error ; Report that we made a boo boo
|
|
Sta RxMsgPage,x ; We were trying to close a nonexistant Driver
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
FakeIndirX Jmp (Close_Vec,x) ; Jump to the channel's close routine
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Init_Driver
|
|
*
|
|
* Passed
|
|
*
|
|
* struct InitMsgType {
|
|
* byte msgNum; /* Init_Channel msg = $03 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct InitErrType {
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* InByPass = -5
|
|
* NotAlloc = -6
|
|
* */
|
|
* }
|
|
*
|
|
*
|
|
* On Entry
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
* On Exit
|
|
*
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Init_Driver Proc
|
|
|
|
Import Kern_Task, \
|
|
Cmd_JmpTable : Code
|
|
|
|
Plx ; Get the message offset
|
|
Phy ; Save the message number
|
|
|
|
Ldy RxMsgPage+1,x ; Get the driver byte (DvrA or DvrB)
|
|
Cpy #MaxDvr+1 ; Are we trying to init a nonexistant driver
|
|
Bge ERR
|
|
Cpy #DvrA
|
|
Blt ERR
|
|
|
|
Stz RxMsgPage+1,x ; Clear driver byte
|
|
|
|
Lda Dvr_Use,y ; Check to see if driver has been allocated
|
|
Beq Not_Open
|
|
|
|
Phx ; Save the message offset
|
|
Lda Dvr_TaskID,y ; Get the Task ID for this driver
|
|
Pha ; Save Task ID temp.
|
|
Tya ; Load A with the driver byte
|
|
Asl A ; Make the driver byte a word offset
|
|
Tax
|
|
Lda Init_Vec,x ; Get the address of the driver init routine
|
|
Ldy Init_Vec+1,x
|
|
Plx ; Get the task ID
|
|
Sec ; Set Carry for Install Task
|
|
|
|
Jsr Cmd_JmpTable+(InstTask*3) ; Install the task into the Q
|
|
|
|
_Wait_Event InitFin ; Wait for the task to signal it's completion
|
|
|
|
_Reset_Event InitFin ; Reset tWait so we don't get this event again
|
|
|
|
Plx ; Restore the message offset
|
|
Stz RxMsgPage,x ; Signal No Error
|
|
Bra Complete
|
|
|
|
Not_Open Lda #NotAlloc ; Not open, therefore cannot be initialized
|
|
Sta RxMsgPage,x
|
|
Bra Complete
|
|
|
|
ERR Lda #Error ; Report that we made a boo boo
|
|
Sta RxMsgPage,x ; We were trying to close a nonexistant driver
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* ByPass_Mode
|
|
*
|
|
* Passed
|
|
*
|
|
* struct ByPassMsgType {
|
|
* byte msgNum; /* ByPass_Mode msg = $04 */
|
|
* byte On_Off; /* Off = 0 On = 1 */
|
|
* byte ClientID; /* ID > 0 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct ByPassErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* ChanInUse = -4
|
|
* InByPass = -5
|
|
* BadID = -7
|
|
* */
|
|
* Union {
|
|
* byte ByPassID; /* ID > 0 if in ByPass */
|
|
* byte ClientAID; /* ID > 0 If currently allocated */
|
|
* } switch;
|
|
* byte ClientBID; /* ID > 0 IF currently allocated */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
ByPass_Mode Proc
|
|
|
|
Import Kern_Task, \
|
|
Cmd_JmpTable : Code
|
|
|
|
Plx
|
|
Phy
|
|
Ldy RxMsgPage+1,x ; Get the On/Off byte
|
|
Beq ByPass_Off ; = 0 means we want to turn ByPass Off
|
|
Stz RxMsgPage+1,x ; Clear On/Off byte
|
|
|
|
Lda Dvr_AUse ; Check to see if Driver A is closed
|
|
Bne In_Use
|
|
|
|
Lda Dvr_BUse ; Check to see if Driver B is closed
|
|
Bne In_Use
|
|
|
|
Lda ByPass_Flag ; Check to see if we are in ByPass Mode
|
|
Bne In_ByPass
|
|
|
|
Php
|
|
Sei
|
|
Phx
|
|
Jsr OnReset
|
|
Plx
|
|
Plp
|
|
Lda #ByPass_Bit|RTXCA_36|RTXCB_36 ; Put PIC into ByPass Mode, RTXC=3.6 MHZ
|
|
Tsb SCC_Cntl
|
|
Lda RxMsgPage+2,x ; Set ByPass_Flag to ID byte
|
|
Sta ByPass_Flag ;
|
|
Bra Complete
|
|
|
|
ByPass_Off Stz RxMsgPage+1,x ; Clear on off byte
|
|
Lda RxMsgPage+2,x ; Get the ID
|
|
Stz RxMsgPage+2,x ; Clear the ID
|
|
Cmp ByPass_Flag ; Make sure who ever put us in ByPass takes us out
|
|
Bne WrongID
|
|
|
|
Php
|
|
Sei
|
|
Lda #ByPass_Bit ; Take PIC out of ByPass Mode
|
|
Trb SCC_Cntl
|
|
Jsr OffReset
|
|
Plp
|
|
Stz ByPass_Flag ; Turn ByPass mode off
|
|
Bra Complete
|
|
|
|
WrongID Lda ByPass_Flag
|
|
Sta RxMsgPage+1,x ; Return the ID of the person who turned ByPass on
|
|
Lda #BadID ; Make sure who ever put us
|
|
Sta RxMsgPage,x ; in bypass mode takes us out!!!!
|
|
Bra Complete
|
|
|
|
In_ByPass Lda ByPass_Flag
|
|
Sta RxMsgPage+1,x ; Return the ID of the person who turned ByPass on
|
|
Stz RxMsgPage+2,x ; Clear ID
|
|
Lda #InByPass
|
|
Sta RxMsgPage,x ; Return the error In ByPass mode
|
|
Bra Complete
|
|
|
|
In_Use Lda Dvr_AUse
|
|
Sta RxMsgPage+1,x ; Return driver A ClientID
|
|
Lda Dvr_BUse
|
|
Sta RxMsgPage+2,x ; Return driver B ClientID
|
|
|
|
Lda #DvrInUse ; Report that a driver is in use
|
|
Sta RxMsgPage,x
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
OffReset Lda #SCC_Msk
|
|
Tsb Int_Mask ; Enable SCC interrupts
|
|
Bra OffSkip
|
|
|
|
OnReset Lda #SCC_Msk
|
|
Trb Int_Mask
|
|
|
|
*
|
|
* Initialize the SCC
|
|
*
|
|
|
|
OffSkip Lda SCC_ACntl ; Synchronize Read/Writes
|
|
|
|
*
|
|
* Initialize the SCC
|
|
*
|
|
|
|
DoB Lda #Wr_reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanB_Reset
|
|
Sta SCC_ACntl
|
|
|
|
Ldx #0
|
|
@LoopB Lda Table,X ; Get the register number
|
|
Tay ; Transfer indicator bits
|
|
Iny ; Was it an $FF? (END OF TABLE?)
|
|
Beq DoA ; If so, exit
|
|
|
|
Sta SCC_BCntl ; Set register
|
|
Inx ; Move to next character
|
|
Lda Table,X ; Register contents
|
|
Sta SCC_BCntl
|
|
Inx
|
|
Bra @LoopB
|
|
|
|
DoA Lda #Wr_reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanA_Reset
|
|
Sta SCC_ACntl
|
|
|
|
Ldx #0
|
|
@LoopA Lda Table,X ; Get the register number
|
|
Tay ; Transfer indicator bits
|
|
Iny ; Was it an $FF? (END OF TABLE?)
|
|
Beq @Done ; If so, exit
|
|
|
|
Sta SCC_ACntl ; Set register
|
|
Inx ; Move to next character
|
|
Lda Table,X ; Register contents
|
|
Sta SCC_ACntl
|
|
Inx
|
|
Bra @LoopA
|
|
|
|
|
|
@Done Rts
|
|
|
|
Table
|
|
DC.B Wr_reg4, X16CLK|StopBit2
|
|
DC.B Wr_reg2, 00
|
|
DC.B Wr_reg3, Rx_8Bits
|
|
DC.B Wr_reg15, 00 ; Clear External status interrupts
|
|
DC.B Wr_reg0, Reset_ExtInt ; Reset external status interrupts twice ??
|
|
DC.B Wr_reg0, Reset_ExtInt
|
|
DC.B Wr_reg1, 00 ; Disable all interrupts
|
|
DC.B $ff ; End of table
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Version
|
|
*
|
|
* Passed
|
|
*
|
|
* struct VersionMsgType {
|
|
* byte msgNum; /* Version = $05 */
|
|
* byte DriverID; /* Driver A = $00 */
|
|
* /* Driver B = $01 */
|
|
* /* Kernel = $02 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct VersionErrType{
|
|
* byte errNum; /* NoErr = $00 */
|
|
* ptr VerInfo; /* Pointer to version info */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Version Proc
|
|
|
|
Import VerInfo : Data
|
|
Import Kern_Task : Code
|
|
|
|
Plx
|
|
Phy
|
|
|
|
Lda RxMsgPage+1,x
|
|
Cmp #00 ; Do a range check on Driver ID
|
|
Blt Err
|
|
|
|
Cmp #Kern+1
|
|
Bge Err
|
|
|
|
Asl A
|
|
Tay
|
|
Lda Ver_Ptr,y ; Return the address of the version info
|
|
Sta RxMsgPage+1,x
|
|
Lda Ver_Ptr+1,y
|
|
Sta RxMsgPage+2,x
|
|
|
|
Cmplt Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
Err Lda #Error ; Report Error and complete
|
|
Sta RxMsgPage,x
|
|
Bra Cmplt
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* SCC_Control - turn the general purpose clock source on and
|
|
* off when in bypass.
|
|
*
|
|
* Passed
|
|
*
|
|
* struct SCCCntlMsgType {
|
|
* byte msgNum; /* SCC_Cntl msg = $06 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* byte GPI; /* GPI = 0 (Off) GPI = 1 (On) */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct SCCCntlErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
SCC_Control Proc
|
|
|
|
Import Kern_Task : Code
|
|
|
|
Plx ; Restore X - msg number * 32
|
|
Phy
|
|
Lda ByPass_Flag ; This command only works in bypass.
|
|
Beq ERR
|
|
|
|
Stz RxMsgPage,x ; Assume no error.
|
|
Lda RxMsgPage+1,x ; Get the channel byte (DvrA or DvrB)
|
|
Cpa #DvrA ; Is it for DvrA or DvrB
|
|
Bne @Chck_B
|
|
|
|
Lda RxMsgPage+2,x ; Get the GPI
|
|
Bne @GPIA_On ; <> 0 means turn GPIA on
|
|
|
|
Lda SCC_Cntl ; Get the current value of the register.
|
|
And #ÅRTXCA_GPIA ; Make the clock source for RTXCA 3.686 MHZ
|
|
Sta SCC_Cntl ; Store the new value
|
|
Bra Complete
|
|
|
|
@GPIA_On Lda #RTXCA_GPIA ; Get the bit mask.
|
|
Tsb SCC_Cntl ; Make the clock source for RTXCA GPIA
|
|
Bra Complete
|
|
|
|
@Chck_B Cpa #DvrB ; Was it Driver B
|
|
Bne ERR ; It is an error if it isn't.
|
|
|
|
Lda RxMsgPage+2,x ; Get the GPI
|
|
Bne @GPIB_On ; <> 0 means turn GPIB on
|
|
|
|
Lda SCC_Cntl ; Get the current value of the register
|
|
And #ÅRTXCB_GPIA ; Make the clock source for RTXCB 3.686 MHZ
|
|
Sta SCC_Cntl ; Store the new value.
|
|
Bra Complete
|
|
|
|
@GPIB_On Lda #RTXCB_GPIA ; Get the bit mask.
|
|
Tsb SCC_Cntl ; Make the clock source for RTXB GPIB
|
|
Bra Complete
|
|
|
|
ERR Lda #Error ; We made a boo boo
|
|
Sta RxMsgPage,x ; Return the code
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Kernel Subroutines called by indirect jump through table
|
|
* in the Zero Page
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remv_RxMsgSgn - Install receive message signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at 1)
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_RxMsgSgn Proc
|
|
|
|
Import RxMsgSignal : Data
|
|
|
|
Export NullRxMsgSgn
|
|
|
|
Bcs Inst_RxMsgSgn
|
|
Ldy #>NullRxMsgSgn
|
|
Lda #<NullRxMsgSgn
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_RxMsgSgn - Install receive message signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at 1)
|
|
* Register Y - High byte of message signaller address
|
|
* Register A - Low byte of message signaller address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_RxMsgSgn Php ; Save the status byte
|
|
Sei
|
|
Sta RxMsgSignal-2,x ; Store the low byte of the msg handler
|
|
Inx
|
|
Tya
|
|
Sta RxMsgSignal-2,x ; Store the high byte of the msg handler
|
|
Plp ; Restore the status byte
|
|
|
|
Rts ; Return to caller
|
|
|
|
NullRxMsgSgn Rts
|
|
|
|
EndProc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remv_TxCmplSgn - Removel transmit completion signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at one)
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_TxCmplSgn Proc
|
|
|
|
Import TxMsgSignal : Data
|
|
Export NullTxCmplSgn
|
|
|
|
Bcs Inst_TxCmplSgn
|
|
Ldy #>NullTxCmplSgn
|
|
Lda #<NullTxCmplSgn
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_TxCmplSgn - Install transmit completion signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at one)
|
|
* Register Y - High byte of completion handler
|
|
* Register A - Low byte of completion handler
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_TxCmplSgn Php
|
|
Sei
|
|
Sta TxMsgSignal-2,x ; Store the low byte of the msg handler
|
|
Tya
|
|
Sta TxMsgSignal-1,x ; Store the high byte of the msg handler
|
|
Plp
|
|
|
|
Rts ; Return to caller
|
|
|
|
NullTxCmplSgn Rts
|
|
|
|
EndProc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_Task - Install task. Always installs task after Kernel.
|
|
*
|
|
* Register X - TaskID of task to install
|
|
* Register Y - High byte of task address
|
|
* Register A - Low byte of task address
|
|
*
|
|
* Carry Clear - Branch to Kill Task
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_Task Proc
|
|
|
|
Import tPrev, \
|
|
tNext, \
|
|
tSp, \
|
|
tEvent, \
|
|
tWait : Data
|
|
|
|
Bcc Kill_Task
|
|
Php
|
|
Sei
|
|
|
|
Cpx #DvrA_ID
|
|
Beq Do_DvrA
|
|
|
|
Sta DvrB_Sp-1 ; Push the task address on the stack
|
|
Sty DvrB_Sp
|
|
Lda #<(DvrB_Sp-2) ; Init the stack pointer
|
|
Bra SkipA
|
|
|
|
Do_DvrA Sta DvrA_Sp-1 ; Push the task address on the stack
|
|
Sty DvrA_Sp
|
|
Lda #<(DvrA_Sp-2) ; Init the stack pointer
|
|
|
|
SkipA Sta tSp,x
|
|
|
|
Ldy #Kern_ID ; Insert the Task after the Kernel task
|
|
Lda tNext,y ; Get the Kernel's tNext link.
|
|
Sta tNext,x ; Make the new Task link to what the kernel
|
|
; was linked to.
|
|
Tay ; Make y-reg point to what the tNext of the Kernel
|
|
Txa ; Make a-reg the value of the one we are inserting
|
|
Sta tPrev,y ; Update the previous pointer of the task we are inserting
|
|
; in front of.
|
|
|
|
Ldy #Kern_ID
|
|
Sta tNext,y ; Make the Kernel TCB point to the new TCB
|
|
Tya ; Make a-reg point to Kernel
|
|
Sta tPrev,x ; Make tPrev of the task to be inserted
|
|
; point to the Kernel.
|
|
|
|
Lda #$80
|
|
Sta tEvent,x ; Set the Always Event
|
|
Sta tWait,x ; Wait for the Always event
|
|
|
|
Plp
|
|
|
|
Rts
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Kill_Task - Kill task.
|
|
*
|
|
* Register X - TaskID of the task to be removed. (ChanA_ID ChanB_ID only !)
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Kill_Task
|
|
|
|
Php
|
|
Sei
|
|
Lda tPrev,x ; Get tPrev of task to be removed.
|
|
Tay ; y-reg points to task before the task to be removed.
|
|
Lda tNext,x ; Get tNext from task to be removed
|
|
Tax ; Make x-reg point to next task.
|
|
Sta tNext,y ; Make tNext of previous task point the task
|
|
; after the task to be removed.
|
|
Tya ; Make a-reg point to prev task
|
|
Sta tPrev,x ; Make tPrev of next task point to the tNext
|
|
; of the task that is removed.
|
|
Plp
|
|
|
|
Rts
|
|
|
|
EndProc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remv_ISR - remove an Interrupt service routine
|
|
*
|
|
* Register X - interrupt type
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_ISR Proc
|
|
|
|
Export Inst_ISR
|
|
|
|
Import UnKnown_Int : Code
|
|
Import ISR_Table :Data
|
|
|
|
Bcs Inst_ISR ; Is this a Remove or Install ISR call??
|
|
Ldy #>UnKnown_Int ; For Remove we fill in values for the
|
|
Lda #<Unknown_Int ; Unknown interrupt handler.
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_ISR - make an entry into the interrupt table.
|
|
*
|
|
* Register X - interrupt type
|
|
* Register Y - High byte of interrupt address
|
|
* Register A - Low byte of interrupt address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_ISR Php
|
|
Sei
|
|
Sta ISR_table,x ; Store the low byte of the address
|
|
Tya
|
|
Sta ISR_table+1,x ; Store the high byte of the address
|
|
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remove_ISR - remove an Interrupt service routine
|
|
*
|
|
* Carry -> Set - install SCC ISR routine
|
|
* Clear - remove SCC ISR routine
|
|
*
|
|
* Register X - interrupt type
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_SCC_ISR Proc
|
|
|
|
Import UnKnown_SCCInt : Code
|
|
Import SCC_table : Data
|
|
|
|
Bcs Inst_SCC_ISR
|
|
Ldy #>UnKnown_Int
|
|
Lda #<Unknown_Int
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_SCC_ISR - make an entry into the SCC interrupt table.
|
|
*
|
|
* Register X - interrupt type
|
|
* Register Y - High byte of interrupt address
|
|
* Register A - Low byte of interrupt address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
Inst_SCC_ISR Php
|
|
Sei
|
|
Sta SCC_table,x ; Store the low byte of the address
|
|
Tya
|
|
Sta SCC_table+1,x ; Store the high byte of the address
|
|
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Reset Channel - reset channel A or B of the SCC
|
|
*
|
|
* Carry -> Set - reset A
|
|
* Clear - reset B
|
|
*
|
|
* A Register is corrupted
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Reset_Chan Proc
|
|
|
|
Bcc ChanB ; Is this a ChanA or ChanB reset
|
|
Php ; Do ChanA reset
|
|
Sei
|
|
Lda #Wr_Reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanA_Reset|MIE|No_Vec
|
|
Sta SCC_ACntl
|
|
Plp
|
|
Rts
|
|
|
|
ChanB Php ; Do ChanB Reset
|
|
Sei
|
|
Lda #Wr_Reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanB_Reset|MIE|No_Vec
|
|
Sta SCC_ACntl
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Reg_Ver - register version info.
|
|
*
|
|
* Register A - Driver ID
|
|
* Register Y - High byte of info address
|
|
* Register X - Low byte of info address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Reg_Ver Proc
|
|
|
|
Phx
|
|
Asl A
|
|
Tax
|
|
Pla
|
|
Sta Ver_Ptr+1, x ; Store the Low byte of the address
|
|
Sty Ver_Ptr, x ; Store the High byte of the address
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_TmTask - install a timer task.
|
|
*
|
|
* Carry -> Set - Branch to cancel timer task
|
|
* Clear - execute install timer task
|
|
*
|
|
* Register A - Low byte of parameter address
|
|
* Register X - High byte of the parameter address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_TmTask Proc
|
|
|
|
Export Get_TMPB, \
|
|
Init_Timer, \
|
|
Timer_ISR
|
|
|
|
Php
|
|
Sei
|
|
Bcs Can_TmTask ; Cancel timer task if carry is set
|
|
|
|
Sta AddressLo ; Store the parameter address
|
|
Stx AddressHi
|
|
|
|
Ldy #$00
|
|
Lda (AddressLo),y
|
|
Tax
|
|
Iny
|
|
Lda (AddressLo),y ; Get the low byte of the completion routine
|
|
Sta TMPBpcL,x
|
|
Iny
|
|
Lda (AddressLo),y ; Get the high byte of the completion routine
|
|
Sta TMPBpcH,x
|
|
Iny
|
|
Lda (AddressLo),y ; Get the low byte of the time value
|
|
Sta TMPBTimeL,x
|
|
Iny
|
|
Lda (AddressLo),y ; Get the high byte of the time value
|
|
Sta TMPBTimeH,x
|
|
Txa
|
|
Tay
|
|
Jsr StartTimer
|
|
|
|
Plp
|
|
Rts
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Can_TmTask
|
|
* Inputs: Y - TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, X, Y, n, v, z, c
|
|
* Size: 100 bytes
|
|
* Cycles: 15 - if it is already completed
|
|
* 50 - if it is the only running timer
|
|
* 98 + StopTimer + StartTimer - if head, with successors
|
|
* 65..94+ - if not head
|
|
* Stack Usage: 2 bytes
|
|
* Calls: Stop, Start
|
|
* Called by:
|
|
*
|
|
* Function: Cancels the timer in the indicated TMPB, and removes it
|
|
* from the timer queue.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Can_TmTask
|
|
|
|
Lda TMPBcmplt,y ; Exit if the task has completed
|
|
Beq @Exit
|
|
|
|
Cpy tmReqQhead ; See if it is running
|
|
Bne @SrchQueue ; If not head of queue
|
|
|
|
Lda TMPBnext,y ; Check for successor
|
|
Bpl @HasSucc ; If not the only entry
|
|
|
|
Sta tmReqQhead ; Set head to nil
|
|
Lda #$00
|
|
Sta TMPBpcH,y ; Indicate that the routine is complete
|
|
Lda #Timer_Msk ; Disable timer interrupts
|
|
Trb Int_Mask ; Update the interrupt mask
|
|
Plp
|
|
Rts ; All done
|
|
|
|
@HasSucc Ldx tmReqQhead ; Get queue head
|
|
Jsr Stop ; Stop the head timer
|
|
Ldy tmReqQhead ; Get index of entry to dequeue
|
|
Lda #$00
|
|
Sta TMPBpcH,y ; Indicate that the routine is complete
|
|
Ldx TMPBnext,y ; Get index of successor
|
|
Stx tmReqQhead ; Successor is new head
|
|
Jsr @AdjTime ; Adjust the delay time
|
|
Jsr Start ; Start new head timer
|
|
Plp
|
|
Rts
|
|
|
|
@SrchQueue Lda tmReqQhead ; Index tmReqQhead
|
|
Sty tmIndexTemp
|
|
|
|
@loop Tax ; Load index register
|
|
Bmi @exit ; Exit if end of list (nil)
|
|
|
|
Lda TMPBnext,x ; Get index to next TMPB
|
|
Cpa tmIndexTemp ; Check for a match
|
|
Bne @loop ; Loop if not a match
|
|
|
|
Lda TMPBnext,y ; Prepare to un-link entry
|
|
Sta TMPBnext,x ; Finish unlinking it
|
|
Bmi @exit ; If entry has no successor
|
|
|
|
Tax
|
|
@AdjTime Clc ; Clear carry for addition
|
|
lda TMPBtimeL,x ; TMPBtime,x := TMPBtime,x + TMPBtime,y
|
|
Adc TMPBtimeL,y
|
|
Sta TMPBtimeL,x
|
|
Lda TMPBtimeH,x
|
|
Adc TMPBtimeH,y
|
|
Sta TMPBtimeH,x
|
|
@exit Plp
|
|
Rts ; All done
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: StartTimer
|
|
* Inputs: Y - TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, X, Y, n, v, z, c
|
|
* Size: 114 bytes
|
|
* Cycles: 39 + Start - if timer queue was empty
|
|
* 142 + Stop + StartTimer +..., otherwise
|
|
* Stack Usage: 2 bytes
|
|
* Calls: Stop, Start
|
|
* Called by:
|
|
*
|
|
* Function: Starts running the timer in the TMPB.
|
|
* The timeH and timeL fields of the TMPB must be filled in prior
|
|
* to this call.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
StartTimer
|
|
Lda #$ff
|
|
Sta TMPBcmplt,y ; Completion state is active
|
|
Ldx tmReqQhead ; Get index of head of list
|
|
Bpl @QNotEmpty ; If queue already has entries
|
|
|
|
Sty tmReqQhead ; New entry is queue head
|
|
Lda #nil ; Mark this entry as end of list
|
|
Sta TMPBnext,y ; Indicate no successor
|
|
Lda #Timer_Msk ; Enable timer interrupts
|
|
Tsb Int_Mask ; Update the interrupt mask
|
|
Tya ; Get TMPB index
|
|
Tax ; Setup TMPB index for StartTimer
|
|
Bra Start ; Start the timer and return
|
|
|
|
@QNotEmpty Jsr Stop ; Stop the currently running timer
|
|
|
|
Ldx tmReqQhead ; Index tmReqQhead
|
|
@loop Stx tmIndexTemp ; Remember index of previous TMPB
|
|
Lda TMPBnext,x ; Get index to next TMPB
|
|
Tax ; Load index register
|
|
Bmi @exit ; Exit if end of list (nil)
|
|
|
|
Sec ; Set carry for subtract
|
|
Lda TMPBtimeL,y ; TMPBtime,y := TMPBtime,y - TMPBtime,x
|
|
Sbc TMPBtimeL,x
|
|
Sta TMPBtimeL,y
|
|
Lda TMPBtimeH,y
|
|
Sbc TMPBtimeH,x
|
|
Sta TMPBtimeH,y
|
|
Bpl @loop ; Loop until TMPBtime,y is < 0
|
|
|
|
Clc ; Clear carry for addition
|
|
Lda TMPBtimeL,y ; TMPBtime,y := TMPBtime,y + TMPBtime,x
|
|
Adc TMPBtimeL,x
|
|
Sta TMPBtimeL,y
|
|
Lda TMPBtimeH,y
|
|
Adc TMPBtimeH,x
|
|
Sta TMPBtimeH,y
|
|
Sec ; Set carry for subtract
|
|
Lda TMPBtimeL,x ; TMPBtime,x := TMPBtime,x - TMPBtime,y
|
|
Sbc TMPBtimeL,y
|
|
Sta TMPBtimeL,x
|
|
Lda TMPBtimeH,x
|
|
Sbc TMPBtimeH,y
|
|
Sta TMPBtimeH,x
|
|
@exit Txa ; Prepare to store x
|
|
Sta TMPBnext,y ; Add rest of list after new entry
|
|
Ldx tmIndexTemp ; Get index of entry before new one
|
|
Tya ; Prepare to store y
|
|
Sta TMPBnext,x ; Add new entry after beginning of list
|
|
Ldx tmReqQhead ; Get index of head of list
|
|
*
|
|
* Falls into Start, which follows below
|
|
* bra Start ; Start the timer for the list head
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Start
|
|
* Inputs: X - tmReqQhead TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, n, v, z, c
|
|
* Size: 75 bytes
|
|
* Cycles: 26 - when timer in range $8000..$FFFF
|
|
* 33 - when timer in range $0000..$007F
|
|
* 40 - when timer in range $0080..$00FF
|
|
* 43/49 - when timer in range $0100..$7FFF
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by: StartTmr, Timer_Int, CancelTmr
|
|
*
|
|
* Function: Loads the hardware timer with the amount of time remaining
|
|
* for the timer at the head of the timer queue, or $0080 if
|
|
* the amount is less than 1, or $7F80 if the value is greater
|
|
* than $007F. Adjusts the value in the TMPB to reflect the
|
|
* value loaded into the hardware time. The timer is then
|
|
* started and will interrupt when it reaches zero.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Start
|
|
|
|
Lda TMPBtimeH,x ; Get high bits
|
|
Bne @OORangeH ; If way too big
|
|
|
|
Lda TMPBtimeL,x ; Get low bits
|
|
Bmi @OORangeL ; Not in range $0000..$007F
|
|
|
|
Stz TMPBtimeL,x ; Clear low bits
|
|
Sta TmCnt_Hi ; Start the counter
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Rts
|
|
|
|
@OORangeH Bmi @OORangeNeg ; If negative
|
|
Lda #$7F ; Load timer with $7F80
|
|
Sta TmCnt_Hi ; Start the counter
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Sec ; Set carry for subtract
|
|
Lda TMPBtimeL,x ; Get low bits
|
|
Sbc #$7F ; TMPBtime,x := TMPBtime,x - $007F
|
|
Sta TMPBtimeL,x
|
|
Bcs @exit ; If no borrow
|
|
|
|
Dec TMPBtimeH,x ; Adjust for borrow
|
|
@exit Rts
|
|
|
|
|
|
@OORangeNeg Stz TmCnt_Hi ; Use $0080 if negative
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Rts
|
|
|
|
@OORangeL Sec ; Set carry for subtract
|
|
Sbc #$7F ; TMPBtime,x := TMPBtime,x - $007F
|
|
Sta TMPBtimeL,x
|
|
Lda #$7F ; Load timer with $7F80
|
|
Sta TmCnt_Hi ; Start the counter
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Rts
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Stop
|
|
* Inputs: X - tmReqQhead TMPB index
|
|
* Outputs: n - indicates sign of TMPBtimer,x
|
|
* Destroys: A, v, z, c
|
|
* Size: 41 bytes
|
|
* Cycles: 38..40 + 9 if close to rollover
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by: StartTimer, Timer_Int, CancelTmr
|
|
*
|
|
* Function: Reads the hardware timer, and updates the head of the
|
|
* timer queue to reflect the actual amount of time remaining.
|
|
* It also returns an indication of the sign of the timer
|
|
* remaining for the queue head.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Stop
|
|
Lda TmCnt_Lo ; Read low byte of counter
|
|
Cpa #$10 ; See if close to rollover
|
|
Blt Stop ; Wait if close to rollover
|
|
|
|
Lda TmCnt_Hi ; Read high byte of counter
|
|
Clc ; Clear carry for addition
|
|
Bpl @ZeroExt ; If high byte was positive
|
|
|
|
Adc TMPBtimeL,x ; Add to timer remaining for the
|
|
Sta TMPBtimeL,x ; Timer at head of the timer list
|
|
Bcs @TestSign ; If no borrow from high byte
|
|
|
|
Dec TMPBtimeH,x ; Adjust high byte
|
|
|
|
@TestSign Lda TMPBtimeH,x
|
|
Rts ; All done
|
|
|
|
@ZeroExt Adc TMPBtimeL,x ; Add to timer remaining for the
|
|
Sta TMPBtimeL,x ; Timer at head of the timer list
|
|
Bcc @TestSign ; If no carry into high byte
|
|
|
|
Inc TMPBtimeH,x ; Adjust high byte
|
|
Rts ; All done
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Timer_ISR
|
|
* Inputs: none
|
|
* Outputs: none
|
|
* Destroys: A, X, Y, n, v, z, c
|
|
* Size: 60 bytes
|
|
* Cycles: 15 + Stop + Start, if timer not fully expired
|
|
* 60 + Stop, if expired, no more queued,
|
|
* no completion handler
|
|
* 84 + Stop, if expired, no more queued,
|
|
* has completion handler
|
|
* 62 + Stop + Start, if expired, and more queued
|
|
* no completion handler
|
|
* 86 + Stop + Start, if expired, and more queued
|
|
* has completion handler
|
|
* Stack Usage: 2 bytes
|
|
* Calls: Stop, Start, TMPB completion handler
|
|
* Called by: Interrupt
|
|
*
|
|
* Function: Handles timer interrupts, determines if the timer at the
|
|
* head of the timer queue has fully expired, and calls the
|
|
* completion handler for that timer if one exists. Start
|
|
* any remaining timers on the timer queue.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Timer_ISR
|
|
|
|
Ldx tmReqQhead ; Get index of head of list
|
|
Bmi @unknownint ; If timer queue is empty
|
|
|
|
Jsr Stop ; Stop timer and update queue head
|
|
Bpl Start ; If not expired, restart timer and exit
|
|
|
|
Phx ; Save pointer to dequeued entry
|
|
Stz TMPBcmplt,x ; Zero completion routine indicating done.
|
|
Lda TMPBnext,x ; Get next queue entry
|
|
Sta tmReqQhead ; Dequeue head
|
|
Bmi @DisTimer ; If no successor disable timer
|
|
|
|
Tax ; Get pointer to new queue head
|
|
Jsr Start ; Start next timer
|
|
Bra @RunCmplt ; Go run the completion routine
|
|
|
|
@DisTimer Lda #Timer_Msk ; Disable timer interrupts
|
|
Trb Int_Mask ; Update the interrupt mask
|
|
|
|
@RunCmplt Plx ; Restore pointer to dequeued entry
|
|
Lda TMPBpcL,x
|
|
Sta AddressLo
|
|
Lda TMPBpcH,x
|
|
Sta AddressHi
|
|
Jmp (AddressLo) ; Jump to the completion routine. The
|
|
; return address is on the stack
|
|
|
|
@unknownint Brk ; TMR interrupt with empty Q
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Get_TMPB
|
|
* Inputs: none
|
|
* Outputs: Y - TMPB index
|
|
* n - 1 if no free TMPBs available
|
|
* Destroys: A, Y, n, z
|
|
* Size: 22 bytes
|
|
* Cycles: 34 if BCPB was available
|
|
* 13 if BCPB was not available
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by:
|
|
*
|
|
* Function: Returns the index of a free TiMer Parameter Block
|
|
* (TMPB), and removes it from the list of free TMPBs.
|
|
* A flag is returned to indicate if a free TMPB existed.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Get_TMPB
|
|
|
|
Php
|
|
Sei
|
|
Bcs Free_TMPB
|
|
|
|
Ldy availhead ; Get the available list head
|
|
Bmi @exit ; If list empty
|
|
|
|
Lda TMPBnext,y ; Get successor
|
|
Sta availhead ; Update list head
|
|
@exit Plp
|
|
Rts ; All done
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Free_TMPB
|
|
* Inputs: Y - TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, n, z, c
|
|
* Size: 19 bytes
|
|
* Cycles: 27
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by:
|
|
*
|
|
* Function: Adds the TMPB that was passed in, to the list of available
|
|
* TMPBs. If the TMPB is currently active, the system crashes
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Free_TMPB
|
|
|
|
Lda availhead ; Get the available list head
|
|
Sta TMPBnext,y ; Link it to end of new entry
|
|
Sty availhead ; New entry is new list head
|
|
Lda TMPBstate,y ; Check the state
|
|
|
|
Plp
|
|
Rts ; All done
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Init_Timer
|
|
* Inputs: none
|
|
* Outputs: none
|
|
* Destroys: A, X, n, v, z, c
|
|
* Size: 40 bytes
|
|
* Cycles: 45 + 14 per TMPB
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by: Reset
|
|
*
|
|
* Function: Initializes Timer data structures.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Init_Timer
|
|
|
|
last Equ TMPBsize*(MaxTMPBs-1)
|
|
|
|
Lda #0 ; Index of first TMPB
|
|
Sta availhead ; All TMPBs are now available
|
|
Clc ; Clear carry for additions
|
|
@loop Tax ; Setup index register
|
|
Adc #TMPBsize ; Point to next TMPB
|
|
Sta TMPBnext,x ; Have previous point to next
|
|
Stz TMPBcmplt,x ; Zero the completion state
|
|
Cpa #last ; Check for last TMPB
|
|
Blt @loop ; Process next TMPB
|
|
|
|
Lda #nil ; Mark empty lists
|
|
Sta last+TMPBnext ; Mark end of free list
|
|
Sta tmReqQhead ; Request queue head is empty
|
|
Lda #Timer_Msk ; Disable timer interrupts
|
|
Trb Int_Mask ; Update the interrupt mask
|
|
Lda #One_Shot ; Prepare to force one shot mode
|
|
Trb TD_Cntl ; Reset the bit
|
|
Lda #$80 ; Low byte latch is a constant
|
|
Sta TmLatch_Lo ; Initialize the low timer latch
|
|
Rts
|
|
|
|
*
|
|
* TiMer Parameter Block (TMPB)
|
|
*
|
|
|
|
|
|
TMPBnext DS.B 1 ; Next TMPB on list
|
|
TMPBstate DS.B 1 ; Request state
|
|
|
|
TMPBtimeH DS.B 1 ; High byte of 2 byte timer
|
|
TMPBtimeL DS.B 1 ; Low byte of 2 byte timer
|
|
|
|
TMPBpcH DS.B 1 ; Completion routine PC high
|
|
TMPBpcL DS.B 1 ; Completion routine PC low
|
|
TMPBcmplt DS.B 1 ; Completion state (0 = complete 0<> means running)
|
|
|
|
DS.B TMPBsize*(MaxTMPBs-1) ; Allocate remaining TMPBs
|
|
|
|
availhead DS.B 1 ; Head of TMPB free list
|
|
tmReqQhead DS.B 1 ; Head of TMPB request queue
|
|
tmIndexTemp DS.B 1 ; Temporary storage for index
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Global Data Area for kernel
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Global_Kernel Proc
|
|
|
|
Export ISR_table, \
|
|
ISR_Decode, \
|
|
MsgNumTimes2, \
|
|
MsgNumTimes32, \
|
|
Cmd_Table, \
|
|
Cmd_JmpTable, \
|
|
SCC_Table, \
|
|
TxMsgSignal, \
|
|
RxMsgSignal, \
|
|
tPrev, \
|
|
tNext, \
|
|
tSp, \
|
|
tEvent, \
|
|
tWait, \
|
|
tTNxt, \
|
|
tTEvent, \
|
|
tTISR, \
|
|
VerInfo
|
|
|
|
Import Remv_RxMsgSgn, \
|
|
Remv_TxCmplSgn, \
|
|
Remv_ISR, \
|
|
Remv_SCC_ISR, \
|
|
Inst_Task, \
|
|
Rel_Task, \
|
|
Ev_Wait, \
|
|
Ev_Signal, \
|
|
Ev_Reset, \
|
|
TEv_Reset, \
|
|
Get_TMPB, \
|
|
Inst_TmTask, \
|
|
Reg_Ver, \
|
|
NullRxMsgSgn, \
|
|
NullTxCmplSgn, \
|
|
UnKnown_Int, \
|
|
UnKnown_SCCInt, \
|
|
Dsptch_KRxMsg
|
|
|
|
*
|
|
* TCB - Task Control Block
|
|
*
|
|
* Each active task has a TCB entry in the TCB linked list.
|
|
* The List starts out with only one active task which is the kernel task.
|
|
* The Kernel is always active.
|
|
*
|
|
|
|
tPrev DC.B Kern_ID ; Previous Task
|
|
tNext DC.B Kern_ID ; Next task ID (initially 0 for Kernel Task only)
|
|
tSp DC.B $FF ; Stack Pointer for this task
|
|
tEvent DC.B $80 ; Event flags (bit 7 event always active)
|
|
tWait DC.B $00 ; Event Mask (Wait for RxMsg1 event always)
|
|
|
|
TCB_Size Equ *-tPrev
|
|
|
|
DC.B $00 ; tPrev ChanA Task
|
|
DC.B $00 ; tNext ChanA Task
|
|
DC.B $a9 ; tSp ChanA Task
|
|
DC.B $80 ; tEvent ChanA Task (bit 7 event always active)
|
|
DC.B $00 ; tWait ChanA Task
|
|
|
|
DC.B $00 ; tPrev ChanB Task
|
|
DC.B $00 ; tNext ChanB Task
|
|
DC.B $54 ; tSp ChanB Task
|
|
DC.B $80 ; tEvent ChanB Task (bit 7 event always active)
|
|
DC.B $00 ; tWait ChanB Task
|
|
|
|
*
|
|
* 6502 and SCC interrupt service routine vector tables
|
|
*
|
|
|
|
ISR_table DC.W Unknown_Int ; 0 priority
|
|
DC.W UnKnown_Int ; DMA1
|
|
DC.W Unknown_Int ; DMA2
|
|
DC.W UnKnown_Int ; SCC
|
|
DC.W UnKnown_Int ; Timer
|
|
DC.W UnKnown_Int ; Host
|
|
|
|
ISR_Decode DC.B 0 ; %00000000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00000010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00000100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00000110
|
|
DC.B 0
|
|
DC.B SCC ; %00001000
|
|
DC.B 0
|
|
DC.B SCC ; %00001010
|
|
DC.B 0
|
|
DC.B SCC ; %00001100
|
|
DC.B 0
|
|
DC.B SCC ; %00001110
|
|
DC.B 0
|
|
DC.B HOST ; %00010000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00010010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00010100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00010110
|
|
DC.B 0
|
|
DC.B SCC ; %00011000
|
|
DC.B 0
|
|
DC.B SCC ; %00011010
|
|
DC.B 0
|
|
DC.B SCC ; %00011100
|
|
DC.B 0
|
|
DC.B SCC ; %00011110
|
|
DC.B 0
|
|
DC.B TIMER ; %00100000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00100010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00100100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00100110
|
|
DC.B 0
|
|
DC.B SCC ; %00101000
|
|
DC.B 0
|
|
DC.B SCC ; %00101010
|
|
DC.B 0
|
|
DC.B SCC ; %00101100
|
|
DC.B 0
|
|
DC.B SCC ; %00101110
|
|
DC.B 0
|
|
DC.B TIMER ; %00110000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00110010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00110100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00110110
|
|
DC.B 0
|
|
DC.B SCC ; %00111000
|
|
DC.B 0
|
|
DC.B SCC ; %00111010
|
|
DC.B 0
|
|
DC.B SCC ; %00111100
|
|
DC.B 0
|
|
DC.B SCC ; %00111110
|
|
|
|
SCC_table DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
|
|
*
|
|
* Transmit message Request Completion signal routines
|
|
*
|
|
|
|
TxMsgSignal DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
|
|
*
|
|
* Receive new message signal routines
|
|
*
|
|
|
|
RxMsgSignal DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
|
|
*
|
|
* Kernel Command Table - this contains the addresses of the kernel commands
|
|
* which will later be tranfered to the zero page,
|
|
* Up to twenty commands are supported. They must be
|
|
* in order of their command numbers.
|
|
*
|
|
|
|
Cmd_Table DC.W Remv_RxMsgSgn
|
|
DC.W Remv_TxCmplSgn
|
|
DC.W Remv_ISR
|
|
DC.W Remv_SCC_ISR
|
|
DC.W Inst_Task
|
|
DC.W Rel_Task
|
|
DC.W Ev_Wait
|
|
DC.W Ev_Signal
|
|
DC.W Ev_Reset
|
|
DC.W Reset_Chan
|
|
DC.W Get_TMPB
|
|
DC.W Inst_TmTask
|
|
DC.W Reg_Ver
|
|
DS.W 7
|
|
|
|
Cmd_JmpTable Jmp (Krn_CmdBase+(RemvRxMsg*2))
|
|
Jmp (Krn_CmdBase+(RemvTxCmpl*2))
|
|
Jmp (Krn_CmdBase+(RemvISR*2))
|
|
Jmp (Krn_CmdBase+(RemvSCCISR*2))
|
|
Jmp (Krn_CmdBase+(InstTask*2))
|
|
Jmp (Krn_CmdBase+(RelTask*2))
|
|
Jmp (Krn_CmdBase+(WaitEvent*2))
|
|
Jmp (Krn_CmdBase+(SignalTask*2))
|
|
Jmp (Krn_CmdBase+(ResetEvent*2))
|
|
Jmp (Krn_CmdBase+(ResetChan*2))
|
|
Jmp (Krn_CmdBase+(GetTMPB*2))
|
|
Jmp (Krn_CmdBase+(InstTmTask*2))
|
|
Jmp (Krn_CmdBase+(RegVer*2))
|
|
|
|
MsgNumTimes2 DC.B 0,2,4,6,8,10,12,14
|
|
|
|
MsgNumTimes32 DC.B 0,32,64,96,128,160,192,224
|
|
|
|
*
|
|
* Version Information
|
|
*
|
|
|
|
String Pascal
|
|
|
|
VerInfo DC.B $02
|
|
DC.B 'Enhanced Communications Controller'
|
|
DC.B 'Version 1.0A0 © Apple Computer Inc. 1988'
|
|
|
|
DCB.W 512, $00FF ; Needed for AURORA ROM resource padding
|
|
|
|
|
|
endproc
|
|
|
|
|
|
End
|
|
|
|
|