	.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 -> -23,x
;         result -> -21,x
;  temp_heap_ptr -> -19,x
;      last_task -> -17,x
;           temp -> -15,x
; temp_heap_size -> -14,x
;      next_task -> -12,x
;              i -> -10,x
_main::
	pshx
	tfr s,x
	leas -30,sp
; /*	RLPOS: RLPotter Operating System
; 
; 		   Version 0.3 for the 68HC12D60A microcontroller
; 		   by Ryan Potter
; 		   ryan@rlpotter.com
; 	
; 	
; 	v0.3 April 12, 2003:
; 		   - added a beginning shell user interface.
; 		   - added a resource control block and 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;
; 		   - 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.
; 		   
; 	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)
; 		3) kernel takes up too large a fraction of the processing time
; */
; 	
; 	
; 	
; #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 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[4];				// 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 -17,x
; 	 unsigned int next_task = 0;
	ldd #0
	std -12,x
; 	 unsigned char *temp_heap_ptr, temp;
; 	 
; 	 
; 	 // INITIALIZE GLOBAL VARIABLES
; 	 extern int _bss_end, _textmode;
; 	 extern unsigned int current;		   	 		
; 	 extern unsigned long int system_tick;
; 	  
; 	  
; 	 _textmode = 1;
	ldd #1
	std __textmode
; 	 current = 0;
	ldd #0
	std _current
; 	 system_tick = 0;
	ldy #L4
	movw 0,y,-4,x
	movw 2,y,-2,x
	ldy #_system_tick
	movw -4,x,0,y
	movw -2,x,2,y
	ldd #0
	std -10,x
	bra L8
L5:
	ldd #9
	ldy -10,x
	emul
	addd #_task
	xgdy
	ldd -10,x
	stab 0,y
	ldd #9
	ldy -10,x
	emul
	addd #_task+1
	xgdy
	ldab #4
	stab 0,y
	ldd #9
	ldy -10,x
	emul
	addd #_task+2
	xgdy
	ldab #255
	stab 0,y
	ldd #9
	ldy -10,x
	emul
	addd #_task+3
	xgdy
	ldd #0
	std 0,y
	ldd #9
	ldy -10,x
	emul
	addd #_task+5
	xgdy
	ldd #0
	std 0,y
	ldd #9
	ldy -10,x
	emul
	addd #_task+7
	xgdy
	ldd #0
	std 0,y
L6:
	ldd -10,x
	addd #1
	std -10,x
L8:
; 	 
; 	 
; 	 // initialize the task structures
; 	 for (i=0; i<numtasks; i++) {
	ldd -10,x
	cpd #3
	blo L5
; 	 	 task[i].id = i;
;  	 	 task[i].state = finished;
; 		 task[i].priority = 255;
; 		 task[i].heap_ptr = NULL;
; 		 task[i].heap_size = 0;
; 		 task[i].frame_ptr = NULL;
; 	 }
; 	 
; 	 
; 	 
; 	 
;  	 // 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 = 0x87;			  	 	// enable RTI at 263.44 msec.
	ldab #135
	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+450
	std 0,sp
	ldd #__bss_end
	jsr __NewHeap
; 	 for (i=0; i<numtasks; i++)
	ldd #0
	std -10,x
	bra L18
L15:
	ldd #50
	jsr _malloc
	std -26,x
	ldd #9
	ldy -10,x
	emul
	addd #_task+3
	xgdy
	ldd -26,x
	std 0,y
L16:
	ldd -10,x
	addd #1
	std -10,x
L18:
	ldd -10,x
	cpd #3
	blo L15
; 	 	 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 = 0;
	clr _task+2
; 	 task[0].state = pending;
	ldab #1
	stab _task+1
; 	 
; 	 // print the opening comments
; 	 puts("rlpOS v0.3\n\n");
	ldd #L22
	jsr _puts
	lbra L24
L23:
; 
; 	 
; 
; 	 
; 	 // MULTITASKING KERNEL: round-robin
; 	 while(1) {	 
; 	 	
; 		/* No interrupts allowed inside of main().  Only allowed
; 		   inside of non-critical sections of tasks */ 
; 	 	INTR_OFF();	
		sei

; 		
; 		
; 		// set the current task id
; 		current = next_task;
	movw -12,x,_current
	ldd #9
	ldy _current
	emul
	addd #_task+1
	xgdy
	ldab 0,y
	clra
	std -23,x
	lbeq L27
	ldd -23,x
	cpd #1
	beq L31
	ldd -23,x
	cpd #2
	beq L34
	ldd -23,x
	cpd #3
	lbeq L27
	ldd -23,x
	cpd #4
	lbeq L27
	lbra L26
X0:
; 		
; 		// DISPATCH, or otherwise deal with the current task
; 		switch (task[current].state) {
; 			   case idle:			   // skip task
; 					break;
L31:
; 			   case pending:		   // ready and waiting to run
; 			   		task[current].state = running;
	ldd #9
	ldy _current
	emul
	addd #_task+1
	xgdy
	ldab #2
	stab 0,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
; 					//putchar('F');
; 					//putchar(current + 48);
; 					task[current].state = idle;				// task finished
	ldd #9
	ldy _current
	emul
	addd #_task+1
	xgdy
	clr 0,y
; 			   		break;
	lbra L27
L34:
; 			   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 #9
	ldy _current
	emul
	addd #_task+5
	xgdy
	ldy 0,y
	sty -14,x
; 					temp_heap_ptr = task[current].heap_ptr;
	ldd #9
	ldy _current
	emul
	addd #_task+3
	xgdy
	ldy 0,y
	sty -19,x
	ldd #0
	std -10,x
	bra L40
L37:
	ldd -10,x
	addd -19,x
	xgdy
	ldab 0,y
	stab -15,x
	ldd _main_frame_ptr
	subd -14,x
	std -28,x
	ldd -10,x
	addd -28,x
	xgdy
	ldab -15,x
	stab 0,y
L38:
	ldd -10,x
	addd #1
	std -10,x
L40:
; 			   		for (i=0; i<temp_heap_size; i++) {
	ldd -10,x
	cpd -14,x
	blo L37
; 						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 #9
	ldy _current
	emul
	addd #_task+7
	xgdy
	ldy 0,y
	sty _temp_task_frame_ptr
; 					asm("LDS _temp_task_frame_ptr");
		LDS _temp_task_frame_ptr

; 					asm("RTI");
		RTI

; 					//putchar('&');
; 			   		break;
	bra L27
X1:
; 			   case waiting:   		   // waiting on a resource
; 			   		break;
; 			   case finished:		   // done running until later
; 			   		break;
L26:
; 			   default:	  			   // shouldn't happen, but, error if so.
; 			   		puts("KERNEL: task status error\n");
	ldd #L44
	jsr _puts
; 			   		exit(1);
	ldd #1
	jsr _exit
; 		}
L27:
; 		
; 		
; 		
; 		// REENTRY POINT after either 1) task finishes, or 2) RTI
; 		// deal with status setting/resetting here
; 		if (task[current].state == idle)
	ldd #9
	ldy _current
	emul
	addd #_task+1
	xgdy
	tst 0,y
	bne L45
; 		   task[current].state = pending;
	ldd #9
	ldy _current
	emul
	addd #_task+1
	xgdy
	ldab #1
	stab 0,y
L45:
; 		
; 		
; 		
; 		// update task counters
; 		last_task=current;
	ldy _current
	sty -17,x
; 		next_task++;
	ldd -12,x
	addd #1
	std -12,x
; 		if (next_task >= numtasks)
	ldd -12,x
	cpd #3
	blo L49
; 	 	   next_task = 0;
	ldd #0
	std -12,x
L49:
L24:
	lbra L23
X2:
; 		
; 		   
; 		
; 		// diagnostic
; 		//putchar(':');
; 		
; 	 }	// end 'while(1)'
; 	 
; 	 return 0;
	ldd #0
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('I');
; 	 //putchar(current + 48);
; 	 //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 #9
	ldy _current
	emul
	addd #_task+7
	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 #9
	ldy _current
	emul
	addd #_task+5
	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 #9
	ldy _current
	emul
	std -16,x
	addd #_task+3
	xgdy
	ldd 0,y
	jsr _realloc
	std -18,x
	ldd -16,x
	addd #_task+3
	xgdy
	ldd -18,x
	std 0,y
; 	 local_thp = task[current].heap_ptr;	
	ldd #9
	ldy _current
	emul
	addd #_task+3
	xgdy
	ldy 0,y
	sty -14,x
; 
; 	 if (local_thp == NULL) {
	ldd -14,x
	bne L57
; 	 	puts("out of heap space!");
	ldd #L59
	jsr _puts
; 		exit(0);
	ldd #0
	jsr _exit
; 	 }
L57:
	ldd #0
	std -10,x
	bra L63
L60:
	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
L61:
	ldd -10,x
	addd #1
	std -10,x
L63:
; 	 
; 	 // 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 L60
; 		*(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 #L64
	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
; 	 //putchar(')');
; 	 
; 	 
; 	 
; 	 // 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 4640");
		JMP 4640

; 	 
; 
; 	 // 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.  */
; 	 	 
; }
L51:
	tfr x,s
	pulx
	.dbline 0 ; func end
	rti
L64:
	.word 0,1
L59:
	.byte 'o,'u,'t,32,'o,'f,32,'h,'e,'a,'p,32,'s,'p,'a,'c
	.byte 'e,33,0
L44:
	.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
L22:
	.byte 'r,'l,'p,'O,'S,32,'v,48,46,51,10,10,0
L4:
	.word 0,0

