| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014 |
- ;
- ; Command & Conquer Red Alert(tm)
- ; Copyright 2025 Electronic Arts Inc.
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful,
- ; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ; GNU General Public License for more details.
- ;
- ; You should have received a copy of the GNU General Public License
- ; along with this program. If not, see <http://www.gnu.org/licenses/>.
- ;
- ;***************************************************************************
- ;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
- ;***************************************************************************
- ;* *
- ;* Project Name : Timer stuff *
- ;* *
- ;* File Name : TIMERA.ASM *
- ;* *
- ;* Programmer : Scott K. Bowen *
- ;* *
- ;* Start Date : July 6, 1994 *
- ;* *
- ;* Last Update : March 14, 1995 [PWG] *
- ;* *
- ;*-------------------------------------------------------------------------*
- ;* Functions: *
- ;* Get_RM_Timer_Address -- Return address of real mode code for copy. *
- ;* Get_RM_Timer_Size -- return size of real mode timer code. *
- ;* Increment_Tick_Count -- Increments WW system timer. *
- ;* Timer_Interrupt -- Temp routine to mimic a timer system calling our. *
- ;* Install_Timer_Interrupt -- Installs the timer interrupt routine. *
- ;* Remove_Timer_Interrupt -- Removes the timer interrupt vectors. **
- ;* Set_Timer_Frequency -- Set the frequency of the timer. *
- ;* Set_Timer_Rate -- Set the rate of the timer. *
- ;* Get_System_Tick_Count -- Returns the system tick count. *
- ;* Get_User_Tick_Count -- Get tick count of user clock. *
- ;* Increment_Timers -- Increments system and user timers. *
- ;* Get_Num_Interrupts -- Returns the number of interrupts that have occured*
- ;* Timer_Interrupt_Func -- Handles core timer code *
- ;* Disable_Timer_Interrupt -- Disables at the hardware level *
- ;* Enable_Timer_Interrupt -- Enables at the hardware level *
- ;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
- IDEAL
- P386
- MODEL USE32 FLAT
- DPMI_INTR equ 31h
- TRUE equ 1 ; Boolean 'true' value
- FALSE equ 0 ; Boolean 'false' value
- LOCALS ??
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;///////////////////////////////////// Equates ////////////////////////////////////////
- SYSTEM_TIMER_FREQ equ 60 ; Frequency of system timer.
- ;********************************************************************
- ; There are two ways to call our interrupt chain in protected mode.
- ; The obvious way it to call the address that we replaced in the
- ; PM interrupt chain. This method is a little difficult but works and
- ; should always work.
- ;********************************************************************
- ; The RM and PM interrupts can be installed at the same time or seperately.
- ; Installing at the same time is the best method, the other method
- ; can be used to faciliated debugging when you only want one or the other
- ; called. Both methods work.
- ; -SKB July 21, 1994.
- INSTALL_SEPERATE equ FALSE
- INTCHIP0 EQU 20h ; 8259 interrupt chip controller 0
- CLEARISR EQU 20h ; Value to write to 8259 to reenable interrupts.
- IRQ0INTNUM EQU 08h ; IRQ0 interrupt vector number.
- DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls.
- LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT
- UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT
- TIMER_CONST EQU 1193180 ; TIMER_CONST / FREQ = rate to set 8259 chip
- TIMERCMDREG EQU 043H ; timer command register port.
- TIMER0PORT EQU 040H ; timer channel 0 port
- TIMETYPE EQU 036H ; type of timer.
- ; 36H = 0011 0110
- ; -- select channel 0
- ; -- read/load low byte then high byte for timer
- ; --- timer mode 3
- ; - 0 - binary , 1 - BCD data
-
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;///////////////////////////////////// Structs ////////////////////////////////////////
- ; Structure of memory in real mode handler.
- ; This is at the very start of the real mode code.
- ; This information may not change unless the real mode version is also changed.
- STRUC TimerType
- ; For speed, PM uses a DD while RM used DW
- ; For speed, SysRate and SysError are DD in PM and are DW in real mode.
- TrueRate DD ? ; True rate of clock. (only use word)
- SysTicks DD ? ; Tick count of timer.
- SysRate DD ? ; Desired rate of timer.
- SysError DD ? ; Amount of error in clock rate for desired frequency.
- SysCurRate DW ? ; Keeps track of when to increment timer.
- SysCurError DW ? ; Keeps track of amount of error in timer.
- UserTicks DD ? ; Tick count of timer.
- UserRate DD ? ; Desired rate of timer.
- UserError DD ? ; Amount of error in clock rate for desired frequency.
- UserCurRate DW ? ; Keeps track of when to increment timer.
- UserCurError DW ? ; Keeps track of amount of error in timer.
- DosAdder DW ? ; amount to add to DosFraction each interrupt.
- DosFraction DW ? ; Call dos when overflowed.
- OldRMI DD ? ; The origianl RM interrupt seg:off.
- OldPMIOffset DD ? ; The origianl PM interrupt offset
- OldPMISelector DD ? ; The original PM interrupt segment.
- CodeOffset DW ? ; Offset of the code in the RM stuff.
- CallRMIntOffset DW ? ; Offset of function to call DOS timer interrupt.
- CallRMIntAddr DD ? ; PM address of CallRealIntOffset for speed.
- PMIssuedInt DD 0 ; PM signals RM to just call Int chain.
- ; These are just used for information on testing. When all is done, they can
- ; be removed, but why? The don't add too much proccessing time and can
- ; be useful.
- NumPMInts DD ? ; Number of PM interrupts
- NumRMInts DD ? ; Number of RM interrupts.
- ENDS
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;/////////////////////////////////// Prototypes //////////////////////////////////////
- GLOBAL Get_System_Tick_Count:NEAR
- GLOBAL Get_User_Tick_Count:NEAR
- GLOBAL Get_RM_Timer_Address:NEAR
- GLOBAL Get_RM_Timer_Size:NEAR
- GLOBAL Set_Timer_Frequency:NEAR
- GLOBAL Timer_Interrupt:NEAR
- GLOBAL Install_Timer_Interrupt:NEAR
- GLOBAL Remove_Timer_Interrupt:NEAR
- GLOBAL Get_Num_Interrupts:NEAR
- GLOBAL Timer_Interrupt_Func:FAR
- GLOBAL Disable_Timer_Interrupt:NEAR
- GLOBAL Enable_Timer_Interrupt:NEAR
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;//////////////////////////////////////// Data ////////////////////////////////////////
- DATASEG
- ; The current time we will just include the real mode stuff
- ; into the protected mode code and then copy it down. The C side of
- ; this will handle this method or reading it off of disk in the real
- ; method.
- LABEL RealBinStart BYTE
- include "timereal.ibn"
- LABEL RealBinEnd BYTE
- LABEL LockedDataStart BYTE
- RealModeSel DD 0
- RealModePtr DD 0 ; Pointer to real mode memory.
- RealModeSize DD 0 ; Pointer to real mode memory.
- LABEL LockedDataEnd BYTE
-
- InitFlags DD 0 ; Flags to indicate what has been initialized.
- ; InitFlags that are set to have a fully functional interrupt.
- IF_ALLOC_RM equ 1h ; Allocation of RM was successful.
- IF_SET_VECTORS equ 2h ; Vectors have been set.
- IF_LOCKED_PM_CODE equ 4h ; Locked PM code for DPMI.
- IF_LOCKED_PM_DATA equ 8h ; Locked PM code for DPMI.
- IF_RATE_CHANGE equ 10h ; Timer rate was changed.
- IF_FUNCTIONAL equ 20h ; Timer is in and functional.
- IF_LOCKED_RM_CODE equ 40h ; Timer is in and functional.
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;//////////////////////////////////////// Code ////////////////////////////////////////
- CODESEG
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;/////////////////////////////// Init routines ////////////////////////////////////////
- ;***************************************************************************
- ;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. *
- ;* *
- ;* *
- ;* *
- ;* INPUT: *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/06/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Get_RM_Timer_Address C Near
- mov eax, OFFSET RealBinStart
- ret
- ENDP
- ;***************************************************************************
- ;* GET_RM_TIMER_SIZE -- return size of real mode timer code. *
- ;* *
- ;* *
- ;* *
- ;* INPUT: *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/06/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Get_RM_Timer_Size C Near
- mov eax, OFFSET RealBinEnd - OFFSET RealBinStart
- ret
- ENDP
- ;***************************************************************************
- ;* SET_TIMER_RATE -- Set the rate of the timer. *
- ;* *
- ;* *
- ;* INPUT: ebx = rate to set timer were rate = 1193180 / freq *
- ;* *
- ;* OUTPUT: none *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/11/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Set_Timer_Rate Near
- push eax
- pushf ; to save int enable flag
- cli ; disable interupts while setting up swapper
- mov al,TIMETYPE ; setup to modify timer 0
- out TIMERCMDREG,al ; send command.
- mov eax,ebx ; get rate.
- out TIMER0PORT,al ; output low byte
- mov al,ah
- out TIMER0PORT,al ; output high byte
- sti
- popf ; get int enable flag.
- pop eax
- ret
- ENDP Set_Timer_Rate
- ;***************************************************************************
- ;* SET_TIMER_FREQUENCY -- Set the frequency of the timer. *
- ;* *
- ;* *
- ;* INPUT: INT Frequency of user timer. *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* *
- ;* HISTORY: *
- ;* 07/06/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Set_Timer_Frequency C NEAR
- USES eax,ebx,ecx,edx
- ARG freq:DWORD
- LOCAL clockrate:DWORD
- LOCAL clockfreq:DWORD
- test [InitFlags],IF_FUNCTIONAL ; Is our timer system installed?
- jz ??timer_not_installed ; if not, this is not legal.
- ; Find out the greater of the frequencies (user or system.)
- ; Assign the True rate value based of of that.
- ; store the max frequency in ecx.
- mov ecx,[freq] ; get user frequency.
- cmp ecx,SYSTEM_TIMER_FREQ ; compare it with system frequency
- jg ??user_is_fastest ; is user frequency faster?
- mov ecx,SYSTEM_TIMER_FREQ ; no, set clock freq to system frequency.
- ??user_is_fastest:
- ; now get the rate that the clock will be set at.
- ; ecx is still max frequency.
- mov esi,[RealModePtr]
- mov eax,TIMER_CONST ; get the clock constant value.
- xor edx,edx ; zero for divide.
- div ecx ; rate = TC/freq => eax = eax/ecx;
- mov [(TimerType PTR esi).TrueRate],eax ; Set our true rate.
- mov ebx,eax ; save for later. DO NOT USE UNTIL CALL
- ; Set up variables to call DOS correctly.
- ; When DosFraction overflows, DOS is called.
- mov [(TimerType PTR esi).DosAdder],ax ; Init count to until call dos.
- mov [(TimerType PTR esi).DosFraction],0 ; init the fraction.
- ; now set up the system timer.
- mov ecx,SYSTEM_TIMER_FREQ ; get frequency.
- mov eax,TIMER_CONST ; get constant for formula rate=C/freq.
- xor edx,edx ; make sure zero for divide
- div ecx ; calculate rate
- mov [(TimerType PTR esi).SysCurRate],ax ; Init current stuff.
- mov [(TimerType PTR esi).SysCurError],ax ; Init current stuff.
- mov [(TimerType PTR esi).SysRate],eax ; Save rate of timer
- mov [(TimerType PTR esi).SysError],edx ; Save error of timer
- ; Do not set SysTicks to zero since it always has the same frequency. It
- ; should be zero out only when the system clock is installed.
- ; now set up the user timer.
- mov ecx,[freq] ; get frequency of user timer.
- mov eax,TIMER_CONST ; get constant for formula rate=C/freq.
- xor edx,edx ; make sure zero for divide
- div ecx ; calculate rate
- mov [(TimerType PTR esi).UserCurRate],ax ; Init current stuff.
- mov [(TimerType PTR esi).UserCurError],ax; Init current stuff.
- mov [(TimerType PTR esi).UserRate],eax ; Save rate of timer
- mov [(TimerType PTR esi).UserError],edx ; Save error of timer
- mov [(TimerType PTR esi).UserTicks],0 ; User timer sets to zero when freq change.
- ; Call function to set the rate of the chip, ebx = rate from above.
- call Set_Timer_Rate
- ??timer_not_installed:
- ret
- ENDP
- ;***************************************************************************
- ;* INSTALL_TIMER_Interrupt -- Installs the timer interrupt routine. *
- ;* *
- ;* *
- ;* INPUT: VOID * pointer to RM binary in PM memory. *
- ;* LONG Size of RM binary. *
- ;* INT frequency of user timer. *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/06/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Install_Timer_Interrupt C Near
- USES ebx,ecx,edx
- ARG rm_ptr:DWORD
- ARG rm_size:DWORD
- ARG freq:DWORD
- ARG partial:DWORD
- ; Are they attempting to set timer again?
- cmp [RealModePtr],0
- jnz ??error
- ; Make sure all flags are cleared.
- cmp [InitFlags],0
- jnz ??error
- ; Before setting the interrupt vectors, the code needs to be locked
- ; for DPMI compatability. Any code or data accessed must be lockded
- ; so that no page faults accure during an interrupt.
- ; First lock the code, then the data. The stack will already be locked.
- ; The real mode code is also already locked be default.
- ; To lock a page set up registers :
- ; AX = 0600h
- ; BX:CX = starting linear address of memory block
- ; SI:DI = size of region
- mov eax,0600h ; function number.
- mov ecx,OFFSET LockedCodeStart ; ecx must have start of memory.
- mov edi,OFFSET LockedCodeEnd ; edi will have size of region in bytes.
- shld ebx,ecx,16
- sub edi, ecx
- shld esi,edi,16
- int DPMI_INTR ; do call.
- jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
- or [InitFlags],IF_LOCKED_PM_CODE
- mov eax,0600h ; function number.
- mov ecx,OFFSET LockedDataStart ; ecx must have start of memory.
- mov edi,OFFSET LockedDataEnd ; edi will have size of region in bytes.
- shld ebx,ecx,16
- sub edi, ecx
- shld esi,edi,16
- int DPMI_INTR ; do call.
- jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
- or [InitFlags],IF_LOCKED_PM_DATA
- ; now allocate real mode memory and copy the rm binary down to it.
- mov eax,0100h ; set function number
- mov ebx,[rm_size] ; get size of RM binary.
- add ebx,15 ; round up
- shr ebx,4 ; convert to pages.
- int DPMI_INTR ; do call.
- jc ??error ; check for error.
- mov [ RealModeSel ] , edx
- shl eax,4 ; convert segment to offset.
- mov [RealModePtr],eax ; save offset to global variable.
- ; now lock the real mode memory that we allocated, just in
- ; case it needs to be.
- mov eax,0600h ; function number.
- mov ecx,[RealModePtr] ; ecx must have start of memory.
- mov edi,[rm_size] ; edi will have size of region in bytes.
- mov [RealModeSize],edi ; save off the size for the unlock
- shld ebx,ecx,16
- shld esi,edi,16
- int DPMI_INTR ; do call.
- jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
- or [InitFlags],IF_LOCKED_RM_CODE
- ; set up source and destination pointers for the copy.
- mov eax,[RealModePtr]
- mov esi,[rm_ptr] ; Set up our source pointer.
- or [InitFlags],IF_ALLOC_RM ; set successful
- mov edi,eax ; put it into esi for copy.
- mov ecx,[rm_size]
- rep movsb ; write RM bin to RM memory.
- ; restore esi to point to data and initialize some of it.
- mov esi,[RealModePtr]
- mov eax,esi ; get real mode 32 offset.
- shl eax,12 ; make seg in high eax.
- mov ax,[(TimerType PTR esi).CallRMIntOffset] ; create RM addr of call chain.
- mov [(TimerType PTR esi).CallRMIntAddr],eax ; save it for use in PM int.
- cmp [partial],0
- jne ??partial_exit
- ;==========================================================================
- ; Get the protected mode interrupt vector keyboard.
- ; input ax = 0204
- ; bl = number of interrupt to get
- ; output: cf error
- ;==========================================================================
- mov eax,0204h ; Get proteced mode
- mov bl,IRQ0INTNUM ; IRQ1 interrupt vector
- int DPMI_INTR ; do call.
- jc ??error
- mov [(TimerType PTR esi).OldPMIOffset],edx ; save offset.
- mov [(TimerType PTR esi).OldPMISelector],ecx ; save selector.
- ;==========================================================================
- ; Get the real mode interrupt vector keyboard
- ; input ax = 2503, cl = number of interrupt to get
- ; output cf error, CX:DX = address (seg:off) of RM int handler routine.
- ; cl set above
- ;==========================================================================
- mov eax,0200h
- mov bl,IRQ0INTNUM ; IRQ1 interrupt vector
- int DPMI_INTR ; do call.
- jc ??error
- shl edx,16
- shld ecx,edx,16
- mov [(TimerType PTR esi).OldRMI],ecx
- ;==========================================================================
- ; only separate method of installation is posible.
- ; Now it is time to set the Protected mode interrupt Keyboard
- ; ax = function number (0205
- ; bl = number of interrupt to set
- ; cx:edx = address of PM interrupt handler
- ;==========================================================================
- mov eax, 0205h
- mov bl,IRQ0INTNUM
- mov cx , cs
- lea edx, [Timer_Interrupt] ; get address of protected code int hand.
- int DPMI_INTR ; do call.
- jc ??error
- or [InitFlags],IF_SET_VECTORS
- ;==========================================================================
- ; Now it is time to set the real Interrupt Keyboard
- ; ax = function number (0201
- ; bl = number of interrupt to set
- ; cx:dx = address of RM interrupt handler
- ;==========================================================================
- mov eax, 0201h
- mov bl,IRQ0INTNUM
- mov ecx,[RealModePtr] ; get address of real code int hand.
- shr ecx,4 ; put segment in hi word.
- mov dx,[(TimerType PTR esi).CodeOffset] ; Get address of code
- int DPMI_INTR ; do call.
- jc ??error
- or [InitFlags],IF_SET_VECTORS
- ; now set the frequency.
- mov eax,[freq]
- or [InitFlags],IF_FUNCTIONAL
- push eax
- call NEAR Set_Timer_Frequency
- mov [(TimerType PTR esi).SysTicks],0 ; Only place SysTicks in inited.
- mov [(TimerType PTR esi).UserTicks],0 ; Timers start off on same foot.
- pop eax
- or [InitFlags],IF_RATE_CHANGE
- ; we have finished with success.
- ??partial_exit:
- mov eax,1 ; signal success.
- ret
- ??error:
- xor eax,eax ; signal an error.
- ret
- ENDP
- ;***************************************************************************
- ;* REMOVE_TIMER_INTERRUPT -- Removes the timer interrupt vectors. *
- ;* *
- ;* *
- ;* *
- ;* INPUT: *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/06/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Remove_Timer_Interrupt C NEAR
- USES ebx,ecx,edx
- ; check if timer was previosly install
- mov esi,[RealModePtr]
- test esi,esi
- jz ??error
- test [InitFlags],IF_SET_VECTORS
- jz ??vectors_not_set
- ;==========================================================================
- ; Now it is time to set the real Interrupt Keyboard
- ; ax = function number (0201
- ; bl = number of interrupt to set
- ; cx:dx = address of RM interrupt handler
- ;====================================================================
- mov eax, 0201h
- mov bl,IRQ0INTNUM
- mov edx,[(TimerType esi).OldRMI] ; get the RM address.
- shld ecx , edx , 16
- int DPMI_INTR ; do call.
- jc ??error
- ;==========================================================================
- ; Now it is time to set the Protected mode interrupt
- ; ax = function number (0205
- ; bl = number of interrupt to set
- ; cx:edx = address of PM interrupt handler
- ;==========================================================================
- mov eax, 0205h
- mov bl,IRQ0INTNUM
- mov ecx,[(TimerType esi).OldPMISelector] ; Get PM segment to put int ds later.
- mov edx,[(TimerType esi).OldPMIOffset] ; Get PM offset
- int DPMI_INTR ; do call.
- jc ??error
- ??vectors_not_set:
- ; Call function to set the rate of the chip, ebx = rate.
- test [InitFlags],IF_RATE_CHANGE ; was it changed?
- jz ??rate_not_changed
- mov ebx,0FFFFh ; back to 18.2 time per second.
- call Set_Timer_Rate ; call function to set timer.
- ??rate_not_changed:
- ; now free up the real mode memory.
- test [InitFlags],IF_LOCKED_RM_CODE
- jz ??rm_not_locked
- mov eax , 0601h
- mov ecx, [RealModePtr]
- mov edi, [RealModeSize]
- shld ebx , ecx , 16
- shld esi , edi , 16
- int DPMI_INTR ; do call.
- jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
- ??rm_not_locked:
- test [InitFlags],IF_ALLOC_RM
- jz ??mem_not_allocated
- mov eax , 0101h
- mov edx,[ RealModeSel ] ; get physical address of real mode buffer.
- int DPMI_INTR ; do call.
- jc ??error
- ??mem_not_allocated:
- ; Now we can unlock all stuff needed for the interrupt.
- ; Unlock code
- test [InitFlags],IF_LOCKED_PM_CODE
- jz ??code_not_locked
- mov eax , 0601h
- mov ecx,OFFSET LockedCodeStart ; ecx must have start of memory.
- mov edi,OFFSET LockedCodeEnd ; edx will have size of region in bytes.
- sub edi,ecx ; - figure size.
- shld ebx , ecx , 16
- shld esi , edi , 16
- int DPMI_INTR ; do call.
- jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
- ??code_not_locked:
- ; Unlock data
- test [InitFlags],IF_LOCKED_PM_DATA
- jz ??data_not_locked
- mov ax,0601h ; set es to descriptor of data.
- mov ecx,OFFSET LockedDataStart ; ecx must have start of memory.
- mov edi,OFFSET LockedDataEnd ; edx will have size of region in bytes.
- sub edi,ecx ; - figure size.
- shld ebx , ecx , 16
- shld esi , edi , 16
- int DPMI_INTR ; do call.
- jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
- ??data_not_locked:
- ; we have finished with success.
- mov eax,1 ; signal success.
- mov [RealModePtr],0 ; To say we can do it again sometime.
- mov [InitFlags],0 ; To say we can do it again sometime.
- ret
- ??error:
- xor eax,eax ; signal an error.
- ret
- ENDP
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;/////////////////////////////// Access routines //////////////////////////////////////
- ;***************************************************************************
- ;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured*
- ;* *
- ;* INPUT: TRUE - returns num RM ints. *
- ;* FALSE - return num PM ints. *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/12/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Get_Num_Interrupts C Near
- USES esi
- ARG realmode:DWORD
- mov esi,[RealModePtr]
- cmp [realmode],0
- je ??prot_mode
- mov eax,[(TimerType PTR esi).NumRMInts]
- ret
- ??prot_mode:
- mov eax,[(TimerType PTR esi).NumPMInts]
- ret
- ENDP
- ;***************************************************************************
- ;* GET_SYSTEM_TICK_COUNT -- Returns the system tick count. *
- ;* *
- ;* *
- ;* INPUT: *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/06/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Get_System_Tick_Count C Near
- USES esi
- mov esi,[RealModePtr]
- mov eax,[(TimerType PTR esi).SysTicks]
- ret
- ENDP
- ;***************************************************************************
- ;* GET_USER_TICK_COUNT -- Get tick count of user clock. *
- ;* *
- ;* *
- ;* INPUT: *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/12/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Get_User_Tick_Count C Near
- USES esi
- mov esi,[RealModePtr]
- mov eax,[(TimerType PTR esi).UserTicks]
- ret
- ENDP
- ;//////////////////////////////////////////////////////////////////////////////////////
- ;/////////////////////////////// Interrupt routines ///////////////////////////////////
- ; These macros are placed here to handle to duplicate code in 2 methods in
- ; the Timer_Interrupt function.
- MACRO SET_DS_ESI_TO_RM
- ; Sets DS : ES to point to DGROUP _DATA selector
- ; Set esi to point to RealModePtr
- ; Corrupts eax
- mov ax , _DATA
- mov ds , ax
- mov es , ax
- mov esi,[RealModePtr] ; Point to start of RM data.
- ENDM
-
- MACRO INCREMENT_TIMERS
- ; Expects ESI to point to real mode memory.
- inc [(TimerType PTR esi).NumPMInts] ; For testing.
- mov eax,[(TimerType PTR esi).TrueRate] ; Get the rate of the PC clock.
- sub [(TimerType PTR esi).SysCurRate],ax ; Sub from our rate counter.
- ja ??end_sys ; If !below zero, do not inc.
- mov ebx,[(TimerType PTR esi).SysRate] ; Get rate of timer.
- mov ecx,[(TimerType PTR esi).SysError] ; Get amount of error.
- add [(TimerType PTR esi).SysCurRate],bx ; Add rate to the current.
- sub [(TimerType PTR esi).SysCurError],cx ; Subtract err from error count.
- jb ??error_adj_sys ; If wrapped don't inc.
- inc [(TimerType PTR esi).SysTicks] ; increment the timer.
- jmp short ??end_sys
- ??error_adj_sys:
- add [(TimerType PTR esi).SysCurError],bx ; reajust the error by timer rate.
- ??end_sys:
- sub [(TimerType PTR esi).UserCurRate],ax ; Sub from our rate counter.
- ja ??end_user ; If !below zero, do not inc.
- mov ebx,[(TimerType PTR esi).UserRate] ; Get rate of timer.
- mov ecx,[(TimerType PTR esi).UserError] ; Get amount of error.
- add [(TimerType PTR esi).UserCurRate],bx ; Add rate to the current.
- sub [(TimerType PTR esi).UserCurError],cx; Subtract err from error count.
- jb ??error_adj_user ; If wrapped don't inc.
- inc [(TimerType PTR esi).UserTicks] ; increment the timer.
- jmp short ??end_user
- ??error_adj_user:
- add [(TimerType PTR esi).UserCurError],bx; reajust the error by timer rate.
- ??end_user:
- ENDM
- MACRO ENABLE_CLOCK_INT
- ; Signal 8259 that we are done and that it can bug us again.
- ; Corrupts al.
- mov al,CLEARISR ; Signal EOI
- sti ; enable interrupts.
- out INTCHIP0,al ; 8259 interrupt chip controller 0
- ENDM
- LABEL LockedCodeStart BYTE
- ;***************************************************************************
- ;* TIMER_INTERRUPT -- Temp routine to mimic a timer system calling ours *
- ;* This is a temp routine to call our tick count routine. It will be *
- ;* replaced once another timer system is put in (such as HMI). *
- ;* *
- ;* INPUT: *
- ;* *
- ;* OUTPUT: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 07/06/1994 SKB : Created. *
- ;*=========================================================================*
- PROC Timer_Interrupt Near
- ;////////////////////////////// Call prot mode interrupt vector ////////////////////////////////////////
- ; This routine will do what it needs to,
- ; then it will decide if the old vector should be called.
- ; if so, it calls it and never returns to this function.
- ; if not, we do our own return.
- ; the method for doing this is found in:
- ; "Phar Lap TNT DOS-Extender Reference Manual, First Addition, p. 142"
- ; It says:
- ; 1 - Execute a PUSHFD to save the current flags.
- ; 2 - Dec the stack ptr by 8 bytes to save room for the addr of orig handler
- ; 3 - Push register I use.
- ; 4 - Do any processing.
- ; 5 - Put the address of the original handler in the reserved slot.
- ; 6 - Pop saved register values
- ; 7 - Execute an IRETD to transfer control to original handler.
- pushfd ; Step 1
- sub esp,8 ; Step 2
- push ebp ; Step 3
- mov ebp,esp ; Set up a stack frame to know where to poke address.
- ; Step 3 continued. Push used varables.
- pushad
- push fs gs es ds
- ; Step 4. Now do processing before I chain.
- ; Set up ds:esi to point at start of real memory block (data is first)
- call far Timer_Interrupt_Func
- SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff.
- ; Now take care of calling the old DOS timer interrupt vector.
- ; This must be the last operation of this module since if we call
- ; DOS, we will never return.
- mov ax,[(TimerType PTR ds:esi).DosAdder]
- add [(TimerType PTR esi).DosFraction],ax
- jnc ??no_dos_call ; if not, skip the call.
- ; Tell RM that we forced the int and not to update timers.
- mov [(TimerType PTR esi).PMIssuedInt],1 ; Make it TRUE
- ; Step 5.
- ; Now it is time to set up for the call by returning by poking
- ; the old interrupt handle address in.
- mov eax,[(TimerType PTR esi).OldPMIOffset] ; Get orig offset.
- mov ebx,[(TimerType PTR esi).OldPMISelector] ; Get orig selector.
- mov [ss:ebp+4],eax ; Poke offset.
- mov [ss:ebp+8],ebx ; Poke selector.
- ; Step 6.
- pop ds es gs fs
- popad
- pop ebp
- ; Step 7.
- iretd ; transfer control to original handler.
- ??no_dos_call:
- ENABLE_CLOCK_INT
- ; Restore all registers.
- pop ds es gs fs
- popad
- pop ebp
- add esp,8
- popfd
- iretd
- ;////////////////////////////// Call prot mode interrupt vector ////////////////////////////////////////
- ;//////////////////////////////////////////////////////////////////////////////////////////////////////
- ENDP
- ;***************************************************************************
- ;* TIMER_INTERRUPT_FUNC -- Handles core timer code *
- ;* *
- ;* This function exists so that we can call it from the core HMI driver *
- ;* code when our timer interrupt has not been installed. *
- ;* *
- ;* INPUT: none *
- ;* *
- ;* OUTPUT: none *
- ;* *
- ;* PROTO: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 03/14/1995 PWG : Created. *
- ;*=========================================================================*
- PROC Timer_Interrupt_Func C Far
- pushfd ; save off the flags
- pushad ; save off the main registers
- push fs gs es ds ; save off the seg registers
-
- SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff.
- INCREMENT_TIMERS ; Increment Westwoods timers
- pop ds es gs fs
- popad
- popfd
- retf
- ENDP
- LABEL LockedCodeEnd BYTE
- ;***************************************************************************
- ;* DISABLE_TIMER_INTERRUPT_ONLY -- Disables at the hardware level *
- ;* *
- ;* *
- ;* INPUT: none *
- ;* *
- ;* OUTPUT: none *
- ;* *
- ;* PROTO: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 06/07/1995 DRD : Created. *
- ;*=========================================================================*
- PROC Disable_Timer_Interrupt C Near
- push eax
- pushf
- sti ; disable all interrupts if not disabled
- in al,021h ; read interrupt Mask register bits 0-7
- ; apply to irq's 0-7
- ; value 0 of enabled
- ; value 1 of disabled
- or al,001h ; setup to disable irq 0
- out 021h,al ; disable irq 0
- popf ; possibly enable all interrupts (except 0)
- pop eax
- retf
- ENDP
- ;***************************************************************************
- ;* ENABLE_TIMER_INTERRUPT_ONLY -- Enables at the hardware level *
- ;* *
- ;* *
- ;* INPUT: none *
- ;* *
- ;* OUTPUT: none *
- ;* *
- ;* PROTO: *
- ;* *
- ;* WARNINGS: *
- ;* *
- ;* HISTORY: *
- ;* 06/07/1995 DRD : Created. *
- ;*=========================================================================*
- PROC Enable_Timer_Interrupt C Near
- push eax
- pushf
- sti ; disable all interrupts if not disabled
- in al,021h ; read interrupt Mask register bits 0-7
- ; apply to irq's 0-7
- ; value 0 of enabled
- ; value 1 of disabled
- and al,0FEh ; setup to enable irq 0
- out 021h,al ; enable irq 0
- popf ; possibly enable all interrupts
- pop eax
- retf
- ENDP
- END
|