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:
commit
f724de8dde
14
MAINTAINERS
14
MAINTAINERS
@ -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
|
||||
|
@ -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;
|
||||
|
@ -7,3 +7,4 @@ CONFIG_SEMIHOSTING=y
|
||||
CONFIG_AN5206=y
|
||||
CONFIG_MCF5208=y
|
||||
CONFIG_NEXTCUBE=y
|
||||
CONFIG_Q800=y
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
489
hw/block/swim.c
Normal 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)
|
@ -132,3 +132,8 @@ config ATI_VGA
|
||||
select VGA
|
||||
select BITBANG_I2C
|
||||
select DDC
|
||||
|
||||
config MACFB
|
||||
bool
|
||||
select FRAMEBUFFER
|
||||
depends on NUBUS
|
||||
|
@ -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
477
hw/display/macfb.c
Normal 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)
|
@ -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
|
||||
|
@ -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
114
hw/m68k/bootinfo.h
Normal 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
401
hw/m68k/q800.c
Normal 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)
|
@ -120,4 +120,9 @@ config AUX
|
||||
config UNIMP
|
||||
bool
|
||||
|
||||
config MAC_VIA
|
||||
bool
|
||||
select MOS6522
|
||||
select ADB
|
||||
|
||||
source macio/Kconfig
|
||||
|
@ -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
964
hw/misc/mac_via.c
Normal 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);
|
@ -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
2
hw/nubus/Kconfig
Normal file
@ -0,0 +1,2 @@
|
||||
config NUBUS
|
||||
bool
|
4
hw/nubus/Makefile.objs
Normal file
4
hw/nubus/Makefile.objs
Normal 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
|
45
hw/nubus/mac-nubus-bridge.c
Normal file
45
hw/nubus/mac-nubus-bridge.c
Normal 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
34
hw/nubus/nubus-bridge.c
Normal 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
111
hw/nubus/nubus-bus.c
Normal 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
215
hw/nubus/nubus-device.c
Normal 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)
|
340
hw/scsi/esp.c
340
hw/scsi/esp.c
@ -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) {
|
||||
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;
|
||||
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);
|
||||
s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
|
||||
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) {
|
||||
s->dma_memory_read(s->dma_opaque, s->async_buf, len);
|
||||
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 {
|
||||
s->dma_memory_write(s->dma_opaque, s->async_buf, len);
|
||||
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
76
include/hw/block/swim.h
Normal 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
|
64
include/hw/display/macfb.h
Normal file
64
include/hw/display/macfb.h
Normal 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
115
include/hw/misc/mac_via.h
Normal 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
|
24
include/hw/nubus/mac-nubus-bridge.h
Normal file
24
include/hw/nubus/mac-nubus-bridge.h
Normal 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
69
include/hw/nubus/nubus.h
Normal 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
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -378,3 +378,27 @@ class BootLinuxConsole(Test):
|
||||
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
3
vl.c
@ -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') {
|
||||
|
Loading…
Reference in New Issue
Block a user