1 /*
2 * Convert an outfile into a main 32k binary + multiple 16k expansion banks
3 *
4 * Harold O. Pinheiro - 2006 - pascal
5 * Dominic Morris - 02/06/2007 - rewritten and placed into appmake
6 * Alvin Albrecht - 08/2016 - update to sms files that support bankswitched memory
7 */
8
9 #include <time.h>
10 #include "appmake.h"
11
12 static char help = 0;
13 static char *binname = NULL;
14 static char *extension = ".sms";
15 static char *crtfile = NULL;
16 static char *outfile = NULL;
17 static int romfill = 255;
18
19 /* Options that are available for this module */
20 option_t sms_options[] = {
21 { 'h', "help", "Display this help", OPT_BOOL, &help },
22 { 'b', "binfile", "Linked binary file", OPT_STR, &binname },
23 { 'c', "crt0file", "crt0 used to link binary", OPT_STR, &crtfile },
24 { 'e', "extension", "Extension for the ROM", OPT_STR, &extension },
25 { 'o', "output", "Name of output file", OPT_STR, &outfile },
26 { 'f', "filler", "Filler byte (default: 0xFF)", OPT_INT, &romfill },
27 { 0, NULL, NULL, OPT_NONE, NULL }
28 };
29
30 #define SMS_HEADER_ADDR 0x7ff0
31 #define SDSC_HEADER_ADDR 0x7fe0
32
33 static unsigned char memory[0xc000];
34
35 struct sms_header {
36 unsigned char signature[10];
37 unsigned int checksum;
38 unsigned int product_code;
39 unsigned int version;
40 unsigned int region;
41 unsigned int romsize;
42 };
43
44 struct sdsc_header {
45 unsigned char signature[4];
46 unsigned int version;
47 unsigned int date;
48 unsigned int author;
49 unsigned int name;
50 unsigned int description;
51 };
52
53 struct sms_header sega_hdr = {"TMR SEGA ",0,0,0,0x4,0xc};
54 struct sdsc_header sdsc_hdr = {"SDSC",0,0,0xffff,0xffff,0xffff};
55
sms_exec(char * target)56 int sms_exec(char *target)
57 {
58 time_t t;
59 struct tm *lt;
60 struct stat st_file;
61 char filename[FILENAME_MAX+1];
62 FILE *fpin, *fpout;
63 int checksum, len, sdsc_present, sega_present, i, c, count;
64
65 if ((help) || (binname == NULL))
66 return -1;
67
68 // output filename
69
70 if (outfile == NULL)
71 {
72 strcpy(filename, binname);
73 suffix_change(filename, extension);
74 }
75 else
76 strcpy(filename, outfile);
77
78 // gather header info
79
80 sdsc_present = 0;
81 sega_present = 1;
82
83 t = time(NULL); lt = localtime(&t);
84 sdsc_hdr.date = lt->tm_mday + (lt->tm_mon + 1)*100 + (lt->tm_year + 1900)*100*100;
85
86 if (crtfile != NULL)
87 {
88 if ((i = parameter_search(crtfile, ".sym", "SMS_HDR_PRODUCT_CODE")) >= 0)
89 sega_hdr.product_code = i;
90 if ((i = parameter_search(crtfile, ".sym", "SMS_HDR_VERSION")) >= 0)
91 sega_hdr.version = i;
92 if ((i = parameter_search(crtfile, ".sym", "SMS_HDR_REGION")) >= 0)
93 sega_hdr.region = i;
94 if ((i = parameter_search(crtfile, ".sym", "SMS_HDR_ROM_SIZE")) >= 0)
95 fprintf(stderr, "Notice: ROM size is always set to 32k for checksum purposes\n");
96
97 if ((i = parameter_search(crtfile, ".sym", "SDSC_HDR_VERSION")) >= 0)
98 {
99 sdsc_present = 1;
100 sdsc_hdr.version = i;
101 }
102 if ((i = parameter_search(crtfile, ".sym", "SDSC_HDR_DATE")) >= 0)
103 {
104 sdsc_present = 1;
105 sdsc_hdr.date = i;
106 }
107 if ((i = parameter_search(crtfile, ".map", "SDSC_HDR_AUTHOR")) >= 0)
108 {
109 sdsc_present = 1;
110 sdsc_hdr.author = i;
111 }
112 if ((i = parameter_search(crtfile, ".map", "SDSC_HDR_NAME")) >= 0)
113 {
114 sdsc_present = 1;
115 sdsc_hdr.name = i;
116 }
117 if ((i = parameter_search(crtfile, ".map", "SDSC_HDR_DESCRIPTION")) >= 0)
118 {
119 sdsc_present = 1;
120 sdsc_hdr.description = i;
121 }
122 }
123
124 // create 32k/48k portion of output binary
125
126 if ((fpin = fopen_bin(binname, crtfile)) == NULL)
127 exit_log(1, "Can't open input file %s\n", binname);
128 else if (fseek(fpin, 0, SEEK_END))
129 {
130 fclose(fpin);
131 exit_log(1, "Couldn't determine size of file %s\n", binname);
132 }
133
134 if ((len = ftell(fpin)) > 0xc000)
135 {
136 fclose(fpin);
137 exit_log(1, "Main output binary exceeds 48k by %d bytes\n", len - 0xc000);
138 }
139
140 if (sega_present && (len > SMS_HEADER_ADDR))
141 {
142 sega_present = 0;
143 fprintf(stderr, "Notice: SEGA header will not be inserted because main ROM is too large by %d bytes\n", len - SMS_HEADER_ADDR);
144 }
145
146 if (sdsc_present && (len > SDSC_HEADER_ADDR))
147 {
148 sdsc_present = 0;
149 fprintf(stderr, "Notice: SDSC header will not be inserted because main ROM is too large by %d bytes\n", len - SDSC_HEADER_ADDR);
150 }
151
152 rewind(fpin);
153
154 memset(memory, romfill, sizeof(memory));
155 if (len != fread(memory, sizeof(memory[0]), len, fpin)) { fclose(fpin); exit_log(1, "Could not read required data from <%s>\n",binname); }
156
157 fclose(fpin);
158
159 if (sdsc_present)
160 {
161 memcpy(&memory[SDSC_HEADER_ADDR], sdsc_hdr.signature, 4);
162 memory[SDSC_HEADER_ADDR+5] = (unsigned char)(sdsc_hdr.version = num2bcd(sdsc_hdr.version));
163 memory[SDSC_HEADER_ADDR+4] = (unsigned char)(sdsc_hdr.version >> 8);
164 memory[SDSC_HEADER_ADDR+6] = (unsigned char)(sdsc_hdr.date = num2bcd(sdsc_hdr.date));
165 memory[SDSC_HEADER_ADDR+7] = (unsigned char)(sdsc_hdr.date >> 8);
166 memory[SDSC_HEADER_ADDR+8] = (unsigned char)(sdsc_hdr.date >> 16);
167 memory[SDSC_HEADER_ADDR+9] = (unsigned char)(sdsc_hdr.date >> 24);
168 memory[SDSC_HEADER_ADDR+10] = (unsigned char)sdsc_hdr.author;
169 memory[SDSC_HEADER_ADDR+11] = (unsigned char)(sdsc_hdr.author >> 8);
170 memory[SDSC_HEADER_ADDR+12] = (unsigned char)sdsc_hdr.name;
171 memory[SDSC_HEADER_ADDR+13] = (unsigned char)(sdsc_hdr.name >> 8);
172 memory[SDSC_HEADER_ADDR+14] = (unsigned char)sdsc_hdr.description;
173 memory[SDSC_HEADER_ADDR+15] = (unsigned char)(sdsc_hdr.description >> 8);
174 }
175
176 if (sega_present)
177 {
178 memcpy(&memory[SMS_HEADER_ADDR], sega_hdr.signature, 10);
179 memory[SMS_HEADER_ADDR+14] = (unsigned char)(((sega_hdr.product_code/10000 << 4) & 0xf0) + (sega_hdr.version & 0x0f));
180 memory[SMS_HEADER_ADDR+12] = (unsigned char)(sega_hdr.product_code = num2bcd(sega_hdr.product_code));
181 memory[SMS_HEADER_ADDR+13] = (unsigned char)(sega_hdr.product_code >> 8);
182 memory[SMS_HEADER_ADDR+15] = (unsigned char)(((sega_hdr.region << 4) & 0xf0) + (sega_hdr.romsize & 0x0f));
183
184 for (i = checksum = 0; i < SMS_HEADER_ADDR; ++i)
185 checksum += memory[i];
186
187 memory[SMS_HEADER_ADDR+10] = (unsigned char)checksum;
188 memory[SMS_HEADER_ADDR+11] = (unsigned char)(checksum >> 8);
189 }
190
191 // write first 32k/48k of output file
192
193 if ((fpout = fopen(filename, "wb")) == NULL)
194 exit_log(1, "Can't create output file %s\n", filename);
195
196 fwrite(memory, sizeof(memory[0]), (len > 0x8000) ? 0xc000 : 0x8000, fpout);
197
198 // check available ram space
199
200 if ((c = parameter_search(crtfile, ".map", "__BSS_END_tail")) >= 0)
201 {
202 if ((i = parameter_search(crtfile, ".map", "__DATA_head")) >= 0)
203 {
204 c -= i - 8;
205 if (c <= 0x2000)
206 fprintf(stderr, "Notice: Available RAM space is %d bytes ignoring the stack\n", 0x2000 - c);
207 else
208 fprintf(stderr, "Warning: Exceeded 8k RAM by %d bytes.\n", c - 0x2000);
209 }
210 }
211
212 // look for and append memory banks
213
214 fprintf(stderr, "Adding main banks 0x00,0x01%s (%d bytes free)\n", (len > 0x8000) ? ",0x02" : "", ((len > 0x8000) ? 0xc000 : 0x8000)-len-16*(sega_present+sdsc_present));
215
216 count = 0;
217 for (i = 0x02 + (len > 0x8000); i <= 0x1f; ++i)
218 {
219 sprintf(filename, "%s_BANK_%02X.bin", binname, i);
220
221 if ((stat(filename, &st_file) < 0) || (st_file.st_size == 0) || ((fpin = fopen(filename, "rb")) == NULL))
222 count += 0x4000;
223 else
224 {
225 if (len > 0x8000)
226 {
227 fprintf(stderr, "Warning: The main binary extends into slot 2 by %d bytes\n", len - 0x8000);
228 len = 0;
229 }
230
231 fprintf(stderr, "Adding bank 0x%02X", i);
232
233 while (count--)
234 fputc(romfill, fpout);
235
236 for (count = 0; ((c = fgetc(fpin)) != EOF) && (count < 0x4000); ++count)
237 fputc(c, fpout);
238
239 if (count < 0x4000)
240 fprintf(stderr, " (%d bytes free)", 0x4000 - count);
241
242 while (count++ < 0x4000)
243 fputc(romfill, fpout);
244
245 if (!feof(fpin))
246 {
247 fseek(fpin, 0, SEEK_END);
248 count = ftell(fpin);
249 fprintf(stderr, " (error truncating %d bytes from %s)", count - 0x4000, filename);
250 }
251
252 count = 0;
253 fputc('\n', stderr);
254
255 fclose(fpin);
256 }
257 }
258
259 fclose(fpout);
260 return 0;
261 }
262