Better IO support, notably serial in- and output
[virtual-nascom.git] / sdl-nascom.c
blob8ba4cce45434d5449ca53f50de81eb769030bc46
1 /* VirtualNascom, a Nascom II emulator.
3 Copyright (C) 2000,2009 Tommy Thorn
5 Z80 emulator portition Copyright (C) 1995,1998 Frank D. Cringle.
7 NasEmu is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
23 A Nascom consists of:
25 - a Z80 CPU,
26 - an UART,
27 - a bitmapped keyboard,
28 - memory:
29 0000 - 07ff 2 KB ROM monitor,
30 0800 - 0bff 1 KB screen memory,
31 0c00 - 0fff 1 KB workspace
32 1000 - dfff memory
33 e000 - ffff 8 KB of MS Basic
35 With the Z80 emulator in place the first thing to get working is the
36 screen memory. The "correct" way to simulate screen memory is to
37 trap upon writes, but that would be slow. We do it any just to get
38 started.
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <getopt.h>
44 #include <ctype.h>
45 #include "simz80.h"
46 #include "nascom.h"
47 #include <SDL.h>
49 #define FONT_H_PITCH 16
50 #define FONT_H 15
51 #define FONT_W 8
53 static SDL_Surface *screen;
54 static struct font {
55 SDL_Surface *surf;
56 int w, h, h_pitch;
57 } nascom_font;
59 FILE *serial_out, *serial_in;
60 int tape_led;
62 static unsigned framebuffer_generation;
64 static void RenderItem(struct font *font, int index, int x, int y)
66 SDL_Rect dest = { x, y, font->w, font->h };
67 SDL_Rect clip = { 0, index * font->h_pitch, font->w, font->h };
68 SDL_BlitSurface(font->surf, &clip, screen, &dest);
71 void RenderLetters(struct font *font, char *s, int x, int y)
73 for (; *s; ++s, x += font->w)
74 RenderItem(font, *s, x, y);
77 int mysetup(int argc, char **argv)
79 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
80 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
81 return 1;
85 atexit(SDL_Quit);
88 screen = SDL_SetVideoMode(48 * FONT_W, 16 * FONT_H, 8, SDL_SWSURFACE);
89 if (screen == NULL) {
90 fprintf(stderr, "Unable to set video: %s\n", SDL_GetError());
91 return 1;
94 /* Set the window caption */
95 SDL_WM_SetCaption("Nascom II", "Nascom II");
97 #if 0
98 /* Populate the palette */
99 SDL_Color colors[256];
101 colors[0].r = colors[0].g = colors[0].b = 0;
102 colors[255].r = colors[255].b = 0;
103 colors[255].g = 255;
105 /* Set palette */
106 if (!SDL_SetColors(screen, colors, 0, 256)) {
107 fprintf(stderr, "Unable to create framebuffer palette: %s\n",
108 SDL_GetError());
109 screen = 0; //XXX should free it
110 return 1;
112 #endif
114 /* Load font */
115 extern uint8_t nascom_font_raw[];
117 // Unsuccessful :-(
118 nascom_font.surf =
119 SDL_CreateRGBSurfaceFrom(
120 nascom_font_raw,
121 8 /* width */,
122 256*16 /* height */,
123 1 /* depth */,
124 1 /* pitch */,
125 0 /* Rmask */,
126 1 /* Gmask */,
127 0 /* Bmask */,
128 0 /* Amask */);
129 nascom_font.w = FONT_W;
130 nascom_font.h = FONT_H;
131 nascom_font.h_pitch = FONT_H_PITCH;
133 if (!nascom_font.surf) {
134 fprintf(stderr, "no font :-( \n");
135 return 1;
138 nascom_font.surf = SDL_DisplayFormat(nascom_font.surf);
140 return 0;
145 /* */
147 unsigned char keym[9] = {
148 0, /* ? ? ? Shift ? ? ? ? */
149 0, /* ?!TXF5BH ! = Up*/
150 0, /* ?!YZD6NJ ! = Left*/
151 0, /* ?!USE7MK ! = Down */
152 0, /* ?!IAW8,L ! = Right */
153 0, /* ??OQ39.; */
154 0, /* ?[P120/: */
155 0, /* ?]R C4VG */
156 0 /* ? ? CR - Newline BS */
159 unsigned char keyp = 0;
160 unsigned char port0;
162 static void
163 usage(void)
165 fprintf(stderr,
166 "Usage: %s {flags} {commands}\n"
167 " -m <file> use <file> as monitor (default is nassys3.nal)\n"
168 " -v verbose\n"
169 ,progname);
170 exit (1);
173 void load_nascom(const char *file)
175 FILE *f = fopen(file, "r");
176 int a, b1, b2, b3, b4, b5, b6, b7, b8;
177 int count = 0;
178 int ch;
180 if (!f) {
181 perror(file);
182 exit(1);
185 if (vflag)
186 printf("Loading %s", file);
188 for (; !feof(f) ;) {
189 if (fscanf(f, "%x %x %x %x %x %x %x %x %x",
190 &a, &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8) == 9) {
191 RAM(a) = b1;
192 RAM(a+1) = b2;
193 RAM(a+2) = b3;
194 RAM(a+3) = b4;
195 RAM(a+4) = b5;
196 RAM(a+5) = b6;
197 RAM(a+6) = b7;
198 RAM(a+7) = b8;
199 count += 8;
203 ch = fgetc(f);
204 while (ch != -1 && ch != '\n');
206 if (ch == -1)
207 break;
210 fclose(f);
211 if (vflag)
212 printf(". Successfully loaded %d bytes\n", count);
214 if (count == 2048) {
215 FILE *f;
216 f = fopen("blob", "w");
217 fwrite((const void *) ram, 1, 2048, f);
218 fclose(f);
222 static char * kbd_translation[] = {
223 /* 0 */ "xxzzzxxx",
224 /* 1 */ "xzTXF5BH",
225 /* 2 */ "xzYZD6NJ",
226 /* 3 */ "xzUSE7MK",
227 /* 4 */ "xzIAW8,L",
228 /* 5 */ "xzOQ39.;",
229 /* 6 */ "x[P120/'",
230 /* 7 */ "x]R C4VG",
231 /* 8 */ "xzxxx-\r\010"
234 int reset = 0;
236 void mainloop(void)
238 int i = -1, bit = 0;
239 unsigned last_generation = 0;
241 for (;;) {
242 SDL_Event event;
244 while (SDL_PollEvent(&event)) {
245 switch (event.type) {
246 case SDL_MOUSEMOTION:
247 /*printf("Mouse moved by %d,%d to (%d,%d)\n",
248 event.motion.xrel, event.motion.yrel,
249 event.motion.x, event.motion.y);*/
250 break;
251 case SDL_MOUSEBUTTONDOWN:
252 /*printf("Mouse button %d pressed at (%d,%d)\n",
253 event.button.button, event.button.x, event.button.y);*/
254 break;
255 case SDL_KEYDOWN:
256 case SDL_KEYUP:
257 if (event.key.keysym.sym == '\\' && event.type == SDL_KEYDOWN) {
258 reset = 1;
259 break;
262 if (event.key.keysym.sym == 27)
263 exit(0);
265 if (event.key.keysym.sym < 128) {
266 int ch = toupper(event.key.keysym.sym);
267 for (i = 0; i < 9; ++i)
268 for (bit = 0; bit < 8; ++bit)
269 if (kbd_translation[i][7-bit] == ch) {
270 //printf(" -> %d/%d", i, bit);
271 goto found;
273 i = -1;
274 //printf("%d?\n", event.key.keysym.sym);
275 found:;
276 //printf("\n");
277 } else {
278 switch (event.key.keysym.sym) {
279 case SDLK_LCTRL: i = 0, bit = 3; break;
280 case SDLK_LSHIFT:
281 case SDLK_RSHIFT: i = 0, bit = 4; break;
282 case SDLK_RCTRL: i = 0, bit = 5; break;
283 case SDLK_UP: i = 1, bit = 6; break;
284 case SDLK_LEFT: i = 2, bit = 6; break;
285 case SDLK_DOWN: i = 3, bit = 6; break;
286 case SDLK_RIGHT: i = 4, bit = 6; break;
287 case SDLK_RMETA:
288 case SDLK_LMETA:
289 case SDLK_RALT:
290 case SDLK_LALT: i = 5, bit = 6; break;
291 case SDLK_KP_ENTER:i = 8, bit = 6; break;
292 case SDLK_END: {
293 /* Undocumented hack */
294 FILE *f;
295 f = fopen("screendump", "w");
296 fwrite((const void *) (ram+0x800), 1, 1024, f);
297 fclose(f);
298 if (vflag) printf("Screen dumped\n");
299 break;
301 default:
302 //printf("%d? ", event.key.keysym.sym);
303 //printf(" keysym %s\n", SDL_GetKeyName(event.key.keysym.sym));
308 if (i != -1) {
309 if (event.type == SDL_KEYDOWN)
310 keym[i] |= 1 << bit;
311 else
312 keym[i] &= ~(1 << bit);
314 break;
315 case SDL_QUIT:
316 //printf("Quit\n");
317 return;
318 default:
319 //printf("Unknown event: %d\n", event.type);
320 break;
324 /* Only update the screen if the framebuffer has been written
325 since last update */
326 if (last_generation != framebuffer_generation) {
327 int x, y;
328 unsigned p = 0x800 + 10;
329 last_generation = framebuffer_generation;
331 for (y = 1; y < 16; ++y, p += 64) {
332 for (x = 0; x < 48; ++x)
333 RenderItem(&nascom_font, RAM(p + x), x * FONT_W, y * FONT_H);
336 // Nascom is strange in that the last line is the first line!
337 for (x = 0; x < 48; ++x)
338 RenderItem(&nascom_font, RAM(p + x), x * FONT_W, 0);
340 SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
341 // SDL_Flip(screen); either seem to work
344 SDL_Delay(1000 / 30); // 30 fps
348 int sim_delay()
350 if (reset) {
351 reset = 0;
352 return 1;
355 SDL_Delay(2);
357 return 0;
360 void simulate(void *dummy)
362 simz80(pc, 1000, sim_delay);
365 int main(int argc, char **argv)
367 int c;
369 serial_out = fopen("serialout.txt", "w+");
371 if (!serial_out)
372 exit(3);
374 if (mysetup(argc, argv))
375 return 1;
377 monitor = "nassys3.nal";
378 progname = argv[0];
381 #ifdef MMU
382 for (c=0; c<MEMSIZE/4; ++c) pagetable[c]=ram+(c<<12);
383 #endif
385 while ((c = getopt(argc, argv, "i:m:v")) != EOF)
386 switch (c) {
387 case 'i':
388 serial_in = fopen(optarg, "r");
389 printf("serial input %s -> %p\n", optarg, serial_in);
390 break;
391 case 'm':
392 monitor = optarg;
393 break;
394 case 'v':
395 vflag = 1;
396 break;
397 case '?':
398 usage();
401 if (vflag)
402 puts("VirtualNascom, a Nascom 2 emulator version " VERSION "\n"
403 "Copyright 2000,2009 Tommy Thorn.\n"
404 "Uses software from \n"
405 "Yet Another Z80 Emulator version " YAZEVERSION
406 ", Copyright 1995,1998 Frank D. Cringle.\n"
407 "VirtualNascom comes with ABSOLUTELY NO WARRANTY; for details\n"
408 "see the file \"COPYING\" in the distribution directory.\n");
410 load_nascom(monitor);
411 load_nascom("basic.nal");
413 for (; optind < argc; optind++)
414 load_nascom(argv[optind]);
416 SDL_CreateThread((int (*)(void *))simulate, NULL);
417 mainloop();
418 exit(0);
420 fprintf(stderr,"HALT\n");
421 fprintf(stderr,"PC SP IR IX IY AF BC DE HL AF' BC' DE' HL'\n");
422 fprintf(stderr,"%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
423 pc,sp,ir,ix,iy,af[af_sel],regs[regs_sel].bc,regs[regs_sel].de,regs[regs_sel].hl,af[1-af_sel],regs[1-regs_sel].bc,regs[1-regs_sel].de,regs[1-regs_sel].hl);
427 * 1.7 Input/output port addressing
429 * Output Bit
430 * P0 7 Not available 7 Unused
431 * 6 Not used 6 Keyboard S6
432 * 5 Unused 5 Keyboard S3
433 * 4 Tape drive LED 4 Keyboard S5
434 * 3 Single step 3 Keyboard S4
435 * 2 Unused 2 Keyboard S0
436 * 1 Reset keyb'd count 1 Keyboard S2
437 * 0 Clock keyb'd count 0 Keyboard S1
440 #define P0_OUT_TAPE_DRIVE_LED 16
441 #define P0_OUT_SINGLE_STEP 8
442 #define P0_OUT_KEYBOARD_RESET 2
443 #define P0_OUT_KEYBOARD_CLOCK 1
446 * P1 0 - 7 Data to UART 0 - 7 Data from UART
447 * (Serial port) (Serial port)
449 * P2 0 - 7 Not assigned 7 Data received from UART
450 * 6 UART TBR empty
451 * 5 Not assigned
452 * 4 Not assigned
453 * 3 F error on UART
454 * 2 P error on UART
455 * 1 O error on UART
456 * 0 Not assigned
459 #define UART_DATA_READY 128
460 #define UART_TBR_EMPTY 64
461 #define UART_F_ERROR 8
462 #define UART_P_ERROR 4
463 #define UART_O_ERROR 2
466 * P3 Not assigned Not assigned
468 * P4 PIO port A data input and output
470 * P5 PIO port B data input and output
472 * P6 PIO port A control
474 * P7 PIO port B control
477 void out(unsigned int port, unsigned char value)
479 unsigned int down_trans;
481 if (0) fprintf(stdout, "[%02x] <- %02x\n", port, value);
483 switch (port) {
484 case 0:
485 /* KBD */
486 down_trans = port0 & ~value;
487 port0 = value;
489 if ((down_trans & P0_OUT_KEYBOARD_CLOCK) && keyp < 9)
490 keyp++;
491 if (down_trans & P0_OUT_KEYBOARD_RESET)
492 keyp = 0;
494 if (tape_led != !!(value & P0_OUT_TAPE_DRIVE_LED))
495 fprintf(stderr, "Tape LED = %d\n", !!(value & P0_OUT_TAPE_DRIVE_LED));
496 tape_led = !!(value & P0_OUT_TAPE_DRIVE_LED);
497 break;
499 case 1:
500 fputc(value, serial_out);
501 break;
503 default:
504 fprintf(stdout, "OUT [%02x] <- %02x\n", port, value);
508 int in(unsigned int port)
510 if (0) fprintf(stdout, "<- [%02x]\n", port);
512 switch (port) {
513 case 0:
514 /* KBD */
515 /* printf("[%d]", keyp); */
516 return ~keym[keyp];
517 case 1:
518 if (tape_led && serial_in && !feof(serial_in))
519 return fgetc(serial_in);
520 else
521 return 0;
522 case 2:
523 /* Status port on the UART */
524 return UART_TBR_EMPTY |
525 (tape_led && serial_in && !feof(serial_in) ?
526 UART_DATA_READY : 0);
527 default:
528 fprintf(stdout, "IN <- [%02x]\n", port);
529 return 0;
533 void slow_write(unsigned int a, unsigned char v)
535 if (INSCREEN(a)) {
536 unsigned int y = (a-0x800) / 64;
537 unsigned int x = (a-0x800) % 64;
538 /* fprintf(stdout, "putbyte %04x %02x '%c'\n", a, v, v); */
539 if (10 <= x && x < 58 && ' ' <= v) {
540 if (y == 15)
541 y = 0;
542 else
543 ++y;
545 //xputch(x-10, y, v);
546 //fprintf(stderr, "\033[%d;%dH%c", 1+y, 1+x-10, v);
547 framebuffer_generation++;
551 if (0x800 <= a && a <= 0xE000)
552 RAM(a) = v;