]> pd.if.org Git - mmurtl/commitdiff
autocommit for file dated 1994-12-05 14:44:00
authorRichard Burgess <>
Mon, 5 Dec 1994 14:44:00 +0000 (14:44 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Mon, 17 Oct 2016 14:03:47 +0000 (14:03 +0000)
ossource/tmrcode.asm [new file with mode: 0644]

diff --git a/ossource/tmrcode.asm b/ossource/tmrcode.asm
new file mode 100644 (file)
index 0000000..15fa289
--- /dev/null
@@ -0,0 +1,517 @@
+;   MMURTL Operating System Source Code\r
+;   Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+;   ALL RIGHTS RESERVED\r
+;   Version 1.0\r
+\r
+; This file contains the following internal and Public calls\r
+; dealing with time:\r
+;\r
+; IntTimer  - The Timer ISR\r
+; Sleep()      - A function to delay execution of a task\r
+; Alarm()      - A function to notify a task of a timer event\r
+; KillAlarm()  - Cancels an Alarm\r
+; GetCMOSTime()        - Reads the CMOS Time\r
+; GetCMOSDate()        - Reads the CMOS Date\r
+; MicroDelay() - Very small delays with no task suspension\r
+; GetTimerTick()   - Gets system tick count\r
+;\r
+;Beep() and Tone() are also here for lack of a better place\r
+;to put them.\r
+\r
+.DATA\r
+.INCLUDE MOSEDF.INC\r
+.INCLUDE TSS.INC\r
+\r
+.ALIGN DWORD\r
+\r
+; TIMER INTERRUPT COUNTER and TimerBlocks\r
+;-------------------------------------------\r
+; The Timer Block is a structure that contains\r
+; the exchange number of a task that is sleeping,\r
+; or one that has requested an alarm.\r
+; Each TimerBlock is 12 Bytes long\r
+; These are the offsets into the structure\r
+;\r
+sTmrBlk        EQU 12\r
+fInUse         EQU 0   ;DD 00000000h\r
+TmrRespExch    EQU 4   ;DD 00000000h\r
+CountDown      EQU 8   ;DD 00000000h\r
+;\r
+EXTRN SwitchTick DD\r
+EXTRN dfHalted   DD\r
+\r
+PUBLIC TimerTick       DD 0                    ;Incremented every 10ms (0 on bootup).\r
+PUBLIC nTmrBlksUsed    DD 0                    ;Number of timer blocks in use\r
+PUBLIC rgTmrBlks       DB (sTmrBlk * nTmrBlks) DUP (0)\r
+\r
+;\r
+;==== Begin Code =============================================================\r
+;\r
+.CODE\r
+;\r
+EXTRN ChkRdyQ NEAR\r
+EXTRN enQueueRdy NEAR\r
+EXTRN deQueueRdy NEAR\r
+;\r
+;\r
+; The timer interrupt checks up to n timer blocks for values to decrement.\r
+; The timer interrupt fires off every 10 milliseconds.  This sounds\r
+; like a lot (and it is), but even on a 20 Mhz processor it doesn't consume\r
+; too much bandwidth at all (CPU Time).\r
+;\r
+; Interrupt latency can be a problem for high speed non-DMA comms.\r
+; A single channel operating at 19,200 bps will interrupt every 520 us.\r
+; Two channels will do it every 260us (continuous comms).\r
+; (with non-buffered UARTS that is...).\r
+;\r
+; The timer int code also performs the important function of keeping\r
+; tabs on CPU hogs. It is really the onyl part of task scheduling that\r
+; isn't cooperative. It is a small but VERY important part.\r
+\r
+; At all times on the system, only one task is actually executing.\r
+; We have now interrupted that task.  Other tasks maybe waiting\r
+; at the ReadyQ to run. They may have been placed there by other\r
+; ISRs, and they may be an equal or higher priority than the task\r
+; that is now running (the one we interrupted).\r
+; We check to see if the same task has been running for 100ms or\r
+; more. If so.. we call ChkRdyQ and check the priority of that\r
+; task. If it is the same or higher, we switch to it. Then we place\r
+; the task we interrupted on the RdyQ in exactly the state we\r
+; interrupted it.\r
+; We couldn't even do it this way if we were using "interrupt tasks"\r
+; because this would "nest" hardware task swtiches. It would\r
+; require manipulating items in the TSS to make it look as if it\r
+; wasn't nested. Keep this in mind if you use the interrupt tasks.\r
+;\r
+PUBLIC IntTimer:\r
+               PUSHAD                                  ;INTS are disabled automatically\r
+               INC TimerTick               ;Timer Tick, INT 20\r
+               CMP nTmrBlksUsed, 0                     ;Anyone sleeping or have an alarm set?\r
+               JE  SHORT TaskCheck                     ;No...\r
+\r
+IntTmr00:                                                      ;Yes!\r
+               LEA EAX,rgTmrBlks           ;EAX has addr of Timer Blocks\r
+               MOV ECX,nTmrBlks            ;ECX has count of Timer Blocks\r
+               CLD                                                     ;Move forward thru the blocks\r
+\r
+IntTmr01:\r
+               CMP DWORD PTR [EAX+fInUse],FALSE                ;Timer Block found?\r
+               JE IntTmr03                                     ;No  - goto next block\r
+               CMP DWORD PTR [EAX+CountDown],0h      ;Yes - is count at zero?\r
+               JNE IntTmr02                ;No  - goto decrement it\r
+               PUSH EAX                    ;save ptr to rgTmpBlk\r
+               PUSH ECX                    ;save current count\r
+               PUSH DWORD PTR [EAX+TmrRespExch]        ;Yes - ISend Message\r
+               MOV EAX, -1                                     ;FFFFFFFFh\r
+               PUSH EAX                            ;bogus msg\r
+               PUSH EAX                            ;bogus msg\r
+               CALL FWORD PTR _ISendMsg        ;tell him his times up!\r
+               POP ECX                     ;get count back\r
+               POP EAX                                 ;get ptr back\r
+               MOV DWORD PTR [EAX+fInUse],FALSE      ;Free up the timer block\r
+               DEC nTmrBlksUsed                                        ;Correct count of used blocks\r
+               JMP IntTmr03                            ;skip decrement - empty blk\r
+\r
+IntTmr02:\r
+               DEC DWORD PTR [EAX+CountDown]   ;10ms more gone...\r
+\r
+IntTmr03:\r
+               ADD EAX,sTmrBlk             ;next block please!\r
+               LOOP IntTmr01               ;unless were done\r
+\r
+               ;We will now check to see if this guy has been running\r
+               ;for more then 30ms (3 ticks). If so, we will\r
+               ;switch him out if someone with an equal or higher pri\r
+               ;is on the RdyQ\r
+\r
+TaskCheck:\r
+               MOV EAX, dfHalted\r
+               OR EAX, EAX\r
+               JNZ TaskCheckDone\r
+\r
+               MOV     EAX, TimerTick\r
+               MOV EBX, SwitchTick\r
+               SUB EAX, EBX\r
+               CMP EAX, 3                                      ;Change this to change the "time slice"\r
+               JL SHORT TaskCheckDone          ;Hasn't been 30ms yet for this guy!\r
+\r
+               CALL ChkRdyQ                    ;Get next highest Pri in EAX (Leave Queued)\r
+               OR EAX, EAX                             ;Is there one at all??\r
+               JZ TaskCheckDone                ;No...\r
+\r
+               MOV ESI, pRunTSS\r
+               MOV DL, [ESI+Priority]  ;DL is pri of current\r
+               CMP DL, [EAX+Priority]  ;Compare current pri to highest queued\r
+                                                               ;The CMP subtracts Pri of Queued from\r
+                                                               ;current pri. If the Queued Pri is LOWER\r
+                                                               ;or Equal, we want to Switch. That means\r
+                                                               ;if we got a CARRY on the subtract, his\r
+                                                               ;number was higher and we DON'T\r
+               JC TaskCheckDone                ;If current is higher(lower num), keep going!\r
+\r
+               CALL deQueueRdy         ; Get high priority TSS off the RdyQ\r
+               MOV EDI, EAX                    ;  and save in EDI\r
+               MOV EAX, ESI                    ; Put current one in EAX\r
+               CALL enQueueRdy         ;  and on the RdyQ\r
+\r
+        MOV pRunTSS,EDI         ; Make the TSS in EDI the Running TSS\r
+               MOV BX,[EDI+Tid]        ; Get the task Id (TR)\r
+               MOV TSS_Sel,BX          ; Put it in the JumpAddr for Task Swtich\r
+               INC _nSwitches                  ; Keep track of how many switches for stats\r
+               INC _nSlices                    ; Keep track of how many sliced switches\r
+               MOV EAX, TimerTick              ; Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               PUSH 0                                          ;Must do this before we switch!\r
+               CALL FWORD PTR _EndOfIRQ    ;\r
+               JMP FWORD PTR [TSS]     ; JMP TSS (This is the task swtich)\r
+               POPAD                   ;\r
+               IRETD                   ;\r
+\r
+TaskCheckDone:\r
+               PUSH 0                                          ;Must do this before we switch!\r
+               CALL FWORD PTR _EndOfIRQ    ;\r
+               POPAD                   ;\r
+               IRETD                   ;\r
+\r
+;=============================================================================\r
+;\r
+; Sleep - A Public routine that delays the calling process by setting\r
+;       up a timer block with CountDown value and an Exchange to\r
+;       send a message to when the countdown reaches zero.\r
+;       The timer interrupt sends the message and clears the block\r
+;       when it reaches zero.\r
+;       This requires an exchange. The exchange\r
+;       used is the TSS_Exch in the TSS for the current task.\r
+;\r
+DelayCnt       EQU [EBP+0Ch]\r
+;\r
+;\r
+PUBLIC __Sleep:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               MOV EAX, DelayCnt\r
+               CMP EAX, 0                                      ;See if there's no delay\r
+               JE Delay03\r
+               LEA EAX,rgTmrBlks           ;EAX points to timer blocks\r
+               MOV ECX,nTmrBlks            ;Count of timer blocks\r
+               CLD                         ;clear direction flag\r
+Delay01:\r
+               CLI                         ;can't let others interfere\r
+               CMP DWORD PTR [EAX+fInUse],FALSE      ;Empty block?\r
+               JNE Delay02                 ;No  - goto next block\r
+               MOV EBX,DelayCnt                        ;Get delay count\r
+               MOV [EAX+CountDown],EBX     ;\r
+               MOV DWORD PTR [EAX+fInUse],TRUE       ;Use the Timer Block\r
+               INC nTmrBlksUsed                        ;Up the blocksInUse count\r
+               MOV ECX,pRunTSS             ;Get TSS_Exch for our use\r
+               MOV EBX,[ECX+TSS_Exch]      ;\r
+               MOV [EAX+TmrRespExch],EBX   ;put it in timer block!\r
+               STI\r
+               PUSH EBX                    ;Pass exchange (for WaitMsg)\r
+               ADD ECX,TSS_Msg             ;Offset of msg area\r
+               PUSH ECX\r
+               CALL FWORD PTR _WaitMsg       ;and Wait for it to come back\r
+               MOV EAX,ErcOk               ;all is well\r
+               JMP Delay03\r
+Delay02:\r
+               STI\r
+               ADD EAX,sTmrBlk\r
+               LOOP Delay01                ;unless were done\r
+               MOV EAX,ErcNoMoreTBs        ;Sorry, out of timer blocks\r
+Delay03:\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 4                      ;FAR return from publics\r
+\r
+;=============================================================================\r
+;\r
+; Alarm - A Public routine that sends a message to the exchange that the\r
+;       caller specifies after the number of ticks specified.\r
+;       The message is NOT repeatable (must be set up each time).\r
+;       The message will always be two DWords with 0FFFFFFFFh in each.\r
+;\r
+;              Alarm(nAlarmExch, AlarmCnt):dErc\r
+;\r
+AlarmExch      EQU [EBP+10h]\r
+AlarmCnt       EQU [EBP+0Ch]\r
+;\r
+;\r
+PUBLIC __Alarm:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               MOV EAX, AlarmCnt\r
+               CMP EAX, 0                                      ;See if there's no delay\r
+               JE Alarm03\r
+               LEA EAX,rgTmrBlks           ;EAX points to timer blocks\r
+               MOV ECX,nTmrBlks            ;Count of timer blocks\r
+               CLD                         ;clear direction flag\r
+Alarm01:\r
+               CLI                         ;can't let others interfere\r
+               CMP DWORD PTR [EAX+fInUse],FALSE      ;Empty block?\r
+               JNE Alarm02                 ;No  - goto next block\r
+               MOV EBX,AlarmCnt                        ;Get delay count\r
+               MOV [EAX+CountDown],EBX     ;\r
+               MOV DWORD PTR [EAX+fInUse],TRUE       ;Use the Timer Block\r
+               INC nTmrBlksUsed                        ;Up the blocksInUse count\r
+               MOV EBX, AlarmExch\r
+               MOV [EAX+TmrRespExch],EBX   ;put it in timer block!\r
+               STI                         ;It's OK to interrupt now\r
+               MOV EAX,ErcOk               ;all is well\r
+               JMP Alarm03\r
+Alarm02:\r
+               STI                         ;It's OK to interrupt now\r
+               ADD EAX,sTmrBlk\r
+               LOOP Alarm01                ;unless were done\r
+               MOV EAX,ErcNoMoreTBs        ;Sorry, out of timer blocks\r
+Alarm03:\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 8                      ;\r
+;\r
+;=============================================================================\r
+;\r
+; KillAlarm - A Public routine that kills an alarm message that is\r
+;       set to be sent to an exchange.  ALL alarms set to fire off\r
+;              to that exchange are killed. If the alarm is already queued\r
+;              through the kernel, NOTHING will stop it...\r
+;\r
+;              KillAlarm(nAlarmExch):dErc\r
+;\r
+KAlarmExch     EQU [EBP+0Ch]\r
+;\r
+;\r
+PUBLIC __KillAlarm:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               CMP nTmrBlksUsed, 0                     ;No blocks in use\r
+               JE KAlarm03                                     ; so we get out!\r
+               MOV EBX,KAlarmExch                      ;Get exchange for killing alarms to\r
+               LEA EAX,rgTmrBlks           ;EAX points to timer blocks\r
+               MOV ECX,nTmrBlks            ;Count of timer blocks\r
+               CLD                         ;clear direction flag\r
+KAlarm01:\r
+               CLI                         ;can't let others interfere\r
+               CMP DWORD PTR [EAX+fInUse],TRUE         ;Block in use?\r
+               JNE KAlarm02                ;No - goto next block\r
+               CMP [EAX+TmrRespExch],EBX   ;Does this match the Exchange?\r
+               JNE KAlarm02\r
+               MOV DWORD PTR [EAX+fInUse],FALSE     ;Make Empty\r
+               DEC nTmrBlksUsed                        ;Make blocksInUse correct\r
+KAlarm02:\r
+               STI                         ;It's OK to interrupt now\r
+               ADD EAX,sTmrBlk\r
+               LOOP KAlarm01               ;unless were done\r
+KAlarm03:\r
+               XOR EAX,EAX                             ;ALl done -- ErcOk\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 4                      ;\r
+;\r
+;=============================================================================\r
+;   MicroDelay(dDelay):derror  Delay count is in 15 us increments\r
+;\r
+; The timing for this delay is based on the toggle of the refresh bit\r
+; from the System Status port.  The toggle is approximately 15us.  This\r
+; means this call will not be very accurate for values less than\r
+; 3 or 4 (45 to 60 microseconds).  BUT, it's still very much needed.\r
+;\r
+PUBLIC __MicroDelay:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               MOV ECX, [EBP+0Ch]                      ;Get delay count\r
+               CMP ECX, 0\r
+               JE MDL01                                        ;get out if they came in with 0!\r
+MDL00:\r
+               IN AL, 61h                                      ;Get system status port\r
+               AND AL, 10h                                     ;check refrest bit\r
+               CMP AH, AL                                      ;Check toggle of bit\r
+               JE MDL00                                        ;No toggle yet\r
+               MOV AH, AL                                      ;Toggle! Move to AH for next compare\r
+               LOOP MDL00\r
+MDL01:\r
+               XOR EAX, EAX\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 4                      ;\r
+\r
+;=============================================================================\r
+;   GetCMOSTime(pdTimeRet):derror\r
+;\r
+; The Time is returned from the CMOS clock as a DWord.\r
+; Low order byte is the Seconds (BCD),\r
+; Next byte is the Minutes (BCD),\r
+; Next byte is the Hours (BCD),\r
+; High order byte is 0.\r
+;\r
+pCMOSTimeRet EQU [EBP+12]\r
+\r
+PUBLIC __GetCMOSTime:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               XOR EBX, EBX                            ;Clear time return\r
+\r
+           MOV EAX,04h                                 ;Hours\r
+               OUT 70h,AL\r
+               IN AL,71h\r
+               MOV BL, AL\r
+\r
+               SHL EBX, 8                                      ;Minutes\r
+           MOV EAX,02h\r
+               OUT 70h,AL\r
+               IN AL,71h\r
+               MOV BL,AL\r
+\r
+               SHL EBX, 8                      ;Seconds\r
+           MOV EAX,00h\r
+               OUT 70h,AL\r
+               IN AL,71h\r
+               MOV BL,AL\r
+\r
+               MOV ESI, pCMOSTimeRet           ;Give 'em the time\r
+               MOV [ESI], EBX\r
+               XOR EAX, EAX                            ; No Error\r
+\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 4                      ;\r
+\r
+;=============================================================================\r
+;   GetCMOSDate(pdTimeRet):derror\r
+;\r
+; The Date is returned from the CMOS clock as a DWord.\r
+; Low order byte is the Day of Week (BCD 0-6 0=Sunday),\r
+; Next byte is the Day (BCD 1-31),\r
+; Next byte is the Month (BCD 1-12),\r
+; High order byte is year (BCD 0-99).\r
+;\r
+pCMOSDateRet EQU [EBP+12]\r
+\r
+PUBLIC __GetCMOSDate:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               XOR EBX, EBX                            ;Clear date return\r
+\r
+           MOV EAX,09h                                 ;Year\r
+               OUT 70h,AL\r
+               IN AL,71h\r
+               MOV BL, AL\r
+               SHL EBX, 8                                      ;\r
+\r
+           MOV EAX,08h                                 ;Month\r
+               OUT 70h,AL\r
+               IN AL,71h\r
+               MOV BL, AL\r
+               SHL EBX, 8\r
+\r
+           MOV EAX,07h                                 ;Day of month\r
+               OUT 70h,AL\r
+               IN AL,71h\r
+               MOV BL,AL\r
+               SHL EBX, 8                      ;\r
+\r
+           MOV EAX,06h                                 ;Day of week\r
+               OUT 70h,AL\r
+               IN AL,71h\r
+               MOV BL,AL\r
+\r
+               MOV ESI, pCMOSDateRet           ;Give 'em the time\r
+               MOV [ESI], EBX\r
+               XOR EAX, EAX                            ; No Error\r
+\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 4                      ;\r
+\r
+;=============================================================================\r
+;   GetTimerTick(pdTickRet):derror\r
+;\r
+; The Current Timer Tick is returned (it's a DWord).\r
+;\r
+pTickRet EQU [EBP+12]\r
+\r
+PUBLIC __GetTimerTick:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               MOV ESI, pTickRet\r
+               MOV EAX, TimerTick\r
+               MOV [ESI], EAX\r
+               XOR EAX, EAX                            ;No Error\r
+               POP EBP                     ;\r
+               RETF 4                      ;\r
+\r
+;=======================================================\r
+; Noise makers:\r
+; BEEP is a FAR call with no params that beeps the speaker\r
+;      at 300 Hz for 350ms.\r
+; TONE is a FAR call with two parmas:\r
+;      1) FREQ in HZ (a DD)\r
+;      2) "ON" time in 10ms increments (a DD)\r
+;\r
+;The clock freq to Timer 2 is 1.193182 Mhz\r
+;To find the divisor of the clock, divide 1.193182Mhz by Desired Freq.\r
+;\r
+;================================================\r
+;This does all work for BEEP and TONE\r
+;EBX needs FREQ in HERTZ\r
+;ECX needs length of tone in 10ms increments\r
+\r
+BEEP_Work:\r
+               MOV AL, 10110110b           ;Timer 2, LSB, MSB, Binary\r
+               OUT 43h, AL\r
+               XOR EDX, EDX\r
+               MOV EAX, 1193182            ;1.193182Mhz\r
+               DIV EBX                     ;DIVISOR is in EBX (Freq)\r
+               OUT 42h, AL                 ;Send quotient (left in AX)\r
+               MOV AL, AH\r
+               NOP\r
+               NOP\r
+               NOP\r
+               NOP\r
+               OUT 42h, AL\r
+               IN AL, 61h\r
+               OR AL, 00000011b\r
+               PUSH EAX\r
+               POP EAX\r
+               OUT 61h, AL\r
+               PUSH ECX                    ;\r
+               CALL FWORD PTR _Sleep       ;ECX is TIME ON in 50ms incs.\r
+               IN AL, 61h\r
+               NOP\r
+               NOP\r
+               NOP\r
+               NOP\r
+               AND AL, 11111100b\r
+               OUT 61h, AL\r
+               RETN\r
+\r
+\r
+;================================================\r
+PUBLIC __Beep:\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               MOV EBX, 800                            ;Freq\r
+               MOV ECX, 35                                     ;350ms\r
+               CALL Beep_Work\r
+               POP EBP                     ;\r
+               RETF\r
+\r
+;================================================\r
+;TONE allows the caller to specify a frequency and duration of a tone\r
+;from the speaker.  This call uses Beep_Work (A NEAR call to do the job)\r
+; PARAM 1 is a DD which is the FREQUENCY in HERTZ\r
+; PARAM 2 is a DD which is the length of the tone in 50ms increments\r
+;\r
+ToneFreq       EQU [EBP+10h]\r
+ToneTime       EQU [EBP+0Ch]\r
+\r
+PUBLIC __Tone:\r
+               PUSH EBP                        ;\r
+               MOV EBP,ESP                     ;\r
+               MOV EBX, ToneFreq\r
+               MOV ECX, ToneTime\r
+               CALL Beep_Work\r
+               POP EBP                         ;\r
+               RETF 8\r
+\r
+\r
+;====================== Module End =================================\r