-
Notifications
You must be signed in to change notification settings - Fork 3
/
adb.v
332 lines (290 loc) · 7.58 KB
/
adb.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/* ADB implementation for plus_too */
module adb(
input clk,
input clk_en,
input reset,
input [1:0] st,
output _int,
input viaBusy,
output reg listen,
input [7:0] adb_din,
input adb_din_strobe,
output reg [7:0] adb_dout,
output reg adb_dout_strobe,
input mouseStrobe,
input [8:0] mouseX,
input [8:0] mouseY,
input mouseButton,
input keyStrobe,
input [7:0] keyData
);
localparam TALKINTERVAL = 17'd8000*4'd11; // 11 ms
reg [3:0] cmd_r;
reg [1:0] st_r;
wire [1:0] r_r = cmd_r[1:0];
reg [3:0] addr_r;
reg [3:0] respCnt;
reg [16:0] talkTimer;
reg idleActive;
wire [3:0] cmd = adb_din[3:0];
wire [3:0] addr = adb_din[7:4];
reg sendResponse;
always @(posedge clk) begin
if (reset) begin
respCnt <= 0;
idleActive <= 0;
cmd_r <= 0;
listen <= 0;
sendResponse <= 0;
end else if (clk_en) begin
st_r <= st;
adb_dout_strobe <= 0;
sendResponse <= 0;
case (st)
2'b00: // new command
begin
if (st_r != 2'b00)
listen <= 1;
respCnt <= 0;
if (adb_din_strobe) begin
idleActive <= 1;
cmd_r <= cmd;
addr_r <= addr;
listen <= 0;
if (addr_r != addr || cmd_r != cmd)
talkTimer <= 0;
else
talkTimer <= TALKINTERVAL;
end
end
2'b01, 2'b10: // even byte, odd byte
begin
// Reset, flush, talk
if (!viaBusy && (cmd_r[3:1] == 0 || cmd_r[3:2] == 2'b11) && respCnt[0] == st[1]) begin
sendResponse <= 1;
respCnt <= respCnt + 1'd1;
end
if (sendResponse) begin
adb_dout <= adbReg;
adb_dout_strobe <= 1;
end
// Listen
if (st_r != st) listen <= cmd_r[3:2] == 2'b10;
if (cmd_r[3:2] == 2'b10 && respCnt[0] == st[1]) begin
if (adb_din_strobe) begin
listen <= 0;
respCnt <= respCnt + 1'd1;
// Listen : it's handled in the device specific part
// The Listen command is to write to registers, some use cases:
// - device ID and device handler writes
// - LED status for the keyboard
end
end
end
2'b11: // idle
begin
if (cmd_r[3:2] == 2'b11 && idleActive) begin
if (talkTimer != 0)
talkTimer <= talkTimer - 1'd1;
else begin
adb_dout <= 8'hFF;
adb_dout_strobe <= 1;
talkTimer <= TALKINTERVAL;
idleActive <= 0;
end
end
end
default: ;
endcase
end
end
// Device handlers
wire [3:0] addrKeyboard = kbdReg3[11:8];
wire [3:0] addrMouse = mouseReg3[11:8];
wire mouseInt = (addr_r != addrMouse && mouseValid == 2'b01);
wire keyboardInt = (addr_r != addrKeyboard && (keyboardValid == 1 || keyboardValid == 2));
wire irq = mouseInt | keyboardInt | !adbValid;
wire int_inhibit = respCnt < 3 &&
((addr_r == addrMouse && mouseValid == 2'b01) ||
(addr_r == addrKeyboard && (keyboardValid == 1 || keyboardValid == 2)));
assign _int = ~(irq && (st == 2'b01 || st == 2'b10)) | int_inhibit;
// Mouse handler
reg [15:0] mouseReg3;
reg [6:0] X,Y;
reg button;
reg [1:0] mouseValid;
always @(posedge clk) begin
if (reset || cmd_r == 0) begin
mouseReg3 <= 16'h6301; // device id: 3 device handler id: 1
X <= 0;
Y <= 0;
mouseValid <= 0;
end else if (clk_en) begin
if (mouseStrobe) begin
if (~mouseX[8] & |mouseX[7:6]) X <= 7'h3F;
else if (mouseX[8] & ~mouseX[6]) X <= 7'h40;
else X <= mouseX[6:0];
if (~mouseY[8] & |mouseY[7:6]) Y <= 7'h40;
else if (mouseY[8] & ~mouseY[6]) Y <= 7'h3F;
else Y <= -mouseY[6:0];
button <= mouseButton;
mouseValid <= 2'b01;
end
if (addr_r == addrMouse) begin
if (mouseValid == 2'b01 && respCnt == 3)
// mouse data sent
mouseValid <= 2'b10;
if ((mouseValid == 2'b10 && st == 2'b00) || cmd_r == 4'b0001) begin
// Flush mouse data after read or flush command
mouseValid <= 0;
X <= 0;
Y <= 0;
end
end
end
end
// Keyboard handler
reg [1:0] keyboardValid;
reg [15:0] kbdReg0;
reg [15:0] kbdReg2;
reg [15:0] kbdReg3;
reg [7:0] kbdFifo[8];
reg [2:0] kbdFifoRd, kbdFifoWr;
always @(posedge clk) begin
if (reset || cmd_r == 0) begin
kbdReg0 <= 16'hFFFF;
kbdReg2 <= 16'hFFFF;
kbdReg3 <= 16'h6202; // device id: 2 device handler id: 2
keyboardValid <= 0;
kbdFifoRd <= 0;
kbdFifoWr <= 0;
end else if (clk_en) begin
if (keyStrobe && keyData[6:0] != 7'h7F) begin
// Store the keypress in the FIFO
kbdFifo[kbdFifoWr] <= keyData;
kbdFifoWr <= kbdFifoWr + 1'd1;
end
if (kbdFifoWr != kbdFifoRd && st == 2'b11 && keyboardValid < 2) begin
// Read the FIFO when no other key processing in progress
if (kbdReg0[6:0] == kbdFifo[kbdFifoRd][6:0])
kbdReg0[7:0] <= kbdFifo[kbdFifoRd];
else if (kbdReg0[14:8] == kbdFifo[kbdFifoRd][6:0])
kbdReg0[15:8] <= kbdFifo[kbdFifoRd];
else if (kbdReg0[7:0] == 8'hFF)
kbdReg0[7:0] <= kbdFifo[kbdFifoRd];
else
kbdReg0[15:8] <= kbdFifo[kbdFifoRd];
// kbdReg0 has a valid key
keyboardValid <= keyboardValid + 1'd1;
kbdFifoRd <= kbdFifoRd + 1'd1;
end
if (addr_r == addrKeyboard) begin
if (cmd_r == 4'b1010 && adb_din_strobe && st[1]^st[0]) begin
// write into reg2 (keyboard LEDs)
if (respCnt == 1) kbdReg2[2:0] <= adb_din[2:0];
end
if (keyboardValid != 0 && respCnt == 2)
// Beginning of keyboard data read
keyboardValid <= 2'd3;
if ((keyboardValid == 3 && st == 2'b00) || cmd_r == 4'b0001) begin
// Flush keyboard data after read or flush command
keyboardValid <= 0;
kbdReg0 <= 16'hFFFF;
if (cmd_r == 4'b0001) begin
// Flush
kbdFifoRd <= 0;
kbdFifoWr <= 0;
end
end
end
end
end
// Register 0 in the Apple Standard Mouse
// Bit Meaning
// 15 Button status; 0 = down
// 14-8 Y move counts'
// 7 Not used (always 1)
// 6-0 X move counts
// Register 0 in the Apple Standard Keyboard
// Bit Meaning
// 15 Key status for first key; 0 = down
// 14-8 Key code for first key; a 7-bit ASCII value
// 7 Key status for second key; 0 = down
// 6-0 Key code for second key; a 7-bit ASCII value
// Register 2 in the Apple Extended Keyboard
// Bit Key
// 15 None (reserved)
// 14 Delete
// 13 Caps Lock
// 12 Reset
// 11 Control
// 10 Shift
// 9 Option
// 8 Command
// 7 Num Lock/Clear
// 6 Scroll Lock
// 5-3 None (reserved)
// 2 LED 3 (Scroll Lock) *
// 1 LED 2 (Caps Lock) *
// 0 LED 1 (Num Lock) *
//
// *Changeable via Listen Register 2
// Register 3 (common for all devices):
// Bit Description
// 15 Reserved; must be 0
// 14 Exceptional event, device specific; always 1 if not used
// 13 Service Request enable; 1 = enabled
// 12 Reserved; must be 0
// 11-8 Device address
// 7-0 Device Handler ID
reg [7:0] adbReg;
reg adbValid;
reg [15:0] talkReg;
always @(*) begin
adbReg = 8'hFF;
adbValid = 0;
talkReg = 0;
if (addr_r == addrKeyboard) begin
if (cmd_r[3:1] == 0) begin
// reset
if (respCnt == 0) adbValid = 1;
end else begin
// talk
case (r_r)
2'b00: talkReg = kbdReg0;
2'b10: talkReg = kbdReg2;
2'b11: talkReg = kbdReg3;
default: ;
endcase
if (respCnt == 1) begin
adbReg = talkReg[15:8];
adbValid = 1;
end
if (respCnt == 2) begin
adbReg = talkReg[7:0];
adbValid = 1;
end
end
end else if (addr_r == addrMouse) begin
if (cmd_r[3:1] == 0) begin
// reset
if (respCnt == 0) adbValid = 1;
end else begin
// talk
case (r_r)
2'b00: talkReg = { button, Y, 1'b1, X };
2'b11: talkReg = mouseReg3;
default: ;
endcase
if (respCnt == 1) begin
adbReg = talkReg[15:8];
adbValid = 1;
end
if (respCnt == 2) begin
adbReg = talkReg[7:0];
adbValid = 1;
end
end
end
end
endmodule