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, §ion_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, §ion_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