Cleaned up a bit
[virtual-nascom.git] / sdl-nascom.c
blobe8982a9579e141aefeb5cc44d12809d2d5dcdd99
1 /* Virtual Nascom, 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 extern uint8_t nascom_font_raw[];
55 static SDL_Surface *screen;
56 static struct font {
57 SDL_Surface *surf;
58 int w, h, h_pitch;
59 } nascom_font;
61 FILE *serial_out, *serial_in;
62 int tape_led = 0;
63 int serial_input_available = 0;
65 static unsigned framebuffer_generation;
67 static void RenderItem(struct font *font, int idx, int x, int y)
69 auto SDL_Rect dest = { x, y, font->w, font->h };
70 SDL_Rect clip = { 0, idx * font->h_pitch, font->w, font->h };
71 SDL_BlitSurface(font->surf, &clip, screen, &dest);
74 void RenderLetters(struct font *font, char *s, int x, int y)
76 for (; *s; ++s, x += font->w)
77 RenderItem(font, *s, x, y);
80 int mysetup(int argc, char **argv)
82 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
83 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
84 return 1;
88 atexit(SDL_Quit);
91 screen = SDL_SetVideoMode(48 * FONT_W, 16 * FONT_H, 8, SDL_SWSURFACE);
92 if (screen == NULL) {
93 fprintf(stderr, "Unable to set video: %s\n", SDL_GetError());
94 return 1;
97 /* Set the window caption */
98 SDL_WM_SetCaption("Nascom II", "Nascom II");
100 #if 0
101 /* Populate the palette */
102 SDL_Color colors[256];
104 colors[0].r = colors[0].g = colors[0].b = 0;
105 colors[255].r = colors[255].b = 0;
106 colors[255].g = 255;
108 /* Set palette */
109 if (!SDL_SetColors(screen, colors, 0, 256)) {
110 fprintf(stderr, "Unable to create framebuffer palette: %s\n",
111 SDL_GetError());
112 screen = 0; //XXX should free it
113 return 1;
115 #endif
117 /* Load font */
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 perror("Couldn't load the 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 void load_nascom(const char *file)
164 FILE *f = fopen(file, "r");
165 int a, b1, b2, b3, b4, b5, b6, b7, b8;
166 int count = 0;
167 int ch;
169 if (!f) {
170 perror(file);
171 exit(1);
174 if (vflag)
175 printf("Loading %s", file);
177 for (; !feof(f) ;) {
178 if (fscanf(f, "%x %x %x %x %x %x %x %x %x",
179 &a, &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8) == 9) {
180 RAM(a) = b1;
181 RAM(a+1) = b2;
182 RAM(a+2) = b3;
183 RAM(a+3) = b4;
184 RAM(a+4) = b5;
185 RAM(a+5) = b6;
186 RAM(a+6) = b7;
187 RAM(a+7) = b8;
188 count += 8;
192 ch = fgetc(f);
193 while (ch != -1 && ch != '\n');
195 if (ch == -1)
196 break;
199 fclose(f);
200 if (vflag)
201 printf(". Successfully loaded %d bytes\n", count);
203 if (count == 2048) {
204 FILE *f;
205 f = fopen("blob", "w");
206 fwrite((const void *) ram, 1, 2048, f);
207 fclose(f);
211 static char * kbd_translation[] = {
212 /* 0 */ "xxzzzxxx",
213 /* 1 */ "xzTXF5BH",
214 /* 2 */ "xzYZD6NJ",
215 /* 3 */ "xzUSE7MK",
216 /* 4 */ "xzIAW8,L",
217 /* 5 */ "xzOQ39.;",
218 /* 6 */ "x[P120/'",
219 /* 7 */ "x]R C4VG",
220 /* 8 */ "xzxxx-\r\010"
223 int reset = 0;
225 void mainloop(void)
227 int i = -1, bit = 0;
228 unsigned last_generation = 0;
230 for (;;) {
231 SDL_Event event;
233 while (SDL_PollEvent(&event)) {
234 switch (event.type) {
235 case SDL_MOUSEMOTION:
236 /*printf("Mouse moved by %d,%d to (%d,%d)\n",
237 event.motion.xrel, event.motion.yrel,
238 event.motion.x, event.motion.y);*/
239 break;
240 case SDL_MOUSEBUTTONDOWN:
241 /*printf("Mouse button %d pressed at (%d,%d)\n",
242 event.button.button, event.button.x, event.button.y);*/
243 break;
244 case SDL_KEYDOWN:
245 case SDL_KEYUP:
246 if (event.key.keysym.sym == '\\' && event.type == SDL_KEYDOWN) {
247 reset = 1;
248 break;
251 if (event.key.keysym.sym == 27)
252 exit(0);
254 if (event.key.keysym.sym < 128) {
255 int ch = toupper(event.key.keysym.sym);
256 for (i = 0; i < 9; ++i)
257 for (bit = 0; bit < 8; ++bit)
258 if (kbd_translation[i][7-bit] == ch) {
259 goto found;
261 i = -1;
262 found:;
263 } else {
264 switch (event.key.keysym.sym) {
265 case SDLK_LCTRL: i = 0, bit = 3; break;
266 case SDLK_LSHIFT:
267 case SDLK_RSHIFT: i = 0, bit = 4; break;
268 case SDLK_RCTRL: i = 0, bit = 5; break;
269 case SDLK_UP: i = 1, bit = 6; break;
270 case SDLK_LEFT: i = 2, bit = 6; break;
271 case SDLK_DOWN: i = 3, bit = 6; break;
272 case SDLK_RIGHT: i = 4, bit = 6; break;
273 case SDLK_RMETA:
274 case SDLK_LMETA:
275 case SDLK_RALT:
276 case SDLK_LALT: i = 5, bit = 6; break;
277 case SDLK_KP_ENTER:i = 8, bit = 6; break;
278 case SDLK_END: {
279 /* Undocumented hack */
280 FILE *f;
281 f = fopen("screendump", "w");
282 fwrite((const void *) (ram+0x800), 1, 1024, f);
283 fclose(f);
284 if (vflag) printf("Screen dumped\n");
285 break;
287 default:
288 //printf("%d? ", event.key.keysym.sym);
289 //printf(" keysym %s\n", SDL_GetKeyName(event.key.keysym.sym));
294 if (i != -1) {
295 if (event.type == SDL_KEYDOWN)
296 keym[i] |= 1 << bit;
297 else
298 keym[i] &= ~(1 << bit);
300 break;
301 case SDL_QUIT:
302 //printf("Quit\n");
303 return;
304 default:
305 //printf("Unknown event: %d\n", event.type);
306 break;
310 /* Only update the screen if the framebuffer has been written
311 since last update */
312 if (last_generation != framebuffer_generation) {
313 int x, y;
314 unsigned p = 0x800 + 10;
315 last_generation = framebuffer_generation;
317 for (y = 1; y < 16; ++y, p += 64) {
318 for (x = 0; x < 48; ++x)
319 RenderItem(&nascom_font, RAM(p + x), x * FONT_W, y * FONT_H);
322 // Nascom is strange in that the last line is the first line!
323 for (x = 0; x < 48; ++x)
324 RenderItem(&nascom_font, RAM(p + x), x * FONT_W, 0);
326 SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
327 // SDL_Flip(screen); either seem to work
330 SDL_Delay(1000 / 30); // 30 fps
334 int sim_delay()
336 if (reset) {
337 reset = 0;
338 return 1;
341 SDL_Delay(1);
343 return 0;
346 void simulate(void *dummy)
348 simz80(pc, 900, sim_delay);
351 static void
352 usage(void)
354 fprintf(stderr,
355 "Usage: %s {flags} {commands}\n"
356 " -i <file> take serial port input from file (when tape led is on)\n"
357 " -m <file> use <file> as monitor (default is nassys3.nal)\n"
358 " -v verbose\n"
359 ,progname);
360 exit (1);
363 int main(int argc, char **argv)
365 int c;
367 serial_out = fopen("serialout.txt", "w+");
369 if (!serial_out)
370 exit(3);
372 if (mysetup(argc, argv))
373 return 1;
375 monitor = "nassys3.nal";
376 progname = argv[0];
379 #ifdef MMU
380 for (c=0; c<MEMSIZE/4; ++c) pagetable[c]=ram+(c<<12);
381 #endif
383 while ((c = getopt(argc, argv, "i:m:v")) != EOF)
384 switch (c) {
385 case 'i':
386 serial_in = fopen(optarg, "r");
387 //printf("serial input %s -> %p\n", optarg, serial_in);
388 serial_input_available = !feof(serial_in);
389 break;
390 case 'm':
391 monitor = optarg;
392 break;
393 case 'v':
394 vflag = 1;
395 break;
396 case '?':
397 usage();
400 if (vflag)
401 puts("Virtual Nascom, a Nascom 2 emulator version " VERSION "\n"
402 "Copyright 2000,2009 Tommy Thorn.\n"
403 "Uses software from \n"
404 "Yet Another Z80 Emulator version " YAZEVERSION
405 ", Copyright 1995,1998 Frank D. Cringle.\n"
406 "VirtualNascom comes with ABSOLUTELY NO WARRANTY; for details\n"
407 "see the file \"COPYING\" in the distribution directory.\n");
409 load_nascom(monitor);
410 load_nascom("basic.nal");
412 for (; optind < argc; optind++)
413 load_nascom(argv[optind]);
415 SDL_CreateThread((int (*)(void *))simulate, NULL);
416 mainloop();
418 exit(0);
422 * 1.7 Input/output port addressing
424 * Output Bit
425 * P0 7 Not available 7 Unused
426 * 6 Not used 6 Keyboard S6
427 * 5 Unused 5 Keyboard S3
428 * 4 Tape drive LED 4 Keyboard S5
429 * 3 Single step 3 Keyboard S4
430 * 2 Unused 2 Keyboard S0
431 * 1 Reset keyb'd count 1 Keyboard S2
432 * 0 Clock keyb'd count 0 Keyboard S1
435 #define P0_OUT_TAPE_DRIVE_LED 16
436 #define P0_OUT_SINGLE_STEP 8
437 #define P0_OUT_KEYBOARD_RESET 2
438 #define P0_OUT_KEYBOARD_CLOCK 1
441 * P1 0 - 7 Data to UART 0 - 7 Data from UART
442 * (Serial port) (Serial port)
444 * P2 0 - 7 Not assigned 7 Data received from UART
445 * 6 UART TBR empty
446 * 5 Not assigned
447 * 4 Not assigned
448 * 3 F error on UART
449 * 2 P error on UART
450 * 1 O error on UART
451 * 0 Not assigned
454 #define UART_DATA_READY 128
455 #define UART_TBR_EMPTY 64
456 #define UART_F_ERROR 8
457 #define UART_P_ERROR 4
458 #define UART_O_ERROR 2
461 * P3 Not assigned Not assigned
463 * P4 PIO port A data input and output
465 * P5 PIO port B data input and output
467 * P6 PIO port A control
469 * P7 PIO port B control
472 void out(unsigned int port, unsigned char value)
474 unsigned int down_trans;
476 if (0) fprintf(stdout, "[%02x] <- %02x\n", port, value);
478 switch (port) {
479 case 0:
480 /* KBD */
481 down_trans = port0 & ~value;
482 port0 = value;
484 if ((down_trans & P0_OUT_KEYBOARD_CLOCK) && keyp < 9)
485 keyp++;
486 if (down_trans & P0_OUT_KEYBOARD_RESET)
487 keyp = 0;
489 #if 0
490 if (tape_led != !!(value & P0_OUT_TAPE_DRIVE_LED))
491 fprintf(stderr, "Tape LED = %d\n", !!(value & P0_OUT_TAPE_DRIVE_LED));
492 #endif
493 tape_led = !!(value & P0_OUT_TAPE_DRIVE_LED);
494 break;
496 case 1:
497 fputc(value, serial_out);
498 break;
500 default:
501 fprintf(stdout, "OUT [%02x] <- %02x\n", port, value);
505 int in(unsigned int port)
507 if (0) fprintf(stdout, "<- [%02x]\n", port);
509 switch (port) {
510 case 0:
511 /* KBD */
512 /* printf("[%d]", keyp); */
513 return ~keym[keyp];
514 case 1:
515 if (serial_input_available & tape_led) {
516 char ch = fgetc(serial_in);
517 serial_input_available = !feof(serial_in);
518 return ch;
520 else
521 return 0;
522 case 2:
523 /* Status port on the UART */
524 return UART_TBR_EMPTY |
525 (serial_input_available & tape_led ? UART_DATA_READY : 0);
526 default:
527 fprintf(stdout, "IN <- [%02x]\n", port);
528 return 0;
532 void slow_write(unsigned int a, unsigned char v)
534 if (INSCREEN(a)) {
535 unsigned int y = (a-0x800) / 64;
536 unsigned int x = (a-0x800) % 64;
537 /* fprintf(stdout, "putbyte %04x %02x '%c'\n", a, v, v); */
538 if (10 <= x && x < 58 && ' ' <= v) {
539 if (y == 15)
540 y = 0;
541 else
542 ++y;
544 //xputch(x-10, y, v);
545 //fprintf(stderr, "\033[%d;%dH%c", 1+y, 1+x-10, v);
546 framebuffer_generation++;
550 if (0x800 <= a && a <= 0xE000)
551 RAM(a) = v;