+++ /dev/null
-
-;$include xa-g3.equ ;will implement included later on
-
-
-; Include file for XA-G3 SFR Definitions
-; Philips Semiconductors. Revision 1.8, 1/21/97
-; $nolist ;will implement this later on
-
-; Register equates.
-
-acc reg R4L ; for translated 80C51 code
-dpl reg R6L ; for translated 80C51 code
-dph reg R6H ; for translated 80C51 code
-sp reg R7 ; for translated 80C51 code
-
-
-; SFR byte and bit definitions.
-
-bcr sfr 46ah
-btrh sfr 469h
-btrl sfr 468h
-cs sfr 443h
-ds sfr 441h
-es sfr 442h
-
-ieh sfr 427h
-eri0 bit ieh.0
-eti0 bit ieh.1
-eri1 bit ieh.2
-eti1 bit ieh.3
-
-iel sfr 426h
-ex0 bit iel.0
-et0 bit iel.1
-ex1 bit iel.2
-et1 bit iel.3
-et2 bit iel.4
-ea bit iel.7
-
-ipa0 sfr 4a0h
-ipa1 sfr 4a1h
-ipa2 sfr 4a2h
-ipa4 sfr 4a4h
-ipa5 sfr 4a5h
-
-p0 sfr 430h
-p1 sfr 431h
-p2 sfr 432h
-p3 sfr 433h
-p0cfga sfr 470h
-p1cfga sfr 471h
-p2cfga sfr 472h
-p3cfga sfr 473h
-p0cfgb sfr 4f0h
-p1cfgb sfr 4f1h
-p2cfgb sfr 4f2h
-p3cfgb sfr 4f3h
-
-pcon sfr 404h
-idl bit pcon.0
-pd bit pcon.1
-
-pswh sfr 401h
-im0 bit pswh.0
-im1 bit pswh.1
-im2 bit pswh.2
-im3 bit pswh.3
-rs0 bit pswh.4
-rs1 bit pswh.5
-tm bit pswh.6
-sm bit pswh.7
-
-pswl sfr 400h
-z bit pswl.0
-n bit pswl.1
-v bit pswl.2
-ac bit pswl.6
-cy bit pswl.7
-
-psw51 sfr 402h
-
-rth0 sfr 455h
-rth1 sfr 457h
-rtl0 sfr 454h
-rtl1 sfr 456h
-
-s0con sfr 420h
-ri_0 bit s0con.0
-ti_0 bit s0con.1
-rb8_0 bit s0con.2
-tb8_0 bit s0con.3
-ren_0 bit s0con.4
-sm2_0 bit s0con.5
-sm1_0 bit s0con.6
-sm0_0 bit s0con.7
-scon sfr 420h ; duplicate label for translated 80C51 code.
-ri bit scon.0 ; duplicate label for translated 80C51 code.
-ti bit scon.1 ; duplicate label for translated 80C51 code.
-rb8 bit s0con.2 ; duplicate label for translated 80C51 code.
-tb8 bit s0con.3 ; duplicate label for translated 80C51 code.
-ren bit s0con.4 ; duplicate label for translated 80C51 code.
-sm2 bit s0con.5 ; duplicate label for translated 80C51 code.
-sm1 bit s0con.6 ; duplicate label for translated 80C51 code.
-sm0 bit s0con.7 ; duplicate label for translated 80C51 code.
-
-s0stat sfr 421h
-stint0 bit s0stat.0
-oe0 bit s0stat.1
-br0 bit s0stat.2
-fe0 bit s0stat.3
-
-s0buf sfr 460h
-sbuf sfr 460h ; duplicate label for translated 80C51 code.
-s0addr sfr 461h
-s0aden sfr 462h
-
-s1con sfr 424h
-ri_1 bit s1con.0
-ti_1 bit s1con.1
-rb8_1 bit s1con.2
-tb8_1 bit s1con.3
-ren_1 bit s1con.4
-sm2_1 bit s1con.5
-sm1_1 bit s1con.6
-sm0_1 bit s1con.7
-
-s1stat sfr 425h
-stint1 bit s1stat.0
-oe1 bit s1stat.1
-br1 bit s1stat.2
-fe1 bit s1stat.3
-
-s1buf sfr 464h
-s1addr sfr 465h
-s1aden sfr 466h
-
-scr sfr 440h
-
-ssel sfr 403h
-r0seg bit ssel.0
-r1seg bit ssel.1
-r2seg bit ssel.2
-r3seg bit ssel.3
-r4seg bit ssel.4
-r5seg bit ssel.5
-r6seg bit ssel.6
-eswen bit ssel.7
-
-swe sfr 47ah
-swr sfr 42ah
-swr1 bit swr.0
-swr2 bit swr.1
-swr3 bit swr.2
-swr4 bit swr.3
-swr5 bit swr.4
-swr6 bit swr.5
-swr7 bit swr.6
-
-swe1 equ 00000001q ; bit masks for software interrupt enables.
-swe2 equ 00000010q
-swe3 equ 00000100q
-swe4 equ 00001000q
-swe5 equ 00010000q
-swe6 equ 00100000q
-swe7 equ 01000000q
-
-t2con sfr 418h
-cprl2 bit t2con.0
-ct2 bit t2con.1
-tr2 bit t2con.2
-exen2 bit t2con.3
-tclk0 bit t2con.4
-rclk0 bit t2con.5
-tclk bit t2con.4 ; duplicate label for translated 80C51 code.
-rclk bit t2con.5 ; duplicate label for translated 80C51 code.
-exf2 bit t2con.6
-tf2 bit t2con.7
-
-t2mod sfr 419h
-dcen bit t2mod.0
-t2oe bit t2mod.1
-t2rd bit t2mod.2
-tclk1 bit t2mod.4
-rclk1 bit t2mod.5
-
-th2 sfr 459h
-tl2 sfr 458h
-t2caph sfr 45bh
-t2capl sfr 45ah
-
-tcon sfr 410h
-it0 bit tcon.0
-ie0 bit tcon.1
-it1 bit tcon.2
-ie1 bit tcon.3
-tr0 bit tcon.4
-tf0 bit tcon.5
-tr1 bit tcon.6
-tf1 bit tcon.7
-
-th0 sfr 451h
-th1 sfr 453h
-tl0 sfr 450h
-tl1 sfr 452h
-tmod sfr 45ch
-tstat sfr 411h
-t0oe bit tstat.0
-t0rd bit tstat.1
-t1oe bit tstat.2
-t1rd bit tstat.3
-
-wdcon sfr 41fh
-wdtof bit wdcon.1
-wdrun bit wdcon.2
-pre0 bit wdcon.5
-pre1 bit wdcon.6
-pre2 bit wdcon.7
-
-wdl sfr 45fh
-wfeed1 sfr 45dh
-wfeed2 sfr 45eh
-
-
-; Port pin name definitions.
-
-a4d0 bit P0.0
-a5d1 bit P0.1
-a6d2 bit P0.2
-a7d3 bit P0.3
-a8d4 bit P0.4
-a9d5 bit P0.5
-a10d6 bit P0.6
-a11d7 bit P0.7
-
-a0 bit P1.0
-wrh bit P1.0
-a1 bit P1.1
-a2 bit P1.2
-a3 bit P1.3
-rxd1 bit P1.4
-txd1 bit P1.5
-t2 bit P1.6
-t2ex bit P1.7
-
-a12d8 bit P2.0
-a13d9 bit P2.1
-a14d10 bit P2.2
-a15d11 bit P2.3
-a16d12 bit P2.4
-a17d13 bit P2.5
-a18d14 bit P2.6
-a19d15 bit P2.7
-
-rxd0 bit P3.0
-txd0 bit P3.1
-int0 bit P3.2
-int1 bit P3.3
-t0 bit P3.4
-t1 bit P3.5
-busw bit P3.5
-wrl bit P3.6
-rd bit P3.7
-
-; End of XA-G3 SFR definitions.
-;$list ;handle this later on
-
-
-
-
-trap_delay equ 0
-trap_cout equ 1
-
- org $8040 ;paulmon2 will jump here on trap #0
- jmp syscall_sleep
- org $8044 ;paulmon2 will jump here on trap #1
- jmp syscall_cout
-
- org $8084 ;paulmon2 will jump here on timer0 interrupt
- jmp timer0
-
- org $80A0 ;paulmon2 will jump here on uart0 rx interrupt
- jmp uart0_recv
- org $80A4 ;paulmon2 will jump here on uart0 tx interrupt
- jmp uart0_xmit
- org $80A8 ;paulmon2 will jump here on uart1 rx interrupt
- jmp uart1_recv
- org $80AC ;paulmon2 will jump here on uart1 tx interrupt
- jmp uart1_xmit
-
- org $8100 ;paulmon2 will jump here on software int1
- jmp context_switch
- org $8104 ;paulmon2 will jump here on software int2
- jmp do_events
-
- org $8120
- jmp poweron
-
-request_do_events bit swr2
-request_context_switch bit swr1
-
-max_num_ticks equ 7 ;max time a process can run if other
- ;processes are waiting in the run queue
-
-
-;this memory allocation should be done with "ds" directives to
-;save space, but for now it's done with "equ" with lots of gaps,
-;so that things will stay put and aligned on 16 byte boundries
-;where they can be easily viewed with the paulmon2 hex dump. When
-;the kernel code is more mature this will have to be changed.
-
-sys_stack equ $FB00
-
-rx0_buf_head equ $FB00 ;2 bytes
-rx0_buf_tail equ $FB02 ;2 bytes
-rx0_buf_size equ $20
-rx0_buf equ $FB10 ;rx0_buf_size bytes
-
-rx1_buf_head equ $FB40 ;2 bytes
-rx1_buf_tail equ $FB42 ;2 bytes
-rx1_buf_size equ $20
-rx1_buf equ $FB50 ;rx1_buf_size bytes
-
-tx0_buf_head equ $FB80 ;2 bytes
-tx0_buf_tail equ $FB82 ;2 bytes
-tx0_buf_size equ $20
-tx0_buf_threshold equ 9
-tx0_buf equ $FB90 ;tx0_buf_size bytes
-
-tx1_buf_head equ $FBC0 ;2 bytes
-tx1_buf_tail equ $FBC2 ;2 bytes
-tx1_buf_size equ $20
-tx1_buf_threshold equ 9
-tx1_buf equ $FBD0 ;tx1_buf_size bytes
-
-
-current_proc equ $FEF0 ;2 bytes, ptr to proc_table entry of
- ;currently running process, 0 if none
-run_queue equ $FEF2 ;2 bytes, pointer to a linked list
- ;of processes waiting to run
-sleep_queue equ $FEF4 ;2 bytes, pointer to a linked list
- ;of sleeping processes
-time_running equ $FEF6 ;1 byte, number of ticks that the
- ;current process has run.
-event_queue equ $FCF0 ;2 bytes, pointer to a linked list
- ;of pending events (event_table)
-
-max_num_proc equ 14
-proc_table equ $FE00 ;(max_num_proc*pt_entry_size) bytes
-pt_entry_size equ 16 ;16 bytes per table entry
-pt_usp equ 0 ;2 bytes, User Stack Pointer
-pt_q_next equ 2 ;2 bytes, ptr to next in queue
-pt_q_prev equ 4 ;2 bytes, ptr to prev in queue (+pt_q_next)
-pt_wait equ 6 ;2 bytes, what are we waiting for?
-pt_wakeup equ 8 ;2 bytes, where do we go on wakeup?
-pt_prog_addr equ 10 ;2 bytes, location of program header
-pt_prog_cs equ 12 ;1 byte, code segment of program header
-pt_priority equ 13 ;1 byte, execution priority
-pt_pid equ 14 ;1 byte, id num for this process
-
- ;need to define a program header, which will include various
- ;info about the program, including the req'd stack space, max
- ;expect memory to malloc, range of directly addressed memory
- ;(which must be saved during a context switch), max number of
- ;open file descriptors reqd, etc.
-
-max_num_event equ 15 ;(max_num_proc*2)
-event_table equ $FC00 ;(max_num_event*evt_entry_size)
-evt_entry_size equ 16 ;8 bytes per event entry
-evt_ticks equ 0 ;# of ticks until event (delta from prev)
-evt_next equ 2 ;pointer to next event in list
-evt_function equ 4 ;place to call (0 if evt slot unused)
-evt_arg1 equ 6 ;optional argument
-
-;the file descriptors will be allocated before the stack in the
-;block of memory which is allocated when the process is created.
-;each individual process can specify the max number of file
-;descriptors that it will require
-
-file_desc_table equ $FE80 ;128 bytes
-fd_entry_size equ 7 ;7 bytes per table entry
-fd_offset equ 0 ;4 bytes, offset in file
-fd_flags equ 4 ;1 byte, read/write/append
-fd_routines equ 5 ;2 bytes, ptr to i/o routines
-
-
-;single character debug symbols:
-; space context switcher is idle
-; * contect switcher entered
-; # timer interrupt (normal)
-; % timer interrupt requested context switch
-; ! timer interrupt requested event service
-; @ entered event service interrupt routine
-; ^ event service function called
-; ~ leaving event service interrupt routine
-
-;this timer interrupt routine is responsible for monitoring the
-;time that the current process has been running and adjusting the
-;process priorities accordingly. It also checks if any events
-;in the event queue need to be processed. This code runs at a
-;high cpu priority and it runs often, so it should get done as
-;quickly as possible... if anything lengthy might need to be done,
-;a software interrupt (low priority) should be set, so it can be
-;done later on without blocking other important interrupts, such
-;as low-level i/o.
-
-timer0:
- push r0, r1, r2, r3, r4, r5, r6
- ;other stuff can be added here, such as maintaining a
- ;system clock...
- ;mov r4l, #'#'
-check_events:
- mov r0, #event_queue
- clr ea
- mov r1, [r0]
- beq ckevt_end ;no events if queue is empty
- mov r2, [r1+evt_ticks]
- beq ckevt_do_em
- ;if we get here, there are events pending but it's not
- ;time to do any of them yet, so we'll just decrement the
- ;ticks of the first one, which will decrement the effective
- ;time for all of them since they use a delta-time between
- ;each entry in the linked list
- adds.w [r1+evt_ticks], #-1
- br ckevt_end
-ckevt_do_em:
- ;if we get here, there is at least one event which needs
- ;to be serviced... but we won't do that here, just set the
- ;software interrupt so it can be handled by "do_event"
- ;setb request_do_events
- ;mov r4l, #'!'
- ;call cout
- ;call do_events ;not nice, but sw int priority not working
- setb request_do_events
-ckevt_end:
- setb ea
- nop
-schedule:
- mov r0, #current_proc
- clr ea
- mov r1, [r0]
- beq sch_end ;don't bother if no process is running
- sub.b [r1+pt_priority], #1 ;decrease priority by one
- bcc sch_priority_ok
- movs.b [r1+pt_priority], #0 ;but don't go less than zero
-sch_priority_ok:
- mov r0, #time_running
- mov r3l, [r0]
- adds r3l, #1 ;increment time_running
- mov [r0], r3l
- cmp r3l, #max_num_ticks
- bl sch_end
- ;if we get here, the currently running process is has been
- ;using the cpu for at least the max number ticks, so it's
- ;time to force it to take a rest and let someone else have
- ;a chance.
- mov r0, #run_queue
- mov r1, [r0]
- beq sch_end ;don't preempt if nothing in run queue
- ;mov r4l, #'%'
- setb request_context_switch ;make something else run
-sch_end:
- setb ea
- ;cmp r4l, #'!'
- ;beq t0_skip_print
- ;call cout
- nop
-t0_skip_print:
- pop r0, r1, r2, r3, r4, r5, r6
- reti
-
-
- ;this routine actually calls the event handlers for all
- ;events which are supposed to happen NOW. The main timer
- ;routine must decrement the time of the events and arrange
- ;for this code to be run when any events are supposed to
- ;happen. The event handler gets the address of the event
- ;in r1.
-do_events:
- push r0, r1, r2, r3, r4, r5, r6
-do_events_begin:
- ;mov r4l, #'@'
- ;call cout
-doevt_loop:
- mov r0, #event_queue
- mov r1, [r0]
- beq doevt_end ;no events to do if queue is empty
- mov r2, [r1+evt_ticks]
- bne doevt_end ;done if event is in the future
- mov r2, [r1+evt_next]
- mov [r0], r2 ;remove event from queue
- mov r2, [r1+evt_function]
- movs.w [r1+evt_function], #0
- ;mov r4l, #'^'
- ;call cout
- call [r2] ;call the event handler specified
- br doevt_loop
-doevt_end:
- ;mov r4l, #'~'
- ;call cout
- clr request_do_events
- pop r0, r1, r2, r3, r4, r5, r6
- reti
-
-
-
-;interrupt receive routine for uart #0
-uart0_recv:
- push r0, r1, r2, r5, r6
- clr ri_0
- mov r5l, s0buf
- mov r2, #rx0_buf
- mov r0, #rx0_buf_head
- mov r6, #rx0_buf_tail
- mov r1, [r0]
- adds r1, #1
- cjne r1, #rx0_buf+rx0_buf_size, uart_recv_check2
- mov r1, #rx0_buf
- br uart_recv_check2
-;interrupt receive routine for uart #1
-uart1_recv:
- push r0, r1, r2, r5, r6
- clr ri_1
- mov r5l, s1buf
- mov r2, #rx1_buf
- mov r0, #rx1_buf_head
- mov r6, #rx1_buf_tail
- mov r1, [r0]
- adds r1, #1
- cjne r1, #rx1_buf+rx1_buf_size, uart_recv_check2
- mov r1, #rx1_buf
-;this remaining code is shared by both uart receive routines
-uart_recv_check2:
- cmp r1, [r6]
- bne uart_recv_ok
- ;if we get here, the buffer is full, discard data
- call wakeup_intr_io
- pop r0, r1, r2, r5, r6
- reti
-uart_recv_ok:
- mov.b [r1], r5l ;store byte into buffer
- mov [r0], r1 ;update buffer pointer
- ;perhaps there should be a way for us to know how much data a
- ;sleeping process wants to extract from the buffer, so we can
- ;avoid waking it up until there is at least that much data.
- call wakeup_intr_io
- pop r0, r1, r2, r5, r6
- reti
-
-;interrupt transmit routine for uart #0
-uart0_xmit:
- push r0, r1, r2, r5, r6
- clr ti_0
- mov r0, #tx0_buf_tail
- mov r2, #tx0_buf_head
- mov r1, [r0]
- cmp r1, [r2]
- beq uart_no_xmit
- adds r1, #1
- cjne r1, #tx0_buf+tx0_buf_size, uart0_xmit2
- mov r1, #tx0_buf
-uart0_xmit2:
- mov.b s0buf, [r1]
- mov [r0], r1
- ;now figure out if we want to wake up processes which are
- ;waiting to put more data into this buffer
- mov r1, [r2]
- sub r1, [r0]
- bcc uart0_xmit3
- add r1, #tx0_buf_size
-uart0_xmit3:
- cmp r1, #(tx0_buf_threshold)
- bcc uart0_xmit_end ;don't wake proc unless over threshold
- mov r2, #tx0_buf
- call wakeup_intr_io
-uart0_xmit_end:
- pop r0, r1, r2, r5, r6
- reti
-;interrupt transmit routine for uart #1
-uart1_xmit:
- push r0, r1, r2, r5, r6
- clr ti_1
- mov r0, #tx1_buf_tail
- mov r2, #tx1_buf_head
- mov r1, [r0]
- cmp r1, [r2]
- beq uart_no_xmit
- adds r1, #1
- cjne r1, #tx1_buf+tx1_buf_size, uart1_xmit2
- mov r1, #tx1_buf
-uart1_xmit2:
- mov.b s1buf, [r1]
- mov [r0], r1
- ;now figure out if we want to wake up processes which are
- ;waiting to put more data into this buffer
- mov r1, [r2]
- sub r1, [r0]
- bcc uart1_xmit3
- add r1, #tx1_buf_size
-uart1_xmit3:
- cmp r1, #tx1_buf_threshold
- bcc uart1_xmit_end ;don't wake proc unless over threshold
- mov r2, #tx1_buf
- call wakeup_intr_io
-uart1_xmit_end:
- pop r0, r1, r2, r5, r6
- reti
-
-;this is shared by both transmit interrupt routines
-uart_no_xmit:
- ;if we got here, there is no data waiting to transmit
- ;load the _head pointer with 0, so a routine which loads
- ;data into the buffer will know to set the ti bit.
- movs.w [r2], #0
- pop r0, r1, r2, r5, r6
- reti
-
-
-;This routine wakes up any sleeping processes that are waiting
-;for i/o in the buffer pointed to by r2. Interrupt routines
-;should call here after doing i/o one their buffers, so that
-;any processes which are waiting for that i/o will be awakened.
-
-wakeup_intr_io:
- mov r0, #sleep_queue
- mov r1, [r0]
- beq wkintio_empty ;sleep_queue is empty
- clr ea ;disable interrupts while we look at the queue
-wkintio_loop:
- cmp [r1+pt_wait], r2
- beq wkintio_wakeup ;wake proc if waiting for i/o on this buffer
- mov r1, [r1+pt_q_next]
- bne wkintio_loop
- setb ea ;ok for other interrupts now
-wkintio_empty:
- ret
-wkintio_wakeup:
- ;if we get here, this sleeping processes was waiting for
- ;i/o on the buffer pointed to by r2, so let's wake it up.
- ;d_que_proc: (remove from sleep queue)
- mov r6, [r1+pt_q_next]
- push r6 ;save ptr to next in sleep queue
- mov r5, [r1+pt_q_prev]
- mov [r5], r6
- mov [r6+pt_q_prev], r5
- ;en_que_proc: (add to run queue)
- mov r0, #run_queue
- mov [r1+pt_q_prev], r0
- mov r5, [r0]
- mov [r1+pt_q_next], r5
- lea r6, r1+pt_q_next
- mov [r5+pt_q_prev], r6
- mov [r0], r1
- adds.b [r1+pt_priority], #5 ;give it a bit of priority boost...
- ;need to check here for overrange on user priority
- setb request_context_switch ;get it running asap.
- ;continue looking for processes waiting on this i/o buffer,
- ;because there may be more than one, and we need to get them
- ;all into the run queue so that the context switcher can choose
- ;the one with the highest priority.
- pop r1
- cmp r1, #0
- bne wkintio_loop
- setb ea
- ret
-
-
-
-
-
-
-
-;**************************************************************
-
-
-
-
-
-
-;The context switcher is responsible for figuring out which process
-;is supposed to run next (looks at pt_priority) and if a context
-;change is required this is where it will happen. If any
-;interrupt driven part of the kernel wants to make a particular
-;process run, it must change the priority for that process and
-;then set the software interrupt to run the context switcher later.
-
-;For system calls that want to make their calling process sleep,
-;they should jump to the ksleep routine, which will jump to
-;"ctsw_begin" to allow something else to run.
-
-;Note: the context switcher doesn't enforce preemptive multitasking...
-;a timer routine must count how long the currently running process
-;has be running since a context switch ("time_running" variable)
-;and if it's being a hog the timer routine that detects it must
-;lower the priority and set the software interrupt to cause this
-;context switcher to make a different process run. The context
-;switcher also depends on interrupt routines to change the
-;"pt_priority" field of each process table entry, all the context
-;switcher does is pick the process with highest priority. The
-;currently running process is always switched out if there is
-;something in the run queue, even if the item in the run queue
-;has a lower priority.
-
-
-context_switch:
- pushu r0, r1, r2, r3, r4, r5, r6 ;save registers
- pop r2, r3, r4 ;get pc and psw
- pushu r2, r3, r4 ;save them on user stack
- pushu.b ds
- pushu.b es
- pushu.b ssel
-
-
-ctsw_begin:
- ;mov r4l, #'*'
- ;call cout
-ctsw_retry:
- ;find the process on the run queue with highest priority
- mov r0, #run_queue
- mov r1, [r0]
- beq ctsw_queue_empty ;run_queue is empty
- movs r2, #0 ;r2 is proc w/ highest priority
- mov r3l, #0 ;r3l is highest priority we've seen
- clr ea ;disable interrupts while we look at the
- ;run queue, since interrupt routines will be
- ;changing priorities to influence which process
- ;should be run next
-ctsw_loop:
- cmp [r1+pt_priority], r3l
- bcs ctsw_skip
- ;if we get here, this process is the highest
- ;priority so far, so forget about any other
- mov r3l, [r1+pt_priority]
- mov r2, r1
-ctsw_skip:
- mov r1, [r1+pt_q_next]
- bne ctsw_loop
- ;now r2 points to the process which should be running...
- ;check to see if "current_proc" is valid... if it is zero then
- ;nothing was running (a sys call probably put the process to
- ;sleep). If "current_proc" has a valid process pointer, then we
- ;need to save that process's context (apart from the pushed regs)
- ;and put that process back into the run queue
- mov r0, #current_proc
- mov r1, [r0]
- beq ctsw_switch
- call save_context
- movs.w [r1+pt_wakeup], #0 ;wants to go back to user mode
- mov r0, #run_queue
- call en_que_proc ;put back into run queue
-ctsw_switch:
- ;now it's time to switch to the new process that is about
- ;to start running.
- mov r1, r2
- call d_que_proc ;remove new proc from run queue
- call restore_context
- mov r0, #time_running
- movs.b [r0], #0 ;reset "time_running"
- mov r0, #current_proc
- mov [r0], r1 ;set "current_proc" to new process
- ;we may need to return to executing the process in user mode,
- ;or we may need to jump to a location in the kernel (to continue
- ;a system call). If pt_wakup for this process is not zero, then
- ;some kernel routine caused this process to sleep or be preempted
- ;and wants to do more work before finally returning to the user
- ;mode and running the program again. Usually this is due to a
- ;system call which needed to put the process to sleep while
- ;waiting for I/O.
- mov r6, [r1+pt_wakeup]
- setb ea
- beq ctsw_return
- movs.w [r1+pt_wakeup], #0 ;don't do this again unless set again
- jmp [r6]
-ctsw_return:
- clr request_context_switch
- popu.b ssel
- popu.b es
- popu.b ds
- popu r2, r3, r4
- push r2, r3, r4
- popu r0, r1, r2, r3, r4, r5, r6
- reti
-
-ctsw_queue_empty:
- ;if we get here, there was no process waiting in the run
- ;queue. However, the currently running process isn't
- ;normally in the run queue, so check if there was a process
- ;running... if so reset its time slice and let it continue
- mov r0, #current_proc
- mov r1, [r0]
- beq ctsw_idle ;idle if nothing was running
- mov r0, #time_running
- movs.b [r0], #0
- jmp ctsw_return
- ;if we get here, the run queue is empty and there in no
- ;process running now, so we just keep looping inside the
- ;context switcher until something appears in the run queue
-ctsw_idle:
- ;need to make a special user-mode idle process... someday
- ;that could put the processor into idle-mode to save power
- ;mov r4l, #' '
- ;call cout
- jmp ctsw_retry
-
-
-
-
-;This "ksleep" routine causes the current process to sleep, afterwhich
-;it jumps to the context switcher, which tries to pick something else to
-;run, if anything is ready. This code should only be called from
-;within the service code of system calls, which often times need to
-;make their calling process sleep. R3 should have a value to stuff
-;into "pt_wait" for the process. ksleep should be CALLed, because
-;it will arrange for the scheduler to return back to the kernel code
-;which called ksleep. The call to ksleep should be followed by a
-;single NOP, for jump alignment. Note, before calling ksleep some mechanism
-;to wake the sleeping process back up again must be in place... there
-;is nothing within ksleep which arranges for the process to become
-;awake again, though in the case of i/o the value in r3 should cause it
-;to wake up of the i/o interrupt routine checks for that value.
-
-ksleep:
- clr ea
- mov r0, #current_proc
- mov r1, [r0] ;get pointer to process
- ;cmp r1, #$FE10
- ;beq ksleep_die
- movs.w [r0], #0 ;no current process anymore
- nop
-ksleep_any:
- clr ea
- mov [r1+pt_wait], r3 ;record what we're waiting for
- pop r5, r6 ;get return address
- adds r6, #1 ;make sure return addr is word addr
- and r6, #$FFFE
- mov [r1+pt_wakeup], r6 ;set return addr when it wakes up
- adds.b [r1+pt_priority], #3 ;increase priority a bit
- ;need to check here for overrange on user priority
- mov r0, #sleep_queue
- call en_que_proc ;add it to the sleep queue
- setb ea
- call save_context
- ;cmp r1, #$FE10
- ;beq ksleep_die
- jmp ctsw_begin ;scheduler will pick a new
- ;process to run (or run idle loop)
-
-ksleep_die:
- jmp die
-
-
-syscall_sleep:
- ;all system calls should run at the same priority as the
- ;context switcher (level #1), but the code in paulmon2
- ;sets the trap priority to #8... need to change that.
- mov.b pswh, #10000001q
- pushu r0, r1, r2, r3, r4, r5, r6 ;save registers
- pop r4, r5, r6 ;get pc and psw
- pushu r4, r5, r6 ;save them on user stack
- pushu.b ds
- pushu.b es
- pushu.b ssel
- ;r0 is number of ticks to sleep, from caller
- mov r4, #wake_process
- mov r2, #current_proc
- mov r5, [r2] ;"wake_process" will need to know which proc
- call add_event
- mov r3, #0 ;no other routines will need to know it's sleeping
- call ksleep
- nop
- popu.b ssel
- popu.b es
- popu.b ds
- popu r2, r3, r4
- push r2, r3, r4
- popu r0, r1, r2, r3, r4, r5, r6
- reti
-
- ;do_events is supposed to call here when the process is supposed
- ;to wake up again... all we have to do it remove the process
- ;from the sleep queue, add it to the run queue, and set the
- ;software interrupt for the context switcher so it will select
- ;a new process to run, and it will likely be this one since we
- ;gave the priority a boost when it was put to sleep.
- nop
-wake_process:
- ;mov r4l, #'$'
- ;call cout
- ;r1 should point to event table entry created earlier
- clr ea
- mov r1, [r1+evt_arg1] ;now r1 points to process entry
- call d_que_proc ;remove it from the sleep queue
- mov r0, #run_queue
- call en_que_proc ;add it to the run queue
- setb ea
- ;should really test the priority of this waking process and
- ;the priority of the currently running one, and only do the
- ;context switch if the waking one has a higher priority
- setb request_context_switch
- ret
-
-
-
-
- ;transmit the character in r4l
-syscall_cout:
- mov.b pswh, #10000001q
- pushu r0, r1, r2, r3, r4, r5, r6 ;save registers
- pop r3, r5, r6 ;get pc and psw
- pushu r3, r5, r6 ;save them on user stack
- pushu.b ds
- pushu.b es
- pushu.b ssel
-sc_cout_begin:
- mov r0, #tx0_buf_head
- mov r2, #tx0_buf_tail
- clr ea
- mov r1, [r0]
- bne sc_cout2
- ;if we get here, the transmit buffer is empty and the interrupt
- ;routine cleared the ti bit so no more interrupts are expected
- setb ti_0 ;give the transmitter a kick-start
- mov r1, #tx0_buf
- mov [r0], r1 ;restore buffer pointers to useful values
- mov [r2], r1
-sc_cout2:
- adds r1, #1
- cjne r1, #tx0_buf+tx0_buf_size, sc_cout3
- mov r1, #tx0_buf
-sc_cout3:
- cmp r1, [r2]
- bne sc_cout4
- ;if we get here, the buffer is full, so let's just wait
- ;in a loop... eventually this routine will be replaced
- ;with "write" which will put the process to sleep and set
- ;the pt_wait so the context switcher will return here when
- ;there is space available. How do we save the variables
- ;associated with the system call?? Push them onto the
- ;user's stack for this process?
- setb ea
- mov r3, #tx0_buf
- pushu r4
- call ksleep
- nop
- popu r4
- br sc_cout_begin ;try again now that the intr i/o woke us up
-sc_cout4:
- mov.b [r1], r4l ;put data into buffer
- mov [r0], r1 ;update pointer
- setb ea
- popu.b ssel
- popu.b es
- popu.b ds
- popu r2, r3, r4
- push r2, r3, r4
- popu r0, r1, r2, r3, r4, r5, r6
- reti
-
-
-
-
-
-
-
-
- ;this function adds an event to the event queue. These
- ;parameters are required:
- ; r0 number of ticks until the event should occur
- ; r4 function to call when event happens (not zero!!)
- ; r5 optional argument
- ;the event list uses delta time between each entry, so that
- ;the timer interrupt will only have to deal with the front
- ;of the queue... but it makes adding an entry here much more
- ;difficult, but we only have to do this once whereas the
- ;timer will have to look at the queue however many times the
- ;evt_ticks value specifies.
-add_event:
- ;the first thing to do is find a empty place in the table
- ;to store this event
- mov r1, #event_table
- mov r2, #max_num_event
- clr ea
-aevt_find_slot:
- mov r3, [r1+evt_function]
- beq aevt_found_slot
- add r1, #evt_entry_size
- djnz r2, aevt_find_slot
- ;if we get here there were no slots available, which is a
- ;very bad thing...
- jmp kernel_panic
-aevt_found_slot:
- ;now that we've located the slot we'll use, let's dump the
- ;data into it before making a mistake and reusing one of the
- ;registers that are holding the input parameters
- mov [r1+evt_ticks], r0 ;store time value
- mov [r1+evt_function], r4 ;store function to be called
- mov [r1+evt_arg1], r5 ;store argument
- ;now we've got to add this event into the linked list in
- ;the correct time sequence position, and also subtract the
- ;sum of all the previous evt_ticks values from this one.
- mov r6, #event_queue
- mov r2, [r6]
- bne aevt_check_first
- ;if we get here, it's easy because the list is empty...
- mov [r6], r1
- movs.w [r1+evt_next], #0
- setb ea
- ret
-aevt_check_first:
- ;it may be the case that our new event is supposed to happen
- ;before the events that are already on the queue
- cmp r0, [r2+evt_ticks]
- bcc aevt_search
- mov [r6], r1
- mov [r1+evt_next], r2
-aevt_adjust:
- ;now that we've added an entry (at r1), we need to adjust the
- ;evt_ticks values stored in the entry that comes after it.
- mov r3, [r1+evt_next] ;r3 points to next event after ours
- mov r4, [r1+evt_ticks]
- sub [r3+evt_ticks], r4
- setb ea
- ret
-aevt_search:
- ;r4 is the adjusted value of ticks for our event as we progress
- ;through the linked list
- mov r4, [r1+evt_ticks]
-aevt_search_loop:
- sub r4, [r2+evt_ticks]
- mov r3, [r2+evt_next]
- beq aevt_add
- cmp r4, [r3+evt_ticks]
- bcs aevt_add
- mov r2, r3
- jmp aevt_search_loop
-aevt_add:
- ;r2 points to the event before the place where we will add
- ;and r3 points to the place after it in the list
- mov [r2+evt_next], r1
- mov [r1+evt_next], r3
- mov [r1+evt_ticks], r4
- bne aevt_adjust ;adjust rest of list if it exists
- setb ea
- ret
-
-
-
-
-save_context: ;process pointer in r1
- mov r0, usp
- mov [r1+pt_usp], r0 ;save user stack pointer
- ;save any directly addressed memory range
- ;assigned to this process
- ret
-
-restore_context: ;process pointer in r1
- mov r0, [r1+pt_usp]
- mov usp, r0 ;restore usp to value for new process
- ;copy contents of directly addresses memory
- ;back if necessary
- ret
-
-
-
-
-;This routine removes a process (at r1) from a process queue
-;(run/sleep). This might be better as an assembler macro, but
-;it's in a subroutine because of the difficulty of figuring out
-;how to correctly manipulate the pointers of a doubly-linked list
-d_que_proc:
- mov r6, [r1+pt_q_next] ;<-- should be macro (begin)
- mov r5, [r1+pt_q_prev]
- mov [r5], r6
- mov [r6+pt_q_prev], r5 ;<--- should be macro (end)
- ret
-
-;this routine takes a process (at r1) and adds it to a queue, where
-;the location of the head of the queue is in r0. This is more or
-;less the opposite of "d_que_proc" except that the new process is
-;always added to the head of the list.
-en_que_proc:
- mov [r1+pt_q_prev], r0 ;<-- should be macro (begin)
- mov r5, [r0]
- mov [r1+pt_q_next], r5
- lea r6, r1+pt_q_next
- mov [r5+pt_q_prev], r6
- mov [r0], r1 ;<--- should be macro (end)
- ret
-
-;pt_usp equ 0 ;2 bytes, User Stack Pointer
-;pt_q_next equ 2 ;2 bytes, ptr to next in queue
-;pt_q_prev equ 4 ;2 bytes, ptr to prev in queue (+pt_q_next)
-
-
-;this routine creates a new process, and returns a pointer to it
-;in r1. If the process can't be created, r1 is zero. Data about
-;the new process is required:
-; r4 = location of executable program
-; r5 = location for user stack (eventually malloc will do this)
-
-
-create_process:
- mov r1, #proc_table
- mov r2, #max_num_proc
-cproc_find_slot:
- mov r0, [r1+pt_usp]
- beq cproc_found_slot ;ok to use this process entry
- add r1, #pt_entry_size
- djnz r2, cproc_find_slot
- ;if we get here, there is no more room in the process
- ;table so we can't create a new process.
- movs r1, #0
- ret
-cproc_found_slot:
- push r4, r5
- ;first initialize the various kernel data required
- mov r0, #run_queue
- clr ea
- call en_que_proc ;add it to the run queue
- mov.b [r1+pt_priority], #100 ;start at priority 100
- movs.w [r1+pt_wait], #0
- movs.w [r1+pt_wakeup], #0
- ;next we need to set up the process's context
- pop r4, r5
- sub r5, #26 ;advance r5 to context storage area
- mov [r1+pt_usp], r5
- movs.w [r5+], #0 ;segment select is 0
- movs.w [r5+], #0 ;es is 0
- movs.w [r5+], #0 ;ds is 0
- movs.w [r5+], #0 ;psw is zero (user mode)
- movs.w [r5+], #0 ;run is page zero only (for now)
- mov [r5+], r4 ;set return addr to beginning
- ;could create initial values for the registers here...
- setb ea
- ret
-
-;user stack while in kernel mode:
-; byte
-; offset Variable
-; -2 r6
-; -4 r5
-; -6 r4
-; -8 r3
-; -10 r2
-; -12 r1
-; -14 r0
-; -16 program counter (lsw)
-; -18 program counter (msb)
-; -20 program status word
-; -22 ds
-; -24 es
-; -26 ssel <-- usp points here
-
-
-poweron:
- mov r6, #$F000
-clear_memory_loop:
- movs.w [r6+], #0
- cjne r6, #0, clear_memory_loop
- setb p1.6
- setb p1.7
-
- mov r7, #sys_stack ;set sys stack pointer
- clr ea
- ;initialize serial port buffers
- mov r0, #rx0_buf_head
- mov.w [r0], #rx0_buf
- mov r0, #rx0_buf_tail
- mov.w [r0], #rx0_buf
- mov r0, #tx0_buf_head
- movs.w [r0], #0
- mov r0, #tx0_buf_tail
- movs.w [r0], #0
- mov r0, #rx1_buf_head
- mov.w [r0], #rx1_buf
- mov r0, #rx1_buf_tail
- mov.w [r0], #rx1_buf
- mov r0, #tx1_buf_head
- movs.w [r0], #0
- mov r0, #tx1_buf_tail
- movs.w [r0], #0
- ;clear serial port flags
- clr ri_0
- clr ti_0
- clr ri_1
- clr ti_1
- ;initialize interrupts
- mov.b swe, #255 ;allow all software interrupts
- mov.b ipa0, #10010000q ;priority #9: timer0 interrupt
- mov.b ipa4, #10011001q ;priority #9: uart0 rx and tx interrupts
- mov.b ipa5, #10011001q ;priority #9: uart1 rx and tx interrupts
- mov.b ieh, #00001111q ;enable uart interrupts
- ;set up timer 0 to 16-bit auto reload (10 ms)
- clr et0 ;no timer 0 interrupt yet
- clr tr0 ;stop timer 0
- clr tf0
- mov.b rtl0, #low 37798 ;set 10 ms rate
- mov.b rth0, #high 37798
- movs.b tl0, #0 ;zero timer
- movs.b th0, #0
- and.b tmod, #11110000q ;set for 16 bit auto reload
-
-
- ;now set up the variables so we're in idle mode
- mov r0, #current_proc
- movs.w [r0], #0 ;set no process running
- mov r0, #run_queue
- movs.b [r0], #0 ;all queues empty
- mov r0, #sleep_queue
- movs.b [r0], #0
- mov r0, #time_running
- movs.b [r0], #0
- ;initialize pt_usp of all processes to zero, so that
- ;there won't appear to be any processes
-
- nop
-clr_proc_tbl:
- mov r1, #proc_table
- mov r2, #max_num_proc
-clr_proc_tbl_loop:
- movs.w [r1+pt_usp], #0
- add r1, #pt_entry_size
- djnz r2, clr_proc_tbl_loop
-clr_events:
- mov r1, #event_table
- mov r2, #max_num_event
-clr_events_loop:
- movs.w [r1+evt_function], #0
- add r1, #evt_entry_size
- djnz r2, clr_events_loop
-
-;now create process table entries for the test programs
- mov r4, #program1
- mov r5, #$F000
- call create_process ;create process for program1
- mov r4, #program2
- mov r5, #$EC00
- call create_process ;create process for program2
- mov r4, #program3
- mov r5, #$E800
- ;call create_process ;create process for program3
-
-
-
- ;now how can we get into user mode for the first time???
- ;normally the kernel is entered by an interrupt (hardware
- ;or software) or a trap (sys call), so we get back to a
- ;user program with reti... but the first time has to be
- ;handled a bit differently
- mov r1, #proc_table
- call d_que_proc ;remove new proc to run from run queue
- call restore_context
- mov r0, #time_running
- movs.b [r0], #0 ;reset "time_running"
- mov r0, #current_proc
- mov [r0], r1 ;set "current_proc" to new process
- popu.b ssel
- popu.b es
- popu.b ds
- popu r0, r1, r2 ;r0=psw, r1=pc_high, r2=pc_low
- popu r3, r4, r5 ;extract r0/r2 reg from stack
- popu r3, r4, r5, r6 ;extract r3/r6 reg from stack
- mov cs, r1l ;set code segment
- clr tf0 ;clear timer0 flag
- setb tr0 ;start timer0
- setb et0 ;enable timer0 interrupt
- setb ea
- mov pswl, r0l
- mov pswh, r0h ;this makes the switch to user mode
- ;and lower priority to 0 (from max)
- jmp [r2] ;jump to the user program
-
-
-
-
-kernel_panic:
-die:
- clr ea
- clr ri_0
-die_loop:
- jnb ri_0, die_loop
- clr ri_0
- jmp $120 ;reboot!
-
-
-
-;unix system calls: (which ones to try first??)
-;process mgt: fork, exec/execve, exit, wait/wait4
-;file i/o: open, read, write, select, ioclt, lseek, close, fcntl
-;signals: kill, signal, alarm, pause
-;pipes: pipe, dup/dup2
-;network/ipc: socket, bind, connect/listen/accept, sendto, recvfrom
-;network/ipc: socketpair, getsockname/getpeername, setsockopt/getsockopt
-;filesystems: chroot/chdir, link, unlink, mkdir, rmdir,
-;filesystems: chown/chmod, stat, rename, truncate
-
-
-
-
-;**************************************************************
-;** **
-;** User Programs **
-;** **
-;**************************************************************
-
-
-
-cout:
- trap #trap_cout
- ret
-
-newline:
- push r4l
- mov r4l, #13
- call cout
- mov r4l, #10
- call cout
- pop r4l
- ret
-
-pstr: PUSH r4
-pstr1: MOVC r4l,[r6+]
- beq pstr2
- AND R4L,#0x7F
- CALL cout
- BR pstr1
-pstr2: POP r4
- RET
-
-phex:
-phex8:
- PUSH.B acc
- RL.B R4L,#4
- AND.B R4L,#15
- ADD.B R4L,#246
- BCC phex_b
- ADD.B R4L,#7
-phex_b: ADD.B R4L,#58
- CALL cout
- POP.B acc
-phex1: PUSH.B acc
- AND.B R4L,#15
- ADD.B R4L,#246
- BCC phex_c
- ADD.B R4L,#7
-phex_c: ADD.B R4L,#58
- CALL cout
- POP.B acc
- RET
-
-phex16:
- PUSH.B acc
- MOV.B R4L,dph
- CALL phex
- MOV.B R4L,dpl
- CALL phex
- POP.B acc
- RET
-
-
-
-
-
- ;program #1 prints "Paul" to serial port #0, with a
- ;fairly long delay between each printing
-program1:
- mov r6, #str_paul
- call pstr
- mov r0, #130
- trap #trap_delay
- jmp program1
-
-str_paul: db "Paul",0
-
-pgm2_speed equ 10 ;number of ticks between led blinks
-
- nop
- ;program #2 blinks the LEDs and prints a "." to the
- ;serial port #0 after every sequence of blinks.
-program2:
- or.b p1cfga, #$C0 ;p1.6 and p1.7 outputs
- or.b p1cfgb, #$C0
-pg2_loop:
- ;br pg2_skip_blink
- setb p1.6
- setb p1.7
- mov r0, #pgm2_speed
- trap #trap_delay
- clr p1.6
- setb p1.7
- mov r0, #pgm2_speed
- trap #trap_delay
- setb p1.6
- setb p1.7
- mov r0, #pgm2_speed
- trap #trap_delay
- setb p1.6
- clr p1.7
- mov r0, #pgm2_speed
- trap #trap_delay
- setb p1.6
- setb p1.7
-
-pg2_skip_blink:
- mov r4l, #'.'
- call cout
- br pg2_loop
-
-program3:
- mov r4l, #'3'
- call cout
- ;mov r6, #test_str
- ;call pstr
- jmp program3
-
-
-test_str: db "This_is_a_test", 0
-
-
-
-
-
-
-
-
-