Control serial input with the tape led, adjust speed
[virtual-nascom.git] / sdl-nascom.c
blob389154b8d123ec8772670401f220d3e79d3ea572
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 = 0;
61 int serial_input_available = 0;
63 static unsigned framebuffer_generation;
65 static void RenderItem(struct font *font, int index, int x, int y)
67 SDL_Rect dest = { x, y, font->w, font->h };
68 SDL_Rect clip = { 0, index * font->h_pitch, font->w, font->h };
69 SDL_BlitSurface(font->surf, &clip, screen, &dest);
72 void RenderLetters(struct font *font, char *s, int x, int y)
74 for (; *s; ++s, x += font->w)
75 RenderItem(font, *s, x, y);
78 int mysetup(int argc, char **argv)
80 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
81 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
82 return 1;
86 atexit(SDL_Quit);
89 screen = SDL_SetVideoMode(48 * FONT_W, 16 * FONT_H, 8, SDL_SWSURFACE);
90 if (screen == NULL) {
91 fprintf(stderr, "Unable to set video: %s\n", SDL_GetError());
92 return 1;
95 /* Set the window caption */
96 SDL_WM_SetCaption("Nascom II", "Nascom II");
98 #if 0
99 /* Populate the palette */
100 SDL_Color colors[256];
102 colors[0].r = colors[0].g = colors[0].b = 0;
103 colors[255].r = colors[255].b = 0;
104 colors[255].g = 255;
106 /* Set palette */
107 if (!SDL_SetColors(screen, colors, 0, 256)) {
108 fprintf(stderr, "Unable to create framebuffer palette: %s\n",
109 SDL_GetError());
110 screen = 0; //XXX should free it
111 return 1;
113 #endif
115 /* Load font */
116 extern uint8_t nascom_font_raw[];
118 // Unsuccessful :-(
119 nascom_font.surf =
120 SDL_CreateRGBSurfaceFrom(
121 nascom_font_raw,
122 8 /* width */,
123 256*16 /* height */,
124 1 /* depth */,
125 1 /* pitch */,
126 0 /* Rmask */,
127 1 /* Gmask */,
128 0 /* Bmask */,
129 0 /* Amask */);
130 nascom_font.w = FONT_W;
131 nascom_font.h = FONT_H;
132 nascom_font.h_pitch = FONT_H_PITCH;
134 if (!nascom_font.surf) {
135 fprintf(stderr, "no font :-( \n");
136 return 1;
139 nascom_font.surf = SDL_DisplayFormat(nascom_font.surf);
141 return 0;
146 /* */
148 unsigned char keym[9] = {
149 0, /* ? ? ? Shift ? ? ? ? */
150 0, /* ?!TXF5BH ! = Up*/
151 0, /* ?!YZD6NJ ! = Left*/
152 0, /* ?!USE7MK ! = Down */
153 0, /* ?!IAW8,L ! = Right */
154 0, /* ??OQ39.; */
155 0, /* ?[P120/: */
156 0, /* ?]R C4VG */
157 0 /* ? ? CR - Newline BS */
160 unsigned char keyp = 0;
161 unsigned char port0;
163 static void
164 usage(void)
166 fprintf(stderr,
167 "Usage: %s {flags} {commands}\n"
168 " -m <file> use <file> as monitor (default is nassys3.nal)\n"
169 " -v verbose\n"
170 ,progname);
171 exit (1);
174 void load_nascom(const char *file)
176 FILE *f = fopen(file, "r");
177 int a, b1, b2, b3, b4, b5, b6, b7, b8;
178 int count = 0;
179 int ch;
181 if (!f) {
182 perror(file);
183 exit(1);
186 if (vflag)
187 printf("Loading %s", file);
189 for (; !feof(f) ;) {
190 if (fscanf(f, "%x %x %x %x %x %x %x %x %x",
191 &a, &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8) == 9) {
192 RAM(a) = b1;
193 RAM(a+1) = b2;
194 RAM(a+2) = b3;
195 RAM(a+3) = b4;
196 RAM(a+4) = b5;
197 RAM(a+5) = b6;
198 RAM(a+6) = b7;
199 RAM(a+7) = b8;
200 count += 8;
204 ch = fgetc(f);
205 while (ch != -1 && ch != '\n');
207 if (ch == -1)
208 break;
211 fclose(f);
212 if (vflag)
213 printf(". Successfully loaded %d bytes\n", count);
215 if (count == 2048) {
216 FILE *f;
217 f = fopen("blob", "w");
218 fwrite((const void *) ram, 1, 2048, f);
219 fclose(f);
223 static char * kbd_translation[] = {
224 /* 0 */ "xxzzzxxx",
225 /* 1 */ "xzTXF5BH",
226 /* 2 */ "xzYZD6NJ",
227 /* 3 */ "xzUSE7MK",
228 /* 4 */ "xzIAW8,L",
229 /* 5 */ "xzOQ39.;",
230 /* 6 */ "x[P120/'",
231 /* 7 */ "x]R C4VG",
232 /* 8 */ "xzxxx-\r\010"
235 int reset = 0;
237 void mainloop(void)
239 int i = -1, bit = 0;
240 unsigned last_generation = 0;
242 for (;;) {
243 SDL_Event event;
245 while (SDL_PollEvent(&event)) {
246 switch (event.type) {
247 case SDL_MOUSEMOTION:
248 /*printf("Mouse moved by %d,%d to (%d,%d)\n",
249 event.motion.xrel, event.motion.yrel,
250 event.motion.x, event.motion.y);*/
251 break;
252 case SDL_MOUSEBUTTONDOWN:
253 /*printf("Mouse button %d pressed at (%d,%d)\n",
254 event.button.button, event.button.x, event.button.y);*/
255 break;
256 case SDL_KEYDOWN:
257 case SDL_KEYUP:
258 if (event.key.keysym.sym == '\\' && event.type == SDL_KEYDOWN) {
259 reset = 1;
260 break;
263 if (event.key.keysym.sym == 27)
264 exit(0);
266 if (event.key.keysym.sym < 128) {
267 int ch = toupper(event.key.keysym.sym);
268 for (i = 0; i < 9; ++i)
269 for (bit = 0; bit < 8; ++bit)
270 if (kbd_translation[i][7-bit] == ch) {
271 //printf(" -> %d/%d", i, bit);
272 goto found;
274 i = -1;
275 //printf("%d?\n", event.key.keysym.sym);
276 found:;
277 //printf("\n");
278 } else {
279 switch (event.key.keysym.sym) {
280 case SDLK_LCTRL: i = 0, bit = 3; break;
281 case SDLK_LSHIFT:
282 case SDLK_RSHIFT: i = 0, bit = 4; break;
283 case SDLK_RCTRL: i = 0, bit = 5; break;
284 case SDLK_UP: i = 1, bit = 6; break;
285 case SDLK_LEFT: i = 2, bit = 6; break;
286 case SDLK_DOWN: i = 3, bit = 6; break;
287 case SDLK_RIGHT: i = 4, bit = 6; break;
288 case SDLK_RMETA:
289 case SDLK_LMETA:
290 case SDLK_RALT:
291 case SDLK_LALT: i = 5, bit = 6; break;
292 case SDLK_KP_ENTER:i = 8, bit = 6; break;
293 case SDLK_END: {
294 /* Undocumented hack */
295 FILE *f;
296 f = fopen("screendump", "w");
297 fwrite((const void *) (ram+0x800), 1, 1024, f);
298 fclose(f);
299 if (vflag) printf("Screen dumped\n");
300 break;
302 default:
303 //printf("%d? ", event.key.keysym.sym);
304 //printf(" keysym %s\n", SDL_GetKeyName(event.key.keysym.sym));
309 if (i != -1) {
310 if (event.type == SDL_KEYDOWN)
311 keym[i] |= 1 << bit;
312 else
313 keym[i] &= ~(1 << bit);
315 break;
316 case SDL_QUIT:
317 //printf("Quit\n");
318 return;
319 default:
320 //printf("Unknown event: %d\n", event.type);
321 break;
325 /* Only update the screen if the framebuffer has been written
326 since last update */
327 if (last_generation != framebuffer_generation) {
328 int x, y;
329 unsigned p = 0x800 + 10;
330 last_generation = framebuffer_generation;
332 for (y = 1; y < 16; ++y, p += 64) {
333 for (x = 0; x < 48; ++x)
334 RenderItem(&nascom_font, RAM(p + x), x * FONT_W, y * FONT_H);
337 // Nascom is strange in that the last line is the first line!
338 for (x = 0; x < 48; ++x)
339 RenderItem(&nascom_font, RAM(p + x), x * FONT_W, 0);
341 SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
342 // SDL_Flip(screen); either seem to work
345 SDL_Delay(1000 / 30); // 30 fps
349 int sim_delay()
351 if (reset) {
352 reset = 0;
353 return 1;
356 SDL_Delay(1);
358 return 0;
361 void simulate(void *dummy)
363 simz80(pc, 900, sim_delay);
366 int main(int argc, char **argv)
368 int c;
370 serial_out = fopen("serialout.txt", "w+");
372 if (!serial_out)
373 exit(3);
375 if (mysetup(argc, argv))
376 return 1;
378 monitor = "nassys3.nal";
379 progname = argv[0];
382 #ifdef MMU
383 for (c=0; c<MEMSIZE/4; ++c) pagetable[c]=ram+(c<<12);
384 #endif
386 while ((c = getopt(argc, argv, "i:m:v")) != EOF)
387 switch (c) {
388 case 'i':
389 serial_in = fopen(optarg, "r");
390 //printf("serial input %s -> %p\n", optarg, serial_in);
391 serial_input_available = !feof(serial_in);
392 break;
393 case 'm':
394 monitor = optarg;
395 break;
396 case 'v':
397 vflag = 1;
398 break;
399 case '?':
400 usage();
403 if (vflag)
404 puts("VirtualNascom, a Nascom 2 emulator version " VERSION "\n"
405 "Copyright 2000,2009 Tommy Thorn.\n"
406 "Uses software from \n"
407 "Yet Another Z80 Emulator version " YAZEVERSION
408 ", Copyright 1995,1998 Frank D. Cringle.\n"
409 "VirtualNascom comes with ABSOLUTELY NO WARRANTY; for details\n"
410 "see the file \"COPYING\" in the distribution directory.\n");
412 load_nascom(monitor);
413 load_nascom("basic.nal");
415 for (; optind < argc; optind++)
416 load_nascom(argv[optind]);
418 SDL_CreateThread((int (*)(void *))simulate, NULL);
419 mainloop();
420 exit(0);
422 fprintf(stderr,"HALT\n");
423 fprintf(stderr,"PC SP IR IX IY AF BC DE HL AF' BC' DE' HL'\n");
424 fprintf(stderr,"%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
425 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);
429 * 1.7 Input/output port addressing
431 * Output Bit
432 * P0 7 Not available 7 Unused
433 * 6 Not used 6 Keyboard S6
434 * 5 Unused 5 Keyboard S3
435 * 4 Tape drive LED 4 Keyboard S5
436 * 3 Single step 3 Keyboard S4
437 * 2 Unused 2 Keyboard S0
438 * 1 Reset keyb'd count 1 Keyboard S2
439 * 0 Clock keyb'd count 0 Keyboard S1
442 #define P0_OUT_TAPE_DRIVE_LED 16
443 #define P0_OUT_SINGLE_STEP 8
444 #define P0_OUT_KEYBOARD_RESET 2
445 #define P0_OUT_KEYBOARD_CLOCK 1
448 * P1 0 - 7 Data to UART 0 - 7 Data from UART
449 * (Serial port) (Serial port)
451 * P2 0 - 7 Not assigned 7 Data received from UART
452 * 6 UART TBR empty
453 * 5 Not assigned
454 * 4 Not assigned
455 * 3 F error on UART
456 * 2 P error on UART
457 * 1 O error on UART
458 * 0 Not assigned
461 #define UART_DATA_READY 128
462 #define UART_TBR_EMPTY 64
463 #define UART_F_ERROR 8
464 #define UART_P_ERROR 4
465 #define UART_O_ERROR 2
468 * P3 Not assigned Not assigned
470 * P4 PIO port A data input and output
472 * P5 PIO port B data input and output
474 * P6 PIO port A control
476 * P7 PIO port B control
479 void out(unsigned int port, unsigned char value)
481 unsigned int down_trans;
483 if (0) fprintf(stdout, "[%02x] <- %02x\n", port, value);
485 switch (port) {
486 case 0:
487 /* KBD */
488 down_trans = port0 & ~value;
489 port0 = value;
491 if ((down_trans & P0_OUT_KEYBOARD_CLOCK) && keyp < 9)
492 keyp++;
493 if (down_trans & P0_OUT_KEYBOARD_RESET)
494 keyp = 0;
496 #if 0
497 if (tape_led != !!(value & P0_OUT_TAPE_DRIVE_LED))
498 fprintf(stderr, "Tape LED = %d\n", !!(value & P0_OUT_TAPE_DRIVE_LED));
499 #endif
500 tape_led = !!(value & P0_OUT_TAPE_DRIVE_LED);
501 break;
503 case 1:
504 fputc(value, serial_out);
505 break;
507 default:
508 fprintf(stdout, "OUT [%02x] <- %02x\n", port, value);
512 int in(unsigned int port)
514 if (0) fprintf(stdout, "<- [%02x]\n", port);
516 switch (port) {
517 case 0:
518 /* KBD */
519 /* printf("[%d]", keyp); */
520 return ~keym[keyp];
521 case 1:
522 if (serial_input_available & tape_led) {
523 char ch = fgetc(serial_in);
524 serial_input_available = !feof(serial_in);
525 return ch;
527 else
528 return 0;
529 case 2:
530 /* Status port on the UART */
531 return UART_TBR_EMPTY |
532 (serial_input_available & tape_led ? UART_DATA_READY : 0);
533 default:
534 fprintf(stdout, "IN <- [%02x]\n", port);
535 return 0;
539 void slow_write(unsigned int a, unsigned char v)
541 if (INSCREEN(a)) {
542 unsigned int y = (a-0x800) / 64;
543 unsigned int x = (a-0x800) % 64;
544 /* fprintf(stdout, "putbyte %04x %02x '%c'\n", a, v, v); */
545 if (10 <= x && x < 58 && ' ' <= v) {
546 if (y == 15)
547 y = 0;
548 else
549 ++y;
551 //xputch(x-10, y, v);
552 //fprintf(stderr, "\033[%d;%dH%c", 1+y, 1+x-10, v);
553 framebuffer_generation++;
557 if (0x800 <= a && a <= 0xE000)
558 RAM(a) = v;