Commit 87a34e2a authored by Laurent Vivier's avatar Laurent Vivier
Browse files

hw/m68k: implement ADB bus support for via



VIA needs to be able to poll the ADB interface and to read/write data
from/to the bus.

This patch adds functions allowing that.

Co-developed-by: default avatarMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: default avatarMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: default avatarLaurent Vivier <laurent@vivier.eu>
Reviewed-by: default avatarHervé Poussineau <hpoussin@reactos.org>
Reviewed-by: default avatarThomas Huth <huth@tuxfamily.org>
Message-Id: <20191026164546.30020-7-laurent@vivier.eu>
parent 6dca62a0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -123,5 +123,6 @@ config UNIMP
config MAC_VIA
    bool
    select MOS6522
    select ADB

source macio/Kconfig
+197 −0
Original line number Diff line number Diff line
@@ -264,10 +264,16 @@
 * Table 19-10 ADB transaction states
 */

#define ADB_STATE_NEW       0
#define ADB_STATE_EVEN      1
#define ADB_STATE_ODD       2
#define ADB_STATE_IDLE      3

#define VIA1B_vADB_StateMask    (VIA1B_vADBS1 | VIA1B_vADBS2)
#define VIA1B_vADB_StateShift   4

#define VIA_TIMER_FREQ (783360)
#define VIA_ADB_POLL_FREQ 50 /* XXX: not real */

/* VIA returns time offset from Jan 1, 1904, not 1970 */
#define RTC_OFFSET 2082844800
@@ -472,6 +478,181 @@ static void via1_rtc_update(MacVIAState *m)
    }
}

static int adb_via_poll(MacVIAState *s, int state, uint8_t *data)
{
    if (state != ADB_STATE_IDLE) {
        return 0;
    }

    if (s->adb_data_in_size < s->adb_data_in_index) {
        return 0;
    }

    if (s->adb_data_out_index != 0) {
        return 0;
    }

    s->adb_data_in_index = 0;
    s->adb_data_out_index = 0;
    s->adb_data_in_size = adb_poll(&s->adb_bus, s->adb_data_in, 0xffff);

    if (s->adb_data_in_size) {
        *data = s->adb_data_in[s->adb_data_in_index++];
        qemu_irq_raise(s->adb_data_ready);
    }

    return s->adb_data_in_size;
}

static int adb_via_send(MacVIAState *s, int state, uint8_t data)
{
    switch (state) {
    case ADB_STATE_NEW:
        s->adb_data_out_index = 0;
        break;
    case ADB_STATE_EVEN:
        if ((s->adb_data_out_index & 1) == 0) {
            return 0;
        }
        break;
    case ADB_STATE_ODD:
        if (s->adb_data_out_index & 1) {
            return 0;
        }
        break;
    case ADB_STATE_IDLE:
        return 0;
    }

    assert(s->adb_data_out_index < sizeof(s->adb_data_out) - 1);

    s->adb_data_out[s->adb_data_out_index++] = data;
    qemu_irq_raise(s->adb_data_ready);
    return 1;
}

static int adb_via_receive(MacVIAState *s, int state, uint8_t *data)
{
    switch (state) {
    case ADB_STATE_NEW:
        return 0;

    case ADB_STATE_EVEN:
        if (s->adb_data_in_size <= 0) {
            qemu_irq_raise(s->adb_data_ready);
            return 0;
        }

        if (s->adb_data_in_index >= s->adb_data_in_size) {
            *data = 0;
            qemu_irq_raise(s->adb_data_ready);
            return 1;
        }

        if ((s->adb_data_in_index & 1) == 0) {
            return 0;
        }

        break;

    case ADB_STATE_ODD:
        if (s->adb_data_in_size <= 0) {
            qemu_irq_raise(s->adb_data_ready);
            return 0;
        }

        if (s->adb_data_in_index >= s->adb_data_in_size) {
            *data = 0;
            qemu_irq_raise(s->adb_data_ready);
            return 1;
        }

        if (s->adb_data_in_index & 1) {
            return 0;
        }

        break;

    case ADB_STATE_IDLE:
        if (s->adb_data_out_index == 0) {
            return 0;
        }

        s->adb_data_in_size = adb_request(&s->adb_bus, s->adb_data_in,
                                          s->adb_data_out,
                                          s->adb_data_out_index);
        s->adb_data_out_index = 0;
        s->adb_data_in_index = 0;
        if (s->adb_data_in_size < 0) {
            *data = 0xff;
            qemu_irq_raise(s->adb_data_ready);
            return -1;
        }

        if (s->adb_data_in_size == 0) {
            return 0;
        }

        break;
    }

    assert(s->adb_data_in_index < sizeof(s->adb_data_in) - 1);

    *data = s->adb_data_in[s->adb_data_in_index++];
    qemu_irq_raise(s->adb_data_ready);
    if (*data == 0xff || *data == 0) {
        return 0;
    }
    return 1;
}

static void via1_adb_update(MacVIAState *m)
{
    MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1);
    MOS6522State *s = MOS6522(v1s);
    int state;
    int ret;

    state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;

    if (s->acr & VIA1ACR_vShiftOut) {
        /* output mode */
        ret = adb_via_send(m, state, s->sr);
        if (ret > 0) {
            s->b &= ~VIA1B_vADBInt;
        } else {
            s->b |= VIA1B_vADBInt;
        }
    } else {
        /* input mode */
        ret = adb_via_receive(m, state, &s->sr);
        if (ret > 0 && s->sr != 0xff) {
            s->b &= ~VIA1B_vADBInt;
        } else {
            s->b |= VIA1B_vADBInt;
        }
    }
}

static void via_adb_poll(void *opaque)
{
    MacVIAState *m = opaque;
    MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1);
    MOS6522State *s = MOS6522(v1s);
    int state;

    if (s->b & VIA1B_vADBInt) {
        state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;
        if (adb_via_poll(m, state, &s->sr)) {
            s->b &= ~VIA1B_vADBInt;
        }
    }

    timer_mod(m->adb_poll_timer,
              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
              (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ));
}

static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size)
{
    MOS6522Q800VIA1State *s = MOS6522_Q800_VIA1(opaque);
@@ -553,6 +734,10 @@ static void mac_via_reset(DeviceState *dev)
    MacVIAState *m = MAC_VIA(dev);
    MOS6522Q800VIA1State *v1s = &m->mos6522_via1;

    timer_mod(m->adb_poll_timer,
              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
              (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ));

    timer_del(v1s->VBL_timer);
    v1s->next_VBL = 0;
    timer_del(v1s->one_second_timer);
@@ -593,6 +778,10 @@ static void mac_via_realize(DeviceState *dev, Error **errp)

    qemu_get_timedate(&tm, 0);
    m->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;

    m->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via_adb_poll, m);
    m->adb_data_ready = qdev_get_gpio_in_named(dev, "via1-irq",
                                               VIA1_IRQ_ADB_READY_BIT);
}

static void mac_via_init(Object *obj)
@@ -642,6 +831,13 @@ static const VMStateDescription vmstate_mac_via = {
        VMSTATE_UINT8(cmd, MacVIAState),
        VMSTATE_INT32(wprotect, MacVIAState),
        VMSTATE_INT32(alt, MacVIAState),
        /* ADB */
        VMSTATE_TIMER_PTR(adb_poll_timer, MacVIAState),
        VMSTATE_INT32(adb_data_in_size, MacVIAState),
        VMSTATE_INT32(adb_data_in_index, MacVIAState),
        VMSTATE_INT32(adb_data_out_index, MacVIAState),
        VMSTATE_BUFFER(adb_data_in, MacVIAState),
        VMSTATE_BUFFER(adb_data_out, MacVIAState),
        VMSTATE_END_OF_LIST()
    }
};
@@ -671,6 +867,7 @@ static void mos6522_q800_via1_portB_write(MOS6522State *s)
    MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1);

    via1_rtc_update(m);
    via1_adb_update(m);

    v1s->last_b = s->b;
}
+7 −0
Original line number Diff line number Diff line
@@ -103,6 +103,13 @@ typedef struct MacVIAState {

    /* ADB */
    ADBBusState adb_bus;
    QEMUTimer *adb_poll_timer;
    qemu_irq adb_data_ready;
    int adb_data_in_size;
    int adb_data_in_index;
    int adb_data_out_index;
    uint8_t adb_data_in[128];
    uint8_t adb_data_out[16];
} MacVIAState;

#endif