Merge branch 'ibmpc_update'

This commit is contained in:
tmk 2020-03-02 14:36:38 +09:00
commit 4052955535
9 changed files with 4019 additions and 2514 deletions

View file

@ -145,6 +145,9 @@ https://www.seasip.info/VintagePC/ibm_1391406.html)
[7] [The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard](
https://www.seasip.info/VintagePC/ibm_6110344.html)
[8] [IBM PC AT Technical Reference 1986](
(http://bitsavers.org/pdf/ibm/pc/at/6183355_PC_AT_Technical_Reference_Mar86.pdf)
[y] [TrackPoint Engineering Specifications for version 3E](
https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -44,38 +44,47 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* Pin and interrupt configuration
*/
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32U2__) || defined(__AVR_AT90USB1286__)
/* uses INT1 for clock line */
/* clock line */
#define IBMPC_CLOCK_PORT PORTD
#define IBMPC_CLOCK_PIN PIND
#define IBMPC_CLOCK_DDR DDRD
#define IBMPC_CLOCK_BIT 1
/* data line */
#define IBMPC_DATA_PORT PORTD
#define IBMPC_DATA_PIN PIND
#define IBMPC_DATA_DDR DDRD
#define IBMPC_DATA_BIT 0
/* reset line */
#define IBMPC_RST_PORT PORTB
#define IBMPC_RST_PIN PINB
#define IBMPC_RST_DDR DDRB
#define IBMPC_RST_BIT1 6
#define IBMPC_RST_BIT2 7
/* reset for XT keyboard: low pulse for 500ms and after that HiZ for safety */
#define IBMPC_RESET() do { \
/* reset for XT Type-1 keyboard: low pulse for 500ms */
#define IBMPC_RST_HIZ() do { \
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT1); \
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT1); \
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT2); \
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT2); \
} while (0)
#define IBMPC_RST_LO() do { \
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT1); \
IBMPC_RST_DDR |= (1<<IBMPC_RST_BIT1); \
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT2); \
IBMPC_RST_DDR |= (1<<IBMPC_RST_BIT2); \
_delay_ms(500); \
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT1); \
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT2); \
} while (0)
/* interrupt for clock line */
#define IBMPC_INT_INIT() do { \
EICRA |= ((1<<ISC11) | \
(0<<ISC10)); \
} while (0)
/* NOTE: clear flag and enabling to ditch unwanted interrupt */
#define IBMPC_INT_ON() do { \
EIFR |= (1<<INTF1); \
EIMSK |= (1<<INT1); \
} while (0)

View file

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "timer.h"
#include "action.h"
#include "ibmpc_usb.h"
#include "ibmpc.h"
static void matrix_make(uint8_t code);
@ -55,33 +56,37 @@ static uint16_t read_keyboard_id(void)
int16_t code = 0;
// Disable
code = ibmpc_host_send(0xF5);
//code = ibmpc_host_send(0xF5);
// Read ID
code = ibmpc_host_send(0xF2);
if (code == -1) return 0xFFFF; // XT or No keyboard
if (code != 0xFA) return 0xFFFE; // Broken PS/2?
if (code == -1) { id = 0xFFFF; goto DONE; } // XT or No keyboard
if (code != 0xFA) { id = 0xFFFE; goto DONE; } // Broken PS/2?
code = read_wait(1000);
if (code == -1) return 0x0000; // AT
// ID takes 500ms max TechRef [8] 4-41
code = read_wait(500);
if (code == -1) { id = 0x0000; goto DONE; } // AT
id = (code & 0xFF)<<8;
code = read_wait(1000);
code = read_wait(500);
id |= code & 0xFF;
DONE:
// Enable
code = ibmpc_host_send(0xF4);
//code = ibmpc_host_send(0xF4);
return id;
}
void hook_early_init(void)
{
ibmpc_host_init();
ibmpc_host_enable();
}
void matrix_init(void)
{
debug_enable = true;
ibmpc_host_init();
// hard reset for XT keyboard
IBMPC_RESET();
// initialize matrix state: all keys off
for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
@ -106,17 +111,21 @@ uint8_t matrix_scan(void)
// scan code reading states
static enum {
INIT,
WAIT_STARTUP,
XT_RESET,
XT_RESET_WAIT,
XT_RESET_DONE,
WAIT_AA,
WAIT_AABF,
WAIT_AABFBF,
READ_ID,
LED_SET,
SETUP,
LOOP,
END
} state = INIT;
static uint16_t last_time;
static uint16_t init_time;
if (ibmpc_error) {
xprintf("err: %02X\n", ibmpc_error);
xprintf("\nERR:%02X\n", ibmpc_error);
// when recv error, neither send error nor buffer full
if (!(ibmpc_error & (IBMPC_ERR_SEND | IBMPC_ERR_FULL))) {
@ -133,82 +142,135 @@ uint8_t matrix_scan(void)
switch (state) {
case INIT:
ibmpc_protocol = IBMPC_PROTOCOL_AT;
xprintf("I%u ", timer_read());
keyboard_kind = NONE;
keyboard_id = 0x0000;
last_time = timer_read();
state = WAIT_STARTUP;
matrix_clear();
clear_keyboard();
state = XT_RESET;
break;
case WAIT_STARTUP:
// read and ignore BAT code and other codes when power-up
ibmpc_host_recv();
if (timer_elapsed(last_time) > 1000) {
case XT_RESET:
// Reset XT-initialize keyboard
// XT: hard reset 500ms for IBM XT Type-1 keyboard and clones
// XT: soft reset 20ms min(clock Lo)
ibmpc_host_disable(); // soft reset: inihibit(clock Lo/Data Hi)
IBMPC_RST_LO(); // hard reset: reset pin Lo
init_time = timer_read();
state = XT_RESET_WAIT;
break;
case XT_RESET_WAIT:
if (timer_elapsed(init_time) > 500) {
state = XT_RESET_DONE;
}
break;
case XT_RESET_DONE:
IBMPC_RST_HIZ(); // hard reset: reset pin HiZ
ibmpc_host_isr_clear();
ibmpc_host_enable(); // soft reset: idle(clock Hi/Data Hi)
xprintf("X%u ", timer_read());
init_time = timer_read();
state = WAIT_AA;
break;
case WAIT_AA:
// 1) Read BAT code and ID on keybaord power-up
// For example, XT/AT sends 'AA' and Terminal sends 'AA BF BF' after BAT
// AT 84-key: POR and BAT can take 900-9900ms according to AT TechRef [8] 4-7
// AT 101/102-key: POR and BAT can take 450-2500ms according to AT TechRef [8] 4-39
// 2) Read key typed by user or anything after error on protocol or scan code
// This can happen in case of keyboard hotswap, unstable hardware, signal integrity problem or bug
/* wait until keyboard sends any code without 10000ms timeout
if (timer_elapsed(init_time) > 10000) {
state = READ_ID;
}
*/
if (ibmpc_host_recv() != -1) { // wait for AA
xprintf("W%u ", timer_read());
init_time = timer_read();
state = WAIT_AABF;
}
break;
case WAIT_AABF:
// NOTE: we can omit to wait BF BF
// ID takes 500ms max? TechRef [8] 4-41, though 1ms is enough for 122-key Terminal 6110345
if (timer_elapsed(init_time) > 500) {
state = READ_ID;
}
if (ibmpc_host_recv() != -1) { // wait for BF
xprintf("W%u ", timer_read());
init_time = timer_read();
state = WAIT_AABFBF;
}
break;
case WAIT_AABFBF:
if (timer_elapsed(init_time) > 500) {
state = READ_ID;
}
if (ibmpc_host_recv() != -1) { // wait for BF
xprintf("W%u ", timer_read());
state = READ_ID;
}
break;
case READ_ID:
xprintf("R%u ", timer_read());
keyboard_id = read_keyboard_id();
if (ibmpc_error) {
xprintf("err: %02X\n", ibmpc_error);
xprintf("\nERR:%02X\n", ibmpc_error);
ibmpc_error = IBMPC_ERR_NONE;
}
xprintf("ID: %04X\n", keyboard_id);
if (0xAB00 == (keyboard_id & 0xFF00)) {
// CodeSet2 PS/2
if (0xAB00 == (keyboard_id & 0xFF00)) { // CodeSet2 PS/2
keyboard_kind = PC_AT;
} else if (0xBF00 == (keyboard_id & 0xFF00)) {
// CodeSet3 Terminal
} else if (0xBF00 == (keyboard_id & 0xFF00)) { // CodeSet3 Terminal
keyboard_kind = PC_TERMINAL;
} else if (0x0000 == keyboard_id) {
// CodeSet2 AT
} else if (0x0000 == keyboard_id) { // CodeSet2 AT
keyboard_kind = PC_AT;
} else if (0xFFFF == keyboard_id) {
// CodeSet1 XT
} else if (0xFFFF == keyboard_id) { // CodeSet1 XT
keyboard_kind = PC_XT;
} else if (0xFFFE == keyboard_id) {
// CodeSet2 PS/2 fails to response?
} else if (0xFFFE == keyboard_id) { // CodeSet2 PS/2 fails to response?
keyboard_kind = PC_AT;
} else if (0x00FF == keyboard_id) {
// Mouse is not supported
} else if (0x00FF == keyboard_id) { // Mouse is not supported
xprintf("Mouse: not supported\n");
keyboard_kind = NONE;
} else {
keyboard_kind = PC_AT;
}
// protocol
if (keyboard_kind == PC_XT) {
xprintf("kbd: XT\n");
ibmpc_protocol = IBMPC_PROTOCOL_XT;
} else if (keyboard_kind == PC_AT) {
xprintf("kbd: AT\n");
ibmpc_protocol = IBMPC_PROTOCOL_AT;
} else if (keyboard_kind == PC_TERMINAL) {
xprintf("kbd: Terminal\n");
ibmpc_protocol = IBMPC_PROTOCOL_AT;
// Set all keys - make/break [3]p.23
ibmpc_host_send(0xF8);
} else {
xprintf("kbd: Unknown\n");
ibmpc_protocol = IBMPC_PROTOCOL_AT;
}
state = LED_SET;
xprintf("ID:%04X(%d)\n", keyboard_id, keyboard_kind);
state = SETUP;
break;
case LED_SET:
led_set(host_keyboard_leds());
case SETUP:
xprintf("S%u ", timer_read());
switch (keyboard_kind) {
case PC_XT:
break;
case PC_AT:
led_set(host_keyboard_leds());
break;
case PC_TERMINAL:
ibmpc_host_send(0xF8);
break;
default:
break;
}
state = LOOP;
xprintf("L%u ", timer_read());
case LOOP:
switch (keyboard_kind) {
case PC_XT:
process_cs1();
if (process_cs1() == -1) state = INIT;
break;
case PC_AT:
process_cs2();
if (process_cs2() == -1) state = INIT;
break;
case PC_TERMINAL:
process_cs3();
if (process_cs3() == -1) state = INIT;
break;
default:
break;
@ -349,7 +411,7 @@ static uint8_t cs1_e0code(uint8_t code) {
case 0x63: return 0x7B; // Wake (MUHENKAN)
default:
xprintf("!CS1_?!\n");
xprintf("!CS1_E0_%02X!\n", code);
return code;
}
return 0x00;
@ -360,7 +422,7 @@ static int8_t process_cs1(void)
static enum {
INIT,
E0,
// Pause: E1 1D 45, E1 9D C5
// Pause: E1 1D 45, E1 9D C5 [a] (TODO: test)
E1,
E1_1D,
E1_9D,
@ -371,9 +433,22 @@ static int8_t process_cs1(void)
return 0;
}
// Check invalid codes; 0x59-7F won't be used in real XT keyboards probably
// 0x62 is used to handle escape code E0 and E1
if ((code & 0x7F) >= 0x62) {
xprintf("!CS1_INV!\n");
state = INIT;
return -1;
}
switch (state) {
case INIT:
switch (code) {
case 0x00:
case 0xFF: // Error/Overrun [3]p.26
xprintf("!CS1_ERR!\n");
return -1;
break;
case 0xE0:
state = E0;
break;
@ -591,6 +666,17 @@ static int8_t process_cs2(void)
switch (state) {
case INIT:
switch (code) {
case 0x00: // Error/Overrun [3]p.26
xprintf("!CS2_OVR!\n");
matrix_clear();
clear_keyboard();
break;
case 0xFF:
matrix_clear();
xprintf("!CS2_ERR!\n");
state = INIT;
return -1;
break;
case 0xE0:
state = E0;
break;
@ -608,11 +694,6 @@ static int8_t process_cs2(void)
matrix_make(0x6F);
state = INIT;
break;
case 0x00: // Overrun [3]p.26
matrix_clear();
xprintf("!CS2_OVERRUN!\n");
state = INIT;
break;
case 0xAA: // Self-test passed
case 0xFC: // Self-test failed
// reset or plugin-in new keyboard
@ -620,13 +701,14 @@ static int8_t process_cs2(void)
return -1;
break;
default: // normal key make
state = INIT;
if (code < 0x80) {
matrix_make(code);
} else {
matrix_clear();
xprintf("!CS2_INIT!\n");
return -1;
}
state = INIT;
}
break;
case E0: // E0-Prefixed
@ -639,13 +721,14 @@ static int8_t process_cs2(void)
state = E0_F0;
break;
default:
state = INIT;
if (code < 0x80) {
matrix_make(cs2_e0code(code));
} else {
matrix_clear();
xprintf("!CS2_E0!\n");
return -1;
}
state = INIT;
}
break;
case F0: // Break code
@ -659,13 +742,14 @@ static int8_t process_cs2(void)
state = INIT;
break;
default:
state = INIT;
if (code < 0x80) {
matrix_break(code);
} else {
matrix_clear();
xprintf("!CS2_F0!\n");
return -1;
}
state = INIT;
}
break;
case E0_F0: // Break code of E0-prefixed
@ -675,13 +759,14 @@ static int8_t process_cs2(void)
state = INIT;
break;
default:
state = INIT;
if (code < 0x80) {
matrix_break(cs2_e0code(code));
} else {
matrix_clear();
xprintf("!CS2_E0_F0!\n");
return -1;
}
state = INIT;
}
break;
// Pause make: E1 14 77
@ -764,9 +849,14 @@ static int8_t process_cs3(void)
switch (state) {
case READY:
switch (code) {
case 0x00:
case 0xff:
xprintf("!CS3_%02X!\n", code);
case 0x00: // Error/Overrun [3]p.26
xprintf("!CS3_OVR!\n");
matrix_clear();
clear_keyboard();
break;
case 0xFF:
xprintf("!CS3_ERR!\n");
return -1;
break;
case 0xF0:
state = F0;
@ -781,18 +871,24 @@ static int8_t process_cs3(void)
if (code < 0x80) {
matrix_make(code);
} else {
xprintf("!CS3_%02X!\n", code);
xprintf("!CS3_READY!\n");
return -1;
}
state = READY;
}
break;
case F0: // Break code
switch (code) {
case 0x00:
case 0xff:
xprintf("!CS3_F0_%02X!\n", code);
xprintf("!CS3_F0_OVR!\n");
matrix_clear();
clear_keyboard();
state = READY;
break;
case 0xFF:
xprintf("!CS3_F0_ERR!\n");
state = READY;
return -1;
break;
case 0x83: // F7
matrix_break(0x02);
state = READY;
@ -802,12 +898,13 @@ static int8_t process_cs3(void)
state = READY;
break;
default:
state = READY;
if (code < 0x80) {
matrix_break(code);
} else {
xprintf("!CS3_F0_%02X!\n", code);
xprintf("!CS3_F0!\n");
return -1;
}
state = READY;
}
break;
}

View file

@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "unimap_trans.h"
#define AC_FN0 ACTION_LAYER_MOMENTARY(1)
#define AC_FN0 ACTION_LAYER_TAP_KEY(1, KC_APPLICATION)
#ifdef KEYMAP_SECTION_ENABLE
const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS] __attribute__ ((section (".keymap.keymaps"))) = {

View file

@ -41,7 +41,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <stdbool.h>
#include <avr/interrupt.h>
#include "ringbuf.h"
#include <util/atomic.h>
#include "ibmpc.h"
#include "debug.h"
#include "timer.h"
@ -56,27 +56,40 @@ POSSIBILITY OF SUCH DAMAGE.
} while (0)
#define BUF_SIZE 16
static uint8_t buf[BUF_SIZE];
static ringbuf_t rb = {
.buffer = buf,
.head = 0,
.tail = 0,
.size_mask = BUF_SIZE - 1
};
volatile uint8_t ibmpc_protocol = IBMPC_PROTOCOL_AT;
volatile uint8_t ibmpc_protocol = IBMPC_PROTOCOL_NO;
volatile uint8_t ibmpc_error = IBMPC_ERR_NONE;
/* 2-byte buffer for data received from keyhboard
* buffer states:
* FFFF: empty
* FFss: one data
* sstt: two data(full)
* 0xFF can not be stored as data in buffer because it means empty or no data.
*/
static volatile uint16_t recv_data = 0xFFFF;
/* internal state of receiving data */
static volatile uint16_t isr_state = 0x8000;
static uint8_t timer_start = 0;
void ibmpc_host_init(void)
{
clock_init();
data_init();
idle();
// initialize reset pin to HiZ
IBMPC_RST_HIZ();
inhibit();
IBMPC_INT_INIT();
IBMPC_INT_OFF();
}
void ibmpc_host_enable(void)
{
IBMPC_INT_ON();
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
//wait_ms(2500);
idle();
}
void ibmpc_host_disable(void)
{
IBMPC_INT_OFF();
inhibit();
}
int16_t ibmpc_host_send(uint8_t data)
@ -84,8 +97,6 @@ int16_t ibmpc_host_send(uint8_t data)
bool parity = true;
ibmpc_error = IBMPC_ERR_NONE;
if (ibmpc_protocol == IBMPC_PROTOCOL_XT) return -1;
dprintf("w%02X ", data);
IBMPC_INT_OFF();
@ -130,6 +141,10 @@ int16_t ibmpc_host_send(uint8_t data)
WAIT(clock_hi, 50, 8);
WAIT(data_hi, 50, 9);
// clear buffer to get response correctly
recv_data = 0xFFFF;
ibmpc_host_isr_clear();
idle();
IBMPC_INT_ON();
return ibmpc_host_recv_response();
@ -140,119 +155,179 @@ ERROR:
return -1;
}
/*
* Receive data from keyboard
*/
int16_t ibmpc_host_recv(void)
{
uint16_t data = 0;
uint8_t ret = 0xFF;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
data = recv_data;
if ((data&0xFF00) != 0xFF00) { // recv_data:sstt -> recv_data:FFtt, ret:ss
ret = (data>>8)&0x00FF;
recv_data = data | 0xFF00;
} else if (data != 0xFFFF) { // recv_data:FFss -> recv_data:FFFF, ret:ss
ret = data&0x00FF;
recv_data = data | 0x00FF;
}
}
if ((data | 0x00FF) != 0xFFFF) dprintf("b%04X ", data);
if (ret != 0xFF) dprintf("r%02X ", ret);
return ((ret != 0xFF) ? ret : -1);
}
int16_t ibmpc_host_recv_response(void)
{
// Command may take 25ms/20ms at most([5]p.46, [3]p.21)
uint8_t retry = 25;
while (retry-- && ringbuf_is_empty(&rb)) {
int16_t data = -1;
while (retry-- && (data = ibmpc_host_recv()) == -1) {
wait_ms(1);
}
int16_t data = ringbuf_get(&rb);
if (data != -1) dprintf("r%02X ", data);
return data;
}
/* get data received by interrupt */
int16_t ibmpc_host_recv(void)
void ibmpc_host_isr_clear(void)
{
int16_t data = ringbuf_get(&rb);
if (data != -1) dprintf("r%02X ", data);
return data;
isr_state = 0x8000;
recv_data = 0xFFFF;
}
// NOTE: With this ISR data line can be read within 2us after clock falling edge.
// To read data line early as possible:
// write naked ISR with asembly code to read the line and call C func to do other job?
ISR(IBMPC_INT_VECT)
{
static uint16_t last_time = 0;
static enum {
START,
BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
PARITY,
STOP,
} state = START;
static uint8_t data = 0;
static uint8_t parity = 1;
uint8_t dbit;
dbit = IBMPC_DATA_PIN&(1<<IBMPC_DATA_BIT);
// return unless falling edge
if (clock_in()) {
return;
// Timeout check
uint8_t t;
// use only the least byte of millisecond timer
asm("lds %0, %1" : "=r" (t) : "p" (&timer_count));
//t = (uint8_t)timer_count; // compiler uses four registers instead of one
if (isr_state == 0x8000) {
timer_start = t;
} else {
// should not take more than 1ms
if (timer_start != t && (uint8_t)(timer_start + 1) != t) {
ibmpc_error = IBMPC_ERR_TIMEOUT;
//goto ERROR;
// timeout error recovery by clearing isr_state?
timer_start = t;
isr_state = 0x8000;
}
}
// Reset state when taking more than 1ms
if (last_time && timer_elapsed(last_time) > 10) {
ibmpc_error = IBMPC_ERR_TIMEOUT | IBMPC_ERR_RECV | state;
state = START;
data = 0;
parity = 1;
}
last_time = timer_read();
isr_state = isr_state>>1;
if (dbit) isr_state |= 0x8000;
switch (state) {
case START:
if (ibmpc_protocol == IBMPC_PROTOCOL_XT) {
// ignore start(0) bit
if (!data_in()) return;
} else {
if (data_in())
goto ERROR;
}
case BIT0:
case BIT1:
case BIT2:
case BIT3:
case BIT4:
case BIT5:
case BIT6:
case BIT7:
data >>= 1;
if (data_in()) {
data |= 0x80;
parity++;
}
if (state == BIT7 && ibmpc_protocol == IBMPC_PROTOCOL_XT) {
if (!ringbuf_put(&rb, data)) {
ibmpc_error = IBMPC_ERR_FULL;
goto ERROR;
}
ibmpc_error = IBMPC_ERR_NONE;
goto DONE;
}
// isr_state: state of receiving data from keyboard
//
// This should be initialized with 0x8000 before receiving data and
// the MSB '*1' works as marker to discrimitate between protocols.
// It stores sampled bit at MSB after right shift on each clock falling edge.
//
// XT protocol has two variants of signaling; XT_IBM and XT_Clone.
// XT_IBM uses two start bits 0 and 1 while XT_Clone uses just start bit 1.
// https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
//
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// -----------------------------------------------------
// *1 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 Initial state(0x8000)
//
// x x x x x x x x | 0 0 0 0 0 0 0 0 midway(0-7 bits received)
// x x x x x x x x | *1 0 0 0 0 0 0 0 midway(8 bits received)
// b6 b5 b4 b3 b2 b1 b0 1 | 0 *1 0 0 0 0 0 0 XT_IBM-midway ^1
// b7 b6 b5 b4 b3 b2 b1 b0 | 0 *1 0 0 0 0 0 0 AT-midway ^1
// b7 b6 b5 b4 b3 b2 b1 b0 | 1 *1 0 0 0 0 0 0 XT_Clone-done
// pr b7 b6 b5 b4 b3 b2 b1 | 0 0 *1 0 0 0 0 0 AT-midway[b0=0]
// b7 b6 b5 b4 b3 b2 b1 b0 | 1 0 *1 0 0 0 0 0 XT_IBM-done ^2
// pr b7 b6 b5 b4 b3 b2 b1 | 1 0 *1 0 0 0 0 0 AT-midway[b0=1] ^2
// x x x x x x x x | x 1 1 0 0 0 0 0 illegal
// st pr b7 b6 b5 b4 b3 b2 | b1 b0 0 *1 0 0 0 0 AT-done
// x x x x x x x x | x x 1 *1 0 0 0 0 illegal
// all other states than above illegal
//
// ^1: AT and XT_IBM takes same state.
// ^2: AT and XT_IBM takes same state in case that AT b0 is 1,
// we have to check AT stop bit to discriminate between the two protocol.
switch (isr_state & 0xFF) {
case 0b00000000:
case 0b10000000:
case 0b01000000: // ^1
case 0b00100000:
// midway
goto NEXT;
break;
case PARITY:
if (data_in()) {
if (!(parity & 0x01))
goto ERROR;
} else {
if (parity & 0x01)
goto ERROR;
}
break;
case STOP:
if (!data_in())
goto ERROR;
if (!ringbuf_put(&rb, data)) {
ibmpc_error = IBMPC_ERR_FULL;
goto ERROR;
}
ibmpc_error = IBMPC_ERR_NONE;
case 0b11000000:
// XT_Clone-done
isr_state = isr_state>>8;
ibmpc_protocol = IBMPC_PROTOCOL_XT_CLONE;
goto DONE;
break;
default:
case 0b10100000: // ^2
{
uint8_t us = 100;
// wait for rising and falling edge of AT stop bit to discriminate between XT and AT
while (!(IBMPC_CLOCK_PIN&(1<<IBMPC_CLOCK_BIT)) && us) { wait_us(1); us--; }
while ( IBMPC_CLOCK_PIN&(1<<IBMPC_CLOCK_BIT) && us) { wait_us(1); us--; }
if (us) {
// found stop bit: AT-midway - process the stop bit in next ISR
goto NEXT;
} else {
// no stop bit: XT_IBM-done
isr_state = isr_state>>8;
ibmpc_protocol = IBMPC_PROTOCOL_XT_IBM;
goto DONE;
}
}
break;
case 0b00010000:
case 0b10010000:
case 0b01010000:
case 0b11010000:
// AT-done
// TODO: parity check?
isr_state = isr_state>>6;
ibmpc_protocol = IBMPC_PROTOCOL_AT;
goto DONE;
break;
case 0b01100000:
case 0b11100000:
case 0b00110000:
case 0b10110000:
case 0b01110000:
case 0b11110000:
default: // xxxx_oooo(any 1 in low nibble)
// Illegal
ibmpc_error = IBMPC_ERR_ILLEGAL;
goto ERROR;
break;
}
goto NEXT;
ERROR:
ibmpc_error |= state;
ibmpc_error |= IBMPC_ERR_RECV;
ringbuf_reset(&rb);
DONE:
last_time = 0;
state = START;
data = 0;
parity = 1;
isr_state = 0x8000;
recv_data = 0xFF00; // clear data and scancode of error 0x00
return;
DONE:
if ((isr_state & 0x00FF) == 0x00FF) {
// receive error code 0xFF
ibmpc_error = IBMPC_ERR_FF;
}
if ((recv_data & 0xFF00) != 0xFF00) {
// buffer full and overwritten
ibmpc_error = IBMPC_ERR_FULL;
}
recv_data = recv_data<<8;
recv_data |= isr_state & 0xFF;
isr_state = 0x8000; // clear to next data
NEXT:
state++;
return;
}

View file

@ -70,15 +70,19 @@ POSSIBILITY OF SUCH DAMAGE.
#define IBMPC_RESEND 0xFE
#define IBMPC_SET_LED 0xED
#define IBMPC_PROTOCOL_AT 0
#define IBMPC_PROTOCOL_XT 1
#define IBMPC_PROTOCOL_NO 0
#define IBMPC_PROTOCOL_AT 1
#define IBMPC_PROTOCOL_XT_IBM 2
#define IBMPC_PROTOCOL_XT_CLONE 3
// TODO: error numbers
// Error numbers
#define IBMPC_ERR_NONE 0
#define IBMPC_ERR_RECV 0x00
#define IBMPC_ERR_SEND 0x10
#define IBMPC_ERR_TIMEOUT 0x20
#define IBMPC_ERR_FULL 0x40
#define IBMPC_ERR_ILLEGAL 0x80
#define IBMPC_ERR_FF 0xFF
#define IBMPC_LED_SCROLL_LOCK 0
#define IBMPC_LED_NUM_LOCK 1
@ -89,9 +93,12 @@ extern volatile uint8_t ibmpc_protocol;
extern volatile uint8_t ibmpc_error;
void ibmpc_host_init(void);
void ibmpc_host_enable(void);
void ibmpc_host_disable(void);
int16_t ibmpc_host_send(uint8_t data);
int16_t ibmpc_host_recv_response(void);
int16_t ibmpc_host_recv(void);
void ibmpc_host_isr_clear(void);
void ibmpc_host_set_led(uint8_t usb_led);
@ -102,12 +109,6 @@ void ibmpc_host_set_led(uint8_t usb_led);
/*
* Clock
*/
static inline void clock_init(void)
{
IBMPC_CLOCK_PORT &= ~(1<<IBMPC_CLOCK_BIT);
IBMPC_CLOCK_DDR |= (1<<IBMPC_CLOCK_BIT);
}
static inline void clock_lo(void)
{
IBMPC_CLOCK_PORT &= ~(1<<IBMPC_CLOCK_BIT);
@ -132,12 +133,6 @@ static inline bool clock_in(void)
/*
* Data
*/
static inline void data_init(void)
{
IBMPC_DATA_DDR &= ~(1<<IBMPC_DATA_BIT);
IBMPC_DATA_PORT |= (1<<IBMPC_DATA_BIT);
}
static inline void data_lo(void)
{
IBMPC_DATA_PORT &= ~(1<<IBMPC_DATA_BIT);