Fixed the "minor bug fix" in the AT / PS/2 keyboard controller, reworked PS/2 keyboard controller IRQ latches, and correctly disabled memory top remaps if there's more than (16 MB - remap size) RAM (fixes segmentation faults on some machines with 16+ MB of RAM).

This commit is contained in:
OBattler
2023-04-11 23:21:52 +02:00
parent 5da3e78fc1
commit ef17003f1b
17 changed files with 194 additions and 107 deletions

View File

@@ -129,12 +129,12 @@ typedef struct {
/* Keyboard. */
uint8_t kbd_state, key_command, key_wantdata, key_wantcmd,
key_dat, kbd_last_scan_code, sc_or,
key_cmd_queue_start, key_cmd_queue_end, key_queue_start, key_queue_end;
key_dat, kbd_last_scan_code, sc_or, key_cmd_queue_start,
key_cmd_queue_end, key_queue_start, key_queue_end;
/* Mouse. */
uint8_t mouse_state, mouse_wantcmd, mouse_dat, mouse_cmd_queue_start, mouse_cmd_queue_end,
mouse_queue_start, mouse_queue_end;
uint8_t mouse_state, mouse_wantcmd, mouse_dat, mouse_cmd_queue_start,
mouse_cmd_queue_end, mouse_queue_start, mouse_queue_end;
/* Controller. */
uint8_t mem[0x100];
@@ -154,9 +154,6 @@ typedef struct {
/* Mouse - scan FIFO. */
uint8_t mouse_queue[16];
/* Controller. */
uint16_t irq_levels, pad1;
/* Keyboard. */
int out_new, reset_delay;
@@ -624,8 +621,6 @@ static const scancode scancode_set3[512] = {
// clang-format on
};
static void add_data_kbd(uint16_t val);
// #define ENABLE_KEYBOARD_AT_LOG 1
#ifdef ENABLE_KEYBOARD_AT_LOG
int keyboard_at_do_log = ENABLE_KEYBOARD_AT_LOG;
@@ -675,10 +670,8 @@ set_scancode_map(atkbd_t *dev)
}
static void
kbc_queue_reset(uint8_t channel)
kbc_queue_reset(atkbd_t *dev, uint8_t channel)
{
atkbd_t *dev = SavedKbd;
switch (channel) {
case 1:
dev->key_queue_start = dev->key_queue_end = 0;
@@ -738,20 +731,9 @@ kbc_queue_add(atkbd_t *dev, uint8_t val, uint8_t channel)
}
}
static void
kbc_irq(atkbd_t *dev, uint16_t irq, int raise)
{
picint_common(irq, (dev->flags & KBC_TYPE_MASK) > KBC_TYPE_PS2_NOREF, raise);
if (raise)
dev->irq_levels = irq;
else
dev->irq_levels &= ~irq;
}
static int
kbc_translate(uint8_t val)
kbc_translate(atkbd_t *dev, uint8_t val)
{
atkbd_t *dev = SavedKbd;
int xt_mode = (keyboard_mode & 0x20) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF);
int translate = (keyboard_mode & 0x40);
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
@@ -848,7 +830,7 @@ static void
add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_hi)
{
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
int temp = (channel == 1) ? kbc_translate(val) : val;
int temp = (channel == 1) ? kbc_translate(dev, val) : val;
if (temp == -1)
return;
@@ -869,12 +851,12 @@ add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_
dev->status |= STAT_MFULL;
if (dev->mem[0x20] & 0x02)
kbc_irq(dev, 1 << 12, 1);
kbc_irq(dev, 1 << 1, 0);
picint_common(1 << 12, 0, 1);
picint_common(1 << 1, 0, 0);
} else {
if (dev->mem[0x20] & 0x01)
kbc_irq(dev, 1 << 1, 1);
kbc_irq(dev, 1 << 12, 0);
picint_common(1 << 1, 0, 1);
picint_common(1 << 12, 0, 0);
}
} else if (dev->mem[0x20] & 0x01)
picintlevel(1 << 1); /* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */
@@ -1030,7 +1012,7 @@ kbc_poll_at(atkbd_t *dev)
}
/* Do not continue dumping until OBF is clear. */
if (!(dev->status & STAT_OFULL)) {
kbd_log("ATkbc: %02X coming from channel 0\n", dev->key_ctrl_queue[dev-.key_ctrl_queue_start]);
kbd_log("ATkbc: %02X coming from channel 0\n", dev->key_ctrl_queue[dev->key_ctrl_queue_start]);
add_to_kbc_queue_front(dev, dev->key_ctrl_queue[dev->key_ctrl_queue_start], 0, 0x00);
dev->key_ctrl_queue_start = (dev->key_ctrl_queue_start + 1) & 0x3f;
if (dev->key_ctrl_queue_start == dev->key_ctrl_queue_end)
@@ -1221,7 +1203,7 @@ kbc_poll_kbd(atkbd_t *dev)
/* Process the command if needed and then return to main loop #2. */
if (dev->key_wantcmd) {
kbd_log("ATkbc: Processing keyboard command...\n");
kbc_queue_reset(4);
kbc_queue_reset(dev, 4);
// dev->out_new = -1;
kbd_process_cmd(dev);
dev->key_wantcmd = 0;
@@ -1231,18 +1213,18 @@ kbc_poll_kbd(atkbd_t *dev)
case DEV_STATE_MAIN_2:
/* Output from scan queue if needed and then return to main loop #1. */
if (keyboard_scan && (dev->out_new == -1) && (dev->key_queue_start != dev->key_queue_end)) {
kbd_log("ATkbc: %02X (DATA) on channel 1\n", dev->key_queue[key_queue_start]);
kbd_log("ATkbc: %02X (DATA) on channel 1\n", dev->key_queue[dev->key_queue_start]);
dev->out_new = dev->key_queue[dev->key_queue_start];
dev->key_queue_start = (dev->key_queue_start + 1) & 0xf;
}
if (!keyboard_scan || (dev->key_queue_start == dev->key_queue_end))
if (!keyboard_scan || dev->key_wantcmd)
dev->kbd_state = DEV_STATE_MAIN_1;
break;
case DEV_STATE_MAIN_OUT:
case DEV_STATE_RESET_OUT:
/* Output command response and then return to main loop #2. */
if ((dev->out_new == -1) && (dev->key_cmd_queue_start != dev->key_cmd_queue_end)) {
kbd_log("ATkbc: %02X (CMD ) on channel 1\n", key_cmd_queue[key_cmd_queue_start]);
kbd_log("ATkbc: %02X (CMD ) on channel 1\n", dev->key_cmd_queue[dev->key_cmd_queue_start]);
dev->out_new = dev->key_cmd_queue[dev->key_cmd_queue_start];
dev->key_cmd_queue_start = (dev->key_cmd_queue_start + 1) & 0xf;
}
@@ -1263,7 +1245,7 @@ kbc_poll_kbd(atkbd_t *dev)
/* Wait for host data. */
if (dev->key_wantcmd) {
kbd_log("ATkbc: Processing keyboard command...\n");
kbc_queue_reset(4);
kbc_queue_reset(dev, 4);
// dev->out_new = -1;
kbd_process_cmd(dev);
dev->key_wantcmd = 0;
@@ -1304,7 +1286,7 @@ kbc_poll_aux(atkbd_t *dev)
/* Process the command if needed and then return to main loop #2. */
if (dev->mouse_wantcmd) {
kbd_log("ATkbc: Processing mouse command...\n");
kbc_queue_reset(3);
kbc_queue_reset(dev, 3);
// dev->out_new_mouse = -1;
dev->mouse_state = DEV_STATE_MAIN_OUT;
mouse_write(dev->mouse_dat, mouse_p);
@@ -1321,7 +1303,7 @@ kbc_poll_aux(atkbd_t *dev)
dev->out_new_mouse = dev->mouse_queue[dev->mouse_queue_start];
dev->mouse_queue_start = (dev->mouse_queue_start + 1) & 0xf;
}
if (!mouse_scan || (dev->mouse_queue_start == dev->mouse_queue_end))
if (!mouse_scan || dev->mouse_wantcmd)
dev->mouse_state = DEV_STATE_MAIN_1;
break;
case DEV_STATE_MAIN_OUT:
@@ -1349,7 +1331,7 @@ kbc_poll_aux(atkbd_t *dev)
/* Wait for host data. */
if (dev->mouse_wantcmd) {
kbd_log("ATkbc: Processing mouse command...\n");
kbc_queue_reset(3);
kbc_queue_reset(dev, 3);
// dev->out_new_mouse = -1;
dev->mouse_state = DEV_STATE_MAIN_OUT;
mouse_write(dev->mouse_dat, mouse_p);
@@ -1578,10 +1560,10 @@ write_output(atkbd_t *dev, uint8_t val)
/* PS/2: Handle IRQ's. */
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) {
/* IRQ 12 */
kbc_irq(dev, 1 << 12, val & 0x20);
picint_common(1 << 12, 0, val & 0x20);
/* IRQ 1 */
kbc_irq(dev, 1 << 12, val & 0x10);
picint_common(1 << 1, 0, val & 0x10);
}
/* AT, PS/2: Handle A20. */
@@ -1619,6 +1601,23 @@ write_output(atkbd_t *dev, uint8_t val)
dev->output_port = val;
}
static void
write_output_fast_a20(atkbd_t *dev, uint8_t val)
{
uint8_t old = dev->output_port;
kbd_log("ATkbc: write output port in fast A20 mode: %02X (old: %02X)\n", val, dev->output_port);
/* AT, PS/2: Handle A20. */
if ((old ^ val) & 0x02) { /* A20 enable change */
mem_a20_key = val & 0x02;
mem_a20_recalc();
flushmmucache();
}
/* Do this here to avoid an infinite reset loop. */
dev->output_port = val;
}
static void
write_cmd(atkbd_t *dev, uint8_t val)
{
@@ -2261,7 +2260,7 @@ static void
kbd_key_reset(atkbd_t *dev, int do_fa)
{
dev->out_new = -1;
kbc_queue_reset(1);
kbc_queue_reset(dev, 1);
dev->kbd_last_scan_code = 0x00;
@@ -2288,7 +2287,7 @@ static void
kbd_aux_reset(atkbd_t *dev, int do_fa)
{
dev->out_new_mouse = -1;
kbc_queue_reset(2);
kbc_queue_reset(dev, 2);
mouse_scan = 1;
@@ -2395,7 +2394,6 @@ kbd_process_cmd(void *priv)
break;
case 0xf2: /* read ID */
/* Fixed as translation will be done in add_data_kbd(). */
kbd_log("ATkbd: read keyboard id\n");
/* TODO: After keyboard type selection is implemented, make this
return the correct keyboard ID for the selected type. */
@@ -2492,7 +2490,7 @@ kbc_process_cmd(void *priv)
dev->kbc_state = KBC_STATE_MAIN_IBF;
/* Clear the keyboard controller queue. */
kbc_queue_reset(0);
kbc_queue_reset(dev, 0);
switch (dev->ib) {
/* Read data from KBC memory. */
@@ -2555,7 +2553,7 @@ kbc_process_cmd(void *priv)
}
dev->out_new = dev->out_new_mouse = -1;
kbc_queue_reset(0);
kbc_queue_reset(dev, 0);
// dev->kbc_state = KBC_STATE_MAIN_IBF;
dev->kbc_state = KBC_STATE_KBC_OUT;
@@ -2731,7 +2729,6 @@ kbc_process_cmd(void *priv)
if (bad) {
kbd_log("ATkbc: bad controller command %02x data %02x\n", dev->command, dev->ib);
add_data_kbd(0xfe);
}
}
}
@@ -2749,6 +2746,10 @@ kbd_write(uint16_t port, uint8_t val, void *priv)
dev->status &= ~STAT_CD;
if (dev->want60 && (dev->command == 0xd1)) {
kbd_log("ATkbc: write output port\n");
/* Fast A20 - ignore all other bits. */
val = (val & 0x02) | (dev->output_port & 0xfd);
/* Bit 2 of AMI flags is P22-P23 blocked (1 = yes, 0 = no),
discovered by reverse-engineering the AOpeN Vi15G BIOS. */
if (dev->ami_flags & 0x04) {
@@ -2757,7 +2758,9 @@ kbd_write(uint16_t port, uint8_t val, void *priv)
val &= ~0x0c;
val |= (dev->output_port & 0x0c);
}
write_output(dev, val | 0x01);
write_output_fast_a20(dev, val | 0x01);
dev->want60 = 0;
dev->kbc_state = KBC_STATE_MAIN_IBF;
return;
@@ -2798,11 +2801,6 @@ kbd_read(uint16_t port, void *priv)
This also means that in AT mode, the IRQ is level-triggered. */
if ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF)
picintc(1 << 1);
else if (pic_get_pci_flag() || ((dev->flags & KBC_TYPE_MASK) > KBC_TYPE_PS2_NOREF)) {
/* PS/2 MCA: Latched as level-sensitive until port 0x60 is read (and with it, OBF is cleared),
in accordance with the IBM PS/2 Model 80 Technical Reference Manual. */
kbc_irq(dev, dev->irq_levels, 0);
}
break;
case 0x64:
@@ -2849,7 +2847,7 @@ kbd_reset(void *priv)
dev->out_new = dev->out_new_mouse = -1;
for (i = 0; i < 3; i++)
kbc_queue_reset(i);
kbc_queue_reset(dev, i);
dev->kbd_last_scan_code = 0;
dev->sc_or = 0;
@@ -3290,14 +3288,6 @@ keyboard_at_set_mouse(void (*func)(uint8_t val, void *priv), void *priv)
mouse_p = priv;
}
void
keyboard_at_adddata_keyboard_raw(uint8_t val)
{
atkbd_t *dev = SavedKbd;
add_data_kbd_queue(dev, val);
}
void
keyboard_at_adddata_mouse(uint8_t val)
{
@@ -3330,28 +3320,6 @@ keyboard_at_mouse_pos(void)
return ((dev->mouse_queue_end - dev->mouse_queue_start) & 0xf);
}
void
keyboard_at_set_mouse_scan(uint8_t val)
{
atkbd_t *dev = SavedKbd;
uint8_t temp_mouse_scan = val ? 1 : 0;
if (temp_mouse_scan == !(dev->mem[0x20] & 0x20))
return;
set_enable_mouse(dev, val ? 1 : 0);
kbd_log("ATkbc: mouse scan %sabled via PCI\n", mouse_scan ? "en" : "dis");
}
uint8_t
keyboard_at_get_mouse_scan(void)
{
atkbd_t *dev = SavedKbd;
return ((dev->mem[0x20] & 0x20) ? 0x00 : 0x10);
}
void
keyboard_at_set_a20_key(int state)
{