-
Notifications
You must be signed in to change notification settings - Fork 44
/
kc85.c
301 lines (287 loc) · 10 KB
/
kc85.c
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
/*
kc85.c
KC85/2, /3 and /4.
*/
#include "common.h"
#define CHIPS_IMPL
#include "chips/z80.h"
#include "chips/z80ctc.h"
#include "chips/z80pio.h"
#include "chips/beeper.h"
#include "chips/clk.h"
#include "chips/kbd.h"
#include "chips/mem.h"
#include "systems/kc85.h"
#include "kc85-roms.h"
/* imports from kc85-ui.cc */
#ifdef CHIPS_USE_UI
#include "ui.h"
void kc85ui_init(kc85_t* kc85);
void kc85ui_discard(void);
void kc85ui_draw(void);
void kc85ui_exec(kc85_t* kc85, uint32_t frame_time_us);
static const int ui_extra_height = 16;
#else
static const int ui_extra_height = 0;
#endif
static kc85_t kc85;
/* module to insert after ROM module image has been loaded */
kc85_module_type_t delay_insert_module = KC85_MODULE_NONE;
/* sokol-app entry, configure application callbacks and window */
static void app_init(void);
static void app_frame(void);
static void app_input(const sapp_event*);
static void app_cleanup(void);
sapp_desc sokol_main(int argc, char* argv[]) {
sargs_setup(&(sargs_desc) { .argc = argc, .argv = argv });
return (sapp_desc) {
.init_cb = app_init,
.frame_cb = app_frame,
.event_cb = app_input,
.cleanup_cb = app_cleanup,
.width = 2 * kc85_std_display_width(),
.height = 2 * kc85_std_display_height() + ui_extra_height,
.window_title = "KC85",
.ios_keyboard_resizes_canvas = true
};
}
/* audio-streaming callback */
static void push_audio(const float* samples, int num_samples, void* user_data) {
(void)user_data;
saudio_push(samples, num_samples);
}
/* a callback to patch some known problems in game snapshot files */
static void patch_snapshots(const char* snapshot_name, void* user_data) {
(void)user_data;
if (strcmp(snapshot_name, "JUNGLE ") == 0) {
/* patch start level 1 into memory */
mem_wr(&kc85.mem, 0x36b7, 1);
mem_wr(&kc85.mem, 0x3697, 1);
for (int i = 0; i < 5; i++) {
mem_wr(&kc85.mem, 0x1770 + i, mem_rd(&kc85.mem, 0x36b6 + i));
}
}
else if (strcmp(snapshot_name, "DIGGER COM\x01") == 0) {
mem_wr16(&kc85.mem, 0x09AA, 0x0160); /* time for delay-loop 0160 instead of 0260 */
mem_wr(&kc85.mem, 0x3d3a, 0xB5); /* OR L instead of OR (HL) */
}
else if (strcmp(snapshot_name, "DIGGERJ") == 0) {
mem_wr16(&kc85.mem, 0x09AA, 0x0260);
mem_wr(&kc85.mem, 0x3d3a, 0xB5); /* OR L instead of OR (HL) */
}
}
/* get kc85_desc_t struct for a given KC85 type */
kc85_desc_t kc85_desc(kc85_type_t type) {
return (kc85_desc_t) {
.type = type,
.pixel_buffer = gfx_framebuffer(),
.pixel_buffer_size = gfx_framebuffer_size(),
.audio_cb = push_audio,
.audio_sample_rate = saudio_sample_rate(),
.patch_cb = patch_snapshots,
.rom_caos22 = dump_caos22_852,
.rom_caos22_size = sizeof(dump_caos22_852),
.rom_caos31 = dump_caos31_853,
.rom_caos31_size = sizeof(dump_caos31_853),
.rom_caos42c = dump_caos42c_854,
.rom_caos42c_size = sizeof(dump_caos42c_854),
.rom_caos42e = dump_caos42e_854,
.rom_caos42e_size = sizeof(dump_caos42e_854),
.rom_kcbasic = dump_basic_c0_853,
.rom_kcbasic_size = sizeof(dump_basic_c0_853)
};
}
void app_init(void) {
gfx_init(&(gfx_desc_t) {
#ifdef CHIPS_USE_UI
.draw_extra_cb = ui_draw,
#endif
.top_offset = ui_extra_height
});
keybuf_init(10);
clock_init();
saudio_setup(&(saudio_desc){0});
fs_init();
/* specific KC85 model? */
kc85_type_t type = KC85_TYPE_2;
if (sargs_exists("type")) {
if (sargs_equals("type", "kc85_3")) {
type = KC85_TYPE_3;
}
else if (sargs_equals("type", "kc85_4")) {
type = KC85_TYPE_4;
}
}
/* initialize the KC85 emulator */
kc85_desc_t desc = kc85_desc(type);
kc85_init(&kc85, &desc);
#ifdef CHIPS_USE_UI
kc85ui_init(&kc85);
#endif
bool delay_input = false;
/* snapshot file or rom-module image */
if (sargs_exists("file")) {
delay_input=true;
if (!fs_load_file(sargs_value("file"))) {
gfx_flash_error();
}
}
else if (sargs_exists("mod_image")) {
if (!fs_load_file(sargs_value("mod_image"))) {
gfx_flash_error();
}
}
/* check if any modules should be inserted */
if (sargs_exists("mod")) {
/* RAM modules can be inserted immediately, ROM modules
only after the ROM image has been loaded
*/
if (sargs_equals("mod", "m022")) {
kc85_insert_ram_module(&kc85, 0x08, KC85_MODULE_M022_16KBYTE);
}
else if (sargs_equals("mod", "m011")) {
kc85_insert_ram_module(&kc85, 0x08, KC85_MODULE_M011_64KBYE);
}
else {
/* a ROM module */
delay_input = true;
if (sargs_equals("mod", "m006")) {
delay_insert_module = KC85_MODULE_M006_BASIC;
}
else if (sargs_equals("mod", "m012")) {
delay_insert_module = KC85_MODULE_M012_TEXOR;
}
else if (sargs_equals("mod", "m026")) {
delay_insert_module = KC85_MODULE_M026_FORTH;
}
else if (sargs_equals("mod", "m027")) {
delay_insert_module = KC85_MODULE_M027_DEVELOPMENT;
}
}
}
/* keyboard input to send to emulator */
if (!delay_input) {
if (sargs_exists("input")) {
keybuf_put(sargs_value("input"));
}
}
}
void app_frame(void) {
const uint32_t frame_time = clock_frame_time();
#if CHIPS_USE_UI
kc85ui_exec(&kc85, frame_time);
#else
kc85_exec(&kc85, frame_time);
#endif
gfx_draw(kc85_display_width(&kc85), kc85_display_height(&kc85));
const uint32_t load_delay_frames = kc85.type == KC85_TYPE_4 ? 180 : 480;
if (fs_ptr() && clock_frame_count_60hz() > load_delay_frames) {
bool load_success = false;
if (sargs_exists("mod_image")) {
/* insert the rom module */
if (delay_insert_module != KC85_MODULE_NONE) {
load_success = kc85_insert_rom_module(&kc85, 0x08, delay_insert_module, fs_ptr(), fs_size());
}
}
else if (fs_ext("txt") || fs_ext("bas")) {
load_success = true;
keybuf_put((const char*)fs_ptr());
}
else {
load_success = kc85_quickload(&kc85, fs_ptr(), fs_size());
}
if (load_success) {
if (clock_frame_count_60hz() > (load_delay_frames + 10)) {
gfx_flash_success();
}
if (sargs_exists("input")) {
keybuf_put(sargs_value("input"));
}
}
else {
gfx_flash_error();
}
fs_free();
}
uint8_t key_code;
if (0 != (key_code = keybuf_get(frame_time))) {
kc85_key_down(&kc85, key_code);
kc85_key_up(&kc85, key_code);
}
}
void app_input(const sapp_event* event) {
#ifdef CHIPS_USE_UI
if (ui_input(event)) {
/* input was handled by UI */
return;
}
#endif
const bool shift = event->modifiers & SAPP_MODIFIER_SHIFT;
switch (event->type) {
int c;
case SAPP_EVENTTYPE_CHAR:
c = (int) event->char_code;
if ((c > 0x20) && (c < 0x7F)) {
/* need to invert case (unshifted is upper caps, shifted is lower caps */
if (isupper(c)) {
c = tolower(c);
}
else if (islower(c)) {
c = toupper(c);
}
kc85_key_down(&kc85, c);
kc85_key_up(&kc85, c);
}
break;
case SAPP_EVENTTYPE_KEY_DOWN:
case SAPP_EVENTTYPE_KEY_UP:
switch (event->key_code) {
case SAPP_KEYCODE_SPACE: c = shift ? 0x5B : 0x20; break; /* 0x5B: neg space, 0x20: space */
case SAPP_KEYCODE_ENTER: c = 0x0D; break;
case SAPP_KEYCODE_RIGHT: c = 0x09; break;
case SAPP_KEYCODE_LEFT: c = 0x08; break;
case SAPP_KEYCODE_DOWN: c = 0x0A; break;
case SAPP_KEYCODE_UP: c = 0x0B; break;
case SAPP_KEYCODE_HOME: c = 0x10; break;
case SAPP_KEYCODE_INSERT: c = shift ? 0x0C : 0x1A; break; /* 0x0C: cls, 0x1A: ins */
case SAPP_KEYCODE_BACKSPACE: c = shift ? 0x0C : 0x01; break; /* 0x0C: cls, 0x01: del */
case SAPP_KEYCODE_ESCAPE: c = shift ? 0x13 : 0x03; break; /* 0x13: stop, 0x03: brk */
case SAPP_KEYCODE_F1: c = 0xF1; break;
case SAPP_KEYCODE_F2: c = 0xF2; break;
case SAPP_KEYCODE_F3: c = 0xF3; break;
case SAPP_KEYCODE_F4: c = 0xF4; break;
case SAPP_KEYCODE_F5: c = 0xF5; break;
case SAPP_KEYCODE_F6: c = 0xF6; break;
case SAPP_KEYCODE_F7: c = 0xF7; break;
case SAPP_KEYCODE_F8: c = 0xF8; break;
case SAPP_KEYCODE_F9: c = 0xF9; break;
case SAPP_KEYCODE_F10: c = 0xFA; break;
case SAPP_KEYCODE_F11: c = 0xFB; break;
case SAPP_KEYCODE_F12: c = 0xFC; break;
default: c = 0; break;
}
if (c) {
if (event->type == SAPP_EVENTTYPE_KEY_DOWN) {
kc85_key_down(&kc85, c);
}
else {
kc85_key_up(&kc85, c);
}
}
break;
case SAPP_EVENTTYPE_TOUCHES_BEGAN:
sapp_show_keyboard(true);
break;
default:
break;
}
}
void app_cleanup(void) {
kc85_discard(&kc85);
#ifdef CHIPS_USE_UI
kc85ui_discard();
#endif
saudio_shutdown();
gfx_shutdown();
sargs_shutdown();
}