2 ;$include xa-g3.equ ;will implement included later on
5 ; Include file for XA-G3 SFR Definitions
6 ; Philips Semiconductors. Revision 1.8, 1/21/97
7 ; $nolist ;will implement this later on
11 acc reg R4L ; for translated 80C51 code
12 dpl reg R6L ; for translated 80C51 code
13 dph reg R6H ; for translated 80C51 code
14 sp reg R7 ; for translated 80C51 code
17 ; SFR byte and bit definitions.
96 scon sfr 420h ; duplicate label for translated 80C51 code.
97 ri bit scon.0 ; duplicate label for translated 80C51 code.
98 ti bit scon.1 ; duplicate label for translated 80C51 code.
99 rb8 bit s0con.2 ; duplicate label for translated 80C51 code.
100 tb8 bit s0con.3 ; duplicate label for translated 80C51 code.
101 ren bit s0con.4 ; duplicate label for translated 80C51 code.
102 sm2 bit s0con.5 ; duplicate label for translated 80C51 code.
103 sm1 bit s0con.6 ; duplicate label for translated 80C51 code.
104 sm0 bit s0con.7 ; duplicate label for translated 80C51 code.
113 sbuf sfr 460h ; duplicate label for translated 80C51 code.
159 swe1 equ 00000001q ; bit masks for software interrupt enables.
174 tclk bit t2con.4 ; duplicate label for translated 80C51 code.
175 rclk bit t2con.5 ; duplicate label for translated 80C51 code.
224 ; Port pin name definitions.
264 ; End of XA-G3 SFR definitions.
265 ;$list ;handle this later on
273 org $8040 ;paulmon2 will jump here on trap #0
275 org $8044 ;paulmon2 will jump here on trap #1
278 org $8084 ;paulmon2 will jump here on timer0 interrupt
281 org $80A0 ;paulmon2 will jump here on uart0 rx interrupt
283 org $80A4 ;paulmon2 will jump here on uart0 tx interrupt
285 org $80A8 ;paulmon2 will jump here on uart1 rx interrupt
287 org $80AC ;paulmon2 will jump here on uart1 tx interrupt
290 org $8100 ;paulmon2 will jump here on software int1
292 org $8104 ;paulmon2 will jump here on software int2
298 request_do_events bit swr2
299 request_context_switch bit swr1
301 max_num_ticks equ 7 ;max time a process can run if other
302 ;processes are waiting in the run queue
305 ;this memory allocation should be done with "ds" directives to
306 ;save space, but for now it's done with "equ" with lots of gaps,
307 ;so that things will stay put and aligned on 16 byte boundries
308 ;where they can be easily viewed with the paulmon2 hex dump. When
309 ;the kernel code is more mature this will have to be changed.
313 rx0_buf_head equ $FB00 ;2 bytes
314 rx0_buf_tail equ $FB02 ;2 bytes
316 rx0_buf equ $FB10 ;rx0_buf_size bytes
318 rx1_buf_head equ $FB40 ;2 bytes
319 rx1_buf_tail equ $FB42 ;2 bytes
321 rx1_buf equ $FB50 ;rx1_buf_size bytes
323 tx0_buf_head equ $FB80 ;2 bytes
324 tx0_buf_tail equ $FB82 ;2 bytes
326 tx0_buf_threshold equ 9
327 tx0_buf equ $FB90 ;tx0_buf_size bytes
329 tx1_buf_head equ $FBC0 ;2 bytes
330 tx1_buf_tail equ $FBC2 ;2 bytes
332 tx1_buf_threshold equ 9
333 tx1_buf equ $FBD0 ;tx1_buf_size bytes
336 current_proc equ $FEF0 ;2 bytes, ptr to proc_table entry of
337 ;currently running process, 0 if none
338 run_queue equ $FEF2 ;2 bytes, pointer to a linked list
339 ;of processes waiting to run
340 sleep_queue equ $FEF4 ;2 bytes, pointer to a linked list
341 ;of sleeping processes
342 time_running equ $FEF6 ;1 byte, number of ticks that the
343 ;current process has run.
344 event_queue equ $FCF0 ;2 bytes, pointer to a linked list
345 ;of pending events (event_table)
348 proc_table equ $FE00 ;(max_num_proc*pt_entry_size) bytes
349 pt_entry_size equ 16 ;16 bytes per table entry
350 pt_usp equ 0 ;2 bytes, User Stack Pointer
351 pt_q_next equ 2 ;2 bytes, ptr to next in queue
352 pt_q_prev equ 4 ;2 bytes, ptr to prev in queue (+pt_q_next)
353 pt_wait equ 6 ;2 bytes, what are we waiting for?
354 pt_wakeup equ 8 ;2 bytes, where do we go on wakeup?
355 pt_prog_addr equ 10 ;2 bytes, location of program header
356 pt_prog_cs equ 12 ;1 byte, code segment of program header
357 pt_priority equ 13 ;1 byte, execution priority
358 pt_pid equ 14 ;1 byte, id num for this process
360 ;need to define a program header, which will include various
361 ;info about the program, including the req'd stack space, max
362 ;expect memory to malloc, range of directly addressed memory
363 ;(which must be saved during a context switch), max number of
364 ;open file descriptors reqd, etc.
366 max_num_event equ 15 ;(max_num_proc*2)
367 event_table equ $FC00 ;(max_num_event*evt_entry_size)
368 evt_entry_size equ 16 ;8 bytes per event entry
369 evt_ticks equ 0 ;# of ticks until event (delta from prev)
370 evt_next equ 2 ;pointer to next event in list
371 evt_function equ 4 ;place to call (0 if evt slot unused)
372 evt_arg1 equ 6 ;optional argument
374 ;the file descriptors will be allocated before the stack in the
375 ;block of memory which is allocated when the process is created.
376 ;each individual process can specify the max number of file
377 ;descriptors that it will require
379 file_desc_table equ $FE80 ;128 bytes
380 fd_entry_size equ 7 ;7 bytes per table entry
381 fd_offset equ 0 ;4 bytes, offset in file
382 fd_flags equ 4 ;1 byte, read/write/append
383 fd_routines equ 5 ;2 bytes, ptr to i/o routines
386 ;single character debug symbols:
387 ; space context switcher is idle
388 ; * contect switcher entered
389 ; # timer interrupt (normal)
390 ; % timer interrupt requested context switch
391 ; ! timer interrupt requested event service
392 ; @ entered event service interrupt routine
393 ; ^ event service function called
394 ; ~ leaving event service interrupt routine
396 ;this timer interrupt routine is responsible for monitoring the
397 ;time that the current process has been running and adjusting the
398 ;process priorities accordingly. It also checks if any events
399 ;in the event queue need to be processed. This code runs at a
400 ;high cpu priority and it runs often, so it should get done as
401 ;quickly as possible... if anything lengthy might need to be done,
402 ;a software interrupt (low priority) should be set, so it can be
403 ;done later on without blocking other important interrupts, such
407 push r0, r1, r2, r3, r4, r5, r6
408 ;other stuff can be added here, such as maintaining a
415 beq ckevt_end ;no events if queue is empty
416 mov r2, [r1+evt_ticks]
418 ;if we get here, there are events pending but it's not
419 ;time to do any of them yet, so we'll just decrement the
420 ;ticks of the first one, which will decrement the effective
421 ;time for all of them since they use a delta-time between
422 ;each entry in the linked list
423 adds.w [r1+evt_ticks], #-1
426 ;if we get here, there is at least one event which needs
427 ;to be serviced... but we won't do that here, just set the
428 ;software interrupt so it can be handled by "do_event"
429 ;setb request_do_events
432 ;call do_events ;not nice, but sw int priority not working
433 setb request_do_events
438 mov r0, #current_proc
441 beq sch_end ;don't bother if no process is running
442 sub.b [r1+pt_priority], #1 ;decrease priority by one
444 movs.b [r1+pt_priority], #0 ;but don't go less than zero
446 mov r0, #time_running
448 adds r3l, #1 ;increment time_running
450 cmp r3l, #max_num_ticks
452 ;if we get here, the currently running process is has been
453 ;using the cpu for at least the max number ticks, so it's
454 ;time to force it to take a rest and let someone else have
458 beq sch_end ;don't preempt if nothing in run queue
460 setb request_context_switch ;make something else run
468 pop r0, r1, r2, r3, r4, r5, r6
472 ;this routine actually calls the event handlers for all
473 ;events which are supposed to happen NOW. The main timer
474 ;routine must decrement the time of the events and arrange
475 ;for this code to be run when any events are supposed to
476 ;happen. The event handler gets the address of the event
479 push r0, r1, r2, r3, r4, r5, r6
486 beq doevt_end ;no events to do if queue is empty
487 mov r2, [r1+evt_ticks]
488 bne doevt_end ;done if event is in the future
489 mov r2, [r1+evt_next]
490 mov [r0], r2 ;remove event from queue
491 mov r2, [r1+evt_function]
492 movs.w [r1+evt_function], #0
495 call [r2] ;call the event handler specified
500 clr request_do_events
501 pop r0, r1, r2, r3, r4, r5, r6
506 ;interrupt receive routine for uart #0
508 push r0, r1, r2, r5, r6
512 mov r0, #rx0_buf_head
513 mov r6, #rx0_buf_tail
516 cjne r1, #rx0_buf+rx0_buf_size, uart_recv_check2
519 ;interrupt receive routine for uart #1
521 push r0, r1, r2, r5, r6
525 mov r0, #rx1_buf_head
526 mov r6, #rx1_buf_tail
529 cjne r1, #rx1_buf+rx1_buf_size, uart_recv_check2
531 ;this remaining code is shared by both uart receive routines
535 ;if we get here, the buffer is full, discard data
537 pop r0, r1, r2, r5, r6
540 mov.b [r1], r5l ;store byte into buffer
541 mov [r0], r1 ;update buffer pointer
542 ;perhaps there should be a way for us to know how much data a
543 ;sleeping process wants to extract from the buffer, so we can
544 ;avoid waking it up until there is at least that much data.
546 pop r0, r1, r2, r5, r6
549 ;interrupt transmit routine for uart #0
551 push r0, r1, r2, r5, r6
553 mov r0, #tx0_buf_tail
554 mov r2, #tx0_buf_head
559 cjne r1, #tx0_buf+tx0_buf_size, uart0_xmit2
564 ;now figure out if we want to wake up processes which are
565 ;waiting to put more data into this buffer
569 add r1, #tx0_buf_size
571 cmp r1, #(tx0_buf_threshold)
572 bcc uart0_xmit_end ;don't wake proc unless over threshold
576 pop r0, r1, r2, r5, r6
578 ;interrupt transmit routine for uart #1
580 push r0, r1, r2, r5, r6
582 mov r0, #tx1_buf_tail
583 mov r2, #tx1_buf_head
588 cjne r1, #tx1_buf+tx1_buf_size, uart1_xmit2
593 ;now figure out if we want to wake up processes which are
594 ;waiting to put more data into this buffer
598 add r1, #tx1_buf_size
600 cmp r1, #tx1_buf_threshold
601 bcc uart1_xmit_end ;don't wake proc unless over threshold
605 pop r0, r1, r2, r5, r6
608 ;this is shared by both transmit interrupt routines
610 ;if we got here, there is no data waiting to transmit
611 ;load the _head pointer with 0, so a routine which loads
612 ;data into the buffer will know to set the ti bit.
614 pop r0, r1, r2, r5, r6
618 ;This routine wakes up any sleeping processes that are waiting
619 ;for i/o in the buffer pointed to by r2. Interrupt routines
620 ;should call here after doing i/o one their buffers, so that
621 ;any processes which are waiting for that i/o will be awakened.
626 beq wkintio_empty ;sleep_queue is empty
627 clr ea ;disable interrupts while we look at the queue
630 beq wkintio_wakeup ;wake proc if waiting for i/o on this buffer
631 mov r1, [r1+pt_q_next]
633 setb ea ;ok for other interrupts now
637 ;if we get here, this sleeping processes was waiting for
638 ;i/o on the buffer pointed to by r2, so let's wake it up.
639 ;d_que_proc: (remove from sleep queue)
640 mov r6, [r1+pt_q_next]
641 push r6 ;save ptr to next in sleep queue
642 mov r5, [r1+pt_q_prev]
644 mov [r6+pt_q_prev], r5
645 ;en_que_proc: (add to run queue)
647 mov [r1+pt_q_prev], r0
649 mov [r1+pt_q_next], r5
651 mov [r5+pt_q_prev], r6
653 adds.b [r1+pt_priority], #5 ;give it a bit of priority boost...
654 ;need to check here for overrange on user priority
655 setb request_context_switch ;get it running asap.
656 ;continue looking for processes waiting on this i/o buffer,
657 ;because there may be more than one, and we need to get them
658 ;all into the run queue so that the context switcher can choose
659 ;the one with the highest priority.
672 ;**************************************************************
679 ;The context switcher is responsible for figuring out which process
680 ;is supposed to run next (looks at pt_priority) and if a context
681 ;change is required this is where it will happen. If any
682 ;interrupt driven part of the kernel wants to make a particular
683 ;process run, it must change the priority for that process and
684 ;then set the software interrupt to run the context switcher later.
686 ;For system calls that want to make their calling process sleep,
687 ;they should jump to the ksleep routine, which will jump to
688 ;"ctsw_begin" to allow something else to run.
690 ;Note: the context switcher doesn't enforce preemptive multitasking...
691 ;a timer routine must count how long the currently running process
692 ;has be running since a context switch ("time_running" variable)
693 ;and if it's being a hog the timer routine that detects it must
694 ;lower the priority and set the software interrupt to cause this
695 ;context switcher to make a different process run. The context
696 ;switcher also depends on interrupt routines to change the
697 ;"pt_priority" field of each process table entry, all the context
698 ;switcher does is pick the process with highest priority. The
699 ;currently running process is always switched out if there is
700 ;something in the run queue, even if the item in the run queue
701 ;has a lower priority.
705 pushu r0, r1, r2, r3, r4, r5, r6 ;save registers
706 pop r2, r3, r4 ;get pc and psw
707 pushu r2, r3, r4 ;save them on user stack
717 ;find the process on the run queue with highest priority
720 beq ctsw_queue_empty ;run_queue is empty
721 movs r2, #0 ;r2 is proc w/ highest priority
722 mov r3l, #0 ;r3l is highest priority we've seen
723 clr ea ;disable interrupts while we look at the
724 ;run queue, since interrupt routines will be
725 ;changing priorities to influence which process
728 cmp [r1+pt_priority], r3l
730 ;if we get here, this process is the highest
731 ;priority so far, so forget about any other
732 mov r3l, [r1+pt_priority]
735 mov r1, [r1+pt_q_next]
737 ;now r2 points to the process which should be running...
738 ;check to see if "current_proc" is valid... if it is zero then
739 ;nothing was running (a sys call probably put the process to
740 ;sleep). If "current_proc" has a valid process pointer, then we
741 ;need to save that process's context (apart from the pushed regs)
742 ;and put that process back into the run queue
743 mov r0, #current_proc
747 movs.w [r1+pt_wakeup], #0 ;wants to go back to user mode
749 call en_que_proc ;put back into run queue
751 ;now it's time to switch to the new process that is about
754 call d_que_proc ;remove new proc from run queue
756 mov r0, #time_running
757 movs.b [r0], #0 ;reset "time_running"
758 mov r0, #current_proc
759 mov [r0], r1 ;set "current_proc" to new process
760 ;we may need to return to executing the process in user mode,
761 ;or we may need to jump to a location in the kernel (to continue
762 ;a system call). If pt_wakup for this process is not zero, then
763 ;some kernel routine caused this process to sleep or be preempted
764 ;and wants to do more work before finally returning to the user
765 ;mode and running the program again. Usually this is due to a
766 ;system call which needed to put the process to sleep while
768 mov r6, [r1+pt_wakeup]
771 movs.w [r1+pt_wakeup], #0 ;don't do this again unless set again
774 clr request_context_switch
780 popu r0, r1, r2, r3, r4, r5, r6
784 ;if we get here, there was no process waiting in the run
785 ;queue. However, the currently running process isn't
786 ;normally in the run queue, so check if there was a process
787 ;running... if so reset its time slice and let it continue
788 mov r0, #current_proc
790 beq ctsw_idle ;idle if nothing was running
791 mov r0, #time_running
794 ;if we get here, the run queue is empty and there in no
795 ;process running now, so we just keep looping inside the
796 ;context switcher until something appears in the run queue
798 ;need to make a special user-mode idle process... someday
799 ;that could put the processor into idle-mode to save power
807 ;This "ksleep" routine causes the current process to sleep, afterwhich
808 ;it jumps to the context switcher, which tries to pick something else to
809 ;run, if anything is ready. This code should only be called from
810 ;within the service code of system calls, which often times need to
811 ;make their calling process sleep. R3 should have a value to stuff
812 ;into "pt_wait" for the process. ksleep should be CALLed, because
813 ;it will arrange for the scheduler to return back to the kernel code
814 ;which called ksleep. The call to ksleep should be followed by a
815 ;single NOP, for jump alignment. Note, before calling ksleep some mechanism
816 ;to wake the sleeping process back up again must be in place... there
817 ;is nothing within ksleep which arranges for the process to become
818 ;awake again, though in the case of i/o the value in r3 should cause it
819 ;to wake up of the i/o interrupt routine checks for that value.
823 mov r0, #current_proc
824 mov r1, [r0] ;get pointer to process
827 movs.w [r0], #0 ;no current process anymore
831 mov [r1+pt_wait], r3 ;record what we're waiting for
832 pop r5, r6 ;get return address
833 adds r6, #1 ;make sure return addr is word addr
835 mov [r1+pt_wakeup], r6 ;set return addr when it wakes up
836 adds.b [r1+pt_priority], #3 ;increase priority a bit
837 ;need to check here for overrange on user priority
839 call en_que_proc ;add it to the sleep queue
844 jmp ctsw_begin ;scheduler will pick a new
845 ;process to run (or run idle loop)
852 ;all system calls should run at the same priority as the
853 ;context switcher (level #1), but the code in paulmon2
854 ;sets the trap priority to #8... need to change that.
855 mov.b pswh, #10000001q
856 pushu r0, r1, r2, r3, r4, r5, r6 ;save registers
857 pop r4, r5, r6 ;get pc and psw
858 pushu r4, r5, r6 ;save them on user stack
862 ;r0 is number of ticks to sleep, from caller
863 mov r4, #wake_process
864 mov r2, #current_proc
865 mov r5, [r2] ;"wake_process" will need to know which proc
867 mov r3, #0 ;no other routines will need to know it's sleeping
875 popu r0, r1, r2, r3, r4, r5, r6
878 ;do_events is supposed to call here when the process is supposed
879 ;to wake up again... all we have to do it remove the process
880 ;from the sleep queue, add it to the run queue, and set the
881 ;software interrupt for the context switcher so it will select
882 ;a new process to run, and it will likely be this one since we
883 ;gave the priority a boost when it was put to sleep.
888 ;r1 should point to event table entry created earlier
890 mov r1, [r1+evt_arg1] ;now r1 points to process entry
891 call d_que_proc ;remove it from the sleep queue
893 call en_que_proc ;add it to the run queue
895 ;should really test the priority of this waking process and
896 ;the priority of the currently running one, and only do the
897 ;context switch if the waking one has a higher priority
898 setb request_context_switch
904 ;transmit the character in r4l
906 mov.b pswh, #10000001q
907 pushu r0, r1, r2, r3, r4, r5, r6 ;save registers
908 pop r3, r5, r6 ;get pc and psw
909 pushu r3, r5, r6 ;save them on user stack
914 mov r0, #tx0_buf_head
915 mov r2, #tx0_buf_tail
919 ;if we get here, the transmit buffer is empty and the interrupt
920 ;routine cleared the ti bit so no more interrupts are expected
921 setb ti_0 ;give the transmitter a kick-start
923 mov [r0], r1 ;restore buffer pointers to useful values
927 cjne r1, #tx0_buf+tx0_buf_size, sc_cout3
932 ;if we get here, the buffer is full, so let's just wait
933 ;in a loop... eventually this routine will be replaced
934 ;with "write" which will put the process to sleep and set
935 ;the pt_wait so the context switcher will return here when
936 ;there is space available. How do we save the variables
937 ;associated with the system call?? Push them onto the
938 ;user's stack for this process?
945 br sc_cout_begin ;try again now that the intr i/o woke us up
947 mov.b [r1], r4l ;put data into buffer
948 mov [r0], r1 ;update pointer
955 popu r0, r1, r2, r3, r4, r5, r6
965 ;this function adds an event to the event queue. These
966 ;parameters are required:
967 ; r0 number of ticks until the event should occur
968 ; r4 function to call when event happens (not zero!!)
969 ; r5 optional argument
970 ;the event list uses delta time between each entry, so that
971 ;the timer interrupt will only have to deal with the front
972 ;of the queue... but it makes adding an entry here much more
973 ;difficult, but we only have to do this once whereas the
974 ;timer will have to look at the queue however many times the
975 ;evt_ticks value specifies.
977 ;the first thing to do is find a empty place in the table
980 mov r2, #max_num_event
983 mov r3, [r1+evt_function]
985 add r1, #evt_entry_size
986 djnz r2, aevt_find_slot
987 ;if we get here there were no slots available, which is a
991 ;now that we've located the slot we'll use, let's dump the
992 ;data into it before making a mistake and reusing one of the
993 ;registers that are holding the input parameters
994 mov [r1+evt_ticks], r0 ;store time value
995 mov [r1+evt_function], r4 ;store function to be called
996 mov [r1+evt_arg1], r5 ;store argument
997 ;now we've got to add this event into the linked list in
998 ;the correct time sequence position, and also subtract the
999 ;sum of all the previous evt_ticks values from this one.
1000 mov r6, #event_queue
1002 bne aevt_check_first
1003 ;if we get here, it's easy because the list is empty...
1005 movs.w [r1+evt_next], #0
1009 ;it may be the case that our new event is supposed to happen
1010 ;before the events that are already on the queue
1011 cmp r0, [r2+evt_ticks]
1014 mov [r1+evt_next], r2
1016 ;now that we've added an entry (at r1), we need to adjust the
1017 ;evt_ticks values stored in the entry that comes after it.
1018 mov r3, [r1+evt_next] ;r3 points to next event after ours
1019 mov r4, [r1+evt_ticks]
1020 sub [r3+evt_ticks], r4
1024 ;r4 is the adjusted value of ticks for our event as we progress
1025 ;through the linked list
1026 mov r4, [r1+evt_ticks]
1028 sub r4, [r2+evt_ticks]
1029 mov r3, [r2+evt_next]
1031 cmp r4, [r3+evt_ticks]
1034 jmp aevt_search_loop
1036 ;r2 points to the event before the place where we will add
1037 ;and r3 points to the place after it in the list
1038 mov [r2+evt_next], r1
1039 mov [r1+evt_next], r3
1040 mov [r1+evt_ticks], r4
1041 bne aevt_adjust ;adjust rest of list if it exists
1048 save_context: ;process pointer in r1
1050 mov [r1+pt_usp], r0 ;save user stack pointer
1051 ;save any directly addressed memory range
1052 ;assigned to this process
1055 restore_context: ;process pointer in r1
1057 mov usp, r0 ;restore usp to value for new process
1058 ;copy contents of directly addresses memory
1065 ;This routine removes a process (at r1) from a process queue
1066 ;(run/sleep). This might be better as an assembler macro, but
1067 ;it's in a subroutine because of the difficulty of figuring out
1068 ;how to correctly manipulate the pointers of a doubly-linked list
1070 mov r6, [r1+pt_q_next] ;<-- should be macro (begin)
1071 mov r5, [r1+pt_q_prev]
1073 mov [r6+pt_q_prev], r5 ;<--- should be macro (end)
1076 ;this routine takes a process (at r1) and adds it to a queue, where
1077 ;the location of the head of the queue is in r0. This is more or
1078 ;less the opposite of "d_que_proc" except that the new process is
1079 ;always added to the head of the list.
1081 mov [r1+pt_q_prev], r0 ;<-- should be macro (begin)
1083 mov [r1+pt_q_next], r5
1084 lea r6, r1+pt_q_next
1085 mov [r5+pt_q_prev], r6
1086 mov [r0], r1 ;<--- should be macro (end)
1089 ;pt_usp equ 0 ;2 bytes, User Stack Pointer
1090 ;pt_q_next equ 2 ;2 bytes, ptr to next in queue
1091 ;pt_q_prev equ 4 ;2 bytes, ptr to prev in queue (+pt_q_next)
1094 ;this routine creates a new process, and returns a pointer to it
1095 ;in r1. If the process can't be created, r1 is zero. Data about
1096 ;the new process is required:
1097 ; r4 = location of executable program
1098 ; r5 = location for user stack (eventually malloc will do this)
1103 mov r2, #max_num_proc
1106 beq cproc_found_slot ;ok to use this process entry
1107 add r1, #pt_entry_size
1108 djnz r2, cproc_find_slot
1109 ;if we get here, there is no more room in the process
1110 ;table so we can't create a new process.
1115 ;first initialize the various kernel data required
1118 call en_que_proc ;add it to the run queue
1119 mov.b [r1+pt_priority], #100 ;start at priority 100
1120 movs.w [r1+pt_wait], #0
1121 movs.w [r1+pt_wakeup], #0
1122 ;next we need to set up the process's context
1124 sub r5, #26 ;advance r5 to context storage area
1126 movs.w [r5+], #0 ;segment select is 0
1127 movs.w [r5+], #0 ;es is 0
1128 movs.w [r5+], #0 ;ds is 0
1129 movs.w [r5+], #0 ;psw is zero (user mode)
1130 movs.w [r5+], #0 ;run is page zero only (for now)
1131 mov [r5+], r4 ;set return addr to beginning
1132 ;could create initial values for the registers here...
1136 ;user stack while in kernel mode:
1146 ; -16 program counter (lsw)
1147 ; -18 program counter (msb)
1148 ; -20 program status word
1151 ; -26 ssel <-- usp points here
1158 cjne r6, #0, clear_memory_loop
1162 mov r7, #sys_stack ;set sys stack pointer
1164 ;initialize serial port buffers
1165 mov r0, #rx0_buf_head
1166 mov.w [r0], #rx0_buf
1167 mov r0, #rx0_buf_tail
1168 mov.w [r0], #rx0_buf
1169 mov r0, #tx0_buf_head
1171 mov r0, #tx0_buf_tail
1173 mov r0, #rx1_buf_head
1174 mov.w [r0], #rx1_buf
1175 mov r0, #rx1_buf_tail
1176 mov.w [r0], #rx1_buf
1177 mov r0, #tx1_buf_head
1179 mov r0, #tx1_buf_tail
1181 ;clear serial port flags
1186 ;initialize interrupts
1187 mov.b swe, #255 ;allow all software interrupts
1188 mov.b ipa0, #10010000q ;priority #9: timer0 interrupt
1189 mov.b ipa4, #10011001q ;priority #9: uart0 rx and tx interrupts
1190 mov.b ipa5, #10011001q ;priority #9: uart1 rx and tx interrupts
1191 mov.b ieh, #00001111q ;enable uart interrupts
1192 ;set up timer 0 to 16-bit auto reload (10 ms)
1193 clr et0 ;no timer 0 interrupt yet
1194 clr tr0 ;stop timer 0
1196 mov.b rtl0, #low 37798 ;set 10 ms rate
1197 mov.b rth0, #high 37798
1198 movs.b tl0, #0 ;zero timer
1200 and.b tmod, #11110000q ;set for 16 bit auto reload
1203 ;now set up the variables so we're in idle mode
1204 mov r0, #current_proc
1205 movs.w [r0], #0 ;set no process running
1207 movs.b [r0], #0 ;all queues empty
1208 mov r0, #sleep_queue
1210 mov r0, #time_running
1212 ;initialize pt_usp of all processes to zero, so that
1213 ;there won't appear to be any processes
1218 mov r2, #max_num_proc
1220 movs.w [r1+pt_usp], #0
1221 add r1, #pt_entry_size
1222 djnz r2, clr_proc_tbl_loop
1224 mov r1, #event_table
1225 mov r2, #max_num_event
1227 movs.w [r1+evt_function], #0
1228 add r1, #evt_entry_size
1229 djnz r2, clr_events_loop
1231 ;now create process table entries for the test programs
1234 call create_process ;create process for program1
1237 call create_process ;create process for program2
1240 ;call create_process ;create process for program3
1244 ;now how can we get into user mode for the first time???
1245 ;normally the kernel is entered by an interrupt (hardware
1246 ;or software) or a trap (sys call), so we get back to a
1247 ;user program with reti... but the first time has to be
1248 ;handled a bit differently
1250 call d_que_proc ;remove new proc to run from run queue
1251 call restore_context
1252 mov r0, #time_running
1253 movs.b [r0], #0 ;reset "time_running"
1254 mov r0, #current_proc
1255 mov [r0], r1 ;set "current_proc" to new process
1259 popu r0, r1, r2 ;r0=psw, r1=pc_high, r2=pc_low
1260 popu r3, r4, r5 ;extract r0/r2 reg from stack
1261 popu r3, r4, r5, r6 ;extract r3/r6 reg from stack
1262 mov cs, r1l ;set code segment
1263 clr tf0 ;clear timer0 flag
1264 setb tr0 ;start timer0
1265 setb et0 ;enable timer0 interrupt
1268 mov pswh, r0h ;this makes the switch to user mode
1269 ;and lower priority to 0 (from max)
1270 jmp [r2] ;jump to the user program
1286 ;unix system calls: (which ones to try first??)
1287 ;process mgt: fork, exec/execve, exit, wait/wait4
1288 ;file i/o: open, read, write, select, ioclt, lseek, close, fcntl
1289 ;signals: kill, signal, alarm, pause
1290 ;pipes: pipe, dup/dup2
1291 ;network/ipc: socket, bind, connect/listen/accept, sendto, recvfrom
1292 ;network/ipc: socketpair, getsockname/getpeername, setsockopt/getsockopt
1293 ;filesystems: chroot/chdir, link, unlink, mkdir, rmdir,
1294 ;filesystems: chown/chmod, stat, rename, truncate
1299 ;**************************************************************
1301 ;** User Programs **
1303 ;**************************************************************
1321 pstr1: MOVC r4l,[r6+]
1337 phex_b: ADD.B R4L,#58
1345 phex_c: ADD.B R4L,#58
1363 ;program #1 prints "Paul" to serial port #0, with a
1364 ;fairly long delay between each printing
1372 str_paul: db "Paul",0
1374 pgm2_speed equ 10 ;number of ticks between led blinks
1377 ;program #2 blinks the LEDs and prints a "." to the
1378 ;serial port #0 after every sequence of blinks.
1380 or.b p1cfga, #$C0 ;p1.6 and p1.7 outputs
1416 test_str: db "This_is_a_test", 0