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


