OXYGENHUB
◆ main enhanced-x16-emulator / src / i2c.c
10.1 KiB · C · last changed chore(): initial commit 25 days ago source blame history raw
1
// Commander X16 Emulator
2
// Copyright (c) 2021 Michael Steil
3
// All rights reserved. License: 2-clause BSD
4
5
#include <stdio.h>
6
#include <stdbool.h>
7
#include "i2c.h"
8
#include "smc.h"
9
#include "rtc.h"
10
#include "io_trace.h"
11
12
#define LOG_LEVEL 0
13
14
#define DEVICE_SMC 0x42
15
#define DEVICE_RTC 0x6F
16
17
#define STATE_START 0
18
#define STATE_STOP -1
19
20
i2c_port_t i2c_port;
21
22
static int state = STATE_STOP;
23
static bool read_mode = false;
24
static uint8_t value = 0;
25
static int count = 0;
26
static uint8_t device;
27
28
// Debug-only session counters; pure increments, no effect on emulation.
29
static uint32_t i2c_debug_transactions_started   = 0;
30
static uint32_t i2c_debug_transactions_completed = 0;
31
static uint32_t i2c_debug_bytes_read             = 0;
32
static uint32_t i2c_debug_bytes_written          = 0;
33
34
#define KBD_SIZE 16
35
uint8_t kbd_buffer[KBD_SIZE];						//Ring buffer for key codes
36
37
#define MSE_SIZE 16
38
uint8_t mse_buffer[MSE_SIZE];						//Ring buffer for mouse movement data
39
40
void i2c_reset_state() {
41
	state = STATE_STOP;
42
	read_mode = false;
43
	value = 0;
44
	count = 0;
45
	smc_requested_reset = false;
46
	i2c_mse_buffer_flush();
47
	i2c_kbd_buffer_flush();
48
}
49
50
static void
51
i2c_data(uint8_t device, uint8_t v) {
52
	switch (device) {
53
		case DEVICE_SMC:
54
			smc_i2c_data(v);
55
			break;
56
		case DEVICE_RTC:
57
			rtc_i2c_data(v);
58
			break;
59
		default:
60
			break;
61
	}
62
#if LOG_LEVEL >= 1
63
	printf("I2C DATA($%02X) => $%02X\n", device, value);
64
#endif
65
}
66
67
static uint8_t
68
i2c_read(uint8_t device) {
69
	uint8_t value;
70
	switch (device) {
71
		case DEVICE_SMC:
72
			value = smc_read();
73
			break;
74
		case DEVICE_RTC:
75
			value = rtc_read();
76
			break;
77
		default:
78
			value = 0xff;
79
			break;
80
	}
81
#if LOG_LEVEL >= 1
82
	printf("I2C READ($%02X) = $%02X\n", device, value);
83
#endif
84
	return value;
85
}
86
87
static void
88
i2c_write(uint8_t device) {
89
	switch (device) {
90
		case DEVICE_SMC:
91
			smc_write();
92
			break;
93
		case DEVICE_RTC:
94
			rtc_write();
95
			break;
96
			//        default:
97
			// no-op
98
	}
99
#if LOG_LEVEL >= 1
100
	printf("I2C WRITE $%02X\n", device);
101
#endif
102
}
103
104
void
105
i2c_step()
106
{
107
	static i2c_port_t old_i2c_port;
108
109
	if (old_i2c_port.clk_in != i2c_port.clk_in || old_i2c_port.data_in != i2c_port.data_in) {
110
#if LOG_LEVEL >= 5
111
		printf("I2C(%d) C:%d D:%d\n", state, i2c_port.clk_in, i2c_port.data_in);
112
#endif
113
		if (state == STATE_STOP && i2c_port.clk_in == 0 && i2c_port.data_in == 0) {
114
#if LOG_LEVEL >= 2
115
			printf("I2C START\n");
116
#endif
117
			state = STATE_START;
118
			i2c_debug_transactions_started++;
119
		}
120
121
		if (state == 1 && i2c_port.clk_in == 1 &&i2c_port.data_in == 1 &&  old_i2c_port.data_in == 0) {
122
#if LOG_LEVEL >= 2
123
			printf("I2C STOP\n");
124
#endif
125
			if (io_trace_wants(IO_DEV_I2C) && count > 0) {
126
				const char *dname = (device == DEVICE_SMC) ? "SMC" : (device == DEVICE_RTC) ? "RTC" : "?";
127
				io_trace_event(IO_DEV_I2C, "i2c STOP $%02X(%s) %s %d byte%s",
128
					device, dname, read_mode ? "read" : "write", count, count == 1 ? "" : "s");
129
			}
130
			state = STATE_STOP;
131
			count = 0;
132
			read_mode = false;
133
			i2c_debug_transactions_completed++;
134
		}
135
136
		if (state != STATE_STOP && i2c_port.clk_in == 1 && old_i2c_port.clk_in == 0) {
137
			i2c_port.data_out = 1;
138
			if (state < 8) {
139
				if (read_mode) {
140
					if (state == 0) {
141
						i2c_debug_bytes_read++;
142
						value = i2c_read(device);
143
					}
144
					i2c_port.data_out = !!(value & 0x80);
145
					value <<= 1;
146
#if LOG_LEVEL >= 4
147
					printf("I2C OUT#%d: %d\n", state, i2c_port.data_out);
148
#endif
149
					state++;
150
				} else {
151
#if LOG_LEVEL >= 4
152
					printf("I2C BIT#%d: %d\n", state, i2c_port.data_in);
153
#endif
154
					value <<= 1;
155
					value |= i2c_port.data_in;
156
					state++;
157
				}
158
			} else { // state == 8
159
				if (read_mode) {
160
					bool nack = i2c_port.data_in;
161
					if (nack) {
162
#if LOG_LEVEL >= 3
163
					printf("I2C OUT DONE (NACK)\n");
164
#endif
165
						count = 0;
166
						read_mode = false;
167
					} else {
168
#if LOG_LEVEL >= 3
169
					printf("I2C OUT DONE (ACK)\n");
170
#endif
171
					}
172
				} else {
173
					bool ack = true;
174
					switch (count) {
175
						case 0:
176
							device = value >> 1;
177
							read_mode = value & 1;
178
							if (device != DEVICE_SMC && device != DEVICE_RTC) {
179
								ack = false;
180
							}
181
							break;
182
						case 1:
183
							i2c_data(device, value);
184
							break;
185
						default:
186
							i2c_data(device, value);
187
							i2c_write(device);
188
							break;
189
					}
190
					if (ack) {
191
#if LOG_LEVEL >= 3
192
						printf("I2C ACK(%d) $%02X\n", count, value);
193
#endif
194
							if (count > 0) i2c_debug_bytes_written++;
195
							i2c_port.data_out = 0;
196
							count++;
197
					} else {
198
#if LOG_LEVEL >= 3
199
						printf("I2C NACK(%d) $%02X\n", count, value);
200
#endif
201
							if (io_trace_wants(IO_DEV_I2C) && count == 0) {
202
								io_trace_event(IO_DEV_I2C, "i2c NACK $%02X unknown device", device);
203
							}
204
							count = 0;
205
							read_mode = false;
206
						}
207
				}
208
				state = STATE_START;
209
			}
210
		}
211
		old_i2c_port = i2c_port;
212
	}
213
}
214
215
216
/**
217
 * Keyboard buffer functions
218
 **/
219
uint8_t kbd_head=0;
220
uint8_t kbd_tail=0;
221
222
/**
223
 * Adds value to the keyboard ring buffer; the value is discarded if the buffer is full
224
 **/
225
void i2c_kbd_buffer_add(uint8_t value) {
226
	uint8_t next = (kbd_head + 1) & (KBD_SIZE - 1);		//Next available index
227
    if (next != kbd_tail) {								//next = tail => buffer full
228
    	kbd_buffer[kbd_head] = value;					//Set new value
229
        kbd_head = next;								//Update buffer head pointer
230
    }
231
}
232
233
/**
234
 * Returns the tail value from the keyboard ring buffer; if the buffer is empty, it returns 0
235
 **/
236
uint8_t i2c_kbd_buffer_next() {
237
	uint8_t value = 0;									//Prepare to return 0 of buffer is empty
238
	if (kbd_head != kbd_tail) {							//head = tail => empty
239
    	value = kbd_buffer[kbd_tail];					//Get value
240
        kbd_tail = (kbd_tail + 1) & (KBD_SIZE - 1);		//Update buffer tail pointer
241
    }
242
	return value;
243
}
244
245
/**
246
 * Clears the keyboard ring buffer
247
 **/
248
void i2c_kbd_buffer_flush() {
249
	kbd_tail = kbd_head = 0;
250
}
251
252
/**
253
 * Returns number of values currently stored in the keyboard ring buffer
254
 **/
255
uint8_t i2c_kbd_buffer_count() {
256
	return (KBD_SIZE + kbd_head - kbd_tail) & (KBD_SIZE - 1);
257
}
258
259
/**
260
 * Mouse buffer functions
261
 **/
262
uint8_t mse_head=0;
263
uint8_t mse_tail=0;
264
265
/**
266
 * Adds value to the mouse ring buffer; discards the value if the buffer is full
267
 **/
268
void i2c_mse_buffer_add(uint8_t value) {
269
	uint8_t next = (mse_head + 1) & (MSE_SIZE - 1);				//Next available index
270
    if (next != mse_tail) {										//next = tail => buffer full
271
    	mse_buffer[mse_head] = value;							//Set value
272
        mse_head = next;										//Update head pointer
273
    }
274
}
275
276
/**
277
 * Returns the tail value from the mouse ring buffer; if the buffer is empty, it returns 0
278
 **/
279
uint8_t i2c_mse_buffer_next() {
280
	uint8_t value = 0;											//Prepare to return 0 if buffer empty
281
	if (mse_head != mse_tail) {									//head = tail => buffer empty
282
    	value = mse_buffer[mse_tail];							//Get tail value
283
        mse_tail = (mse_tail + 1) & (MSE_SIZE - 1);				//Update tail pointer
284
    }
285
	return value;
286
}
287
288
/**
289
 * Clears the mouse ring buffer
290
 **/
291
void i2c_mse_buffer_flush() {
292
	mse_tail = mse_head = 0;
293
}
294
295
/**
296
 * Returns number of values currently stored in the ring buffer
297
 **/
298
uint8_t i2c_mse_buffer_count() {
299
    return (MSE_SIZE + mse_head - mse_tail) & (MSE_SIZE - 1);
300
}
301
302
303
/**
304
 *  fake mouse
305
 **/
306
307
static uint8_t buttons;
308
static int16_t mouse_diff_x = 0;
309
static int16_t mouse_diff_y = 0;
310
static int8_t wheel = 0;
311
static uint8_t mouse_device_id = 3;
312
313
// byte 0, bit 7: Y overflow
314
// byte 0, bit 6: X overflow
315
// byte 0, bit 5: Y sign bit
316
// byte 0, bit 4: X sign bit
317
// byte 0, bit 3: Always 1
318
// byte 0, bit 2: Middle Btn
319
// byte 0, bit 1: Right Btn
320
// byte 0, bit 0: Left Btn
321
// byte 2:        X Movement
322
// byte 3:        Y Movement
323
324
static bool
325
mouse_send(int x, int y, int b, uint8_t w)
326
{
327
	uint8_t psize;
328
	if (mouse_device_id == 3 || mouse_device_id == 4) {
329
		psize = 4;
330
	}
331
	else {
332
		psize  = 3;
333
	}
334
335
	if (i2c_mse_buffer_count() < MSE_SIZE-psize) {
336
		uint8_t byte0 =
337
		    ((y >> 9) & 1) << 5 |
338
		    ((x >> 9) & 1) << 4 |
339
		    1 << 3 |
340
		    b;
341
		i2c_mse_buffer_add(byte0);
342
		i2c_mse_buffer_add(x);
343
		i2c_mse_buffer_add(y);
344
345
		if (mouse_device_id == 3 || mouse_device_id == 4) {
346
			i2c_mse_buffer_add(w);
347
		}
348
349
		return true;
350
	} else {
351
		return false;
352
	}
353
}
354
355
void
356
mouse_send_state(void)
357
{
358
	do {
359
		int send_diff_x = mouse_diff_x > 255 ? 255 : (mouse_diff_x < -256 ? -256 : mouse_diff_x);
360
		int send_diff_y = mouse_diff_y > 255 ? 255 : (mouse_diff_y < -256 ? -256 : mouse_diff_y);
361
362
		mouse_send(send_diff_x, send_diff_y, buttons, wheel);
363
364
		mouse_diff_x -= send_diff_x;
365
		mouse_diff_y -= send_diff_y;
366
		wheel = 0;
367
	} while (mouse_diff_x != 0 && mouse_diff_y != 0);
368
}
369
370
void
371
mouse_button_down(int num)
372
{
373
	buttons |= 1 << num;
374
}
375
376
void
377
mouse_button_up(int num)
378
{
379
	buttons &= (1 << num) ^ 0xff;
380
}
381
382
void
383
mouse_move(int x, int y)
384
{
385
	mouse_diff_x += x;
386
	mouse_diff_y -= y;
387
}
388
389
uint8_t
390
mouse_read(uint8_t reg)
391
{
392
	return 0xff;
393
}
394
395
void 
396
mouse_set_wheel(int8_t y)
397
{
398
	if (mouse_device_id == 3 || mouse_device_id == 4) {
399
		if (y < -7) {
400
			wheel = 7;
401
		}
402
		else if (y > 8) {
403
			wheel = -8;
404
		}
405
		else {
406
			wheel = -y;
407
		}
408
	}
409
}
410
411
uint8_t 
412
mouse_get_device_id() {
413
	return mouse_device_id;
414
}
415
416
void 
417
mouse_set_device_id(uint8_t d) {
418
	switch(d) {
419
		case 0:
420
		case 3:
421
			mouse_device_id = d;
422
			break;
423
		case 4:
424
			mouse_device_id =3;
425
			break;
426
		default:
427
			mouse_device_id = 0;
428
			break;
429
	}
430
	i2c_mse_buffer_flush();
431
}
432
433
// Side-effect-free snapshot of I2C bus state for the ImGui debugger.
434
void
435
i2c_debug_get_state(i2c_debug_state_t *out)
436
{
437
	out->state    = state;
438
	out->read_mode = read_mode;
439
	out->value    = value;
440
	out->count    = count;
441
	out->device   = device;
442
443
	switch (device) {
444
		case 0x42: out->device_name[0] = 'S'; out->device_name[1] = 'M'; out->device_name[2] = 'C'; out->device_name[3] = '\0'; break;
445
		case 0x6F: out->device_name[0] = 'R'; out->device_name[1] = 'T'; out->device_name[2] = 'C'; out->device_name[3] = '\0'; break;
446
		default:   out->device_name[0] = '\0'; break;
447
	}
448
449
	out->clk_in  = i2c_port.clk_in;
450
	out->data_in = i2c_port.data_in;
451
	out->data_out = i2c_port.data_out;
452
453
	out->transactions_started   = i2c_debug_transactions_started;
454
	out->transactions_completed = i2c_debug_transactions_completed;
455
	out->bytes_read             = i2c_debug_bytes_read;
456
	out->bytes_written          = i2c_debug_bytes_written;
457
458
	// keyboard buffer fill = (KBD_SIZE + head - tail) & (KBD_SIZE - 1)
459
	out->kbd_fill = i2c_kbd_buffer_count();
460
	out->mse_fill = i2c_mse_buffer_count();
461
}