1 | ;
|
---|
2 | ; RMINT.ASM -- Initialize interrupts and handle real-mode interrupts
|
---|
3 | ;
|
---|
4 | ; Copyright (c) 1991-1995 by Eberhard Mattes
|
---|
5 | ;
|
---|
6 | ; This file is part of emx.
|
---|
7 | ;
|
---|
8 | ; emx is free software; you can redistribute it and/or modify it
|
---|
9 | ; under the terms of the GNU General Public License as published by
|
---|
10 | ; the Free Software Foundation; either version 2, or (at your option)
|
---|
11 | ; any later version.
|
---|
12 | ;
|
---|
13 | ; emx is distributed in the hope that it will be useful,
|
---|
14 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
15 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
16 | ; GNU General Public License for more details.
|
---|
17 | ;
|
---|
18 | ; You should have received a copy of the GNU General Public License
|
---|
19 | ; along with emx; see the file COPYING. If not, write to
|
---|
20 | ; the Free Software Foundation, 59 Temple Place - Suite 330,
|
---|
21 | ; Boston, MA 02111-1307, USA.
|
---|
22 | ;
|
---|
23 | ; See emx.asm for a special exception.
|
---|
24 | ;
|
---|
25 |
|
---|
26 | INCLUDE EMX.INC
|
---|
27 | INCLUDE TABLES.INC
|
---|
28 | INCLUDE VCPI.INC
|
---|
29 | INCLUDE PMINT.INC
|
---|
30 | INCLUDE EXCEPT.INC
|
---|
31 | INCLUDE RPRINT.INC
|
---|
32 | INCLUDE SIGNAL.INC
|
---|
33 | INCLUDE PROCESS.INC
|
---|
34 | INCLUDE OPTIONS.INC
|
---|
35 | INCLUDE MISC.INC
|
---|
36 |
|
---|
37 | PUBLIC INIT_INT, CLEANUP_INT, INIT_TIMER, CLEANUP_TIMER
|
---|
38 | PUBLIC SET_RM_INT, RESTORE_RM_INT
|
---|
39 |
|
---|
40 | RM_DEBUGGER = FALSE
|
---|
41 |
|
---|
42 | ;
|
---|
43 | ; The interrupt redirection machinery has four states:
|
---|
44 | ;
|
---|
45 | ; - IC_NOT
|
---|
46 | ; This is the initial state: interrupt vectors have not been
|
---|
47 | ; changed yet. Do not restore interrupt vectors on exit!
|
---|
48 | ;
|
---|
49 | ; - IC_VCPI_SERVER
|
---|
50 | ; There is a VCPI server (EMS emulator) which has already reprogrammed
|
---|
51 | ; the interrupt controller to redirect hardware interrupts. Or a
|
---|
52 | ; different client of the VCPI server has redirected the hardware
|
---|
53 | ; interrupts. This is the simple case as we don't have to touch
|
---|
54 | ; the interrupt controller.
|
---|
55 | ;
|
---|
56 | ; - IC_VCPI_CLIENT
|
---|
57 | ; There is a VCPI server (EMS emulator) which hasn't reprogrammed
|
---|
58 | ; the interrupt controller. We have to reprogram the interrupt
|
---|
59 | ; controller in order to distinguish hardware interrupts from
|
---|
60 | ; processor exceptions. PC design flaw...
|
---|
61 | ;
|
---|
62 | ; - IC_MYSELF
|
---|
63 | ; There is no VCPI server (EMS emulator). We have to reprogram the
|
---|
64 | ; interrupt controller in order to distinguish hardware interrupts from
|
---|
65 | ; processor exceptions. PC design flaw...
|
---|
66 | ;
|
---|
67 |
|
---|
68 | IC_NOT = 0 ; Interrupt vectors not changed
|
---|
69 | IC_VCPI_SERVER = 1 ; VCPI server has changed vectors
|
---|
70 | IC_VCPI_CLIENT = 2 ; VCPI server hasn't changed vectors
|
---|
71 | IC_MYSELF = 3 ; This program has changed vectors
|
---|
72 |
|
---|
73 |
|
---|
74 |
|
---|
75 | SV_DATA SEGMENT
|
---|
76 |
|
---|
77 | IF RM_DEBUGGER
|
---|
78 | ;
|
---|
79 | ; The original debug interrupt vectors.
|
---|
80 | ;
|
---|
81 | OLD_RM_INT01_OFF DW ?
|
---|
82 | OLD_RM_INT01_SEG DW ?
|
---|
83 |
|
---|
84 | OLD_RM_INT03_OFF DW ?
|
---|
85 | OLD_RM_INT03_SEG DW ?
|
---|
86 |
|
---|
87 | ENDIF
|
---|
88 |
|
---|
89 | ;
|
---|
90 | ;
|
---|
91 | ; The original interrupt vectors IRQ0_BASE .. IRQ0_BASE+7.
|
---|
92 | ;
|
---|
93 | SAVED_IVECS DD 8 DUP (?)
|
---|
94 |
|
---|
95 | ;
|
---|
96 | ; Check the interrupt vectors in this table in this sequence for
|
---|
97 | ; 8 successive unused interrupts vectors. The first entry should
|
---|
98 | ; be the safest, the last the most dangerous. But all vectors
|
---|
99 | ; are checked, so that nothing evil should happen if a vector
|
---|
100 | ; is already in use. The danger is that some other program may
|
---|
101 | ; change an interrupt vector used by this program for redirecting
|
---|
102 | ; a hardware interrupt. This would cause a system crash.
|
---|
103 | ;
|
---|
104 | UNUSED_INT_TAB DB 50H, 88H, 90H, 98H
|
---|
105 | DB 0A8H, 0B8H, 0C0H, 0C8H, 0D0H, 0D8H
|
---|
106 | DB 80H, 0A0H, 0B0H, 0E0H, 0E8H, 38H, 78H, 58H, 0F0H
|
---|
107 | DB 00H
|
---|
108 |
|
---|
109 | ;
|
---|
110 | ; These are the offsets for the new interrupt vectors used for IRQ0..IRQ7.
|
---|
111 | ; The code at that address just issues INT 08H .. INT 0FH.
|
---|
112 | ;
|
---|
113 | NEW_IVECS DW HWINT0, HWINT1, HWINT2, HWINT3
|
---|
114 | DW HWINT4, HWINT5, HWINT6, HWINT7
|
---|
115 |
|
---|
116 | ;
|
---|
117 | ; These are the first interrupt vector used for redirecting IRQ0..IRQ7
|
---|
118 | ; and IRQ8..IRQ15, respectively.
|
---|
119 | ;
|
---|
120 | IRQ0_BASE DB ?
|
---|
121 | IRQ8_BASE DB ?
|
---|
122 |
|
---|
123 | ;
|
---|
124 | ; This is the interrupt redirection status.
|
---|
125 | ;
|
---|
126 | INT_CHANGED DB IC_NOT
|
---|
127 | ;
|
---|
128 | ; This is the future interrupt redirection status.
|
---|
129 | ;
|
---|
130 | NEW_INT_CHANGED DB IC_NOT
|
---|
131 |
|
---|
132 | ;
|
---|
133 | ; This message should never be required: The VCPI server (or one of its
|
---|
134 | ; clients) has changed the interrupt vectors. But that mappings aren't
|
---|
135 | ; usable.
|
---|
136 | ;
|
---|
137 | $BAD_VCPI DB "Unusable interrupt vector mappings set by VCPI server", CR, LF, 0
|
---|
138 |
|
---|
139 | IF RM_DEBUGGER
|
---|
140 | ;
|
---|
141 | ; There's no real-mode debugger
|
---|
142 | ;
|
---|
143 | $RM_DEBUG DB "Real-mode breakpoint at ", 0
|
---|
144 |
|
---|
145 | ENDIF
|
---|
146 |
|
---|
147 | SV_DATA ENDS
|
---|
148 |
|
---|
149 |
|
---|
150 | INIT_CODE SEGMENT
|
---|
151 |
|
---|
152 | ASSUME CS:INIT_CODE, DS:NOTHING
|
---|
153 |
|
---|
154 | ;
|
---|
155 | ; The original timer interrupt vector. This is in the code segment because
|
---|
156 | ; we need OLD_RM_INT1C for jumping to the next interrupt handler in the
|
---|
157 | ; chain.
|
---|
158 | ;
|
---|
159 | OLD_RM_INT1C LABEL DWORD
|
---|
160 | OLD_RM_INT1C_OFF DW ?
|
---|
161 | OLD_RM_INT1C_SEG DW ?
|
---|
162 | OLD_RM_INT1C_FLAG DB FALSE
|
---|
163 |
|
---|
164 |
|
---|
165 | ;
|
---|
166 | ; Redirect hardware interrupt IRQ X to real-mode interrupt Y
|
---|
167 | ;
|
---|
168 | ASSUME DS:NOTHING
|
---|
169 | HWINTX MACRO X,Y
|
---|
170 | TALIGN 2
|
---|
171 | HWINT&X: INT Y ; Issue software interrupt
|
---|
172 | IRET ; Return
|
---|
173 | ENDM
|
---|
174 | ;
|
---|
175 | ; Redirect IRQ 0 (-> INT 08H) to IRQ 7 (-> INT 0FH)
|
---|
176 | ;
|
---|
177 | IRPC X, <01234567>
|
---|
178 | HWINTX X, %&X+8
|
---|
179 | ENDM
|
---|
180 |
|
---|
181 |
|
---|
182 | ;
|
---|
183 | ; Access rights for 32-bit interrupt gates
|
---|
184 | ;
|
---|
185 | INT_GATE_0 = 08EH ; DPL=0 (hardware interrupt)
|
---|
186 | INT_GATE_1 = 0AEH ; DPL=1
|
---|
187 | INT_GATE_2 = 0CEH ; DPL=2
|
---|
188 | INT_GATE_3 = 0EEH ; DPL=3 (software interrupt)
|
---|
189 |
|
---|
190 |
|
---|
191 | ;
|
---|
192 | ; Initialize IDT, redirect hardware interrupts
|
---|
193 | ;
|
---|
194 | ; This procedure is also called after spawning a DOS program
|
---|
195 | ;
|
---|
196 |
|
---|
197 | ASSUME DS:SV_DATA
|
---|
198 |
|
---|
199 | TALIGN 2
|
---|
200 | INIT_INT PROC NEAR
|
---|
201 | MOV IRQ0_BASE, 40H
|
---|
202 | MOV IRQ8_BASE, 48H
|
---|
203 | CMP MACHINE, MACH_FMR70 ; Fujitsu FMR70?
|
---|
204 | JE II_TABLE ; Yes -> use above values
|
---|
205 | MOV IRQ0_BASE, 40H
|
---|
206 | MOV IRQ8_BASE, 48H
|
---|
207 | CMP MACHINE, MACH_PC98 ; NEC PC-98?
|
---|
208 | JE II_TABLE ; Yes -> use above values
|
---|
209 | MOV IRQ8_BASE, 70H ; The standard configuration
|
---|
210 | MOV NEW_INT_CHANGED, IC_MYSELF
|
---|
211 | CMP VCPI_FLAG, FALSE ; Use VCPI?
|
---|
212 | JE SHORT II_CHECK_INT ; No -> redirect interrupts
|
---|
213 | CALL GET_INT_MAP ; Get interrupt vector mappings
|
---|
214 | MOV IRQ0_BASE, BL ; Controller 1 address
|
---|
215 | MOV IRQ8_BASE, CL ; Controller 2 address
|
---|
216 | CMP BL, 17 ; Conflict with exceptions?
|
---|
217 | JBE SHORT II_NEED_REMAP ; Yes -> remapping required
|
---|
218 | CMP CL, 17 ; Conflict with exceptions?
|
---|
219 | JBE SHORT II_NEED_REMAP ; Yes -> remapping required
|
---|
220 | ;
|
---|
221 | ; Interrupt vector mapping is usable, VCPI server has done this.
|
---|
222 | ;
|
---|
223 | MOV NEW_INT_CHANGED, IC_VCPI_SERVER
|
---|
224 | JMP SHORT II_TABLE ; VCPI server has done the work
|
---|
225 |
|
---|
226 | II_NEED_REMAP: CMP BL, 08H ; Standard configuration?
|
---|
227 | JNE II_ERROR ; No -> almost impossible
|
---|
228 | CMP CL, 70H ; Standard configuration?
|
---|
229 | JNE II_ERROR ; No -> almost impossible
|
---|
230 | MOV NEW_INT_CHANGED, IC_VCPI_CLIENT
|
---|
231 | JMP SHORT II_MYSELF
|
---|
232 |
|
---|
233 | ;
|
---|
234 | ; There may be programs which redirect IRQ0..IRQ7. As we're not
|
---|
235 | ; running under a VCPI server, we should find this out by hand.
|
---|
236 | ; Here's how:
|
---|
237 | ; - save all the interrupt vectors
|
---|
238 | ; - setup 256 *different* interrupt handlers
|
---|
239 | ; - set a variable V in the code segment to some number I unequal 0..255
|
---|
240 | ; - each handler stores its vector number to V if V equals I
|
---|
241 | ; (this must be done in an atomic operation) and then jumps to the
|
---|
242 | ; original handler for that interrupt vector
|
---|
243 | ; - the main program loops until V != I or a loop counter expires
|
---|
244 | ; - restore interrupt vectors
|
---|
245 | ; - if V == 8, we have the original mapping
|
---|
246 | ; Actually, we aren't doing all this. It isn't worth the trouble.
|
---|
247 | ; There's an explicit check for DESQview in VCPI.ASM, instead.
|
---|
248 | ;
|
---|
249 | II_CHECK_INT:
|
---|
250 |
|
---|
251 | ;
|
---|
252 | ; We have to redirect hardware interrupts. Find 8 sucessive unused
|
---|
253 | ; interrupt vectors for redirecting IRQ0..IRQ7.
|
---|
254 | ;
|
---|
255 | II_MYSELF: LEA SI, UNUSED_INT_TAB ; Table of vectors to check
|
---|
256 | XOR AX, AX ; AH:=0
|
---|
257 | MOV ES, AX ; Access interrupt table
|
---|
258 | II_FIND_1: LODS UNUSED_INT_TAB ; Get interupt vector
|
---|
259 | OR AL, AL ; End of table?
|
---|
260 | JZ SHORT II_FIND_FAIL ; Yes -> be brutal
|
---|
261 | MOV BX, AX ; AH=0
|
---|
262 | SHL BX, 2 ; Compute address of vector
|
---|
263 | MOV EDX, ES:[BX] ; Fetch first vector
|
---|
264 | MOV CX, 7 ; Compare to the following 7
|
---|
265 | II_FIND_2: ADD BX, 4 ; Next vector
|
---|
266 | CMP EDX, ES:[BX] ; Same as first vector?
|
---|
267 | JNE II_FIND_1 ; No -> try next table entry
|
---|
268 | LOOP II_FIND_2 ; Repeat for 7 vectors
|
---|
269 | MOV IRQ0_BASE, AL ; Use this vector
|
---|
270 | JMP SHORT II_TABLE ; Continue
|
---|
271 |
|
---|
272 | ;
|
---|
273 | ; No unused interrupt vectors found. We don't have mercy and use some
|
---|
274 | ; interrupt vectors reserved for BASIC. Starting BASIC while this
|
---|
275 | ; program is running will cause a system crash.
|
---|
276 | ;
|
---|
277 | II_FIND_FAIL: MOV IRQ0_BASE, 0D0H
|
---|
278 |
|
---|
279 | ;
|
---|
280 | ; Initialize all IDT entries.
|
---|
281 | ;
|
---|
282 | II_TABLE: LEA DI, IDT+0
|
---|
283 | MOV CX, 256
|
---|
284 | MOV AL, INT_GATE_0
|
---|
285 | MOV DX, OFFSET SV_CODE:INTERRUPT
|
---|
286 | II_UNUSED_1: CALL SET_INT_GATE
|
---|
287 | ADD DI, 8
|
---|
288 | LOOP II_UNUSED_1
|
---|
289 | ;
|
---|
290 | ; Put exceptions into IDT.
|
---|
291 | ;
|
---|
292 | ; Use interrupt gate instead of trap gate to disable interrupts
|
---|
293 | ;
|
---|
294 | LEA SI, EXC_TAB ; Table of handlers
|
---|
295 | II_EXC_1: LODS EXC_TAB ; Get exception number
|
---|
296 | CMP AX, 0FFFFH ; End of table?
|
---|
297 | JE SHORT II_EXC_3
|
---|
298 | MOV BX, AX
|
---|
299 | SHL AX, 3 ; Compute offset into IDT
|
---|
300 | LEA DI, IDT
|
---|
301 | ADD DI, AX ; Compute pointer into IDT
|
---|
302 | LODS EXC_TAB ; Get offset of handler
|
---|
303 | MOV DX, AX
|
---|
304 | MOV AL, INT_GATE_0 ; Interrupt gate, DPL=0
|
---|
305 | CMP BL, 3 ; Breakpoint?
|
---|
306 | JNE SHORT II_EXC_2 ; No -> use DPL=0
|
---|
307 | MOV AL, INT_GATE_3 ; Use DPL=3 (for breakpoints)
|
---|
308 | II_EXC_2: CALL SET_INT_GATE ; Insert into IDT
|
---|
309 | JMP SHORT II_EXC_1 ; Repeat for all table entries
|
---|
310 | II_EXC_3:
|
---|
311 | ;
|
---|
312 | ; Put task gates for exceptions 8 & 10 into IDT.
|
---|
313 | ;
|
---|
314 | LEA DI, IDT+8*8
|
---|
315 | MOV AX, G_TSS_EX8_SEL
|
---|
316 | CALL SET_TASK_GATE
|
---|
317 | LEA DI, IDT+10*8
|
---|
318 | MOV AX, G_TSS_EX10_SEL
|
---|
319 | CALL SET_TASK_GATE
|
---|
320 | ;
|
---|
321 | ; Put hardware interrupts into IDT.
|
---|
322 | ;
|
---|
323 | LEA SI, PMINT0_TAB
|
---|
324 | MOVZX BX, IRQ0_BASE
|
---|
325 | CALL PUT_HW_INT
|
---|
326 | LEA SI, PMINT8_TAB
|
---|
327 | MOVZX BX, IRQ8_BASE
|
---|
328 | CALL PUT_HW_INT
|
---|
329 | ;
|
---|
330 | ; Put software interrupts into IDT.
|
---|
331 | ;
|
---|
332 | IRP SWINT, <10H, 11H, 14H, 16H, 17H, 21H, 31H, 33H>
|
---|
333 | LEA DI, IDT + SWINT*8
|
---|
334 | MOV DX, OFFSET SV_CODE:PMINT_&SWINT
|
---|
335 | MOV AL, INT_GATE_3
|
---|
336 | CALL SET_INT_GATE
|
---|
337 | ENDM
|
---|
338 | ;
|
---|
339 | ; Setup debug interrupt
|
---|
340 | ;
|
---|
341 | IF RM_DEBUGGER
|
---|
342 | LEA DX, RM_DEBUG
|
---|
343 | MOV AL, 01H
|
---|
344 | CALL SET_RM_INT
|
---|
345 | MOV OLD_RM_INT01_OFF, DX
|
---|
346 | MOV OLD_RM_INT01_SEG, AX
|
---|
347 | LEA DX, RM_DEBUG
|
---|
348 | MOV AL, 03H
|
---|
349 | CALL SET_RM_INT
|
---|
350 | MOV OLD_RM_INT03_OFF, DX
|
---|
351 | MOV OLD_RM_INT03_SEG, AX
|
---|
352 | ENDIF
|
---|
353 | ;
|
---|
354 | ; Copy IRQ0_BASE and IRQ8_BASE to IRQ0_ADD and IRQ8_ADD (in the SV_CODE
|
---|
355 | ; segment), respectively, to make them accessible in the protected-mode
|
---|
356 | ; interrupt handler without setting any segment register (CS will be set
|
---|
357 | ; automatically to SV_CODE_SEL).
|
---|
358 | ;
|
---|
359 | MOV AX, SV_CODE ; Used for setting IRQ0_ADD
|
---|
360 | MOV ES, AX ; and IRQ8_ADD
|
---|
361 | ASSUME ES:SV_CODE
|
---|
362 | MOV AL, IRQ0_BASE
|
---|
363 | MOV IRQ0_ADD, AL ; Set IRQ0_ADD
|
---|
364 | MOV AL, IRQ8_BASE
|
---|
365 | MOV IRQ8_ADD, AL ; Set IRQ8_ADD
|
---|
366 | ASSUME ES:NOTHING ; Access to SV_CODE finished
|
---|
367 | ;
|
---|
368 | ; Redirect hardware interrupts if required: reprogram interrupt controller 1
|
---|
369 | ; and notify VCPI server (if present).
|
---|
370 | ;
|
---|
371 | CLI
|
---|
372 | CMP NEW_INT_CHANGED, IC_MYSELF
|
---|
373 | JNE SHORT II_REDIR_1
|
---|
374 | CALL REDIR_INT_VEC
|
---|
375 | MOV CL, IRQ0_BASE
|
---|
376 | CALL SET_HW_INT_BASE
|
---|
377 | JMP SHORT II_REDIR_END
|
---|
378 |
|
---|
379 | II_REDIR_1: CMP NEW_INT_CHANGED, IC_VCPI_CLIENT
|
---|
380 | JNE SHORT II_REDIR_END
|
---|
381 | CALL REDIR_INT_VEC
|
---|
382 | MOV CL, IRQ0_BASE
|
---|
383 | CALL SET_HW_INT_BASE
|
---|
384 | MOVZX BX, IRQ0_BASE
|
---|
385 | MOVZX CX, IRQ8_BASE ; This is always 70H
|
---|
386 | CALL SET_INT_MAP ; Inform VCPI server
|
---|
387 | ;
|
---|
388 | ; Now we can copy NEW_INT_CHANGED to INT_CHANGED.
|
---|
389 | ;
|
---|
390 | II_REDIR_END: MOV AL, NEW_INT_CHANGED
|
---|
391 | MOV INT_CHANGED, AL
|
---|
392 | RET
|
---|
393 |
|
---|
394 | ;
|
---|
395 | ; We cannot cooperate with a buggy VCPI server; give up.
|
---|
396 | ;
|
---|
397 | II_ERROR: LEA DX, $BAD_VCPI
|
---|
398 | CALL RTEXT
|
---|
399 | MOV AL, 0FFH
|
---|
400 | JMP EXIT
|
---|
401 |
|
---|
402 | INIT_INT ENDP
|
---|
403 |
|
---|
404 | ;
|
---|
405 | ; Put hardware interrupts into IDT
|
---|
406 | ;
|
---|
407 | ; In: BX First interrupt number
|
---|
408 | ; DS:SI Table of vectors
|
---|
409 | ;
|
---|
410 | ASSUME DS:SV_DATA
|
---|
411 | PUT_HW_INT PROC NEAR
|
---|
412 | SHL BX, 3
|
---|
413 | LEA DI, IDT[BX]
|
---|
414 | MOV CX, 8
|
---|
415 | PHI_1: LODS PMINT0_TAB ; ...or PMINT8_TAB
|
---|
416 | MOV DX, AX
|
---|
417 | MOV AL, INT_GATE_0 ; Not accessible from ring 3
|
---|
418 | CALL SET_INT_GATE
|
---|
419 | ADD DI, 8
|
---|
420 | LOOP PHI_1
|
---|
421 | RET
|
---|
422 | PUT_HW_INT ENDP
|
---|
423 |
|
---|
424 | ;
|
---|
425 | ; Put interrupt gate (disables interrupts) into IDT
|
---|
426 | ;
|
---|
427 | ; In: DS:DI Pointer to IDT entry
|
---|
428 | ; DX Offset of interrupt routine (SV_CODE:DX)
|
---|
429 | ; AL INT_GATE_0 ... INT_GATE_3
|
---|
430 | ;
|
---|
431 | ASSUME DS:NOTHING
|
---|
432 |
|
---|
433 | SET_INT_GATE PROC NEAR
|
---|
434 | MOV WORD PTR [DI+0], DX
|
---|
435 | MOV WORD PTR [DI+2], G_SV_CODE_SEL
|
---|
436 | MOV BYTE PTR [DI+4], 0
|
---|
437 | MOV BYTE PTR [DI+5], AL
|
---|
438 | MOV WORD PTR [DI+6], 0
|
---|
439 | RET
|
---|
440 | SET_INT_GATE ENDP
|
---|
441 |
|
---|
442 |
|
---|
443 | ;
|
---|
444 | ; Put task gate into IDT
|
---|
445 | ;
|
---|
446 | ; In: DS:DI Pointer to IDT entry
|
---|
447 | ; AX TSS selector
|
---|
448 | ;
|
---|
449 | ASSUME DS:NOTHING
|
---|
450 | SET_TASK_GATE PROC NEAR
|
---|
451 | MOV WORD PTR [DI+0], 0
|
---|
452 | MOV WORD PTR [DI+2], AX
|
---|
453 | MOV BYTE PTR [DI+4], 0
|
---|
454 | MOV BYTE PTR [DI+5], 85H
|
---|
455 | MOV WORD PTR [DI+6], 0
|
---|
456 | RET
|
---|
457 | SET_TASK_GATE ENDP
|
---|
458 |
|
---|
459 |
|
---|
460 | ;
|
---|
461 | ; Setup real-mode interrupt vectors for redirecting IRQ0..IRQ7.
|
---|
462 | ;
|
---|
463 | ASSUME DS:SV_DATA
|
---|
464 | REDIR_INT_VEC PROC NEAR
|
---|
465 | XOR AX, AX
|
---|
466 | MOV ES, AX
|
---|
467 | LEA SI, NEW_IVECS
|
---|
468 | LEA DI, SAVED_IVECS
|
---|
469 | MOV CX, 8
|
---|
470 | MOVZX BX, IRQ0_BASE
|
---|
471 | SHL BX, 2
|
---|
472 | II_41: MOV AX, ES:[BX+0]
|
---|
473 | MOV [DI+0], AX
|
---|
474 | MOV AX, ES:[BX+2]
|
---|
475 | MOV [DI+2], AX
|
---|
476 | MOV AX, [SI]
|
---|
477 | MOV ES:[BX+0], AX
|
---|
478 | MOV ES:[BX+2], CS
|
---|
479 | ADD BX, 4
|
---|
480 | ADD DI, 4
|
---|
481 | ADD SI, 2
|
---|
482 | LOOP II_41
|
---|
483 | RET
|
---|
484 | REDIR_INT_VEC ENDP
|
---|
485 |
|
---|
486 | ;
|
---|
487 | ; Restore interrupt vectors
|
---|
488 | ;
|
---|
489 | ASSUME DS:SV_DATA
|
---|
490 | RESTORE_INT_VEC PROC NEAR
|
---|
491 | XOR AX, AX
|
---|
492 | MOV ES, AX
|
---|
493 | MOVZX DI, IRQ0_BASE
|
---|
494 | SHL DI, 2
|
---|
495 | LEA SI, SAVED_IVECS
|
---|
496 | MOV CX, 8
|
---|
497 | CLD
|
---|
498 | REP MOVSD
|
---|
499 | RET
|
---|
500 | RESTORE_INT_VEC ENDP
|
---|
501 |
|
---|
502 | ;
|
---|
503 | ; Delay for accessing interrupt controller registers
|
---|
504 | ;
|
---|
505 | IO_DELAY MACRO
|
---|
506 | LOCAL SKIP
|
---|
507 | JMP SHORT SKIP
|
---|
508 | SKIP:
|
---|
509 | ENDM
|
---|
510 |
|
---|
511 |
|
---|
512 | ;
|
---|
513 | ; Reprogram interrupt controller #1
|
---|
514 | ;
|
---|
515 | ; In: CL Interrupt base
|
---|
516 | ;
|
---|
517 | ASSUME DS:SV_DATA
|
---|
518 | SET_HW_INT_BASE PROC NEAR
|
---|
519 | IN AL, 21H ; Read IMR
|
---|
520 | MOV AH, AL ; Save old IMR
|
---|
521 | CMP MACHINE, MACH_INBOARD ; Inboard 386/PC
|
---|
522 | JE SHORT SET_HIB_INB
|
---|
523 | MOV AL, 11H ; ICW1
|
---|
524 | OUT 20H, AL
|
---|
525 | IO_DELAY
|
---|
526 | MOV AL, CL ; ICW2
|
---|
527 | OUT 21H, AL
|
---|
528 | IO_DELAY
|
---|
529 | MOV AL, 04H ; ICW3
|
---|
530 | OUT 21H, AL
|
---|
531 | IO_DELAY
|
---|
532 | MOV AL, 01H ; ICW4
|
---|
533 | OUT 21H, AL
|
---|
534 | IO_DELAY
|
---|
535 | SET_HIB_END: MOV AL, AH
|
---|
536 | OUT 21H, AL ; Write IMR
|
---|
537 | RET
|
---|
538 |
|
---|
539 | ;
|
---|
540 | ; Use the values of the original PC for the Inboard 386/PC
|
---|
541 | ;
|
---|
542 | SET_HIB_INB: MOV AL, 13H ; ICW1
|
---|
543 | OUT 20H,AL
|
---|
544 | IO_DELAY
|
---|
545 | MOV AL, CL ; ICW2
|
---|
546 | OUT 21H,AL
|
---|
547 | IO_DELAY
|
---|
548 | MOV AL, 09H ; ICW4
|
---|
549 | OUT 21H, AL
|
---|
550 | JMP SHORT SET_HIB_END
|
---|
551 |
|
---|
552 | SET_HW_INT_BASE ENDP
|
---|
553 |
|
---|
554 |
|
---|
555 | ;
|
---|
556 | ; Install real-mode interrupt handler
|
---|
557 | ;
|
---|
558 | ; In: AL Interrupt number
|
---|
559 | ; DX Offset of interrupt handler (CS=INIT_CODE)
|
---|
560 | ;
|
---|
561 | ; Out: AX Segment of original interrupt handler
|
---|
562 | ; DX Offset of original interrupt handler
|
---|
563 | ;
|
---|
564 | ASSUME DS:NOTHING
|
---|
565 | SET_RM_INT PROC NEAR
|
---|
566 | PUSH DS
|
---|
567 | PUSH ES
|
---|
568 | PUSH BX
|
---|
569 | MOV AH, 35H
|
---|
570 | INT 21H
|
---|
571 | PUSH CS
|
---|
572 | POP DS
|
---|
573 | PUSH BX
|
---|
574 | PUSH ES
|
---|
575 | MOV AH, 25H
|
---|
576 | INT 21H
|
---|
577 | POP AX
|
---|
578 | POP DX
|
---|
579 | POP BX
|
---|
580 | POP ES
|
---|
581 | POP DS
|
---|
582 | RET
|
---|
583 | SET_RM_INT ENDP
|
---|
584 |
|
---|
585 | ;
|
---|
586 | ; Restore real-mode interrupt vector
|
---|
587 | ;
|
---|
588 | ; This is done only if the interrupt vector still points into INIT_CODE
|
---|
589 | ; (avoid restoring the vector if it has been hooked by somebody else)
|
---|
590 | ;
|
---|
591 | ; In: AL Interrupt number
|
---|
592 | ; DX Original offset
|
---|
593 | ; BX Original segment
|
---|
594 | ;
|
---|
595 | ASSUME DS:NOTHING
|
---|
596 | RESTORE_RM_INT PROC NEAR
|
---|
597 | PUSH ES
|
---|
598 | PUSH BX
|
---|
599 | MOV AH, 35H
|
---|
600 | INT 21H
|
---|
601 | MOV BX, ES
|
---|
602 | CMP BX, INIT_CODE
|
---|
603 | POP BX
|
---|
604 | POP ES
|
---|
605 | JNE SHORT RRI_RET
|
---|
606 | PUSH DS
|
---|
607 | MOV DS, BX
|
---|
608 | MOV AH, 25H
|
---|
609 | INT 21H
|
---|
610 | POP DS
|
---|
611 | RRI_RET: RET
|
---|
612 | RESTORE_RM_INT ENDP
|
---|
613 |
|
---|
614 |
|
---|
615 | ;
|
---|
616 | ; Restore interrupts
|
---|
617 | ;
|
---|
618 | ; This procedure is also called before spawning a DOS program
|
---|
619 | ;
|
---|
620 | ASSUME DS:SV_DATA
|
---|
621 | CLEANUP_INT PROC NEAR
|
---|
622 | CMP INT_CHANGED, IC_NOT ; Interrupt vectors remapped?
|
---|
623 | JE SHORT CI_9 ; No -> skip
|
---|
624 | CLI ; Interrupts must be disabled
|
---|
625 | CMP INT_CHANGED, IC_MYSELF ; Done without VCPI?
|
---|
626 | JNE SHORT CI_1 ; No -> try next case
|
---|
627 | CALL RESTORE_INT_VEC ; Restore interrupt vectors
|
---|
628 | MOV CL, 8 ; Restore interrupt controller
|
---|
629 | CALL SET_HW_INT_BASE
|
---|
630 | JMP SHORT CI_8 ; Done
|
---|
631 |
|
---|
632 | CI_1: CMP INT_CHANGED, IC_VCPI_CLIENT ; Done with VCPI?
|
---|
633 | JNE SHORT CI_8 ; No -> skip
|
---|
634 | CALL RESTORE_INT_VEC ; Restore interrupt vectors
|
---|
635 | MOV CL, 8 ; Restore interrupt controller
|
---|
636 | CALL SET_HW_INT_BASE
|
---|
637 | MOV BX, 8 ; Tell VCPI server about change
|
---|
638 | MOV CX, 70H
|
---|
639 | CALL SET_INT_MAP
|
---|
640 |
|
---|
641 | CI_8: MOV INT_CHANGED, IC_NOT ; Back at original state
|
---|
642 | STI ; Interrupts allowed again
|
---|
643 | CI_9: IF RM_DEBUGGER
|
---|
644 | MOV DX, OLD_RM_INT01_OFF
|
---|
645 | MOV BX, OLD_RM_INT01_SEG
|
---|
646 | MOV AL, 01H
|
---|
647 | CALL RESTORE_RM_INT
|
---|
648 | MOV DX, OLD_RM_INT03_OFF
|
---|
649 | MOV BX, OLD_RM_INT03_SEG
|
---|
650 | MOV AL, 03H
|
---|
651 | CALL RESTORE_RM_INT
|
---|
652 | ENDIF
|
---|
653 | RET
|
---|
654 |
|
---|
655 | CLEANUP_INT ENDP
|
---|
656 |
|
---|
657 |
|
---|
658 | ;
|
---|
659 | ; Initialize timer interrupt
|
---|
660 | ;
|
---|
661 | ; This procedure is not called after spawning a DOS program
|
---|
662 | ;
|
---|
663 |
|
---|
664 | ASSUME DS:SV_DATA
|
---|
665 | TALIGN 2
|
---|
666 | INIT_TIMER PROC NEAR
|
---|
667 | CMP MACHINE, MACH_PC98 ; NEC PC-98?
|
---|
668 | JE SHORT IT_DONE ; Yes -> don't set INT 1CH
|
---|
669 | LEA DX, RM_TIMER
|
---|
670 | MOV AL, 1CH
|
---|
671 | CALL SET_RM_INT
|
---|
672 | MOV OLD_RM_INT1C_FLAG, NOT FALSE
|
---|
673 | MOV OLD_RM_INT1C_OFF, DX
|
---|
674 | MOV OLD_RM_INT1C_SEG, AX
|
---|
675 | IT_DONE: RET
|
---|
676 | INIT_TIMER ENDP
|
---|
677 |
|
---|
678 | ;
|
---|
679 | ; Restore timer interrupt
|
---|
680 | ;
|
---|
681 | ; This procedure is not called before spawning a DOS program
|
---|
682 | ;
|
---|
683 | ASSUME DS:SV_DATA
|
---|
684 | TALIGN 2
|
---|
685 | CLEANUP_TIMER PROC NEAR
|
---|
686 | CMP OLD_RM_INT1C_FLAG, FALSE
|
---|
687 | JE SHORT CT_DONE
|
---|
688 | MOV OLD_RM_INT1C_FLAG, FALSE
|
---|
689 | MOV DX, OLD_RM_INT1C_OFF
|
---|
690 | MOV BX, OLD_RM_INT1C_SEG
|
---|
691 | MOV AL, 1CH
|
---|
692 | CALL RESTORE_RM_INT
|
---|
693 | CT_DONE: RET
|
---|
694 | CLEANUP_TIMER ENDP
|
---|
695 |
|
---|
696 | ;
|
---|
697 | ; The timer interrupt handler. Note that interrupt 1CH is not
|
---|
698 | ; available on NEC PC-98.
|
---|
699 | ;
|
---|
700 | ASSUME DS:NOTHING
|
---|
701 | TALIGN 4
|
---|
702 | RM_TIMER PROC NEAR
|
---|
703 | PUSH EAX
|
---|
704 | PUSH DS
|
---|
705 | MOV AX, SV_DATA
|
---|
706 | MOV DS, AX
|
---|
707 | ASSUME DS:SV_DATA
|
---|
708 | ;
|
---|
709 | ; Update clock for __clock()
|
---|
710 | ;
|
---|
711 | ADD CLOCK_LO, 1
|
---|
712 | ADC CLOCK_HI, 0
|
---|
713 | ;
|
---|
714 | ; Decrement timer for emergency exit
|
---|
715 | ;
|
---|
716 | CMP EMERGENCY_TIMER, 0
|
---|
717 | JE SHORT RMT_SKIP_EMERGENCY
|
---|
718 | DEC EMERGENCY_TIMER
|
---|
719 | TALIGN 4
|
---|
720 | RMT_SKIP_EMERGENCY:
|
---|
721 | ;
|
---|
722 | ; Update software timers
|
---|
723 | ;
|
---|
724 | CMP TIMER_MIN, 0
|
---|
725 | JE SHORT RMT_DONE
|
---|
726 | INC TIMER_TICKS
|
---|
727 | DEC TIMER_MIN
|
---|
728 | JNZ SHORT RMT_DONE
|
---|
729 | CALL RMT_CHECK
|
---|
730 | TALIGN 4
|
---|
731 | RMT_DONE: POP DS
|
---|
732 | ASSUME DS:NOTHING
|
---|
733 | POP EAX
|
---|
734 | JMP OLD_RM_INT1C
|
---|
735 | RM_TIMER ENDP
|
---|
736 |
|
---|
737 | ;
|
---|
738 | ; At least one timer expired; examine all timers
|
---|
739 | ;
|
---|
740 | TALIGN 4
|
---|
741 | ASSUME DS:SV_DATA
|
---|
742 | RMT_CHECK PROC NEAR
|
---|
743 | PUSH BX
|
---|
744 | PUSH CX
|
---|
745 | PUSH EDX
|
---|
746 | PUSH DI
|
---|
747 | MOV CX, MAX_TIMERS
|
---|
748 | LEA BX, TIMER_TABLE
|
---|
749 | ASSUME BX:PTR TIMER
|
---|
750 | XOR EDX, EDX
|
---|
751 | MOV TIMER_MIN, EDX
|
---|
752 | XCHG EDX, TIMER_TICKS
|
---|
753 | RMT_LOOP: CMP [BX].T_TYPE, TT_RUNNING
|
---|
754 | JB SHORT RMT_NEXT
|
---|
755 | SUB [BX].T_TICKS, EDX
|
---|
756 | JA SHORT RMT_MIN
|
---|
757 | MOV AX, TT_EXPIRED
|
---|
758 | XCHG AX, [BX].T_TYPE
|
---|
759 | CMP AX, TT_TERMIO_TIME
|
---|
760 | JE SHORT RMT_TERMIO
|
---|
761 | CMP AX, TT_SLEEP
|
---|
762 | JE SHORT RMT_SLEEP
|
---|
763 | CMP AX, TT_ALARM
|
---|
764 | JNE SHORT RMT_NEXT
|
---|
765 | RMT_ALARM: MOV DI, [BX].T_PROCESS
|
---|
766 | ASSUME DI:PTR PROCESS
|
---|
767 | CMP [DI].P_SIG_HANDLERS[4*SIGALRM], SIG_IGN
|
---|
768 | JE SHORT RMT_NEXT
|
---|
769 | BTS [DI].P_SIG_PENDING, SIGALRM ; Generate SIGALRM
|
---|
770 | ASSUME DI:NOTHING
|
---|
771 | JMP SHORT RMT_NEXT
|
---|
772 |
|
---|
773 | RMT_TERMIO: MOV DI, [BX].T_PROCESS
|
---|
774 | OR (PROCESS PTR [DI]).P_FLAGS, PF_TERMIO_TIME
|
---|
775 | JMP SHORT RMT_NEXT
|
---|
776 |
|
---|
777 | RMT_SLEEP: MOV DI, [BX].T_PROCESS
|
---|
778 | OR (PROCESS PTR [DI]).P_FLAGS, PF_SLEEP_FLAG
|
---|
779 | JMP SHORT RMT_NEXT
|
---|
780 |
|
---|
781 | RMT_MIN: MOV EAX, [BX].T_TICKS
|
---|
782 | CMP TIMER_MIN, 0
|
---|
783 | JE SHORT RMT_SET
|
---|
784 | CMP EAX, TIMER_MIN
|
---|
785 | JAE SHORT RMT_NEXT
|
---|
786 | RMT_SET: MOV TIMER_MIN, EAX
|
---|
787 | RMT_NEXT: ADD BX, SIZE TIMER
|
---|
788 | LOOP RMT_LOOP
|
---|
789 | ASSUME BX:NOTHING
|
---|
790 | POP DI
|
---|
791 | POP EDX
|
---|
792 | POP CX
|
---|
793 | POP BX
|
---|
794 | RET
|
---|
795 | RMT_CHECK ENDP
|
---|
796 |
|
---|
797 | IF RM_DEBUGGER
|
---|
798 | ;
|
---|
799 | ; Debug interrupt in real mode
|
---|
800 | ;
|
---|
801 | ASSUME DS:NOTHING
|
---|
802 | RM_DEBUG PROC NEAR
|
---|
803 | MOV AX, SV_DATA
|
---|
804 | MOV DS, AX
|
---|
805 | ASSUME DS:SV_DATA
|
---|
806 | LEA DX, $RM_DEBUG
|
---|
807 | CALL RTEXT
|
---|
808 | MOV BP, SP
|
---|
809 | MOV AX, [BP+2]
|
---|
810 | CALL RWORD
|
---|
811 | MOV AL, ":"
|
---|
812 | CALL RCHAR
|
---|
813 | MOV AX, [BP+0]
|
---|
814 | CALL RWORD
|
---|
815 | CALL RCRLF
|
---|
816 | JMP EXIT
|
---|
817 | RM_DEBUG ENDP
|
---|
818 |
|
---|
819 | ENDIF
|
---|
820 |
|
---|
821 | INIT_CODE ENDS
|
---|
822 |
|
---|
823 | END
|
---|