1 #include "appmake.h"
2 #include "zx-util.h"
3 #include <inttypes.h>
4 
5 #define FILENAMELEN 1024
6 
7 // TAPE
8 
9 // These values are set accordingly with the turbo loader timing and should not be changed
10 
11 #define	tperiod0	5
12 #define	tperiod1	10
13 #define	tperiod2	16
14 
15 // SNA
16 
17 #define ZX48_SNA_PROTOTYPE    "src/appmake/data/zx_48.sna"
18 #define ZX128_SNA_PROTOTYPE   "src/appmake/data/zx_128.sna"
19 #define ZXN_NEXTOS_PROTOTYPE  "src/appmake/data/zxn_nextos.sna"
20 
21 // ZXN UNIVERSAL DOT
22 
23 #define ZXN_UNIVERSAL_DOT_BINARY  "libsrc/_DEVELOPMENT/target/zxn/zxn_universal_dot.bin"
24 
25 /*
26     ffs
27     missing from vs2015
28 */
29 
z88dk_ffs(int n)30 int z88dk_ffs(int n)
31 {
32     int res;
33 
34     if (n == 0) return 0;
35 
36     for (res = 1; (n & 0x1) == 0; ++res)
37         n >>= 1;
38 
39     return res;
40 }
41 
42 /*
43     ZX Next Utility
44     Construct Contents of 8k Page or 16k Bank
45 */
46 
zxn_construct_page_contents(unsigned char * mem,struct memory_bank * mb,int mbsz,int fillbyte)47 void zxn_construct_page_contents(unsigned char *mem, struct memory_bank *mb, int mbsz, int fillbyte)
48 {
49     FILE *fin;
50     int   j;
51     int   first = 0 , last = 0, gap;
52 
53     if ((mbsz != 0x2000) && (mbsz != 0x4000))
54         exit_log(1, "Error: Page construction for a size that is not 8k or 16k: %u\n", mbsz);
55 
56     memset(mem, fillbyte, mbsz);
57 
58     gap = 0;
59 
60     for (j = 0; j < mb->num; ++j)
61     {
62         struct section_bin *sb = &mb->secbin[j];
63 
64         if (j == 0)
65             first = sb->org & (mbsz - 1);
66         else
67             gap += (sb->org & (mbsz - 1)) - last;
68 
69         if (((sb->org & (mbsz - 1)) + sb->size) > mbsz)
70             exit_log(1, "Error: Section %s exceeds %s [%d,%d)\n", sb->section_name, (mbsz == 0x2000) ? "8k page" : "16k bank", sb->org & (mbsz - 1), (sb->org & (mbsz - 1)) + sb->size);
71 
72         if ((fin = fopen(sb->filename, "rb")) == NULL)
73             exit_log(1, "Error: Can't open \"%s\"\n", sb->filename);
74 
75         if (fseek(fin, sb->offset, SEEK_SET) != 0)
76             exit_log(1, "Error: Can't seek \"%s\" to %d\n", sb->filename, sb->offset);
77 
78         if (fread(&mem[sb->org & (mbsz - 1)], sb->size, 1, fin) != 1)
79             exit_log(1, "Error: Can't read [%d,%d) from \"%s\"\n", sb->offset, sb->offset + sb->size);
80 
81         fclose(fin);
82 
83         last = (sb->org & (mbsz - 1)) + sb->size;
84     }
85 
86     // information
87 
88     if (first) printf(", %d head bytes free", first);
89     if (gap) printf(", %d gap bytes free", gap);
90     if (last - mbsz < 0) printf(", %d tail bytes free", mbsz - last);
91     printf("\n");
92 }
93 
94 
95 /*
96    TAPE
97    2000-2013 Dominic Morris, Stefano Bodrato
98    See zx.c for more comments.
99 */
100 
101 // turbo Loader]
102 
103 static unsigned char turbo_loader[] = {
104     0x60, 0x69,	    //  ld h,b / ld l,c
105     17,52,0,        //	ld	de,52 (offset)
106     0x19,           //	add	hl,de
107     17,0x69,0xff,   //	ld	de,65385
108     1,150,0,        //	ld	bc,150
109     0xed, 0xb0,     //	ldir
110     221,33,0,64,    //	ld	ix,16384 (position [14])
111     // length is not checked, we load all the data we find
112     0,0,0,          //	placeholder for: call	65385
113     221,33,0,64,    //	ld	ix,16384
114     // length is not checked, we load all the data we find
115     0,0,0,          //	placeholder for: call	65385
116     221,33,0,64,    //	ld	ix,16384
117     // length is not checked, we load all the data we find
118     0,0,0,          //	placeholder for: call	65385
119     221,33,0,128,   //	ld	ix,loc	(pos 37/38)
120     // length is not checked, we load all the data we find
121     205,0x69,0xff,  //	call	65385
122     0x3a, 0x48, 0x5c, // ld a,(23624)	; Restore border color
123     0x1f, 0x1f, 0x1f, // rra (3 times)
124     0xd3, 254,        // out (254),a
125     0xfb, 0xc9,     //  ei / ret
126 
127     0xF3, 0xDB, 0xFE, 0x1F, 0xE6, 0x20, 0x4F, 0xBF, 0x06, 0x9C, 0xCD, 0xDC, 0xFF, 0x30, 0xF8, 0x3E,
128     0xC6, 0xB8, 0x30, 0xF4, 0x24, 0x20, 0xF1, 0x06, 0xC9, 0xCD, 0xE0, 0xFF, 0x30, 0xE9, 0x78, 0xFE,
129     0xD4, 0x30, 0xF4, 0xCD, 0xE0, 0xFF, 0xD0, 0x79, 0xEE, 0x03, 0x4F, 0x06, 0xD0, 0x18, 0x07, 0xDD,
130     0x75, 0x00, 0xDD, 0x23, 0x06, 0xD1, 0x2E, 0x01, 0xCD, 0xDC, 0xFF, 0xD0, 0x78, 0xFE, 0xDE, 0xD2,
131     0xB8, 0xFF, 0xFE, 0xD5, 0x3F, 0xCB, 0x15, 0x06, 0xD0, 0xD2, 0xA1, 0xFF, 0xC3, 0x98, 0xFF, 0x2D,
132     0x06, 0xD1, 0xCD, 0xDC, 0xFF, 0xD0, 0x3E, 0xD7, 0xB8, 0xDA, 0xB5, 0xFF, 0xDD, 0x6E, 0xFF, 0x06,
133     0xD1, 0xCD, 0xDC, 0xFF, 0xD0, 0x3E, 0xD5, 0xB8, 0xDA, 0xB5, 0xFF, 0xDD, 0x75, 0x00, 0xDD, 0x23,
134     0xC3, 0xC8, 0xFF, 0xCD, 0xE0, 0xFF, 0xD0, 0x3E, 0x0D, 0x3D, 0x20, 0xFD, 0xA7, 0x04, 0xC8, 0x3E,
135     0x7F, 0xDB, 0xFE, 0x1F, 0xA9, 0xE6, 0x20, 0x28, 0xF4, 0x79, 0x2F, 0x4F, 0x78, 0x00, 0xE6, 0x07,
136     0xF6, 0x08, 0xD3, 0xFE, 0x37, 0xC9, 0x37, 0xC9
137 };
138 
139 
140 static unsigned char ts_loader[] = {
141     //basic hdr
142     19,0
143     ,0x00,0x00,'2','0','6','8',' ','r','e','l','o','c',0x3A,0x00,0x05,0x00,0x3A,0x00,0x5e
144     //basic data
145     ,60,0
146     ,0xFF,0x00,0x05,0x13,0x00,0xFA,0xBE,0xB0,0x22,0x37,0x35,0x22,0xC9,0xB0,0x22,0x32
147     ,0x32,0x35,0x22,0xCB,0xEF,0x22,0x22,0x0D,0x00,0x0A,0x1F,0x00,0xEF,0x22,0x22,0xAF
148     ,0x3A,0xF9,0xC0,0xB0,0x22,0x33,0x32,0x37,0x36,0x38,0x22,0x3A,0xDF,0xB0,0x22,0x32
149     ,0x35,0x35,0x22,0x2C,0xC3,0xA7,0x3A,0xEF,0x22,0x22,0x0D,0x53
150 
151     //lm hdr
152     ,19,0
153     ,0x00,0x03,'v','i','d','.','b','i','n',' ',' ',' ',0x32,0x00,0x00,0x80,0x00,0x00,0xA1
154     //lm data
155     ,52,0
156     ,0xFF,0x3E,0x06,0x21,0x8E,0x0E,0xF5,0xCD,0x1F,0x80,0xF1,0xFE,0x80,0x20,0x03,0x32
157     ,0xC2,0x5C,0x3A,0x1E,0x80,0xD3,0xF4,0xDB,0xFF,0xCB,0xBF,0xD3,0xFF,0xFB,0xC9,0x00
158     ,0xF3,0xF5,0xDB,0xFF,0xCB,0xFF,0xD3,0xFF,0xDB,0xF4,0x32,0x1E,0x80,0x3E,0x01,0xD3
159     ,0xF4,0xF1,0xE9,0xB5
160 };
161 
162 
turbo_one(FILE * fpout)163 void turbo_one(FILE *fpout)
164 {
165     int i;
166 
167     for (i = 0; i < tperiod1; i++)
168         fputc(0x20, fpout);
169     for (i = 0; i < tperiod0; i++)
170         fputc(0xe0, fpout);
171 }
172 
173 
turbo_rawout(FILE * fpout,unsigned char b,char extreme)174 void turbo_rawout(FILE *fpout, unsigned char b, char extreme)
175 {
176     static unsigned char c[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
177     int i;
178 
179     if (!b && (extreme)) {
180         /* if byte is zero then we shortcut to a single bit ! */
181         // Experimental min limit is 14
182         //zx_rawbit(fpout, tperiod2);
183         zx_rawbit(fpout, tperiod1);
184         //zx_rawbit(fpout, tperiod1);
185         turbo_one(fpout);
186     }
187     else {
188         for (i = 0; i < 8; i++)
189         {
190             if (b & c[i])
191                 // Experimental min limit is 7
192                 //zx_rawbit(fpout, tperiod1);
193                 turbo_one(fpout);
194             else
195                 zx_rawbit(fpout, tperiod0);
196         }
197     }
198 }
199 
200 
zx_tape(struct zx_common * zxc,struct zx_tape * zxt,struct banked_memory * memory)201 int zx_tape(struct zx_common *zxc, struct zx_tape *zxt, struct banked_memory *memory)
202 {
203     char    filename[FILENAME_MAX + 1];
204     char    wavfile[FILENAME_MAX + 1];
205     char    name[11];
206     char    mybuf[20];
207     FILE    *fpin, *fpout, *fpmerge;
208     long    pos = 0;
209     int     c = 0, d;
210     int     warping;
211     int     i, j, blocklen;
212     int     len, mlen;
213     int		blockcount, bsnum_bank;
214     unsigned char * loader;
215     int		loader_len;
216 
217     loader = turbo_loader;
218     loader_len = sizeof(turbo_loader);
219 
220     if (zxt->extreme) {
221         //loader = xtreme_loader;
222         //loader_len = sizeof(xtreme_loader);
223         zxt->turbo = TRUE;
224         //fast=TRUE;
225     }
226 
227     if (zxt->turbo) {
228         zxt->audio = TRUE;
229         fprintf(stderr, "WARNING: 'tap' file in turbo mode is inconsistent, use the WAV audio file.\n");
230     }
231 
232 
233     if ((zxt->patchpos >= 0) && (zxt->patchpos < loader_len) && (zxt->patchdata != NULL)) {
234         i = 0;
235         fprintf(stderr, "Patching the turbo loader at position %u: ", zxt->patchpos);
236         while (zxt->patchdata[i]) {
237             if (i & 1) {
238                 c += hexdigit(zxt->patchdata[i]);
239                 loader[zxt->patchpos] = (unsigned char)c;
240                 fprintf(stderr, "$%x ", c);
241                 zxt->patchpos++;
242             }
243             else {
244                 c = 16 * hexdigit(zxt->patchdata[i]);
245             }
246             i++;
247         }
248         fprintf(stderr, " (%i bytes)\n", i / 2);
249 
250     }
251 
252     if (zxt->dumb) {
253         strcpy(filename, zxc->binname);
254         if (zxt->turbo) fprintf(stderr, "WARNING: turbo option in dumb mode requires extra editing of the wav file.\n");
255 
256     }
257     else {
258         if (zxc->outfile == NULL) {
259             strcpy(filename, zxc->binname);
260             suffix_change(filename, ".tap");
261         }
262         else {
263             strcpy(filename, zxc->outfile);
264         }
265 
266         if (zxt->blockname == NULL)
267             zxt->blockname = zbasename(zxc->binname);
268 
269 
270         if (strcmp(zxc->binname, filename) == 0) {
271             exit_log(1, "Input and output file names must be different\n");
272         }
273 
274 
275         if (zxc->origin != -1) {
276             pos = zxc->origin;
277         }
278         else {
279             if ((pos = get_org_addr(zxc->crtfile)) == -1) {
280                 exit_log(1,"Could not find parameter ZORG (not z88dk compiled?)\n");
281             }
282         }
283 
284         if ((fpin = fopen_bin(zxc->binname, zxc->crtfile)) == NULL) {
285             exit_log(1, "Can't open input file %s\n", zxc->binname);
286         }
287 
288         /*
289         *        Now we try to determine the size of the file
290         *        to be converted
291         */
292         if (fseek(fpin, 0, SEEK_END)) {
293             fclose(fpin);
294             exit_log(1, "Couldn't determine size of file\n");
295         }
296 
297         len = ftell(fpin);
298         fseek(fpin, 0L, SEEK_SET);
299 
300         if ((fpout = fopen(filename, "wb")) == NULL) {
301             fclose(fpin);
302             exit_log(1,"Can't open output file\n");
303         }
304 
305         if (zxt->ts2068) {
306             if (pos<33000)
307                 printf("\nInfo: Position %u is too low, not relocating TS2068 BASIC.", (int)pos);
308             else
309                 for (i = 0; (i < sizeof(ts_loader)); i++)
310                     fputc(ts_loader[i], fpout);
311         }
312 
313         if ((pos>23700) && (pos<24000)) {
314             if (zxt->turbo) fprintf(stderr, "WARNING: turbo has no effect in single BASIC block mode.\n");
315             /* All in a BASIC line */
316             /* Write out the BASIC header file */
317             writeword_p(19, fpout, &zxt->parity);         /* Header len */
318             writebyte_p(0, fpout, &zxt->parity);          /* Header is a type 0 block */
319             zxt->parity = 0;
320             writebyte_p(0, fpout, &zxt->parity);          /* Filetype (Basic) */
321 
322             /* Deal with the filename */
323             snprintf(name, sizeof(name), "%-*s", (int) sizeof(name)-1, zxt->blockname);
324 
325             for (i = 0; i <= 9; i++)
326                 writebyte_p(name[i], fpout, &zxt->parity);
327             writeword_p(21 + len, fpout, &zxt->parity);    /* length */
328             writeword_p(10, fpout, &zxt->parity);         /* line for auto-start */
329             writeword_p(21 + len, fpout, &zxt->parity);   /* length (?) */
330             writebyte_p(zxt->parity, fpout, &zxt->parity);
331 
332             /* Write out the 'BASIC' program */
333             writeword_p(23 + len, fpout, &zxt->parity);         /* block lenght */
334             zxt->parity = 0;
335             writebyte_p(255, fpout, &zxt->parity);        /* Data is a type 255 block */
336 
337             writebyte_p(0, fpout, &zxt->parity);          /* MSB of BASIC line number */
338             writebyte_p(1, fpout, &zxt->parity);          /* LSB... */
339             writeword_p(2 + len, fpout, &zxt->parity);      /* BASIC line length */
340             writebyte_p(0xea, fpout, &zxt->parity);       /* REM */
341             for (i = 0; i<len; i++) {
342                 c = getc(fpin);
343                 writebyte_p(c, fpout, &zxt->parity);
344             }
345             writebyte_p(0x0d, fpout, &zxt->parity);       /* ENTER (end of BASIC line) */
346 
347             writebyte_p(0, fpout, &zxt->parity);          /* MSB of BASIC line number */
348             writebyte_p(10, fpout, &zxt->parity);         /* LSB... */
349             writeword_p(11, fpout, &zxt->parity);         /* BASIC line length */
350             writebyte_p(0xf9, fpout, &zxt->parity);       /* RANDOMIZE */
351             writebyte_p(0xc0, fpout, &zxt->parity);       /* USR */
352             writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
353             sprintf(mybuf, "\"%i\"", (int)pos);      /* Location for USR */
354             writestring_p(mybuf, fpout, &zxt->parity);
355             writebyte_p(0x0d, fpout, &zxt->parity);       /* ENTER (end of BASIC line) */
356             writebyte_p(zxt->parity, fpout, &zxt->parity);
357         }
358         else {
359             /* ===============
360             Loader block
361             =============== */
362 
363             mlen = 0;
364             if (!zxt->noloader) {
365                 /* If requested, merge an external loader */
366                 if (zxt->merge != NULL) {
367                     if (zxt->turbo) {
368                         fclose(fpin);
369                         fclose(fpout);
370                         exit_log(1,"ERROR: turbo mode conflicts with the 'merge' option.\n");
371                     }
372 
373                     if ((fpmerge = fopen(zxt->merge, "rb")) == NULL) {
374                         fclose(fpin);
375                         fclose(fpout);
376                         exit_log(1, "File for 'merge' not found: %s\n", zxt->merge);
377                     }
378                     /* check the header type (first block must be BASIC) */
379                     fseek(fpmerge, 3, SEEK_SET);
380                     c = getc(fpmerge);
381                     if (c != 0) {
382                         fclose(fpin);
383                         fclose(fpout);
384                         exit_log(1, "BASIC block not found in file %s\n", zxt->merge);
385                     }
386 
387                     fseek(fpmerge, 21, SEEK_SET);
388                     mlen = getc(fpmerge) + 256 * getc(fpmerge);  /* get block length */
389 
390                     fseek(fpmerge, 0, SEEK_SET);
391                     blocklen = getc(fpmerge) + 256 * getc(fpmerge);  /* get block length */
392                     if (blocklen != 19) {
393                         fclose(fpin);
394                         fclose(fpout);
395                         exit_log(1, "Error locating the external loader header in file %s\n", zxt->merge);
396                     }
397                     fseek(fpmerge, 0, SEEK_SET);
398                     /* Total ext. loader size (headerblock + data block) */
399                     blocklen += mlen + 4;
400                     /* Now import the external BASIC loader */
401                     for (i = 0; (i < blocklen); i++) {
402                         c = getc(fpmerge);
403                         writebyte_p(c, fpout, &zxt->parity);
404                     }
405                     fclose(fpmerge);
406 
407                 }
408                 else {
409 
410 
411                     /* BASIC loader */
412 
413                     if (zxt->turbo) {
414                         mlen += 22 + loader_len + 32;	/* extra BASIC size for REM line + turbo block + turbo caller code */
415                         loader[37] = pos % 256;
416                         loader[38] = pos / 256;
417                         if (zxt->screen) {
418                             turbo_loader[18] = 0xcd;		/* activate the extra screen block loading */
419                             turbo_loader[19] = 0x69;
420                             turbo_loader[20] = 0xff;
421                         }
422                     }
423 
424                     if (zxt->screen && !zxt->turbo)  mlen += (5 + 17);			/* Add the space count for -- LOAD "" SCREEN$: */
425 
426                                                                 /* Write out the BASIC header file */
427                     writeword_p(19, fpout, &zxt->parity);         /* Header len */
428                     writebyte_p(0, fpout, &zxt->parity);          /* Header is a type 0 block */
429 
430                     zxt->parity = 0;
431                     writebyte_p(0, fpout, &zxt->parity);             /* Filetype (Basic) */
432                     writestring_p("Loader    ", fpout, &zxt->parity);
433                     writeword_p(0x1e + mlen, fpout, &zxt->parity);   /* length */
434                     writeword_p(10, fpout, &zxt->parity);            /* line for auto-start */
435                     writeword_p(0x1e + mlen, fpout, &zxt->parity);   /* length (?) */
436                     writebyte_p(zxt->parity, fpout, &zxt->parity);
437 
438                     /* Write out the BASIC loader program */
439                     writeword_p(32 + mlen, fpout, &zxt->parity);         /* block lenght */
440                     zxt->parity = 0;
441                     writebyte_p(255, fpout, &zxt->parity);        /* Data is a type 255 block */
442 
443                                                              /* REM line is <compiled program length> + 22 bytes long */
444                     if (zxt->turbo) {
445                         writebyte_p(0, fpout, &zxt->parity);         /* MSB of BASIC line number for REM */
446                         writebyte_p(1, fpout, &zxt->parity);         /* LSB... */
447                         writeword_p(18 + loader_len, fpout, &zxt->parity);         /* BASIC line length */
448                         writebyte_p(0x10, fpout, &zxt->parity);         /* Cosmetics (ink) */
449                         writebyte_p(7, fpout, &zxt->parity);          /* Cosmetics (white) */
450                         writebyte_p(0xea, fpout, &zxt->parity);       /* REM */
451                         writebyte_p(0x11, fpout, &zxt->parity);         /* Cosmetics (paper) */
452                         writebyte_p(0, fpout, &zxt->parity);          /* Cosmetics (black) */
453                         writestring_p(" Z88DK C+ ", fpout, &zxt->parity);
454                         writebyte_p(0x11, fpout, &zxt->parity);         /* Cosmetics (paper) */
455                         writebyte_p(7, fpout, &zxt->parity);          /* Cosmetics (white) */
456                         for (i = 0; (i < loader_len); i++)
457                             writebyte_p(loader[i], fpout, &zxt->parity);
458                         writebyte_p(0x0d, fpout, &zxt->parity);       /* ENTER (end of BASIC line) */
459                     }
460 
461                     writebyte_p(0, fpout, &zxt->parity);          /* MSB of BASIC line number */
462                     writebyte_p(10, fpout, &zxt->parity);         /* LSB... */
463                     if (!zxt->turbo)
464                         if (zxt->screen)
465                             writeword_p(26 + 5 + 17, fpout, &zxt->parity);         /* BASIC line length */
466                         else
467                             writeword_p(26, fpout, &zxt->parity);         /* BASIC line length */
468                     else
469                         writeword_p(26 + 32, fpout, &zxt->parity);         /* BASIC line length */
470                     writebyte_p(0xfd, fpout, &zxt->parity);       /* CLEAR */
471                     writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
472                     if (zxt->clear_address == -1) {
473                         zxt->clear_address = pos - 1;
474                     }
475                     sprintf(mybuf, "\"%i\":", zxt->clear_address);        /* location for CLEAR */
476                     writestring_p(mybuf, fpout, &zxt->parity);
477                     if (zxt->turbo) {
478                         /* 36 bytes, which means 32 extra bytes */
479                         writebyte_p(0xf9, fpout, &zxt->parity);       /* RANDOMIZE */
480                         writebyte_p(0xc0, fpout, &zxt->parity);       /* USR */
481                         writebyte_p('(', fpout, &zxt->parity);
482                         writebyte_p(0xbe, fpout, &zxt->parity);       /* PEEK */
483                         writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
484                         writestring_p("\"23635\"+", fpout, &zxt->parity);
485                         writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
486                         writestring_p("\"256\"*", fpout, &zxt->parity);
487                         writebyte_p(0xbe, fpout, &zxt->parity);       /* PEEK */
488                         writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
489                         writestring_p("\"23636\"+", fpout, &zxt->parity);
490                         writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
491                         writestring_p("\"21\"", fpout, &zxt->parity);
492                         writebyte_p(')', fpout, &zxt->parity);
493                     }
494                     else {
495                         if (zxt->screen && !zxt->turbo) {
496                             writebyte_p(0xef, fpout, &zxt->parity);       /* LOAD */
497                             writebyte_p('"', fpout, &zxt->parity);
498                             writebyte_p('"', fpout, &zxt->parity);
499                             writebyte_p(0xaa, fpout, &zxt->parity);       /* SCREEN$ */
500                             writebyte_p(':', fpout, &zxt->parity);
501                             writebyte_p(0xf4, fpout, &zxt->parity);   /* POKE */
502                             writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
503                             sprintf(mybuf, "\"23739\",");
504                             writestring_p(mybuf, fpout, &zxt->parity);
505                             writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
506                             sprintf(mybuf, "\"111\":");
507                             writestring_p(mybuf, fpout, &zxt->parity);
508                         }
509                         writebyte_p(0xef, fpout, &zxt->parity);       /* LOAD */
510                         writebyte_p('"', fpout, &zxt->parity);
511                         writebyte_p('"', fpout, &zxt->parity);
512                         writebyte_p(0xaf, fpout, &zxt->parity);       /* CODE */
513                     }
514                     writebyte_p(':', fpout, &zxt->parity);
515                     writebyte_p(0xf9, fpout, &zxt->parity);       /* RANDOMIZE */
516                     writebyte_p(0xc0, fpout, &zxt->parity);       /* USR */
517                     writebyte_p(0xb0, fpout, &zxt->parity);       /* VAL */
518                     sprintf(mybuf, "\"%i\"", (int)pos);           /* Location for USR */
519                     writestring_p(mybuf, fpout, &zxt->parity);
520                     writebyte_p(0x0d, fpout, &zxt->parity);       /* ENTER (end of BASIC line) */
521                     writebyte_p(zxt->parity, fpout, &zxt->parity);
522                 }
523             }
524 
525             /* Title screen */
526             if (zxt->screen != NULL) {
527 
528                 if ((fpmerge = fopen(zxt->screen, "rb")) == NULL) {
529                     fclose(fpin);
530                     fclose(fpout);
531                     exit_log(1,"Title screen file not found: %s\n", zxt->screen);
532                 }
533 
534                 if (fseek(fpmerge, 0, SEEK_END)) {
535                     fclose(fpin);
536                     fclose(fpout);
537                     fclose(fpmerge);
538                     exit_log(1,"Couldn't determine size of file\n");
539                 }
540 
541                 mlen = ftell(fpmerge);
542                 if (((mlen < 6912) || (mlen >= 7000)) && (mlen != 6144) && (mlen != 2048)) {
543                     fclose(fpin);
544                     fclose(fpout);
545                     fclose(fpmerge);
546                     exit_log(1,  "ERROR: Title screen size not recognized: %u\n", mlen);
547                 }
548 
549                 if (mlen <= 6912) {
550                     fseek(fpmerge, 0, SEEK_SET);
551                     j = mlen;
552                 }
553                 else {
554                     fseek(fpmerge, mlen - 6913, SEEK_SET);
555                     j = 6912;
556                 }
557 
558                 writeword_p(19, fpout, &zxt->parity);         /* Header len */
559                 writebyte_p(0, fpout, &zxt->parity);          /* Header is a type 0 block */
560                 zxt->parity = 0;
561                 writebyte_p(3, fpout, &zxt->parity);          /* Filetype (Code) */
562 
563                 /* Deal with the filename */
564                 snprintf(name, sizeof(name), "$%-*s", (int) sizeof(name)-2, zxt->blockname);
565 
566                 for (i = 0; i <= 9; i++)
567                     writebyte_p(name[i], fpout, &zxt->parity);
568 
569                 writeword_p(j, fpout, &zxt->parity);
570                 writeword_p(16384, fpout, &zxt->parity);        /* load address */
571                 writeword_p(0, fpout, &zxt->parity);          /* offset */
572                 writebyte_p(zxt->parity, fpout, &zxt->parity);
573 
574                 /* Now onto the data bit */
575                 writeword_p(j + 2, fpout, &zxt->parity);      /* Length of next block */
576 
577                 zxt->parity = 0;
578                 writebyte_p(255, fpout, &zxt->parity);        /* Data is a type 255 block */
579                 for (i = 0; i<j; i++) {
580                     c = getc(fpmerge);
581                     writebyte_p(c, fpout, &zxt->parity);
582                 }
583                 writebyte_p(zxt->parity, fpout, &zxt->parity);
584 
585                 fclose(fpmerge);
586             }
587 
588             /* M/C program */
589 
590             if (!zxt->noheader) {
591                 /* Write out the code header file */
592                 writeword_p(19, fpout, &zxt->parity);         /* Header len */
593                 writebyte_p(0, fpout, &zxt->parity);          /* Header is a type 0 block */
594                 zxt->parity = 0;
595                 writebyte_p(3, fpout, &zxt->parity);          /* Filetype (Code) */
596 
597                 /* Deal with the filename */
598                 snprintf(name, sizeof(name), "%-*s", (int) sizeof(name)-1, zxt->blockname);
599 
600                 for (i = 0; i <= 9; i++)
601                     writebyte_p(name[i], fpout, &zxt->parity);
602                 writeword_p(len, fpout, &zxt->parity);
603                 writeword_p(pos, fpout, &zxt->parity);        /* load address */
604                 writeword_p(0, fpout, &zxt->parity);          /* offset */
605                 writebyte_p(zxt->parity, fpout, &zxt->parity);
606             }
607 
608             /* Now onto the data bit */
609             writeword_p(len + 2, fpout, &zxt->parity);      /* Length of next block */
610 
611             zxt->parity = 0;
612             writebyte_p(255, fpout, &zxt->parity);        /* Data is a type 255 block */
613             for (i = 0; i<len; i++) {
614                 c = getc(fpin);
615                 writebyte_p(c, fpout, &zxt->parity);
616             }
617             writebyte_p(zxt->parity, fpout, &zxt->parity);
618 
619             // Write the memory banks
620              // Write the banks
621             bsnum_bank = mb_find_bankspace(memory, "BANK");
622             for ( i = 0; i < 8; i++ ) {
623                 struct memory_bank *mb = &memory->bankspace[bsnum_bank].membank[i];
624                 if (mb->num > 0) {
625                     int      j;
626                     unsigned char bank_buf[16384];
627                     FILE    *fpbank = fopen(mb->secbin->filename, "rb");
628 
629                     if ( fpbank == NULL ) {
630                         exit_log(1,"Cannot open BANK file %s\n", mb->secbin->filename);
631                     }
632                     if ( mb->secbin->size != fread(bank_buf, 1,  mb->secbin->size, fpbank)) {  exit_log(1, "Could not read required data from <%s>\n",mb->secbin->filename); }
633 
634                     /* Now onto the data bit */
635                     writeword_p(mb->secbin->size + 2, fpout, &zxt->parity);      /* Length of next block */
636                     zxt->parity = 0;
637                     writebyte_p(255, fpout, &zxt->parity);        /* Data is a type 255 block */
638                     for (j = 0; j<mb->secbin->size; j++) {
639                         c = bank_buf[j];
640                         writebyte_p(c, fpout, &zxt->parity);
641                     }
642                     writebyte_p(zxt->parity, fpout, &zxt->parity);
643                     fclose(fpbank);
644                 }
645             }
646         }
647         fclose(fpin);
648         fclose(fpout);
649     }
650 
651     /* ***************************************** */
652     /*  Now, if requested, create the audio file */
653     /* ***************************************** */
654     if (zxt->audio) {
655         if ((fpin = fopen(filename, "rb")) == NULL) {
656             exit_log(1, "Can't open file %s for wave conversion\n", filename);
657         }
658 
659         if (fseek(fpin, 0, SEEK_END)) {
660             fclose(fpin);
661             exit_log(1,"Couldn't determine size of file\n");
662         }
663         len = ftell(fpin);
664         fseek(fpin, 0L, SEEK_SET);
665 
666         strcpy(wavfile, filename);
667         suffix_change(wavfile, ".RAW");
668         if ((fpout = fopen(wavfile, "wb")) == NULL) {
669             exit_log(1, "Can't open output raw audio file %s\n", wavfile);
670         }
671 
672         blockcount = 0;
673         if (zxt->noloader) blockcount = 2;
674 
675         if ((zxt->ts2068) && (pos >= 33000))
676             blockcount -= 4;
677 
678         /* leading silence */
679         for (i = 0; i < 0x500; i++)
680             fputc(0x80, fpout);
681 
682         /* Data blocks */
683         while (ftell(fpin) < len) {
684             blockcount++;
685             blocklen = (getc(fpin) + 256 * getc(fpin));
686             if (zxt->dumb) {
687                 if (blocklen == 19)
688                     printf("\n  Header found: ");
689                 else
690                     printf("\n  Block found, length: %d Byte(s) ", blocklen);
691             }
692 
693             if (zxt->dumb || !zxt->turbo || ((blockcount != 3) && (blockcount != 5))) {        /* byte block headers must be ignored in turbo mode */
694 
695                 if (zxt->turbo && (zxt->dumb || (blockcount == 4) || (blockcount == 6))) {             /* get rid of the first byte in the data block if in turbo mode */
696                     c = getc(fpin);
697                     blocklen -= 2; 	/* and of the parity byte too ! */
698                 }
699 
700                 if (zxt->turbo && ((blockcount == 4) || (blockcount == 6)))
701                     zx_pilot(500, fpout);
702                 else
703                     zx_pilot(2500, fpout);
704 
705                 c = -1;
706                 warping = FALSE;
707 
708                 for (i = 0; (i < blocklen); i++) {
709                     d = c;
710                     c = getc(fpin);
711 
712                     if ((zxt->dumb) && (blocklen == 19) && (c >= 32) && (c <= 126) && (i>1) && (i<12))
713                         printf("%c", c);
714 
715                     if (zxt->turbo && (zxt->dumb || (blockcount == 4) || (blockcount == 6))) {
716                         if (zxt->extreme) {
717                             if (d == c) {
718                                 if (!warping) {
719                                     warping = TRUE;
720                                     //zx_rawbit(fpout, tperiod2);
721                                     zx_rawbit(fpout, tperiod1);
722                                     zx_rawbit(fpout, tperiod0);
723                                 }
724                                 else
725                                     zx_rawbit(fpout, tperiod0);
726                             }
727                             else {
728                                 if (warping) {
729                                     //zx_rawbit(fpout, tperiod1);
730                                     turbo_one(fpout);
731                                     warping = FALSE;
732                                 }
733                                 turbo_rawout(fpout, c, zxt->extreme);
734                             }
735                         }
736                         else
737                             turbo_rawout(fpout, c, zxt->extreme);
738                     }
739                     else
740                         zx_rawout(fpout, c, zxt->fast);
741                 }
742             }
743             else
744                 for (i = 0; (i < blocklen); i++)		/* Skip the block we're excluding */
745                     c = getc(fpin);
746 
747             if ((zxt->turbo && (blockcount == 4 || blockcount == 6)) || (zxt->turbo && zxt->dumb)) {
748                 //zx_rawout(fpout,1,fast);
749                 zx_rawbit(fpout, tperiod0);
750                 zx_rawbit(fpout, 75);
751                 c = getc(fpin);	/* parity byte must go away */
752             }
753 
754             if (zxt->dumb) printf("\n");
755         }
756 
757         /* trailing silence */
758         for (i = 0; i < 0x500; i++)
759             fputc(0x80, fpout);
760 
761         fclose(fpin);
762         fclose(fpout);
763 
764         /* Now complete with the WAV header */
765 		if (zxt->khz22)
766 			raw2wav_22k(wavfile,2);
767 		else
768 			raw2wav(wavfile);
769     }
770 
771     return 0;
772 }
773 
774 
775 /*
776    ESXDOS Dot Command
777 
778    * dot  : standard dot command resident at 0x2000 limited to ~7k+
779    * dotx : extended dot command with first part at 0x2000 limited to 7k+ and a second part in main ram
780 
781    July/Nov 2017 aralbrec
782    August 2018 aralbrec
783 */
784 
zx_dot_command(struct zx_common * zxc,struct banked_memory * memory)785 int zx_dot_command(struct zx_common *zxc, struct banked_memory *memory)
786 {
787     FILE *fout;
788 
789     struct memory_bank *mb;
790     struct section_bin *sb;
791     int section_num;
792 
793     char outname[9];
794     char outnamex[13];
795 
796     int c;
797     int z_dtx_filename, z_alt_filename;
798 
799     // determine output filename
800 
801     if (zxc->outfile == NULL)
802         sprintf(outname, "%.8s", zxc->binname);
803     else
804         sprintf(outname, "%.8s", zxc->outfile);
805 
806     suffix_change(outname, "");
807 
808     // truncate output filename to eight characters
809 
810     outname[8] = 0;
811     for (c = 0; outname[c]; ++c)
812         outname[c] = toupper(outname[c]);
813 
814     // generate the dot command from section CODE
815 
816     if (mb_find_section(memory, "CODE", &mb, &section_num) == 0)
817         exit_log(1, "Error: Section CODE not found\n");
818 
819     sb = &mb->secbin[section_num];
820 
821     if ((fout = fopen(outname, "wb")) == NULL)
822         exit_log(1, "Error: Couldn't create output file %s\n", outname);
823 
824     if (mb_output_section_binary(fout, sb) != 0)
825     {
826         fclose(fout);
827         remove(outname);
828         exit_log(1, "Error: Couldn't read section binary %s\n", sb->filename);
829     }
830 
831     mb_remove_section(memory, "CODE", zxc->clean);
832 
833     // generate the dotx portion from section MAIN
834 
835     if ((z_dtx_filename = parameter_search(zxc->crtfile, ".map", "__z_dtx_filename")) >= 0)
836     {
837         fclose(fout);
838 
839         printf("Creating DOTX command\n");
840 
841         if (mb_find_section(memory, "MAIN", &mb, &section_num) == 0)
842         {
843             remove(outname);
844             exit_log(1, "Error: Section MAIN not found\n");
845         }
846 
847         sprintf(outnamex, "%s.X", outname);
848 
849         sb = &mb->secbin[section_num];
850 
851         if (sb->org < 0x4000)
852         {
853             remove(outname);
854             exit_log(1, "Error: Section %s org of %d is less than 0x4000\n", sb->section_name, sb->org);
855         }
856 
857         if ((fout = fopen(outnamex, "wb")) == NULL)
858         {
859             remove(outname);
860             exit_log(1, "Error: Couldn't create output file %s\n", outnamex);
861         }
862 
863         if (mb_output_section_binary(fout, sb) != 0)
864         {
865             fclose(fout);
866             remove(outnamex);
867             remove(outname);
868             exit_log(1, "Error: Couldn't read section binary %s\n", sb->filename);
869         }
870 
871         mb_remove_section(memory, "MAIN", zxc->clean);
872 
873         fclose(fout);
874 
875         // insert variables into main dot binary
876 
877         if ((fout = fopen(outname, "rb+")) == NULL)
878         {
879             remove(outname);
880             remove(outnamex);
881             exit_log(1, "Error: Couldn't write variables into main dot binary\n");
882         }
883     }
884 
885     if (z_dtx_filename >= 0)
886     {
887         fseek(fout, z_dtx_filename - 0x2000, SEEK_SET);
888         fprintf(fout, "%s", outnamex);
889     }
890 
891     if ((z_alt_filename = parameter_search(zxc->crtfile, ".map", "__z_alt_filename")) >= 0)
892     {
893         fseek(fout, z_alt_filename - 0x2000, SEEK_SET);
894         fprintf(fout, "%s", outname);
895     }
896 
897     fclose(fout);
898     return 0;
899 }
900 
901 /*
902 NextOS Dotn Command
903 zx next only; ram pages are allocated from NextOS so as not to overwrite main ram
904 
905 June/August 2018 aralbrec
906 */
907 
908 #define ZXN_MAX_PAGE 223
909 #define ZXN_MAX_BANK 111
910 #define ZXN_MAX_DIV  15
911 
912 #define ZXNM_SKIP           0xff
913 #define ZXNM_ALLOCATE       0xfe
914 #define ZXNM_LOAD           0xfd
915 #define ZXNM_ALLOCATE_LOAD  0xfc
916 
zxn_dotn_command(struct zx_common * zxc,struct banked_memory * memory,int fillbyte)917 int zxn_dotn_command(struct zx_common *zxc, struct banked_memory *memory, int fillbyte)
918 {
919     int  i;
920     int  bsnum_page;
921     int  bsnum_div;
922 
923     char outname[9];
924 
925     int main_org, main_end;
926     int overlay_alloc_mask, overlay_load_mask;
927 
928     int appmake_handle;
929     int user_handle;
930     int z_alt_filename;
931 
932     int dotn_last_page = 0, actual_last_page;
933     int dotn_last_div = 0, actual_last_div;
934     int dotn_num_extra;
935     int dotn_main_overlay_mask = 0;
936     int dotn_main_absolute_mask = 0;
937 
938     unsigned char mem[64 * 1024];
939 
940     unsigned char z_page_alloc_table[MAXBANKS];
941     unsigned char z_page_table[MAXBANKS];
942 
943     unsigned char z_div_alloc_table[ZXN_MAX_DIV + 1];
944     unsigned char z_div_table[ZXN_MAX_DIV + 1];
945 
946     int main_bin_start;
947     int main_bin_end;
948     int dot_bin_end;
949 
950     FILE *fout;
951 
952     // find PAGE space
953 
954     if ((bsnum_page = mb_find_bankspace(memory, "PAGE")) < 0)
955         exit_log(1, "Error: Can't find PAGE space\n");
956 
957     // find DIV space
958 
959     if ((bsnum_div = mb_find_bankspace(memory, "DIV")) < 0)
960         exit_log(1, "Error: Can't find DIV space\n");
961 
962     // determine output filename
963 
964     if (zxc->outfile == NULL)
965         snprintf(outname, sizeof(outname), "%.8s", zxc->binname);
966     else
967         snprintf(outname, sizeof(outname), "%.8s", zxc->outfile);
968 
969     suffix_change(outname, "");
970 
971     // capitalize output filename
972 
973     for (i = 0; outname[i]; ++i)
974         outname[i] = toupper(outname[i]);
975 
976     // collect parameters
977 
978     appmake_handle = user_handle = -1;
979 
980     if ((appmake_handle = parameter_search(zxc->crtfile, ".map", "__appmake_handle")) < 0)
981         if ((user_handle = parameter_search(zxc->crtfile, ".map", "__user_handle")) < 0)
982             exit_log(1, "Error: Cannot locate __appmake_handle or __user_handle\n");
983 
984     main_bin_start = parameter_search(zxc->crtfile, ".map", "__MAIN_head");
985     main_bin_end = parameter_search(zxc->crtfile, ".map", "__MAIN_END_tail");
986 
987     if ((main_bin_start >= 0) && (main_bin_end > main_bin_start))
988         printf("Notice: Main binary occupies [%d,%d]\n", main_bin_start, main_bin_end - 1);
989 
990     // initialize memory contents
991 
992     memset(mem, fillbyte, sizeof(mem));
993 
994     // write main bank into memory image
995 
996     main_org = 0x10000;
997     main_end = 0;
998 
999     dot_bin_end = 0;
1000 
1001     for (i = 0; i < memory->mainbank.num; ++i)
1002     {
1003         FILE *fin;
1004         struct section_bin *sb = &memory->mainbank.secbin[i];
1005 
1006         if (sb->org < 0x2000)
1007             exit_log(1, "Error: Section %s has org too low 0x%04x\n", sb->section_name, sb->org);
1008 
1009         if ((sb->org < 0x4000) && (sb->org + sb->size > 0x4000))
1010             exit_log(1, "Error: Section %s extends past end of dot [0x%04x,0x%04x)\n", sb->section_name, sb->org, sb->org + sb->size);
1011 
1012         if ((sb->org < 0x4000) && (sb->org + sb->size > (0x4000 - 256)))
1013             printf("Warning: Section %s may overlap stack area in divmmc memory [0x%04x,0x%04x)\n", sb->section_name, sb->org, sb->org + sb->size);
1014 
1015         if ((sb->org >= 0x4000) && (sb->org + sb->size > 0x10000))
1016             exit_log(1, "Error: Section %s extends past end of main bank [0x%04x,0x%04x)\n", sb->section_name, sb->org, sb->org + sb->size);
1017 
1018         if (sb->org < 0x4000)
1019         {
1020             if (dot_bin_end < (sb->org + sb->size))
1021                 dot_bin_end = sb->org + sb->size;
1022         }
1023 
1024         if (sb->org >= 0x4000)
1025         {
1026             if (sb->org < main_org)
1027                 main_org = sb->org;
1028 
1029             if ((sb->org + sb->size - 1) > main_end)
1030                 main_end = sb->org + sb->size - 1;
1031         }
1032 
1033         if ((fin = fopen(sb->filename, "rb")) == NULL)
1034             exit_log(1, "Error: Can't open file %s for reading\n", sb->filename);
1035 
1036         if (fseek(fin, sb->offset, SEEK_SET) != 0)
1037             exit_log(1, "Error: Can't seek to %" PRIu32 " in file %s\n", sb->offset, sb->filename);
1038 
1039         if (fread(&mem[sb->org], sb->size, 1, fin) < 1)
1040         {
1041             fclose(fin);
1042             exit_log(1, "Error: Expected %d bytes from file %s\n", sb->size, sb->filename);
1043         }
1044 
1045         fclose(fin);
1046     }
1047 
1048     printf("Notice: Space to end of dot is %d bytes\n", 0x4000 - dot_bin_end);
1049 
1050     // round main_org down to nearest start of 8k page
1051     // round main_end down to nearest start of 8k page
1052 
1053     main_org &= 0x1e000;
1054     main_end &= 0xe000;
1055 
1056     // collect allocation table parameters
1057 
1058     if ((dotn_last_div = parameter_search(zxc->crtfile, ".map", "__DOTN_LAST_DIVMMC")) > ZXN_MAX_DIV)
1059         exit_log(1, "Error: __DOTN_LAST_DIVMMC must lie in range [0,%d]\n", ZXN_MAX_DIV);
1060 
1061     if (appmake_handle >= 0)
1062     {
1063         if ((dotn_last_page = parameter_search(zxc->crtfile, ".map", "__DOTN_LAST_PAGE")) < 0)
1064             exit_log(1, "Error: __DOTN_LAST_PAGE not defined\n");
1065 
1066         if ((dotn_last_page < 11) || (dotn_last_page > ZXN_MAX_PAGE))
1067             exit_log(1, "Error: __DOTN_LAST_PAGE %d must lie in range [11,%d]\n", dotn_last_page, ZXN_MAX_PAGE);
1068 
1069         if ((dotn_num_extra = parameter_search(zxc->crtfile, ".map", "__DOTN_NUM_EXTRA")) < 0)
1070             exit_log(1, "Error: __DOTN_NUM_EXTRA not defined\n");
1071 
1072         if (dotn_num_extra > ZXN_MAX_PAGE)
1073             exit_log(1, "Error: __DOTN_NUM_EXTRA %d must lie in range [0,%d]\n", dotn_num_extra, ZXN_MAX_PAGE);
1074 
1075         if ((dotn_last_page >= 0) && ((dotn_last_page + dotn_num_extra) > ZXN_MAX_PAGE))
1076             exit_log(1, "Error: __DOTN_LAST_PAGE and __DOTN_NUM_EXTRA together must not exceed %d\n", ZXN_MAX_PAGE);
1077 
1078         if ((dotn_main_overlay_mask = parameter_search(zxc->crtfile, ".map", "__DOTN_MAIN_OVERLAY_MASK")) < 0)
1079             exit_log(1, "Error: __DOTN_MAIN_OVERLAY_MASK not defined\n");
1080 
1081         if ((dotn_main_absolute_mask = parameter_search(zxc->crtfile, ".map", "__DOTN_MAIN_ABSOLUTE_MASK")) < 0)
1082             exit_log(1, "Error: __DOTN_MAIN_ABSOLUTE_MASK not defined\n");
1083     }
1084     else
1085     {
1086         dotn_last_page = mem[user_handle + 0] - 1;
1087         dotn_num_extra = mem[user_handle + 1];
1088 
1089         if ((dotn_last_page < 11) || (dotn_last_page > ZXN_MAX_PAGE))
1090             exit_log(1, "Error: Actual __DOTN_LAST_PAGE %d must lie in range [11,%d]\n", dotn_last_page, ZXN_MAX_PAGE);
1091 
1092         if (dotn_num_extra > ZXN_MAX_PAGE)
1093             exit_log(1, "Error: Actual __DOTN_NUM_EXTRA %d must lie in range [0,%d]\n", dotn_num_extra, ZXN_MAX_PAGE);
1094 
1095         if ((dotn_last_page >= 0) && ((dotn_last_page + dotn_num_extra) > ZXN_MAX_PAGE))
1096             exit_log(1, "Error: Actual __DOTN_LAST_PAGE and __DOTN_NUM_EXTRA together must not exceed %d\n", ZXN_MAX_PAGE);
1097 
1098         memset(z_page_alloc_table, ZXNM_SKIP, sizeof(z_page_alloc_table));
1099         memcpy(z_page_alloc_table, &mem[user_handle + 2], dotn_last_page + dotn_num_extra + 1);
1100 
1101         memcpy(z_page_table, &mem[user_handle + 2 + dotn_last_page + dotn_num_extra + 1], dotn_last_page + dotn_num_extra + 1);
1102 
1103         for (i = 0; i < (dotn_last_page + dotn_num_extra + 1); ++i)
1104             if (z_page_table[i] > ZXN_MAX_PAGE)
1105                 exit_log(1, "Error: User z_page_table[%d] has illegal value %d\n", i, z_page_table[i]);
1106 
1107         if (dotn_last_div >= 0)
1108         {
1109             dotn_last_div = mem[user_handle + 2 + 2 * (dotn_last_page + dotn_num_extra + 1)] - 1;
1110 
1111             if (dotn_last_div > ZXN_MAX_DIV)
1112                 exit_log(1, "Error: Actual __DOTN_LAST_DIVMMC must lie in range [0,%d]\n", ZXN_MAX_DIV);
1113 
1114             memset(z_div_alloc_table, ZXNM_SKIP, sizeof(z_div_alloc_table));
1115             memcpy(z_div_alloc_table, &mem[user_handle + 2 + 2 * (dotn_last_page + dotn_num_extra + 1) + 1], dotn_last_div + 1);
1116 
1117             memcpy(z_div_table, &mem[user_handle + 2 + 2 * (dotn_last_page + dotn_num_extra + 1) + 1 + dotn_last_div + 1], dotn_last_div + 1);
1118 
1119             for (i = 0; i < (dotn_last_div + 1); ++i)
1120                 if (z_div_table[i] > ZXN_MAX_DIV)
1121                     exit_log(1, "Error: User z_div_table[%d] has illegal value %d\n", i, z_div_table[i]);
1122         }
1123     }
1124 
1125     // determine main bank overlay mask
1126 
1127     if (main_org < 0x10000)
1128     {
1129         overlay_alloc_mask = (0xff << (main_org / 0x2000)) & 0xff;
1130         overlay_load_mask = (0xff >> (7 - main_end / 0x2000)) & overlay_alloc_mask;
1131     }
1132     else
1133     {
1134         overlay_alloc_mask = 0;
1135         overlay_load_mask = 0;
1136     }
1137 
1138     overlay_alloc_mask |= dotn_main_overlay_mask;
1139     overlay_alloc_mask &= ~dotn_main_absolute_mask;
1140 
1141     if (appmake_handle >= 0)
1142     {
1143         printf("Notice: Main bank allocation mask is 0x%02x\n", overlay_alloc_mask);
1144         printf("Notice: Main bank load mask is 0x%02x\n", overlay_load_mask);
1145     }
1146     else
1147     {
1148         printf("Notice: Main bank occupied mask is 0x%02x\n", overlay_load_mask);
1149     }
1150 
1151     // overlay_load_mask: bits indicate which main bank pages should be loaded
1152     // overlay_alloc_mask: bits indicate which main bank pages should be allocated
1153 
1154     // create output file
1155 
1156     if ((fout = fopen(outname, "wb")) == NULL)
1157         exit_log(1, "Error: Couldn't create output file %s\n", outname);
1158 
1159     // write dot portion to file
1160 
1161     fwrite(&mem[0x2000], 8192, 1, fout);
1162 
1163     // create page table contents
1164 
1165     actual_last_page = -1;
1166 
1167     for (i = 0; i <= ZXN_MAX_PAGE; ++i)
1168     {
1169         int main_mask;
1170         struct memory_bank *mb = &memory->bankspace[bsnum_page].membank[i];
1171 
1172         // check for main bank page
1173 
1174         switch (i)
1175         {
1176             // BANK 0
1177 
1178         case 0:
1179             main_mask = 0x40;   // mmu6
1180             break;
1181 
1182         case 1:
1183             main_mask = 0x80;   // mmu7
1184             break;
1185 
1186             // BANK 2
1187 
1188         case 4:
1189             main_mask = 0x10;   // mmu4
1190             break;
1191 
1192         case 5:
1193             main_mask = 0x20;   // mmu5
1194             break;
1195 
1196             // BANK 5
1197 
1198         case 10:
1199             main_mask = 0x04;   // mmu2
1200             break;
1201 
1202         case 11:
1203             main_mask = 0x08;   // mmu3
1204             break;
1205 
1206             // not main bank
1207 
1208         default:
1209             main_mask = 0;
1210             break;
1211         }
1212 
1213         // create allocation table entry
1214 
1215         if (user_handle >= 0)
1216         {
1217             // allocation table is dictated by user
1218 
1219             if (z_page_alloc_table[i] != ZXNM_SKIP)
1220                 actual_last_page = i;
1221 
1222             if ((z_page_alloc_table[i] == ZXNM_ALLOCATE_LOAD) || (z_page_alloc_table[i] == ZXNM_ALLOCATE_LOAD))
1223             {
1224                 if (main_mask)
1225                 {
1226                     printf("Page %d, main bank %s\n", i, (z_page_alloc_table[i] == ZXNM_ALLOCATE_LOAD) ? "allocate and load" : "load");
1227                     fwrite(&mem[(z88dk_ffs(main_mask) - 1) * 0x2000], 8192, 1, fout);
1228                 }
1229                 else
1230                 {
1231                     unsigned char page[8192];
1232 
1233                     memset(page, fillbyte, sizeof(page));
1234 
1235                     if (mb->num > 0)
1236                     {
1237                         printf("Page %d, %s", i, (z_page_alloc_table[i] == ZXNM_ALLOCATE_LOAD) ? "allocate and load" : "load");
1238 
1239                         zxn_construct_page_contents(page, mb, sizeof(page), fillbyte);
1240                         mb_remove_bank(&memory->bankspace[bsnum_page], i, zxc->clean);
1241                     }
1242                     else
1243                         printf("Warning: Empty Page %d loaded\n", i);
1244 
1245                     // append to dotn
1246 
1247                     fwrite(page, sizeof(page), 1, fout);
1248                 }
1249             }
1250             else if (z_page_alloc_table[i] == ZXNM_ALLOCATE)
1251                 printf("Page %d, allocated\n", i);
1252         }
1253         else
1254         {
1255             // logical to physical mapping is 1:1 until modified at load time
1256 
1257             z_page_table[i] = i;
1258 
1259             // create allocation entry
1260 
1261             z_page_alloc_table[i] = ZXNM_SKIP;                   // page is physical
1262 
1263             if (main_mask)
1264             {
1265                 // page is in main bank
1266 
1267                 if (overlay_alloc_mask & overlay_load_mask & main_mask)
1268                     z_page_alloc_table[i] = ZXNM_ALLOCATE_LOAD;  // indicate allocate and load
1269                 else if (overlay_alloc_mask & main_mask)
1270                     z_page_alloc_table[i] = ZXNM_ALLOCATE;       // indicate allocate
1271                 else if (overlay_load_mask & main_mask)
1272                     z_page_alloc_table[i] = ZXNM_LOAD;           // indicate load
1273 
1274                 if (z_page_alloc_table[i] != ZXNM_SKIP)
1275                 {
1276                     printf("Page %d, main bank %s\n", i, (z_page_alloc_table[i] == ZXNM_ALLOCATE_LOAD) ? "allocate and load" : ((z_page_alloc_table[i] == ZXNM_ALLOCATE) ? "allocate" : "load"));
1277 
1278                     actual_last_page = i;
1279 
1280                     if ((z_page_alloc_table[i] == ZXNM_ALLOCATE_LOAD) || (z_page_alloc_table[i] == ZXNM_LOAD))
1281                         fwrite(&mem[(z88dk_ffs(main_mask) - 1) * 0x2000], 8192, 1, fout);
1282                 }
1283             }
1284             else
1285             {
1286                 // page not in main bank, check if part of the program
1287 
1288                 if (mb->num > 0)
1289                 {
1290                     unsigned char page[8192];
1291 
1292                     // indicate allocate and load
1293 
1294                     z_page_alloc_table[i] = ZXNM_ALLOCATE_LOAD;
1295 
1296                     printf("Page %d, allocate and load", i);
1297                     zxn_construct_page_contents(page, mb, sizeof(page), fillbyte);
1298 
1299                     // append to dotn
1300 
1301                     actual_last_page = i;
1302                     fwrite(page, sizeof(page), 1, fout);
1303 
1304                     // remove this PAGE from memory model
1305 
1306                     mb_remove_bank(&memory->bankspace[bsnum_page], i, zxc->clean);
1307                 }
1308             }
1309         }
1310     }
1311 
1312     printf("dotn_last_page = %d\n", dotn_last_page);
1313     printf("dotn_num_extra = %d\n", dotn_num_extra);
1314 
1315     // append extra pages to alloc table
1316 
1317     if (appmake_handle >= 0)
1318         memset(&z_page_alloc_table[dotn_last_page + 1], ZXNM_ALLOCATE, dotn_num_extra);
1319 
1320     // create divmmc table
1321 
1322     actual_last_div = -1;
1323 
1324     for (i = 0; i <= ZXN_MAX_DIV; ++i)
1325     {
1326         struct memory_bank *mb = &memory->bankspace[bsnum_div].membank[i];
1327 
1328         if (user_handle >= 0)
1329         {
1330             // allocation table is dictated by user
1331 
1332             if (z_div_alloc_table[i] != ZXNM_SKIP)
1333                 actual_last_div = i;
1334 
1335             if ((z_div_alloc_table[i] == ZXNM_ALLOCATE_LOAD) || (z_div_alloc_table[i] == ZXNM_ALLOCATE_LOAD))
1336             {
1337                 unsigned char page[8192];
1338 
1339                 memset(page, fillbyte, sizeof(page));
1340 
1341                 if (mb->num > 0)
1342                 {
1343                     printf("DIVMMC Page %d, %s", i, (z_page_alloc_table[i] == ZXNM_ALLOCATE_LOAD) ? "allocate and load" : "load");
1344 
1345                     zxn_construct_page_contents(page, mb, sizeof(page), fillbyte);
1346                     mb_remove_bank(&memory->bankspace[bsnum_page], i, zxc->clean);
1347                 }
1348                 else
1349                     printf("Warning: Empty DIVMMC Page %d loaded\n", i);
1350 
1351                 // append to dotn
1352 
1353                 fwrite(page, sizeof(page), 1, fout);
1354             }
1355             else if (z_div_alloc_table[i] == ZXNM_ALLOCATE)
1356                 printf("DIVMMC Page %d, allocated\n", i);
1357         }
1358         else
1359         {
1360             // logical to physical mapping is 1:1 until modified at load time
1361 
1362             z_div_table[i] = i;
1363 
1364             // create allocation entry
1365 
1366             z_div_alloc_table[i] = ZXNM_SKIP;                    // page is physical
1367 
1368             if (mb->num > 0)
1369             {
1370                 unsigned char page[8192];
1371 
1372                 // indicate allocate and load
1373 
1374                 z_div_alloc_table[i] = ZXNM_ALLOCATE_LOAD;
1375 
1376                 printf("DIVMMC Page %d, allocate and load", i);
1377                 zxn_construct_page_contents(page, mb, sizeof(page), fillbyte);
1378 
1379                 // append to dotn
1380 
1381                 actual_last_div = i;
1382                 fwrite(page, sizeof(page), 1, fout);
1383 
1384                 // remove this PAGE from memory model
1385 
1386                 mb_remove_bank(&memory->bankspace[bsnum_div], i, zxc->clean);
1387             }
1388         }
1389     }
1390 
1391     if (dotn_last_div >= 0)
1392         printf("dotn_last_div = %d\n", dotn_last_div);
1393 
1394     // write page table data into dotn command
1395 
1396     if ((actual_last_page >= 0) && (dotn_last_page < actual_last_page))
1397     {
1398         fclose(fout);
1399         remove(outname);
1400         exit_log(1, "Error: Insufficient space for allocation table\n       Increase DOTN_LAST_PAGE to %d\n", actual_last_page);
1401     }
1402 
1403     if ((actual_last_div >= 0) && (dotn_last_div < actual_last_div))
1404     {
1405         fclose(fout);
1406         remove(outname);
1407         exit_log(1, "Error: Insufficient space for allocation table\n       Increase DOTN_LAST_DIVMMC to %d\n", actual_last_div);
1408     }
1409 
1410 /*
1411     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1412     ;; contiguous section filled in by appmake
1413 
1414     __appmake_handle:
1415 
1416     PUBLIC __z_page_sz
1417     PUBLIC __z_extra_sz
1418 
1419     ; z_page_sz must be at least 11
1420     ; z_extra_sz can be 0
1421 
1422     __z_page_sz:             defb __DOTN_LAST_PAGE + 1  ; must be in this order
1423     __z_extra_sz:            defb __DOTN_NUM_EXTRA      ; must be in this order
1424 
1425     PUBLIC __z_page_alloc_table
1426     PUBLIC __z_extra_alloc_table
1427 
1428     ; 0xff = skip
1429     ; 0xfe = allocate but do not load
1430     ; 0xfd = load but do not allocate
1431     ; 0xfc = allocate and load
1432 
1433     __z_page_alloc_table:    defs __DOTN_LAST_PAGE + 1, 0xff  ; must be in this order
1434     __z_extra_alloc_table:   defs __DOTN_NUM_EXTRA, 0xfe      ; must be in this order
1435 
1436     PUBLIC __z_page_table
1437     PUBLIC __z_extra_table
1438 
1439     ; these tables perform logical to physical page mapping and
1440     ; are normally filled in with a 1:1 mapping ie numbers 0,1,2,...
1441     ; (appmake does this when it builds the dotn command)
1442 
1443     ; entries are overwritten if a particular page is allocated
1444 
1445     __z_page_table:          defs __DOTN_LAST_PAGE + 1, 0xff  ; must be in this order
1446     __z_extra_table:         defs __DOTN_NUM_EXTRA            ; must be in this order
1447 
1448     IF __DOTN_LAST_DIVMMC >= 0
1449 
1450     PUBLIC __z_divmmc_sz
1451 
1452     __z_div_sz:           defb __DOTN_LAST_DIVMMC + 1
1453 
1454     PUBLIC __z_div_alloc_table
1455 
1456     __z_div_alloc_table:  defs __DOTN_LAST_DIVMMC + 1, 0xff
1457 
1458     PUBLIC __z_div_table
1459 
1460     __z_div_table:        defs __DOTN_LAST_DIVMMC + 1, 0xff
1461 
1462     ENDIF
1463 
1464     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1465 */
1466 
1467     if (appmake_handle >= 0)
1468     {
1469         fseek(fout, appmake_handle - 0x2000, SEEK_SET);
1470 
1471         writebyte(dotn_last_page + 1, fout);
1472         writebyte(dotn_num_extra, fout);
1473 
1474         fwrite(z_page_alloc_table, dotn_last_page + dotn_num_extra + 1, 1, fout);
1475         fwrite(z_page_table, dotn_last_page + 1, 1, fout);
1476 
1477         for (i = 0; i < dotn_num_extra; ++i)
1478             writebyte(0xff, fout);
1479 
1480         if (dotn_last_div >= 0)
1481         {
1482             writebyte(dotn_last_div + 1, fout);
1483 
1484             fwrite(z_div_alloc_table, dotn_last_div + 1, 1, fout);
1485             fwrite(z_div_table, dotn_last_div + 1, 1, fout);
1486         }
1487     }
1488 
1489     if ((z_alt_filename = parameter_search(zxc->crtfile, ".map", "__z_alt_filename")) >= 0)
1490     {
1491         fseek(fout, z_alt_filename - 0x2000, SEEK_SET);
1492         fprintf(fout, "%s", outname);
1493     }
1494 
1495     fclose(fout);
1496 
1497     return 0;
1498 }
1499 
1500 
1501 /*
1502 48k/128k SNA SNAPSHOT
1503 July-September 2017 aralbrec
1504 */
1505 
1506 enum
1507 {
1508     SNA_REG_I = 0,
1509     SNA_REGP_HL = 1,
1510     SNA_REGP_DE = 3,
1511     SNA_REGP_BC = 5,
1512     SNA_REGP_AF = 7,
1513     SNA_REG_HL = 9,
1514     SNA_REG_DE = 11,
1515     SNA_REG_BC = 13,
1516     SNA_REG_IY = 15,
1517     SNA_REG_IX = 17,
1518     SNA_IFF2 = 19,
1519     SNA_REG_R = 20,
1520     SNA_REG_AF = 21,
1521     SNA_REG_SP = 23,
1522     SNA_IntMode = 25,
1523     SNA_BorderColor = 26,
1524     SNA_128_PC = 27,
1525     SNA_128_port_7FFD = 29,
1526     SNA_128_TRDOS = 30
1527 };
1528 
1529 uint8_t sna_state[31];
1530 uint8_t mem128[49152 + 16384 * 8];
1531 
zx_sna(struct zx_common * zxc,struct zx_sna * zxs,struct banked_memory * memory,int is_zxn)1532 int zx_sna(struct zx_common *zxc, struct zx_sna *zxs, struct banked_memory *memory, int is_zxn)
1533 {
1534     FILE *fin, *fout;
1535     char filename[FILENAME_MAX + 1];
1536     int i, j;
1537     int is_128 = 0;
1538     int bsnum_bank;
1539     int return_to_basic = 0;
1540     int z_sna_filename;
1541 
1542     // force 128k snapshot
1543 
1544     is_128 = zxs->force_128 != 0;
1545 
1546     // find bankspace BANK
1547 
1548     bsnum_bank = mb_find_bankspace(memory, "BANK");
1549 
1550     // find code origin
1551 
1552     if ((zxc->origin == -1) && ((zxc->origin = get_org_addr(zxc->crtfile)) == -1))
1553         exit_log(1, "Error: ORG address cannot be determined\n");
1554 
1555     if ((zxc->origin < 0) || (zxc->origin > 0xffff))
1556         exit_log(1, "Error: ORG address %d not in range\n", zxc->origin);
1557 
1558     // determine stack location
1559 
1560     if (zxs->stackloc == -1)
1561         zxs->stackloc = parameter_search(zxc->crtfile, ".map", "__register_sp");
1562 
1563     if (abs(zxs->stackloc) > 0xffff)
1564         exit_log(1, "Error: Stack pointer %d out of range\n", zxs->stackloc);
1565 
1566     // determine initial ei/di state
1567 
1568     if (zxs->intstate == -1)
1569         zxs->intstate = parameter_search(zxc->crtfile, ".map", "__crt_enable_eidi");
1570 
1571     zxs->intstate = (zxs->intstate == -1) ? 0xff : ((zxs->intstate & 0x01) ? 0 : 0xff);
1572 
1573     // returning to basic?
1574 
1575     return_to_basic = parameter_search(zxc->crtfile, ".map", "__crt_on_exit");
1576     return_to_basic = (return_to_basic != -1) && ((return_to_basic & 0x30000) == 0x30000) && ((return_to_basic & 0xa) == 0x2);
1577 
1578     // check if 128k snapshot is needed
1579 
1580     if (bsnum_bank >= 0)
1581     {
1582         for (i = 0; i < 8; ++i)
1583         {
1584             struct memory_bank *mb = &memory->bankspace[bsnum_bank].membank[i];
1585 
1586             if (mb->num > 0)
1587             {
1588                 // banks 5,2,0 should be empty as they've been merged already
1589 
1590                 if ((i == 0) || (i == 2) || (i == 5))
1591                     exit_log(1, "Error: Bank %d should have been merged into the main bank\n", i);
1592 
1593                 is_128++;
1594                 break;
1595             }
1596         }
1597     }
1598 
1599     // check for section overlaps
1600 
1601     if (mb_sort_banks(memory))
1602         exit_log(1, "Aborting... errors in one or more binaries\n");
1603 
1604     // prime snapshot memory contents
1605 
1606     memset(mem128, 0, sizeof(mem128));
1607 
1608     snprintf(filename, sizeof(filename), "%s%s", c_install_dir, is_128 ? (is_zxn ? ZXN_NEXTOS_PROTOTYPE : ZX128_SNA_PROTOTYPE) : ZX48_SNA_PROTOTYPE);
1609 
1610     if ((fin = fopen(filename, "rb")) == NULL)
1611         exit_log(1, "Error: SNA prototype %s not found\n", filename);
1612 
1613     if (1 != fread(sna_state, 27, 1, fin)) { fclose(fin); exit_log(1, "Could not read required data from <%s>\n",filename); }
1614 
1615     if (fread(mem128, 49152, 1, fin) < 1)
1616     {
1617         fclose(fin);
1618         exit_log(1, "Error: SNA prototype %s is shorter than 49179 bytes\n", filename);
1619     }
1620 
1621     if (is_128)
1622     {
1623         if (1 != fread(&sna_state[SNA_128_PC], 4, 1, fin)) { fclose(fin); exit_log(1, "Could not read required data from <%s>\n",filename); }
1624 
1625         // 5,2,0 (48k) 1,3,4,6,7
1626 
1627         for (i = 0; i < 8; ++i)
1628         {
1629             if ((i != 0) && (i != 2) && (i != 5))
1630             {
1631                 if (fread(&mem128[49152 + i * 16384], 16384, 1, fin) < 1)
1632                 {
1633                     fclose(fin);
1634                     exit_log(1, "Error: SNA prototype %s is shorter than 131103 bytes\n", filename);
1635                 }
1636             }
1637         }
1638     }
1639 
1640     fclose(fin);
1641 
1642     // clear screen
1643 
1644     memset(mem128, 0, 6144);
1645     memset(mem128 + 6144, 0x38, 768);
1646 
1647     // write main bank into memory image
1648 
1649     for (i = 0; i < memory->mainbank.num; ++i)
1650     {
1651         struct section_bin *sb = &memory->mainbank.secbin[i];
1652 
1653         if (sb->org < 0x4000)
1654             exit_log(1, "Error: Section %s has org in rom 0x%04x\n", sb->section_name, sb->org);
1655 
1656         if ((fin = fopen(sb->filename, "rb")) == NULL)
1657             exit_log(1, "Error: Can't open file %s for reading\n", sb->filename);
1658 
1659         if (fseek(fin, sb->offset, SEEK_SET) != 0)
1660             exit_log(1, "Error: Can't seek to %" PRIu32 " in file %s\n", sb->offset, sb->filename);
1661 
1662         if (fread(&mem128[sb->org - 0x4000], sb->size, 1, fin) < 1)
1663         {
1664             fclose(fin);
1665             exit_log(1, "Error: Expected %d bytes from file %s\n", sb->size, sb->filename);
1666         }
1667 
1668         fclose(fin);
1669     }
1670 
1671     // write other memory banks into memory image
1672 
1673     if (is_128 && (bsnum_bank >= 0))
1674     {
1675         for (i = 0; i < 8; ++i)
1676         {
1677             for (j = 0; j < memory->bankspace[bsnum_bank].membank[i].num; ++j)
1678             {
1679                 struct section_bin *sb = &memory->bankspace[bsnum_bank].membank[i].secbin[j];
1680 
1681                 if ((fin = fopen(sb->filename, "rb")) == NULL)
1682                     exit_log(1, "Error: Can't open file %s for reading\n", sb->filename);
1683 
1684                 if (fseek(fin, sb->offset, SEEK_SET) != 0)
1685                     exit_log(1, "Error: Can't seek to %" PRIu32 " in file %s\n", sb->offset, sb->filename);
1686 
1687                 if (fread(&mem128[49152 + i * 16384 + sb->org - 0xc000], sb->size, 1, fin) < 1)
1688                 {
1689                     fclose(fin);
1690                     exit_log(1, "Error: Expected %d bytes from file %s\n", sb->size, sb->filename);
1691                 }
1692 
1693                 fclose(fin);
1694             }
1695         }
1696     }
1697 
1698     // initialize snapshot state
1699 
1700     if (zxs->stackloc < -1)
1701     {
1702         if (zxs->stackloc > -0x4000)
1703             zxs->stackloc = -1;
1704         else
1705             zxs->stackloc = mem128[(abs(zxs->stackloc) - 0x4000) % 0xc000] + 256 * mem128[(abs(zxs->stackloc) - 0x4000 + 1) % 0xc000];
1706     }
1707 
1708     if (return_to_basic || (zxs->stackloc < 0))
1709     {
1710         zxs->stackloc = sna_state[SNA_REG_SP] + 256 * sna_state[SNA_REG_SP + 1];
1711 
1712         if (!is_128)
1713             zxs->stackloc = (zxs->stackloc + 2) & 0xffff;   // 48k sna contains an extra address on the stack to restart the snapshot
1714     }
1715 
1716     if (!is_128)
1717     {
1718         zxs->stackloc = (zxs->stackloc - 2) & 0xffff;
1719         mem128[(zxs->stackloc - 0x4000) % 0xc000] = zxc->origin & 0xff;
1720         mem128[(zxs->stackloc - 0x4000 + 1) % 0xc000] = zxc->origin / 256;
1721     }
1722 
1723     sna_state[SNA_REG_SP] = zxs->stackloc & 0xff;
1724     sna_state[SNA_REG_SP + 1] = zxs->stackloc / 256;
1725 
1726     sna_state[SNA_IFF2] = zxs->intstate;
1727 
1728     sna_state[SNA_128_PC] = zxc->origin & 0xff;
1729     sna_state[SNA_128_PC + 1] = zxc->origin / 256;
1730 
1731     sna_state[SNA_128_port_7FFD] = 0x10;
1732     sna_state[SNA_128_TRDOS] = 0;
1733 
1734     // determine output filename
1735 
1736     if (zxc->outfile == NULL)
1737     {
1738         strcpy(filename, zxc->binname);
1739         suffix_change(filename, (zxs->snx == 0) ? ".sna" : ".snx");
1740     }
1741     else
1742         strcpy(filename, zxc->outfile);
1743 
1744     if (strcmp(zxc->binname, filename) == 0)
1745         exit_log(1, "Error: Input and output filenames must be different\n");
1746 
1747     // write sna filename into memory image
1748 
1749     z_sna_filename = parameter_search(zxc->crtfile, ".map", "__z_sna_filename");
1750 
1751     if (z_sna_filename >= 0x4000)
1752         sprintf((char *)&mem128[z_sna_filename - 0x4000], "%.12s", filename);
1753 
1754     // create sna file
1755 
1756     if ((fout = fopen(filename, "wb")) == NULL)
1757         exit_log(1, "Error: Could not create output file %s\n", filename);
1758 
1759     fwrite(sna_state, 27, 1, fout);
1760     fwrite(mem128, 49152, 1, fout);
1761 
1762     if (is_128)
1763     {
1764         fwrite(&sna_state[SNA_128_PC], 4, 1, fout);
1765 
1766         // 5,2,0 (48k) 1,3,4,6,7
1767 
1768         for (i = 0; i < 8; ++i)
1769         {
1770             if ((i != 0) && (i != 2) && (i != 5))
1771                 fwrite(&mem128[49152 + i * 16384], 16384, 1, fout);
1772         }
1773     }
1774 
1775     // output file is kept open
1776 
1777     zxs->fsna = fout;
1778 
1779     return 0;
1780 }
1781 
1782 
1783 /*
1784    ZX NEXT NEX FORMAT
1785    June, Sept 2018 aralbrec
1786 
1787    .NEX file format (V1.0)
1788    =======================
1789    unsigned char Next[4];			//"Next"
1790    unsigned char VersionNumber[4];	//"V1.1"
1791    unsigned char RAM_Required;		//0=768K, 1=1792K
1792    unsigned char NumBanksToLoad;	//0-112 x 16K banks
1793    unsigned char LoadingScreen;	    //see implementation
1794    unsigned char BorderColour;		//0-7 ld a,BorderColour:out(254),a
1795    unsigned short SP;				//Stack Pointer
1796    unsigned short PC;				//Code Entry Point : $0000 = Don't run just load.
1797    unsigned short NumExtraFiles;	//NumExtraFiles
1798    unsigned char Banks[64+48];		//Which 16K Banks load
1799    unsigned char LoadBar;           //Enable the layer 2 load bar
1800    unsigned char LoadColour;        //Colour of the load bar
1801    unsigned char LoadDelay;         //Delay in interrupts after each 16k loaded
1802    unsigned char StartDelay;        //Delay in interrupts before starting the program
1803    unsigned char DontSetNextRegs;   //Do not reset nextreg to known state
1804    unsigned char CoreMajor;         //Minimum core version
1805    unsigned char CoreMinor;
1806    unsigned char CoreSubMinor;
1807    unsigned char HiResCol;          //Timex hi-res loading screen colour (paper bits)
1808 
1809    unsigned char RestOf512Bytes[512-(4+4 +1+1+1+1 +2+2+2 +64+48 +1+1+1+1)];
1810    if LoadingScreen!=0 {
1811      uint16_t palette[256];  // 8 bits of palette entry followed by 9th bit of palette entry in two bytes (optional)
1812      uint8_t  Layer2LoadingScreen[49152];  // can also be other ula mode screens see implementation
1813    }
1814    Banks 5,2,0 in order if present
1815    Banks 1,3,4,6,7,... in order if present (ie not 5,2,0)
1816 */
1817 
1818 struct nex_hdr
1819 {
1820     uint8_t Next[4];
1821     uint8_t VersionNumber[4];
1822     uint8_t RAM_Required;
1823     uint8_t NumBanksToLoad;
1824     uint8_t LoadingScreen;
1825     uint8_t BorderColour;
1826     uint8_t SP[2];
1827     uint8_t PC[2];
1828     uint8_t NumExtraFiles[2];
1829     uint8_t Banks[48 + 64];
1830     uint8_t LoadBar;
1831     uint8_t LoadColour;
1832     uint8_t LoadDelay;
1833     uint8_t StartDelay;
1834     uint8_t DontSetNextRegs;
1835     uint8_t CoreMajor;
1836     uint8_t CoreMinor;
1837     uint8_t CoreSubMinor;
1838     uint8_t HiResCol;
1839     uint8_t Padding[512 - (4 + 4 + 1 + 1 + 1 + 1 + 2 + 2 + 2 + 64 + 48 + 4 + 5)];
1840 };
1841 
1842 struct nex_hdr nh;
1843 
1844 #define NEX_SCREEN_SIZE (512+49152)
1845 
zxn_nex(struct zx_common * zxc,struct zxn_nex * zxnex,struct banked_memory * memory,int fillbyte)1846 int zxn_nex(struct zx_common *zxc, struct zxn_nex *zxnex, struct banked_memory *memory, int fillbyte)
1847 {
1848     int  i, j;
1849     int  bsnum_bank;
1850     char outname[FILENAME_MAX];
1851 
1852     int register_sp;
1853     int crt_org_code;
1854     int core_version;
1855 
1856     int mainbank_occupied;
1857 
1858     FILE *fin;
1859     FILE *fout;
1860 
1861     // find BANK space
1862 
1863     if ((bsnum_bank = mb_find_bankspace(memory, "BANK")) < 0)
1864         exit_log(1, "Error: Can't find BANK space\n");
1865 
1866     // determine output filename
1867 
1868     if (zxc->outfile == NULL)
1869         snprintf(outname, sizeof(outname), "%s", zxc->binname);
1870     else
1871         snprintf(outname, sizeof(outname), "%s", zxc->outfile);
1872 
1873     suffix_change(outname, ".nex");
1874 
1875     // collect parameters
1876 
1877     if (zxnex->norun)
1878     {
1879         crt_org_code = 0;
1880         register_sp = 0;
1881     }
1882     else
1883     {
1884         if ((register_sp = parameter_search(zxc->crtfile, ".map", "__register_sp")) < 0)
1885             exit_log(1, "Error: Stack location must be set (%d)\n", register_sp);
1886 
1887         if ((crt_org_code = parameter_search(zxc->crtfile, ".map", "__crt_org_code")) < 0)
1888             exit_log(1, "Error: Unable to find org address\n");
1889     }
1890 
1891     core_version = parameter_search(zxc->crtfile, ".map", "__CRT_CORE_VERSION");
1892 
1893     // open output file
1894 
1895     if ((fout = fopen(outname, "wb")) == NULL)
1896         exit_log(1, "Error: Couldn't create output file %s\n", outname);
1897 
1898     // write incomplete nex header
1899 
1900     memset(&nh, 0, sizeof(nh));
1901     fwrite(&nh, sizeof(nh), 1, fout);
1902 
1903     // write loading screen if present
1904 
1905     if (zxnex->screen)
1906     {
1907         unsigned char scr[NEX_SCREEN_SIZE];
1908 
1909         memset(scr, 0, sizeof(scr));
1910 
1911         if ((fin = fopen(zxnex->screen, "rb")) == NULL)
1912             fprintf(stderr, "Warning: NEX loading screen not added; can't open %s\n", zxnex->screen);
1913         else
1914         {
1915             int size;
1916 
1917             if (zxnex->screen_ula)
1918             {
1919                 size = 6912;
1920                 nh.LoadingScreen = 0x02;
1921             }
1922             else if (zxnex->screen_lores)
1923             {
1924                 size = 12288;
1925                 nh.LoadingScreen = 0x04;
1926             }
1927             else if (zxnex->screen_hires)
1928             {
1929                 size = 12288;
1930                 nh.LoadingScreen = 0x08;
1931             }
1932             else if (zxnex->screen_hicol)
1933             {
1934                 size = 12288;
1935                 nh.LoadingScreen = 0x10;
1936             }
1937             else
1938             {
1939                 size = 48 * 1024;
1940                 nh.LoadingScreen = 0x01;
1941             }
1942 
1943             if (zxnex->nopalette)
1944                 nh.LoadingScreen += 0x80;
1945             else
1946                 size += 512;
1947 
1948             if (fread(scr, size, 1, fin) == 1)
1949                 fwrite(scr, size, 1, fout);
1950             else
1951             {
1952                 nh.LoadingScreen = 0;
1953                 fprintf(stderr, "Warning: Loading screen not used (wrong size, palette %sexpected)\n", zxnex->nopalette ? "not " : "");
1954             }
1955 
1956             fclose(fin);
1957         }
1958     }
1959 
1960     // write main memory bank: 16k banks 5,2,0
1961 
1962     {
1963         unsigned char mem[48*1024];
1964 
1965         memset(mem, fillbyte, sizeof(mem));
1966 
1967         mainbank_occupied = 0;
1968 
1969         for (i = 0; i < memory->mainbank.num; ++i)
1970         {
1971             struct section_bin *sb = &memory->mainbank.secbin[i];
1972 
1973             if (sb->org < 0x4000)
1974                 exit_log(1, "Error: Section %s has org in rom 0x%04x\n", sb->section_name, sb->org);
1975 
1976             if ((fin = fopen(sb->filename, "rb")) == NULL)
1977                 exit_log(1, "Error: Can't open file %s for reading\n", sb->filename);
1978 
1979             if (fseek(fin, sb->offset, SEEK_SET) != 0)
1980                 exit_log(1, "Error: Can't seek to %" PRIu32 " in file %s\n", sb->offset, sb->filename);
1981 
1982             if (fread(&mem[sb->org - 0x4000], sb->size, 1, fin) < 1)
1983             {
1984                 fclose(fin);
1985                 exit_log(1, "Error: Expected %d bytes from file %s\n", sb->size, sb->filename);
1986             }
1987 
1988             fclose(fin);
1989 
1990             if (sb->size > 0)
1991             {
1992                 for (j = (sb->org >> 13) & 0x7; j <= (((sb->org + sb->size - 1) >> 13) & 0x7); ++j)
1993                     mainbank_occupied |= 1 << j;
1994             }
1995         }
1996 
1997         // bank 5
1998 
1999         if (mainbank_occupied & 0x0c)
2000         {
2001             nh.Banks[5] = 1;
2002             fwrite(mem, 16384, 1, fout);
2003             nh.NumBanksToLoad++;
2004         }
2005 
2006         // bank 2
2007 
2008         if (mainbank_occupied & 0x30)
2009         {
2010             nh.Banks[2] = 1;
2011             fwrite(&mem[16384], 16384, 1, fout);
2012             nh.NumBanksToLoad++;
2013         }
2014 
2015         // bank 0
2016 
2017         if (mainbank_occupied & 0xc0)
2018         {
2019             nh.Banks[0] = 1;
2020             fwrite(&mem[32768], 16384, 1, fout);
2021             nh.NumBanksToLoad++;
2022         }
2023     }
2024 
2025     // write all occupied 16k banks but skip 5,2,0
2026 
2027     for (i = 0; i <= ZXN_MAX_BANK; ++i)
2028     {
2029         struct memory_bank *mb = &memory->bankspace[bsnum_bank].membank[i];
2030 
2031         // banks 5, 2, 0 were in the main memory bank
2032 
2033         if ((i != 5) && (i != 2) && (i != 0))
2034         {
2035             if (mb->num > 0)
2036             {
2037                 unsigned char bank[0x4000];
2038 
2039                 // indicate bank is present
2040 
2041                 nh.Banks[i] = 1;                     // bank is present
2042                 if (i >= 48) nh.RAM_Required = 1;    // if need memory expansion
2043                 nh.NumBanksToLoad++;                 // increase bank count
2044 
2045                 // construct bank contents
2046 
2047                 printf("Bank %d", i);
2048                 zxn_construct_page_contents(bank, mb, sizeof(bank), fillbyte);
2049 
2050                 // append to output
2051 
2052                 fwrite(bank, sizeof(bank), 1, fout);
2053 
2054                 // remove this BANK from memory model
2055 
2056                 mb_remove_bank(&memory->bankspace[bsnum_bank], i, zxc->clean);
2057             }
2058         }
2059     }
2060 
2061     // complete the header
2062 
2063     memcpy(&nh.Next, "Next", 4);
2064     memcpy(&nh.VersionNumber, "V1.1", 4);
2065 
2066     nh.BorderColour = zxnex->border & 0x7;
2067 
2068     if (zxnex->loadbar >= 0)
2069     {
2070         nh.LoadBar = 1;
2071         nh.LoadColour = zxnex->loadbar;
2072     }
2073 
2074     nh.LoadDelay = zxnex->loaddelay;
2075     nh.StartDelay = zxnex->startdelay;
2076 
2077     nh.SP[0] = register_sp & 0xff;
2078     nh.SP[1] = (register_sp >> 8) & 0xff;
2079 
2080     nh.PC[0] = crt_org_code & 0xff;
2081     nh.PC[1] = (crt_org_code >> 8) & 0xff;
2082 
2083     if (core_version > 0)
2084     {
2085         nh.CoreMajor = core_version / 100000;
2086         nh.CoreMinor = (core_version / 1000) % 100;
2087         nh.CoreSubMinor = core_version % 1000;
2088     }
2089 
2090     nh.DontSetNextRegs = (zxnex->noreset != 0);
2091 
2092     // write the completed header to output
2093 
2094     rewind(fout);
2095     fwrite(&nh, sizeof(nh), 1, fout);
2096 
2097     fclose(fout);
2098 
2099     return 0;
2100 }
2101 
2102 // Layout a +3 file
2103 // \param inbuf The input buffer containing the file
2104 // \param filelen Number of bytes to read
2105 // \param file_type Spectrum file type
2106 // \param total_len How long the returned block is
2107 // \return Allocated buffer containing the whole file (should be freed)
zx3_layout_file(uint8_t * inbuf,size_t filelen,int start_address,int file_type,size_t * total_len_ptr)2108 uint8_t *zx3_layout_file(uint8_t *inbuf, size_t filelen, int start_address, int file_type, size_t *total_len_ptr)
2109 {
2110      uint8_t *buf = must_malloc(filelen + 128);
2111      int      cksum, i;
2112      size_t   total_len = 0;
2113 
2114      memcpy(buf,"PLUS3DOS", 8);
2115      buf[8] = 0x1a;
2116      buf[9] = 0x01;	// Issue number
2117      buf[10] = 0x00;    // Version number
2118      // 11 - 14 = filelength
2119      // 15 - 22 = header data
2120      // +---------------+-------+-------+-------+-------+-------+-------+-------+
2121      // | BYTE		|   0	|   1	|   2	|   3	|   4	|   5	|   6	|
2122      // +---------------+-------+-------+-------+-------+-------+-------+-------+
2123      // | Program	    0	file length	8000h or LINE	offset to prog	|
2124      // | Numeric array	    1	file length	xxx	name	xxx	xxx	|
2125      // | Character array   2	file length	xxx	name	xxx	xxx	|
2126      // | CODE or SCREEN$   3	file length	load address	xxx	xxx	|
2127      // +-----------------------------------------------------------------------+
2128      // 127 = checksum (sum of 0..126 mod 256)
2129      while ( total_len < filelen ) {
2130          buf[128 + total_len] = inbuf[total_len];
2131          total_len++;
2132      }
2133 
2134      // Now populate the +3 dos header
2135      buf[15] = file_type;
2136      buf[16] = total_len % 256;
2137      buf[17] = total_len / 256;
2138      buf[18] = start_address % 256;
2139      buf[19] = start_address / 256;
2140      buf[20] = file_type == 0 ? total_len % 256 : 0;
2141      buf[21] = file_type == 0 ? total_len / 256 : 0;
2142 
2143      // And then the overall file header
2144      total_len += 128;
2145      buf[11] = total_len % 256;
2146      buf[12] = (total_len / 256) % 256;
2147      buf[13] = (total_len / 65536) % 256;
2148      buf[14] = (total_len / 65536) / 256;
2149 
2150      // And now do the checksum
2151      for ( i = 0, cksum = 0; i < 127; i++ ) {
2152          cksum += buf[i];
2153      }
2154      buf[127] = cksum % 256;
2155      *total_len_ptr = total_len;
2156      return buf;
2157 }
2158 
zx_plus3(struct zx_common * zxc,struct zx_tape * zxt,struct banked_memory * memory)2159 int zx_plus3(struct zx_common *zxc, struct zx_tape *zxt, struct banked_memory *memory)
2160 {
2161     uint8_t  buffer[1024];  // Temporary buffer
2162     uint8_t *ptr;
2163     char    disc_image_name[FILENAME_MAX+1];
2164     char    cpm_filename[20];
2165     char    basic_filename[20];
2166     char    tbuf[50];
2167     size_t  origin;
2168     size_t  binary_length;
2169     int     len, i, bsnum_bank;
2170     disc_handle *h;
2171     FILE   *fpin;
2172     void   *file_buf;
2173     size_t  file_len;
2174 
2175     if (zxc->outfile == NULL) {
2176         strcpy(disc_image_name, zxc->binname);
2177         suffix_change(disc_image_name, ".dsk");
2178     } else {
2179         strcpy(disc_image_name, zxc->outfile);
2180     }
2181 
2182 
2183     if (zxt->blockname == NULL)
2184         zxt->blockname = zbasename(zxc->binname);
2185 
2186 
2187     if (strcmp(zxc->binname, disc_image_name) == 0) {
2188         exit_log(1, "Input and output file names must be different\n");
2189     }
2190 
2191 
2192     if (zxc->origin != -1) {
2193         origin = zxc->origin;
2194     } else {
2195         if ((origin = get_org_addr(zxc->crtfile)) == -1) {
2196             exit_log(1,"Could not find parameter ZORG (not z88dk compiled?)\n");
2197         }
2198     }
2199 
2200     if ((fpin = fopen_bin(zxc->binname, zxc->crtfile)) == NULL) {
2201         exit_log(1,"Can't open input file %s\n", zxc->binname);
2202     }
2203 
2204     if (fseek(fpin, 0, SEEK_END)) {
2205         fclose(fpin);
2206         exit_log(1,"Couldn't determine size of file\n");
2207     }
2208 
2209     binary_length = ftell(fpin);
2210     fseek(fpin, 0L, SEEK_SET);
2211 
2212     if ( (h = cpm_create_with_format("plus3")) == NULL ) {
2213         fclose(fpin);
2214         exit_log(1,"Cannot find disc specification\n");
2215     }
2216 
2217     // Lets do some filename mangling
2218     cpm_create_filename(zxt->blockname, basic_filename, 0, 1);
2219 
2220     // Create the basic file
2221 
2222     // Write the basic file
2223     ptr = buffer;
2224     writebyte_b(0, &ptr);		// MSB of basic line
2225     writebyte_b(10, &ptr);		// MSB
2226     writeword_b(0, &ptr);		// Line length, we'll fix up later
2227     writebyte_b(0xfd, &ptr);		// CLEAR
2228     writebyte_b(0xb0, &ptr);		// VAL
2229     if ( zxt->clear_address == -1 ) {
2230         zxt->clear_address = origin - 1;
2231     }
2232     snprintf(tbuf,sizeof(tbuf),"\"%i\":", zxt->clear_address);
2233     writestring_b(tbuf, &ptr);
2234     if ( zxt->screen ) {
2235         suffix_change(basic_filename, ".SCR");
2236         writebyte_b(0xef, &ptr);	/* LOAD */
2237         writebyte_b('"', &ptr);
2238         writestring_b(basic_filename, &ptr);
2239         writebyte_b('"', &ptr);
2240         writebyte_b(0xaa, &ptr);	/* SCREEN$ */
2241         writebyte_b(':', &ptr);
2242     }
2243     suffix_change(basic_filename, ".BIN");
2244     writebyte_b(0xef, &ptr);	/* LOAD */
2245     writebyte_b('"', &ptr);
2246     writestring_b(basic_filename, &ptr);
2247     writebyte_b('"', &ptr);
2248     writebyte_b(0xaf, &ptr);	/* CODE */
2249     writebyte_b(':', &ptr);
2250     writebyte_b(0xf9, &ptr);	/* RANDOMIZE */
2251     writebyte_b(0xc0, &ptr);	/* USR */
2252     writebyte_b(0xb0, &ptr);	/* VAL */
2253     snprintf(tbuf,sizeof(tbuf), "\"%i\"", (int)origin);           /* Location for USR */
2254     writestring_b(tbuf, &ptr);
2255     writebyte_b(':', &ptr);
2256     writebyte_b(234, &ptr);      /* REM */
2257     writestring_b(basic_filename, &ptr);
2258     writebyte_b(0x0d, &ptr);	/* ENTER (end of BASIC line) */
2259     len = ptr - buffer;
2260     buffer[2] = (len-4) % 256;
2261     buffer[3] = (len-4) / 256;
2262 
2263     // And now we can write the file
2264     file_buf = zx3_layout_file(buffer, len, 10, 0, &file_len);
2265     disc_write_file(h, "DISK       ", file_buf, file_len);
2266     free(file_buf);
2267 
2268     // Read the screen$
2269     if ( zxt->screen ) {
2270         uint8_t scrbuf[6912] = {0};
2271         FILE    *fpscr = fopen(zxt->screen, "rb");
2272 
2273         if ( fpscr == NULL ) {
2274             fclose(fpin);
2275             exit_log(1,"Cannot open SCREEN$ file %s\n", zxt->screen);
2276         }
2277         suffix_change(basic_filename, ".SCR");
2278         if (6912 != fread(scrbuf, 1, 6912, fpscr)) { fclose(fpscr); exit_log(1, "Could not read required data from <%s>\n",zxt->screen); }
2279         fclose(fpscr);
2280         file_buf = zx3_layout_file(scrbuf, 6912, 16384, 3, &file_len);
2281         cpm_create_filename(basic_filename, cpm_filename, 0, 0);
2282         disc_write_file(h, cpm_filename, file_buf, file_len);
2283         free(file_buf);
2284     }
2285     // Read the binary
2286     ptr = must_malloc(binary_length);
2287     if (binary_length != fread(ptr, 1, binary_length, fpin)) { fclose(fpin); exit_log(1, "Could not read required data from <%s>\n",zxc->binname); }
2288     fclose(fpin);
2289 
2290     // And write it
2291     suffix_change(basic_filename, ".BIN");
2292     cpm_create_filename(basic_filename, cpm_filename, 0, 0);
2293     file_buf = zx3_layout_file(ptr, binary_length, origin, 3, &file_len);
2294     disc_write_file(h, cpm_filename, file_buf, file_len);
2295     free(file_buf);
2296     free(ptr);
2297 
2298     // Write the banks
2299     bsnum_bank = mb_find_bankspace(memory, "BANK");
2300     for ( i = 0; i < 8; i++ ) {
2301         struct memory_bank *mb = &memory->bankspace[bsnum_bank].membank[i];
2302         if (mb->num > 0) {
2303             unsigned char bank_buf[16384];
2304             char          numbuf[32];
2305             FILE    *fpbank = fopen(mb->secbin->filename, "rb");
2306 
2307             if ( fpbank == NULL ) {
2308                 exit_log(1,"Cannot open BANK file %s\n", mb->secbin->filename);
2309             }
2310             if ( mb->secbin->size != fread(bank_buf, 1,  mb->secbin->size, fpbank)) {  exit_log(1, "Could not read required data from <%s>\n",mb->secbin->filename); }
2311             snprintf(numbuf,sizeof(numbuf),".%d",i);
2312             suffix_change(basic_filename, numbuf);
2313             cpm_create_filename(basic_filename, cpm_filename, 0, 0);
2314             file_buf = zx3_layout_file(bank_buf, mb->secbin->size, 0xc000, 3, &file_len);
2315             disc_write_file(h, cpm_filename, file_buf, file_len);
2316             fclose(fpbank);
2317         }
2318     }
2319 
2320 
2321     // Finalise the image
2322     if ( disc_write_edsk(h, disc_image_name) < 0 ) {
2323         exit_log(1,"Can't write disc image");
2324     }
2325     return 0;
2326 }
2327