OXYGENHUB
◆ main x16-emulator / src / smc.c
3.7 KiB · C · last changed chore(): initial commit 15 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
// System Management Controller
6
7
#include <stdio.h>
8
#include <stdbool.h>
9
#include <stdlib.h>
10
#include "smc.h"
11
#include "glue.h"
12
#include "i2c.h"
13
#ifdef __EMSCRIPTEN__
14
#include <emscripten.h>
15
#endif
16
17
// 0x01 0x00      - Power Off
18
// 0x01 0x01      - Hard Reboot
19
// 0x02 0x00      - Reset Button Press
20
// 0x03 0x00      - NMI Button Press
21
// 0x04 0x00-0xFF - Power LED Level (PWM)
22
// 0x05 0x00-0xFF - Activity LED Level (PWM)
23
24
uint8_t default_read_op = 0x41;
25
uint8_t default_read_state = 0;
26
uint8_t activity_led;
27
uint8_t mse_count = 0;
28
#define I2C_DATA_LEN 16
29
static uint8_t i2c_data[I2C_DATA_LEN];
30
static uint8_t i2c_data_pos = 0;
31
bool smc_requested_reset = false;
32
33
void
34
smc_i2c_data(uint8_t v) {
35
	if (i2c_data_pos < I2C_DATA_LEN) {
36
		i2c_data[i2c_data_pos] = v;
37
		i2c_data_pos++;
38
	}
39
}
40
41
uint8_t
42
smc_read() {
43
	uint8_t mouse_id;
44
	uint8_t mouse_size;
45
	uint8_t ret;
46
47
	switch (i2c_data[0]){
48
		case 0x43: // keycode + mouse
49
			if (default_read_state == 0) {
50
				default_read_state = 1;
51
				ret = i2c_kbd_buffer_next();
52
				break;
53
			}
54
			// fall through
55
		case 0x42: // mouse only
56
		// fall through
57
		// Offset that returns three bytes from mouse buffer (one movement packet) or a single zero if there is not complete packet in the buffer
58
		// mse_count keeps track of which one of the three bytes it's sending
59
		case 0x21:
60
			mouse_id = mouse_get_device_id();
61
			if (mouse_id == 3 || mouse_id == 4) {
62
				mouse_size = 4;
63
			}
64
			else {
65
				mouse_size = 3;
66
			}
67
68
			if (mse_count == 0 && i2c_mse_buffer_count() >= mouse_size) {		// If start of packet, check if there are at least three bytes in the buffer
69
				mse_count++;
70
				ret = i2c_mse_buffer_next();
71
			}
72
			else if (mse_count > 0) {								// If we have already started sending bytes, assume there is enough data in the buffer
73
				mse_count++;
74
				if (mse_count == mouse_size) {
75
					mse_count = 0;
76
					default_read_state = 0;
77
				}
78
				ret = i2c_mse_buffer_next();
79
			}
80
			else {													// Return a single zero if no complete packet available
81
				mse_count = 0;
82
				default_read_state = 0;
83
				ret = 0x00;
84
			}
85
			break;
86
		case 0x41: // keycode only
87
			default_read_state = 0;
88
			// fall through
89
		// Offset that returns one byte from the keyboard buffer
90
		case 0x07:
91
			ret = i2c_kbd_buffer_next();
92
			break;
93
		// Offset, initially used for debugging in the SMC, but now
94
		// also used to inform kernal whether power on was done by
95
		// holding the power button
96
		case 0x09:
97
			if (pwr_long_press) {
98
				// Reset pwr_long_press so a reset will start normally
99
				pwr_long_press = false;
100
				ret = 1;
101
			} else {
102
				ret = 0;
103
			}
104
			break;
105
106
		case 0x22:
107
			ret = mouse_get_device_id();
108
			break;
109
110
		case 0x30:
111
			ret = SMC_VERSION_MAJOR;
112
			break;
113
114
		case 0x31:
115
			ret = SMC_VERSION_MINOR;
116
			break;
117
118
		case 0x32:
119
			ret = SMC_VERSION_PATCH;
120
			break;
121
122
		default:
123
			ret = 0xff;
124
	}
125
126
	i2c_data[0] = default_read_op;
127
	i2c_data_pos = 0;
128
	return ret;
129
}
130
131
void
132
smc_write() {
133
	switch (i2c_data[0]) {
134
		case 1:
135
			if (i2c_data[1] == 0) {
136
				printf("SMC Power Off.\n");
137
				main_shutdown();
138
#ifdef __EMSCRIPTEN__
139
				emscripten_force_exit(0);
140
#endif
141
				exit(0);
142
			} else if (i2c_data[1] == 1) {
143
				smc_requested_reset = true;
144
			}
145
			break;
146
		case 2:
147
			if (i2c_data[1] == 0) {
148
				smc_requested_reset = true;
149
			}
150
			break;
151
		case 3:
152
			if (i2c_data[1] == 0) {
153
				nmi6502();
154
			}
155
			break;
156
		case 4:
157
			// Power LED is not controllable
158
			break;
159
		case 5:
160
			activity_led = i2c_data[1] >= 128 ? 255 : 0;
161
			break;
162
163
		case 0x20:
164
			mouse_set_device_id(i2c_data[1]);
165
			break;
166
167
		case 0x40:
168
			default_read_op = i2c_data[1];
169
			default_read_state = 0;
170
			break;
171
	}
172
173
	i2c_data[0] = default_read_op;
174
	i2c_data_pos = 0;
175
}
176