Add Macintosh Quadra 800 machine in hw/m68k

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEEzS913cjjpNwuT1Fz8ww4vT8vvjwFAl23MAESHGxhdXJlbnRA
 dml2aWVyLmV1AAoJEPMMOL0/L748UPQQAIfzpB0KRb7InPFNUgGvShoCwKXQmbZm
 wkYoPdDnzgy3FBdbkFM0YcvvSH7Cnbq1njwaTOp8li+NBBiyZCqbCMBKLOusKqhR
 tlP/vF/Jj+o7pbggS+ruSYTtT0+4Q8T5AKEmWmm6ga+PhpT8kFCL+jC8j5bUTmDX
 0lTGwzANDNjGllP+aS6qhY1j9PEWcUpOWr42hW9ZXhAStsPOSzaV7SAZ2XZKPVJz
 KSdnsCPHJVzF4Ps6QK9iMw8szVGat/fq8UU/9w1o2HkmkTRNRW6vuagxmAG1iheW
 fPSpxNnXtoZqTY/qSP9/mMv8cntOtV5YjA/fAeSBa24vY/JFE/zstOrWUY0yhV2y
 CdllFzd/4duR5EThIatJZ4CE3ye89O4or73706l6fnpCI6+UADbneI+0I6VV0fyJ
 I+BxokoI4IgheYOkxD/oxGVx14Ax5vI+R4DkJ09m2koL9h/mALXKeKjb277sJJA/
 GrRx9ttdCGYp7beD84u38aSvNZ9Cs3foljBIYKH6/GqUY5Hl1FlFPE5cDRa+p/yR
 UA/ZDZ4FnJkyhg6Y5mrE8Fx7dvax+9QapKhPqeC9uEuLvUz8yPquMcICx8ehRnNx
 bl0IISTDZvIJwb8ZzemvWHPz63GfQzD1nJjxLZ8c4Q4XYtktETr45tYJd61vuV8x
 gzf/MzaBXSkX
 =HLjK
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/vivier/tags/q800-branch-pull-request' into staging

Add Macintosh Quadra 800 machine in hw/m68k

# gpg: Signature made Mon 28 Oct 2019 18:14:25 GMT
# gpg:                using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C
# gpg:                issuer "laurent@vivier.eu"
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full]
# gpg:                 aka "Laurent Vivier <laurent@vivier.eu>" [full]
# gpg:                 aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full]
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F  5173 F30C 38BD 3F2F BE3C

* remotes/vivier/tags/q800-branch-pull-request:
  BootLinuxConsoleTest: Test the Quadra 800
  hw/m68k: define Macintosh Quadra 800
  hw/m68k: add a dummy SWIM floppy controller
  hw/m68k: add Nubus macfb video card
  hw/m68k: add Nubus support
  hw/m68k: implement ADB bus support for via
  hw/m68k: add VIA support
  dp8393x: manage big endian bus
  esp: add pseudo-DMA as used by Macintosh
  esp: move get_cmd() post-DMA code to get_cmd_cb()
  esp: move handle_ti_cmd() cleanup code to esp_do_dma().

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-10-29 16:27:48 +00:00
commit f724de8dde
35 changed files with 3661 additions and 63 deletions

View file

@ -917,6 +917,20 @@ F: hw/m68k/next-*.c
F: hw/display/next-fb.c
F: include/hw/m68k/next-cube.h
q800
M: Laurent Vivier <laurent@vivier.eu>
S: Maintained
F: hw/m68k/q800.c
F: hw/misc/mac_via.c
F: hw/nubus/*
F: hw/display/macfb.c
F: hw/block/swim.c
F: hw/m68k/bootinfo.h
F: include/hw/misc/mac_via.h
F: include/hw/nubus/*
F: include/hw/display/macfb.h
F: include/hw/block/swim.h
MicroBlaze Machines
-------------------
petalogix_s3adsp1800

View file

@ -38,6 +38,10 @@
int graphic_width = 1024;
int graphic_height = 768;
int graphic_depth = 8;
#elif defined(TARGET_M68K)
int graphic_width = 800;
int graphic_height = 600;
int graphic_depth = 8;
#else
int graphic_width = 800;
int graphic_height = 600;

View file

@ -7,3 +7,4 @@ CONFIG_SEMIHOSTING=y
CONFIG_AN5206=y
CONFIG_MCF5208=y
CONFIG_NEXTCUBE=y
CONFIG_Q800=y

View file

@ -21,6 +21,7 @@ source isa/Kconfig
source mem/Kconfig
source misc/Kconfig
source net/Kconfig
source nubus/Kconfig
source nvram/Kconfig
source pci-bridge/Kconfig
source pci-host/Kconfig

View file

@ -38,6 +38,7 @@ devices-dirs-y += virtio/
devices-dirs-y += watchdog/
devices-dirs-y += xen/
devices-dirs-$(CONFIG_MEM_DEVICE) += mem/
devices-dirs-$(CONFIG_NUBUS) += nubus/
devices-dirs-y += semihosting/
devices-dirs-y += smbios/
endif

View file

@ -37,3 +37,6 @@ config VHOST_USER_BLK
# Only PCI devices are provided for now
default y if VIRTIO_PCI
depends on VIRTIO && VHOST_USER && LINUX
config SWIM
bool

View file

@ -8,6 +8,7 @@ common-obj-$(CONFIG_XEN) += xen-block.o
common-obj-$(CONFIG_ECC) += ecc.o
common-obj-$(CONFIG_ONENAND) += onenand.o
common-obj-$(CONFIG_NVME_PCI) += nvme.o
common-obj-$(CONFIG_SWIM) += swim.o
obj-$(CONFIG_SH4) += tc58128.o

489
hw/block/swim.c Normal file
View file

@ -0,0 +1,489 @@
/*
* QEMU Macintosh floppy disk controller emulator (SWIM)
*
* Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Only the basic support: it allows to switch from IWM (Integrated WOZ
* Machine) mode to the SWIM mode and makes the linux driver happy.
*/
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qapi/error.h"
#include "sysemu/block-backend.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "hw/block/block.h"
#include "hw/block/swim.h"
#include "hw/qdev-properties.h"
/* IWM registers */
#define IWM_PH0L 0
#define IWM_PH0H 1
#define IWM_PH1L 2
#define IWM_PH1H 3
#define IWM_PH2L 4
#define IWM_PH2H 5
#define IWM_PH3L 6
#define IWM_PH3H 7
#define IWM_MTROFF 8
#define IWM_MTRON 9
#define IWM_INTDRIVE 10
#define IWM_EXTDRIVE 11
#define IWM_Q6L 12
#define IWM_Q6H 13
#define IWM_Q7L 14
#define IWM_Q7H 15
/* SWIM registers */
#define SWIM_WRITE_DATA 0
#define SWIM_WRITE_MARK 1
#define SWIM_WRITE_CRC 2
#define SWIM_WRITE_PARAMETER 3
#define SWIM_WRITE_PHASE 4
#define SWIM_WRITE_SETUP 5
#define SWIM_WRITE_MODE0 6
#define SWIM_WRITE_MODE1 7
#define SWIM_READ_DATA 8
#define SWIM_READ_MARK 9
#define SWIM_READ_ERROR 10
#define SWIM_READ_PARAMETER 11
#define SWIM_READ_PHASE 12
#define SWIM_READ_SETUP 13
#define SWIM_READ_STATUS 14
#define SWIM_READ_HANDSHAKE 15
#define REG_SHIFT 9
#define SWIM_MODE_IWM 0
#define SWIM_MODE_SWIM 1
/* bits in phase register */
#define SWIM_SEEK_NEGATIVE 0x074
#define SWIM_STEP 0x071
#define SWIM_MOTOR_ON 0x072
#define SWIM_MOTOR_OFF 0x076
#define SWIM_INDEX 0x073
#define SWIM_EJECT 0x077
#define SWIM_SETMFM 0x171
#define SWIM_SETGCR 0x175
#define SWIM_RELAX 0x033
#define SWIM_LSTRB 0x008
#define SWIM_CA_MASK 0x077
/* Select values for swim_select and swim_readbit */
#define SWIM_READ_DATA_0 0x074
#define SWIM_TWOMEG_DRIVE 0x075
#define SWIM_SINGLE_SIDED 0x076
#define SWIM_DRIVE_PRESENT 0x077
#define SWIM_DISK_IN 0x170
#define SWIM_WRITE_PROT 0x171
#define SWIM_TRACK_ZERO 0x172
#define SWIM_TACHO 0x173
#define SWIM_READ_DATA_1 0x174
#define SWIM_MFM_MODE 0x175
#define SWIM_SEEK_COMPLETE 0x176
#define SWIM_ONEMEG_MEDIA 0x177
/* Bits in handshake register */
#define SWIM_MARK_BYTE 0x01
#define SWIM_CRC_ZERO 0x02
#define SWIM_RDDATA 0x04
#define SWIM_SENSE 0x08
#define SWIM_MOTEN 0x10
#define SWIM_ERROR 0x20
#define SWIM_DAT2BYTE 0x40
#define SWIM_DAT1BYTE 0x80
/* bits in setup register */
#define SWIM_S_INV_WDATA 0x01
#define SWIM_S_3_5_SELECT 0x02
#define SWIM_S_GCR 0x04
#define SWIM_S_FCLK_DIV2 0x08
#define SWIM_S_ERROR_CORR 0x10
#define SWIM_S_IBM_DRIVE 0x20
#define SWIM_S_GCR_WRITE 0x40
#define SWIM_S_TIMEOUT 0x80
/* bits in mode register */
#define SWIM_CLFIFO 0x01
#define SWIM_ENBL1 0x02
#define SWIM_ENBL2 0x04
#define SWIM_ACTION 0x08
#define SWIM_WRITE_MODE 0x10
#define SWIM_HEDSEL 0x20
#define SWIM_MOTON 0x80
static void fd_recalibrate(FDrive *drive)
{
}
static void swim_change_cb(void *opaque, bool load, Error **errp)
{
FDrive *drive = opaque;
if (!load) {
blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
} else {
if (!blkconf_apply_backend_options(drive->conf,
blk_is_read_only(drive->blk), false,
errp)) {
return;
}
}
}
static const BlockDevOps swim_block_ops = {
.change_media_cb = swim_change_cb,
};
static Property swim_drive_properties[] = {
DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1),
DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf),
DEFINE_PROP_END_OF_LIST(),
};
static void swim_drive_realize(DeviceState *qdev, Error **errp)
{
SWIMDrive *dev = SWIM_DRIVE(qdev);
SWIMBus *bus = SWIM_BUS(qdev->parent_bus);
FDrive *drive;
int ret;
if (dev->unit == -1) {
for (dev->unit = 0; dev->unit < SWIM_MAX_FD; dev->unit++) {
drive = &bus->ctrl->drives[dev->unit];
if (!drive->blk) {
break;
}
}
}
if (dev->unit >= SWIM_MAX_FD) {
error_setg(errp, "Can't create floppy unit %d, bus supports "
"only %d units", dev->unit, SWIM_MAX_FD);
return;
}
drive = &bus->ctrl->drives[dev->unit];
if (drive->blk) {
error_setg(errp, "Floppy unit %d is in use", dev->unit);
return;
}
if (!dev->conf.blk) {
/* Anonymous BlockBackend for an empty drive */
dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
ret = blk_attach_dev(dev->conf.blk, qdev);
assert(ret == 0);
}
blkconf_blocksizes(&dev->conf);
if (dev->conf.logical_block_size != 512 ||
dev->conf.physical_block_size != 512)
{
error_setg(errp, "Physical and logical block size must "
"be 512 for floppy");
return;
}
/*
* rerror/werror aren't supported by fdc and therefore not even registered
* with qdev. So set the defaults manually before they are used in
* blkconf_apply_backend_options().
*/
dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
if (!blkconf_apply_backend_options(&dev->conf,
blk_is_read_only(dev->conf.blk),
false, errp)) {
return;
}
/*
* 'enospc' is the default for -drive, 'report' is what blk_new() gives us
* for empty drives.
*/
if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC &&
blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) {
error_setg(errp, "fdc doesn't support drive option werror");
return;
}
if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
error_setg(errp, "fdc doesn't support drive option rerror");
return;
}
drive->conf = &dev->conf;
drive->blk = dev->conf.blk;
drive->swimctrl = bus->ctrl;
blk_set_dev_ops(drive->blk, &swim_block_ops, drive);
}
static void swim_drive_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
k->realize = swim_drive_realize;
set_bit(DEVICE_CATEGORY_STORAGE, k->categories);
k->bus_type = TYPE_SWIM_BUS;
k->props = swim_drive_properties;
k->desc = "virtual SWIM drive";
}
static const TypeInfo swim_drive_info = {
.name = TYPE_SWIM_DRIVE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(SWIMDrive),
.class_init = swim_drive_class_init,
};
static const TypeInfo swim_bus_info = {
.name = TYPE_SWIM_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(SWIMBus),
};
static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value,
unsigned size)
{
SWIMCtrl *swimctrl = opaque;
reg >>= REG_SHIFT;
swimctrl->regs[reg >> 1] = reg & 1;
if (swimctrl->regs[IWM_Q6] &&
swimctrl->regs[IWM_Q7]) {
if (swimctrl->regs[IWM_MTR]) {
/* data register */
swimctrl->iwm_data = value;
} else {
/* mode register */
swimctrl->iwm_mode = value;
/* detect sequence to switch from IWM mode to SWIM mode */
switch (swimctrl->iwm_switch) {
case 0:
if (value == 0x57) {
swimctrl->iwm_switch++;
}
break;
case 1:
if (value == 0x17) {
swimctrl->iwm_switch++;
}
break;
case 2:
if (value == 0x57) {
swimctrl->iwm_switch++;
}
break;
case 3:
if (value == 0x57) {
swimctrl->mode = SWIM_MODE_SWIM;
swimctrl->iwm_switch = 0;
}
break;
}
}
}
}
static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size)
{
SWIMCtrl *swimctrl = opaque;
reg >>= REG_SHIFT;
swimctrl->regs[reg >> 1] = reg & 1;
return 0;
}
static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value,
unsigned size)
{
SWIMCtrl *swimctrl = opaque;
if (swimctrl->mode == SWIM_MODE_IWM) {
iwmctrl_write(opaque, reg, value, size);
return;
}
reg >>= REG_SHIFT;
switch (reg) {
case SWIM_WRITE_PHASE:
swimctrl->swim_phase = value;
break;
case SWIM_WRITE_MODE0:
swimctrl->swim_mode &= ~value;
break;
case SWIM_WRITE_MODE1:
swimctrl->swim_mode |= value;
break;
case SWIM_WRITE_DATA:
case SWIM_WRITE_MARK:
case SWIM_WRITE_CRC:
case SWIM_WRITE_PARAMETER:
case SWIM_WRITE_SETUP:
break;
}
}
static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size)
{
SWIMCtrl *swimctrl = opaque;
uint32_t value = 0;
if (swimctrl->mode == SWIM_MODE_IWM) {
return iwmctrl_read(opaque, reg, size);
}
reg >>= REG_SHIFT;
switch (reg) {
case SWIM_READ_PHASE:
value = swimctrl->swim_phase;
break;
case SWIM_READ_HANDSHAKE:
if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) {
/* always answer "no drive present" */
value = SWIM_SENSE;
}
break;
case SWIM_READ_DATA:
case SWIM_READ_MARK:
case SWIM_READ_ERROR:
case SWIM_READ_PARAMETER:
case SWIM_READ_SETUP:
case SWIM_READ_STATUS:
break;
}
return value;
}
static const MemoryRegionOps swimctrl_mem_ops = {
.write = swimctrl_write,
.read = swimctrl_read,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void sysbus_swim_reset(DeviceState *d)
{
SWIM *sys = SWIM(d);
SWIMCtrl *ctrl = &sys->ctrl;
int i;
ctrl->mode = 0;
ctrl->iwm_switch = 0;
for (i = 0; i < 8; i++) {
ctrl->regs[i] = 0;
}
ctrl->iwm_data = 0;
ctrl->iwm_mode = 0;
ctrl->swim_phase = 0;
ctrl->swim_mode = 0;
for (i = 0; i < SWIM_MAX_FD; i++) {
fd_recalibrate(&ctrl->drives[i]);
}
}
static void sysbus_swim_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
SWIM *sbs = SWIM(obj);
SWIMCtrl *swimctrl = &sbs->ctrl;
memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl,
"swim", 0x2000);
sysbus_init_mmio(sbd, &swimctrl->iomem);
}
static void sysbus_swim_realize(DeviceState *dev, Error **errp)
{
SWIM *sys = SWIM(dev);
SWIMCtrl *swimctrl = &sys->ctrl;
qbus_create_inplace(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev,
NULL);
swimctrl->bus.ctrl = swimctrl;
}
static const VMStateDescription vmstate_fdrive = {
.name = "fdrive",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
},
};
static const VMStateDescription vmstate_swim = {
.name = "swim",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT32(mode, SWIMCtrl),
/* IWM mode */
VMSTATE_INT32(iwm_switch, SWIMCtrl),
VMSTATE_UINT16_ARRAY(regs, SWIMCtrl, 8),
VMSTATE_UINT8(iwm_data, SWIMCtrl),
VMSTATE_UINT8(iwm_mode, SWIMCtrl),
/* SWIM mode */
VMSTATE_UINT8(swim_phase, SWIMCtrl),
VMSTATE_UINT8(swim_mode, SWIMCtrl),
/* Drives */
VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1,
vmstate_fdrive, FDrive),
VMSTATE_END_OF_LIST()
},
};
static const VMStateDescription vmstate_sysbus_swim = {
.name = "SWIM",
.version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(ctrl, SWIM, 0, vmstate_swim, SWIMCtrl),
VMSTATE_END_OF_LIST()
}
};
static void sysbus_swim_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = sysbus_swim_realize;
dc->reset = sysbus_swim_reset;
dc->vmsd = &vmstate_sysbus_swim;
}
static const TypeInfo sysbus_swim_info = {
.name = TYPE_SWIM,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SWIM),
.instance_init = sysbus_swim_init,
.class_init = sysbus_swim_class_init,
};
static void swim_register_types(void)
{
type_register_static(&sysbus_swim_info);
type_register_static(&swim_bus_info);
type_register_static(&swim_drive_info);
}
type_init(swim_register_types)

View file

@ -132,3 +132,8 @@ config ATI_VGA
select VGA
select BITBANG_I2C
select DDC
config MACFB
bool
select FRAMEBUFFER
depends on NUBUS

View file

@ -26,6 +26,7 @@ common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o
common-obj-$(CONFIG_FRAMEBUFFER) += framebuffer.o
obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o
common-obj-$(CONFIG_ZAURUS) += tc6393xb.o
common-obj-$(CONFIG_MACFB) += macfb.o
obj-$(CONFIG_MILKYMIST_TMU2) += milkymist-tmu2.o
milkymist-tmu2.o-cflags := $(X11_CFLAGS) $(OPENGL_CFLAGS)

477
hw/display/macfb.c Normal file
View file

@ -0,0 +1,477 @@
/*
* QEMU Motorola 680x0 Macintosh Video Card Emulation
* Copyright (c) 2012-2018 Laurent Vivier
*
* some parts from QEMU G364 framebuffer Emulator.
* Copyright (c) 2007-2011 Herve Poussineau
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "hw/sysbus.h"
#include "ui/console.h"
#include "ui/pixel_ops.h"
#include "hw/nubus/nubus.h"
#include "hw/display/macfb.h"
#include "qapi/error.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#define VIDEO_BASE 0x00001000
#define DAFB_BASE 0x00800000
#define MACFB_PAGE_SIZE 4096
#define MACFB_VRAM_SIZE (4 * MiB)
#define DAFB_RESET 0x200
#define DAFB_LUT 0x213
typedef void macfb_draw_line_func(MacfbState *s, uint8_t *d, uint32_t addr,
int width);
static inline uint8_t macfb_read_byte(MacfbState *s, uint32_t addr)
{
return s->vram[addr & s->vram_bit_mask];
}
/* 1-bit color */
static void macfb_draw_line1(MacfbState *s, uint8_t *d, uint32_t addr,
int width)
{
uint8_t r, g, b;
int x;
for (x = 0; x < width; x++) {
int bit = x & 7;
int idx = (macfb_read_byte(s, addr) >> (7 - bit)) & 1;
r = g = b = ((1 - idx) << 7);
addr += (bit == 7);
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
d += 4;
}
}
/* 2-bit color */
static void macfb_draw_line2(MacfbState *s, uint8_t *d, uint32_t addr,
int width)
{
uint8_t r, g, b;
int x;
for (x = 0; x < width; x++) {
int bit = (x & 3);
int idx = (macfb_read_byte(s, addr) >> ((3 - bit) << 1)) & 3;
r = s->color_palette[idx * 3];
g = s->color_palette[idx * 3 + 1];
b = s->color_palette[idx * 3 + 2];
addr += (bit == 3);
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
d += 4;
}
}
/* 4-bit color */
static void macfb_draw_line4(MacfbState *s, uint8_t *d, uint32_t addr,
int width)
{
uint8_t r, g, b;
int x;
for (x = 0; x < width; x++) {
int bit = x & 1;
int idx = (macfb_read_byte(s, addr) >> ((1 - bit) << 2)) & 15;
r = s->color_palette[idx * 3];
g = s->color_palette[idx * 3 + 1];
b = s->color_palette[idx * 3 + 2];
addr += (bit == 1);
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
d += 4;
}
}
/* 8-bit color */
static void macfb_draw_line8(MacfbState *s, uint8_t *d, uint32_t addr,
int width)
{
uint8_t r, g, b;
int x;
for (x = 0; x < width; x++) {
r = s->color_palette[macfb_read_byte(s, addr) * 3];
g = s->color_palette[macfb_read_byte(s, addr) * 3 + 1];
b = s->color_palette[macfb_read_byte(s, addr) * 3 + 2];
addr++;
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
d += 4;
}
}
/* 16-bit color */
static void macfb_draw_line16(MacfbState *s, uint8_t *d, uint32_t addr,
int width)
{
uint8_t r, g, b;
int x;
for (x = 0; x < width; x++) {
uint16_t pixel;
pixel = (macfb_read_byte(s, addr) << 8) | macfb_read_byte(s, addr + 1);
r = ((pixel >> 10) & 0x1f) << 3;
g = ((pixel >> 5) & 0x1f) << 3;
b = (pixel & 0x1f) << 3;
addr += 2;
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
d += 4;
}
}
/* 24-bit color */
static void macfb_draw_line24(MacfbState *s, uint8_t *d, uint32_t addr,
int width)
{
uint8_t r, g, b;
int x;
for (x = 0; x < width; x++) {
r = macfb_read_byte(s, addr);
g = macfb_read_byte(s, addr + 1);
b = macfb_read_byte(s, addr + 2);
addr += 3;
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
d += 4;
}
}
enum {
MACFB_DRAW_LINE1,
MACFB_DRAW_LINE2,
MACFB_DRAW_LINE4,
MACFB_DRAW_LINE8,
MACFB_DRAW_LINE16,
MACFB_DRAW_LINE24,
MACFB_DRAW_LINE_NB,
};
static macfb_draw_line_func * const
macfb_draw_line_table[MACFB_DRAW_LINE_NB] = {
macfb_draw_line1,
macfb_draw_line2,
macfb_draw_line4,
macfb_draw_line8,
macfb_draw_line16,
macfb_draw_line24,
};
static int macfb_check_dirty(MacfbState *s, DirtyBitmapSnapshot *snap,
ram_addr_t addr, int len)
{
return memory_region_snapshot_get_dirty(&s->mem_vram, snap, addr, len);
}
static void macfb_draw_graphic(MacfbState *s)
{
DisplaySurface *surface = qemu_console_surface(s->con);
DirtyBitmapSnapshot *snap = NULL;
ram_addr_t page;
uint32_t v = 0;
int y, ymin;
int macfb_stride = (s->depth * s->width + 7) / 8;
macfb_draw_line_func *macfb_draw_line;
switch (s->depth) {
case 1:
v = MACFB_DRAW_LINE1;
break;
case 2:
v = MACFB_DRAW_LINE2;
break;
case 4:
v = MACFB_DRAW_LINE4;
break;
case 8:
v = MACFB_DRAW_LINE8;
break;
case 16:
v = MACFB_DRAW_LINE16;
break;
case 24:
v = MACFB_DRAW_LINE24;
break;
}
macfb_draw_line = macfb_draw_line_table[v];
assert(macfb_draw_line != NULL);
snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0x0,
memory_region_size(&s->mem_vram),
DIRTY_MEMORY_VGA);
ymin = -1;
page = 0;
for (y = 0; y < s->height; y++, page += macfb_stride) {
if (macfb_check_dirty(s, snap, page, macfb_stride)) {
uint8_t *data_display;
data_display = surface_data(surface) + y * surface_stride(surface);
macfb_draw_line(s, data_display, page, s->width);
if (ymin < 0) {
ymin = y;
}
} else {
if (ymin >= 0) {
dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin);
ymin = -1;
}
}
}
if (ymin >= 0) {
dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin);
}
g_free(snap);
}
static void macfb_invalidate_display(void *opaque)
{
MacfbState *s = opaque;
memory_region_set_dirty(&s->mem_vram, 0, MACFB_VRAM_SIZE);
}
static void macfb_update_display(void *opaque)
{
MacfbState *s = opaque;
DisplaySurface *surface = qemu_console_surface(s->con);
qemu_flush_coalesced_mmio_buffer();
if (s->width == 0 || s->height == 0) {
return;
}
if (s->width != surface_width(surface) ||
s->height != surface_height(surface)) {
qemu_console_resize(s->con, s->width, s->height);
}
macfb_draw_graphic(s);
}
static void macfb_reset(MacfbState *s)
{
int i;
s->palette_current = 0;
for (i = 0; i < 256; i++) {
s->color_palette[i * 3] = 255 - i;
s->color_palette[i * 3 + 1] = 255 - i;
s->color_palette[i * 3 + 2] = 255 - i;
}
memset(s->vram, 0, MACFB_VRAM_SIZE);
macfb_invalidate_display(s);
}
static uint64_t macfb_ctrl_read(void *opaque,
hwaddr addr,
unsigned int size)
{
return 0;
}
static void macfb_ctrl_write(void *opaque,
hwaddr addr,
uint64_t val,
unsigned int size)
{
MacfbState *s = opaque;
switch (addr) {
case DAFB_RESET:
s->palette_current = 0;
break;
case DAFB_LUT:
s->color_palette[s->palette_current++] = val;
if (s->palette_current % 3) {
macfb_invalidate_display(s);
}
break;
}
}
static const MemoryRegionOps macfb_ctrl_ops = {
.read = macfb_ctrl_read,
.write = macfb_ctrl_write,
.endianness = DEVICE_BIG_ENDIAN,
.impl.min_access_size = 1,
.impl.max_access_size = 4,
};
static int macfb_post_load(void *opaque, int version_id)
{
macfb_invalidate_display(opaque);
return 0;
}
static const VMStateDescription vmstate_macfb = {
.name = "macfb",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.post_load = macfb_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3),
VMSTATE_UINT32(palette_current, MacfbState),
VMSTATE_END_OF_LIST()
}
};
static const GraphicHwOps macfb_ops = {
.invalidate = macfb_invalidate_display,
.gfx_update = macfb_update_display,
};
static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp)
{
DisplaySurface *surface;
if (s->depth != 1 && s->depth != 2 && s->depth != 4 && s->depth != 8 &&
s->depth != 16 && s->depth != 24) {
error_setg(errp, "unknown guest depth %d", s->depth);
return;
}
s->con = graphic_console_init(dev, 0, &macfb_ops, s);
surface = qemu_console_surface(s->con);
if (surface_bits_per_pixel(surface) != 32) {
error_setg(errp, "unknown host depth %d",
surface_bits_per_pixel(surface));
return;
}
memory_region_init_io(&s->mem_ctrl, NULL, &macfb_ctrl_ops, s, "macfb-ctrl",
0x1000);
memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(s), "macfb-vram",
MACFB_VRAM_SIZE, errp);
s->vram = memory_region_get_ram_ptr(&s->mem_vram);
s->vram_bit_mask = MACFB_VRAM_SIZE - 1;
vmstate_register_ram(&s->mem_vram, dev);
memory_region_set_coalescing(&s->mem_vram);
}
static void macfb_sysbus_realize(DeviceState *dev, Error **errp)
{
MacfbSysBusState *s = MACFB(dev);
MacfbState *ms = &s->macfb;
macfb_common_realize(dev, ms, errp);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram);
}
const uint8_t macfb_rom[] = {
255, 0, 0, 0,
};
static void macfb_nubus_realize(DeviceState *dev, Error **errp)
{
NubusDevice *nd = NUBUS_DEVICE(dev);
MacfbNubusState *s = NUBUS_MACFB(dev);
MacfbNubusDeviceClass *ndc = MACFB_NUBUS_GET_CLASS(dev);
MacfbState *ms = &s->macfb;
ndc->parent_realize(dev, errp);
macfb_common_realize(dev, ms, errp);
memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl);
memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram);
nubus_register_rom(nd, macfb_rom, sizeof(macfb_rom), 1, 9, 0xf);
}
static void macfb_sysbus_reset(DeviceState *d)
{
MacfbSysBusState *s = MACFB(d);
macfb_reset(&s->macfb);
}
static void macfb_nubus_reset(DeviceState *d)
{
MacfbNubusState *s = NUBUS_MACFB(d);
macfb_reset(&s->macfb);
}
static Property macfb_sysbus_properties[] = {
DEFINE_PROP_UINT32("width", MacfbSysBusState, macfb.width, 640),
DEFINE_PROP_UINT32("height", MacfbSysBusState, macfb.height, 480),
DEFINE_PROP_UINT8("depth", MacfbSysBusState, macfb.depth, 8),
DEFINE_PROP_END_OF_LIST(),
};
static Property macfb_nubus_properties[] = {
DEFINE_PROP_UINT32("width", MacfbNubusState, macfb.width, 640),
DEFINE_PROP_UINT32("height", MacfbNubusState, macfb.height, 480),
DEFINE_PROP_UINT8("depth", MacfbNubusState, macfb.depth, 8),
DEFINE_PROP_END_OF_LIST(),
};
static void macfb_sysbus_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = macfb_sysbus_realize;
dc->desc = "SysBus Macintosh framebuffer";
dc->reset = macfb_sysbus_reset;
dc->vmsd = &vmstate_macfb;
dc->props = macfb_sysbus_properties;
}
static void macfb_nubus_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
MacfbNubusDeviceClass *ndc = MACFB_NUBUS_DEVICE_CLASS(klass);
device_class_set_parent_realize(dc, macfb_nubus_realize,
&ndc->parent_realize);
dc->desc = "Nubus Macintosh framebuffer";
dc->reset = macfb_nubus_reset;
dc->vmsd = &vmstate_macfb;
dc->props = macfb_nubus_properties;
}
static TypeInfo macfb_sysbus_info = {
.name = TYPE_MACFB,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MacfbSysBusState),
.class_init = macfb_sysbus_class_init,
};
static TypeInfo macfb_nubus_info = {
.name = TYPE_NUBUS_MACFB,
.parent = TYPE_NUBUS_DEVICE,
.instance_size = sizeof(MacfbNubusState),
.class_init = macfb_nubus_class_init,
.class_size = sizeof(MacfbNubusDeviceClass),
};
static void macfb_register_types(void)
{
type_register_static(&macfb_sysbus_info);
type_register_static(&macfb_nubus_info);
}
type_init(macfb_register_types)

View file

@ -12,3 +12,13 @@ config NEXTCUBE
bool
select FRAMEBUFFER
select ESCC
config Q800
bool
select MAC_VIA
select NUBUS
select MACFB
select SWIM
select ESCC
select ESP
select DP8393X

View file

@ -1,3 +1,4 @@
obj-$(CONFIG_AN5206) += an5206.o mcf5206.o
obj-$(CONFIG_MCF5208) += mcf5208.o mcf_intc.o
obj-$(CONFIG_NEXTCUBE) += next-kbd.o next-cube.o
obj-$(CONFIG_Q800) += q800.o

114
hw/m68k/bootinfo.h Normal file
View file

@ -0,0 +1,114 @@
/*
* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
*
* Bootinfo tags from linux bootinfo.h and bootinfo-mac.h:
* This is an easily parsable and extendable structure containing all
* information to be passed from the bootstrap to the kernel
*
* This structure is copied right after the kernel by the bootstrap
* routine.
*/
#ifndef HW_M68K_BOOTINFO_H
#define HW_M68K_BOOTINFO_H
struct bi_record {
uint16_t tag; /* tag ID */
uint16_t size; /* size of record */
uint32_t data[0]; /* data */
};
/* machine independent tags */
#define BI_LAST 0x0000 /* last record */
#define BI_MACHTYPE 0x0001 /* machine type (u_long) */
#define BI_CPUTYPE 0x0002 /* cpu type (u_long) */
#define BI_FPUTYPE 0x0003 /* fpu type (u_long) */
#define BI_MMUTYPE 0x0004 /* mmu type (u_long) */
#define BI_MEMCHUNK 0x0005 /* memory chunk address and size */
/* (struct mem_info) */
#define BI_RAMDISK 0x0006 /* ramdisk address and size */
/* (struct mem_info) */
#define BI_COMMAND_LINE 0x0007 /* kernel command line parameters */
/* (string) */
/* Macintosh-specific tags (all u_long) */
#define BI_MAC_MODEL 0x8000 /* Mac Gestalt ID (model type) */
#define BI_MAC_VADDR 0x8001 /* Mac video base address */
#define BI_MAC_VDEPTH 0x8002 /* Mac video depth */
#define BI_MAC_VROW 0x8003 /* Mac video rowbytes */
#define BI_MAC_VDIM 0x8004 /* Mac video dimensions */
#define BI_MAC_VLOGICAL 0x8005 /* Mac video logical base */
#define BI_MAC_SCCBASE 0x8006 /* Mac SCC base address */
#define BI_MAC_BTIME 0x8007 /* Mac boot time */
#define BI_MAC_GMTBIAS 0x8008 /* Mac GMT timezone offset */
#define BI_MAC_MEMSIZE 0x8009 /* Mac RAM size (sanity check) */
#define BI_MAC_CPUID 0x800a /* Mac CPU type (sanity check) */
#define BI_MAC_ROMBASE 0x800b /* Mac system ROM base address */
/* Macintosh hardware profile data */
#define BI_MAC_VIA1BASE 0x8010 /* Mac VIA1 base address (always present) */
#define BI_MAC_VIA2BASE 0x8011 /* Mac VIA2 base address (type varies) */
#define BI_MAC_VIA2TYPE 0x8012 /* Mac VIA2 type (VIA, RBV, OSS) */
#define BI_MAC_ADBTYPE 0x8013 /* Mac ADB interface type */
#define BI_MAC_ASCBASE 0x8014 /* Mac Apple Sound Chip base address */
#define BI_MAC_SCSI5380 0x8015 /* Mac NCR 5380 SCSI (base address, multi) */
#define BI_MAC_SCSIDMA 0x8016 /* Mac SCSI DMA (base address) */
#define BI_MAC_SCSI5396 0x8017 /* Mac NCR 53C96 SCSI (base address, multi) */
#define BI_MAC_IDETYPE 0x8018 /* Mac IDE interface type */
#define BI_MAC_IDEBASE 0x8019 /* Mac IDE interface base address */
#define BI_MAC_NUBUS 0x801a /* Mac Nubus type (none, regular, pseudo) */
#define BI_MAC_SLOTMASK 0x801b /* Mac Nubus slots present */
#define BI_MAC_SCCTYPE 0x801c /* Mac SCC serial type (normal, IOP) */
#define BI_MAC_ETHTYPE 0x801d /* Mac builtin ethernet type (Sonic, MACE */
#define BI_MAC_ETHBASE 0x801e /* Mac builtin ethernet base address */
#define BI_MAC_PMU 0x801f /* Mac power management / poweroff hardware */
#define BI_MAC_IOP_SWIM 0x8020 /* Mac SWIM floppy IOP */
#define BI_MAC_IOP_ADB 0x8021 /* Mac ADB IOP */
#define BOOTINFO0(as, base, id) \
do { \
stw_phys(as, base, id); \
base += 2; \
stw_phys(as, base, sizeof(struct bi_record)); \
base += 2; \
} while (0)
#define BOOTINFO1(as, base, id, value) \
do { \
stw_phys(as, base, id); \
base += 2; \
stw_phys(as, base, sizeof(struct bi_record) + 4); \
base += 2; \
stl_phys(as, base, value); \
base += 4; \
} while (0)
#define BOOTINFO2(as, base, id, value1, value2) \
do { \
stw_phys(as, base, id); \
base += 2; \
stw_phys(as, base, sizeof(struct bi_record) + 8); \
base += 2; \
stl_phys(as, base, value1); \
base += 4; \
stl_phys(as, base, value2); \
base += 4; \
} while (0)
#define BOOTINFOSTR(as, base, id, string) \
do { \
int i; \
stw_phys(as, base, id); \
base += 2; \
stw_phys(as, base, \
(sizeof(struct bi_record) + strlen(string) + 2) & ~1); \
base += 2; \
for (i = 0; string[i]; i++) { \
stb_phys(as, base++, string[i]); \
} \
stb_phys(as, base++, 0); \
base = (parameters_base + 1) & ~1; \
} while (0)
#endif

401
hw/m68k/q800.c Normal file
View file

@ -0,0 +1,401 @@
/*
* QEMU Motorla 680x0 Macintosh hardware System Emulator
*
* 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 "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu-common.h"
#include "sysemu/sysemu.h"
#include "cpu.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "hw/irq.h"
#include "elf.h"
#include "hw/loader.h"
#include "ui/console.h"
#include "exec/address-spaces.h"
#include "hw/char/escc.h"
#include "hw/sysbus.h"
#include "hw/scsi/esp.h"
#include "bootinfo.h"
#include "hw/misc/mac_via.h"
#include "hw/input/adb.h"
#include "hw/nubus/mac-nubus-bridge.h"
#include "hw/display/macfb.h"
#include "hw/block/swim.h"
#include "net/net.h"
#include "qapi/error.h"
#include "sysemu/qtest.h"
#include "sysemu/runstate.h"
#include "sysemu/reset.h"
#define MACROM_ADDR 0x40000000
#define MACROM_SIZE 0x00100000
#define MACROM_FILENAME "MacROM.bin"
#define Q800_MACHINE_ID 35
#define Q800_CPU_ID (1 << 2)
#define Q800_FPU_ID (1 << 2)
#define Q800_MMU_ID (1 << 2)
#define MACH_MAC 3
#define Q800_MAC_CPU_ID 2
#define VIA_BASE 0x50f00000
#define SONIC_PROM_BASE 0x50f08000
#define SONIC_BASE 0x50f0a000
#define SCC_BASE 0x50f0c020
#define ESP_BASE 0x50f10000
#define ESP_PDMA 0x50f10100
#define ASC_BASE 0x50F14000
#define SWIM_BASE 0x50F1E000
#define NUBUS_SUPER_SLOT_BASE 0x60000000
#define NUBUS_SLOT_BASE 0xf0000000
/*
* the video base, whereas it a Nubus address,
* is needed by the kernel to have early display and
* thus provided by the bootloader
*/
#define VIDEO_BASE 0xf9001000
#define MAC_CLOCK 3686418
/*
* The GLUE (General Logic Unit) is an Apple custom integrated circuit chip
* that performs a variety of functions (RAM management, clock generation, ...).
* The GLUE chip receives interrupt requests from various devices,
* assign priority to each, and asserts one or more interrupt line to the
* CPU.
*/
typedef struct {
M68kCPU *cpu;
uint8_t ipr;
} GLUEState;
static void GLUE_set_irq(void *opaque, int irq, int level)
{
GLUEState *s = opaque;
int i;
if (level) {
s->ipr |= 1 << irq;
} else {
s->ipr &= ~(1 << irq);
}
for (i = 7; i >= 0; i--) {
if ((s->ipr >> i) & 1) {
m68k_set_irq_level(s->cpu, i + 1, i + 25);
return;
}
}
m68k_set_irq_level(s->cpu, 0, 0);
}
static void main_cpu_reset(void *opaque)
{
M68kCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
cpu_reset(cs);
cpu->env.aregs[7] = ldl_phys(cs->as, 0);
cpu->env.pc = ldl_phys(cs->as, 4);
}
static void q800_init(MachineState *machine)
{
M68kCPU *cpu = NULL;
int linux_boot;
int32_t kernel_size;
uint64_t elf_entry;
char *filename;
int bios_size;
ram_addr_t initrd_base;
int32_t initrd_size;
MemoryRegion *rom;
MemoryRegion *ram;
ram_addr_t ram_size = machine->ram_size;
const char *kernel_filename = machine->kernel_filename;
const char *initrd_filename = machine->initrd_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
hwaddr parameters_base;
CPUState *cs;
DeviceState *dev;
DeviceState *via_dev;
SysBusESPState *sysbus_esp;
ESPState *esp;
SysBusDevice *sysbus;
BusState *adb_bus;
NubusBus *nubus;
GLUEState *irq;
qemu_irq *pic;
linux_boot = (kernel_filename != NULL);
if (ram_size > 1 * GiB) {
error_report("Too much memory for this machine: %" PRId64 " MiB, "
"maximum 1024 MiB", ram_size / MiB);
exit(1);
}
/* init CPUs */
cpu = M68K_CPU(cpu_create(machine->cpu_type));
qemu_register_reset(main_cpu_reset, cpu);
ram = g_malloc(sizeof(*ram));
memory_region_init_ram(ram, NULL, "m68k_mac.ram", ram_size, &error_abort);
memory_region_add_subregion(get_system_memory(), 0, ram);
/* IRQ Glue */
irq = g_new0(GLUEState, 1);
irq->cpu = cpu;
pic = qemu_allocate_irqs(GLUE_set_irq, irq, 8);
/* VIA */
via_dev = qdev_create(NULL, TYPE_MAC_VIA);
qdev_init_nofail(via_dev);
sysbus = SYS_BUS_DEVICE(via_dev);
sysbus_mmio_map(sysbus, 0, VIA_BASE);
qdev_connect_gpio_out_named(DEVICE(sysbus), "irq", 0, pic[0]);
qdev_connect_gpio_out_named(DEVICE(sysbus), "irq", 1, pic[1]);
adb_bus = qdev_get_child_bus(via_dev, "adb.0");
dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD);
qdev_init_nofail(dev);
dev = qdev_create(adb_bus, TYPE_ADB_MOUSE);
qdev_init_nofail(dev);
/* MACSONIC */
if (nb_nics > 1) {
error_report("q800 can only have one ethernet interface");
exit(1);
}
qemu_check_nic_model(&nd_table[0], "dp83932");
/*
* MacSonic driver needs an Apple MAC address
* Valid prefix are:
* 00:05:02 Apple
* 00:80:19 Dayna Communications, Inc.
* 00:A0:40 Apple
* 08:00:07 Apple
* (Q800 use the last one)
*/
nd_table[0].macaddr.a[0] = 0x08;
nd_table[0].macaddr.a[1] = 0x00;
nd_table[0].macaddr.a[2] = 0x07;
dev = qdev_create(NULL, "dp8393x");
qdev_set_nic_properties(dev, &nd_table[0]);
qdev_prop_set_uint8(dev, "it_shift", 2);
qdev_prop_set_bit(dev, "big_endian", true);
qdev_prop_set_ptr(dev, "dma_mr", get_system_memory());
qdev_init_nofail(dev);
sysbus = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(sysbus, 0, SONIC_BASE);
sysbus_mmio_map(sysbus, 1, SONIC_PROM_BASE);
sysbus_connect_irq(sysbus, 0, pic[2]);
/* SCC */
dev = qdev_create(NULL, TYPE_ESCC);
qdev_prop_set_uint32(dev, "disabled", 0);
qdev_prop_set_uint32(dev, "frequency", MAC_CLOCK);
qdev_prop_set_uint32(dev, "it_shift", 1);
qdev_prop_set_bit(dev, "bit_swap", true);
qdev_prop_set_chr(dev, "chrA", serial_hd(0));
qdev_prop_set_chr(dev, "chrB", serial_hd(1));
qdev_prop_set_uint32(dev, "chnBtype", 0);
qdev_prop_set_uint32(dev, "chnAtype", 0);
qdev_init_nofail(dev);
sysbus = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(sysbus, 0, pic[3]);
sysbus_connect_irq(sysbus, 1, pic[3]);
sysbus_mmio_map(sysbus, 0, SCC_BASE);
/* SCSI */
dev = qdev_create(NULL, TYPE_ESP);
sysbus_esp = ESP_STATE(dev);
esp = &sysbus_esp->esp;
esp->dma_memory_read = NULL;
esp->dma_memory_write = NULL;
esp->dma_opaque = NULL;
sysbus_esp->it_shift = 4;
esp->dma_enabled = 1;
qdev_init_nofail(dev);
sysbus = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in_named(via_dev,
"via2-irq",
VIA2_IRQ_SCSI_BIT));
sysbus_connect_irq(sysbus, 1,
qdev_get_gpio_in_named(via_dev, "via2-irq",
VIA2_IRQ_SCSI_DATA_BIT));
sysbus_mmio_map(sysbus, 0, ESP_BASE);
sysbus_mmio_map(sysbus, 1, ESP_PDMA);
scsi_bus_legacy_handle_cmdline(&esp->bus);
/* SWIM floppy controller */
dev = qdev_create(NULL, TYPE_SWIM);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, SWIM_BASE);
/* NuBus */
dev = qdev_create(NULL, TYPE_MAC_NUBUS_BRIDGE);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, NUBUS_SUPER_SLOT_BASE);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE);
nubus = MAC_NUBUS_BRIDGE(dev)->bus;
/* framebuffer in nubus slot #9 */
dev = qdev_create(BUS(nubus), TYPE_NUBUS_MACFB);
qdev_prop_set_uint32(dev, "width", graphic_width);
qdev_prop_set_uint32(dev, "height", graphic_height);
qdev_prop_set_uint8(dev, "depth", graphic_depth);
qdev_init_nofail(dev);
cs = CPU(cpu);
if (linux_boot) {
uint64_t high;
kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
&elf_entry, NULL, &high, 1,
EM_68K, 0, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s'", kernel_filename);
exit(1);
}
stl_phys(cs->as, 4, elf_entry); /* reset initial PC */
parameters_base = (high + 1) & ~1;
BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_MAC);
BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, Q800_FPU_ID);
BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, Q800_MMU_ID);
BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, Q800_CPU_ID);
BOOTINFO1(cs->as, parameters_base, BI_MAC_CPUID, Q800_MAC_CPU_ID);
BOOTINFO1(cs->as, parameters_base, BI_MAC_MODEL, Q800_MACHINE_ID);
BOOTINFO1(cs->as, parameters_base,
BI_MAC_MEMSIZE, ram_size >> 20); /* in MB */
BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size);
BOOTINFO1(cs->as, parameters_base, BI_MAC_VADDR, VIDEO_BASE);
BOOTINFO1(cs->as, parameters_base, BI_MAC_VDEPTH, graphic_depth);
BOOTINFO1(cs->as, parameters_base, BI_MAC_VDIM,
(graphic_height << 16) | graphic_width);
BOOTINFO1(cs->as, parameters_base, BI_MAC_VROW,
(graphic_width * graphic_depth + 7) / 8);
BOOTINFO1(cs->as, parameters_base, BI_MAC_SCCBASE, SCC_BASE);
if (kernel_cmdline) {
BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE,
kernel_cmdline);
}
/* load initrd */
if (initrd_filename) {
initrd_size = get_image_size(initrd_filename);
if (initrd_size < 0) {
error_report("could not load initial ram disk '%s'",
initrd_filename);
exit(1);
}
initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
load_image_targphys(initrd_filename, initrd_base,
ram_size - initrd_base);
BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base,
initrd_size);
} else {
initrd_base = 0;
initrd_size = 0;
}
BOOTINFO0(cs->as, parameters_base, BI_LAST);
} else {
uint8_t *ptr;
/* allocate and load BIOS */
rom = g_malloc(sizeof(*rom));
memory_region_init_ram(rom, NULL, "m68k_mac.rom", MACROM_SIZE,
&error_abort);
if (bios_name == NULL) {
bios_name = MACROM_FILENAME;
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom);
/* Load MacROM binary */
if (filename) {
bios_size = load_image_targphys(filename, MACROM_ADDR, MACROM_SIZE);
g_free(filename);
} else {
bios_size = -1;
}
/* Remove qtest_enabled() check once firmware files are in the tree */
if (!qtest_enabled()) {
if (bios_size < 0 || bios_size > MACROM_SIZE) {
error_report("could not load MacROM '%s'", bios_name);
exit(1);
}
ptr = rom_ptr(MACROM_ADDR, MACROM_SIZE);
stl_phys(cs->as, 0, ldl_p(ptr)); /* reset initial SP */
stl_phys(cs->as, 4,
MACROM_ADDR + ldl_p(ptr + 4)); /* reset initial PC */
}
}
}
static void q800_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "Macintosh Quadra 800";
mc->init = q800_init;
mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
mc->max_cpus = 1;
mc->is_default = 0;
mc->block_default_type = IF_SCSI;
}
static const TypeInfo q800_machine_typeinfo = {
.name = MACHINE_TYPE_NAME("q800"),
.parent = TYPE_MACHINE,
.class_init = q800_machine_class_init,
};
static void q800_machine_register_types(void)
{
type_register_static(&q800_machine_typeinfo);
}
type_init(q800_machine_register_types)

View file

@ -120,4 +120,9 @@ config AUX
config UNIMP
bool
config MAC_VIA
bool
select MOS6522
select ADB
source macio/Kconfig

View file

@ -79,5 +79,6 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_xdma.o
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
common-obj-$(CONFIG_MSF2) += msf2-sysreg.o
common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o
obj-$(CONFIG_MAC_VIA) += mac_via.o
common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o

964
hw/misc/mac_via.c Normal file
View file

@ -0,0 +1,964 @@
/*
* QEMU m68k Macintosh VIA device support
*
* Copyright (c) 2011-2018 Laurent Vivier
* Copyright (c) 2018 Mark Cave-Ayland
*
* Some parts from hw/misc/macio/cuda.c
*
* Copyright (c) 2004-2007 Fabrice Bellard
* Copyright (c) 2007 Jocelyn Mayer
*
* some parts from linux-2.6.29, arch/m68k/include/asm/mac_via.h
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "migration/vmstate.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "qemu/timer.h"
#include "hw/misc/mac_via.h"
#include "hw/misc/mos6522.h"
#include "hw/input/adb.h"
#include "sysemu/runstate.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
/*
* VIAs: There are two in every machine,
*/
#define VIA_SIZE (0x2000)
/*
* Not all of these are true post MacII I think.
* CSA: probably the ones CHRP marks as 'unused' change purposes
* when the IWM becomes the SWIM.
* http://www.rs6000.ibm.com/resource/technology/chrpio/via5.mak.html
* ftp://ftp.austin.ibm.com/pub/technology/spec/chrp/inwork/CHRP_IORef_1.0.pdf
*
* also, http://developer.apple.com/technotes/hw/hw_09.html claims the
* following changes for IIfx:
* VIA1A_vSccWrReq not available and that VIA1A_vSync has moved to an IOP.
* Also, "All of the functionality of VIA2 has been moved to other chips".
*/
#define VIA1A_vSccWrReq 0x80 /*
* SCC write. (input)
* [CHRP] SCC WREQ: Reflects the state of the
* Wait/Request pins from the SCC.
* [Macintosh Family Hardware]
* as CHRP on SE/30,II,IIx,IIcx,IIci.
* on IIfx, "0 means an active request"
*/
#define VIA1A_vRev8 0x40 /*
* Revision 8 board ???
* [CHRP] En WaitReqB: Lets the WaitReq_L
* signal from port B of the SCC appear on
* the PA7 input pin. Output.
* [Macintosh Family] On the SE/30, this
* is the bit to flip screen buffers.
* 0=alternate, 1=main.
* on II,IIx,IIcx,IIci,IIfx this is a bit
* for Rev ID. 0=II,IIx, 1=IIcx,IIci,IIfx
*/
#define VIA1A_vHeadSel 0x20 /*
* Head select for IWM.
* [CHRP] unused.
* [Macintosh Family] "Floppy disk
* state-control line SEL" on all but IIfx
*/
#define VIA1A_vOverlay 0x10 /*
* [Macintosh Family] On SE/30,II,IIx,IIcx
* this bit enables the "Overlay" address
* map in the address decoders as it is on
* reset for mapping the ROM over the reset
* vector. 1=use overlay map.
* On the IIci,IIfx it is another bit of the
* CPU ID: 0=normal IIci, 1=IIci with parity
* feature or IIfx.
* [CHRP] En WaitReqA: Lets the WaitReq_L
* signal from port A of the SCC appear
* on the PA7 input pin (CHRP). Output.
* [MkLinux] "Drive Select"
* (with 0x20 being 'disk head select')
*/
#define VIA1A_vSync 0x08 /*
* [CHRP] Sync Modem: modem clock select:
* 1: select the external serial clock to
* drive the SCC's /RTxCA pin.
* 0: Select the 3.6864MHz clock to drive
* the SCC cell.
* [Macintosh Family] Correct on all but IIfx
*/
/*
* Macintosh Family Hardware sez: bits 0-2 of VIA1A are volume control
* on Macs which had the PWM sound hardware. Reserved on newer models.
* On IIci,IIfx, bits 1-2 are the rest of the CPU ID:
* bit 2: 1=IIci, 0=IIfx
* bit 1: 1 on both IIci and IIfx.
* MkLinux sez bit 0 is 'burnin flag' in this case.
* CHRP sez: VIA1A bits 0-2 and 5 are 'unused': if programmed as
* inputs, these bits will read 0.
*/
#define VIA1A_vVolume 0x07 /* Audio volume mask for PWM */
#define VIA1A_CPUID0 0x02 /* CPU id bit 0 on RBV, others */
#define VIA1A_CPUID1 0x04 /* CPU id bit 0 on RBV, others */
#define VIA1A_CPUID2 0x10 /* CPU id bit 0 on RBV, others */
#define VIA1A_CPUID3 0x40 /* CPU id bit 0 on RBV, others */
/*
* Info on VIA1B is from Macintosh Family Hardware & MkLinux.
* CHRP offers no info.
*/
#define VIA1B_vSound 0x80 /*
* Sound enable (for compatibility with
* PWM hardware) 0=enabled.
* Also, on IIci w/parity, shows parity error
* 0=error, 1=OK.
*/
#define VIA1B_vMystery 0x40 /*
* On IIci, parity enable. 0=enabled,1=disabled
* On SE/30, vertical sync interrupt enable.
* 0=enabled. This vSync interrupt shows up
* as a slot $E interrupt.
*/
#define VIA1B_vADBS2 0x20 /* ADB state input bit 1 (unused on IIfx) */
#define VIA1B_vADBS1 0x10 /* ADB state input bit 0 (unused on IIfx) */
#define VIA1B_vADBInt 0x08 /* ADB interrupt 0=interrupt (unused on IIfx)*/
#define VIA1B_vRTCEnb 0x04 /* Enable Real time clock. 0=enabled. */
#define VIA1B_vRTCClk 0x02 /* Real time clock serial-clock line. */
#define VIA1B_vRTCData 0x01 /* Real time clock serial-data line. */
/*
* VIA2 A register is the interrupt lines raised off the nubus
* slots.
* The below info is from 'Macintosh Family Hardware.'
* MkLinux calls the 'IIci internal video IRQ' below the 'RBV slot 0 irq.'
* It also notes that the slot $9 IRQ is the 'Ethernet IRQ' and
* defines the 'Video IRQ' as 0x40 for the 'EVR' VIA work-alike.
* Perhaps OSS uses vRAM1 and vRAM2 for ADB.
*/
#define VIA2A_vRAM1 0x80 /* RAM size bit 1 (IIci: reserved) */
#define VIA2A_vRAM0 0x40 /* RAM size bit 0 (IIci: internal video IRQ) */
#define VIA2A_vIRQE 0x20 /* IRQ from slot $E */
#define VIA2A_vIRQD 0x10 /* IRQ from slot $D */
#define VIA2A_vIRQC 0x08 /* IRQ from slot $C */
#define VIA2A_vIRQB 0x04 /* IRQ from slot $B */
#define VIA2A_vIRQA 0x02 /* IRQ from slot $A */
#define VIA2A_vIRQ9 0x01 /* IRQ from slot $9 */
/*
* RAM size bits decoded as follows:
* bit1 bit0 size of ICs in bank A
* 0 0 256 kbit
* 0 1 1 Mbit
* 1 0 4 Mbit
* 1 1 16 Mbit
*/
/*
* Register B has the fun stuff in it
*/
#define VIA2B_vVBL 0x80 /*
* VBL output to VIA1 (60.15Hz) driven by
* timer T1.
* on IIci, parity test: 0=test mode.
* [MkLinux] RBV_PARODD: 1=odd,0=even.
*/
#define VIA2B_vSndJck 0x40 /*
* External sound jack status.
* 0=plug is inserted. On SE/30, always 0
*/
#define VIA2B_vTfr0 0x20 /* Transfer mode bit 0 ack from NuBus */
#define VIA2B_vTfr1 0x10 /* Transfer mode bit 1 ack from NuBus */
#define VIA2B_vMode32 0x08 /*
* 24/32bit switch - doubles as cache flush
* on II, AMU/PMMU control.
* if AMU, 0=24bit to 32bit translation
* if PMMU, 1=PMMU is accessing page table.
* on SE/30 tied low.
* on IIx,IIcx,IIfx, unused.
* on IIci/RBV, cache control. 0=flush cache.
*/
#define VIA2B_vPower 0x04 /*
* Power off, 0=shut off power.
* on SE/30 this signal sent to PDS card.
*/
#define VIA2B_vBusLk 0x02 /*
* Lock NuBus transactions, 0=locked.
* on SE/30 sent to PDS card.
*/
#define VIA2B_vCDis 0x01 /*
* Cache control. On IIci, 1=disable cache card
* on others, 0=disable processor's instruction
* and data caches.
*/
/* interrupt flags */
#define IRQ_SET 0x80
/* common */
#define VIA_IRQ_TIMER1 0x40
#define VIA_IRQ_TIMER2 0x20
/*
* Apple sez: http://developer.apple.com/technotes/ov/ov_04.html
* Another example of a valid function that has no ROM support is the use
* of the alternate video page for page-flipping animation. Since there
* is no ROM call to flip pages, it is necessary to go play with the
* right bit in the VIA chip (6522 Versatile Interface Adapter).
* [CSA: don't know which one this is, but it's one of 'em!]
*/
/*
* 6522 registers - see databook.
* CSA: Assignments for VIA1 confirmed from CHRP spec.
*/
/* partial address decode. 0xYYXX : XX part for RBV, YY part for VIA */
/* Note: 15 VIA regs, 8 RBV regs */
#define vBufB 0x0000 /* [VIA/RBV] Register B */
#define vBufAH 0x0200 /* [VIA only] Buffer A, with handshake. DON'T USE! */
#define vDirB 0x0400 /* [VIA only] Data Direction Register B. */
#define vDirA 0x0600 /* [VIA only] Data Direction Register A. */
#define vT1CL 0x0800 /* [VIA only] Timer one counter low. */
#define vT1CH 0x0a00 /* [VIA only] Timer one counter high. */
#define vT1LL 0x0c00 /* [VIA only] Timer one latches low. */
#define vT1LH 0x0e00 /* [VIA only] Timer one latches high. */
#define vT2CL 0x1000 /* [VIA only] Timer two counter low. */
#define vT2CH 0x1200 /* [VIA only] Timer two counter high. */
#define vSR 0x1400 /* [VIA only] Shift register. */
#define vACR 0x1600 /* [VIA only] Auxilary control register. */
#define vPCR 0x1800 /* [VIA only] Peripheral control register. */
/*
* CHRP sez never ever to *write* this.
* Mac family says never to *change* this.
* In fact we need to initialize it once at start.
*/
#define vIFR 0x1a00 /* [VIA/RBV] Interrupt flag register. */
#define vIER 0x1c00 /* [VIA/RBV] Interrupt enable register. */
#define vBufA 0x1e00 /* [VIA/RBV] register A (no handshake) */
/* from linux 2.6 drivers/macintosh/via-macii.c */
/* Bits in ACR */
#define VIA1ACR_vShiftCtrl 0x1c /* Shift register control bits */
#define VIA1ACR_vShiftExtClk 0x0c /* Shift on external clock */
#define VIA1ACR_vShiftOut 0x10 /* Shift out if 1 */
/*
* Apple Macintosh Family Hardware Refenece
* 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
static void via1_VBL_update(MOS6522Q800VIA1State *v1s)
{
MOS6522State *s = MOS6522(v1s);
/* 60 Hz irq */
v1s->next_VBL = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 16630) /
16630 * 16630;
if (s->ier & VIA1_IRQ_VBLANK) {
timer_mod(v1s->VBL_timer, v1s->next_VBL);
} else {
timer_del(v1s->VBL_timer);
}
}
static void via1_one_second_update(MOS6522Q800VIA1State *v1s)
{
MOS6522State *s = MOS6522(v1s);
v1s->next_second = (qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000) /
1000 * 1000;
if (s->ier & VIA1_IRQ_ONE_SECOND) {
timer_mod(v1s->one_second_timer, v1s->next_second);
} else {
timer_del(v1s->one_second_timer);
}
}
static void via1_VBL(void *opaque)
{
MOS6522Q800VIA1State *v1s = opaque;
MOS6522State *s = MOS6522(v1s);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(s);
s->ifr |= VIA1_IRQ_VBLANK;
mdc->update_irq(s);
via1_VBL_update(v1s);
}
static void via1_one_second(void *opaque)
{
MOS6522Q800VIA1State *v1s = opaque;
MOS6522State *s = MOS6522(v1s);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(s);
s->ifr |= VIA1_IRQ_ONE_SECOND;
mdc->update_irq(s);
via1_one_second_update(v1s);
}
static void via1_irq_request(void *opaque, int irq, int level)
{
MOS6522Q800VIA1State *v1s = opaque;
MOS6522State *s = MOS6522(v1s);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(s);
if (level) {
s->ifr |= 1 << irq;
} else {
s->ifr &= ~(1 << irq);
}
mdc->update_irq(s);
}
static void via2_irq_request(void *opaque, int irq, int level)
{
MOS6522Q800VIA2State *v2s = opaque;
MOS6522State *s = MOS6522(v2s);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(s);
if (level) {
s->ifr |= 1 << irq;
} else {
s->ifr &= ~(1 << irq);
}
mdc->update_irq(s);
}
static void via1_rtc_update(MacVIAState *m)
{
MOS6522Q800VIA1State *v1s = &m->mos6522_via1;
MOS6522State *s = MOS6522(v1s);
if (s->b & VIA1B_vRTCEnb) {
return;
}
if (s->dirb & VIA1B_vRTCData) {
/* send bits to the RTC */
if (!(v1s->last_b & VIA1B_vRTCClk) && (s->b & VIA1B_vRTCClk)) {
m->data_out <<= 1;
m->data_out |= s->b & VIA1B_vRTCData;
m->data_out_cnt++;
}
} else {
/* receive bits from the RTC */
if ((v1s->last_b & VIA1B_vRTCClk) &&
!(s->b & VIA1B_vRTCClk) &&
m->data_in_cnt) {
s->b = (s->b & ~VIA1B_vRTCData) |
((m->data_in >> 7) & VIA1B_vRTCData);
m->data_in <<= 1;
m->data_in_cnt--;
}
}
if (m->data_out_cnt == 8) {
m->data_out_cnt = 0;
if (m->cmd == 0) {
if (m->data_out & 0x80) {
/* this is a read command */
uint32_t time = m->tick_offset +
(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
NANOSECONDS_PER_SECOND);
if (m->data_out == 0x81) { /* seconds register 0 */
m->data_in = time & 0xff;
m->data_in_cnt = 8;
} else if (m->data_out == 0x85) { /* seconds register 1 */
m->data_in = (time >> 8) & 0xff;
m->data_in_cnt = 8;
} else if (m->data_out == 0x89) { /* seconds register 2 */
m->data_in = (time >> 16) & 0xff;
m->data_in_cnt = 8;
} else if (m->data_out == 0x8d) { /* seconds register 3 */
m->data_in = (time >> 24) & 0xff;
m->data_in_cnt = 8;
} else if ((m->data_out & 0xf3) == 0xa1) {
/* PRAM address 0x10 -> 0x13 */
int addr = (m->data_out >> 2) & 0x03;
m->data_in = v1s->PRAM[addr];
m->data_in_cnt = 8;
} else if ((m->data_out & 0xf3) == 0xa1) {
/* PRAM address 0x00 -> 0x0f */
int addr = (m->data_out >> 2) & 0x0f;
m->data_in = v1s->PRAM[addr];
m->data_in_cnt = 8;
} else if ((m->data_out & 0xf8) == 0xb8) {
/* extended memory designator and sector number */
m->cmd = m->data_out;
}
} else {
/* this is a write command */
m->cmd = m->data_out;
}
} else {
if (m->cmd & 0x80) {
if ((m->cmd & 0xf8) == 0xb8) {
/* extended memory designator and sector number */
int sector = m->cmd & 0x07;
int addr = (m->data_out >> 2) & 0x1f;
m->data_in = v1s->PRAM[sector * 8 + addr];
m->data_in_cnt = 8;
}
} else if (!m->wprotect) {
/* this is a write command */
if (m->alt != 0) {
/* extended memory designator and sector number */
int sector = m->cmd & 0x07;
int addr = (m->alt >> 2) & 0x1f;
v1s->PRAM[sector * 8 + addr] = m->data_out;
m->alt = 0;
} else if (m->cmd == 0x01) { /* seconds register 0 */
/* FIXME */
} else if (m->cmd == 0x05) { /* seconds register 1 */
/* FIXME */
} else if (m->cmd == 0x09) { /* seconds register 2 */
/* FIXME */
} else if (m->cmd == 0x0d) { /* seconds register 3 */
/* FIXME */
} else if (m->cmd == 0x31) {
/* Test Register */
} else if (m->cmd == 0x35) {
/* Write Protect register */
m->wprotect = m->data_out & 1;
} else if ((m->cmd & 0xf3) == 0xa1) {
/* PRAM address 0x10 -> 0x13 */
int addr = (m->cmd >> 2) & 0x03;
v1s->PRAM[addr] = m->data_out;
} else if ((m->cmd & 0xf3) == 0xa1) {
/* PRAM address 0x00 -> 0x0f */
int addr = (m->cmd >> 2) & 0x0f;
v1s->PRAM[addr] = m->data_out;
} else if ((m->cmd & 0xf8) == 0xb8) {
/* extended memory designator and sector number */
m->alt = m->cmd;
}
}
}
m->data_out = 0;
}
}
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);
MOS6522State *ms = MOS6522(s);
int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
/*
* If IRQs are disabled, timers are disabled, but we need to update
* VIA1_IRQ_VBLANK and VIA1_IRQ_ONE_SECOND bits in the IFR
*/
if (now >= s->next_VBL) {
ms->ifr |= VIA1_IRQ_VBLANK;
via1_VBL_update(s);
}
if (now >= s->next_second) {
ms->ifr |= VIA1_IRQ_ONE_SECOND;
via1_one_second_update(s);
}
addr = (addr >> 9) & 0xf;
return mos6522_read(ms, addr, size);
}
static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque);
MOS6522State *ms = MOS6522(v1s);
addr = (addr >> 9) & 0xf;
mos6522_write(ms, addr, val, size);
via1_one_second_update(v1s);
via1_VBL_update(v1s);
}
static const MemoryRegionOps mos6522_q800_via1_ops = {
.read = mos6522_q800_via1_read,
.write = mos6522_q800_via1_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static uint64_t mos6522_q800_via2_read(void *opaque, hwaddr addr, unsigned size)
{
MOS6522Q800VIA2State *s = MOS6522_Q800_VIA2(opaque);
MOS6522State *ms = MOS6522(s);
addr = (addr >> 9) & 0xf;
return mos6522_read(ms, addr, size);
}
static void mos6522_q800_via2_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
MOS6522Q800VIA2State *s = MOS6522_Q800_VIA2(opaque);
MOS6522State *ms = MOS6522(s);
addr = (addr >> 9) & 0xf;
mos6522_write(ms, addr, val, size);
}
static const MemoryRegionOps mos6522_q800_via2_ops = {
.read = mos6522_q800_via2_read,
.write = mos6522_q800_via2_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
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);
v1s->next_second = 0;
}
static void mac_via_realize(DeviceState *dev, Error **errp)
{
MacVIAState *m = MAC_VIA(dev);
MOS6522State *ms;
struct tm tm;
/* Init VIAs 1 and 2 */
sysbus_init_child_obj(OBJECT(dev), "via1", &m->mos6522_via1,
sizeof(m->mos6522_via1), TYPE_MOS6522_Q800_VIA1);
sysbus_init_child_obj(OBJECT(dev), "via2", &m->mos6522_via2,
sizeof(m->mos6522_via2), TYPE_MOS6522_Q800_VIA2);
/* Pass through mos6522 output IRQs */
ms = MOS6522(&m->mos6522_via1);
object_property_add_alias(OBJECT(dev), "irq[0]", OBJECT(ms),
SYSBUS_DEVICE_GPIO_IRQ "[0]", &error_abort);
ms = MOS6522(&m->mos6522_via2);
object_property_add_alias(OBJECT(dev), "irq[1]", OBJECT(ms),
SYSBUS_DEVICE_GPIO_IRQ "[0]", &error_abort);
/* Pass through mos6522 input IRQs */
qdev_pass_gpios(DEVICE(&m->mos6522_via1), dev, "via1-irq");
qdev_pass_gpios(DEVICE(&m->mos6522_via2), dev, "via2-irq");
/* VIA 1 */
m->mos6522_via1.one_second_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
via1_one_second,
&m->mos6522_via1);
m->mos6522_via1.VBL_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via1_VBL,
&m->mos6522_via1);
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)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MacVIAState *m = MAC_VIA(obj);
/* MMIO */
memory_region_init(&m->mmio, obj, "mac-via", 2 * VIA_SIZE);
sysbus_init_mmio(sbd, &m->mmio);
memory_region_init_io(&m->via1mem, obj, &mos6522_q800_via1_ops,
&m->mos6522_via1, "via1", VIA_SIZE);
memory_region_add_subregion(&m->mmio, 0x0, &m->via1mem);
memory_region_init_io(&m->via2mem, obj, &mos6522_q800_via2_ops,
&m->mos6522_via2, "via2", VIA_SIZE);
memory_region_add_subregion(&m->mmio, VIA_SIZE, &m->via2mem);
/* ADB */
qbus_create_inplace((BusState *)&m->adb_bus, sizeof(m->adb_bus),
TYPE_ADB_BUS, DEVICE(obj), "adb.0");
}
static const VMStateDescription vmstate_mac_via = {
.name = "mac-via",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
/* VIAs */
VMSTATE_STRUCT(mos6522_via1.parent_obj, MacVIAState, 0, vmstate_mos6522,
MOS6522State),
VMSTATE_UINT8(mos6522_via1.last_b, MacVIAState),
VMSTATE_BUFFER(mos6522_via1.PRAM, MacVIAState),
VMSTATE_TIMER_PTR(mos6522_via1.one_second_timer, MacVIAState),
VMSTATE_INT64(mos6522_via1.next_second, MacVIAState),
VMSTATE_TIMER_PTR(mos6522_via1.VBL_timer, MacVIAState),
VMSTATE_INT64(mos6522_via1.next_VBL, MacVIAState),
VMSTATE_STRUCT(mos6522_via2.parent_obj, MacVIAState, 0, vmstate_mos6522,
MOS6522State),
/* RTC */
VMSTATE_UINT32(tick_offset, MacVIAState),
VMSTATE_UINT8(data_out, MacVIAState),
VMSTATE_INT32(data_out_cnt, MacVIAState),
VMSTATE_UINT8(data_in, MacVIAState),
VMSTATE_UINT8(data_in_cnt, MacVIAState),
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()
}
};
static void mac_via_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = mac_via_realize;
dc->reset = mac_via_reset;
dc->vmsd = &vmstate_mac_via;
}
static TypeInfo mac_via_info = {
.name = TYPE_MAC_VIA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MacVIAState),
.instance_init = mac_via_init,
.class_init = mac_via_class_init,
};
/* VIA 1 */
static void mos6522_q800_via1_portB_write(MOS6522State *s)
{
MOS6522Q800VIA1State *v1s = container_of(s, MOS6522Q800VIA1State,
parent_obj);
MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1);
via1_rtc_update(m);
via1_adb_update(m);
v1s->last_b = s->b;
}
static void mos6522_q800_via1_reset(DeviceState *dev)
{
MOS6522State *ms = MOS6522(dev);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms);
mdc->parent_reset(dev);
ms->timers[0].frequency = VIA_TIMER_FREQ;
ms->timers[1].frequency = VIA_TIMER_FREQ;
ms->b = VIA1B_vADB_StateMask | VIA1B_vADBInt | VIA1B_vRTCEnb;
}
static void mos6522_q800_via1_init(Object *obj)
{
qdev_init_gpio_in_named(DEVICE(obj), via1_irq_request, "via1-irq",
VIA1_IRQ_NB);
}
static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc);
dc->reset = mos6522_q800_via1_reset;
mdc->portB_write = mos6522_q800_via1_portB_write;
}
static const TypeInfo mos6522_q800_via1_type_info = {
.name = TYPE_MOS6522_Q800_VIA1,
.parent = TYPE_MOS6522,
.instance_size = sizeof(MOS6522Q800VIA1State),
.instance_init = mos6522_q800_via1_init,
.class_init = mos6522_q800_via1_class_init,
};
/* VIA 2 */
static void mos6522_q800_via2_portB_write(MOS6522State *s)
{
if (s->dirb & VIA2B_vPower && (s->b & VIA2B_vPower) == 0) {
/* shutdown */
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
}
static void mos6522_q800_via2_reset(DeviceState *dev)
{
MOS6522State *ms = MOS6522(dev);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms);
mdc->parent_reset(dev);
ms->timers[0].frequency = VIA_TIMER_FREQ;
ms->timers[1].frequency = VIA_TIMER_FREQ;
ms->dirb = 0;
ms->b = 0;
}
static void mos6522_q800_via2_init(Object *obj)
{
qdev_init_gpio_in_named(DEVICE(obj), via2_irq_request, "via2-irq",
VIA2_IRQ_NB);
}
static void mos6522_q800_via2_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc);
dc->reset = mos6522_q800_via2_reset;
mdc->portB_write = mos6522_q800_via2_portB_write;
}
static const TypeInfo mos6522_q800_via2_type_info = {
.name = TYPE_MOS6522_Q800_VIA2,
.parent = TYPE_MOS6522,
.instance_size = sizeof(MOS6522Q800VIA2State),
.instance_init = mos6522_q800_via2_init,
.class_init = mos6522_q800_via2_class_init,
};
static void mac_via_register_types(void)
{
type_register_static(&mos6522_q800_via1_type_info);
type_register_static(&mos6522_q800_via2_type_info);
type_register_static(&mac_via_info);
}
type_init(mac_via_register_types);

View file

@ -153,6 +153,7 @@ typedef struct dp8393xState {
/* Hardware */
uint8_t it_shift;
bool big_endian;
qemu_irq irq;
#ifdef DEBUG_SONIC
int irq_level;
@ -223,6 +224,29 @@ static uint32_t dp8393x_wt(dp8393xState *s)
return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
}
static uint16_t dp8393x_get(dp8393xState *s, int width, uint16_t *base,
int offset)
{
uint16_t val;
if (s->big_endian) {
val = be16_to_cpu(base[offset * width + width - 1]);
} else {
val = le16_to_cpu(base[offset * width]);
}
return val;
}
static void dp8393x_put(dp8393xState *s, int width, uint16_t *base, int offset,
uint16_t val)
{
if (s->big_endian) {
base[offset * width + width - 1] = cpu_to_be16(val);
} else {
base[offset * width] = cpu_to_le16(val);
}
}
static void dp8393x_update_irq(dp8393xState *s)
{
int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
@ -254,12 +278,12 @@ static void dp8393x_do_load_cam(dp8393xState *s)
/* Fill current entry */
address_space_rw(&s->as, dp8393x_cdp(s),
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->cam[index][0] = data[1 * width] & 0xff;
s->cam[index][1] = data[1 * width] >> 8;
s->cam[index][2] = data[2 * width] & 0xff;
s->cam[index][3] = data[2 * width] >> 8;
s->cam[index][4] = data[3 * width] & 0xff;
s->cam[index][5] = data[3 * width] >> 8;
s->cam[index][0] = dp8393x_get(s, width, data, 1) & 0xff;
s->cam[index][1] = dp8393x_get(s, width, data, 1) >> 8;
s->cam[index][2] = dp8393x_get(s, width, data, 2) & 0xff;
s->cam[index][3] = dp8393x_get(s, width, data, 2) >> 8;
s->cam[index][4] = dp8393x_get(s, width, data, 3) & 0xff;
s->cam[index][5] = dp8393x_get(s, width, data, 3) >> 8;
DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
s->cam[index][0], s->cam[index][1], s->cam[index][2],
s->cam[index][3], s->cam[index][4], s->cam[index][5]);
@ -272,7 +296,7 @@ static void dp8393x_do_load_cam(dp8393xState *s)
/* Read CAM enable */
address_space_rw(&s->as, dp8393x_cdp(s),
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_CE] = data[0 * width];
s->regs[SONIC_CE] = dp8393x_get(s, width, data, 0);
DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
/* Done */
@ -293,10 +317,10 @@ static void dp8393x_do_read_rra(dp8393xState *s)
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
/* Update SONIC registers */
s->regs[SONIC_CRBA0] = data[0 * width];
s->regs[SONIC_CRBA1] = data[1 * width];
s->regs[SONIC_RBWC0] = data[2 * width];
s->regs[SONIC_RBWC1] = data[3 * width];
s->regs[SONIC_CRBA0] = dp8393x_get(s, width, data, 0);
s->regs[SONIC_CRBA1] = dp8393x_get(s, width, data, 1);
s->regs[SONIC_RBWC0] = dp8393x_get(s, width, data, 2);
s->regs[SONIC_RBWC1] = dp8393x_get(s, width, data, 3);
DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
@ -411,12 +435,12 @@ static void dp8393x_do_transmit_packets(dp8393xState *s)
tx_len = 0;
/* Update registers */
s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
s->regs[SONIC_TPS] = data[1 * width];
s->regs[SONIC_TFC] = data[2 * width];
s->regs[SONIC_TSA0] = data[3 * width];
s->regs[SONIC_TSA1] = data[4 * width];
s->regs[SONIC_TFS] = data[5 * width];
s->regs[SONIC_TCR] = dp8393x_get(s, width, data, 0) & 0xf000;
s->regs[SONIC_TPS] = dp8393x_get(s, width, data, 1);
s->regs[SONIC_TFC] = dp8393x_get(s, width, data, 2);
s->regs[SONIC_TSA0] = dp8393x_get(s, width, data, 3);
s->regs[SONIC_TSA1] = dp8393x_get(s, width, data, 4);
s->regs[SONIC_TFS] = dp8393x_get(s, width, data, 5);
/* Handle programmable interrupt */
if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
@ -442,9 +466,9 @@ static void dp8393x_do_transmit_packets(dp8393xState *s)
address_space_rw(&s->as,
dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width,
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_TSA0] = data[0 * width];
s->regs[SONIC_TSA1] = data[1 * width];
s->regs[SONIC_TFS] = data[2 * width];
s->regs[SONIC_TSA0] = dp8393x_get(s, width, data, 0);
s->regs[SONIC_TSA1] = dp8393x_get(s, width, data, 1);
s->regs[SONIC_TFS] = dp8393x_get(s, width, data, 2);
}
}
@ -471,7 +495,8 @@ static void dp8393x_do_transmit_packets(dp8393xState *s)
s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
/* Write status */
data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
dp8393x_put(s, width, data, 0,
s->regs[SONIC_TCR] & 0x0fff); /* status */
size = sizeof(uint16_t) * width;
address_space_rw(&s->as,
dp8393x_ttda(s),
@ -485,8 +510,8 @@ static void dp8393x_do_transmit_packets(dp8393xState *s)
sizeof(uint16_t) *
(4 + 3 * s->regs[SONIC_TFC]) * width,
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
if (data[0 * width] & 0x1) {
s->regs[SONIC_CTDA] = dp8393x_get(s, width, data, 0) & ~0x1;
if (dp8393x_get(s, width, data, 0) & 0x1) {
/* EOL detected */
break;
}
@ -749,7 +774,7 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
(uint8_t *)data, size, 0);
if (data[0 * width] & 0x1) {
if (dp8393x_get(s, width, data, 0) & 0x1) {
/* Still EOL ; stop reception */
return -1;
} else {
@ -793,11 +818,11 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
/* Write status to memory */
DPRINTF("Write status at %08x\n", dp8393x_crda(s));
data[0 * width] = s->regs[SONIC_RCR]; /* status */
data[1 * width] = rx_len; /* byte count */
data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
dp8393x_put(s, width, data, 0, s->regs[SONIC_RCR]); /* status */
dp8393x_put(s, width, data, 1, rx_len); /* byte count */
dp8393x_put(s, width, data, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
dp8393x_put(s, width, data, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
dp8393x_put(s, width, data, 4, s->regs[SONIC_RSC]); /* seq_no */
size = sizeof(uint16_t) * 5 * width;
address_space_rw(&s->as, dp8393x_crda(s),
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
@ -806,12 +831,12 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
size = sizeof(uint16_t) * width;
address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_LLFA] = data[0 * width];
s->regs[SONIC_LLFA] = dp8393x_get(s, width, data, 0);
if (s->regs[SONIC_LLFA] & 0x1) {
/* EOL detected */
s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
} else {
data[0 * width] = 0; /* in_use */
dp8393x_put(s, width, data, 0, 0); /* in_use */
address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 6 * width,
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
@ -924,6 +949,7 @@ static Property dp8393x_properties[] = {
DEFINE_NIC_PROPERTIES(dp8393xState, conf),
DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
DEFINE_PROP_END_OF_LIST(),
};

2
hw/nubus/Kconfig Normal file
View file

@ -0,0 +1,2 @@
config NUBUS
bool

4
hw/nubus/Makefile.objs Normal file
View file

@ -0,0 +1,4 @@
common-obj-y += nubus-device.o
common-obj-y += nubus-bus.o
common-obj-y += nubus-bridge.o
common-obj-$(CONFIG_Q800) += mac-nubus-bridge.o

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2013-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/nubus/mac-nubus-bridge.h"
static void mac_nubus_bridge_init(Object *obj)
{
MacNubusState *s = MAC_NUBUS_BRIDGE(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
s->bus = NUBUS_BUS(qbus_create(TYPE_NUBUS_BUS, DEVICE(s), NULL));
sysbus_init_mmio(sbd, &s->bus->super_slot_io);
sysbus_init_mmio(sbd, &s->bus->slot_io);
}
static void mac_nubus_bridge_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "Nubus bridge";
}
static const TypeInfo mac_nubus_bridge_info = {
.name = TYPE_MAC_NUBUS_BRIDGE,
.parent = TYPE_NUBUS_BRIDGE,
.instance_init = mac_nubus_bridge_init,
.instance_size = sizeof(MacNubusState),
.class_init = mac_nubus_bridge_class_init,
};
static void mac_nubus_bridge_register_types(void)
{
type_register_static(&mac_nubus_bridge_info);
}
type_init(mac_nubus_bridge_register_types)

34
hw/nubus/nubus-bridge.c Normal file
View file

@ -0,0 +1,34 @@
/*
* QEMU Macintosh Nubus
*
* Copyright (c) 2013-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/nubus/nubus.h"
static void nubus_bridge_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->fw_name = "nubus";
}
static const TypeInfo nubus_bridge_info = {
.name = TYPE_NUBUS_BRIDGE,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SysBusDevice),
.class_init = nubus_bridge_class_init,
};
static void nubus_register_types(void)
{
type_register_static(&nubus_bridge_info);
}
type_init(nubus_register_types)

111
hw/nubus/nubus-bus.c Normal file
View file

@ -0,0 +1,111 @@
/*
* QEMU Macintosh Nubus
*
* Copyright (c) 2013-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "hw/nubus/nubus.h"
#include "hw/sysbus.h"
#include "qapi/error.h"
static NubusBus *nubus_find(void)
{
/* Returns NULL unless there is exactly one nubus device */
return NUBUS_BUS(object_resolve_path_type("", TYPE_NUBUS_BUS, NULL));
}
static void nubus_slot_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int size)
{
/* read only */
}
static uint64_t nubus_slot_read(void *opaque, hwaddr addr,
unsigned int size)
{
return 0;
}
static const MemoryRegionOps nubus_slot_ops = {
.read = nubus_slot_read,
.write = nubus_slot_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static void nubus_super_slot_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int size)
{
/* read only */
}
static uint64_t nubus_super_slot_read(void *opaque, hwaddr addr,
unsigned int size)
{
return 0;
}
static const MemoryRegionOps nubus_super_slot_ops = {
.read = nubus_super_slot_read,
.write = nubus_super_slot_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static void nubus_realize(BusState *bus, Error **errp)
{
if (!nubus_find()) {
error_setg(errp, "at most one %s device is permitted", TYPE_NUBUS_BUS);
return;
}
}
static void nubus_init(Object *obj)
{
NubusBus *nubus = NUBUS_BUS(obj);
memory_region_init_io(&nubus->super_slot_io, obj, &nubus_super_slot_ops,
nubus, "nubus-super-slots",
NUBUS_SUPER_SLOT_NB * NUBUS_SUPER_SLOT_SIZE);
memory_region_init_io(&nubus->slot_io, obj, &nubus_slot_ops,
nubus, "nubus-slots",
NUBUS_SLOT_NB * NUBUS_SLOT_SIZE);
nubus->current_slot = NUBUS_FIRST_SLOT;
}
static void nubus_class_init(ObjectClass *oc, void *data)
{
BusClass *bc = BUS_CLASS(oc);
bc->realize = nubus_realize;
}
static const TypeInfo nubus_bus_info = {
.name = TYPE_NUBUS_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(NubusBus),
.instance_init = nubus_init,
.class_init = nubus_class_init,
};
static void nubus_register_types(void)
{
type_register_static(&nubus_bus_info);
}
type_init(nubus_register_types)

215
hw/nubus/nubus-device.c Normal file
View file

@ -0,0 +1,215 @@
/*
* QEMU Macintosh Nubus
*
* Copyright (c) 2013-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "hw/nubus/nubus.h"
#include "qapi/error.h"
/* The Format Block Structure */
#define FBLOCK_DIRECTORY_OFFSET 0
#define FBLOCK_LENGTH 4
#define FBLOCK_CRC 8
#define FBLOCK_REVISION_LEVEL 12
#define FBLOCK_FORMAT 13
#define FBLOCK_TEST_PATTERN 14
#define FBLOCK_RESERVED 18
#define FBLOCK_BYTE_LANES 19
#define FBLOCK_SIZE 20
#define FBLOCK_PATTERN_VAL 0x5a932bc7
static uint64_t nubus_fblock_read(void *opaque, hwaddr addr, unsigned int size)
{
NubusDevice *dev = opaque;
uint64_t val;
#define BYTE(v, b) (((v) >> (24 - 8 * (b))) & 0xff)
switch (addr) {
case FBLOCK_BYTE_LANES:
val = dev->byte_lanes;
val |= (val ^ 0xf) << 4;
break;
case FBLOCK_RESERVED:
val = 0x00;
break;
case FBLOCK_TEST_PATTERN...FBLOCK_TEST_PATTERN + 3:
val = BYTE(FBLOCK_PATTERN_VAL, addr - FBLOCK_TEST_PATTERN);
break;
case FBLOCK_FORMAT:
val = dev->rom_format;
break;
case FBLOCK_REVISION_LEVEL:
val = dev->rom_rev;
break;
case FBLOCK_CRC...FBLOCK_CRC + 3:
val = BYTE(dev->rom_crc, addr - FBLOCK_CRC);
break;
case FBLOCK_LENGTH...FBLOCK_LENGTH + 3:
val = BYTE(dev->rom_length, addr - FBLOCK_LENGTH);
break;
case FBLOCK_DIRECTORY_OFFSET...FBLOCK_DIRECTORY_OFFSET + 3:
val = BYTE(dev->directory_offset, addr - FBLOCK_DIRECTORY_OFFSET);
break;
default:
val = 0;
break;
}
return val;
}
static void nubus_fblock_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int size)
{
/* read only */
}
static const MemoryRegionOps nubus_format_block_ops = {
.read = nubus_fblock_read,
.write = nubus_fblock_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
}
};
static void nubus_register_format_block(NubusDevice *dev)
{
char *fblock_name;
fblock_name = g_strdup_printf("nubus-slot-%d-format-block",
dev->slot_nb);
hwaddr fblock_offset = memory_region_size(&dev->slot_mem) - FBLOCK_SIZE;
memory_region_init_io(&dev->fblock_io, NULL, &nubus_format_block_ops,
dev, fblock_name, FBLOCK_SIZE);
memory_region_add_subregion(&dev->slot_mem, fblock_offset,
&dev->fblock_io);
g_free(fblock_name);
}
static void mac_nubus_rom_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int size)
{
/* read only */
}
static uint64_t mac_nubus_rom_read(void *opaque, hwaddr addr,
unsigned int size)
{
NubusDevice *dev = opaque;
return dev->rom[addr];
}
static const MemoryRegionOps mac_nubus_rom_ops = {
.read = mac_nubus_rom_read,
.write = mac_nubus_rom_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
void nubus_register_rom(NubusDevice *dev, const uint8_t *rom, uint32_t size,
int revision, int format, uint8_t byte_lanes)
{
hwaddr rom_offset;
char *rom_name;
/* FIXME : really compute CRC */
dev->rom_length = 0;
dev->rom_crc = 0;
dev->rom_rev = revision;
dev->rom_format = format;
dev->byte_lanes = byte_lanes;
dev->directory_offset = -size;
/* ROM */
dev->rom = rom;
rom_name = g_strdup_printf("nubus-slot-%d-rom", dev->slot_nb);
memory_region_init_io(&dev->rom_io, NULL, &mac_nubus_rom_ops,
dev, rom_name, size);
memory_region_set_readonly(&dev->rom_io, true);
rom_offset = memory_region_size(&dev->slot_mem) - FBLOCK_SIZE +
dev->directory_offset;
memory_region_add_subregion(&dev->slot_mem, rom_offset, &dev->rom_io);
g_free(rom_name);
}
static void nubus_device_realize(DeviceState *dev, Error **errp)
{
NubusBus *nubus = NUBUS_BUS(qdev_get_parent_bus(DEVICE(dev)));
NubusDevice *nd = NUBUS_DEVICE(dev);
char *name;
hwaddr slot_offset;
if (nubus->current_slot < NUBUS_FIRST_SLOT ||
nubus->current_slot > NUBUS_LAST_SLOT) {
error_setg(errp, "Cannot register nubus card, not enough slots");
return;
}
nd->slot_nb = nubus->current_slot++;
name = g_strdup_printf("nubus-slot-%d", nd->slot_nb);
if (nd->slot_nb < NUBUS_FIRST_SLOT) {
/* Super */
slot_offset = (nd->slot_nb - 6) * NUBUS_SUPER_SLOT_SIZE;
memory_region_init(&nd->slot_mem, OBJECT(dev), name,
NUBUS_SUPER_SLOT_SIZE);
memory_region_add_subregion(&nubus->super_slot_io, slot_offset,
&nd->slot_mem);
} else {
/* Normal */
slot_offset = nd->slot_nb * NUBUS_SLOT_SIZE;
memory_region_init(&nd->slot_mem, OBJECT(dev), name, NUBUS_SLOT_SIZE);
memory_region_add_subregion(&nubus->slot_io, slot_offset,
&nd->slot_mem);
}
g_free(name);
nubus_register_format_block(nd);
}
static void nubus_device_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = nubus_device_realize;
dc->bus_type = TYPE_NUBUS_BUS;
}
static const TypeInfo nubus_device_type_info = {
.name = TYPE_NUBUS_DEVICE,
.parent = TYPE_DEVICE,
.abstract = true,
.instance_size = sizeof(NubusDevice),
.class_init = nubus_device_class_init,
};
static void nubus_register_types(void)
{
type_register_static(&nubus_device_type_info);
}
type_init(nubus_register_types)

View file

@ -38,6 +38,8 @@
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
* and
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
*
* On Macintosh Quadra it is a NCR53C96.
*/
static void esp_raise_irq(ESPState *s)
@ -58,6 +60,16 @@ static void esp_lower_irq(ESPState *s)
}
}
static void esp_raise_drq(ESPState *s)
{
qemu_irq_raise(s->irq_data);
}
static void esp_lower_drq(ESPState *s)
{
qemu_irq_lower(s->irq_data);
}
void esp_dma_enable(ESPState *s, int irq, int level)
{
if (level) {
@ -84,29 +96,35 @@ void esp_request_cancelled(SCSIRequest *req)
}
}
static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen)
static void set_pdma(ESPState *s, enum pdma_origin_id origin,
uint32_t index, uint32_t len)
{
s->pdma_origin = origin;
s->pdma_start = index;
s->pdma_cur = index;
s->pdma_len = len;
}
static uint8_t *get_pdma_buf(ESPState *s)
{
switch (s->pdma_origin) {
case PDMA:
return s->pdma_buf;
case TI:
return s->ti_buf;
case CMD:
return s->cmdbuf;
case ASYNC:
return s->async_buf;
}
return NULL;
}
static int get_cmd_cb(ESPState *s)
{
uint32_t dmalen;
int target;
target = s->wregs[ESP_WBUSID] & BUSID_DID;
if (s->dma) {
dmalen = s->rregs[ESP_TCLO];
dmalen |= s->rregs[ESP_TCMID] << 8;
dmalen |= s->rregs[ESP_TCHI] << 16;
if (dmalen > buflen) {
return 0;
}
s->dma_memory_read(s->dma_opaque, buf, dmalen);
} else {
dmalen = s->ti_size;
if (dmalen > TI_BUFSZ) {
return 0;
}
memcpy(buf, s->ti_buf, dmalen);
buf[0] = buf[2] >> 5;
}
trace_esp_get_cmd(dmalen, target);
s->ti_size = 0;
s->ti_rptr = 0;
@ -120,11 +138,48 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen)
s->current_dev = scsi_device_find(&s->bus, 0, target, 0);
if (!s->current_dev) {
// No such drive
/* No such drive */
s->rregs[ESP_RSTAT] = 0;
s->rregs[ESP_RINTR] = INTR_DC;
s->rregs[ESP_RSEQ] = SEQ_0;
esp_raise_irq(s);
return -1;
}
return 0;
}
static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen)
{
uint32_t dmalen;
int target;
target = s->wregs[ESP_WBUSID] & BUSID_DID;
if (s->dma) {
dmalen = s->rregs[ESP_TCLO];
dmalen |= s->rregs[ESP_TCMID] << 8;
dmalen |= s->rregs[ESP_TCHI] << 16;
if (dmalen > buflen) {
return 0;
}
if (s->dma_memory_read) {
s->dma_memory_read(s->dma_opaque, buf, dmalen);
} else {
memcpy(s->pdma_buf, buf, dmalen);
set_pdma(s, PDMA, 0, dmalen);
esp_raise_drq(s);
return 0;
}
} else {
dmalen = s->ti_size;
if (dmalen > TI_BUFSZ) {
return 0;
}
memcpy(buf, s->ti_buf, dmalen);
buf[0] = buf[2] >> 5;
}
trace_esp_get_cmd(dmalen, target);
if (get_cmd_cb(s) < 0) {
return 0;
}
return dmalen;
@ -165,6 +220,16 @@ static void do_cmd(ESPState *s, uint8_t *buf)
do_busid_cmd(s, &buf[1], busid);
}
static void satn_pdma_cb(ESPState *s)
{
if (get_cmd_cb(s) < 0) {
return;
}
if (s->pdma_cur != s->pdma_start) {
do_cmd(s, get_pdma_buf(s) + s->pdma_start);
}
}
static void handle_satn(ESPState *s)
{
uint8_t buf[32];
@ -174,11 +239,22 @@ static void handle_satn(ESPState *s)
s->dma_cb = handle_satn;
return;
}
s->pdma_cb = satn_pdma_cb;
len = get_cmd(s, buf, sizeof(buf));
if (len)
do_cmd(s, buf);
}
static void s_without_satn_pdma_cb(ESPState *s)
{
if (get_cmd_cb(s) < 0) {
return;
}
if (s->pdma_cur != s->pdma_start) {
do_busid_cmd(s, get_pdma_buf(s) + s->pdma_start, 0);
}
}
static void handle_s_without_atn(ESPState *s)
{
uint8_t buf[32];
@ -188,18 +264,36 @@ static void handle_s_without_atn(ESPState *s)
s->dma_cb = handle_s_without_atn;
return;
}
s->pdma_cb = s_without_satn_pdma_cb;
len = get_cmd(s, buf, sizeof(buf));
if (len) {
do_busid_cmd(s, buf, 0);
}
}
static void satn_stop_pdma_cb(ESPState *s)
{
if (get_cmd_cb(s) < 0) {
return;
}
s->cmdlen = s->pdma_cur - s->pdma_start;
if (s->cmdlen) {
trace_esp_handle_satn_stop(s->cmdlen);
s->do_cmd = 1;
s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD;
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
s->rregs[ESP_RSEQ] = SEQ_CD;
esp_raise_irq(s);
}
}
static void handle_satn_stop(ESPState *s)
{
if (s->dma && !s->dma_enabled) {
s->dma_cb = handle_satn_stop;
return;
}
s->pdma_cb = satn_stop_pdma_cb;;
s->cmdlen = get_cmd(s, s->cmdbuf, sizeof(s->cmdbuf));
if (s->cmdlen) {
trace_esp_handle_satn_stop(s->cmdlen);
@ -211,16 +305,31 @@ static void handle_satn_stop(ESPState *s)
}
}
static void write_response_pdma_cb(ESPState *s)
{
s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST;
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
s->rregs[ESP_RSEQ] = SEQ_CD;
esp_raise_irq(s);
}
static void write_response(ESPState *s)
{
trace_esp_write_response(s->status);
s->ti_buf[0] = s->status;
s->ti_buf[1] = 0;
if (s->dma) {
if (s->dma_memory_write) {
s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST;
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
s->rregs[ESP_RSEQ] = SEQ_CD;
} else {
set_pdma(s, TI, 0, 2);
s->pdma_cb = write_response_pdma_cb;
esp_raise_drq(s);
return;
}
} else {
s->ti_size = 2;
s->ti_rptr = 0;
@ -242,6 +351,41 @@ static void esp_dma_done(ESPState *s)
esp_raise_irq(s);
}
static void do_dma_pdma_cb(ESPState *s)
{
int to_device = (s->ti_size < 0);
int len = s->pdma_cur - s->pdma_start;
if (s->do_cmd) {
s->ti_size = 0;
s->cmdlen = 0;
s->do_cmd = 0;
do_cmd(s, s->cmdbuf);
return;
}
s->dma_left -= len;
s->async_buf += len;
s->async_len -= len;
if (to_device) {
s->ti_size += len;
} else {
s->ti_size -= len;
}
if (s->async_len == 0) {
scsi_req_continue(s->current_req);
/*
* If there is still data to be read from the device then
* complete the DMA operation immediately. Otherwise defer
* until the scsi layer has completed.
*/
if (to_device || s->dma_left != 0 || s->ti_size == 0) {
return;
}
}
/* Partially filled a scsi buffer. Complete immediately. */
esp_dma_done(s);
}
static void esp_do_dma(ESPState *s)
{
uint32_t len;
@ -249,10 +393,26 @@ static void esp_do_dma(ESPState *s)
len = s->dma_left;
if (s->do_cmd) {
/*
* handle_ti_cmd() case: esp_do_dma() is called only from
* handle_ti_cmd() with do_cmd != NULL (see the assert())
*/
trace_esp_do_dma(s->cmdlen, len);
assert (s->cmdlen <= sizeof(s->cmdbuf) &&
len <= sizeof(s->cmdbuf) - s->cmdlen);
if (s->dma_memory_read) {
s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
} else {
set_pdma(s, CMD, s->cmdlen, len);
s->pdma_cb = do_dma_pdma_cb;
esp_raise_drq(s);
return;
}
trace_esp_handle_ti_cmd(s->cmdlen);
s->ti_size = 0;
s->cmdlen = 0;
s->do_cmd = 0;
do_cmd(s, s->cmdbuf);
return;
}
if (s->async_len == 0) {
@ -264,9 +424,23 @@ static void esp_do_dma(ESPState *s)
}
to_device = (s->ti_size < 0);
if (to_device) {
if (s->dma_memory_read) {
s->dma_memory_read(s->dma_opaque, s->async_buf, len);
} else {
set_pdma(s, ASYNC, 0, len);
s->pdma_cb = do_dma_pdma_cb;
esp_raise_drq(s);
return;
}
} else {
if (s->dma_memory_write) {
s->dma_memory_write(s->dma_opaque, s->async_buf, len);
} else {
set_pdma(s, ASYNC, 0, len);
s->pdma_cb = do_dma_pdma_cb;
esp_raise_drq(s);
return;
}
}
s->dma_left -= len;
s->async_buf += len;
@ -373,8 +547,7 @@ static void handle_ti(ESPState *s)
s->dma_left = minlen;
s->rregs[ESP_RSTAT] &= ~STAT_TC;
esp_do_dma(s);
}
if (s->do_cmd) {
} else if (s->do_cmd) {
trace_esp_handle_ti_cmd(s->cmdlen);
s->ti_size = 0;
s->cmdlen = 0;
@ -401,6 +574,7 @@ void esp_hard_reset(ESPState *s)
static void esp_soft_reset(ESPState *s)
{
qemu_irq_lower(s->irq);
qemu_irq_lower(s->irq_data);
esp_hard_reset(s);
}
@ -590,6 +764,28 @@ static bool esp_mem_accepts(void *opaque, hwaddr addr,
return (size == 1) || (is_write && size == 4);
}
static bool esp_pdma_needed(void *opaque)
{
ESPState *s = opaque;
return s->dma_memory_read == NULL && s->dma_memory_write == NULL &&
s->dma_enabled;
}
static const VMStateDescription vmstate_esp_pdma = {
.name = "esp/pdma",
.version_id = 1,
.minimum_version_id = 1,
.needed = esp_pdma_needed,
.fields = (VMStateField[]) {
VMSTATE_BUFFER(pdma_buf, ESPState),
VMSTATE_INT32(pdma_origin, ESPState),
VMSTATE_UINT32(pdma_len, ESPState),
VMSTATE_UINT32(pdma_start, ESPState),
VMSTATE_UINT32(pdma_cur, ESPState),
VMSTATE_END_OF_LIST()
}
};
const VMStateDescription vmstate_esp = {
.name ="esp",
.version_id = 4,
@ -611,6 +807,10 @@ const VMStateDescription vmstate_esp = {
VMSTATE_UINT32(do_cmd, ESPState),
VMSTATE_UINT32(dma_left, ESPState),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * []) {
&vmstate_esp_pdma,
NULL
}
};
@ -641,6 +841,82 @@ static const MemoryRegionOps sysbus_esp_mem_ops = {
.valid.accepts = esp_mem_accepts,
};
static void sysbus_esp_pdma_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
SysBusESPState *sysbus = opaque;
ESPState *s = &sysbus->esp;
uint32_t dmalen;
uint8_t *buf = get_pdma_buf(s);
dmalen = s->rregs[ESP_TCLO];
dmalen |= s->rregs[ESP_TCMID] << 8;
dmalen |= s->rregs[ESP_TCHI] << 16;
if (dmalen == 0 || s->pdma_len == 0) {
return;
}
switch (size) {
case 1:
buf[s->pdma_cur++] = val;
s->pdma_len--;
dmalen--;
break;
case 2:
buf[s->pdma_cur++] = val >> 8;
buf[s->pdma_cur++] = val;
s->pdma_len -= 2;
dmalen -= 2;
break;
}
s->rregs[ESP_TCLO] = dmalen & 0xff;
s->rregs[ESP_TCMID] = dmalen >> 8;
s->rregs[ESP_TCHI] = dmalen >> 16;
if (s->pdma_len == 0 && s->pdma_cb) {
esp_lower_drq(s);
s->pdma_cb(s);
s->pdma_cb = NULL;
}
}
static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr,
unsigned int size)
{
SysBusESPState *sysbus = opaque;
ESPState *s = &sysbus->esp;
uint8_t *buf = get_pdma_buf(s);
uint64_t val = 0;
if (s->pdma_len == 0) {
return 0;
}
switch (size) {
case 1:
val = buf[s->pdma_cur++];
s->pdma_len--;
break;
case 2:
val = buf[s->pdma_cur++];
val = (val << 8) | buf[s->pdma_cur++];
s->pdma_len -= 2;
break;
}
if (s->pdma_len == 0 && s->pdma_cb) {
esp_lower_drq(s);
s->pdma_cb(s);
s->pdma_cb = NULL;
}
return val;
}
static const MemoryRegionOps sysbus_esp_pdma_ops = {
.read = sysbus_esp_pdma_read,
.write = sysbus_esp_pdma_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 1,
.valid.max_access_size = 2,
};
static const struct SCSIBusInfo esp_scsi_info = {
.tcq = false,
.max_target = ESP_MAX_DEVS,
@ -673,12 +949,16 @@ static void sysbus_esp_realize(DeviceState *dev, Error **errp)
ESPState *s = &sysbus->esp;
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->irq_data);
assert(sysbus->it_shift != -1);
s->chip_id = TCHI_FAS100A;
memory_region_init_io(&sysbus->iomem, OBJECT(sysbus), &sysbus_esp_mem_ops,
sysbus, "esp", ESP_REGS << sysbus->it_shift);
sysbus, "esp-regs", ESP_REGS << sysbus->it_shift);
sysbus_init_mmio(sbd, &sysbus->iomem);
memory_region_init_io(&sysbus->pdma, OBJECT(sysbus), &sysbus_esp_pdma_ops,
sysbus, "esp-pdma", 2);
sysbus_init_mmio(sbd, &sysbus->pdma);
qdev_init_gpio_in(dev, sysbus_esp_gpio_demux, 2);

76
include/hw/block/swim.h Normal file
View file

@ -0,0 +1,76 @@
/*
* QEMU Macintosh floppy disk controller emulator (SWIM)
*
* Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#ifndef SWIM_H
#define SWIM_H
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#define SWIM_MAX_FD 2
typedef struct SWIMDrive SWIMDrive;
typedef struct SWIMBus SWIMBus;
typedef struct SWIMCtrl SWIMCtrl;
#define TYPE_SWIM_DRIVE "swim-drive"
#define SWIM_DRIVE(obj) OBJECT_CHECK(SWIMDrive, (obj), TYPE_SWIM_DRIVE)
struct SWIMDrive {
DeviceState qdev;
int32_t unit;
BlockConf conf;
};
#define TYPE_SWIM_BUS "swim-bus"
#define SWIM_BUS(obj) OBJECT_CHECK(SWIMBus, (obj), TYPE_SWIM_BUS)
struct SWIMBus {
BusState bus;
struct SWIMCtrl *ctrl;
};
typedef struct FDrive {
SWIMCtrl *swimctrl;
BlockBackend *blk;
BlockConf *conf;
} FDrive;
struct SWIMCtrl {
MemoryRegion iomem;
FDrive drives[SWIM_MAX_FD];
int mode;
/* IWM mode */
int iwm_switch;
uint16_t regs[8];
#define IWM_PH0 0
#define IWM_PH1 1
#define IWM_PH2 2
#define IWM_PH3 3
#define IWM_MTR 4
#define IWM_DRIVE 5
#define IWM_Q6 6
#define IWM_Q7 7
uint8_t iwm_data;
uint8_t iwm_mode;
/* SWIM mode */
uint8_t swim_phase;
uint8_t swim_mode;
SWIMBus bus;
};
#define TYPE_SWIM "swim"
#define SWIM(obj) OBJECT_CHECK(SWIM, (obj), TYPE_SWIM)
typedef struct SWIM {
SysBusDevice parent_obj;
SWIMCtrl ctrl;
} SWIM;
#endif

View file

@ -0,0 +1,64 @@
/*
* QEMU Motorola 680x0 Macintosh Video Card Emulation
* Copyright (c) 2012-2018 Laurent Vivier
*
* some parts from QEMU G364 framebuffer Emulator.
* Copyright (c) 2007-2011 Herve Poussineau
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef MACFB_H
#define MACFB_H
#include "qemu/osdep.h"
#include "exec/memory.h"
#include "ui/console.h"
typedef struct MacfbState {
MemoryRegion mem_vram;
MemoryRegion mem_ctrl;
QemuConsole *con;
uint8_t *vram;
uint32_t vram_bit_mask;
uint32_t palette_current;
uint8_t color_palette[256 * 3];
uint32_t width, height; /* in pixels */
uint8_t depth;
} MacfbState;
#define TYPE_MACFB "sysbus-macfb"
#define MACFB(obj) \
OBJECT_CHECK(MacfbSysBusState, (obj), TYPE_MACFB)
typedef struct {
SysBusDevice busdev;
MacfbState macfb;
} MacfbSysBusState;
#define MACFB_NUBUS_DEVICE_CLASS(class) \
OBJECT_CLASS_CHECK(MacfbNubusDeviceClass, (class), TYPE_NUBUS_MACFB)
#define MACFB_NUBUS_GET_CLASS(obj) \
OBJECT_GET_CLASS(MacfbNubusDeviceClass, (obj), TYPE_NUBUS_MACFB)
typedef struct MacfbNubusDeviceClass {
DeviceClass parent_class;
DeviceRealize parent_realize;
} MacfbNubusDeviceClass;
#define TYPE_NUBUS_MACFB "nubus-macfb"
#define NUBUS_MACFB(obj) \
OBJECT_CHECK(MacfbNubusState, (obj), TYPE_NUBUS_MACFB)
typedef struct {
NubusDevice busdev;
MacfbState macfb;
} MacfbNubusState;
#endif

115
include/hw/misc/mac_via.h Normal file
View file

@ -0,0 +1,115 @@
/*
*
* Copyright (c) 2011-2018 Laurent Vivier
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef HW_MISC_MAC_VIA_H
#define HW_MISC_MAC_VIA_H
#include "exec/memory.h"
#include "hw/sysbus.h"
#include "hw/misc/mos6522.h"
/* VIA 1 */
#define VIA1_IRQ_ONE_SECOND_BIT 0
#define VIA1_IRQ_VBLANK_BIT 1
#define VIA1_IRQ_ADB_READY_BIT 2
#define VIA1_IRQ_ADB_DATA_BIT 3
#define VIA1_IRQ_ADB_CLOCK_BIT 4
#define VIA1_IRQ_NB 8
#define VIA1_IRQ_ONE_SECOND (1 << VIA1_IRQ_ONE_SECOND_BIT)
#define VIA1_IRQ_VBLANK (1 << VIA1_IRQ_VBLANK_BIT)
#define VIA1_IRQ_ADB_READY (1 << VIA1_IRQ_ADB_READY_BIT)
#define VIA1_IRQ_ADB_DATA (1 << VIA1_IRQ_ADB_DATA_BIT)
#define VIA1_IRQ_ADB_CLOCK (1 << VIA1_IRQ_ADB_CLOCK_BIT)
#define TYPE_MOS6522_Q800_VIA1 "mos6522-q800-via1"
#define MOS6522_Q800_VIA1(obj) OBJECT_CHECK(MOS6522Q800VIA1State, (obj), \
TYPE_MOS6522_Q800_VIA1)
typedef struct MOS6522Q800VIA1State {
/*< private >*/
MOS6522State parent_obj;
qemu_irq irqs[VIA1_IRQ_NB];
uint8_t last_b;
uint8_t PRAM[256];
/* external timers */
QEMUTimer *one_second_timer;
int64_t next_second;
QEMUTimer *VBL_timer;
int64_t next_VBL;
} MOS6522Q800VIA1State;
/* VIA 2 */
#define VIA2_IRQ_SCSI_DATA_BIT 0
#define VIA2_IRQ_SLOT_BIT 1
#define VIA2_IRQ_UNUSED_BIT 2
#define VIA2_IRQ_SCSI_BIT 3
#define VIA2_IRQ_ASC_BIT 4
#define VIA2_IRQ_NB 8
#define VIA2_IRQ_SCSI_DATA (1 << VIA2_IRQ_SCSI_DATA_BIT)
#define VIA2_IRQ_SLOT (1 << VIA2_IRQ_SLOT_BIT)
#define VIA2_IRQ_UNUSED (1 << VIA2_IRQ_SCSI_BIT)
#define VIA2_IRQ_SCSI (1 << VIA2_IRQ_UNUSED_BIT)
#define VIA2_IRQ_ASC (1 << VIA2_IRQ_ASC_BIT)
#define TYPE_MOS6522_Q800_VIA2 "mos6522-q800-via2"
#define MOS6522_Q800_VIA2(obj) OBJECT_CHECK(MOS6522Q800VIA2State, (obj), \
TYPE_MOS6522_Q800_VIA2)
typedef struct MOS6522Q800VIA2State {
/*< private >*/
MOS6522State parent_obj;
} MOS6522Q800VIA2State;
#define TYPE_MAC_VIA "mac_via"
#define MAC_VIA(obj) OBJECT_CHECK(MacVIAState, (obj), TYPE_MAC_VIA)
typedef struct MacVIAState {
SysBusDevice busdev;
/* MMIO */
MemoryRegion mmio;
MemoryRegion via1mem;
MemoryRegion via2mem;
/* VIAs */
MOS6522Q800VIA1State mos6522_via1;
MOS6522Q800VIA2State mos6522_via2;
/* RTC */
uint32_t tick_offset;
uint8_t data_out;
int data_out_cnt;
uint8_t data_in;
uint8_t data_in_cnt;
uint8_t cmd;
int wprotect;
int alt;
/* 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

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2013-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef HW_NUBUS_MAC_H
#define HW_NUBUS_MAC_H
#include "hw/nubus/nubus.h"
#define TYPE_MAC_NUBUS_BRIDGE "mac-nubus-bridge"
#define MAC_NUBUS_BRIDGE(obj) OBJECT_CHECK(MacNubusState, (obj), \
TYPE_MAC_NUBUS_BRIDGE)
typedef struct MacNubusState {
SysBusDevice sysbus_dev;
NubusBus *bus;
} MacNubusState;
#endif

69
include/hw/nubus/nubus.h Normal file
View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2013-2018 Laurent Vivier <laurent@vivier.eu>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef HW_NUBUS_NUBUS_H
#define HW_NUBUS_NUBUS_H
#include "hw/qdev-properties.h"
#include "exec/address-spaces.h"
#define NUBUS_SUPER_SLOT_SIZE 0x10000000U
#define NUBUS_SUPER_SLOT_NB 0x9
#define NUBUS_SLOT_SIZE 0x01000000
#define NUBUS_SLOT_NB 0xF
#define NUBUS_FIRST_SLOT 0x9
#define NUBUS_LAST_SLOT 0xF
#define TYPE_NUBUS_DEVICE "nubus-device"
#define NUBUS_DEVICE(obj) \
OBJECT_CHECK(NubusDevice, (obj), TYPE_NUBUS_DEVICE)
#define TYPE_NUBUS_BUS "nubus-bus"
#define NUBUS_BUS(obj) OBJECT_CHECK(NubusBus, (obj), TYPE_NUBUS_BUS)
#define TYPE_NUBUS_BRIDGE "nubus-bridge"
#define NUBUS_BRIDGE(obj) OBJECT_CHECK(NubusBridge, (obj), TYPE_NUBUS_BRIDGE)
typedef struct NubusBus {
BusState qbus;
MemoryRegion super_slot_io;
MemoryRegion slot_io;
int current_slot;
} NubusBus;
typedef struct NubusDevice {
DeviceState qdev;
int slot_nb;
MemoryRegion slot_mem;
/* Format Block */
MemoryRegion fblock_io;
uint32_t rom_length;
uint32_t rom_crc;
uint8_t rom_rev;
uint8_t rom_format;
uint8_t byte_lanes;
int32_t directory_offset;
/* ROM */
MemoryRegion rom_io;
const uint8_t *rom;
} NubusDevice;
void nubus_register_rom(NubusDevice *dev, const uint8_t *rom, uint32_t size,
int revision, int format, uint8_t byte_lanes);
#endif

View file

@ -14,10 +14,18 @@ typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
typedef struct ESPState ESPState;
enum pdma_origin_id {
PDMA,
TI,
CMD,
ASYNC,
};
struct ESPState {
uint8_t rregs[ESP_REGS];
uint8_t wregs[ESP_REGS];
qemu_irq irq;
qemu_irq irq_data;
uint8_t chip_id;
bool tchi_written;
int32_t ti_size;
@ -48,6 +56,12 @@ struct ESPState {
ESPDMAMemoryReadWriteFunc dma_memory_write;
void *dma_opaque;
void (*dma_cb)(ESPState *s);
uint8_t pdma_buf[32];
int pdma_origin;
uint32_t pdma_len;
uint32_t pdma_start;
uint32_t pdma_cur;
void (*pdma_cb)(ESPState *s);
};
#define TYPE_ESP "esp"
@ -59,6 +73,7 @@ typedef struct {
/*< public >*/
MemoryRegion iomem;
MemoryRegion pdma;
uint32_t it_shift;
ESPState esp;
} SysBusESPState;

View file

@ -1859,7 +1859,7 @@ ETEXI
DEF("g", 1, QEMU_OPTION_g ,
"-g WxH[xDEPTH] Set the initial graphical resolution and depth\n",
QEMU_ARCH_PPC | QEMU_ARCH_SPARC)
QEMU_ARCH_PPC | QEMU_ARCH_SPARC | QEMU_ARCH_M68K)
STEXI
@item -g @var{width}x@var{height}[x@var{depth}]
@findex -g

View file

@ -378,3 +378,27 @@ def test_ppc64_pseries(self):
self.vm.launch()
console_pattern = 'Kernel command line: %s' % kernel_command_line
self.wait_for_console_pattern(console_pattern)
def test_m68k_q800(self):
"""
:avocado: tags=arch:m68k
:avocado: tags=machine:q800
"""
deb_url = ('http://ftp.ports.debian.org/debian-ports/pool-m68k/main'
'/l/linux/kernel-image-5.2.0-2-m68k-di_5.2.9-2_m68k.udeb')
deb_hash = '0797e05129595f22f3c0142db5e199769a723bf9'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinux-5.2.0-2-m68k')
self.vm.set_machine('q800')
self.vm.set_console()
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
'console=ttyS0 vga=off')
self.vm.add_args('-kernel', kernel_path,
'-append', kernel_command_line)
self.vm.launch()
console_pattern = 'Kernel command line: %s' % kernel_command_line
self.wait_for_console_pattern(console_pattern)
console_pattern = 'No filesystem could mount root'
self.wait_for_console_pattern(console_pattern)

3
vl.c
View file

@ -3229,7 +3229,8 @@ int main(int argc, char **argv, char **envp)
if (*p == 'x') {
p++;
depth = strtol(p, (char **)&p, 10);
if (depth != 8 && depth != 15 && depth != 16 &&
if (depth != 1 && depth != 2 && depth != 4 &&
depth != 8 && depth != 15 && depth != 16 &&
depth != 24 && depth != 32)
goto graphic_error;
} else if (*p == '\0') {