1 /* yaze-ag - yet another Z80 emulator by ag.
2 Copyright (C) 1995,1998 Frank D. Cringle.
3 Modifications for CP/M 3.1 Copyright (C) 2000/2004 by Andreas Gerlich (agl)
4
5 Yaze-ag is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free
7 Software Foundation; either version 2 of the License, or (at your
8 option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 /* #include <stdbool.h> */
25
26 #include "ytypes.h"
27
28 #include "mem_mmu.h"
29 #include "simz80.h"
30 #include "yaze.h"
31
32 /* Z80 registers */
33 WORD af[2]; /* accumulator and flags (2 banks) */
34 int af_sel; /* bank select for af */
35
36 struct ddregs regs[2]; /* bc,de,hl */
37 int regs_sel; /* bank select for ddregs */
38
39 WORD ir; /* other Z80 registers */
40 WORD ix;
41 WORD iy;
42 WORD sp;
43 WORD pc;
44 WORD IFF;
45
46 /* definitiions for the brake */
47 FASTREG tStates_save;
48 FASTREG clockFrequency_save = 0; /* default no brake */
49 FASTREG startTime_save;
50 FASTREG tStatesInSlice_save;
51 bool brakeini = true; /* must be true! */
52
53 /* See definitions for memory in mem_mmu.[c,h] (agl) */
54
55 #ifndef LIBDIR
56 #define LIBDIR "/usr/local/lib/yaze"
57 #endif
58
59 #ifdef BOOTSYS
60 static char *bootfile = "yaze-cpm3.boot";
61 #else
62 static char *bootfile = "yaze.boot";
63 #endif
64 static char *startup = ".yazerc";
65 static char *progname;
66 #ifdef BOOTSYS
67 static long loadadr = 0x100;
68 #else
69 static long loadadr = -1;
70 #endif
71 #ifdef BIOS
72 static long basepage = 0xe4;
73 static int vflag;
74 int always128 = 0; /* is set if all drives uses 128 byte sectors */
75 /* only used under CP/M 3.1 */
76 long z3env;
77
78 WORD ccp_base; /* base address of ccp */
79 WORD bdos_base; /* base address of bdos */
80 WORD bios_base; /* base address of bios */
81 WORD bios_top; /* end of bios, start of global work area */
82
83 WORD dirbuff; /* common directory buffer for all disks */
84 WORD dptable; /* base of disk parameter table */
85 BYTE *global_alv; /* global allocation vector */
86 #endif
87
88 extern bool rtc_avail; /* nur zum Testen */
89
90 static void
usage(void)91 usage(void)
92 {
93 fprintf(stderr,
94 "Usage: %s {flags} {commands}\n"
95 " -b <file> boot from this file\n"
96 " -l <xxxx> load bootfile at given (hex) address\n"
97 #ifdef BIOS
98 " -p <xx> base page (top 2 hex digits of ccp base)\n"
99 #endif
100 " -s <file> startup file\n" ,progname);
101
102 #ifdef BIOS
103 fprintf(stderr,
104 " -h <n> screen height (default 25)\n"
105 " -w <n> screen width (default 80)\n"
106 " -v verbose\n"
107 " -z <xxxx> (hex) address of z3env (if desired)\n"
108 " -1 all drives uses 128 byte sectors (under CP/M 3.1)\n"
109 " (normal 2048 byte sectors are used, 128 byte\n"
110 " sectors are necessary to use a disk editor)\n\n");
111 #endif
112
113 exit (1);
114 }
115
116 /* This routine loads the bootstrap file into the simulated processor's ram.
117
118 If a load address is given with the -l flag, the boot file is
119 loaded to that address, the z80 pc is set to it and hl is set to
120 the ccp base address. The bootfile might be a standalone program
121 which ignores the bios vectors or it might contain a
122 self-relocating ccp/bdos (clone), in which case it should relocate
123 itself and jump to the bios cold boot vector.
124
125 Otherwise, the boot file can contain 1 or 2 copies of cp/m. If it
126 contains 1 copy the specified or default basepage must correspond
127 to the run-time location of the cp/m image. If it contains 2
128 copies of the same code assembled to run at different locations,
129 the code can and will be relocated to fit the basepage in use. */
130
131 static void
load_cpm(const char * bfname)132 load_cpm(const char *bfname)
133 {
134 FILE *cpm;
135 #ifdef BIOS
136 int i, skip = 0;
137 WORD offs;
138 struct stat sb;
139 #endif
140
141 if ((cpm = fopen(bfname, "r")) == NULL)
142 {
143 char *p = xmalloc(strlen(bfname) + strlen(LIBDIR) + 1);
144 strcpy(p, LIBDIR);
145 strcat(p, bfname);
146 if ((cpm = fopen(p, "r")) == NULL)
147 free(p);
148 else
149 bfname = p;
150 }
151 if (cpm == NULL)
152 {
153 perror(bfname);
154 exit(1);
155 }
156 if (loadadr >= 0)
157 {
158 fread(ram+loadadr, 1, 64*1024-loadadr, cpm);
159 fclose(cpm);
160 pc = loadadr;
161 #ifdef BIOS
162 regs[regs_sel].hl = ccp_base;
163 #endif
164 printf("Running '%s'\n", bfname);
165 return;
166 }
167 #ifdef BIOS
168 if (fstat(fileno(cpm), &sb) != 0)
169 {
170 perror(bfname);
171 exit(1);
172 }
173 if ((sb.st_size >= CPM_LENGTH+0x880 && sb.st_size < 2*CPM_LENGTH) ||
174 (sb.st_size >= 2*CPM_LENGTH+0x880))
175 {
176 skip = 0x880;
177 fseek(cpm, skip, SEEK_SET);
178 }
179 fread(ram + ccp_base, CPM_LENGTH, 1, cpm);
180 if (sb.st_size < 2*CPM_LENGTH) {
181 /* single, non-relocatable copy */
182 if (ram[ccp_base] == 0xc3 && (ram[ccp_base+2]&0xfc) != basepage)
183 printf("Warning: %s appears to need -p %2x\n", bfname,
184 ram[ccp_base+2]&0xfc);
185 }
186 else
187 {
188 if ((sb.st_size & 1) || ram[ccp_base] != 0xc3)
189 {
190 fprintf(stderr, "%s does not appear to contain 2 cp/m images\n", bfname);
191 exit(1);
192 }
193 fseek(cpm, skip + sb.st_size/2, SEEK_SET);
194 /* this loses if the initial jump points into the 2nd half of ccp */
195 offs = (ccp_base >> 8) - (ram[ccp_base + 2] & 0xfc);
196 for (i = 0; i < CPM_LENGTH; i++)
197 {
198 if ((BYTE) getc(cpm) != ram[ccp_base + i])
199 ram[ccp_base + i] += offs;
200 }
201 }
202 fclose(cpm);
203 printf("Running '%s'\n", bfname);
204 PutBYTE(3, 0x95); /* iobyte */
205 PutBYTE(4, 0); /* disk A: */
206 pc = bios_base;
207 #endif
208 }
209
210 #ifdef BIOS
211 #define N_VEC 17 /* number of bios entry points */
212
213 static void
setvectors(void)214 setvectors(void)
215 {
216 int i;
217 long cpm_brk = 64*1024;
218
219 if (z3env == cpm_brk - 1024) /* z3env at top of memory? */
220 cpm_brk = z3env;
221 cpm_brk -= 128; /* space for directory buffer */
222 dirbuff = cpm_brk;
223 cpm_brk -= 16*(16+15); /* space for 16 disk parameter headers
224 and disk parameter blocks */
225 dptable = cpm_brk;
226
227 if (basepage*256 + CPM_LENGTH + 6*N_VEC >= dptable) {
228 fprintf(stderr, "Not enough space in simulated ram to accommodate base at %02lX\n",
229 basepage);
230 exit(1);
231 }
232
233 for (i = 0; i < N_VEC; i++) { /* build bios jump table */
234 BYTE *p = ram + bios_base + 3*i;
235 p[0] = 0xc3; /* jump */
236 p[1] = 3*(i+N_VEC);
237 p[2] = (bios_base >> 8);
238 p[3*N_VEC] = 0x76; /* halt */
239 p[3*N_VEC+1] = i; /* function code */
240 p[3*N_VEC+2] = 0xc9; /* return */
241 }
242 bios_top = bios_base + 6*N_VEC;
243 i = (dptable - bios_top + 7) / 8;
244 global_alv = xmalloc(i);
245 memclr(global_alv, i);
246 }
247 #endif
248
249 /* getopt declarations */
250 extern int getopt(int argc, char * const argv[],
251 const char *optstring);
252 extern char *optarg;
253 extern int optind, opterr, optopt;
254
255
256 int
main(int argc,char ** argv)257 main(int argc, char **argv)
258 {
259 int
260 c,
261 gostart,
262 term_height = 25,
263 term_width = 80;
264
265 #ifdef BIOS
266 int goopt;
267 #endif
268
269 progname = argv[0];
270
271 /* printf("\033]0;YAZE-AG-%s-%s%s\007",VERSION,DEVVER,BUILD); */
272 printf("\033]0;YAZE-AG-%s%s\007",VERSION,BUILD);
273
274 puts("\nYet Another Z80 Emulator by AG, "
275 "\033[32m"
276
277 "final release " VERSION
278 /* "development v" VERSION "-" DEVVER "_beta" */
279 /* "release candidate " VERSION "-" DEVVER */
280
281 "\033[0m"
282
283 #ifdef UCSD
284 "/UCSD"
285 #endif
286 /*
287 #ifdef CYGWIN
288 "\033[33m"
289 #endif
290 BUILD
291 #ifdef CYGWIN
292 "\033[0m"
293 #endif
294 */
295 /* " (devel version)" */
296 #ifdef MMU
297 " (MMU,KeyTr,WinSz,MHz)"
298 #endif
299 "\r\n"
300 "Copyright 1995,1998 Frank D. Cringle. "
301 "Pagetables Copyright by Michael Haardt.\r\n"
302 "MMU and CP/M 3.1 extensions "
303 "Copyright (c) 2000,2019 by Andreas Gerlich.\r\n"
304 "Keytranslation Copyright (c) 2010,2015 by Jon Saxton\r\n"
305 "yaze-ag comes with ABSOLUTELY NO WARRANTY; for details\r\n"
306 "see the file \"COPYING\" in the distribution directory.\r\n");
307
308 while ((c = getopt(argc, argv, "b:l:s:h:w:"
309 #ifdef BIOS
310 "p:vz:1"
311 #endif
312
313 "?" )) != EOF)
314 switch (c)
315 {
316 case 'b':
317 bootfile = optarg;
318 break;
319 case 'l':
320 loadadr = strtol(optarg, NULL, 16);
321 break;
322 case 's':
323 startup = optarg;
324 break;
325 #ifdef BIOS
326 case 'p':
327 basepage = strtol(optarg, NULL, 16);
328 break;
329 case 'v':
330 vflag = 1;
331 break;
332 case 'z':
333 z3env = strtol(optarg, NULL, 16);
334 break;
335 case '1':
336 always128 = 1;
337 break;
338 #endif
339 case 'h':
340 term_height = strtol(optarg, NULL, 10);
341 break;
342 case 'w':
343 term_width = strtol(optarg, NULL, 10);
344 break;
345 case '?':
346 usage();
347 }
348
349 #ifdef MMU
350 initMEM(); /* initialice memory */
351 initMMU(); /* initialice MMU */
352 #endif
353
354 #ifdef BIOS
355 ccp_base = basepage * 256;
356 bdos_base = ccp_base + CCP_LENGTH;
357 bios_base = bdos_base + BDOS_LENGTH;
358
359 /* Plug screen height and width int SCB */
360 ram[bios_base - 0x100 + 0xBA] = term_height - 1;
361 ram[bios_base - 0x100 + 0xBC] = term_width - 1;
362
363 if (z3env == bios_base)
364 bios_base += 1024;
365 #endif
366
367 load_cpm(bootfile);
368 #ifdef BIOS
369 setvectors();
370
371 if (vflag)
372 {
373 printf("bootfile: %s\n", bootfile);
374 if (loadadr >= 0)
375 printf("loadadr: %4lx\n", loadadr);
376 printf("startup file: %s\n"
377 "basepage: %2lx\n"
378 "ccp_base: %4hx\n"
379 "bdos_base: %4hx\n"
380 "bios_base: %4hx\n"
381 "bios_top: %4hx\n",
382 startup, basepage, ccp_base,
383 bdos_base, bios_base, bios_top);
384 if (z3env)
385 printf("z3env: %4lx\n", z3env);
386 printf("dptable: %4hx\n"
387 "dirbuf: %4hx\n",
388 dptable, dirbuff);
389 }
390 #endif
391
392 gostart = bios_init(startup);
393
394 #ifdef BIOS
395 for (goopt = 0; !goopt && optind < argc; optind++)
396 goopt = docmd(argv[optind]);
397
398 if (!gostart && !goopt)
399 monitor(0);
400 #endif
401
402 #ifdef DEBUG
403 do
404 {
405 FASTWORK adr = simz80(pc);
406 if (adr & 0x10000)
407 monitor(adr);
408 else
409 bios(ram[adr]);
410 } while (1);
411 #elif !BIOS
412 simz80(pc);
413 fprintf(stderr,"HALT\n\r");
414 fprintf(stderr,"PC SP IR IX IY AF BC DE HL AF' BC' DE' HL'\n\r");
415 fprintf(stderr,"%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n\r",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);
416 exit(0);
417 #else
418 do
419 {
420 pc = clockFrequency_save ? simz80_with_tStates(pc) : simz80(pc);
421 bios(GetBYTE(pc));
422 } while (1);
423 #endif
424 }
425
426 #ifndef USE_GNU_READLINE
427 void *
xmalloc(size_t size)428 xmalloc(size_t size)
429 {
430 void *p = malloc(size);
431
432 if (p == NULL)
433 {
434 fputs("insufficient memory\n", stderr);
435 exit(1);
436 }
437 return p;
438 }
439 #endif
440