Revision | 89ea03a7dc83ca36b670ba7f787802791fcb04b1 (tree) |
---|---|
Zeit | 2019-09-09 17:48:34 |
Autor | Peter Maydell <peter.maydell@lina...> |
Commiter | Peter Maydell |
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:
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
@@ -232,15 +232,20 @@ matrix: | ||
232 | 232 | |
233 | 233 | # Acceptance (Functional) tests |
234 | 234 | - env: |
235 | - - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc64-softmmu" | |
235 | + - 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" | |
236 | 236 | - TEST_CMD="make check-acceptance" |
237 | 237 | after_failure: |
238 | 238 | - cat tests/results/latest/job.log |
239 | 239 | addons: |
240 | 240 | apt: |
241 | 241 | packages: |
242 | + - python3-pil | |
242 | 243 | - python3-pip |
243 | 244 | - python3.5-venv |
245 | + - tesseract-ocr | |
246 | + - tesseract-ocr-eng | |
247 | + | |
248 | + | |
244 | 249 | # Using newer GCC with sanitizers |
245 | 250 | - addons: |
246 | 251 | apt: |
@@ -910,6 +910,13 @@ F: hw/char/mcf_uart.c | ||
910 | 910 | F: hw/net/mcf_fec.c |
911 | 911 | F: include/hw/m68k/mcf*.h |
912 | 912 | |
913 | +NeXTcube | |
914 | +M: Thomas Huth <huth@tuxfamily.org> | |
915 | +S: Odd Fixes | |
916 | +F: hw/m68k/next-*.c | |
917 | +F: hw/display/next-fb.c | |
918 | +F: include/hw/m68k/next-cube.h | |
919 | + | |
913 | 920 | MicroBlaze Machines |
914 | 921 | ------------------- |
915 | 922 | petalogix_s3adsp1800 |
@@ -6,3 +6,4 @@ CONFIG_SEMIHOSTING=y | ||
6 | 6 | # |
7 | 7 | CONFIG_AN5206=y |
8 | 8 | CONFIG_MCF5208=y |
9 | +CONFIG_NEXTCUBE=y |
@@ -45,14 +45,21 @@ | ||
45 | 45 | * mouse and keyboard ports don't implement all functions and they are |
46 | 46 | * only asynchronous. There is no DMA. |
47 | 47 | * |
48 | - * Z85C30 is also used on PowerMacs. There are some small differences | |
49 | - * between Sparc version (sunzilog) and PowerMac (pmac): | |
48 | + * Z85C30 is also used on PowerMacs and m68k Macs. | |
49 | + * | |
50 | + * There are some small differences between Sparc version (sunzilog) | |
51 | + * and PowerMac (pmac): | |
50 | 52 | * Offset between control and data registers |
51 | 53 | * There is some kind of lockup bug, but we can ignore it |
52 | 54 | * CTS is inverted |
53 | 55 | * DMA on pmac using DBDMA chip |
54 | 56 | * pmac can do IRDA and faster rates, sunzilog can only do 38400 |
55 | 57 | * pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz |
58 | + * | |
59 | + * Linux driver for m68k Macs is the same as for PowerMac (pmac_zilog), | |
60 | + * but registers are grouped by type and not by channel: | |
61 | + * channel is selected by bit 0 of the address (instead of bit 1) | |
62 | + * and register is selected by bit 1 of the address (instead of bit 0). | |
56 | 63 | */ |
57 | 64 | |
58 | 65 | /* |
@@ -172,6 +179,16 @@ static void handle_kbd_command(ESCCChannelState *s, int val); | ||
172 | 179 | static int serial_can_receive(void *opaque); |
173 | 180 | static void serial_receive_byte(ESCCChannelState *s, int ch); |
174 | 181 | |
182 | +static int reg_shift(ESCCState *s) | |
183 | +{ | |
184 | + return s->bit_swap ? s->it_shift + 1 : s->it_shift; | |
185 | +} | |
186 | + | |
187 | +static int chn_shift(ESCCState *s) | |
188 | +{ | |
189 | + return s->bit_swap ? s->it_shift : s->it_shift + 1; | |
190 | +} | |
191 | + | |
175 | 192 | static void clear_queue(void *opaque) |
176 | 193 | { |
177 | 194 | ESCCChannelState *s = opaque; |
@@ -436,8 +453,8 @@ static void escc_mem_write(void *opaque, hwaddr addr, | ||
436 | 453 | int newreg, channel; |
437 | 454 | |
438 | 455 | val &= 0xff; |
439 | - saddr = (addr >> serial->it_shift) & 1; | |
440 | - channel = (addr >> (serial->it_shift + 1)) & 1; | |
456 | + saddr = (addr >> reg_shift(serial)) & 1; | |
457 | + channel = (addr >> chn_shift(serial)) & 1; | |
441 | 458 | s = &serial->chn[channel]; |
442 | 459 | switch (saddr) { |
443 | 460 | case SERIAL_CTRL: |
@@ -547,8 +564,8 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr, | ||
547 | 564 | uint32_t ret; |
548 | 565 | int channel; |
549 | 566 | |
550 | - saddr = (addr >> serial->it_shift) & 1; | |
551 | - channel = (addr >> (serial->it_shift + 1)) & 1; | |
567 | + saddr = (addr >> reg_shift(serial)) & 1; | |
568 | + channel = (addr >> chn_shift(serial)) & 1; | |
552 | 569 | s = &serial->chn[channel]; |
553 | 570 | switch (saddr) { |
554 | 571 | case SERIAL_CTRL: |
@@ -832,6 +849,7 @@ static void escc_realize(DeviceState *dev, Error **errp) | ||
832 | 849 | static Property escc_properties[] = { |
833 | 850 | DEFINE_PROP_UINT32("frequency", ESCCState, frequency, 0), |
834 | 851 | DEFINE_PROP_UINT32("it_shift", ESCCState, it_shift, 0), |
852 | + DEFINE_PROP_BOOL("bit_swap", ESCCState, bit_swap, false), | |
835 | 853 | DEFINE_PROP_UINT32("disabled", ESCCState, disabled, 0), |
836 | 854 | DEFINE_PROP_UINT32("chnBtype", ESCCState, chn[0].type, 0), |
837 | 855 | DEFINE_PROP_UINT32("chnAtype", ESCCState, chn[1].type, 0), |
@@ -38,6 +38,7 @@ common-obj-$(CONFIG_RASPI) += bcm2835_fb.o | ||
38 | 38 | common-obj-$(CONFIG_SM501) += sm501.o |
39 | 39 | common-obj-$(CONFIG_TCX) += tcx.o |
40 | 40 | common-obj-$(CONFIG_CG3) += cg3.o |
41 | +common-obj-$(CONFIG_NEXTCUBE) += next-fb.o | |
41 | 42 | |
42 | 43 | obj-$(CONFIG_VGA) += vga.o |
43 | 44 |
@@ -0,0 +1,146 @@ | ||
1 | +/* | |
2 | + * NeXT Cube/Station Framebuffer Emulation | |
3 | + * | |
4 | + * Copyright (c) 2011 Bryce Lanham | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include "qemu/osdep.h" | |
25 | +#include "qapi/error.h" | |
26 | +#include "ui/console.h" | |
27 | +#include "hw/hw.h" | |
28 | +#include "hw/boards.h" | |
29 | +#include "hw/loader.h" | |
30 | +#include "hw/display/framebuffer.h" | |
31 | +#include "ui/pixel_ops.h" | |
32 | +#include "hw/m68k/next-cube.h" | |
33 | + | |
34 | +#define NEXTFB(obj) OBJECT_CHECK(NeXTFbState, (obj), TYPE_NEXTFB) | |
35 | + | |
36 | +struct NeXTFbState { | |
37 | + SysBusDevice parent_obj; | |
38 | + | |
39 | + MemoryRegion fb_mr; | |
40 | + MemoryRegionSection fbsection; | |
41 | + QemuConsole *con; | |
42 | + | |
43 | + uint32_t cols; | |
44 | + uint32_t rows; | |
45 | + int invalidate; | |
46 | +}; | |
47 | +typedef struct NeXTFbState NeXTFbState; | |
48 | + | |
49 | +static void nextfb_draw_line(void *opaque, uint8_t *d, const uint8_t *s, | |
50 | + int width, int pitch) | |
51 | +{ | |
52 | + NeXTFbState *nfbstate = NEXTFB(opaque); | |
53 | + static const uint32_t pal[4] = { | |
54 | + 0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000 | |
55 | + }; | |
56 | + uint32_t *buf = (uint32_t *)d; | |
57 | + int i = 0; | |
58 | + | |
59 | + for (i = 0; i < nfbstate->cols / 4; i++) { | |
60 | + int j = i * 4; | |
61 | + uint8_t src = s[i]; | |
62 | + buf[j + 3] = pal[src & 0x3]; | |
63 | + src >>= 2; | |
64 | + buf[j + 2] = pal[src & 0x3]; | |
65 | + src >>= 2; | |
66 | + buf[j + 1] = pal[src & 0x3]; | |
67 | + src >>= 2; | |
68 | + buf[j + 0] = pal[src & 0x3]; | |
69 | + } | |
70 | +} | |
71 | + | |
72 | +static void nextfb_update(void *opaque) | |
73 | +{ | |
74 | + NeXTFbState *s = NEXTFB(opaque); | |
75 | + int dest_width = 4; | |
76 | + int src_width; | |
77 | + int first = 0; | |
78 | + int last = 0; | |
79 | + DisplaySurface *surface = qemu_console_surface(s->con); | |
80 | + | |
81 | + src_width = s->cols / 4 + 8; | |
82 | + dest_width = s->cols * 4; | |
83 | + | |
84 | + if (s->invalidate) { | |
85 | + framebuffer_update_memory_section(&s->fbsection, &s->fb_mr, 0, | |
86 | + s->cols, src_width); | |
87 | + s->invalidate = 0; | |
88 | + } | |
89 | + | |
90 | + framebuffer_update_display(surface, &s->fbsection, s->cols, s->rows, | |
91 | + src_width, dest_width, 0, 1, nextfb_draw_line, | |
92 | + s, &first, &last); | |
93 | + | |
94 | + dpy_gfx_update(s->con, 0, 0, s->cols, s->rows); | |
95 | +} | |
96 | + | |
97 | +static void nextfb_invalidate(void *opaque) | |
98 | +{ | |
99 | + NeXTFbState *s = NEXTFB(opaque); | |
100 | + s->invalidate = 1; | |
101 | +} | |
102 | + | |
103 | +static const GraphicHwOps nextfb_ops = { | |
104 | + .invalidate = nextfb_invalidate, | |
105 | + .gfx_update = nextfb_update, | |
106 | +}; | |
107 | + | |
108 | +static void nextfb_realize(DeviceState *dev, Error **errp) | |
109 | +{ | |
110 | + NeXTFbState *s = NEXTFB(dev); | |
111 | + | |
112 | + memory_region_init_ram(&s->fb_mr, OBJECT(dev), "next-video", 0x1CB100, | |
113 | + &error_fatal); | |
114 | + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->fb_mr); | |
115 | + | |
116 | + s->invalidate = 1; | |
117 | + s->cols = 1120; | |
118 | + s->rows = 832; | |
119 | + | |
120 | + s->con = graphic_console_init(dev, 0, &nextfb_ops, s); | |
121 | + qemu_console_resize(s->con, s->cols, s->rows); | |
122 | +} | |
123 | + | |
124 | +static void nextfb_class_init(ObjectClass *oc, void *data) | |
125 | +{ | |
126 | + DeviceClass *dc = DEVICE_CLASS(oc); | |
127 | + | |
128 | + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); | |
129 | + dc->realize = nextfb_realize; | |
130 | + | |
131 | + /* Note: This device does not any state that we have to reset or migrate */ | |
132 | +} | |
133 | + | |
134 | +static const TypeInfo nextfb_info = { | |
135 | + .name = TYPE_NEXTFB, | |
136 | + .parent = TYPE_SYS_BUS_DEVICE, | |
137 | + .instance_size = sizeof(NeXTFbState), | |
138 | + .class_init = nextfb_class_init, | |
139 | +}; | |
140 | + | |
141 | +static void nextfb_register_types(void) | |
142 | +{ | |
143 | + type_register_static(&nextfb_info); | |
144 | +} | |
145 | + | |
146 | +type_init(nextfb_register_types) |
@@ -7,3 +7,8 @@ config MCF5208 | ||
7 | 7 | bool |
8 | 8 | select COLDFIRE |
9 | 9 | select PTIMER |
10 | + | |
11 | +config NEXTCUBE | |
12 | + bool | |
13 | + select FRAMEBUFFER | |
14 | + select ESCC |
@@ -1,2 +1,3 @@ | ||
1 | 1 | obj-$(CONFIG_AN5206) += an5206.o mcf5206.o |
2 | 2 | obj-$(CONFIG_MCF5208) += mcf5208.o mcf_intc.o |
3 | +obj-$(CONFIG_NEXTCUBE) += next-kbd.o next-cube.o |
@@ -0,0 +1,978 @@ | ||
1 | +/* | |
2 | + * NeXT Cube System Driver | |
3 | + * | |
4 | + * Copyright (c) 2011 Bryce Lanham | |
5 | + * | |
6 | + * This code is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published | |
8 | + * by the Free Software Foundation; either version 2 of the License, | |
9 | + * or (at your option) any later version. | |
10 | + */ | |
11 | + | |
12 | +#include "qemu/osdep.h" | |
13 | +#include "cpu.h" | |
14 | +#include "exec/hwaddr.h" | |
15 | +#include "exec/address-spaces.h" | |
16 | +#include "sysemu/sysemu.h" | |
17 | +#include "sysemu/qtest.h" | |
18 | +#include "hw/irq.h" | |
19 | +#include "hw/m68k/next-cube.h" | |
20 | +#include "hw/boards.h" | |
21 | +#include "hw/loader.h" | |
22 | +#include "hw/scsi/esp.h" | |
23 | +#include "hw/sysbus.h" | |
24 | +#include "hw/char/escc.h" /* ZILOG 8530 Serial Emulation */ | |
25 | +#include "hw/block/fdc.h" | |
26 | +#include "hw/qdev-properties.h" | |
27 | +#include "qapi/error.h" | |
28 | +#include "ui/console.h" | |
29 | +#include "target/m68k/cpu.h" | |
30 | + | |
31 | +/* #define DEBUG_NEXT */ | |
32 | +#ifdef DEBUG_NEXT | |
33 | +#define DPRINTF(fmt, ...) \ | |
34 | + do { printf("NeXT: " fmt , ## __VA_ARGS__); } while (0) | |
35 | +#else | |
36 | +#define DPRINTF(fmt, ...) do { } while (0) | |
37 | +#endif | |
38 | + | |
39 | +#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube") | |
40 | +#define NEXT_MACHINE(obj) OBJECT_CHECK(NeXTState, (obj), TYPE_NEXT_MACHINE) | |
41 | + | |
42 | +#define ENTRY 0x0100001e | |
43 | +#define RAM_SIZE 0x4000000 | |
44 | +#define ROM_FILE "Rev_2.5_v66.bin" | |
45 | + | |
46 | +typedef struct next_dma { | |
47 | + uint32_t csr; | |
48 | + | |
49 | + uint32_t saved_next; | |
50 | + uint32_t saved_limit; | |
51 | + uint32_t saved_start; | |
52 | + uint32_t saved_stop; | |
53 | + | |
54 | + uint32_t next; | |
55 | + uint32_t limit; | |
56 | + uint32_t start; | |
57 | + uint32_t stop; | |
58 | + | |
59 | + uint32_t next_initbuf; | |
60 | + uint32_t size; | |
61 | +} next_dma; | |
62 | + | |
63 | +typedef struct { | |
64 | + MachineState parent; | |
65 | + | |
66 | + uint32_t int_mask; | |
67 | + uint32_t int_status; | |
68 | + | |
69 | + uint8_t scsi_csr_1; | |
70 | + uint8_t scsi_csr_2; | |
71 | + next_dma dma[10]; | |
72 | + qemu_irq *scsi_irq; | |
73 | + qemu_irq scsi_dma; | |
74 | + qemu_irq scsi_reset; | |
75 | + qemu_irq *fd_irq; | |
76 | + | |
77 | + uint32_t scr1; | |
78 | + uint32_t scr2; | |
79 | + | |
80 | + uint8_t rtc_ram[32]; | |
81 | +} NeXTState; | |
82 | + | |
83 | +/* Thanks to NeXT forums for this */ | |
84 | +/* | |
85 | +static const uint8_t rtc_ram3[32] = { | |
86 | + 0x94, 0x0f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, | |
87 | + 0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x7B, 0x00, | |
88 | + 0x00, 0x00, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00, | |
89 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x13 | |
90 | +}; | |
91 | +*/ | |
92 | +static const uint8_t rtc_ram2[32] = { | |
93 | + 0x94, 0x0f, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, | |
94 | + 0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x4b, 0x00, | |
95 | + 0x41, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, | |
96 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x7e, | |
97 | +}; | |
98 | + | |
99 | +#define SCR2_RTCLK 0x2 | |
100 | +#define SCR2_RTDATA 0x4 | |
101 | +#define SCR2_TOBCD(x) (((x / 10) << 4) + (x % 10)) | |
102 | + | |
103 | +static void nextscr2_write(NeXTState *s, uint32_t val, int size) | |
104 | +{ | |
105 | + static int led; | |
106 | + static int phase; | |
107 | + static uint8_t old_scr2; | |
108 | + static uint8_t rtc_command; | |
109 | + static uint8_t rtc_value; | |
110 | + static uint8_t rtc_status = 0x90; | |
111 | + static uint8_t rtc_return; | |
112 | + uint8_t scr2_2; | |
113 | + | |
114 | + if (size == 4) { | |
115 | + scr2_2 = (val >> 8) & 0xFF; | |
116 | + } else { | |
117 | + scr2_2 = val & 0xFF; | |
118 | + } | |
119 | + | |
120 | + if (val & 0x1) { | |
121 | + DPRINTF("fault!\n"); | |
122 | + led++; | |
123 | + if (led == 10) { | |
124 | + DPRINTF("LED flashing, possible fault!\n"); | |
125 | + led = 0; | |
126 | + } | |
127 | + } | |
128 | + | |
129 | + if (scr2_2 & 0x1) { | |
130 | + /* DPRINTF("RTC %x phase %i\n", scr2_2, phase); */ | |
131 | + if (phase == -1) { | |
132 | + phase = 0; | |
133 | + } | |
134 | + /* If we are in going down clock... do something */ | |
135 | + if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) && | |
136 | + ((scr2_2 & SCR2_RTCLK) == 0)) { | |
137 | + if (phase < 8) { | |
138 | + rtc_command = (rtc_command << 1) | | |
139 | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); | |
140 | + } | |
141 | + if (phase >= 8 && phase < 16) { | |
142 | + rtc_value = (rtc_value << 1) | ((scr2_2 & SCR2_RTDATA) ? 1 : 0); | |
143 | + | |
144 | + /* if we read RAM register, output RT_DATA bit */ | |
145 | + if (rtc_command <= 0x1F) { | |
146 | + scr2_2 = scr2_2 & (~SCR2_RTDATA); | |
147 | + if (s->rtc_ram[rtc_command] & (0x80 >> (phase - 8))) { | |
148 | + scr2_2 |= SCR2_RTDATA; | |
149 | + } | |
150 | + | |
151 | + rtc_return = (rtc_return << 1) | | |
152 | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); | |
153 | + } | |
154 | + /* read the status 0x30 */ | |
155 | + if (rtc_command == 0x30) { | |
156 | + scr2_2 = scr2_2 & (~SCR2_RTDATA); | |
157 | + /* for now status = 0x98 (new rtc + FTU) */ | |
158 | + if (rtc_status & (0x80 >> (phase - 8))) { | |
159 | + scr2_2 |= SCR2_RTDATA; | |
160 | + } | |
161 | + | |
162 | + rtc_return = (rtc_return << 1) | | |
163 | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); | |
164 | + } | |
165 | + /* read the status 0x31 */ | |
166 | + if (rtc_command == 0x31) { | |
167 | + scr2_2 = scr2_2 & (~SCR2_RTDATA); | |
168 | + /* for now 0x00 */ | |
169 | + if (0x00 & (0x80 >> (phase - 8))) { | |
170 | + scr2_2 |= SCR2_RTDATA; | |
171 | + } | |
172 | + rtc_return = (rtc_return << 1) | | |
173 | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); | |
174 | + } | |
175 | + | |
176 | + if ((rtc_command >= 0x20) && (rtc_command <= 0x2F)) { | |
177 | + scr2_2 = scr2_2 & (~SCR2_RTDATA); | |
178 | + /* for now 0x00 */ | |
179 | + time_t time_h = time(NULL); | |
180 | + struct tm *info = localtime(&time_h); | |
181 | + int ret = 0; | |
182 | + | |
183 | + switch (rtc_command) { | |
184 | + case 0x20: | |
185 | + ret = SCR2_TOBCD(info->tm_sec); | |
186 | + break; | |
187 | + case 0x21: | |
188 | + ret = SCR2_TOBCD(info->tm_min); | |
189 | + break; | |
190 | + case 0x22: | |
191 | + ret = SCR2_TOBCD(info->tm_hour); | |
192 | + break; | |
193 | + case 0x24: | |
194 | + ret = SCR2_TOBCD(info->tm_mday); | |
195 | + break; | |
196 | + case 0x25: | |
197 | + ret = SCR2_TOBCD((info->tm_mon + 1)); | |
198 | + break; | |
199 | + case 0x26: | |
200 | + ret = SCR2_TOBCD((info->tm_year - 100)); | |
201 | + break; | |
202 | + | |
203 | + } | |
204 | + | |
205 | + if (ret & (0x80 >> (phase - 8))) { | |
206 | + scr2_2 |= SCR2_RTDATA; | |
207 | + } | |
208 | + rtc_return = (rtc_return << 1) | | |
209 | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); | |
210 | + } | |
211 | + | |
212 | + } | |
213 | + | |
214 | + phase++; | |
215 | + if (phase == 16) { | |
216 | + if (rtc_command >= 0x80 && rtc_command <= 0x9F) { | |
217 | + s->rtc_ram[rtc_command - 0x80] = rtc_value; | |
218 | + } | |
219 | + /* write to x30 register */ | |
220 | + if (rtc_command == 0xB1) { | |
221 | + /* clear FTU */ | |
222 | + if (rtc_value & 0x04) { | |
223 | + rtc_status = rtc_status & (~0x18); | |
224 | + s->int_status = s->int_status & (~0x04); | |
225 | + } | |
226 | + } | |
227 | + } | |
228 | + } | |
229 | + } else { | |
230 | + /* else end or abort */ | |
231 | + phase = -1; | |
232 | + rtc_command = 0; | |
233 | + rtc_value = 0; | |
234 | + } | |
235 | + s->scr2 = val & 0xFFFF00FF; | |
236 | + s->scr2 |= scr2_2 << 8; | |
237 | + old_scr2 = scr2_2; | |
238 | +} | |
239 | + | |
240 | +static uint32_t mmio_readb(NeXTState *s, hwaddr addr) | |
241 | +{ | |
242 | + switch (addr) { | |
243 | + case 0xc000: | |
244 | + return (s->scr1 >> 24) & 0xFF; | |
245 | + case 0xc001: | |
246 | + return (s->scr1 >> 16) & 0xFF; | |
247 | + case 0xc002: | |
248 | + return (s->scr1 >> 8) & 0xFF; | |
249 | + case 0xc003: | |
250 | + return (s->scr1 >> 0) & 0xFF; | |
251 | + | |
252 | + case 0xd000: | |
253 | + return (s->scr2 >> 24) & 0xFF; | |
254 | + case 0xd001: | |
255 | + return (s->scr2 >> 16) & 0xFF; | |
256 | + case 0xd002: | |
257 | + return (s->scr2 >> 8) & 0xFF; | |
258 | + case 0xd003: | |
259 | + return (s->scr2 >> 0) & 0xFF; | |
260 | + case 0x14020: | |
261 | + DPRINTF("MMIO Read 0x4020\n"); | |
262 | + return 0x7f; | |
263 | + | |
264 | + default: | |
265 | + DPRINTF("MMIO Read B @ %"HWADDR_PRIx"\n", addr); | |
266 | + return 0x0; | |
267 | + } | |
268 | +} | |
269 | + | |
270 | +static uint32_t mmio_readw(NeXTState *s, hwaddr addr) | |
271 | +{ | |
272 | + switch (addr) { | |
273 | + default: | |
274 | + DPRINTF("MMIO Read W @ %"HWADDR_PRIx"\n", addr); | |
275 | + return 0x0; | |
276 | + } | |
277 | +} | |
278 | + | |
279 | +static uint32_t mmio_readl(NeXTState *s, hwaddr addr) | |
280 | +{ | |
281 | + switch (addr) { | |
282 | + case 0x7000: | |
283 | + /* DPRINTF("Read INT status: %x\n", s->int_status); */ | |
284 | + return s->int_status; | |
285 | + | |
286 | + case 0x7800: | |
287 | + DPRINTF("MMIO Read INT mask: %x\n", s->int_mask); | |
288 | + return s->int_mask; | |
289 | + | |
290 | + case 0xc000: | |
291 | + return s->scr1; | |
292 | + | |
293 | + case 0xd000: | |
294 | + return s->scr2; | |
295 | + | |
296 | + default: | |
297 | + DPRINTF("MMIO Read L @ %"HWADDR_PRIx"\n", addr); | |
298 | + return 0x0; | |
299 | + } | |
300 | +} | |
301 | + | |
302 | +static void mmio_writeb(NeXTState *s, hwaddr addr, uint32_t val) | |
303 | +{ | |
304 | + switch (addr) { | |
305 | + case 0xd003: | |
306 | + nextscr2_write(s, val, 1); | |
307 | + break; | |
308 | + default: | |
309 | + DPRINTF("MMIO Write B @ %x with %x\n", (unsigned int)addr, val); | |
310 | + } | |
311 | + | |
312 | +} | |
313 | + | |
314 | +static void mmio_writew(NeXTState *s, hwaddr addr, uint32_t val) | |
315 | +{ | |
316 | + DPRINTF("MMIO Write W\n"); | |
317 | +} | |
318 | + | |
319 | +static void mmio_writel(NeXTState *s, hwaddr addr, uint32_t val) | |
320 | +{ | |
321 | + switch (addr) { | |
322 | + case 0x7000: | |
323 | + DPRINTF("INT Status old: %x new: %x\n", s->int_status, val); | |
324 | + s->int_status = val; | |
325 | + break; | |
326 | + case 0x7800: | |
327 | + DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, val); | |
328 | + s->int_mask = val; | |
329 | + break; | |
330 | + case 0xc000: | |
331 | + DPRINTF("SCR1 Write: %x\n", val); | |
332 | + break; | |
333 | + case 0xd000: | |
334 | + nextscr2_write(s, val, 4); | |
335 | + break; | |
336 | + | |
337 | + default: | |
338 | + DPRINTF("MMIO Write l @ %x with %x\n", (unsigned int)addr, val); | |
339 | + } | |
340 | +} | |
341 | + | |
342 | +static uint64_t mmio_readfn(void *opaque, hwaddr addr, unsigned size) | |
343 | +{ | |
344 | + NeXTState *ns = NEXT_MACHINE(opaque); | |
345 | + | |
346 | + switch (size) { | |
347 | + case 1: | |
348 | + return mmio_readb(ns, addr); | |
349 | + case 2: | |
350 | + return mmio_readw(ns, addr); | |
351 | + case 4: | |
352 | + return mmio_readl(ns, addr); | |
353 | + default: | |
354 | + g_assert_not_reached(); | |
355 | + } | |
356 | +} | |
357 | + | |
358 | +static void mmio_writefn(void *opaque, hwaddr addr, uint64_t value, | |
359 | + unsigned size) | |
360 | +{ | |
361 | + NeXTState *ns = NEXT_MACHINE(opaque); | |
362 | + | |
363 | + switch (size) { | |
364 | + case 1: | |
365 | + mmio_writeb(ns, addr, value); | |
366 | + break; | |
367 | + case 2: | |
368 | + mmio_writew(ns, addr, value); | |
369 | + break; | |
370 | + case 4: | |
371 | + mmio_writel(ns, addr, value); | |
372 | + break; | |
373 | + default: | |
374 | + g_assert_not_reached(); | |
375 | + } | |
376 | +} | |
377 | + | |
378 | +static const MemoryRegionOps mmio_ops = { | |
379 | + .read = mmio_readfn, | |
380 | + .write = mmio_writefn, | |
381 | + .valid.min_access_size = 1, | |
382 | + .valid.max_access_size = 4, | |
383 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
384 | +}; | |
385 | + | |
386 | +static uint32_t scr_readb(NeXTState *s, hwaddr addr) | |
387 | +{ | |
388 | + switch (addr) { | |
389 | + case 0x14108: | |
390 | + DPRINTF("FD read @ %x\n", (unsigned int)addr); | |
391 | + return 0x40 | 0x04 | 0x2 | 0x1; | |
392 | + case 0x14020: | |
393 | + DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1); | |
394 | + return s->scsi_csr_1; | |
395 | + | |
396 | + case 0x14021: | |
397 | + DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2); | |
398 | + return 0x40; | |
399 | + | |
400 | + /* | |
401 | + * These 4 registers are the hardware timer, not sure which register | |
402 | + * is the latch instead of data, but no problems so far | |
403 | + */ | |
404 | + case 0x1a000: | |
405 | + return 0xff & (clock() >> 24); | |
406 | + case 0x1a001: | |
407 | + return 0xff & (clock() >> 16); | |
408 | + case 0x1a002: | |
409 | + return 0xff & (clock() >> 8); | |
410 | + case 0x1a003: | |
411 | + /* Hack: We need to have this change consistently to make it work */ | |
412 | + return 0xFF & clock(); | |
413 | + | |
414 | + default: | |
415 | + DPRINTF("BMAP Read B @ %x\n", (unsigned int)addr); | |
416 | + return 0; | |
417 | + } | |
418 | +} | |
419 | + | |
420 | +static uint32_t scr_readw(NeXTState *s, hwaddr addr) | |
421 | +{ | |
422 | + DPRINTF("BMAP Read W @ %x\n", (unsigned int)addr); | |
423 | + return 0; | |
424 | +} | |
425 | + | |
426 | +static uint32_t scr_readl(NeXTState *s, hwaddr addr) | |
427 | +{ | |
428 | + DPRINTF("BMAP Read L @ %x\n", (unsigned int)addr); | |
429 | + return 0; | |
430 | +} | |
431 | + | |
432 | +#define SCSICSR_ENABLE 0x01 | |
433 | +#define SCSICSR_RESET 0x02 /* reset scsi dma */ | |
434 | +#define SCSICSR_FIFOFL 0x04 | |
435 | +#define SCSICSR_DMADIR 0x08 /* if set, scsi to mem */ | |
436 | +#define SCSICSR_CPUDMA 0x10 /* if set, dma enabled */ | |
437 | +#define SCSICSR_INTMASK 0x20 /* if set, interrupt enabled */ | |
438 | + | |
439 | +static void scr_writeb(NeXTState *s, hwaddr addr, uint32_t value) | |
440 | +{ | |
441 | + switch (addr) { | |
442 | + case 0x14108: | |
443 | + DPRINTF("FDCSR Write: %x\n", value); | |
444 | + | |
445 | + if (value == 0x0) { | |
446 | + /* qemu_irq_raise(s->fd_irq[0]); */ | |
447 | + } | |
448 | + break; | |
449 | + case 0x14020: /* SCSI Control Register */ | |
450 | + if (value & SCSICSR_FIFOFL) { | |
451 | + DPRINTF("SCSICSR FIFO Flush\n"); | |
452 | + /* will have to add another irq to the esp if this is needed */ | |
453 | + /* esp_puflush_fifo(esp_g); */ | |
454 | + /* qemu_irq_pulse(s->scsi_dma); */ | |
455 | + } | |
456 | + | |
457 | + if (value & SCSICSR_ENABLE) { | |
458 | + DPRINTF("SCSICSR Enable\n"); | |
459 | + /* | |
460 | + * qemu_irq_raise(s->scsi_dma); | |
461 | + * s->scsi_csr_1 = 0xc0; | |
462 | + * s->scsi_csr_1 |= 0x1; | |
463 | + * qemu_irq_pulse(s->scsi_dma); | |
464 | + */ | |
465 | + } | |
466 | + /* | |
467 | + * else | |
468 | + * s->scsi_csr_1 &= ~SCSICSR_ENABLE; | |
469 | + */ | |
470 | + | |
471 | + if (value & SCSICSR_RESET) { | |
472 | + DPRINTF("SCSICSR Reset\n"); | |
473 | + /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ | |
474 | + /* qemu_irq_raise(s->scsi_reset); */ | |
475 | + /* s->scsi_csr_1 &= ~(SCSICSR_INTMASK |0x80|0x1); */ | |
476 | + | |
477 | + } | |
478 | + if (value & SCSICSR_DMADIR) { | |
479 | + DPRINTF("SCSICSR DMAdir\n"); | |
480 | + } | |
481 | + if (value & SCSICSR_CPUDMA) { | |
482 | + DPRINTF("SCSICSR CPUDMA\n"); | |
483 | + /* qemu_irq_raise(s->scsi_dma); */ | |
484 | + | |
485 | + s->int_status |= 0x4000000; | |
486 | + } else { | |
487 | + s->int_status &= ~(0x4000000); | |
488 | + } | |
489 | + if (value & SCSICSR_INTMASK) { | |
490 | + DPRINTF("SCSICSR INTMASK\n"); | |
491 | + /* | |
492 | + * int_mask &= ~0x1000; | |
493 | + * s->scsi_csr_1 |= value; | |
494 | + * s->scsi_csr_1 &= ~SCSICSR_INTMASK; | |
495 | + * if (s->scsi_queued) { | |
496 | + * s->scsi_queued = 0; | |
497 | + * next_irq(s, NEXT_SCSI_I, level); | |
498 | + * } | |
499 | + */ | |
500 | + } else { | |
501 | + /* int_mask |= 0x1000; */ | |
502 | + } | |
503 | + if (value & 0x80) { | |
504 | + /* int_mask |= 0x1000; */ | |
505 | + /* s->scsi_csr_1 |= 0x80; */ | |
506 | + } | |
507 | + DPRINTF("SCSICSR Write: %x\n", value); | |
508 | + /* s->scsi_csr_1 = value; */ | |
509 | + return; | |
510 | + /* Hardware timer latch - not implemented yet */ | |
511 | + case 0x1a000: | |
512 | + default: | |
513 | + DPRINTF("BMAP Write B @ %x with %x\n", (unsigned int)addr, value); | |
514 | + } | |
515 | +} | |
516 | + | |
517 | +static void scr_writew(NeXTState *s, hwaddr addr, uint32_t value) | |
518 | +{ | |
519 | + DPRINTF("BMAP Write W @ %x with %x\n", (unsigned int)addr, value); | |
520 | +} | |
521 | + | |
522 | +static void scr_writel(NeXTState *s, hwaddr addr, uint32_t value) | |
523 | +{ | |
524 | + DPRINTF("BMAP Write L @ %x with %x\n", (unsigned int)addr, value); | |
525 | +} | |
526 | + | |
527 | +static uint64_t scr_readfn(void *opaque, hwaddr addr, unsigned size) | |
528 | +{ | |
529 | + NeXTState *ns = NEXT_MACHINE(opaque); | |
530 | + | |
531 | + switch (size) { | |
532 | + case 1: | |
533 | + return scr_readb(ns, addr); | |
534 | + case 2: | |
535 | + return scr_readw(ns, addr); | |
536 | + case 4: | |
537 | + return scr_readl(ns, addr); | |
538 | + default: | |
539 | + g_assert_not_reached(); | |
540 | + } | |
541 | +} | |
542 | + | |
543 | +static void scr_writefn(void *opaque, hwaddr addr, uint64_t value, | |
544 | + unsigned size) | |
545 | +{ | |
546 | + NeXTState *ns = NEXT_MACHINE(opaque); | |
547 | + | |
548 | + switch (size) { | |
549 | + case 1: | |
550 | + scr_writeb(ns, addr, value); | |
551 | + break; | |
552 | + case 2: | |
553 | + scr_writew(ns, addr, value); | |
554 | + break; | |
555 | + case 4: | |
556 | + scr_writel(ns, addr, value); | |
557 | + break; | |
558 | + default: | |
559 | + g_assert_not_reached(); | |
560 | + } | |
561 | +} | |
562 | + | |
563 | +static const MemoryRegionOps scr_ops = { | |
564 | + .read = scr_readfn, | |
565 | + .write = scr_writefn, | |
566 | + .valid.min_access_size = 1, | |
567 | + .valid.max_access_size = 4, | |
568 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
569 | +}; | |
570 | + | |
571 | +#define NEXTDMA_SCSI(x) (0x10 + x) | |
572 | +#define NEXTDMA_FD(x) (0x10 + x) | |
573 | +#define NEXTDMA_ENTX(x) (0x110 + x) | |
574 | +#define NEXTDMA_ENRX(x) (0x150 + x) | |
575 | +#define NEXTDMA_CSR 0x0 | |
576 | +#define NEXTDMA_NEXT 0x4000 | |
577 | +#define NEXTDMA_LIMIT 0x4004 | |
578 | +#define NEXTDMA_START 0x4008 | |
579 | +#define NEXTDMA_STOP 0x400c | |
580 | +#define NEXTDMA_NEXT_INIT 0x4200 | |
581 | +#define NEXTDMA_SIZE 0x4204 | |
582 | + | |
583 | +static void dma_writel(void *opaque, hwaddr addr, uint64_t value, | |
584 | + unsigned int size) | |
585 | +{ | |
586 | + NeXTState *next_state = NEXT_MACHINE(opaque); | |
587 | + | |
588 | + switch (addr) { | |
589 | + case NEXTDMA_ENRX(NEXTDMA_CSR): | |
590 | + if (value & DMA_DEV2M) { | |
591 | + next_state->dma[NEXTDMA_ENRX].csr |= DMA_DEV2M; | |
592 | + } | |
593 | + | |
594 | + if (value & DMA_SETENABLE) { | |
595 | + /* DPRINTF("SCSI DMA ENABLE\n"); */ | |
596 | + next_state->dma[NEXTDMA_ENRX].csr |= DMA_ENABLE; | |
597 | + } | |
598 | + if (value & DMA_SETSUPDATE) { | |
599 | + next_state->dma[NEXTDMA_ENRX].csr |= DMA_SUPDATE; | |
600 | + } | |
601 | + if (value & DMA_CLRCOMPLETE) { | |
602 | + next_state->dma[NEXTDMA_ENRX].csr &= ~DMA_COMPLETE; | |
603 | + } | |
604 | + | |
605 | + if (value & DMA_RESET) { | |
606 | + next_state->dma[NEXTDMA_ENRX].csr &= ~(DMA_COMPLETE | DMA_SUPDATE | | |
607 | + DMA_ENABLE | DMA_DEV2M); | |
608 | + } | |
609 | + /* DPRINTF("RXCSR \tWrite: %x\n",value); */ | |
610 | + break; | |
611 | + case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT): | |
612 | + next_state->dma[NEXTDMA_ENRX].next_initbuf = value; | |
613 | + break; | |
614 | + case NEXTDMA_ENRX(NEXTDMA_NEXT): | |
615 | + next_state->dma[NEXTDMA_ENRX].next = value; | |
616 | + break; | |
617 | + case NEXTDMA_ENRX(NEXTDMA_LIMIT): | |
618 | + next_state->dma[NEXTDMA_ENRX].limit = value; | |
619 | + break; | |
620 | + case NEXTDMA_SCSI(NEXTDMA_CSR): | |
621 | + if (value & DMA_DEV2M) { | |
622 | + next_state->dma[NEXTDMA_SCSI].csr |= DMA_DEV2M; | |
623 | + } | |
624 | + if (value & DMA_SETENABLE) { | |
625 | + /* DPRINTF("SCSI DMA ENABLE\n"); */ | |
626 | + next_state->dma[NEXTDMA_SCSI].csr |= DMA_ENABLE; | |
627 | + } | |
628 | + if (value & DMA_SETSUPDATE) { | |
629 | + next_state->dma[NEXTDMA_SCSI].csr |= DMA_SUPDATE; | |
630 | + } | |
631 | + if (value & DMA_CLRCOMPLETE) { | |
632 | + next_state->dma[NEXTDMA_SCSI].csr &= ~DMA_COMPLETE; | |
633 | + } | |
634 | + | |
635 | + if (value & DMA_RESET) { | |
636 | + next_state->dma[NEXTDMA_SCSI].csr &= ~(DMA_COMPLETE | DMA_SUPDATE | | |
637 | + DMA_ENABLE | DMA_DEV2M); | |
638 | + /* DPRINTF("SCSI DMA RESET\n"); */ | |
639 | + } | |
640 | + /* DPRINTF("RXCSR \tWrite: %x\n",value); */ | |
641 | + break; | |
642 | + | |
643 | + case NEXTDMA_SCSI(NEXTDMA_NEXT): | |
644 | + next_state->dma[NEXTDMA_SCSI].next = value; | |
645 | + break; | |
646 | + | |
647 | + case NEXTDMA_SCSI(NEXTDMA_LIMIT): | |
648 | + next_state->dma[NEXTDMA_SCSI].limit = value; | |
649 | + break; | |
650 | + | |
651 | + case NEXTDMA_SCSI(NEXTDMA_START): | |
652 | + next_state->dma[NEXTDMA_SCSI].start = value; | |
653 | + break; | |
654 | + | |
655 | + case NEXTDMA_SCSI(NEXTDMA_STOP): | |
656 | + next_state->dma[NEXTDMA_SCSI].stop = value; | |
657 | + break; | |
658 | + | |
659 | + case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT): | |
660 | + next_state->dma[NEXTDMA_SCSI].next_initbuf = value; | |
661 | + break; | |
662 | + | |
663 | + default: | |
664 | + DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)value); | |
665 | + } | |
666 | +} | |
667 | + | |
668 | +static uint64_t dma_readl(void *opaque, hwaddr addr, unsigned int size) | |
669 | +{ | |
670 | + NeXTState *next_state = NEXT_MACHINE(opaque); | |
671 | + | |
672 | + switch (addr) { | |
673 | + case NEXTDMA_SCSI(NEXTDMA_CSR): | |
674 | + DPRINTF("SCSI DMA CSR READ\n"); | |
675 | + return next_state->dma[NEXTDMA_SCSI].csr; | |
676 | + case NEXTDMA_ENRX(NEXTDMA_CSR): | |
677 | + return next_state->dma[NEXTDMA_ENRX].csr; | |
678 | + case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT): | |
679 | + return next_state->dma[NEXTDMA_ENRX].next_initbuf; | |
680 | + case NEXTDMA_ENRX(NEXTDMA_NEXT): | |
681 | + return next_state->dma[NEXTDMA_ENRX].next; | |
682 | + case NEXTDMA_ENRX(NEXTDMA_LIMIT): | |
683 | + return next_state->dma[NEXTDMA_ENRX].limit; | |
684 | + | |
685 | + case NEXTDMA_SCSI(NEXTDMA_NEXT): | |
686 | + return next_state->dma[NEXTDMA_SCSI].next; | |
687 | + case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT): | |
688 | + return next_state->dma[NEXTDMA_SCSI].next_initbuf; | |
689 | + case NEXTDMA_SCSI(NEXTDMA_LIMIT): | |
690 | + return next_state->dma[NEXTDMA_SCSI].limit; | |
691 | + case NEXTDMA_SCSI(NEXTDMA_START): | |
692 | + return next_state->dma[NEXTDMA_SCSI].start; | |
693 | + case NEXTDMA_SCSI(NEXTDMA_STOP): | |
694 | + return next_state->dma[NEXTDMA_SCSI].stop; | |
695 | + | |
696 | + default: | |
697 | + DPRINTF("DMA read @ %x\n", (unsigned int)addr); | |
698 | + return 0; | |
699 | + } | |
700 | + | |
701 | + /* | |
702 | + * once the csr's are done, subtract 0x3FEC from the addr, and that will | |
703 | + * normalize the upper registers | |
704 | + */ | |
705 | +} | |
706 | + | |
707 | +static const MemoryRegionOps dma_ops = { | |
708 | + .read = dma_readl, | |
709 | + .write = dma_writel, | |
710 | + .impl.min_access_size = 4, | |
711 | + .valid.min_access_size = 4, | |
712 | + .valid.max_access_size = 4, | |
713 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
714 | +}; | |
715 | + | |
716 | +/* | |
717 | + * TODO: set the shift numbers as values in the enum, so the first switch | |
718 | + * will not be needed | |
719 | + */ | |
720 | +void next_irq(void *opaque, int number, int level) | |
721 | +{ | |
722 | + M68kCPU *cpu = opaque; | |
723 | + int shift = 0; | |
724 | + NeXTState *ns = NEXT_MACHINE(qdev_get_machine()); | |
725 | + | |
726 | + /* first switch sets interupt status */ | |
727 | + /* DPRINTF("IRQ %i\n",number); */ | |
728 | + switch (number) { | |
729 | + /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */ | |
730 | + case NEXT_FD_I: | |
731 | + shift = 7;; | |
732 | + break; | |
733 | + case NEXT_KBD_I: | |
734 | + shift = 3; | |
735 | + break; | |
736 | + case NEXT_PWR_I: | |
737 | + shift = 2; | |
738 | + break; | |
739 | + case NEXT_ENRX_I: | |
740 | + shift = 9; | |
741 | + break; | |
742 | + case NEXT_ENTX_I: | |
743 | + shift = 10; | |
744 | + break; | |
745 | + case NEXT_SCSI_I: | |
746 | + shift = 12; | |
747 | + break; | |
748 | + case NEXT_CLK_I: | |
749 | + shift = 5; | |
750 | + break; | |
751 | + | |
752 | + /* level 5 - scc (serial) */ | |
753 | + case NEXT_SCC_I: | |
754 | + shift = 17; | |
755 | + break; | |
756 | + | |
757 | + /* level 6 - audio etherrx/tx dma */ | |
758 | + case NEXT_ENTX_DMA_I: | |
759 | + shift = 28; | |
760 | + break; | |
761 | + case NEXT_ENRX_DMA_I: | |
762 | + shift = 27; | |
763 | + break; | |
764 | + case NEXT_SCSI_DMA_I: | |
765 | + shift = 26; | |
766 | + break; | |
767 | + case NEXT_SND_I: | |
768 | + shift = 23; | |
769 | + break; | |
770 | + case NEXT_SCC_DMA_I: | |
771 | + shift = 21; | |
772 | + break; | |
773 | + | |
774 | + } | |
775 | + /* | |
776 | + * this HAS to be wrong, the interrupt handlers in mach and together | |
777 | + * int_status and int_mask and return if there is a hit | |
778 | + */ | |
779 | + if (ns->int_mask & (1 << shift)) { | |
780 | + DPRINTF("%x interrupt masked @ %x\n", 1 << shift, cpu->env.pc); | |
781 | + /* return; */ | |
782 | + } | |
783 | + | |
784 | + /* second switch triggers the correct interrupt */ | |
785 | + if (level) { | |
786 | + ns->int_status |= 1 << shift; | |
787 | + | |
788 | + switch (number) { | |
789 | + /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */ | |
790 | + case NEXT_FD_I: | |
791 | + case NEXT_KBD_I: | |
792 | + case NEXT_PWR_I: | |
793 | + case NEXT_ENRX_I: | |
794 | + case NEXT_ENTX_I: | |
795 | + case NEXT_SCSI_I: | |
796 | + case NEXT_CLK_I: | |
797 | + m68k_set_irq_level(cpu, 3, 27); | |
798 | + break; | |
799 | + | |
800 | + /* level 5 - scc (serial) */ | |
801 | + case NEXT_SCC_I: | |
802 | + m68k_set_irq_level(cpu, 5, 29); | |
803 | + break; | |
804 | + | |
805 | + /* level 6 - audio etherrx/tx dma */ | |
806 | + case NEXT_ENTX_DMA_I: | |
807 | + case NEXT_ENRX_DMA_I: | |
808 | + case NEXT_SCSI_DMA_I: | |
809 | + case NEXT_SND_I: | |
810 | + case NEXT_SCC_DMA_I: | |
811 | + m68k_set_irq_level(cpu, 6, 30); | |
812 | + break; | |
813 | + } | |
814 | + } else { | |
815 | + ns->int_status &= ~(1 << shift); | |
816 | + cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
817 | + } | |
818 | +} | |
819 | + | |
820 | +static void next_serial_irq(void *opaque, int n, int level) | |
821 | +{ | |
822 | + /* DPRINTF("SCC IRQ NUM %i\n",n); */ | |
823 | + if (n) { | |
824 | + next_irq(opaque, NEXT_SCC_DMA_I, level); | |
825 | + } else { | |
826 | + next_irq(opaque, NEXT_SCC_I, level); | |
827 | + } | |
828 | +} | |
829 | + | |
830 | +static void next_escc_init(M68kCPU *cpu) | |
831 | +{ | |
832 | + qemu_irq *ser_irq = qemu_allocate_irqs(next_serial_irq, cpu, 2); | |
833 | + DeviceState *dev; | |
834 | + SysBusDevice *s; | |
835 | + | |
836 | + dev = qdev_create(NULL, TYPE_ESCC); | |
837 | + qdev_prop_set_uint32(dev, "disabled", 0); | |
838 | + qdev_prop_set_uint32(dev, "frequency", 9600 * 384); | |
839 | + qdev_prop_set_uint32(dev, "it_shift", 0); | |
840 | + qdev_prop_set_bit(dev, "bit_swap", true); | |
841 | + qdev_prop_set_chr(dev, "chrB", serial_hd(1)); | |
842 | + qdev_prop_set_chr(dev, "chrA", serial_hd(0)); | |
843 | + qdev_prop_set_uint32(dev, "chnBtype", escc_serial); | |
844 | + qdev_prop_set_uint32(dev, "chnAtype", escc_serial); | |
845 | + qdev_init_nofail(dev); | |
846 | + | |
847 | + s = SYS_BUS_DEVICE(dev); | |
848 | + sysbus_connect_irq(s, 0, ser_irq[0]); | |
849 | + sysbus_connect_irq(s, 1, ser_irq[1]); | |
850 | + sysbus_mmio_map(s, 0, 0x2118000); | |
851 | +} | |
852 | + | |
853 | +static void next_cube_init(MachineState *machine) | |
854 | +{ | |
855 | + M68kCPU *cpu; | |
856 | + CPUM68KState *env; | |
857 | + MemoryRegion *ram = g_new(MemoryRegion, 1); | |
858 | + MemoryRegion *rom = g_new(MemoryRegion, 1); | |
859 | + MemoryRegion *mmiomem = g_new(MemoryRegion, 1); | |
860 | + MemoryRegion *scrmem = g_new(MemoryRegion, 1); | |
861 | + MemoryRegion *dmamem = g_new(MemoryRegion, 1); | |
862 | + MemoryRegion *bmapm1 = g_new(MemoryRegion, 1); | |
863 | + MemoryRegion *bmapm2 = g_new(MemoryRegion, 1); | |
864 | + MemoryRegion *sysmem = get_system_memory(); | |
865 | + NeXTState *ns = NEXT_MACHINE(machine); | |
866 | + DeviceState *dev; | |
867 | + | |
868 | + /* Initialize the cpu core */ | |
869 | + cpu = M68K_CPU(cpu_create(machine->cpu_type)); | |
870 | + if (!cpu) { | |
871 | + error_report("Unable to find m68k CPU definition"); | |
872 | + exit(1); | |
873 | + } | |
874 | + env = &cpu->env; | |
875 | + | |
876 | + /* Initialize CPU registers. */ | |
877 | + env->vbr = 0; | |
878 | + env->sr = 0x2700; | |
879 | + | |
880 | + /* Set internal registers to initial values */ | |
881 | + /* 0x0000XX00 << vital bits */ | |
882 | + ns->scr1 = 0x00011102; | |
883 | + ns->scr2 = 0x00ff0c80; | |
884 | + | |
885 | + /* Load RTC RAM - TODO: provide possibility to load contents from file */ | |
886 | + memcpy(ns->rtc_ram, rtc_ram2, 32); | |
887 | + | |
888 | + /* 64MB RAM starting at 0x04000000 */ | |
889 | + memory_region_allocate_system_memory(ram, NULL, "next.ram", ram_size); | |
890 | + memory_region_add_subregion(sysmem, 0x04000000, ram); | |
891 | + | |
892 | + /* Framebuffer */ | |
893 | + dev = qdev_create(NULL, TYPE_NEXTFB); | |
894 | + qdev_init_nofail(dev); | |
895 | + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000); | |
896 | + | |
897 | + /* MMIO */ | |
898 | + memory_region_init_io(mmiomem, NULL, &mmio_ops, machine, "next.mmio", | |
899 | + 0xD0000); | |
900 | + memory_region_add_subregion(sysmem, 0x02000000, mmiomem); | |
901 | + | |
902 | + /* BMAP memory */ | |
903 | + memory_region_init_ram_shared_nomigrate(bmapm1, NULL, "next.bmapmem", 64, | |
904 | + true, &error_fatal); | |
905 | + memory_region_add_subregion(sysmem, 0x020c0000, bmapm1); | |
906 | + /* The Rev_2.5_v66.bin firmware accesses it at 0x820c0020, too */ | |
907 | + memory_region_init_alias(bmapm2, NULL, "next.bmapmem2", bmapm1, 0x0, 64); | |
908 | + memory_region_add_subregion(sysmem, 0x820c0000, bmapm2); | |
909 | + | |
910 | + /* BMAP IO - acts as a catch-all for now */ | |
911 | + memory_region_init_io(scrmem, NULL, &scr_ops, machine, "next.scr", | |
912 | + 0x20000); | |
913 | + memory_region_add_subregion(sysmem, 0x02100000, scrmem); | |
914 | + | |
915 | + /* KBD */ | |
916 | + dev = qdev_create(NULL, TYPE_NEXTKBD); | |
917 | + qdev_init_nofail(dev); | |
918 | + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000); | |
919 | + | |
920 | + /* Load ROM here */ | |
921 | + if (bios_name == NULL) { | |
922 | + bios_name = ROM_FILE; | |
923 | + } | |
924 | + /* still not sure if the rom should also be mapped at 0x0*/ | |
925 | + memory_region_init_rom(rom, NULL, "next.rom", 0x20000, &error_fatal); | |
926 | + memory_region_add_subregion(sysmem, 0x01000000, rom); | |
927 | + if (load_image_targphys(bios_name, 0x01000000, 0x20000) < 8) { | |
928 | + if (!qtest_enabled()) { | |
929 | + error_report("Failed to load firmware '%s'.", bios_name); | |
930 | + } | |
931 | + } else { | |
932 | + uint8_t *ptr; | |
933 | + /* Initial PC is always at offset 4 in firmware binaries */ | |
934 | + ptr = rom_ptr(0x01000004, 4); | |
935 | + g_assert(ptr != NULL); | |
936 | + env->pc = ldl_p(ptr); | |
937 | + if (env->pc >= 0x01020000) { | |
938 | + error_report("'%s' does not seem to be a valid firmware image.", | |
939 | + bios_name); | |
940 | + exit(1); | |
941 | + } | |
942 | + } | |
943 | + | |
944 | + /* Serial */ | |
945 | + next_escc_init(cpu); | |
946 | + | |
947 | + /* TODO: */ | |
948 | + /* Network */ | |
949 | + /* SCSI */ | |
950 | + | |
951 | + /* DMA */ | |
952 | + memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma", 0x5000); | |
953 | + memory_region_add_subregion(sysmem, 0x02000000, dmamem); | |
954 | +} | |
955 | + | |
956 | +static void next_machine_class_init(ObjectClass *oc, void *data) | |
957 | +{ | |
958 | + MachineClass *mc = MACHINE_CLASS(oc); | |
959 | + | |
960 | + mc->desc = "NeXT Cube"; | |
961 | + mc->init = next_cube_init; | |
962 | + mc->default_ram_size = RAM_SIZE; | |
963 | + mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); | |
964 | +} | |
965 | + | |
966 | +static const TypeInfo next_typeinfo = { | |
967 | + .name = TYPE_NEXT_MACHINE, | |
968 | + .parent = TYPE_MACHINE, | |
969 | + .class_init = next_machine_class_init, | |
970 | + .instance_size = sizeof(NeXTState), | |
971 | +}; | |
972 | + | |
973 | +static void next_register_type(void) | |
974 | +{ | |
975 | + type_register_static(&next_typeinfo); | |
976 | +} | |
977 | + | |
978 | +type_init(next_register_type) |
@@ -0,0 +1,291 @@ | ||
1 | +/* | |
2 | + * QEMU NeXT Keyboard/Mouse emulation | |
3 | + * | |
4 | + * Copyright (c) 2011 Bryce Lanham | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | + | |
25 | +/* | |
26 | + * This is admittedly hackish, but works well enough for basic input. Mouse | |
27 | + * support will be added once we can boot something that needs the mouse. | |
28 | + */ | |
29 | + | |
30 | +#include "qemu/osdep.h" | |
31 | +#include "qemu/log.h" | |
32 | +#include "exec/address-spaces.h" | |
33 | +#include "hw/hw.h" | |
34 | +#include "hw/sysbus.h" | |
35 | +#include "hw/m68k/next-cube.h" | |
36 | +#include "ui/console.h" | |
37 | +#include "sysemu/sysemu.h" | |
38 | +#include "migration/vmstate.h" | |
39 | + | |
40 | +#define NEXTKBD(obj) OBJECT_CHECK(NextKBDState, (obj), TYPE_NEXTKBD) | |
41 | + | |
42 | +/* following defintions from next68k netbsd */ | |
43 | +#define CSR_INT 0x00800000 | |
44 | +#define CSR_DATA 0x00400000 | |
45 | + | |
46 | +#define KD_KEYMASK 0x007f | |
47 | +#define KD_DIRECTION 0x0080 /* pressed or released */ | |
48 | +#define KD_CNTL 0x0100 | |
49 | +#define KD_LSHIFT 0x0200 | |
50 | +#define KD_RSHIFT 0x0400 | |
51 | +#define KD_LCOMM 0x0800 | |
52 | +#define KD_RCOMM 0x1000 | |
53 | +#define KD_LALT 0x2000 | |
54 | +#define KD_RALT 0x4000 | |
55 | +#define KD_VALID 0x8000 /* only set for scancode keys ? */ | |
56 | +#define KD_MODS 0x4f00 | |
57 | + | |
58 | +#define KBD_QUEUE_SIZE 256 | |
59 | + | |
60 | +typedef struct { | |
61 | + uint8_t data[KBD_QUEUE_SIZE]; | |
62 | + int rptr, wptr, count; | |
63 | +} KBDQueue; | |
64 | + | |
65 | + | |
66 | +typedef struct NextKBDState { | |
67 | + SysBusDevice sbd; | |
68 | + MemoryRegion mr; | |
69 | + KBDQueue queue; | |
70 | + uint16_t shift; | |
71 | +} NextKBDState; | |
72 | + | |
73 | +static void queue_code(void *opaque, int code); | |
74 | + | |
75 | +/* lots of magic numbers here */ | |
76 | +static uint32_t kbd_read_byte(void *opaque, hwaddr addr) | |
77 | +{ | |
78 | + switch (addr & 0x3) { | |
79 | + case 0x0: /* 0xe000 */ | |
80 | + return 0x80 | 0x20; | |
81 | + | |
82 | + case 0x1: /* 0xe001 */ | |
83 | + return 0x80 | 0x40 | 0x20 | 0x10; | |
84 | + | |
85 | + case 0x2: /* 0xe002 */ | |
86 | + /* returning 0x40 caused mach to hang */ | |
87 | + return 0x10 | 0x2 | 0x1; | |
88 | + | |
89 | + default: | |
90 | + qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr); | |
91 | + } | |
92 | + | |
93 | + return 0; | |
94 | +} | |
95 | + | |
96 | +static uint32_t kbd_read_word(void *opaque, hwaddr addr) | |
97 | +{ | |
98 | + qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr); | |
99 | + return 0; | |
100 | +} | |
101 | + | |
102 | +/* even more magic numbers */ | |
103 | +static uint32_t kbd_read_long(void *opaque, hwaddr addr) | |
104 | +{ | |
105 | + int key = 0; | |
106 | + NextKBDState *s = NEXTKBD(opaque); | |
107 | + KBDQueue *q = &s->queue; | |
108 | + | |
109 | + switch (addr & 0xf) { | |
110 | + case 0x0: /* 0xe000 */ | |
111 | + return 0xA0F09300; | |
112 | + | |
113 | + case 0x8: /* 0xe008 */ | |
114 | + /* get keycode from buffer */ | |
115 | + if (q->count > 0) { | |
116 | + key = q->data[q->rptr]; | |
117 | + if (++q->rptr == KBD_QUEUE_SIZE) { | |
118 | + q->rptr = 0; | |
119 | + } | |
120 | + | |
121 | + q->count--; | |
122 | + | |
123 | + if (s->shift) { | |
124 | + key |= s->shift; | |
125 | + } | |
126 | + | |
127 | + if (key & 0x80) { | |
128 | + return 0; | |
129 | + } else { | |
130 | + return 0x10000000 | KD_VALID | key; | |
131 | + } | |
132 | + } else { | |
133 | + return 0; | |
134 | + } | |
135 | + | |
136 | + default: | |
137 | + qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr); | |
138 | + return 0; | |
139 | + } | |
140 | +} | |
141 | + | |
142 | +static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size) | |
143 | +{ | |
144 | + switch (size) { | |
145 | + case 1: | |
146 | + return kbd_read_byte(opaque, addr); | |
147 | + case 2: | |
148 | + return kbd_read_word(opaque, addr); | |
149 | + case 4: | |
150 | + return kbd_read_long(opaque, addr); | |
151 | + default: | |
152 | + g_assert_not_reached(); | |
153 | + } | |
154 | +} | |
155 | + | |
156 | +static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value, | |
157 | + unsigned size) | |
158 | +{ | |
159 | + qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx | |
160 | + "val=0x%"PRIx64"\n", size, addr, value); | |
161 | +} | |
162 | + | |
163 | +static const MemoryRegionOps kbd_ops = { | |
164 | + .read = kbd_readfn, | |
165 | + .write = kbd_writefn, | |
166 | + .valid.min_access_size = 1, | |
167 | + .valid.max_access_size = 4, | |
168 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
169 | +}; | |
170 | + | |
171 | +static void nextkbd_event(void *opaque, int ch) | |
172 | +{ | |
173 | + /* | |
174 | + * Will want to set vars for caps/num lock | |
175 | + * if (ch & 0x80) -> key release | |
176 | + * there's also e0 escaped scancodes that might need to be handled | |
177 | + */ | |
178 | + queue_code(opaque, ch); | |
179 | +} | |
180 | + | |
181 | +static const unsigned char next_keycodes[128] = { | |
182 | + 0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F, | |
183 | + 0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00, | |
184 | + 0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06, | |
185 | + 0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A, | |
186 | + 0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C, | |
187 | + 0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34, | |
188 | + 0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00, | |
189 | + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
190 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
191 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
192 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
193 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | |
194 | +}; | |
195 | + | |
196 | +static void queue_code(void *opaque, int code) | |
197 | +{ | |
198 | + NextKBDState *s = NEXTKBD(opaque); | |
199 | + KBDQueue *q = &s->queue; | |
200 | + int key = code & KD_KEYMASK; | |
201 | + int release = code & 0x80; | |
202 | + static int ext; | |
203 | + | |
204 | + if (code == 0xE0) { | |
205 | + ext = 1; | |
206 | + } | |
207 | + | |
208 | + if (code == 0x2A || code == 0x1D || code == 0x36) { | |
209 | + if (code == 0x2A) { | |
210 | + s->shift = KD_LSHIFT; | |
211 | + } else if (code == 0x36) { | |
212 | + s->shift = KD_RSHIFT; | |
213 | + ext = 0; | |
214 | + } else if (code == 0x1D && !ext) { | |
215 | + s->shift = KD_LCOMM; | |
216 | + } else if (code == 0x1D && ext) { | |
217 | + ext = 0; | |
218 | + s->shift = KD_RCOMM; | |
219 | + } | |
220 | + return; | |
221 | + } else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) || | |
222 | + code == (0x36 | 0x80)) { | |
223 | + s->shift = 0; | |
224 | + return; | |
225 | + } | |
226 | + | |
227 | + if (q->count >= KBD_QUEUE_SIZE) { | |
228 | + return; | |
229 | + } | |
230 | + | |
231 | + q->data[q->wptr] = next_keycodes[key] | release; | |
232 | + | |
233 | + if (++q->wptr == KBD_QUEUE_SIZE) { | |
234 | + q->wptr = 0; | |
235 | + } | |
236 | + | |
237 | + q->count++; | |
238 | + | |
239 | + /* | |
240 | + * might need to actually trigger the NeXT irq, but as the keyboard works | |
241 | + * at the moment, I'll worry about it later | |
242 | + */ | |
243 | + /* s->update_irq(s->update_arg, 1); */ | |
244 | +} | |
245 | + | |
246 | +static void nextkbd_reset(DeviceState *dev) | |
247 | +{ | |
248 | + NextKBDState *nks = NEXTKBD(dev); | |
249 | + | |
250 | + memset(&nks->queue, 0, sizeof(KBDQueue)); | |
251 | + nks->shift = 0; | |
252 | +} | |
253 | + | |
254 | +static void nextkbd_realize(DeviceState *dev, Error **errp) | |
255 | +{ | |
256 | + NextKBDState *s = NEXTKBD(dev); | |
257 | + | |
258 | + memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000); | |
259 | + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); | |
260 | + | |
261 | + qemu_add_kbd_event_handler(nextkbd_event, s); | |
262 | +} | |
263 | + | |
264 | +static const VMStateDescription nextkbd_vmstate = { | |
265 | + .name = TYPE_NEXTKBD, | |
266 | + .unmigratable = 1, /* TODO: Implement this when m68k CPU is migratable */ | |
267 | +}; | |
268 | + | |
269 | +static void nextkbd_class_init(ObjectClass *oc, void *data) | |
270 | +{ | |
271 | + DeviceClass *dc = DEVICE_CLASS(oc); | |
272 | + | |
273 | + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); | |
274 | + dc->vmsd = &nextkbd_vmstate; | |
275 | + dc->realize = nextkbd_realize; | |
276 | + dc->reset = nextkbd_reset; | |
277 | +} | |
278 | + | |
279 | +static const TypeInfo nextkbd_info = { | |
280 | + .name = TYPE_NEXTKBD, | |
281 | + .parent = TYPE_SYS_BUS_DEVICE, | |
282 | + .instance_size = sizeof(NextKBDState), | |
283 | + .class_init = nextkbd_class_init, | |
284 | +}; | |
285 | + | |
286 | +static void nextkbd_register_types(void) | |
287 | +{ | |
288 | + type_register_static(&nextkbd_info); | |
289 | +} | |
290 | + | |
291 | +type_init(nextkbd_register_types) |
@@ -51,6 +51,7 @@ typedef struct ESCCState { | ||
51 | 51 | |
52 | 52 | struct ESCCChannelState chn[2]; |
53 | 53 | uint32_t it_shift; |
54 | + bool bit_swap; | |
54 | 55 | MemoryRegion mmio; |
55 | 56 | uint32_t disabled; |
56 | 57 | uint32_t frequency; |
@@ -0,0 +1,47 @@ | ||
1 | + | |
2 | +#ifndef NEXT_CUBE_H | |
3 | +#define NEXT_CUBE_H | |
4 | + | |
5 | +#define TYPE_NEXTFB "next-fb" | |
6 | + | |
7 | +#define TYPE_NEXTKBD "next-kbd" | |
8 | + | |
9 | +enum next_dma_chan { | |
10 | + NEXTDMA_FD, | |
11 | + NEXTDMA_ENRX, | |
12 | + NEXTDMA_ENTX, | |
13 | + NEXTDMA_SCSI, | |
14 | + NEXTDMA_SCC, | |
15 | + NEXTDMA_SND | |
16 | +}; | |
17 | + | |
18 | +#define DMA_ENABLE 0x01000000 | |
19 | +#define DMA_SUPDATE 0x02000000 | |
20 | +#define DMA_COMPLETE 0x08000000 | |
21 | + | |
22 | +#define DMA_M2DEV 0x0 | |
23 | +#define DMA_SETENABLE 0x00010000 | |
24 | +#define DMA_SETSUPDATE 0x00020000 | |
25 | +#define DMA_DEV2M 0x00040000 | |
26 | +#define DMA_CLRCOMPLETE 0x00080000 | |
27 | +#define DMA_RESET 0x00100000 | |
28 | + | |
29 | +enum next_irqs { | |
30 | + NEXT_FD_I, | |
31 | + NEXT_KBD_I, | |
32 | + NEXT_PWR_I, | |
33 | + NEXT_ENRX_I, | |
34 | + NEXT_ENTX_I, | |
35 | + NEXT_SCSI_I, | |
36 | + NEXT_CLK_I, | |
37 | + NEXT_SCC_I, | |
38 | + NEXT_ENTX_DMA_I, | |
39 | + NEXT_ENRX_DMA_I, | |
40 | + NEXT_SCSI_DMA_I, | |
41 | + NEXT_SCC_DMA_I, | |
42 | + NEXT_SND_I | |
43 | +}; | |
44 | + | |
45 | +void next_irq(void *opaque, int number, int level); | |
46 | + | |
47 | +#endif /* NEXT_CUBE_H */ |
@@ -0,0 +1,121 @@ | ||
1 | +# Functional test that boots a VM and run OCR on the framebuffer | |
2 | +# | |
3 | +# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> | |
4 | +# | |
5 | +# This work is licensed under the terms of the GNU GPL, version 2 or | |
6 | +# later. See the COPYING file in the top-level directory. | |
7 | + | |
8 | +import os | |
9 | +import re | |
10 | +import time | |
11 | +import logging | |
12 | +import distutils.spawn | |
13 | + | |
14 | +from avocado_qemu import Test | |
15 | +from avocado import skipUnless | |
16 | +from avocado.utils import process | |
17 | +from avocado.utils.path import find_command, CmdNotFoundError | |
18 | + | |
19 | +PIL_AVAILABLE = True | |
20 | +try: | |
21 | + from PIL import Image | |
22 | +except ImportError: | |
23 | + PIL_AVAILABLE = False | |
24 | + | |
25 | + | |
26 | +def tesseract_available(expected_version): | |
27 | + try: | |
28 | + find_command('tesseract') | |
29 | + except CmdNotFoundError: | |
30 | + return False | |
31 | + res = process.run('tesseract --version') | |
32 | + try: | |
33 | + version = res.stdout_text.split()[1] | |
34 | + except IndexError: | |
35 | + version = res.stderr_text.split()[1] | |
36 | + return int(version.split('.')[0]) == expected_version | |
37 | + | |
38 | + match = re.match(r'tesseract\s(\d)', res) | |
39 | + if match is None: | |
40 | + return False | |
41 | + # now this is guaranteed to be a digit | |
42 | + return int(match.groups()[0]) == expected_version | |
43 | + | |
44 | + | |
45 | +class NextCubeMachine(Test): | |
46 | + | |
47 | + timeout = 15 | |
48 | + | |
49 | + def check_bootrom_framebuffer(self, screenshot_path): | |
50 | + rom_url = ('http://www.nextcomputers.org/NeXTfiles/Software/ROM_Files/' | |
51 | + '68040_Non-Turbo_Chipset/Rev_2.5_v66.BIN') | |
52 | + rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24' | |
53 | + rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) | |
54 | + | |
55 | + self.vm.set_machine('next-cube') | |
56 | + self.vm.add_args('-bios', rom_path) | |
57 | + self.vm.launch() | |
58 | + | |
59 | + self.log.info('VM launched, waiting for display') | |
60 | + # TODO: Use avocado.utils.wait.wait_for to catch the | |
61 | + # 'displaysurface_create 1120x832' trace-event. | |
62 | + time.sleep(2) | |
63 | + | |
64 | + self.vm.command('human-monitor-command', | |
65 | + command_line='screendump %s' % screenshot_path) | |
66 | + | |
67 | + @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') | |
68 | + def test_bootrom_framebuffer_size(self): | |
69 | + """ | |
70 | + :avocado: tags=arch:m68k | |
71 | + :avocado: tags=machine:next_cube | |
72 | + :avocado: tags=device:framebuffer | |
73 | + """ | |
74 | + screenshot_path = os.path.join(self.workdir, "dump.png") | |
75 | + self.check_bootrom_framebuffer(screenshot_path) | |
76 | + | |
77 | + width, height = Image.open(screenshot_path).size | |
78 | + self.assertEqual(width, 1120) | |
79 | + self.assertEqual(height, 832) | |
80 | + | |
81 | + @skipUnless(tesseract_available(3), 'tesseract v3 OCR tool not available') | |
82 | + def test_bootrom_framebuffer_ocr_with_tesseract_v3(self): | |
83 | + """ | |
84 | + :avocado: tags=arch:m68k | |
85 | + :avocado: tags=machine:next_cube | |
86 | + :avocado: tags=device:framebuffer | |
87 | + """ | |
88 | + screenshot_path = os.path.join(self.workdir, "dump.png") | |
89 | + self.check_bootrom_framebuffer(screenshot_path) | |
90 | + | |
91 | + console_logger = logging.getLogger('console') | |
92 | + text = process.run("tesseract %s stdout" % screenshot_path).stdout_text | |
93 | + for line in text.split('\n'): | |
94 | + if len(line): | |
95 | + console_logger.debug(line) | |
96 | + self.assertIn('Backplane', text) | |
97 | + self.assertIn('Ethernet address', text) | |
98 | + | |
99 | + # Tesseract 4 adds a new OCR engine based on LSTM neural networks. The | |
100 | + # new version is faster and more accurate than version 3. The drawback is | |
101 | + # that it is still alpha-level software. | |
102 | + @skipUnless(tesseract_available(4), 'tesseract v4 OCR tool not available') | |
103 | + def test_bootrom_framebuffer_ocr_with_tesseract_v4(self): | |
104 | + """ | |
105 | + :avocado: tags=arch:m68k | |
106 | + :avocado: tags=machine:next_cube | |
107 | + :avocado: tags=device:framebuffer | |
108 | + """ | |
109 | + screenshot_path = os.path.join(self.workdir, "dump.png") | |
110 | + self.check_bootrom_framebuffer(screenshot_path) | |
111 | + | |
112 | + console_logger = logging.getLogger('console') | |
113 | + proc = process.run("tesseract --oem 1 %s stdout" % screenshot_path) | |
114 | + text = proc.stdout_text | |
115 | + for line in text.split('\n'): | |
116 | + if len(line): | |
117 | + console_logger.debug(line) | |
118 | + self.assertIn('Testing the FPU, SCC', text) | |
119 | + self.assertIn('System test failed. Error code 51', text) | |
120 | + self.assertIn('Boot command', text) | |
121 | + self.assertIn('Next>', text) |
@@ -24,6 +24,17 @@ static const uint8_t kernel_mcf5208[] = { | ||
24 | 24 | 0x60, 0xfa /* bra.s loop */ |
25 | 25 | }; |
26 | 26 | |
27 | +static const uint8_t bios_nextcube[] = { | |
28 | + 0x06, 0x00, 0x00, 0x00, /* Initial SP */ | |
29 | + 0x01, 0x00, 0x00, 0x08, /* Initial PC */ | |
30 | + 0x41, 0xf9, 0x02, 0x11, 0x80, 0x00, /* lea 0x02118000,%a0 */ | |
31 | + 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */ | |
32 | + 0x11, 0x7c, 0x00, 0x05, 0x00, 0x01, /* move.b #5,1(%a0) Sel TXCTRL */ | |
33 | + 0x11, 0x7c, 0x00, 0x68, 0x00, 0x01, /* move.b #0x68,1(%a0) Enable TX */ | |
34 | + 0x11, 0x40, 0x00, 0x03, /* move.b %d0,3(%a0) Print 'T' */ | |
35 | + 0x60, 0xfa /* bra.s loop */ | |
36 | +}; | |
37 | + | |
27 | 38 | static const uint8_t kernel_pls3adsp1800[] = { |
28 | 39 | 0xb0, 0x00, 0x84, 0x00, /* imm 0x8400 */ |
29 | 40 | 0x30, 0x60, 0x00, 0x04, /* addik r3,r0,4 */ |
@@ -117,6 +128,7 @@ static testdef_t tests[] = { | ||
117 | 128 | { "sparc64", "sun4u", "", "UltraSPARC" }, |
118 | 129 | { "s390x", "s390-ccw-virtio", "", "device" }, |
119 | 130 | { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, |
131 | + { "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube }, | |
120 | 132 | { "microblaze", "petalogix-s3adsp1800", "", "TT", |
121 | 133 | sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 }, |
122 | 134 | { "microblazeel", "petalogix-ml605", "", "TT", |