OXYGENHUB
◆ main enhanced-x16-emulator / src / serial.c
6.1 KiB · C · last changed chore(): initial commit 25 days ago source blame history raw
9120e874 Andy McCall 1
// Commander X16 Emulator
2
// Copyright (c) 2022 Michael Steil
3
// All rights reserved. License: 2-clause BSD
4
5
// Commodore Bus emulation, L1: Serial Bus
6
// This code is hacky, buggy and incomplete, but
7
// it helped bringing up the serial bus on real
8
// hardware.
9
10
#include <stdio.h>
11
#include <stdint.h>
12
#include <stdbool.h>
13
#include "serial.h"
14
#include "ieee.h"
15
#include "glue.h"
16
17
serial_port_t serial_port;
18
19
static int state = 0;
20
static bool valid;
21
static int bit;
22
static uint8_t byte;
23
static bool listening = false;
24
static bool talking = false;
25
static bool during_atn = false;
26
static bool eoi = false;
27
static bool fnf = false; // file not found
28
static int clocks_since_last_change = 0;
29
30
#define printf(...)
31
32
static uint8_t
33
read_byte(bool *eoi)
34
{
35
	uint8_t byte;
36
	int ret = ACPTR(&byte);
37
	*eoi = ret >= 0;
38
	return byte;
39
}
40
41
bool
42
serial_port_read_clk()
43
{
44
	return serial_port.out.clk & serial_port.in.clk;
45
}
46
47
bool
48
serial_port_read_data()
49
{
50
	return serial_port.out.data & serial_port.in.data;
51
}
52
53
void
54
serial_step(int clocks)
55
{
56
	bool print = false;
57
58
	static bool old_atn = false, old_clk = false, old_data = false;
59
	if (old_atn == serial_port.in.atn &&
60
		old_clk == serial_port_read_clk() &&
61
		old_data == serial_port_read_data()) {
62
		clocks_since_last_change += clocks;
63
		if (state == 2 && valid == true && bit == 0 && clocks_since_last_change > 200 * MHZ) {
64
			if (clocks_since_last_change < (200 + 60) * MHZ) {
65
				printf("XXX EOI ACK\n");
66
				serial_port.out.data = 0;
67
				eoi = true;
68
			} else {
69
				printf("XXX EOI ACK END\n");
70
				serial_port.out.data = 1;
71
				clocks_since_last_change = 0;
72
			}
73
			print = true;
74
		}
75
		if (state == 10 && clocks_since_last_change > 60 * MHZ) {
76
			serial_port.out.clk = 1;
77
			state = 11;
78
			clocks_since_last_change = 0;
79
			print = true;
80
		} else if (state == 11 && serial_port_read_data() && !fnf) {
81
			clocks_since_last_change = 0;
82
			byte = read_byte(&eoi);
83
			bit = 0;
84
			valid = true;
85
			if (eoi) {
86
				printf("XXXEOI1\n");
87
				state = 12;
88
			} else {
89
				printf("XXXEOI0\n");
90
				serial_port.out.clk = 0;
91
				state = 13;
92
			}
93
			print = true;
94
		} else if (state == 12 && clocks_since_last_change > 512 * MHZ) {
95
			// EOI delay
96
			// XXX we'd have to check for the ACK
97
			clocks_since_last_change = 0;
98
			serial_port.out.clk = 0;
99
			state = 13;
100
			print = true;
101
		} else if (state == 13 && clocks_since_last_change > 60 * MHZ) {
102
			if (valid) {
103
				// send bit
104
				serial_port.out.data = (byte >> bit) & 1;
105
				serial_port.out.clk = 1;
106
				printf("*** BIT%d OUT: %d\n", bit, serial_port.out.data);
107
				bit++;
108
				if (bit == 8) {
109
					state = 14;
110
				}
111
			} else {
112
				serial_port.out.clk = 0;
113
			}
114
			valid = !valid;
115
			clocks_since_last_change = 0;
116
			print = true;
117
		} else if (state == 14 && clocks_since_last_change > 60 * MHZ) {
118
			serial_port.out.data = 1;
119
			serial_port.out.clk = 0;
120
			state = 10;
121
			clocks_since_last_change = 0;
122
			print = true;
123
		}
124
	} else {
125
		clocks_since_last_change = 0;
126
127
		printf("-SERIAL IN { ATN:%d CLK:%d DATA:%d }\n", serial_port.in.atn, old_clk, old_data);
128
		printf("+SERIAL IN { ATN:%d CLK:%d DATA:%d } --- IN { CLK:%d DATA:%d } OUT { CLK:%d DATA:%d } -- #%d\n", serial_port.in.atn, serial_port_read_clk(), serial_port_read_data(), serial_port.in.clk, serial_port.in.data, serial_port.out.clk, serial_port.out.data, state);
129
130
		if (!during_atn && serial_port.in.atn) {
131
			serial_port.out.data = 0;
132
			state = 99;
133
			during_atn = true;
134
			printf("XXX START OF ATN\n");
135
		}
136
137
		switch(state) {
138
			case 0:
139
				break;
140
			case 99:
141
				// wait for CLK=0
142
				if (!serial_port_read_clk()) {
143
					state = 1;
144
				}
145
				break;
146
			case 1:
147
				if (during_atn && !serial_port.in.atn) {
148
					// cancelled ATN
149
					serial_port.out.data = 1;
150
					serial_port.out.clk = 1;
151
					during_atn = false;
152
					printf("*** END OF ATN\n");
153
					if (listening) {
154
						// keep holding DATA to indicate we're here
155
						serial_port.out.data = 0;
156
						printf("XXX START OF DATA RECEIVE\n");
157
					} else if (talking) {
158
						serial_port.out.clk = 0;
159
						state = 10;
160
						printf("XXX START OF DATA SEND\n");
161
					} else {
162
						state = 0;
163
					}
164
					break;
165
				}
166
				// wait for CLK=1
167
				if (serial_port_read_clk()) {
168
					serial_port.out.data = 1;
169
					state = 2;
170
					valid = true;
171
					bit = 0;
172
					byte = 0;
173
					eoi = false;
174
					printf("XXX START OF BYTE\n");
175
				}
176
				break;
177
			case 2:
178
				if (during_atn && !serial_port.in.atn) {
179
					// cancelled ATN
180
					serial_port.out.data = 1;
181
					serial_port.out.clk = 1;
182
					printf("*** XEND OF ATN\n");
183
					state = 0;
184
					break;
185
				}
186
				if (valid) {
187
					// wait for CLK=0, data not valid
188
					if (!serial_port_read_clk()) {
189
						valid = false;
190
						printf("XXX NOT VALID\n");
191
					}
192
				} else {
193
					// wait for CLK=1, data valid
194
					if (serial_port_read_clk()) {
195
						bool b = serial_port_read_data();
196
						byte |= (b << bit);
197
						printf("*** BIT%d IN: %d\n", bit, b);
198
						valid = true;
199
						if (++bit == 8) {
200
							printf("*** %s BYTE IN: %02x%s\n", during_atn ? "ATN" : "DATA", byte, eoi ? " (EOI)" : "");
201
							if (during_atn) {
202
								printf("IEEE CMD %x\n", byte);
203
								switch (byte & 0x60) {
204
									case 0x20:
205
										if (byte == 0x3f) {
206
											UNLSN();
207
											listening = false;
208
										} else {
209
											LISTEN(byte);
210
											listening = true;
211
										}
212
										break;
213
									case 0x40:
214
										if (byte == 0x5f) {
215
											UNTLK();
216
											talking = false;
217
										} else {
218
											TALK(byte);
219
											talking = true;
220
										}
221
										break;
222
									case 0x60:
223
										if (listening) {
224
											SECOND(byte);
225
										} else { // talking
226
											TKSA(byte);
227
										}
228
										break;
229
								}
230
							} else {
231
								CIOUT(byte);
232
							}
233
							serial_port.out.data = 0;
234
							state = 1;
235
						}
236
					}
237
				}
238
				break;
239
			case 3:
240
				break;
241
		}
242
		print = true;
243
	}
244
245
	if (print) {
246
		printf(">SERIAL IN { ATN:%d CLK:%d DATA:%d } --- IN { CLK:%d DATA:%d } OUT { CLK:%d DATA:%d } -- #%d\n", serial_port.in.atn, serial_port_read_clk(), serial_port_read_data(), serial_port.in.clk, serial_port.in.data, serial_port.out.clk, serial_port.out.data, state);
247
	}
248
249
	old_atn = serial_port.in.atn;
250
	old_clk = serial_port_read_clk();
251
	old_data = serial_port_read_data();
252
}
253