-- GHDL Run Time (GRT) - Resizable array -- Copyright (C) 2008 - 2014 Tristan Gingold -- -- 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, see . -- -- As a special exception, if other files instantiate generics from this -- unit, or you link this unit with other files to produce an executable, -- this unit does not by itself cause the resulting executable to be -- covered by the GNU General Public License. This exception does not -- however invalidate any other reasons why the executable file might be -- covered by the GNU Public License. with System; use System; with Grt.C; use Grt.C; package body Grt.Table is -- Maximum index of table before resizing. Max : Table_Index_Type := Table_Index_Type'Pred (Table_Low_Bound); -- Current value of Last Last_Val : Table_Index_Type; function Malloc (Size : size_t) return Table_Ptr; pragma Import (C, Malloc); procedure Free (T : Table_Ptr); pragma Import (C, Free); -- Resize and reallocate the table according to LAST_VAL. procedure Resize is function Realloc (T : Table_Ptr; Size : size_t) return Table_Ptr; pragma Import (C, Realloc); New_Size : size_t; begin while Max < Last_Val loop Max := Max + (Max - Table_Low_Bound + 1); end loop; -- Do the multiplication using size_t to avoid overflow if the bounds -- are a 32bit type on a 64bit machine. New_Size := (size_t (Max - Table_Low_Bound + 1) * size_t (Table_Type'Component_Size / Storage_Unit)); Table := Realloc (Table, New_Size); if Table = null then raise Storage_Error; end if; end Resize; procedure Append (New_Val : Table_Component_Type) is begin Increment_Last; Table (Last_Val) := New_Val; end Append; procedure Decrement_Last is begin Last_Val := Table_Index_Type'Pred (Last_Val); end Decrement_Last; procedure Free is begin Free (Table); Table := null; end Free; procedure Increment_Last is begin Last_Val := Table_Index_Type'Succ (Last_Val); if Last_Val > Max then Resize; end if; end Increment_Last; function Last return Table_Index_Type is begin return Last_Val; end Last; procedure Release is begin Max := Last_Val; Resize; end Release; procedure Set_Last (New_Val : Table_Index_Type) is begin if New_Val < Last_Val then Last_Val := New_Val; else Last_Val := New_Val; if Last_Val > Max then Resize; end if; end if; end Set_Last; begin Last_Val := Table_Index_Type'Pred (Table_Low_Bound); Max := Table_Low_Bound + Table_Index_Type (Table_Initial) - 1; Table := Malloc (size_t (Table_Initial) * size_t (Table_Type'Component_Size / Storage_Unit)); end Grt.Table; id='n12' href='#n12'>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 188 189
/*
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
*/
#include <stdbool.h>
#include <util/delay.h>
#include "debug.h"
#include "ring_buffer.h"
#include "ibm4704.h"


#define WAIT(stat, us, err) do { \
    if (!wait_##stat(us)) { \
        ibm4704_error = err; \
        goto ERROR; \
    } \
} while (0)


uint8_t ibm4704_error = 0;


void ibm4704_init(void)
{
    inhibit();  // keep keyboard from sending
    IBM4704_INT_INIT();
    IBM4704_INT_ON();
    idle();     // allow keyboard sending
}

/*
Host to Keyboard
----------------
Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.

        ____        __   __   __   __   __   __   __   __   __   ________
Clock       \______/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/
            ^   ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ___
Data    ____|__/    X____X____X____X____X____X____X____X____X____X   \___
            |  Start   0    1    2    3    4    5    6    7    P   Stop
            Request by host

Start bit:  can be long as 300-350us.
Request:    Host pulls Clock line down to request to send a command.
Timing:     After Request keyboard pull up Data and down Clock line to low for start bit.
            After request host release Clock line once Data line becomes hi.
            Host writes a bit while Clock is hi and Keyboard reads while low.
Stop bit:   Host releases or pulls up Data line to hi after 9th clock and waits for keyboard pull down the line to lo.
*/
uint8_t ibm4704_send(uint8_t data)
{
    bool parity = true; // odd parity
    ibm4704_error = 0;

    IBM4704_INT_OFF();

    /* Request to send */
    idle();
    clock_lo();

    /* wait for Start bit(Clock:lo/Data:hi) */
    WAIT(data_hi, 300, 0x30);

    /* Data bit */
    for (uint8_t i = 0; i < 8; i++) {
        WAIT(clock_hi, 100, 0x40+i);
        if (data&(1<<i)) {
            parity = !parity;
            data_hi();
        } else {
            data_lo();
        }
        WAIT(clock_lo, 100, 0x48+i);
    }

    /* Parity bit */
    WAIT(clock_hi, 100, 0x34);
    if (parity) { data_hi(); } else { data_lo(); }
    WAIT(clock_lo, 100, 0x35);

    /* Stop bit */
    WAIT(clock_hi, 100, 0x34);
    data_hi();

    /* End */
    WAIT(data_lo, 100, 0x36);

    idle();
    IBM4704_INT_ON();
    return 0;
ERROR:
    idle();
    if (ibm4704_error > 0x30) {
        xprintf("S:%02X ", ibm4704_error);
    }
    IBM4704_INT_ON();
    return -1;
}

/* wait forever to receive data */
uint8_t ibm4704_recv_response(void)
{
    while (!rbuf_has_data()) {
        _delay_ms(1);
    }
    return rbuf_dequeue();
}

uint8_t ibm4704_recv(void)
{
    if (rbuf_has_data()) {
        return rbuf_dequeue();
    } else {
        return -1;
    }
}

/*
Keyboard to Host
----------------
Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.

        ____       __   __   __   __   __   __   __   __   __   _______
Clock       \_____/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/
             ____ ____ ____ ____ ____ ____ ____ ____ ____ ____    
Data    ____/    X____X____X____X____X____X____X____X____X____X________
            Start   0    1    2    3    4    5    6    7    P  Stop

Start bit:  can be long as 300-350us.
Inhibit:    Pull Data line down to inhibit keyboard to send.
Timing:     Host reads bit while Clock is hi.(rising edge)
Stop bit:   Keyboard pulls down Data line to lo after 9th clock.
*/
ISR(IBM4704_INT_VECT)
{
    static enum {
        BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, STOP
    } state = BIT0;
    // LSB first
    static uint8_t data = 0;
    // Odd parity
    static uint8_t parity = false;

    ibm4704_error = 0;

    switch (state) {
        case BIT0:
        case BIT1:
        case BIT2:
        case BIT3:
        case BIT4:
        case BIT5:
        case BIT6:
        case BIT7:
            data >>= 1;
            if (data_in()) {
                data |= 0x80;
                parity = !parity;
            }
            break;
        case PARITY:
            if (data_in()) {
                parity = !parity;
            }
            if (!parity)
                goto ERROR;
            break;
        case STOP:
            // Data:Low
            WAIT(data_lo, 100, state);
            rbuf_enqueue(data);
            ibm4704_error = IBM4704_ERR_NONE;
            goto DONE;
            break;
        default:
            goto ERROR;
    }
    state++;
    goto RETURN;
ERROR:
    ibm4704_error = state;
    while (ibm4704_send(0xFE)) _delay_ms(1); // resend
    xprintf("R:%02X%02X\n", state, data);
DONE:
    state = BIT0;
    data = 0;
    parity = false;
RETURN:
    return;
}