TEXT   55
rtx
Guest on 17th August 2022 01:57:12 PM


  1.  
  2.         .PRINTX * RTX.MUB      
  3.  
  4.         .COMMENT \
  5.  
  6. A SIMPLE REAL-TIME EXECUTIVE (RTX).
  7.  
  8. 1. Supports any number of tasks (limited to 255 in some places).
  9. 2. Total timer interrupt task switch time is less than 60us (Z180/6MHz).
  10. 3. Each task must have its own stack.
  11.  
  12.  
  13. MOD RECORD
  14.  
  15.  
  16.         Moved from MB II.
  17.                 rtx_switch entry modded to prevent HL corruption.
  18.                 rtx_switch reloads rtx_timer, to prevent a premature next tick
  19.                 Calc_space task changed to 'onesec'.
  20.                 'get_task_status' routine added.  The rtx_switch does NOT
  21.                 reload rtx_timer if rtx is disabled (rtx_timer=255).
  22.  
  23.  
  24.  
  25. The RTX has five entry points:
  26.  
  27. 1.      Initialisation.
  28.         Entered (with JP) just once after power-up, to set-up initial PC and
  29.         SP values.  This entry kicks-off the RTX and never returns.
  30.  
  31. 2.      Timer interrupt (timer tick).
  32.         This is the normal entry point.  At each timer tick, the RTX switches
  33.         to the next task.
  34.  
  35. 3.      Switch to next task.
  36.         This entry point can be used by the currently executing task to
  37.         ask the RTX to switch to the next task, e.g. if the task has nothing
  38.         more to do.  On the Z180, this entry point is the same as the timer
  39.         tick entry point, because the timer int pushes the same things on the
  40.         stack as a CALL does.
  41.         On the MUB, this entry is implemented as RST 38h, for compact code.
  42.  
  43. 4.      Suspend a task (until re-activated).
  44.         This 'kills' a task.  The RTX will ignore that task, until the task is
  45.         re-activated with the function below.
  46.  
  47. 5.      Re-activate a (suspended) task.
  48.         This re-activates a (previously suspended) task.
  49.  
  50.  
  51. For each task, the RTX maintains the data area below:
  52.  
  53. offset  contents        initial value
  54. +0      af              from table
  55. +2      bc              from table
  56. +4      de              from table
  57. +6      hl              from table
  58. +8      ix              from table
  59. +10     iy              from table
  60. +12     sp              task's sp (*before* the timer interrupt)
  61. +14     pc              task's entry point
  62. +16     msr             task's MSR value (used with Z280 only)
  63. +18     status          0: task active, 1: task suspended
  64. +19     reserved
  65.  
  66. The RTX uses push/pop instructions to save/reload registers because these are
  67. probably the fastest way.
  68.  
  69. The RTX loads iopage to 00h (as part of clearing the timer 0 pending int) - this
  70. is OK because throughout the MUB ints are OFF whenever iopage<>0 and the RTX
  71. should therefore never tick with iopage<>0.
  72.  
  73. Each task's MSR is preserved, although currently all tasks run with 'ei all_ints'
  74. and preserving MSR is not really necessary; it could just be forced to 'all_ints'
  75. when each task is entered.
  76.  
  77. Z180
  78. ====
  79.  
  80. If you want to use this RTX for a Z180, you might prefer to use the slightly
  81. more recent version used in the IPC/180.  This supports different BBR value for
  82. each task.
  83.  
  84.  
  85.         \
  86.  
  87.  
  88.  
  89.         global REAL_RTX_SWITCH
  90.         global RTX_ACTIVATE_ENTRY
  91.         global RTX_GET_TASK_STATUS
  92.         global RTX_INIT_ENTRY
  93.         global RTX_SUSPEND_ENTRY
  94.         global RTX_TIMER_INT_ENTRY
  95.  
  96.         ; code (all these are RTX tasks)
  97.         external BIG_REQUESTS
  98.         external FRONT_PANEL
  99.         external LANC_OPERATIONS
  100.         external MAIN_DATA_LOOP
  101.         external NEW_LINKMAPS
  102.         external ONESEC
  103.         external PROCESS_IPC_BUFS
  104.         external PROCESS_OP_TIMEOUTS
  105.         external PROCESS_OUT_RCBUFS
  106.         external PROCESS_REM_PLOFN
  107.         external SC_EAROM_UPDATE
  108.         external SETUP_MODE
  109.  
  110.         ; data
  111.         external HL_SAVE
  112.         external PC_SAVE
  113.         external RTX_ALL_SUSPENDED
  114.         external RTX_CURRENT_TASK
  115.         external RTX_DATA_AREAS
  116.         external RTX_DATA_PTR
  117.         external RTX_TIMER
  118.         external MSR_SAVE
  119.         external SP_SAVE
  120.         external STACK_START
  121.  
  122.  
  123.         INCLUDE DEFS.MUB
  124.         SECTION CODE,X
  125.  
  126.  
  127. MUB     EQU     TRUE
  128.  
  129.  
  130. ; RTX initialisation table.  Terminated by a line with PC=0.
  131. ; In the future, this should be modded to hold ix,iy,sp,pc,msr values only.
  132. ; All tasks have equal priority and execute on a round-robin basis.
  133.  
  134. ; ** The task *order* (i.e. their #s) is assumed by calls to rtx_activate & rtx_suspend **
  135. ; ** THEREFORE: ADD NEW TASKS ONLY AT THE *END* OF THE TABLE **
  136.  
  137. ; ** Dont forget to EQUate rtx_tasks in defs.mub to #tasks below (incl PC=0 task) **
  138.  
  139. RTX_INITIAL_REGS:                                                               ; task# v
  140.                                                                                 ;       v
  141. ;    AF BC DE HL IX IY SP                          PC                  MSR    stat+res  v
  142.  
  143.  DW  0  0  0  0  0  0  STACK_START+(1*STACK_SIZE)  MAIN_DATA_LOOP      ALL_INTS    0  ; 0
  144.  DW  0  0  0  0  0  0  STACK_START+(2*STACK_SIZE)  LANC_OPERATIONS     ALL_INTS    0  ; 1
  145.  DW  0  0  0  0  0  0  STACK_START+(3*STACK_SIZE)  BIG_REQUESTS        ALL_INTS    0  ; 2
  146.  DW  0  0  0  0  0  0  STACK_START+(4*STACK_SIZE)  SC_EAROM_UPDATE     ALL_INTS 0001H ; 3*
  147.  DW  0  0  0  0  0  0  STACK_START+(5*STACK_SIZE)  NEW_LINKMAPS        ALL_INTS 0001H ; 4*
  148.  DW  0  0  0  0  0  0  STACK_START+(6*STACK_SIZE)  SETUP_MODE          ALL_INTS    0  ; 5
  149.  DW  0  0  0  0  0  0  STACK_START+(7*STACK_SIZE)  ONESEC              ALL_INTS 0001H ; 6*
  150.  DW  0  0  0  0  0  0  STACK_START+(8*STACK_SIZE)  FRONT_PANEL         ALL_INTS 0001H ; 7*
  151.  DW  0  0  0  0  0  0  STACK_START+(9*STACK_SIZE)  PROCESS_OP_TIMEOUTS ALL_INTS 0001H ; 8*
  152.  DW  0  0  0  0  0  0  STACK_START+(10*STACK_SIZE) PROCESS_OUT_RCBUFS  ALL_INTS 0001H ; 9*
  153.  DW  0  0  0  0  0  0  STACK_START+(11*STACK_SIZE) PROCESS_IPC_BUFS    ALL_INTS 0001H ;10*
  154.  DW  0  0  0  0  0  0  STACK_START+(12*STACK_SIZE) PROCESS_REM_PLOFN   ALL_INTS 0001H ;11*
  155.  DW  0  0  0  0  0  0  0                           0                   0           0
  156.  
  157. ; (*)   the above tasks such marked run only once and suspend themselves, and are
  158. ;       re-activated by some event.  The '0001H' value sets status=01h; this makes
  159. ;       the task *initially* suspended (e.g. we *dont* want to update the eeprom at
  160. ;       every power-up, do we?).
  161.  
  162.  
  163. ; RTX initialisation/entry point.
  164.  
  165. RTX_INIT_ENTRY:
  166.  
  167.         DI                      ; Necessary ! (in case of entry with EI)
  168.  
  169.         ; Copy initial register values from ROM into RAM.
  170.  
  171.         LD HL, RTX_INITIAL_REGS
  172.         LD DE, RTX_DATA_AREAS
  173.         LD BC, RTX_TASKS*RTX_DATA_SIZE
  174.         LDIR
  175.  
  176.         ; Reset ptr (IX) to base+20 of the first task
  177.  
  178.         LDW (RTX_DATA_PTR), RTX_DATA_AREAS+RTX_DATA_SIZE
  179.  
  180.         LD (RTX_TIMER), RTX_TC  ; RTX downcounter time constant
  181.  
  182.         LD (RTX_CURRENT_TASK),0 ; Initial task # := 0
  183.  
  184.         ; Enable timer interrupt
  185.  
  186.   IF MUB
  187.         ; (no action necessary because timer 0 int is already always enabled
  188.         ;  at the timer, and rtx1st does an EI etc)
  189.   ELSE
  190.         IN0A TCR
  191.         SET 5, A                ; Timer 1 ints (RTX) ON
  192.         OUT0A TCR
  193.   ENDIF
  194.  
  195.         ; Set-up SP to 1st POP task's initial values and jump to 1st task
  196.  
  197.         LD SP, RTX_DATA_AREAS
  198.         JP RTX1ST               ; (also does EI)
  199.  
  200.  
  201.  
  202.  
  203. ; This is the timer interrupt service routine which switches tasks.
  204. ; At entry here (and at exit) rtx_data_ptr points to the 1st byte after the
  205. ; task's data area (i.e. byte @base+20).  Therefore, when we load SP from
  206. ; rtx_data_ptr and start PUSHing the registers, the first word pushed (MSR)
  207. ; is written in the right place.  Rtx_data_ptr does NOT point at base+0.
  208.  
  209. RTX_TIMER_INT_ENTRY:
  210.  
  211.         ; First, preserve the regs which we are going to corrupt, etc, while
  212.         ; adjusting SP back to its value *before* the timer interrupt.
  213.         ; All this must be done with instructions which dont modify flags.
  214.         ; We get here from ct0 interrupt, with AF'.
  215.  
  216.   IF MUB
  217.         LD (RTX_TIMER), RTX_TC  ; Reload the down-counter
  218.  
  219.         EXX
  220.         IOPAGE 0FEH             ; Clear CC bit (reset 'int pending')
  221.         LD A, 11100000B         ; (this could be done *after* the RTX code
  222.         OUT (0E1H), A           ;  but for some reason doing it there did not
  223.         IOPAGE 0                ;  work properly; it does not really matter)
  224.         EXX
  225.  
  226.         EX AF, AF'              ; and switch back to main AF
  227.  
  228.         INC SP                  ; Skip over Mode 3 Int Reason Code
  229.         INC SP                  ;  (always the same - boring)
  230.         LD (HL_SAVE), HL        ; Save HL of interrupted task
  231.         POP (MSR_SAVE)          ; Save MSR of interrupted task
  232.  
  233.         ; This entry point is used by rtx_switch, which has already loaded
  234.         ; HL and MSR into hl_save and msr_save, and disabled all interrupts.
  235.  
  236. RTX_ES: POP HL                  ; HL := PC of interrupted task
  237.         LD (SP_SAVE), SP        ; Save SP (the value *before* the timer int)
  238.   ELSE
  239.         LD (HL_SAVE), HL        ; Save HL of interrupted task
  240.         POP HL                  ; HL := PC of interrupted task
  241.         LD (SP_SAVE), SP        ; Save SP of interrupted task
  242.   ENDIF
  243.  
  244.         ; HL now holds the interrupted task's PC value.
  245.         ; We save the interrupted task's registers by a series of PUSHes.
  246.  
  247.         LD SP, (RTX_DATA_PTR)   ; Set SP to interrupted task's data area
  248.   IF MUB
  249.         DEC SP                  ; Skip over status+reserved bytes
  250.         DEC SP
  251.         PUSH (MSR_SAVE)         ; Save MSR
  252.         PUSH HL                 ; Save PC
  253.         PUSH (SP_SAVE)          ; Save SP (the value *before* this interrupt)
  254.         PUSH IY                 ; Save IY
  255.         PUSH IX                 ; Save IX
  256.         PUSH (HL_SAVE)          ; Save HL
  257.   ELSE
  258.         DEC SP                  ; Skip over status+reserved bytes
  259.         DEC SP
  260.         DEC SP                  ; Z180: 'push' a dummy value instead of MSR
  261.         DEC SP
  262.         PUSH HL                 ; Save PC
  263.         LD HL, (SP_SAVE)
  264.         PUSH HL                 ; Save SP
  265.         PUSH IY                 ; Save IY
  266.         PUSH IX                 ; Save IX
  267.         LD HL, (HL_SAVE)
  268.         PUSH HL                 ; Save HL
  269.   ENDIF
  270.         PUSH DE                 ; Save DE
  271.         PUSH BC                 ; Save BC
  272.         PUSH AF                 ; Save AF
  273.  
  274.         ; Interrupted task is now saved.  Go to next task.  
  275.         ; If next task's PC=0, this is the end of the task table and we wrap.
  276.  
  277.         LD B, RTX_TASKS         ; Max # of tasks to go through
  278.  
  279. RTX_NX: LD IX, (RTX_DATA_PTR)
  280.         LD SP, IX               ; This loads SP just right for the POPs below
  281.         LD DE, RTX_DATA_SIZE
  282.         ADD IX, DE              ; Move rtx_data_ptr to next task's data area
  283.         LD HL, RTX_CURRENT_TASK
  284.         INC (HL)                ; Current task # ++
  285.   IF MUB
  286.         LD DE, (IX-6)           ; Check if next task's PC=0
  287.         LD A, D
  288.         OR E
  289.   ELSE
  290.         LD A, (IX-6)
  291.         OR A, (IX-5)
  292.   ENDIF
  293.         JR NZ, RTX_01           ; NZ: no, continue
  294.  
  295.         LD IX, RTX_DATA_AREAS+RTX_DATA_SIZE ; else reset ptr to 1st task
  296.         LD SP, RTX_DATA_AREAS   ; and set SP to start POPping for 1st task
  297.         LD (HL), 0              ; and reset 'current task #'
  298.  
  299. RTX_01: LD (RTX_DATA_PTR), IX
  300.  
  301.         LD A, (IX-2)            ; Now check that the newly-selected task is
  302.         OR A                    ; not suspended.
  303.         JR Z, RTX1ST            ; Z: not suspended - continue
  304.         DJNZ RTX_NX             ; else loop through all the tasks in the table
  305.  
  306.         ; (If all tasks are suspended, we fall out here and the next task gets
  307.         ;  executed anyway, which does not really matter unless one actually
  308.         ;  relies on suspended tasks to *never* execute.  Doing this properly
  309.         ;  is more complicated).
  310.  
  311.         LD A, 1                 ; Mark 'all suspended' condition detected
  312.         LD (RTX_ALL_SUSPENDED), A
  313.  
  314.         ; Load registers for next task.
  315.         ; This is also the entry point for starting the RTX (enter with
  316.         ;  SP = base of 1st task's data area)
  317.  
  318. RTX1ST: POP AF                  ; Load AF
  319.         POP BC                  ; Load BC
  320.         POP DE                  ; Load DE
  321.   IF MUB
  322.         POP (HL_SAVE)           ; Recover HL
  323.         POP IX                  ; Load IX
  324.         POP IY                  ; Load IY
  325.         POP (SP_SAVE)           ; Recover SP
  326.         POP (PC_SAVE)           ; Recover PC
  327.         POP HL                  ; Recover MSR
  328.   ELSE
  329.         POP HL
  330.         LD (HL_SAVE), HL
  331.         POP IX
  332.         POP IY
  333.         POP HL   
  334.         LD (SP_SAVE), HL
  335.         POP HL           
  336.         LD (PC_SAVE), HL
  337.   ENDIF
  338.         LD SP, (SP_SAVE)        ; Load SP
  339.  
  340.         ; Clear the pending interrupt, enable ints and enter the new task.
  341.  
  342.   IF MUB
  343.         PUSH (PC_SAVE)          ; Put PC on stack (for RETIL to pop-off)
  344.         PUSH HL                 ; Likewise with MSR
  345.         LD HL, (HL_SAVE)        ; Load HL
  346.         RETIL                   ; Enter new task
  347.   ELSE
  348.         LD HL, (PC_SAVE)
  349.         PUSH HL                 ; Put PC on stack (for RET to pop-off)
  350.         LD HL, (HL_SAVE)        ; Load HL
  351.         EX AF, AF'
  352.         IN0A TCR                ; Clear the pending timer 1 interrupt bit
  353.         IN0A TMDR1L
  354.         EX AF, AF'
  355.         EI                      ; Re-enable interrupts
  356.         RET                     ; Enter new task
  357.   ENDIF
  358.  
  359.  
  360.  
  361. ; CALL this entry point to cause a switch to the next task.  Done via RST 38H.
  362. ; This entry also reloads the RTX tick down-counter, so that the next RTX tick
  363. ; cannot occur until after a whole tick period.
  364.  
  365. REAL_RTX_SWITCH:
  366.  
  367.   IF MUB
  368.         DI                      ; Prevent 'normal' RTX tick coming in here
  369.         PUSH AF
  370.         LD A, (RTX_TIMER)       ; If rtx is NOT disabled,
  371.         INC A
  372.         JR Z, RRS_NL
  373.         LD (RTX_TIMER), RTX_TC  ; then reload its down-counter
  374. RRS_NL: PUSH BC
  375.         LD (HL_SAVE), HL        ; Save HL straight into RTX's HL save location
  376.         LD C, 0
  377.         LDCTL HL, (C)           ; Read current MSR
  378.         LD A, L
  379.         OR ALL_INTS             ; Correct for 'di' above having cleared the EI bits
  380.         LD L, A
  381.         LD (MSR_SAVE), HL       ; Save MSR straight into RTX's MSR save location
  382.         POP BC
  383.         POP AF
  384.         JP RTX_ES               ; Do (nearly) as if a timer tick occured
  385.   ELSE
  386.         DI
  387.         EX AF, AF'
  388.         LD A, RTX_TC            ; Reload RTX downcounter time constant
  389.         LD (RTX_TIMER), A
  390.         EX AF, AF'
  391.         JP RTX_TIMER_INT_ENTRY  ; Do as if timer tick occurred
  392.   ENDIF
  393.  
  394.  
  395.  
  396. ; CALL this entry point to suspend a task.
  397. ; Enter with E = task # (0..254).
  398. ; If E=255, then the task currently executing is suspended.  This feature
  399. ; is useful if a task does not know its own task # (i.e. 'suspend me').
  400.  
  401. ; ALSO, READ THE LARGE COMMENT BELOW.  It effectively means that if a routine
  402. ; wants to suspend itself, it must use the 'task#=255' call, NOT a 'task=own#'
  403. ; call !!
  404.  
  405. ; Kills DE,HL.
  406.  
  407. RTX_SUSPEND_ENTRY:
  408.  
  409.         LD D, E                 ; Make a copy of task#
  410.         INC E                   ; If E=0FFh
  411.         JR NZ, RSE_05
  412.         LD A, (RTX_CURRENT_TASK) ; then use the 'current' task # instead
  413.         LD E, A
  414.         INC E
  415. RSE_05: DEC E
  416.   IF MUB
  417.         LD A, RTX_DATA_SIZE     ; Index to the task's data area
  418.         MULTU A, E
  419.         ADDW HL, RTX_DATA_AREAS+18
  420.   ELSE
  421.         PUSH DE
  422.         LD D, RTX_DATA_SIZE
  423.         MULDE
  424.         LD HL, RTX_DATA_AREAS+18
  425.         ADD HL, DE
  426.         POP DE
  427.   ENDIF
  428.         LD (HL), 1              ; and mark it 'suspended'
  429.  
  430.         ; If entry task# = 255, then we have a 'suspend me' call, and the
  431.         ; task is suspended IMMEDIATELY.  Normally, this is what would be
  432.         ; really required.  However, if task#<>255, then we have a case where
  433.         ; one task (or int routine, etc) is suspending another task, and we
  434.         ; do NOT then perform a rtx_switch.  Doing an rtx_switch makes sense
  435.         ; only on a 'suspend me' request, not on a 'suspend task n' request.
  436.  
  437.         INC D
  438.         CALL Z, REAL_RTX_SWITCH ; and switch immediately to next task
  439.  
  440.         RET                     ; Return via here when task is re-activated
  441.  
  442.  
  443.  
  444. ; CALL this entry point to re-activate a task.
  445. ; Enter with E = task # (0..).
  446. ; Kills AF,HL (+DE on Z180).
  447.  
  448. RTX_ACTIVATE_ENTRY:
  449.  
  450.   IF MUB
  451.         LD A, RTX_DATA_SIZE     ; Index to the task's data area
  452.         MULTU A, E
  453.         ADDW HL, RTX_DATA_AREAS+18
  454.   ELSE
  455.         LD D, RTX_DATA_SIZE
  456.         MULDE
  457.         LD HL, RTX_DATA_AREAS+18
  458.         ADD HL, DE
  459.   ENDIF
  460.         LD (HL), 0              ; and mark it 'active'
  461.         RET
  462.  
  463.  
  464. ; CALL this entry point to find out whether a task is currently activated (not
  465. ; suspended).
  466. ; Enter with E = task # (0..).
  467. ; Returns A=00 if activated, 01 if suspended.
  468. ; Kills AF,HL (+DE on Z180).
  469.  
  470. RTX_GET_TASK_STATUS:
  471.   IF MUB
  472.         LD A, RTX_DATA_SIZE     ; Index to the task's data area
  473.         MULTU A, E
  474.         ADDW HL, RTX_DATA_AREAS+18
  475.   ELSE
  476.         LD D, RTX_DATA_SIZE
  477.         MULDE
  478.         LD HL, RTX_DATA_AREAS+18
  479.         ADD HL, DE
  480.   ENDIF
  481.         LD A, (HL)              ; and get the 'status' byte
  482.         RET
  483.  
  484.  
  485.  
  486.         END

Raw Paste

Login or Register to edit or fork this paste. It's free.