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