1 /*
2 * Copyright (C) 2004-2009 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * COMMENT: Zilog Z8530 "zs" serial controller
29 *
30 * Features:
31 * o) Two channels, 0 = "channel B", 1 = "channel A".
32 * Normally, only channel B is in use.
33 *
34 * This is a work in progress... TODOs include:
35 * o) Implement more of the register set.
36 * o) Verify that it works with other guest OSes than NetBSD and OpenBSD.
37 * o) Implement DMA!
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "console.h"
45 #include "cpu.h"
46 #include "device.h"
47 #include "interrupt.h"
48 #include "machine.h"
49 #include "memory.h"
50 #include "misc.h"
51
52 #include "thirdparty/z8530reg.h"
53
54
55 /* #define debug fatal */
56
57 #define ZS_TICK_SHIFT 14
58 #define ZS_N_REGS 16
59 #define ZS_N_CHANNELS 2
60 #define DEV_Z8530_LENGTH 4
61
62 struct z8530_data {
63 struct interrupt irq;
64 int irq_asserted;
65 int addr_mult;
66
67 int console_handle[ZS_N_CHANNELS];
68 int reg_select[ZS_N_CHANNELS];
69 uint8_t rr[ZS_N_CHANNELS][ZS_N_REGS];
70 uint8_t wr[ZS_N_CHANNELS][ZS_N_REGS];
71 };
72
73
74 /*
75 * check_incoming():
76 *
77 * Sets the RX interrupt flag for ports B and A, if something there is input
78 * available on port 0 or 1, respectively.
79 */
check_incoming(struct cpu * cpu,struct z8530_data * d)80 static void check_incoming(struct cpu *cpu, struct z8530_data *d)
81 {
82 if (console_charavail(d->console_handle[0])) {
83 d->rr[1][3] |= ZSRR3_IP_B_RX;
84 d->rr[0][0] |= ZSRR0_RX_READY;
85 }
86 if (console_charavail(d->console_handle[1])) {
87 d->rr[1][3] |= ZSRR3_IP_A_RX;
88 d->rr[1][0] |= ZSRR0_RX_READY;
89 }
90 }
91
92
DEVICE_TICK(z8530)93 DEVICE_TICK(z8530)
94 {
95 /* Generate transmit and receive interrupts at regular intervals. */
96 struct z8530_data *d = (struct z8530_data *) extra;
97 int asserted = 0;
98
99 if (d->rr[1][3] & ZSRR3_IP_B_TX && d->wr[0][1] & ZSWR1_TIE)
100 asserted = 1;
101 if (d->rr[1][3] & ZSRR3_IP_A_TX && d->wr[1][1] & ZSWR1_TIE)
102 asserted = 1;
103
104 d->rr[1][3] &= ~(ZSRR3_IP_B_RX | ZSRR3_IP_A_RX);
105 if (!asserted)
106 check_incoming(cpu, d);
107
108 if (d->rr[1][3] & ZSRR3_IP_B_RX && (d->wr[0][1]&0x18) != ZSWR1_RIE_NONE)
109 asserted = 1;
110 if (d->rr[1][3] & ZSRR3_IP_A_RX && (d->wr[1][1]&0x18) != ZSWR1_RIE_NONE)
111 asserted = 1;
112
113 if (!(d->wr[1][9] & ZSWR9_MASTER_IE))
114 asserted = 0;
115
116 if (asserted)
117 INTERRUPT_ASSERT(d->irq);
118
119 if (d->irq_asserted && !asserted)
120 INTERRUPT_DEASSERT(d->irq);
121
122 d->irq_asserted = asserted;
123 }
124
125
DEVICE_ACCESS(z8530)126 DEVICE_ACCESS(z8530)
127 {
128 struct z8530_data *d = (struct z8530_data *) extra;
129 uint64_t idata = 0, odata = 0;
130 int port_nr;
131
132 if (writeflag == MEM_WRITE)
133 idata = memory_readmax64(cpu, data, len);
134
135 /* Both ports are always ready to transmit: */
136 d->rr[0][0] |= ZSRR0_TX_READY | ZSRR0_DCD | ZSRR0_CTS;
137 d->rr[1][0] |= ZSRR0_TX_READY | ZSRR0_DCD | ZSRR0_CTS;
138
139 relative_addr /= d->addr_mult;
140
141 port_nr = (relative_addr / 2) % ZS_N_CHANNELS;
142 relative_addr &= 1;
143
144 if (relative_addr == 0) {
145 /* Register access: */
146 if (writeflag == MEM_READ) {
147 odata = d->rr[port_nr][d->reg_select[port_nr]];
148 debug("[ z8530: read from port %i reg %2i: "
149 "0x%02x ]\n", port_nr, d->reg_select[
150 port_nr], (int)odata);
151 d->reg_select[port_nr] = 0;
152 } else {
153 if (d->reg_select[port_nr] == 0) {
154 if (idata < 16)
155 d->reg_select[port_nr] = idata & 15;
156 else
157 d->reg_select[port_nr] = idata & 7;
158 switch (idata & 0xf8) {
159 case ZSWR0_CLR_INTR: /* Interrupt ack: */
160 d->rr[1][3] = 0;
161 break;
162 }
163 } else {
164 d->wr[port_nr][d->reg_select[port_nr]] = idata;
165 switch (d->reg_select[port_nr]) {
166 default:debug("[ z8530: write to port %i reg "
167 "%2i: 0x%02x ]\n", port_nr, d->
168 reg_select[port_nr], (int)idata);
169 }
170 d->reg_select[port_nr] = 0;
171 }
172 }
173 } else {
174 /* Data access: */
175 if (writeflag == MEM_READ) {
176 int x = console_readchar(d->console_handle[port_nr]);
177 d->rr[port_nr][0] &= ~ZSRR0_RX_READY;
178 odata = x < 0? 0 : x;
179 } else {
180 idata &= 255;
181 if (idata != 0)
182 console_putchar(d->console_handle[port_nr],
183 idata);
184 if (1 /* d->wr[port_nr][1] & ZSWR1_TIE */) {
185 if (port_nr == 0)
186 d->rr[1][3] |= ZSRR3_IP_B_TX;
187 else
188 d->rr[1][3] |= ZSRR3_IP_A_TX;
189 }
190 }
191 }
192
193 if (writeflag == MEM_READ)
194 memory_writemax64(cpu, data, len, odata);
195
196 dev_z8530_tick(cpu, extra);
197
198 return 1;
199 }
200
201
DEVINIT(z8530)202 DEVINIT(z8530)
203 {
204 struct z8530_data *d;
205 char tmp[100];
206
207 CHECK_ALLOCATION(d = (struct z8530_data *) malloc(sizeof(struct z8530_data)));
208 memset(d, 0, sizeof(struct z8530_data));
209
210 d->addr_mult = devinit->addr_mult;
211
212 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
213
214 snprintf(tmp, sizeof(tmp), "%s [ch-b]", devinit->name);
215 d->console_handle[0] = console_start_slave(devinit->machine, tmp,
216 devinit->in_use);
217 snprintf(tmp, sizeof(tmp), "%s [ch-a]", devinit->name);
218 d->console_handle[1] = console_start_slave(devinit->machine, tmp, 0);
219
220 if (devinit->name2 != NULL && devinit->name2[0])
221 snprintf(tmp, sizeof(tmp), "%s [%s]", devinit->name,
222 devinit->name2);
223 else
224 snprintf(tmp, sizeof(tmp), "%s", devinit->name);
225
226 memory_device_register(devinit->machine->memory, tmp, devinit->addr,
227 DEV_Z8530_LENGTH * d->addr_mult, dev_z8530_access, d, DM_DEFAULT,
228 NULL);
229
230 machine_add_tickfunction(devinit->machine, dev_z8530_tick, d,
231 ZS_TICK_SHIFT);
232
233 devinit->return_ptr = (void *)(size_t) d->console_handle[0];
234
235 return 1;
236 }
237
238