;; ****************************************************************************
;;  PIC12/16 Pseudo Real Time Operating System
;; ****************************************************************************

	LIST	P=PIC16F88
	INCLUDE	"P16F88.INC"
	__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB3 & _MCLR_OFF & _PWRTE_ON & _WDT_ON & _HS_OSC &_LVP_OFF
	__CONFIG _CONFIG2, _FCMEN_OFF & _IESO_OFF 

; *** Define constant ***
STACKLEVEL	EQU	(D'8'-D'1')
MAXID		EQU	H'0F'
TMR0SET		EQU	H'00'
;;; Task IDs
TMR0TASKID	EQU	D'0'
;;; TMR0IE location
	IFNDEF	TMR0IE
	IFDEF	T0IE
TMR0IE	SET	T0IE
	ELSE
TMR0IE	SET	D'5'
	ENDIF
	ENDIF
;;; TMR0IF location
	IFNDEF	TMR0IF
	IFDEF	T0IF
TMR0IF	SET	T0IF
	ELSE
TMR0IF	SET	D'2'
	ENDIF
	ENDIF

; *** Provide register/routine ***
	GLOBAL	INITIALIZE
	GLOBAL	INTERRUPT
	GLOBAL	IDLE
	GLOBAL	SP,TOPSTACK,RA
	GLOBAL	VALUE0,VALUE1,ARGUMENT0,ARGUMENT1,TEMP0,TEMP1

; *** Request register/routine ***
;	EXTERN	RECIEVE				;Software serial receiver.
	EXTERN	USER_INITIALIZE			;User define initialize routine

; *** Define GPR ***
;;; Interrupt Temporary SFRs
TEMP_REGISTERS	UDATA
STATUS_TEMP	RES	D'1'
FSR_TEMP	RES	D'1'
PCLATH_TEMP	RES	D'1'
SFR_SP		RES	D'1'			;Stack pointer for interrupt temporary SFRs
;;; Stack for temporary SFRs
SFR_STACK	UDATA
SFR_EOS		RES	STACKLEVEL*D'4'		;End of stack for Special Function Register
;;; Stack for general purpose
STACK		UDATA
TOPSTACK	RES	D'96'			;End of stack for general purpose
;;; OS16 system register
SYSTEM_REGISTER	UDATA
CLOCK		RES	D'2'			;System clock (big endian)
TASKSTATUS	RES	(MAXID+D'1')		;Task Status Registers
ACTIVE		EQU	D'7'			;  __7bit____6bit____5bit___4bit-0bit_
READY		EQU	D'6'			; |_ACTIVE_|_READY_|_WAIT_|_parameter_|
WAIT		EQU	D'5'
TASKCON		RES	D'1'			;Task Control Register
EXCLUSION	EQU	D'7'			; ____7bit________6bit-0bit____
						;|_Exclusion_|_current_task_id_|
;;; OS16 system register in shared area
		UDATA_SHR
WREG_TEMP	RES	D'1'			;Interrupt temporary register for WREG
;;; Pseudo CPU registers
SP		RES	D'1'			;Stack pointer for general purpose
RA		RES	D'2'			;2Byte Return Address (big endian)
VALUE0		RES	D'4'			;32bit return value No.0 (big endian)
ARGUMENT0	RES	D'4'			;32bit argument No.0 (big endian)
TEMP0		RES	D'4'			;32bit temporary registr
CPU_REGISTER	UDATA
VALUE1		RES	D'4'			;32bit return value No.1 (big endian)
ARGUMENT1	RES	D'4'			;32bit argument NO.1 (big endian)
TEMP1		RES	D'4'			;32bit temporary registr

; *** Include macro ***
	INCLUDE	"MACRO.INC"

; *** Program area ***

MAIN	CODE	H'0005'

; *** Task Scheduler ***
INTERRUPT
	;;Save registers
	MOVWF	WREG_TEMP			;Save Working register
	SWAPF	STATUS,W			;Save STATUS register
	BANKSEL(STATUS_TEMP)
	MOVWF	STATUS_TEMP
	MOVF	FSR,W				;Save FSR register
	MOVWF	FSR_TEMP
	MOVF	PCLATH,W			;Save PCLATH register
	MOVWF	PCLATH_TEMP
	PAGESEL(INTERRUPT)
	;;; Pseudo non-maskable Interrupt
	;;;; Software serial receiver
	IFDEF	RECIEVE
	BTFSS	INTCON,INTF
	GOTO	$+D'4'
	PAGESEL(RECIEVE)
	GOTO	RECIEVE
	FILL	(NOP),D'2'			;Dumy NOP for PAGESEL
	ENDIF
	;;; Create task from interrupt
	BANKSEL(TASKSTATUS)			;Change BANK
	;;;; TMR0 Overflow Interrup TASK
	IFDEF	TMR0TASKID
	BTFSS	INTCON,TMR0IF
	GOTO	$+D'3'
	BSF	TASKSTATUS+TMR0TASKID,READY
	BCF	INTCON,TMR0IF			;Clear TMR0IF
	ENDIF
	;;;; RB Port Change Interrupt TASK
	IFDEF	CHANGE_TASKID
	BTFSS	INTCON,RBIF
	GOTO	$+D'3'
	BSF	TASKSTATUS+CHANGE_TASKID,READY
	BCF	INTCON,RBIF			;Clear RBIF
	ENDIF
	;;;; INT External Interrup TASK
	IFDEF	INT_TASKID
	BTFSS	INTCON,INTF
	GOTO	$+D'3'
	BSF	TASKSTATUS+INT_TASKID,READY
	BCF	INTCON,INTF			;Clear INTF
	ENDIF
	;;;; EE Write Complete Interrupt TASK
	IFDEF	EE_TASKID
	BANKSEL(EECON1)				;Change BANK
	BTFSS	EECON1,EEIF
	GOTO	$+D'5'
	BCF	EECON1,EEIF			;Clear EEIF
	BANKSEL(TASKSTATUS)			;Change BANK 2 cycle
	BSF	TASKSTATUS+EE_TASKID,READY
	NOP					;Dummy NOP for BANKSEL
	ENDIF
	;;; Check Exclusion Control
	BANKSEL(TASKCON)
	BTFSC	TASKCON,EXCLUSION
	GOTO	RETURN_INTERRUPT
	;;; Save temporary registers to stack
	BANKSEL(SFR_SP)
	MOVLW	LOW(SFR_EOS+D'1')
	SUBWF	SFR_SP,W
	BTFSS	STATUS,C
	GOTO	RETURN_INTERRUPT
	BANKISEL(SFR_EOS)
	DECF	SFR_SP,W
	MOVWF	FSR
	;;;; Save temporary Working register
	MOVF	WREG_TEMP,W
	MOVWF	INDF
	DECF	FSR,F
	;;;; Save temporary STATUS register
	MOVF	STATUS_TEMP,W
	MOVWF	INDF
	DECF	FSR,F
	;;;; Save temporary FSR register
	MOVF	FSR_TEMP,W
	MOVWF	INDF
	DECF	FSR,F
	;;;; Save temporary PCLATH register
	MOVF	PCLATH_TEMP,W
	MOVWF	INDF
	MOVF	FSR,W
	MOVWF	SFR_SP
	BSF	INTCON,GIE			;Enable interrupt
SCHEDULER
	;;; Task schaduling
	BANKSEL(TASKCON)
	CLRF	TASKCON				;Clear Task Control register
SWITCH	MACRO	_TASKID,_LABEL,_STACK
	LOCAL	NEXT
	BTFSS	TASKSTATUS+_TASKID,READY
	GOTO	NEXT				;Next task
	BTFSC	TASKSTATUS+_TASKID,ACTIVE
	GOTO	RESUME				;If current TASK is already active.
	MOVLW	(TOPSTACK-_STACK)
	SUBWF	SP,W
	BTFSS	STATUS,C
	GOTO	RESUME
	BSF	TASKSTATUS+_TASKID,ACTIVE
	PAGESEL(_LABEL)
	GOTO	_LABEL
NEXT
	INCF	TASKCON,F
	ENDM
	SWITCH	TMR0TASKID,TMR0TASK,D'0'	;ID=H'00' : Timer0 Task
RESUME
	;;; Return from Task Scheduler
	BCF	INTCON,GIE			;Forbid interrupt
	;;; Save temporary registers to stack
	BANKISEL(SFR_EOS)
	BANKSEL(SFR_SP)
	MOVF	SFR_SP,W
	MOVWF	FSR
	;;;; Return temporary PCLATH register
	MOVF	INDF,W
	MOVWF	PCLATH_TEMP
	INCF	FSR,F
	;;;; Return temporary FSR register
	MOVF	INDF,W
	MOVWF	FSR_TEMP
	INCF	FSR,F
	;;;; Return temporary STATUS register
	MOVF	INDF,W
	MOVWF	STATUS_TEMP
	INCF	FSR,F
	;;;; Return temporary working register
	MOVF	INDF,W
	MOVWF	WREG_TEMP
RETURN_INTERRUPT
	;;; Return from Task or Interrupt
	MOVF	PCLATH_TEMP,W			;Return PCLATH register
	MOVWF	PCLATH
	MOVF	FSR_TEMP,W			;Return FSR resgister
	MOVWF	FSR
	SWAPF	STATUS_TEMP,W			;Return STATUS register
	MOVWF	STATUS
	SWAPF	WREG_TEMP,F			;Return Working register
	SWAPF	WREG_TEMP,W
	RETFIE

; *** ID=H'00': Timer0 Task ***
TMR0TASK
	CLRWDT					;Clear watch dog timer.
	BANKSEL(TMR0)				;Change BANK : 2cycle
	MOVLW	TMR0SET				;Reset TMR0
	ADDWF	TMR0,F
	;;; Clock countup
	BANKSEL(TASKSTATUS)
	VARIABLE	TASKID=D'0'
	WHILE	MAXID>=TASKID
	BTFSC	TASKSTATUS+TASKID,WAIT
	INCF	TASKSTATUS+TASKID,F
TASKID=TASKID+D'1'
	ENDW
	BANKSEL(CLOCK)				;Change BANK : 2cycle
	INCF	CLOCK+D'1',F
	BTFSC	STATUS,Z
	INCF	CLOCK,F
	;;; END TASK
	BANKSEL(TASKSTATUS)
	CLRF	TASKSTATUS+TMR0TASKID
	PAGESEL(SCHEDULER)
	GOTO	SCHEDULER

; *** In Idle state ***
IDLE
	GOTO	IDLE

; *** Initialize routine ***
INITIALIZE
	;;; Kill all Modules
	;;;; STATUS Register
	CLRF	STATUS				;Change to BANK0
	;;;; Power Control Register
	IFDEF	PCON
	BANKSEL(PCON)				;Change BANK
	BSF	PCON,NOT_POR			;Set Power-on Reset Status
	BSF	PCON,NOT_BOR			;Set Brown-out Reset Status
	ENDIF
	;;;; Interrupt Control Register
	CLRF	INTCON
	;;;; Peripheral Interrupt Enable Register 1
	IFDEF	PIE1
	BANKSEL	PIE1				;Change BANK
	CLRF	PIE1
	ENDIF
	;;;; Peripheral Interrupt Enable Register 2
	IFDEF	PIE2
	BANKSEL(PIE2)				;Change BANK
	CLRF	PIE2
	ENDIF
	;;;; Peripheral Interrupt Request (FLAG) Register 1
	IFDEF	PIR1
	BANKSEL(PIR1)				;Change BANK
	CLRF	PIR1
	ENDIF
	;;;; Peripheral Interrupt Request (FLAG) Register 2
	IFDEF	PIR2
	BANKSEL(PIR2)				;Change BANK
	CLRF	PIR2
	ENDIF
	;;;; OPTION_REG Register
	BANKSEL(OPTION_REG)			;Change BANK
	CLRF	OPTION_REG
	;;;; ADC module
	IFDEF	ADCON0
	BANKSEL(ADCON0)				;Change BANK
	CLRF	ADCON0				;Disable ADC
	ENDIF
	IFDEF	ANSEL
	BANKSEL(ANSEL)				;Change BANK
	CLRF	ANSEL
	ENDIF
	IFDEF	ADCON1
	BANKSEL(ADCON1)				;Change BANK
	CLRF	ADCON1
	ENDIF
	;;;; Comparater module
	IFDEF	CMCON
	BANKSEL(CMCON)
	MOVLW	B'00000111'
	MOVWF	CMCON				;Stop comparater
	ENDIF
	;;;; USART module
	IFDEF	RCSTA
	BANKSEL(RCSTA)
	CLRF	RCSTA				;Disable USART
	ENDIF
	;;;; SSP module
	IFDEF	SSPCON
	BANKSEL(SSPCON)
	CLRF	SSPCON				;Disable SSP
	ENDIF
	;;; Operating System initialize
	;;;; Stack pointer for interrupt temporary SFRs
	BANKSEL(SFR_SP)
	MOVLW	LOW(SFR_EOS+STACKLEVEL*D'4')
	MOVWF	SFR_SP
	;;;; TMR0 initialize
	BANKSEL(TMR0)
	MOVLW	TMR0SET				;Reset TMR0
	MOVWF	TMR0
	;;; User initialize
	PAGESEL(USER_INITIALIZE)
	CALL	USER_INITIALIZE
	;;; Clear watch dog timer.
	CLRWDT
	PAGESEL(IDLE)
	GOTO	IDLE

	END

;; ****************************************************************************
;;
;;  Copyright (c) Hitoshi Gomi
;;  All rights reserved.
;;
;;  Redistribution and use in source and binary forms, with or without
;;  modification, are permitted provided that the following conditions are met:
;;
;;  1.Redistributions of source code must retain the above copyright notice,
;;    this list of conditions and the following disclaimer.
;;  2.Redistributions in binary form must reproduce the above copyright notice,
;;    this list of conditions and the following disclaimer in the documentation
;;    and/or other materials provided with the distribution.
;;  3.The names of the author may not be used to endorse or promote products
;;    derived from this software without specific prior written permission.
;;
;;  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;;  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;;  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;;  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
;;  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
;;  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
;;  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
;;  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
;;  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
;;  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
;;  POSSIBILITY OF SUCH DAMAGE.
;;
;; ****************************************************************************