[Top][All Lists]
[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);
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH 11/14] NEC PC-9821 family system port,
武田 俊也 <=