	.module rlpos.c
	.area data
_task_ptr::
	.blkw 1
	.area idata
	.word _shell
	.area data
	.blkw 1
	.area idata
	.word _task1
	.area data
	.blkw 1
	.area idata
	.word _task2
	.area data
_main_frame_ptr::
	.blkb 2
	.area idata
	.word 0
	.area data
_main_frame_x_ptr::
	.blkb 2
	.area idata
	.word 0
	.area data
_temp_task_frame_ptr::
	.blkb 2
	.area idata
	.word 0
	.area data
	.area text
;  lreg1 -> -4,x
;  lreg2 -> -8,x
;          ?temp -> -26,x
;          ?temp -> -26,x
;          ?temp -> -26,x
;         result -> -24,x
;  temp_heap_ptr -> -22,x
;      last_task -> -20,x
;           temp -> -18,x
; temp_heap_size -> -17,x
; priority_check -> -15,x
;       deadline -> -14,x
;              i -> -10,x
_main::
	pshx
	tfr s,x
	leas -32,sp
; /*	RLPOS: RLPotter Operating System
; 
; 		   Version 0.5 for the 68HC12D60A microcontroller
; 		   by Ryan Potter
; 		   ryan@rlpotter.com
; 	
; 	
; 		   
; 	v0.5 April 16, 2003: 8759 bytes
; 		   - added priority preemption
; 		   - finished kernel task state switcher 
; 		   - it is now officially a legitimate
; 		   	 	rate monotonic
; 				priority preemptive
; 				multitasking
; 				Real Time Operating System  :)
; 		   - made kernel.c and semlib.c consistent with the 
; 		   	 rest of the kernel
; 		   - included the early framework for a task message system
; 			 
; 	v0.4 April 13, 2003: 8096 bytes
; 		   - gerneralized the shell command-line input parser: 
; 		   	 cmd <arg1, arg2, ... argn> (up to 32 chars)
; 		   - added kernel and shell functions
; 		   
; 	v0.3 April 12, 2003: 6155 bytes
; 		   - added a beginning shell user interface.
; 		   - added a resource control block and basic semaphore functions.
; 		   - added basic kernel functions
; 		   
; 	v0.2 April 9, 2003:
; 	  	   - able to round-robin with RTI interrupt.
; 		   - bonified/certified multitasking with 3 tasks. :)
; 		   
; 	v0.1 April 3, 2003:
; 	  	   - able to round-robin without interrupts.
; 		   - not multitasking, really.
; 		   
; 	v0.0 started April 1, 2003; 0 bytes
; 		   - no idea where to start.
; 		   - don't want to look at anyone else's work. ;0)
; 
; 
; 
; 	
; 	Architecture/C assumptions:
; 		1) 'D' register is the accumulator
; 		2) 'X' register points to the top of the current stack
; 		3) 'Y' register is for general use in indexed operations
; 		4) the heap grows upward and mem segments allocated by malloc,
; 		   realloc, and calloc are linear and contiguous.
; 		5) the stack grows downwards, and there is no boundary checking.
; 		   
; 	Potential problem areas:
; 		1) 'running' section of the kernel get's compiled using extra
; 		   push and pop instructions
; 		2) run out of ram (global + stack + heap)
; 		
; */
; 	
; 	
; 	
; #include <912d60.h>
; #include <stdio.h>
; #include <stdlib.h>
; #include "kernel.h"
; 
; 
; 
; 
; // FUNCTION PROTOTYPES
; void RTI_handler(void);
; void shell(void);
; void task1(void);
; void task2(void);
; void (*task_ptr[])(void) = {&shell, &task1, &task2};
; 
; 
; 
; 
; // GLOBAL VARIABLES
; unsigned char *main_frame_ptr = NULL;	   	// bottom of main() frame
; unsigned char *main_frame_x_ptr = NULL;		// top of main() frame (x-reg ptr)
; unsigned char *temp_task_frame_ptr = NULL;	// temp CCR pointer for RTI
; 
; 
; extern unsigned int current;		   	 		// current task id number
; extern unsigned long int system_tick;
; 
; 
; // task control block
; extern struct task_block {
; 	 	unsigned char id; 			  		// ID of task
; 		enum task_state state;				// State
; 		unsigned char priority;				// Priority
; 		unsigned long int period_tick;		// for determining if deadline is up
; 		unsigned int interrupt_msg_box;		// flags for pending interrupts
; 		enum message_box message;			// misc flags
; 		unsigned char message_data[8];		// data for misc_msg_box flags
; 	 	unsigned char *heap_ptr;			// heap addr while not current task
; 		unsigned int heap_size;				// heap size
; 		unsigned char *frame_ptr;			// CCR pointer
; 		};
; 
; 
; // resource control block
; extern struct resource_block {
; 		unsigned char id;	   				// ID of resource
; 		enum resource_state state;			// State (busy, free...)
; 		unsigned char user;					// Current resource user/owner
; 		signed char queue[3];				// Tasks waiting on resource
; 		unsigned char queue_pos;			// Next free spot in queue
; 		};
; 		
; 		
; extern struct task_block task[numtasks];
; extern struct resource_block resource[numresources];
; 
; 
; 
; 
; 
; 
; main() {
; 
; 	 // LOCAL VARIABLES
; 	 int result;
; 	 unsigned int i, temp_heap_size;
; 	 unsigned int last_task = 0;
	ldd #0
	std -20,x
; 	 unsigned char *temp_heap_ptr, temp, priority_check;
; 	 unsigned long int deadline;
; 	 
; 	 extern int _bss_end, _textmode;
; 	 extern unsigned int current;		   	 		
; 	 extern unsigned long int system_tick;
; 	  
; 	 _textmode = 1;	 	  // maps '\n' to "CR/LF" for Windows terminals
	ldd #1
	std __textmode
; 	 current = 0;  	 	  // start the shell first
	ldd #0
	std _current
	ldd #0
	std -10,x
	lbra L7
L4:
	ldd #24
	ldy -10,x
	emul
	addd #_task
	xgdy
	ldd -10,x
	stab 0,y
	ldd #24
	ldy -10,x
	emul
	addd #_task+1
	xgdy
	clr 0,y
	ldd #24
	ldy -10,x
	emul
	addd #_task+2
	xgdy
	ldab #254
	stab 0,y
	ldd #24
	ldy -10,x
	emul
	addd #_task+3
	xgdy
	pshy ; spill
	ldy #L11
	movw 0,y,-4,x
	movw 2,y,-2,x
	puly ; reload
	movw -4,x,0,y
	movw -2,x,2,y
	ldd #24
	ldy -10,x
	emul
	addd #_task+18
	xgdy
	ldd #0
	std 0,y
	ldd #24
	ldy -10,x
	emul
	addd #_task+20
	xgdy
	ldd #0
	std 0,y
	ldd #24
	ldy -10,x
	emul
	addd #_task+22
	xgdy
	ldd #0
	std 0,y
L5:
	ldd -10,x
	addd #1
	std -10,x
L7:
; 
; 	 
; 	 
; 	 // initialize the task structures
; 	 for (i=0; i<numtasks; i++) {
	ldd -10,x
	cpd #3
	lblo L4
	ldd #0
	std -10,x
	bra L18
L15:
	ldd #7
	ldy -10,x
	emul
	addd #_resource
	xgdy
	ldd -10,x
	stab 0,y
	ldd #7
	ldy -10,x
	emul
	addd #_resource+1
	xgdy
	clr 0,y
	ldd #7
	ldy -10,x
	emul
	addd #_resource+2
	xgdy
	clr 0,y
L16:
	ldd -10,x
	addd #1
	std -10,x
L18:
; 	 	 task[i].id = i;
;  	 	 task[i].state = idle;
; 		 task[i].priority = 254;  	  	  // low priority
; 		 task[i].period_tick = 0;
; 		 task[i].heap_ptr = NULL;
; 		 task[i].heap_size = 0;
; 		 task[i].frame_ptr = NULL;
; 	 	 }
; 	 
; 	 // initialize the resource structures
; 	 for (i=0; i<numresources; i++) {
	ldd -10,x
	cpd #2
	blo L15
; 	 	 resource[i].id = i;
; 		 resource[i].state = notbusy;
; 		 resource[i].user = NULL;
; 		 }
; 	 
; 	 // debug code:
; 	 task[1].priority = 5;
	ldab #5
	stab _task+24+2
; 	 task[2].priority = 3;
	ldab #3
	stab _task+48+2
; 	 
; 	 
;  	 // save SP value for use in the RTI
; 	 asm("TFR s,d");
		TFR s,d

; 	 asm("STD _main_frame_ptr");
		STD _main_frame_ptr

; 	 // save X reg value for use in the RTI
; 	 asm("TFR x,d");
		TFR x,d

; 	 asm("STD _main_frame_x_ptr");
		STD _main_frame_x_ptr

; 	 
; 	 
; 	 
; 	 
; 	 // START/INITIALIZE the os
; 	 
; 	 // set up the interrupts
; 	 INTR_OFF();			  	 	// disable all maskable interrupts
		sei

; 	 RTICTL = 0x86;			  	 	// enable RTI at 131.72 msec.
	ldab #134
	stab 0x14
; 	 RTIFLG = 0x80;					// clear real time interrupt flag
	ldab #128
	stab 0x15
; 	 
; 	 // create and initialize the heap (for dynamic (runtime) var allocation)
; 	 _NewHeap(&_bss_end, (char *)(&_bss_end) + (initial_heap_size*numtasks));
	ldd #__bss_end+1050
	std 0,sp
	ldd #__bss_end
	jsr __NewHeap
; 	 for (i=0; i<numtasks; i++)
	ldd #0
	std -10,x
	bra L29
L26:
	ldd #50
	jsr _malloc
	std -28,x
	ldd #24
	ldy -10,x
	emul
	addd #_task+18
	xgdy
	ldd -28,x
	std 0,y
L27:
	ldd -10,x
	addd #1
	std -10,x
L29:
	ldd -10,x
	cpd #3
	blo L26
; 	 	 task[i].heap_ptr = malloc(50);
; 	 
; 	 // set up the serial port
; 	 setbaud(BAUD38K);	 	  // actually running at 19K baud due to xtal speed
	ldd #13
	jsr _setbaud
; 	 
; 	 // initialize the shell
; 	 task[0].priority = 255;  // low until serial port is interrupt driven
	ldab #255
	stab _task+2
; 	 task[0].state = pending;
	ldab #1
	stab _task+1
; 	 
; 	 // print the opening comments
; 	 puts("rlpOS v0.5\n\n");
	ldd #L33
	jsr _puts
; 
; 	 
; 
; 	 
; 	 // MULTITASKING KERNEL: Priority Premptive
;  	 system_tick = 0;
	ldy #L11
	movw 0,y,-4,x
	movw 2,y,-2,x
	ldy #_system_tick
	movw -4,x,0,y
	movw -2,x,2,y
	lbra L35
L34:
; 	 while(1) {	 
; 	 	
; 		/* No interrupts allowed inside of main().  Only allowed
; 		   inside of non-critical sections of tasks */ 
; 	 	INTR_OFF();	
		sei

; 		
; 		
; 		
; 		// REENTRY POINT after either 1) task finishes, or 2) RTI
; 		last_task=current;
	ldy _current
	sty -20,x
	ldd #0
	std -10,x
	lbra L40
L37:
; 		
; 		
; 		/* this is the Round-Robin way of things
; 		if (task[current].state == idle)
; 		   task[current].state = pending;
; 		next_task++;     
; 		if (next_task >= numtasks)
; 	 	   next_task = 0;
; 		current = next_task;*/
; 		
; 		
; 		
; 		
; 		// RT PRIORITY BLOCK:
; 		// set the current task id based on priority and deadline
; 		
; 		// determine if the deadline is up for idle tasks
; 		/* deadline, is equal to period plus an initial time (t0) reference
; 		   (t0 = system_tick).  Period = priority + 1.  
; 		   Changing the state from idle to pending occurs here.  */
; 		for (i=0; i<numtasks; i++) {
; 			if (task[i].state == idle) {
	ldd #24
	ldy -10,x
	emul
	addd #_task+1
	xgdy
	tst 0,y
	bne L41
; 			   deadline = task[i].period_tick + (task[i].priority + 1);
	ldd #24
	ldy -10,x
	emul
	std -26,x
	addd #_task+2
	xgdy
	ldab 0,y
	clra
	addd #1
	jsr __d2lreg2
	ldd -26,x
	addd #_task+3
	xgdy
	movw 0,y,-4,x
	movw 2,y,-2,x
	jsr __ladd
	leay -14,x
	movw -4,x,0,y
	movw -2,x,2,y
; 			   if (system_tick >= deadline) {
	leay -14,x
	movw 0,y,-8,x
	movw 2,y,-6,x
	ldy #_system_tick
	movw 0,y,-4,x
	movw 2,y,-2,x
	jsr __lcmp
	blo L46
; 			   	  task[i].state = pending; 	  // change state at deadline
	ldd #24
	ldy -10,x
	emul
	addd #_task+1
	xgdy
	ldab #1
	stab 0,y
; 				  //puts("promote");
; 			   	  } 	
L46:
; 				}
L41:
L38:
	ldd -10,x
	addd #1
	std -10,x
L40:
	ldd -10,x
	cpd #3
	lblo L37
; 			 // if (task[i].message) {} 		
; 			 }			
; 		
; 		
; 		// set current = highest priority pending/running task.
; 		priority_check = 255;   	  	  // lowest possible
	ldab #255
	stab -15,x
	ldd #0
	std -10,x
	bra L52
L49:
; 		for (i=0; i<numtasks; i++) {
	ldd #24
	ldy -10,x
	emul
	std -26,x
	addd #_task+1
	xgdy
	ldab 0,y
	cmpb #1
	beq L57
	ldd -26,x
	addd #_task+1
	xgdy
	ldab 0,y
	cmpb #2
	bne L53
L57:
; 			if ((task[i].state == pending) || (task[i].state == running)) {
; 			   //puts("tp1");
; 			   if (task[i].priority <= priority_check) {
	ldd #24
	ldy -10,x
	emul
	addd #_task+2
	xgdy
	ldab 0,y
	cmpb -15,x
	bhi L58
; 				 current = i;
	movw -10,x,_current
; 				 priority_check = task[i].priority;
	ldd #24
	ldy -10,x
	emul
	addd #_task+2
	xgdy
	ldab 0,y
	stab -15,x
; 				 //puts("tp2"); 
; 			   	 }
L58:
; 			   }
L53:
L50:
	ldd -10,x
	addd #1
	std -10,x
L52:
	ldd -10,x
	cpd #3
	blo L49
	ldd #24
	ldy _current
	emul
	addd #_task+1
	xgdy
	ldab 0,y
	clra
	std -26,x
	lbeq L63
	ldd -26,x
	cpd #1
	beq L67
	ldd -26,x
	cpd #2
	beq L71
	ldd -26,x
	cpd #3
	lbeq L63
	ldd -26,x
	cpd #4
	lbeq L63
	lbra L62
X0:
; 			 }
; 		
; 		
; 		
; 		// DISPATCH, or otherwise deal with the current task
; 		
; 		//printf("task[%d].state = %d\n", current, task[current].state);
; 		
; 		switch (task[current].state) {
; 			   case idle:			   // skip task
; 					break;
L67:
; 			   case pending:		   // ready and waiting to run
; 			   		task[current].state = running;
	ldd #24
	ldy _current
	emul
	addd #_task+1
	xgdy
	ldab #2
	stab 0,y
; 					task[current].period_tick = system_tick;
	ldd #24
	ldy _current
	emul
	addd #_task+3
	xgdy
	pshy ; spill
	ldy #_system_tick
	movw 0,y,-4,x
	movw 2,y,-2,x
	puly ; reload
	movw -4,x,0,y
	movw -2,x,2,y
; 					/*putchar('S');
; 					  putchar(current + 48); */
; 			   		(*task_ptr[current])();	 	  			// start the task
	ldd _current
	lsld
	addd #_task_ptr
	xgdy
	ldy 0,y
	jsr 0,y
; 					task[current].state = idle;				// task finished
	ldd #24
	ldy _current
	emul
	addd #_task+1
	xgdy
	clr 0,y
; 					/*putchar('F');
; 					  putchar(current + 48);  */				
; 			   		break;
	lbra L63
L71:
; 			   case running:			// interrupted. continue running.
; 					// restore the task heap to the stack
; 					/*putchar('R');
; 					  putchar(current + 48); */
; 					temp_heap_size = task[current].heap_size;
	ldd #24
	ldy _current
	emul
	addd #_task+20
	xgdy
	ldy 0,y
	sty -17,x
; 					temp_heap_ptr = task[current].heap_ptr;
	ldd #24
	ldy _current
	emul
	addd #_task+18
	xgdy
	ldy 0,y
	sty -22,x
	ldd #0
	std -10,x
	bra L77
L74:
	ldd -10,x
	addd -22,x
	xgdy
	ldab 0,y
	stab -18,x
	ldd _main_frame_ptr
	subd -17,x
	std -30,x
	ldd -10,x
	addd -30,x
	xgdy
	ldab -18,x
	stab 0,y
L75:
	ldd -10,x
	addd #1
	std -10,x
L77:
; 			   		for (i=0; i<temp_heap_size; i++) {
	ldd -10,x
	cpd -17,x
	blo L74
; 						temp = *(temp_heap_ptr + i);
; 						*(main_frame_ptr - temp_heap_size + i) = temp;
; 						//putchar('.');
; 						}
; 					
; 					// restore context and run
; 					temp_task_frame_ptr = task[current].frame_ptr;
	ldd #24
	ldy _current
	emul
	addd #_task+22
	xgdy
	ldy 0,y
	sty _temp_task_frame_ptr
; 					asm("LDS _temp_task_frame_ptr");
		LDS _temp_task_frame_ptr

; 					asm("RTI");
		RTI

; 			   		break;
	bra L63
X1:
; 			   case waiting:   		   // waiting on a resource
; 			   		break;
; 			   case finished:		   // done running until later
; 			   		break;
L62:
; 			   default:	  			   // shouldn't happen, but, error if so.
; 			   		puts("KERNEL: task status error\n");
	ldd #L81
	jsr _puts
; 			   		exit(1);
	ldd #1
	jsr _exit
; 				}	// end switch
L63:
L35:
	lbra L34
X2:
; 		
; 	 		}		// end 'while(1)'
; 	 
; 	 exit(0);
	ldd #0
	jsr _exit
; 	 
; }			 		// end main()
L3:
	tfr x,s
	pulx
	.dbline 0 ; func end
	rts
;  lreg1 -> -4,x
;  lreg2 -> -8,x
;          ?temp -> -16,x
;      local_thp -> -14,x
;     frame_size -> -12,x
;              i -> -10,x
_RTI_handler::
	pshx
	tfr s,x
	leas -20,sp
; 
; 
; 
; #pragma interrupt_handler RTI_handler()
; 
; void RTI_handler(void) {
; 
; 	 size_t frame_size;
; 	 unsigned int i;
; 	 unsigned char *local_thp;
; 	 
; 	 
; 	 //ACKNOWLEDGE THE INTERRUPT
; 	 INTR_OFF();  	   	// redundant. automatically done by the processor
		sei

; 	 RTIFLG = 0x0080;  	// acknowledge/clear the interrupt
	ldab #128
	stab 0x15
; 	 
; 	 
; 	 //putchar('.');
; 	 
; 	 
; 	 // SET THE FRAME POINTER for the interupted task.
; 	 // should point to the CCR entry on the stack.
; 	 asm("TFR x,d");  		  	  	  	 // start of RTI stack
		TFR x,d

; 	 asm("ADDD #2");				  	 // adjust to CCR stack entry
		ADDD #2

; 	 asm("STD _temp_task_frame_ptr");	 // put into task_frame_ptr
		STD _temp_task_frame_ptr

; 	 task[current].frame_ptr = temp_task_frame_ptr;
	ldd #24
	ldy _current
	emul
	addd #_task+22
	xgdy
	ldd _temp_task_frame_ptr
	std 0,y
; 
; 	 
; 	 
; 	 //PLACE CURRENT TASK CONTEXT ONTO THE HEAP
; 	 // determine size of heap segment needed
; 	 frame_size = main_frame_ptr - temp_task_frame_ptr; 	// always positive
	ldd _main_frame_ptr
	subd _temp_task_frame_ptr
	std -12,x
; 	 task[current].heap_size = (int)frame_size;
	ldd #24
	ldy _current
	emul
	addd #_task+20
	xgdy
	ldd -12,x
	std 0,y
; 	 
; 	 // reallocate heap memory
; 	 task[current].heap_ptr = realloc(task[current].heap_ptr, frame_size);
	movw -12,x,0,sp
	ldd #24
	ldy _current
	emul
	std -16,x
	addd #_task+18
	xgdy
	ldd 0,y
	jsr _realloc
	std -18,x
	ldd -16,x
	addd #_task+18
	xgdy
	ldd -18,x
	std 0,y
; 	 local_thp = task[current].heap_ptr;	
	ldd #24
	ldy _current
	emul
	addd #_task+18
	xgdy
	ldy 0,y
	sty -14,x
; 
; 	 if (local_thp == NULL) {
	ldd -14,x
	bne L88
; 	 	puts("out of heap space!");
	ldd #L90
	jsr _puts
; 		exit(0);
	ldd #0
	jsr _exit
; 	 	}
L88:
	ldd #0
	std -10,x
	bra L94
L91:
	ldd -10,x
	addd _temp_task_frame_ptr
	xgdy
	ldab 0,y
	pshd ; spill
	ldd -10,x
	addd -14,x
	xgdy
	puld ; reload
	stab 0,y
L92:
	ldd -10,x
	addd #1
	std -10,x
L94:
; 	 
; 	 // transfer task frame to the heap one byte at a time.
; 	 // assumes the heap grows from low to high addr.
; 	 for(i=0; i<frame_size; i++) {
	ldd -10,x
	cpd -12,x
	blo L91
; 		*(local_thp + i) = *(temp_task_frame_ptr + i);
; 		//putchar('.');
; 	 	}
; 	 
; 
; 	 
; 	 // update system time base
; 	 system_tick++;
	ldy #_system_tick
	movw 0,y,-4,x
	movw 2,y,-2,x
	ldy #L95
	movw 0,y,-8,x
	movw 2,y,-6,x
	jsr __ladd
	ldy #_system_tick
	movw -4,x,0,y
	movw -2,x,2,y
; 	 
; 	 
; 	 
; 	 // RETURN: simulate a RTS instruction
; 	 // set stack pointer for main()
; 	 asm("LDS _main_frame_ptr");
		LDS _main_frame_ptr

; 	 asm("LDX _main_frame_x_ptr");
		LDX _main_frame_x_ptr

; 	 /* return to main() at the reentry point
; 	    address must be in decimal!! 
; 	    and must be adjusted after each compilation!!  */
; 	 asm("JMP 4511");
		JMP 4511

; 	 
; 
; 	 // return (0)  -- NOT used 
; 	 /* this ISR should never use the 'return x' command.
; 	    It doesn't make sense since it always interrupts a Task, but
; 	    never returns to it... but to main() instead.  */
; 	 	 
; }
L82:
	tfr x,s
	pulx
	.dbline 0 ; func end
	rti
L95:
	.word 0,1
L90:
	.byte 'o,'u,'t,32,'o,'f,32,'h,'e,'a,'p,32,'s,'p,'a,'c
	.byte 'e,33,0
L81:
	.byte 'K,'E,'R,'N,'E,'L,58,32,'t,'a,'s,'k,32,'s,'t,'a
	.byte 't,'u,'s,32,'e,'r,'r,'o,'r,10,0
L33:
	.byte 'r,'l,'p,'O,'S,32,'v,48,46,53,10,10,0
L11:
	.word 0,0

