diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 76e8072..ed38b87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,6 +59,10 @@ jobs: run: | cd ${{ github.workspace }}/isapnp wmake + - name: Build `kbtest` + run: | + cd ${{ github.workspace }}/kbtest + wmake - name: Build `pcireg` run: | cd ${{ github.workspace }}/pcireg @@ -84,6 +88,7 @@ jobs: acpi/*.md biosdump/*.md isapnp/*.md + kbtest/*.md pcireg/*.md usblgoff/*.md diff --git a/kbtest/Makefile b/kbtest/Makefile new file mode 100644 index 0000000..7764323 --- /dev/null +++ b/kbtest/Makefile @@ -0,0 +1,22 @@ +# +# 86Box A hypervisor and IBM PC system emulator that specializes in +# running old operating systems and software designed for IBM +# PC systems and compatibles from 1981 through fairly recent +# system designs based on the PCI bus. +# +# This file is part of the 86Box Probing Tools distribution. +# +# Makefile for compiling C-based tools with Watcom. +# +# +# +# Authors: RichardG, +# +# Copyright 2021 RichardG. +# + +SYSTEM = DOS +OBJS = kbtest.obj clib_sys.obj clib_term.obj +DEST = KBTEST.EXE + +!include ../clib/watcom.mk diff --git a/kbtest/README.md b/kbtest/README.md new file mode 100644 index 0000000..03e4cae --- /dev/null +++ b/kbtest/README.md @@ -0,0 +1,12 @@ +kbtest +====== +DOS tool for probing AT-compatible keyboards. + +Usage +----- +Run `kbtest` on a system with an AT or PS/2 keyboard (USB may also work through legacy emulation). The controller configuration and keyboard ID will be displayed. + +Building +-------- +* **Windows:** Run `wmake` from an OpenWatcom "Build Environment" command prompt. +* **Linux:** Run `wmake` with OpenWatcom tools present in `$PATH`. diff --git a/kbtest/kbtest.c b/kbtest/kbtest.c new file mode 100644 index 0000000..4c4bcdd --- /dev/null +++ b/kbtest/kbtest.c @@ -0,0 +1,223 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box Probing Tools distribution. + * + * PS/2 keyboard probing tool. + * + * + * + * Authors: RichardG, + * + * Copyright 2024 RichardG. + * + * ┌──────────────────────────────────────────────────────────────┐ + * │ This file is UTF-8 encoded. If this text is surrounded by │ + * │ garbage, please tell your editor to open this file as UTF-8. │ + * └──────────────────────────────────────────────────────────────┘ + */ +#include +#include +#include "clib_sys.h" +#include "clib_term.h" + +static uint8_t +send_cmd(uint8_t cmd) +{ + uint16_t i; + for (i = 1; i; i++) { + if (!(inb(0x64) & 0x02)) + break; + } + if (!i) { + sti(); + printf("send_cmd(%02X) timed out\n", cmd); + cli(); + return 0; + } + outb(0x64, cmd); + return 1; +} + +static uint8_t +send_data(uint8_t data) +{ + uint16_t i; + for (i = 1; i; i++) { + if (!(inb(0x64) & 0x02)) + break; + } + if (!i) { + sti(); + printf("send_data(%02X) timed out\n", data); + cli(); + return 0; + } + outb(0x60, data); + return 1; +} + +static uint16_t +read_data() +{ + uint16_t i; + for (i = 1; i; i++) { + if (inb(0x64) & 0x01) + break; + } + if (!i) + return -1; + return inb(0x60); +} + +int +main(int argc, char **argv) +{ + uint16_t config, data[16]; + uint8_t old_scanset, new_scanset, i, j; + + term_unbuffer_stdout(); + cli(); + + /* Flush output buffer. */ + i = inb(0x60); + + /* Disable scanning. */ + send_data(0xf5); + if ((data[0] = read_data()) != 0xfa) { + sti(); + printf("Disable scanning failed (%02X)\n", data[0]); + return 1; + } + + /* Disable interrupts and translation. */ + send_cmd(0x20); + config = read_data(); + sti(); + if (config > 0xff) { + printf("Controller configuration read failed\n"); + return 1; + } + printf("Controller configuration: %02X\n", config); + cli(); + send_cmd(0x60); + send_data(config & ~0x43); + + /* Read and print keyboard ID. */ + send_data(0xf2); + if ((data[0] = read_data()) != 0xfa) { + sti(); + printf("Keyboard ID failed (%02X)\n", data[0]); + } else { + while ((data[0] = read_data()) > 0xff); + data[1] = read_data(); + sti(); + if (data[1] < 0x100) + printf("Keyboard ID: %02X %02X\n", data[0], data[1]); + else + printf("Keyboard ID: %02X\n", data[0]); + } + cli(); + + /* Read and print scan code set. */ + data[1] = -2; + data[2] = -2; + send_data(0xf0); + if ((data[0] = read_data()) != 0xfa) { +get_set_fail: + sti(); + printf("Get scan code set failed (%02X %02X %02X)\n", data[0], data[1], data[2]); + return 1; + } else { + send_data(0x00); + if ((data[1] = read_data()) != 0xfa) + goto get_set_fail; + else if ((data[2] = read_data()) > 0xff) + goto get_set_fail; + } + old_scanset = data[2]; + new_scanset = (argc > 1) ? atoi(argv[1]) : 3; + sti(); + printf("Scan code set: %02X (switching to %02X)\n", old_scanset, new_scanset); + cli(); + + /* Enable specified scan code set. */ + data[1] = -2; + send_data(0xf0); + if ((data[0] = read_data()) != 0xfa) { +set_3_fail: + sti(); + printf("Enable set %d failed (%02X %02X)\n", new_scanset, data[0], data[1]); + return 1; + } else { + send_data(0x03); + if ((data[1] = read_data()) != 0xfa) + goto set_3_fail; + } + + /* Reenable scanning. */ + send_data(0xf4); + if ((data[0] = read_data()) != 0xfa) { + sti(); + printf("Enable scanning failed (%02X)\n", data[0]); + return 1; + } + + /* Read and print incoming bytes. */ + do { + while ((data[0] = read_data()) > 0xff); + for (i = 1; i < 16; i++) { + if ((data[i] = read_data()) > 0xff) + break; + } + sti(); + for (j = 0; j < i; j++) + printf("%02X ", data[j]); + cli(); + } while (data[0] != 0x1c); + sti(); + printf("\n"); + cli(); + + /* Disable scanning. */ + send_data(0xf5); + if ((data[0] = read_data()) != 0xfa) { + sti(); + printf("Disable scanning failed (%02X)\n", data[0]); + return 1; + } + + /* Restore old scan code set. */ + data[1] = -2; + send_data(0xf0); + if ((data[0] = read_data()) != 0xfa) { +restore_set_fail: + sti(); + printf("Restore old scan code set failed (%02X %02X)\n", data[0], data[1]); + return 1; + } else { + send_data(old_scanset); + if ((data[1] = read_data()) != 0xfa) + goto restore_set_fail; + } + + /* Restore old configuration. */ + send_cmd(0x60); + send_data(config); + + /* Reenable scanning. */ + send_data(0xf4); + if ((data[0] = read_data()) != 0xfa) { + sti(); + printf("Enable scanning failed (%02X)\n", data[0]); + return 1; + } + + /* We're done here. */ + sti(); + + return 0; +}