Add the m68k next-cube machine

-----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCAAxFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAl1zzaUTHGh1dGhAdHV4
 ZmFtaWx5Lm9yZwAKCRAu2dd0/nAttSW4D/0diba+8qhfQnucHFd75xNWetnRoCw2
 PwcQ5U3DVDFAH7JhmUeBnVMeZccs2DappHRoWD1yoCgcwYX83UDlNitZvNi+wp2p
 81oeD6rzY93684xSHc7WHeaCaUoxyCNyTAwZ885CalLO/Jxgwa6VQ5Y3K1kKWblf
 Xne3a0WejRlB+yWxmV9MkG5+34R68RHYD0V5j7L+PWjXczc4kjX4mjTHYJzPtYv+
 WOpPI+aBdoDu+UgjheAbCWIoxz3tIupVOx0tofqv3lGwyOUqGzi63WLUMf50zlZM
 Gs/4l9uxF80C7Ie7sDUnqaOFVRqOCuqPT/Ui9Ojn4iB+0Cs4ZWQ8OTDd4mAsKfIw
 T/3uGvk0+ROyJFhdxG8Tsycb5grSBC5rbUgYusr3MxyDLSr8uWthAlNgEMnnQ05b
 XhYgFKi0aW0iuAvEbzMSptpEXD5OKE59ONAKG3i83l8Noy0IuQG7BHNSeOcNwbHA
 VQLEJoq0TCMZMmRB7J4y1F9ax/dNiZENEt2uaS4QXd6cxdoo+HYnL3QaxT1oM85m
 QfLDMo4t0OF5RRXEQkTt0g6zAluRM7QGO5KfPp3P4sPnpUccZWmwf9icIohEv5Bc
 kVB2vUXBV6768IB75U3UX+pdu1OcESeXgW3u+mVxQtELWhsYRdqyFHjwQZypXh5u
 NFqAgzzNlXGowg==
 =syaC
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/huth-gitlab/tags/m68k-pull-2019-09-07' into staging

Add the m68k next-cube machine

# gpg: Signature made Sat 07 Sep 2019 16:32:53 BST
# gpg:                using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5
# gpg:                issuer "huth@tuxfamily.org"
# gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full]
# gpg:                 aka "Thomas Huth <thuth@redhat.com>" [full]
# gpg:                 aka "Thomas Huth <huth@tuxfamily.org>" [full]
# gpg:                 aka "Thomas Huth <th.huth@posteo.de>" [unknown]
# Primary key fingerprint: 27B8 8847 EEE0 2501 18F3  EAB9 2ED9 D774 FE70 2DB5

* remotes/huth-gitlab/tags/m68k-pull-2019-09-07:
  .travis.yml: Let the avocado job run the NeXTcube tests
  tests/acceptance: Add test of NeXTcube framebuffer using OCR
  m68k: Add an entry for the NeXTcube machine to the MAINTAINERS file
  m68k: Add serial controller to the NeXTcube machine
  escc: introduce a selector for the register bit
  m68k: Add NeXTcube machine
  m68k: Add NeXTcube keyboard device
  m68k: Add NeXTcube framebuffer device emulation

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-09-09 09:48:34 +01:00
commit 89ea03a7dc
14 changed files with 1641 additions and 7 deletions

View File

@ -232,15 +232,20 @@ matrix:
# Acceptance (Functional) tests
- env:
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc64-softmmu"
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc64-softmmu,m68k-softmmu"
- TEST_CMD="make check-acceptance"
after_failure:
- cat tests/results/latest/job.log
addons:
apt:
packages:
- python3-pil
- python3-pip
- python3.5-venv
- tesseract-ocr
- tesseract-ocr-eng
# Using newer GCC with sanitizers
- addons:
apt:

View File

@ -910,6 +910,13 @@ F: hw/char/mcf_uart.c
F: hw/net/mcf_fec.c
F: include/hw/m68k/mcf*.h
NeXTcube
M: Thomas Huth <huth@tuxfamily.org>
S: Odd Fixes
F: hw/m68k/next-*.c
F: hw/display/next-fb.c
F: include/hw/m68k/next-cube.h
MicroBlaze Machines
-------------------
petalogix_s3adsp1800

View File

@ -6,3 +6,4 @@ CONFIG_SEMIHOSTING=y
#
CONFIG_AN5206=y
CONFIG_MCF5208=y
CONFIG_NEXTCUBE=y

View File

@ -45,14 +45,21 @@
* mouse and keyboard ports don't implement all functions and they are
* only asynchronous. There is no DMA.
*
* Z85C30 is also used on PowerMacs. There are some small differences
* between Sparc version (sunzilog) and PowerMac (pmac):
* Z85C30 is also used on PowerMacs and m68k Macs.
*
* There are some small differences between Sparc version (sunzilog)
* and PowerMac (pmac):
* Offset between control and data registers
* There is some kind of lockup bug, but we can ignore it
* CTS is inverted
* DMA on pmac using DBDMA chip
* pmac can do IRDA and faster rates, sunzilog can only do 38400
* pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz
*
* Linux driver for m68k Macs is the same as for PowerMac (pmac_zilog),
* but registers are grouped by type and not by channel:
* channel is selected by bit 0 of the address (instead of bit 1)
* and register is selected by bit 1 of the address (instead of bit 0).
*/
/*
@ -172,6 +179,16 @@ static void handle_kbd_command(ESCCChannelState *s, int val);
static int serial_can_receive(void *opaque);
static void serial_receive_byte(ESCCChannelState *s, int ch);
static int reg_shift(ESCCState *s)
{
return s->bit_swap ? s->it_shift + 1 : s->it_shift;
}
static int chn_shift(ESCCState *s)
{
return s->bit_swap ? s->it_shift : s->it_shift + 1;
}
static void clear_queue(void *opaque)
{
ESCCChannelState *s = opaque;
@ -436,8 +453,8 @@ static void escc_mem_write(void *opaque, hwaddr addr,
int newreg, channel;
val &= 0xff;
saddr = (addr >> serial->it_shift) & 1;
channel = (addr >> (serial->it_shift + 1)) & 1;
saddr = (addr >> reg_shift(serial)) & 1;
channel = (addr >> chn_shift(serial)) & 1;
s = &serial->chn[channel];
switch (saddr) {
case SERIAL_CTRL:
@ -547,8 +564,8 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr,
uint32_t ret;
int channel;
saddr = (addr >> serial->it_shift) & 1;
channel = (addr >> (serial->it_shift + 1)) & 1;
saddr = (addr >> reg_shift(serial)) & 1;
channel = (addr >> chn_shift(serial)) & 1;
s = &serial->chn[channel];
switch (saddr) {
case SERIAL_CTRL:
@ -832,6 +849,7 @@ static void escc_realize(DeviceState *dev, Error **errp)
static Property escc_properties[] = {
DEFINE_PROP_UINT32("frequency", ESCCState, frequency, 0),
DEFINE_PROP_UINT32("it_shift", ESCCState, it_shift, 0),
DEFINE_PROP_BOOL("bit_swap", ESCCState, bit_swap, false),
DEFINE_PROP_UINT32("disabled", ESCCState, disabled, 0),
DEFINE_PROP_UINT32("chnBtype", ESCCState, chn[0].type, 0),
DEFINE_PROP_UINT32("chnAtype", ESCCState, chn[1].type, 0),

View File

@ -38,6 +38,7 @@ common-obj-$(CONFIG_RASPI) += bcm2835_fb.o
common-obj-$(CONFIG_SM501) += sm501.o
common-obj-$(CONFIG_TCX) += tcx.o
common-obj-$(CONFIG_CG3) += cg3.o
common-obj-$(CONFIG_NEXTCUBE) += next-fb.o
obj-$(CONFIG_VGA) += vga.o

146
hw/display/next-fb.c Normal file
View File

@ -0,0 +1,146 @@
/*
* NeXT Cube/Station Framebuffer Emulation
*
* Copyright (c) 2011 Bryce Lanham
*
* 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 "qapi/error.h"
#include "ui/console.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/display/framebuffer.h"
#include "ui/pixel_ops.h"
#include "hw/m68k/next-cube.h"
#define NEXTFB(obj) OBJECT_CHECK(NeXTFbState, (obj), TYPE_NEXTFB)
struct NeXTFbState {
SysBusDevice parent_obj;
MemoryRegion fb_mr;
MemoryRegionSection fbsection;
QemuConsole *con;
uint32_t cols;
uint32_t rows;
int invalidate;
};
typedef struct NeXTFbState NeXTFbState;
static void nextfb_draw_line(void *opaque, uint8_t *d, const uint8_t *s,
int width, int pitch)
{
NeXTFbState *nfbstate = NEXTFB(opaque);
static const uint32_t pal[4] = {
0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000
};
uint32_t *buf = (uint32_t *)d;
int i = 0;
for (i = 0; i < nfbstate->cols / 4; i++) {
int j = i * 4;
uint8_t src = s[i];
buf[j + 3] = pal[src & 0x3];
src >>= 2;
buf[j + 2] = pal[src & 0x3];
src >>= 2;
buf[j + 1] = pal[src & 0x3];
src >>= 2;
buf[j + 0] = pal[src & 0x3];
}
}
static void nextfb_update(void *opaque)
{
NeXTFbState *s = NEXTFB(opaque);
int dest_width = 4;
int src_width;
int first = 0;
int last = 0;
DisplaySurface *surface = qemu_console_surface(s->con);
src_width = s->cols / 4 + 8;
dest_width = s->cols * 4;
if (s->invalidate) {
framebuffer_update_memory_section(&s->fbsection, &s->fb_mr, 0,
s->cols, src_width);
s->invalidate = 0;
}
framebuffer_update_display(surface, &s->fbsection, s->cols, s->rows,
src_width, dest_width, 0, 1, nextfb_draw_line,
s, &first, &last);
dpy_gfx_update(s->con, 0, 0, s->cols, s->rows);
}
static void nextfb_invalidate(void *opaque)
{
NeXTFbState *s = NEXTFB(opaque);
s->invalidate = 1;
}
static const GraphicHwOps nextfb_ops = {
.invalidate = nextfb_invalidate,
.gfx_update = nextfb_update,
};
static void nextfb_realize(DeviceState *dev, Error **errp)
{
NeXTFbState *s = NEXTFB(dev);
memory_region_init_ram(&s->fb_mr, OBJECT(dev), "next-video", 0x1CB100,
&error_fatal);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->fb_mr);
s->invalidate = 1;
s->cols = 1120;
s->rows = 832;
s->con = graphic_console_init(dev, 0, &nextfb_ops, s);
qemu_console_resize(s->con, s->cols, s->rows);
}
static void nextfb_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
dc->realize = nextfb_realize;
/* Note: This device does not any state that we have to reset or migrate */
}
static const TypeInfo nextfb_info = {
.name = TYPE_NEXTFB,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NeXTFbState),
.class_init = nextfb_class_init,
};
static void nextfb_register_types(void)
{
type_register_static(&nextfb_info);
}
type_init(nextfb_register_types)

View File

@ -7,3 +7,8 @@ config MCF5208
bool
select COLDFIRE
select PTIMER
config NEXTCUBE
bool
select FRAMEBUFFER
select ESCC

View File

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

978
hw/m68k/next-cube.c Normal file
View File

@ -0,0 +1,978 @@
/*
* NeXT Cube System Driver
*
* Copyright (c) 2011 Bryce Lanham
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/hwaddr.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
#include "sysemu/qtest.h"
#include "hw/irq.h"
#include "hw/m68k/next-cube.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/scsi/esp.h"
#include "hw/sysbus.h"
#include "hw/char/escc.h" /* ZILOG 8530 Serial Emulation */
#include "hw/block/fdc.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "ui/console.h"
#include "target/m68k/cpu.h"
/* #define DEBUG_NEXT */
#ifdef DEBUG_NEXT
#define DPRINTF(fmt, ...) \
do { printf("NeXT: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do { } while (0)
#endif
#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube")
#define NEXT_MACHINE(obj) OBJECT_CHECK(NeXTState, (obj), TYPE_NEXT_MACHINE)
#define ENTRY 0x0100001e
#define RAM_SIZE 0x4000000
#define ROM_FILE "Rev_2.5_v66.bin"
typedef struct next_dma {
uint32_t csr;
uint32_t saved_next;
uint32_t saved_limit;
uint32_t saved_start;
uint32_t saved_stop;
uint32_t next;
uint32_t limit;
uint32_t start;
uint32_t stop;
uint32_t next_initbuf;
uint32_t size;
} next_dma;
typedef struct {
MachineState parent;
uint32_t int_mask;
uint32_t int_status;
uint8_t scsi_csr_1;
uint8_t scsi_csr_2;
next_dma dma[10];
qemu_irq *scsi_irq;
qemu_irq scsi_dma;
qemu_irq scsi_reset;
qemu_irq *fd_irq;
uint32_t scr1;
uint32_t scr2;
uint8_t rtc_ram[32];
} NeXTState;
/* Thanks to NeXT forums for this */
/*
static const uint8_t rtc_ram3[32] = {
0x94, 0x0f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x7B, 0x00,
0x00, 0x00, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x13
};
*/
static const uint8_t rtc_ram2[32] = {
0x94, 0x0f, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x4b, 0x00,
0x41, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x7e,
};
#define SCR2_RTCLK 0x2
#define SCR2_RTDATA 0x4
#define SCR2_TOBCD(x) (((x / 10) << 4) + (x % 10))
static void nextscr2_write(NeXTState *s, uint32_t val, int size)
{
static int led;
static int phase;
static uint8_t old_scr2;
static uint8_t rtc_command;
static uint8_t rtc_value;
static uint8_t rtc_status = 0x90;
static uint8_t rtc_return;
uint8_t scr2_2;
if (size == 4) {
scr2_2 = (val >> 8) & 0xFF;
} else {
scr2_2 = val & 0xFF;
}
if (val & 0x1) {
DPRINTF("fault!\n");
led++;
if (led == 10) {
DPRINTF("LED flashing, possible fault!\n");
led = 0;
}
}
if (scr2_2 & 0x1) {
/* DPRINTF("RTC %x phase %i\n", scr2_2, phase); */
if (phase == -1) {
phase = 0;
}
/* If we are in going down clock... do something */
if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) &&
((scr2_2 & SCR2_RTCLK) == 0)) {
if (phase < 8) {
rtc_command = (rtc_command << 1) |
((scr2_2 & SCR2_RTDATA) ? 1 : 0);
}
if (phase >= 8 && phase < 16) {
rtc_value = (rtc_value << 1) | ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
/* if we read RAM register, output RT_DATA bit */
if (rtc_command <= 0x1F) {
scr2_2 = scr2_2 & (~SCR2_RTDATA);
if (s->rtc_ram[rtc_command] & (0x80 >> (phase - 8))) {
scr2_2 |= SCR2_RTDATA;
}
rtc_return = (rtc_return << 1) |
((scr2_2 & SCR2_RTDATA) ? 1 : 0);
}
/* read the status 0x30 */
if (rtc_command == 0x30) {
scr2_2 = scr2_2 & (~SCR2_RTDATA);
/* for now status = 0x98 (new rtc + FTU) */
if (rtc_status & (0x80 >> (phase - 8))) {
scr2_2 |= SCR2_RTDATA;
}
rtc_return = (rtc_return << 1) |
((scr2_2 & SCR2_RTDATA) ? 1 : 0);
}
/* read the status 0x31 */
if (rtc_command == 0x31) {
scr2_2 = scr2_2 & (~SCR2_RTDATA);
/* for now 0x00 */
if (0x00 & (0x80 >> (phase - 8))) {
scr2_2 |= SCR2_RTDATA;
}
rtc_return = (rtc_return << 1) |
((scr2_2 & SCR2_RTDATA) ? 1 : 0);
}
if ((rtc_command >= 0x20) && (rtc_command <= 0x2F)) {
scr2_2 = scr2_2 & (~SCR2_RTDATA);
/* for now 0x00 */
time_t time_h = time(NULL);
struct tm *info = localtime(&time_h);
int ret = 0;
switch (rtc_command) {
case 0x20:
ret = SCR2_TOBCD(info->tm_sec);
break;
case 0x21:
ret = SCR2_TOBCD(info->tm_min);
break;
case 0x22:
ret = SCR2_TOBCD(info->tm_hour);
break;
case 0x24:
ret = SCR2_TOBCD(info->tm_mday);
break;
case 0x25:
ret = SCR2_TOBCD((info->tm_mon + 1));
break;
case 0x26:
ret = SCR2_TOBCD((info->tm_year - 100));
break;
}
if (ret & (0x80 >> (phase - 8))) {
scr2_2 |= SCR2_RTDATA;
}
rtc_return = (rtc_return << 1) |
((scr2_2 & SCR2_RTDATA) ? 1 : 0);
}
}
phase++;
if (phase == 16) {
if (rtc_command >= 0x80 && rtc_command <= 0x9F) {
s->rtc_ram[rtc_command - 0x80] = rtc_value;
}
/* write to x30 register */
if (rtc_command == 0xB1) {
/* clear FTU */
if (rtc_value & 0x04) {
rtc_status = rtc_status & (~0x18);
s->int_status = s->int_status & (~0x04);
}
}
}
}
} else {
/* else end or abort */
phase = -1;
rtc_command = 0;
rtc_value = 0;
}
s->scr2 = val & 0xFFFF00FF;
s->scr2 |= scr2_2 << 8;
old_scr2 = scr2_2;
}
static uint32_t mmio_readb(NeXTState *s, hwaddr addr)
{
switch (addr) {
case 0xc000:
return (s->scr1 >> 24) & 0xFF;
case 0xc001:
return (s->scr1 >> 16) & 0xFF;
case 0xc002:
return (s->scr1 >> 8) & 0xFF;
case 0xc003:
return (s->scr1 >> 0) & 0xFF;
case 0xd000:
return (s->scr2 >> 24) & 0xFF;
case 0xd001:
return (s->scr2 >> 16) & 0xFF;
case 0xd002:
return (s->scr2 >> 8) & 0xFF;
case 0xd003:
return (s->scr2 >> 0) & 0xFF;
case 0x14020:
DPRINTF("MMIO Read 0x4020\n");
return 0x7f;
default:
DPRINTF("MMIO Read B @ %"HWADDR_PRIx"\n", addr);
return 0x0;
}
}
static uint32_t mmio_readw(NeXTState *s, hwaddr addr)
{
switch (addr) {
default:
DPRINTF("MMIO Read W @ %"HWADDR_PRIx"\n", addr);
return 0x0;
}
}
static uint32_t mmio_readl(NeXTState *s, hwaddr addr)
{
switch (addr) {
case 0x7000:
/* DPRINTF("Read INT status: %x\n", s->int_status); */
return s->int_status;
case 0x7800:
DPRINTF("MMIO Read INT mask: %x\n", s->int_mask);
return s->int_mask;
case 0xc000:
return s->scr1;
case 0xd000:
return s->scr2;
default:
DPRINTF("MMIO Read L @ %"HWADDR_PRIx"\n", addr);
return 0x0;
}
}
static void mmio_writeb(NeXTState *s, hwaddr addr, uint32_t val)
{
switch (addr) {
case 0xd003:
nextscr2_write(s, val, 1);
break;
default:
DPRINTF("MMIO Write B @ %x with %x\n", (unsigned int)addr, val);
}
}
static void mmio_writew(NeXTState *s, hwaddr addr, uint32_t val)
{
DPRINTF("MMIO Write W\n");
}
static void mmio_writel(NeXTState *s, hwaddr addr, uint32_t val)
{
switch (addr) {
case 0x7000:
DPRINTF("INT Status old: %x new: %x\n", s->int_status, val);
s->int_status = val;
break;
case 0x7800:
DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, val);
s->int_mask = val;
break;
case 0xc000:
DPRINTF("SCR1 Write: %x\n", val);
break;
case 0xd000:
nextscr2_write(s, val, 4);
break;
default:
DPRINTF("MMIO Write l @ %x with %x\n", (unsigned int)addr, val);
}
}
static uint64_t mmio_readfn(void *opaque, hwaddr addr, unsigned size)
{
NeXTState *ns = NEXT_MACHINE(opaque);
switch (size) {
case 1:
return mmio_readb(ns, addr);
case 2:
return mmio_readw(ns, addr);
case 4:
return mmio_readl(ns, addr);
default:
g_assert_not_reached();
}
}
static void mmio_writefn(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
{
NeXTState *ns = NEXT_MACHINE(opaque);
switch (size) {
case 1:
mmio_writeb(ns, addr, value);
break;
case 2:
mmio_writew(ns, addr, value);
break;
case 4:
mmio_writel(ns, addr, value);
break;
default:
g_assert_not_reached();
}
}
static const MemoryRegionOps mmio_ops = {
.read = mmio_readfn,
.write = mmio_writefn,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static uint32_t scr_readb(NeXTState *s, hwaddr addr)
{
switch (addr) {
case 0x14108:
DPRINTF("FD read @ %x\n", (unsigned int)addr);
return 0x40 | 0x04 | 0x2 | 0x1;
case 0x14020:
DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1);
return s->scsi_csr_1;
case 0x14021:
DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2);
return 0x40;
/*
* These 4 registers are the hardware timer, not sure which register
* is the latch instead of data, but no problems so far
*/
case 0x1a000:
return 0xff & (clock() >> 24);
case 0x1a001:
return 0xff & (clock() >> 16);
case 0x1a002:
return 0xff & (clock() >> 8);
case 0x1a003:
/* Hack: We need to have this change consistently to make it work */
return 0xFF & clock();
default:
DPRINTF("BMAP Read B @ %x\n", (unsigned int)addr);
return 0;
}
}
static uint32_t scr_readw(NeXTState *s, hwaddr addr)
{
DPRINTF("BMAP Read W @ %x\n", (unsigned int)addr);
return 0;
}
static uint32_t scr_readl(NeXTState *s, hwaddr addr)
{
DPRINTF("BMAP Read L @ %x\n", (unsigned int)addr);
return 0;
}
#define SCSICSR_ENABLE 0x01
#define SCSICSR_RESET 0x02 /* reset scsi dma */
#define SCSICSR_FIFOFL 0x04
#define SCSICSR_DMADIR 0x08 /* if set, scsi to mem */
#define SCSICSR_CPUDMA 0x10 /* if set, dma enabled */
#define SCSICSR_INTMASK 0x20 /* if set, interrupt enabled */
static void scr_writeb(NeXTState *s, hwaddr addr, uint32_t value)
{
switch (addr) {
case 0x14108:
DPRINTF("FDCSR Write: %x\n", value);
if (value == 0x0) {
/* qemu_irq_raise(s->fd_irq[0]); */
}
break;
case 0x14020: /* SCSI Control Register */
if (value & SCSICSR_FIFOFL) {
DPRINTF("SCSICSR FIFO Flush\n");
/* will have to add another irq to the esp if this is needed */
/* esp_puflush_fifo(esp_g); */
/* qemu_irq_pulse(s->scsi_dma); */
}
if (value & SCSICSR_ENABLE) {
DPRINTF("SCSICSR Enable\n");
/*
* qemu_irq_raise(s->scsi_dma);
* s->scsi_csr_1 = 0xc0;
* s->scsi_csr_1 |= 0x1;
* qemu_irq_pulse(s->scsi_dma);
*/
}
/*
* else
* s->scsi_csr_1 &= ~SCSICSR_ENABLE;
*/
if (value & SCSICSR_RESET) {
DPRINTF("SCSICSR Reset\n");
/* I think this should set DMADIR. CPUDMA and INTMASK to 0 */
/* qemu_irq_raise(s->scsi_reset); */
/* s->scsi_csr_1 &= ~(SCSICSR_INTMASK |0x80|0x1); */
}
if (value & SCSICSR_DMADIR) {
DPRINTF("SCSICSR DMAdir\n");
}
if (value & SCSICSR_CPUDMA) {
DPRINTF("SCSICSR CPUDMA\n");
/* qemu_irq_raise(s->scsi_dma); */
s->int_status |= 0x4000000;
} else {
s->int_status &= ~(0x4000000);
}
if (value & SCSICSR_INTMASK) {
DPRINTF("SCSICSR INTMASK\n");
/*
* int_mask &= ~0x1000;
* s->scsi_csr_1 |= value;
* s->scsi_csr_1 &= ~SCSICSR_INTMASK;
* if (s->scsi_queued) {
* s->scsi_queued = 0;
* next_irq(s, NEXT_SCSI_I, level);
* }
*/
} else {
/* int_mask |= 0x1000; */
}
if (value & 0x80) {
/* int_mask |= 0x1000; */
/* s->scsi_csr_1 |= 0x80; */
}
DPRINTF("SCSICSR Write: %x\n", value);
/* s->scsi_csr_1 = value; */
return;
/* Hardware timer latch - not implemented yet */
case 0x1a000:
default:
DPRINTF("BMAP Write B @ %x with %x\n", (unsigned int)addr, value);
}
}
static void scr_writew(NeXTState *s, hwaddr addr, uint32_t value)
{
DPRINTF("BMAP Write W @ %x with %x\n", (unsigned int)addr, value);
}
static void scr_writel(NeXTState *s, hwaddr addr, uint32_t value)
{
DPRINTF("BMAP Write L @ %x with %x\n", (unsigned int)addr, value);
}
static uint64_t scr_readfn(void *opaque, hwaddr addr, unsigned size)
{
NeXTState *ns = NEXT_MACHINE(opaque);
switch (size) {
case 1:
return scr_readb(ns, addr);
case 2:
return scr_readw(ns, addr);
case 4:
return scr_readl(ns, addr);
default:
g_assert_not_reached();
}
}
static void scr_writefn(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
{
NeXTState *ns = NEXT_MACHINE(opaque);
switch (size) {
case 1:
scr_writeb(ns, addr, value);
break;
case 2:
scr_writew(ns, addr, value);
break;
case 4:
scr_writel(ns, addr, value);
break;
default:
g_assert_not_reached();
}
}
static const MemoryRegionOps scr_ops = {
.read = scr_readfn,
.write = scr_writefn,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
#define NEXTDMA_SCSI(x) (0x10 + x)
#define NEXTDMA_FD(x) (0x10 + x)
#define NEXTDMA_ENTX(x) (0x110 + x)
#define NEXTDMA_ENRX(x) (0x150 + x)
#define NEXTDMA_CSR 0x0
#define NEXTDMA_NEXT 0x4000
#define NEXTDMA_LIMIT 0x4004
#define NEXTDMA_START 0x4008
#define NEXTDMA_STOP 0x400c
#define NEXTDMA_NEXT_INIT 0x4200
#define NEXTDMA_SIZE 0x4204
static void dma_writel(void *opaque, hwaddr addr, uint64_t value,
unsigned int size)
{
NeXTState *next_state = NEXT_MACHINE(opaque);
switch (addr) {
case NEXTDMA_ENRX(NEXTDMA_CSR):
if (value & DMA_DEV2M) {
next_state->dma[NEXTDMA_ENRX].csr |= DMA_DEV2M;
}
if (value & DMA_SETENABLE) {
/* DPRINTF("SCSI DMA ENABLE\n"); */
next_state->dma[NEXTDMA_ENRX].csr |= DMA_ENABLE;
}
if (value & DMA_SETSUPDATE) {
next_state->dma[NEXTDMA_ENRX].csr |= DMA_SUPDATE;
}
if (value & DMA_CLRCOMPLETE) {
next_state->dma[NEXTDMA_ENRX].csr &= ~DMA_COMPLETE;
}
if (value & DMA_RESET) {
next_state->dma[NEXTDMA_ENRX].csr &= ~(DMA_COMPLETE | DMA_SUPDATE |
DMA_ENABLE | DMA_DEV2M);
}
/* DPRINTF("RXCSR \tWrite: %x\n",value); */
break;
case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT):
next_state->dma[NEXTDMA_ENRX].next_initbuf = value;
break;
case NEXTDMA_ENRX(NEXTDMA_NEXT):
next_state->dma[NEXTDMA_ENRX].next = value;
break;
case NEXTDMA_ENRX(NEXTDMA_LIMIT):
next_state->dma[NEXTDMA_ENRX].limit = value;
break;
case NEXTDMA_SCSI(NEXTDMA_CSR):
if (value & DMA_DEV2M) {
next_state->dma[NEXTDMA_SCSI].csr |= DMA_DEV2M;
}
if (value & DMA_SETENABLE) {
/* DPRINTF("SCSI DMA ENABLE\n"); */
next_state->dma[NEXTDMA_SCSI].csr |= DMA_ENABLE;
}
if (value & DMA_SETSUPDATE) {
next_state->dma[NEXTDMA_SCSI].csr |= DMA_SUPDATE;
}
if (value & DMA_CLRCOMPLETE) {
next_state->dma[NEXTDMA_SCSI].csr &= ~DMA_COMPLETE;
}
if (value & DMA_RESET) {
next_state->dma[NEXTDMA_SCSI].csr &= ~(DMA_COMPLETE | DMA_SUPDATE |
DMA_ENABLE | DMA_DEV2M);
/* DPRINTF("SCSI DMA RESET\n"); */
}
/* DPRINTF("RXCSR \tWrite: %x\n",value); */
break;
case NEXTDMA_SCSI(NEXTDMA_NEXT):
next_state->dma[NEXTDMA_SCSI].next = value;
break;
case NEXTDMA_SCSI(NEXTDMA_LIMIT):
next_state->dma[NEXTDMA_SCSI].limit = value;
break;
case NEXTDMA_SCSI(NEXTDMA_START):
next_state->dma[NEXTDMA_SCSI].start = value;
break;
case NEXTDMA_SCSI(NEXTDMA_STOP):
next_state->dma[NEXTDMA_SCSI].stop = value;
break;
case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT):
next_state->dma[NEXTDMA_SCSI].next_initbuf = value;
break;
default:
DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)value);
}
}
static uint64_t dma_readl(void *opaque, hwaddr addr, unsigned int size)
{
NeXTState *next_state = NEXT_MACHINE(opaque);
switch (addr) {
case NEXTDMA_SCSI(NEXTDMA_CSR):
DPRINTF("SCSI DMA CSR READ\n");
return next_state->dma[NEXTDMA_SCSI].csr;
case NEXTDMA_ENRX(NEXTDMA_CSR):
return next_state->dma[NEXTDMA_ENRX].csr;
case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT):
return next_state->dma[NEXTDMA_ENRX].next_initbuf;
case NEXTDMA_ENRX(NEXTDMA_NEXT):
return next_state->dma[NEXTDMA_ENRX].next;
case NEXTDMA_ENRX(NEXTDMA_LIMIT):
return next_state->dma[NEXTDMA_ENRX].limit;
case NEXTDMA_SCSI(NEXTDMA_NEXT):
return next_state->dma[NEXTDMA_SCSI].next;
case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT):
return next_state->dma[NEXTDMA_SCSI].next_initbuf;
case NEXTDMA_SCSI(NEXTDMA_LIMIT):
return next_state->dma[NEXTDMA_SCSI].limit;
case NEXTDMA_SCSI(NEXTDMA_START):
return next_state->dma[NEXTDMA_SCSI].start;
case NEXTDMA_SCSI(NEXTDMA_STOP):
return next_state->dma[NEXTDMA_SCSI].stop;
default:
DPRINTF("DMA read @ %x\n", (unsigned int)addr);
return 0;
}
/*
* once the csr's are done, subtract 0x3FEC from the addr, and that will
* normalize the upper registers
*/
}
static const MemoryRegionOps dma_ops = {
.read = dma_readl,
.write = dma_writel,
.impl.min_access_size = 4,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
/*
* TODO: set the shift numbers as values in the enum, so the first switch
* will not be needed
*/
void next_irq(void *opaque, int number, int level)
{
M68kCPU *cpu = opaque;
int shift = 0;
NeXTState *ns = NEXT_MACHINE(qdev_get_machine());
/* first switch sets interupt status */
/* DPRINTF("IRQ %i\n",number); */
switch (number) {
/* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */
case NEXT_FD_I:
shift = 7;;
break;
case NEXT_KBD_I:
shift = 3;
break;
case NEXT_PWR_I:
shift = 2;
break;
case NEXT_ENRX_I:
shift = 9;
break;
case NEXT_ENTX_I:
shift = 10;
break;
case NEXT_SCSI_I:
shift = 12;
break;
case NEXT_CLK_I:
shift = 5;
break;
/* level 5 - scc (serial) */
case NEXT_SCC_I:
shift = 17;
break;
/* level 6 - audio etherrx/tx dma */
case NEXT_ENTX_DMA_I:
shift = 28;
break;
case NEXT_ENRX_DMA_I:
shift = 27;
break;
case NEXT_SCSI_DMA_I:
shift = 26;
break;
case NEXT_SND_I:
shift = 23;
break;
case NEXT_SCC_DMA_I:
shift = 21;
break;
}
/*
* this HAS to be wrong, the interrupt handlers in mach and together
* int_status and int_mask and return if there is a hit
*/
if (ns->int_mask & (1 << shift)) {
DPRINTF("%x interrupt masked @ %x\n", 1 << shift, cpu->env.pc);
/* return; */
}
/* second switch triggers the correct interrupt */
if (level) {
ns->int_status |= 1 << shift;
switch (number) {
/* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */
case NEXT_FD_I:
case NEXT_KBD_I:
case NEXT_PWR_I:
case NEXT_ENRX_I:
case NEXT_ENTX_I:
case NEXT_SCSI_I:
case NEXT_CLK_I:
m68k_set_irq_level(cpu, 3, 27);
break;
/* level 5 - scc (serial) */
case NEXT_SCC_I:
m68k_set_irq_level(cpu, 5, 29);
break;
/* level 6 - audio etherrx/tx dma */
case NEXT_ENTX_DMA_I:
case NEXT_ENRX_DMA_I:
case NEXT_SCSI_DMA_I:
case NEXT_SND_I:
case NEXT_SCC_DMA_I:
m68k_set_irq_level(cpu, 6, 30);
break;
}
} else {
ns->int_status &= ~(1 << shift);
cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
}
static void next_serial_irq(void *opaque, int n, int level)
{
/* DPRINTF("SCC IRQ NUM %i\n",n); */
if (n) {
next_irq(opaque, NEXT_SCC_DMA_I, level);
} else {
next_irq(opaque, NEXT_SCC_I, level);
}
}
static void next_escc_init(M68kCPU *cpu)
{
qemu_irq *ser_irq = qemu_allocate_irqs(next_serial_irq, cpu, 2);
DeviceState *dev;
SysBusDevice *s;
dev = qdev_create(NULL, TYPE_ESCC);
qdev_prop_set_uint32(dev, "disabled", 0);
qdev_prop_set_uint32(dev, "frequency", 9600 * 384);
qdev_prop_set_uint32(dev, "it_shift", 0);
qdev_prop_set_bit(dev, "bit_swap", true);
qdev_prop_set_chr(dev, "chrB", serial_hd(1));
qdev_prop_set_chr(dev, "chrA", serial_hd(0));
qdev_prop_set_uint32(dev, "chnBtype", escc_serial);
qdev_prop_set_uint32(dev, "chnAtype", escc_serial);
qdev_init_nofail(dev);
s = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(s, 0, ser_irq[0]);
sysbus_connect_irq(s, 1, ser_irq[1]);
sysbus_mmio_map(s, 0, 0x2118000);
}
static void next_cube_init(MachineState *machine)
{
M68kCPU *cpu;
CPUM68KState *env;
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *rom = g_new(MemoryRegion, 1);
MemoryRegion *mmiomem = g_new(MemoryRegion, 1);
MemoryRegion *scrmem = g_new(MemoryRegion, 1);
MemoryRegion *dmamem = g_new(MemoryRegion, 1);
MemoryRegion *bmapm1 = g_new(MemoryRegion, 1);
MemoryRegion *bmapm2 = g_new(MemoryRegion, 1);
MemoryRegion *sysmem = get_system_memory();
NeXTState *ns = NEXT_MACHINE(machine);
DeviceState *dev;
/* Initialize the cpu core */
cpu = M68K_CPU(cpu_create(machine->cpu_type));
if (!cpu) {
error_report("Unable to find m68k CPU definition");
exit(1);
}
env = &cpu->env;
/* Initialize CPU registers. */
env->vbr = 0;
env->sr = 0x2700;
/* Set internal registers to initial values */
/* 0x0000XX00 << vital bits */
ns->scr1 = 0x00011102;
ns->scr2 = 0x00ff0c80;
/* Load RTC RAM - TODO: provide possibility to load contents from file */
memcpy(ns->rtc_ram, rtc_ram2, 32);
/* 64MB RAM starting at 0x04000000 */
memory_region_allocate_system_memory(ram, NULL, "next.ram", ram_size);
memory_region_add_subregion(sysmem, 0x04000000, ram);
/* Framebuffer */
dev = qdev_create(NULL, TYPE_NEXTFB);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000);
/* MMIO */
memory_region_init_io(mmiomem, NULL, &mmio_ops, machine, "next.mmio",
0xD0000);
memory_region_add_subregion(sysmem, 0x02000000, mmiomem);
/* BMAP memory */
memory_region_init_ram_shared_nomigrate(bmapm1, NULL, "next.bmapmem", 64,
true, &error_fatal);
memory_region_add_subregion(sysmem, 0x020c0000, bmapm1);
/* The Rev_2.5_v66.bin firmware accesses it at 0x820c0020, too */
memory_region_init_alias(bmapm2, NULL, "next.bmapmem2", bmapm1, 0x0, 64);
memory_region_add_subregion(sysmem, 0x820c0000, bmapm2);
/* BMAP IO - acts as a catch-all for now */
memory_region_init_io(scrmem, NULL, &scr_ops, machine, "next.scr",
0x20000);
memory_region_add_subregion(sysmem, 0x02100000, scrmem);
/* KBD */
dev = qdev_create(NULL, TYPE_NEXTKBD);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000);
/* Load ROM here */
if (bios_name == NULL) {
bios_name = ROM_FILE;
}
/* still not sure if the rom should also be mapped at 0x0*/
memory_region_init_rom(rom, NULL, "next.rom", 0x20000, &error_fatal);
memory_region_add_subregion(sysmem, 0x01000000, rom);
if (load_image_targphys(bios_name, 0x01000000, 0x20000) < 8) {
if (!qtest_enabled()) {
error_report("Failed to load firmware '%s'.", bios_name);
}
} else {
uint8_t *ptr;
/* Initial PC is always at offset 4 in firmware binaries */
ptr = rom_ptr(0x01000004, 4);
g_assert(ptr != NULL);
env->pc = ldl_p(ptr);
if (env->pc >= 0x01020000) {
error_report("'%s' does not seem to be a valid firmware image.",
bios_name);
exit(1);
}
}
/* Serial */
next_escc_init(cpu);
/* TODO: */
/* Network */
/* SCSI */
/* DMA */
memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma", 0x5000);
memory_region_add_subregion(sysmem, 0x02000000, dmamem);
}
static void next_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "NeXT Cube";
mc->init = next_cube_init;
mc->default_ram_size = RAM_SIZE;
mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
}
static const TypeInfo next_typeinfo = {
.name = TYPE_NEXT_MACHINE,
.parent = TYPE_MACHINE,
.class_init = next_machine_class_init,
.instance_size = sizeof(NeXTState),
};
static void next_register_type(void)
{
type_register_static(&next_typeinfo);
}
type_init(next_register_type)

291
hw/m68k/next-kbd.c Normal file
View File

@ -0,0 +1,291 @@
/*
* QEMU NeXT Keyboard/Mouse emulation
*
* Copyright (c) 2011 Bryce Lanham
*
* 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.
*/
/*
* This is admittedly hackish, but works well enough for basic input. Mouse
* support will be added once we can boot something that needs the mouse.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "exec/address-spaces.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/m68k/next-cube.h"
#include "ui/console.h"
#include "sysemu/sysemu.h"
#include "migration/vmstate.h"
#define NEXTKBD(obj) OBJECT_CHECK(NextKBDState, (obj), TYPE_NEXTKBD)
/* following defintions from next68k netbsd */
#define CSR_INT 0x00800000
#define CSR_DATA 0x00400000
#define KD_KEYMASK 0x007f
#define KD_DIRECTION 0x0080 /* pressed or released */
#define KD_CNTL 0x0100
#define KD_LSHIFT 0x0200
#define KD_RSHIFT 0x0400
#define KD_LCOMM 0x0800
#define KD_RCOMM 0x1000
#define KD_LALT 0x2000
#define KD_RALT 0x4000
#define KD_VALID 0x8000 /* only set for scancode keys ? */
#define KD_MODS 0x4f00
#define KBD_QUEUE_SIZE 256
typedef struct {
uint8_t data[KBD_QUEUE_SIZE];
int rptr, wptr, count;
} KBDQueue;
typedef struct NextKBDState {
SysBusDevice sbd;
MemoryRegion mr;
KBDQueue queue;
uint16_t shift;
} NextKBDState;
static void queue_code(void *opaque, int code);
/* lots of magic numbers here */
static uint32_t kbd_read_byte(void *opaque, hwaddr addr)
{
switch (addr & 0x3) {
case 0x0: /* 0xe000 */
return 0x80 | 0x20;
case 0x1: /* 0xe001 */
return 0x80 | 0x40 | 0x20 | 0x10;
case 0x2: /* 0xe002 */
/* returning 0x40 caused mach to hang */
return 0x10 | 0x2 | 0x1;
default:
qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr);
}
return 0;
}
static uint32_t kbd_read_word(void *opaque, hwaddr addr)
{
qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr);
return 0;
}
/* even more magic numbers */
static uint32_t kbd_read_long(void *opaque, hwaddr addr)
{
int key = 0;
NextKBDState *s = NEXTKBD(opaque);
KBDQueue *q = &s->queue;
switch (addr & 0xf) {
case 0x0: /* 0xe000 */
return 0xA0F09300;
case 0x8: /* 0xe008 */
/* get keycode from buffer */
if (q->count > 0) {
key = q->data[q->rptr];
if (++q->rptr == KBD_QUEUE_SIZE) {
q->rptr = 0;
}
q->count--;
if (s->shift) {
key |= s->shift;
}
if (key & 0x80) {
return 0;
} else {
return 0x10000000 | KD_VALID | key;
}
} else {
return 0;
}
default:
qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr);
return 0;
}
}
static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size)
{
switch (size) {
case 1:
return kbd_read_byte(opaque, addr);
case 2:
return kbd_read_word(opaque, addr);
case 4:
return kbd_read_long(opaque, addr);
default:
g_assert_not_reached();
}
}
static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
{
qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx
"val=0x%"PRIx64"\n", size, addr, value);
}
static const MemoryRegionOps kbd_ops = {
.read = kbd_readfn,
.write = kbd_writefn,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void nextkbd_event(void *opaque, int ch)
{
/*
* Will want to set vars for caps/num lock
* if (ch & 0x80) -> key release
* there's also e0 escaped scancodes that might need to be handled
*/
queue_code(opaque, ch);
}
static const unsigned char next_keycodes[128] = {
0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F,
0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00,
0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06,
0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C,
0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34,
0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00,
0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static void queue_code(void *opaque, int code)
{
NextKBDState *s = NEXTKBD(opaque);
KBDQueue *q = &s->queue;
int key = code & KD_KEYMASK;
int release = code & 0x80;
static int ext;
if (code == 0xE0) {
ext = 1;
}
if (code == 0x2A || code == 0x1D || code == 0x36) {
if (code == 0x2A) {
s->shift = KD_LSHIFT;
} else if (code == 0x36) {
s->shift = KD_RSHIFT;
ext = 0;
} else if (code == 0x1D && !ext) {
s->shift = KD_LCOMM;
} else if (code == 0x1D && ext) {
ext = 0;
s->shift = KD_RCOMM;
}
return;
} else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) ||
code == (0x36 | 0x80)) {
s->shift = 0;
return;
}
if (q->count >= KBD_QUEUE_SIZE) {
return;
}
q->data[q->wptr] = next_keycodes[key] | release;
if (++q->wptr == KBD_QUEUE_SIZE) {
q->wptr = 0;
}
q->count++;
/*
* might need to actually trigger the NeXT irq, but as the keyboard works
* at the moment, I'll worry about it later
*/
/* s->update_irq(s->update_arg, 1); */
}
static void nextkbd_reset(DeviceState *dev)
{
NextKBDState *nks = NEXTKBD(dev);
memset(&nks->queue, 0, sizeof(KBDQueue));
nks->shift = 0;
}
static void nextkbd_realize(DeviceState *dev, Error **errp)
{
NextKBDState *s = NEXTKBD(dev);
memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
qemu_add_kbd_event_handler(nextkbd_event, s);
}
static const VMStateDescription nextkbd_vmstate = {
.name = TYPE_NEXTKBD,
.unmigratable = 1, /* TODO: Implement this when m68k CPU is migratable */
};
static void nextkbd_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
dc->vmsd = &nextkbd_vmstate;
dc->realize = nextkbd_realize;
dc->reset = nextkbd_reset;
}
static const TypeInfo nextkbd_info = {
.name = TYPE_NEXTKBD,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NextKBDState),
.class_init = nextkbd_class_init,
};
static void nextkbd_register_types(void)
{
type_register_static(&nextkbd_info);
}
type_init(nextkbd_register_types)

View File

@ -51,6 +51,7 @@ typedef struct ESCCState {
struct ESCCChannelState chn[2];
uint32_t it_shift;
bool bit_swap;
MemoryRegion mmio;
uint32_t disabled;
uint32_t frequency;

View File

@ -0,0 +1,47 @@
#ifndef NEXT_CUBE_H
#define NEXT_CUBE_H
#define TYPE_NEXTFB "next-fb"
#define TYPE_NEXTKBD "next-kbd"
enum next_dma_chan {
NEXTDMA_FD,
NEXTDMA_ENRX,
NEXTDMA_ENTX,
NEXTDMA_SCSI,
NEXTDMA_SCC,
NEXTDMA_SND
};
#define DMA_ENABLE 0x01000000
#define DMA_SUPDATE 0x02000000
#define DMA_COMPLETE 0x08000000
#define DMA_M2DEV 0x0
#define DMA_SETENABLE 0x00010000
#define DMA_SETSUPDATE 0x00020000
#define DMA_DEV2M 0x00040000
#define DMA_CLRCOMPLETE 0x00080000
#define DMA_RESET 0x00100000
enum next_irqs {
NEXT_FD_I,
NEXT_KBD_I,
NEXT_PWR_I,
NEXT_ENRX_I,
NEXT_ENTX_I,
NEXT_SCSI_I,
NEXT_CLK_I,
NEXT_SCC_I,
NEXT_ENTX_DMA_I,
NEXT_ENRX_DMA_I,
NEXT_SCSI_DMA_I,
NEXT_SCC_DMA_I,
NEXT_SND_I
};
void next_irq(void *opaque, int number, int level);
#endif /* NEXT_CUBE_H */

View File

@ -0,0 +1,121 @@
# Functional test that boots a VM and run OCR on the framebuffer
#
# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org>
#
# This work is licensed under the terms of the GNU GPL, version 2 or
# later. See the COPYING file in the top-level directory.
import os
import re
import time
import logging
import distutils.spawn
from avocado_qemu import Test
from avocado import skipUnless
from avocado.utils import process
from avocado.utils.path import find_command, CmdNotFoundError
PIL_AVAILABLE = True
try:
from PIL import Image
except ImportError:
PIL_AVAILABLE = False
def tesseract_available(expected_version):
try:
find_command('tesseract')
except CmdNotFoundError:
return False
res = process.run('tesseract --version')
try:
version = res.stdout_text.split()[1]
except IndexError:
version = res.stderr_text.split()[1]
return int(version.split('.')[0]) == expected_version
match = re.match(r'tesseract\s(\d)', res)
if match is None:
return False
# now this is guaranteed to be a digit
return int(match.groups()[0]) == expected_version
class NextCubeMachine(Test):
timeout = 15
def check_bootrom_framebuffer(self, screenshot_path):
rom_url = ('http://www.nextcomputers.org/NeXTfiles/Software/ROM_Files/'
'68040_Non-Turbo_Chipset/Rev_2.5_v66.BIN')
rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24'
rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
self.vm.set_machine('next-cube')
self.vm.add_args('-bios', rom_path)
self.vm.launch()
self.log.info('VM launched, waiting for display')
# TODO: Use avocado.utils.wait.wait_for to catch the
# 'displaysurface_create 1120x832' trace-event.
time.sleep(2)
self.vm.command('human-monitor-command',
command_line='screendump %s' % screenshot_path)
@skipUnless(PIL_AVAILABLE, 'Python PIL not installed')
def test_bootrom_framebuffer_size(self):
"""
:avocado: tags=arch:m68k
:avocado: tags=machine:next_cube
:avocado: tags=device:framebuffer
"""
screenshot_path = os.path.join(self.workdir, "dump.png")
self.check_bootrom_framebuffer(screenshot_path)
width, height = Image.open(screenshot_path).size
self.assertEqual(width, 1120)
self.assertEqual(height, 832)
@skipUnless(tesseract_available(3), 'tesseract v3 OCR tool not available')
def test_bootrom_framebuffer_ocr_with_tesseract_v3(self):
"""
:avocado: tags=arch:m68k
:avocado: tags=machine:next_cube
:avocado: tags=device:framebuffer
"""
screenshot_path = os.path.join(self.workdir, "dump.png")
self.check_bootrom_framebuffer(screenshot_path)
console_logger = logging.getLogger('console')
text = process.run("tesseract %s stdout" % screenshot_path).stdout_text
for line in text.split('\n'):
if len(line):
console_logger.debug(line)
self.assertIn('Backplane', text)
self.assertIn('Ethernet address', text)
# Tesseract 4 adds a new OCR engine based on LSTM neural networks. The
# new version is faster and more accurate than version 3. The drawback is
# that it is still alpha-level software.
@skipUnless(tesseract_available(4), 'tesseract v4 OCR tool not available')
def test_bootrom_framebuffer_ocr_with_tesseract_v4(self):
"""
:avocado: tags=arch:m68k
:avocado: tags=machine:next_cube
:avocado: tags=device:framebuffer
"""
screenshot_path = os.path.join(self.workdir, "dump.png")
self.check_bootrom_framebuffer(screenshot_path)
console_logger = logging.getLogger('console')
proc = process.run("tesseract --oem 1 %s stdout" % screenshot_path)
text = proc.stdout_text
for line in text.split('\n'):
if len(line):
console_logger.debug(line)
self.assertIn('Testing the FPU, SCC', text)
self.assertIn('System test failed. Error code 51', text)
self.assertIn('Boot command', text)
self.assertIn('Next>', text)

View File

@ -24,6 +24,17 @@ static const uint8_t kernel_mcf5208[] = {
0x60, 0xfa /* bra.s loop */
};
static const uint8_t bios_nextcube[] = {
0x06, 0x00, 0x00, 0x00, /* Initial SP */
0x01, 0x00, 0x00, 0x08, /* Initial PC */
0x41, 0xf9, 0x02, 0x11, 0x80, 0x00, /* lea 0x02118000,%a0 */
0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */
0x11, 0x7c, 0x00, 0x05, 0x00, 0x01, /* move.b #5,1(%a0) Sel TXCTRL */
0x11, 0x7c, 0x00, 0x68, 0x00, 0x01, /* move.b #0x68,1(%a0) Enable TX */
0x11, 0x40, 0x00, 0x03, /* move.b %d0,3(%a0) Print 'T' */
0x60, 0xfa /* bra.s loop */
};
static const uint8_t kernel_pls3adsp1800[] = {
0xb0, 0x00, 0x84, 0x00, /* imm 0x8400 */
0x30, 0x60, 0x00, 0x04, /* addik r3,r0,4 */
@ -117,6 +128,7 @@ static testdef_t tests[] = {
{ "sparc64", "sun4u", "", "UltraSPARC" },
{ "s390x", "s390-ccw-virtio", "", "device" },
{ "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 },
{ "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube },
{ "microblaze", "petalogix-s3adsp1800", "", "TT",
sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 },
{ "microblazeel", "petalogix-ml605", "", "TT",