-
Notifications
You must be signed in to change notification settings - Fork 0
/
A20.C
187 lines (142 loc) · 2.89 KB
/
A20.C
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <dos.h>
#include <stdint.h>
#include <stdio.h>
#include "a20.h"
int a20_check(void)
{
uint8_t old;
uint8_t far *hi;
uint8_t far *lo;
int a20_enabled;
/* This is a memory location only used by ROM BASIC */
lo = MK_FP(0x0, 0x510);
/* 0xffff0 + 0x520 = 0x100510 is an alias iff A20 is off */
hi = MK_FP(0xffff, 0x520);
old = *lo;
*lo = 0x00;
*hi = 0xff;
a20_enabled = (*lo != *hi);
*lo = old;
return a20_enabled;
}
void a20_disable_bios(void)
{
union REGS iregs, oregs;
iregs.x.ax = 0x2400;
int86(0x15, &iregs, &oregs);
}
void a20_enable_bios(void)
{
union REGS iregs, oregs;
iregs.x.ax = 0x2401;
int86(0x15, &iregs, &oregs);
}
static void i8042_wait(void)
{
uint8_t status;
status = inportb(0x64);
while ((status & 0x02) != 0)
;
}
static void i8042_send_cmd(uint8_t cmd)
{
i8042_wait();
outportb(0x64, cmd);
}
static void i8042_send_data(uint8_t data)
{
i8042_wait();
outportb(0x60, data);
}
static uint8_t i8042_get_data(void)
{
uint8_t status;
status = inportb(0x64);
while ((status & 0x01) == 0)
;
return inportb(0x60);
}
void a20_disable_i8042(void)
{
uint8_t a;
disable();
/* Disable keyboard */
i8042_send_cmd(0xAD);
/* Read from port 2 */
i8042_send_cmd(0xD0);
a = i8042_get_data();
/* Write port 2 */
i8042_send_cmd(0xD1);
i8042_send_data(a & ~0x02);
/* Enable keyboard */
i8042_send_cmd(0xAE);
enable();
}
void a20_enable_i8042(void)
{
uint8_t a;
disable();
/* Disable keyboard */
i8042_send_cmd(0xAD);
/* Read from port 2 */
i8042_send_cmd(0xD0);
a = i8042_get_data();
/* Write port 2 */
i8042_send_cmd(0xD1);
i8042_send_data(a | 0x02);
/* Enable keyboard */
i8042_send_cmd(0xAE);
enable();
}
/** Disable gate A20.
*
* @return Zero on succes, non-zero on error
*/
int a20_disable(void)
{
int state;
/* Check if A20 is already disabled */
state = a20_check();
if (state == 0)
return 0;
/* If not, try disabling using BIOS call */
a20_disable_bios();
/* Check again */
state = a20_check();
if (state == 0)
return 0;
/* If still not disabled, try disabling using i8042 */
a20_disable_i8042();
/* Check again */
state = a20_check();
if (state == 0)
return 0;
/* All attempts failed */
return 1;
}
/** Enable gate A20.
*
* @return Zero on succes, non-zero on error
*/
int a20_enable(void)
{
int state;
/* Check if A20 is already enabled */
state = a20_check();
if (state != 0)
return 0;
/* If not, try enabling using BIOS call */
a20_enable_bios();
/* Check again */
state = a20_check();
if (state != 0)
return 0;
/* If still not enabled, try enabling using i8042 */
a20_enable_i8042();
/* Check again */
state = a20_check();
if (state != 0)
return 0;
/* All attempts failed */
return 1;
}