qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 11/14] NEC PC-9821 family system port


From: 武田 俊也
Subject: [Qemu-devel] [PATCH 11/14] NEC PC-9821 family system port
Date: Thu, 10 Sep 2009 00:42:47 +0900

hw/pc98sys.c

/*
 * QEMU NEC PC-98x1 system port
 *
 * Copyright (c) 2009 TAKEDA, toshiya
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "hw.h"
#include "isa.h"
#include "pc.h"
#include "qemu-timer.h"

#define RTC_STROBE      0x08
#define RTC_CLOCK       0x10
#define RTC_DIN         0x20

#define TSTMP_FREQ      307200

static const int64_t rtc_expire[3] = {
    1000000000LL / 64,
    1000000000LL / 256,
    1000000000LL / 2048,
};

typedef struct SysState {
    uint8_t rtc_cmd;
    uint8_t rtc_mode;
    uint8_t rtc_tpmode;
    uint64_t rtc_shift;
    uint8_t sys_portc;
    int sys_portc_patch;
    uint8_t prn_porta;
    uint8_t prn_portc;

    uint8_t mbcfg1[256];
    uint8_t mbcfg1_bank;
    uint8_t mbcfg2[256];
    uint8_t mbcfg2_bank;

    uint16_t unkreg[4][256];
    uint8_t unkreg_bank1;
    uint8_t unkreg_bank2;

    int64_t initial_clock;
    int64_t expire_clock;
    QEMUTimer *irq_timer;
    qemu_irq irq;
} SysState;

static SysState sys_state;

/* rtc */

static void rtc_irq_timer(void *opaque)
{
    SysState *s = opaque;

    if (s->rtc_tpmode != 3) {
        qemu_set_irq(s->irq, 1);
        qemu_set_irq(s->irq, 0);
        s->expire_clock += rtc_expire[s->rtc_tpmode];
        qemu_mod_timer(s->irq_timer, s->expire_clock);
    }
}

static inline int64_t to_bcd(int a)
{
    return ((a / 10) << 4) | (a % 10);
}

static void rtc_cmd_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    if ((s->rtc_cmd & RTC_STROBE) && !(value & RTC_STROBE)) {
        if (!(s->rtc_cmd & RTC_CLOCK)) {
            if (s->rtc_cmd & 4) {
                uint8_t new_tpmode = s->rtc_cmd & 3;
                if (s->rtc_tpmode == 3 && new_tpmode != 3) {
                    s->expire_clock = qemu_get_clock(vm_clock) + 
rtc_expire[new_tpmode];
                    qemu_mod_timer(s->irq_timer, s->expire_clock);
                } else if (s->rtc_tpmode != 3 && new_tpmode == 3) {
                    qemu_del_timer(s->irq_timer);
                }
                s->rtc_tpmode = new_tpmode;
            } else {
                s->rtc_mode = s->rtc_cmd & 3;
                if (s->rtc_mode == 3) {
                    struct tm tm;
                    qemu_get_timedate(&tm, 0);
                    s->rtc_shift = to_bcd(tm.tm_sec);
                    s->rtc_shift |= to_bcd(tm.tm_min) << 8;
                    s->rtc_shift |= to_bcd(tm.tm_hour) << 16;
                    s->rtc_shift |= to_bcd(tm.tm_mday) << 24;
                    s->rtc_shift |= (int64_t)tm.tm_wday << 32;
                    s->rtc_shift |= (int64_t)tm.tm_mon << 36;
                    s->rtc_shift |= to_bcd(tm.tm_year % 100) << 40;
                }
            }
        }
    }
    if (!(s->rtc_cmd & RTC_CLOCK) && (value & RTC_CLOCK)) {
        if (s->rtc_mode == 1) {
            uint64_t din = ((s->rtc_cmd & 0x20) != 0);
            s->rtc_shift |= (din << 40);
            s->rtc_shift >>= 1;
        }
    }
    s->rtc_cmd = value;
}

static uint32_t rtc_mode_read(void *opaque, uint32_t addr)
{
    return 0xef;
}

/* system port */

static uint32_t sys_porta_read(void *opaque, uint32_t addr)
{
    return 0x73;
}

static uint32_t sys_portb_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return 0xf8 | (s->rtc_shift & 1);
}

static void sys_portc_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->sys_portc = value;
}

static uint32_t sys_portc_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    if (s->sys_portc_patch) {
        /* patch for itf protect mode patch */
        s->sys_portc_patch--;
        if (s->sys_portc_patch == 0 || s->sys_portc_patch == 4) {
            s->sys_portc |= 0x20;
        }
    }
    return s->sys_portc;
}

static void sys_ctrl_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    if (!(value & 0x80)) {
        /* set/reset portc bit */
        uint32_t portc = s->sys_portc;
        uint32_t bit = 1 << ((value >> 1) & 7);
        if (value & 1) {
            portc |= bit;
        } else {
            portc &= ~bit;
        }
        sys_portc_write(s, 0, portc);
    }
}

/* printer port */

static void prn_porta_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->prn_porta = value;
}

static uint32_t prn_porta_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return s->prn_porta;
}

static uint32_t prn_portb_read(void *opaque, uint32_t addr)
{
#ifdef PC98_SYSCLOCK_5MHZ
    return 0x94;
#else
    return 0xb4;
#endif
}

static void prn_portc_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->prn_portc = value;
}

static uint32_t prn_portc_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return s->prn_portc;
}

static void prn_ctrl_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    if (!(value & 0x80)) {
        /* set/reset portc bit */
        uint8_t portc = s->prn_portc;
        uint8_t bit = 1 << ((value >> 1) & 7);
        if (value & 1) {
            portc |= bit;
        } else {
            portc &= ~bit;
        }
        prn_portc_write(s, 0, portc);
    }
}

/* time stamper */

static uint32_t tstmp_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;
    uint64_t d = muldiv64(qemu_get_clock(vm_clock) - s->initial_clock, 
TSTMP_FREQ, ticks_per_sec);

    switch(addr) {
    case 0x5c:
        return d & 0xffff;
    case 0x5e:
        return (d >> 8) & 0xffff;
    }
    return 0xffff;
}

/* mother board configs */

static void mbcfg1_bank_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->mbcfg1_bank = value;
}

static uint32_t mbcfg1_bank_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return s->mbcfg1_bank;
}

static void mbcfg1_data_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->mbcfg1[s->mbcfg1_bank] = value;
}

static uint32_t mbcfg1_data_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    if (s->mbcfg1_bank == 0) {
        uint32_t value = s->mbcfg1[0];
        s->mbcfg1[0] += 0x40;
        return value;
    }
    return s->mbcfg1[s->mbcfg1_bank];
}

static void mbcfg2_bank_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->mbcfg2_bank = value;
}

static uint32_t mbcfg2_bank_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return s->mbcfg2_bank;
}

static void mbcfg2_data_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->mbcfg2[s->mbcfg2_bank] = value;
}

static uint32_t mbcfg2_data_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return s->mbcfg2[s->mbcfg2_bank];
}

static void unkreg_bank_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->unkreg_bank1 = value;
    s->unkreg_bank2 = 0;
}

static uint32_t unkreg_bank_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return s->unkreg_bank1;
}

static void unkreg_data_write(void *opaque, uint32_t addr, uint32_t value)
{
    SysState *s = opaque;

    s->unkreg[(s->unkreg_bank2++) & 3][s->unkreg_bank1] = value;
}

static uint32_t unkreg_data_read(void *opaque, uint32_t addr)
{
    SysState *s = opaque;

    return s->unkreg[(s->unkreg_bank2++) & 3][s->unkreg_bank1];
}

/* interface */

static void pc98_sys_reset(void *opaque)
{
    SysState *s = opaque;

    s->rtc_cmd = RTC_CLOCK;
    s->rtc_mode = 0;
    s->rtc_tpmode = 3;
    s->rtc_shift = 0;

    s->sys_portc = 0xff;//b8;
    s->sys_portc_patch = 8;
    s->prn_porta = 0xff;
    s->prn_portc = 0x81;

    memset(s->mbcfg1, 0, sizeof(s->mbcfg1));
    s->mbcfg1[0] = 0x10;
    s->mbcfg1_bank = 0;

    memset(s->mbcfg2, 0, sizeof(s->mbcfg2));
    s->mbcfg2_bank = 0;

    memset(s->unkreg, 0, sizeof(s->unkreg));
    s->unkreg_bank1 = s->unkreg_bank2 = 0;
}

static void pc98_sys_save(QEMUFile* f, void* opaque)
{
    SysState *s = opaque;

    qemu_put_8s(f, &s->rtc_cmd);
    qemu_put_8s(f, &s->rtc_mode);
    qemu_put_8s(f, &s->rtc_tpmode);
    qemu_put_be64s(f, &s->rtc_shift);
    qemu_put_8s(f, &s->sys_portc);
    qemu_put_8s(f, &s->prn_porta);
    qemu_put_8s(f, &s->prn_portc);
    qemu_put_buffer(f, s->mbcfg1, 256);
    qemu_put_8s(f, &s->mbcfg1_bank);
    qemu_put_buffer(f, s->mbcfg2, 256);
    qemu_put_8s(f, &s->mbcfg2_bank);
}

static int pc98_sys_load(QEMUFile* f, void* opaque, int version_id)
{
    SysState *s = opaque;

    if (version_id != 1)
        return -EINVAL;

    qemu_get_8s(f, &s->rtc_cmd);
    qemu_get_8s(f, &s->rtc_mode);
    qemu_get_8s(f, &s->rtc_tpmode);
    qemu_get_be64s(f, &s->rtc_shift);
    qemu_get_8s(f, &s->sys_portc);
    qemu_get_8s(f, &s->prn_porta);
    qemu_get_8s(f, &s->prn_portc);
    qemu_get_buffer(f, s->mbcfg1, 256);
    qemu_get_8s(f, &s->mbcfg1_bank);
    qemu_get_buffer(f, s->mbcfg2, 256);
    qemu_get_8s(f, &s->mbcfg2_bank);

    if (s->rtc_tpmode != 3) {
        s->expire_clock = qemu_get_clock(vm_clock) + rtc_expire[s->rtc_tpmode];
        qemu_mod_timer(s->irq_timer, s->expire_clock);
    } else {
        qemu_del_timer(s->irq_timer);
    }
    return 0;
}

void pc98_sys_init(qemu_irq irq)
{
    SysState *s = &sys_state;

    s->initial_clock = qemu_get_clock(vm_clock);
    s->irq_timer = qemu_new_timer(vm_clock, rtc_irq_timer, s);
    s->irq = irq;

    register_ioport_write(0x20, 1, 1, rtc_cmd_write, s);
    register_ioport_read(0x22, 1, 1, rtc_mode_read, s);

    register_ioport_read(0x31, 1, 1, sys_porta_read, s);
    register_ioport_read(0x33, 1, 1, sys_portb_read, s);
    register_ioport_write(0x35, 1, 1, sys_portc_write, s);
    register_ioport_read(0x35, 1, 1, sys_portc_read, s);
    register_ioport_write(0x37, 1, 1, sys_ctrl_write, s);

    register_ioport_write(0x40, 1, 1, prn_porta_write, s);
    register_ioport_read(0x40, 1, 1, prn_porta_read, s);
    register_ioport_read(0x42, 1, 1, prn_portb_read, s);
    register_ioport_write(0x44, 1, 1, prn_portc_write, s);
    register_ioport_read(0x44, 1, 1, prn_portc_read, s);
    register_ioport_write(0x46, 1, 1, prn_ctrl_write, s);

    register_ioport_read(0x5c, 2, 2, tstmp_read, s);
    register_ioport_read(0x5e, 2, 2, tstmp_read, s);

    register_ioport_write(0x411, 1, 1, mbcfg1_bank_write, s);
    register_ioport_read(0x411, 1, 1, mbcfg1_bank_read, s);
    register_ioport_write(0x413, 1, 1, mbcfg1_data_write, s);
    register_ioport_read(0x413, 1, 1, mbcfg1_data_read, s);

    register_ioport_write(0xb00, 2, 2, mbcfg2_bank_write, s);
    register_ioport_read(0xb00, 2, 2, mbcfg2_bank_read, s);
    register_ioport_write(0xb02, 1, 1, mbcfg2_data_write, s);
    register_ioport_read(0xb02, 1, 1, mbcfg2_data_read, s);

    register_ioport_write(0x18f0, 2, 2, unkreg_bank_write, s);
    register_ioport_read(0x18f0, 2, 2, unkreg_bank_read, s);
    register_ioport_write(0x18f2, 2, 2, unkreg_data_write, s);
    register_ioport_read(0x18f2, 2, 2, unkreg_data_read, s);

    qemu_register_reset(pc98_sys_reset, s);
    register_savevm("pc98sys", 0, 1, pc98_sys_save, pc98_sys_load, s);

    pc98_sys_reset(s);
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]