1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
/*
Fake86: A portable, open-source 8086 PC emulator.
Copyright (C)2010-2013 Mike Chambers
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* i8259.c: functions to emulate the Intel 8259 prioritized interrupt controller.
note: this is not a very complete 8259 implementation, but for the purposes
of a PC, it's all we need. */
#include <stdint.h>
#include <string.h>
#include "i8259.h"
struct structpic i8259;
extern uint8_t keyboardwaitack;
extern void set_port_write_redirector(uint16_t startport, uint16_t endport, void *callback);
extern void set_port_read_redirector(uint16_t startport, uint16_t endport, void *callback);
uint8_t in8259(uint16_t portnum) {
switch (portnum & 1) {
case 0:
if (i8259.readmode==0) return(i8259.irr); else return(i8259.isr);
case 1: //read mask register
return(i8259.imr);
}
return (0);
}
extern uint32_t makeupticks;
void out8259(uint16_t portnum, uint8_t value) {
uint8_t i;
switch (portnum & 1) {
case 0:
if (value & 0x10) { //begin initialization sequence
i8259.icwstep = 1;
i8259.imr = 0; //clear interrupt mask register
i8259.icw[i8259.icwstep++] = value;
return;
}
if ((value & 0x98)==8) { //it's an OCW3
if (value & 2) i8259.readmode = value & 2;
}
if (value & 0x20) { //EOI command
keyboardwaitack = 0;
for (i=0; i<8; i++)
if ((i8259.isr >> i) & 1) {
i8259.isr ^= (1 << i);
if ((i==0) && (makeupticks>0)) { makeupticks = 0; i8259.irr |= 1; }
return;
}
}
break;
case 1:
if ((i8259.icwstep==3) && (i8259.icw[1] & 2)) i8259.icwstep = 4; //single mode, so don't read ICW3
if (i8259.icwstep<5) { i8259.icw[i8259.icwstep++] = value; return; }
//if we get to this point, this is just a new IMR value
i8259.imr = value;
break;
}
}
uint8_t nextintr() {
uint8_t i, tmpirr;
tmpirr = i8259.irr & (~i8259.imr); //XOR request register with inverted mask register
for (i=0; i<8; i++)
if ((tmpirr >> i) & 1) {
i8259.irr ^= (1 << i);
i8259.isr |= (1 << i);
return(i8259.icw[2] + i);
}
return(0); //this won't be reached, but without it the compiler gives a warning
}
void doirq(uint8_t irqnum) {
i8259.irr |= (1 << irqnum);
if (irqnum == 1) keyboardwaitack = 1;
}
void init8259() {
memset((void *)&i8259, 0, sizeof(i8259));
set_port_write_redirector(0x20, 0x21, &out8259);
set_port_read_redirector(0x20, 0x21, &in8259);
}
|