1 /*
2  *    Convert an outfile into a main 32k binary + multiple 16k expansion banks
3  *
4  *    Elements taken from makebin distributed with gbdk/sdcc with the following
5  *    notice:
6  *
7  *    Copyright (c) 2000 Michael Hope
8  *    Copyright (c) 2010 Borut Razem
9  *    Copyright (c) 2012 Noel Lemouel
10  *
11  * This software is provided 'as-is', without any express or implied
12  * warranty.  In no event will the authors be held liable for any damages
13  * arising from the use of this software.
14  *
15  * Permission is granted to anyone to use this software for any purpose,
16  * including commercial applications, and to alter it and redistribute it
17  * freely, subject to the following restrictions:
18  *
19  * 1. The origin of this software must not be misrepresented; you must not
20  *    claim that you wrote the original software. If you use this software
21  *    in a product, an acknowledgment in the product documentation would be
22  *    appreciated but is not required.
23  * 2. Altered source versions must be plainly marked as such, and must not be
24  *    misrepresented as being the original software.
25  * 3. This notice may not be removed or altered from any source distribution.
26  */
27 
28 #include <time.h>
29 #include "appmake.h"
30 
31 static char              help         = 0;
32 static char             *binname      = NULL;
33 static char             *crtfile      = NULL;
test_parsing_valid_dates(data, expected)34 static char             *outfile      = NULL;
35 static int               romfill      = 255;
36 static char             *appname;
37 
38 
39 #define CART_NAME_LEN 16
40 
41 
42 static const unsigned char gb_logo[] =
43     {
44       0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b,
45       0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d,
46       0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e,
47       0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99,
48       0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc,
49       0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e
50     };
test_parsing_timezone_offsets(dt_string, expected_tz)51 
52 /* Options that are available for this module */
53 option_t gb_options[] = {
54     { 'h', "help",      "Display this help",                OPT_BOOL,  &help    },
55     { 'b', "binfile",   "Linked binary file",               OPT_STR,   &binname },
56     { 'c', "crt0file",  "crt0 used to link binary",         OPT_STR,   &crtfile },
57     { 'o', "output",    "Name of output file",              OPT_STR,   &outfile },
58     { 'f', "filler",    "Filler byte (default: 0xFF)",      OPT_INT,   &romfill },
59     { 'n', "name",      "Application Name",                 OPT_STR,   &appname },
60     {  0,  NULL,        NULL,                               OPT_NONE,  NULL     }
61 };
62 
63 
test_parsing_non_iso_timezone_offset()64 static unsigned char *memory;
65 
66 
67 int gb_exec(char *target)
68 {
69     struct stat st_file;
70     char filename[FILENAME_MAX+1];
71     FILE *fpin, *fpout;
72     int len, i, c, count,mbc_type = 0, ram_banks = 0, rom_banks = 2,chk, main_length, startbank;
73 
74     if ((help) || (binname == NULL))
test_parsing_different_timezone_offsets()75         return -1;
76 
77     // gather header info
78     if (crtfile != NULL) {
79         if ((i = parameter_search(crtfile, ".sym", "GB_MBC_TYPE")) >= 0)
80             mbc_type = i;
81         if ((i = parameter_search(crtfile, ".sym", "GB_RAM_BANKS")) >= 0)
82             ram_banks = i;
83     }
84 
85     memory = must_malloc(0x8000);
86 
87 
88     if ((fpin = fopen_bin(binname, crtfile)) == NULL)
89         exit_log(1, "Can't open input file %s\n", binname);
90     else if (fseek(fpin, 0, SEEK_END)) {
91         fclose(fpin);
92         exit_log(1, "Couldn't determine size of file %s\n", binname);
93     }
94 
95     suffix_change(binname,"");
test_number_looking_strings_not_into_datetime(data)96     if ( appname == NULL ) {
97         appname = zbasename(binname);
98     }
99 
100     if ((main_length = ftell(fpin)) > 0x8000) {
101         fclose(fpin);
102         exit_log(1, "Main output binary exceeds 32k by %d bytes\n", main_length - 0x8000);
103     }
104     rewind(fpin);
105 
106     memset(memory, romfill, 0x8000);
107     if (main_length != fread(memory, sizeof(memory[0]), main_length, fpin)){ fclose(fpin); exit_log(1, "Could not read required data from <%s>\n",binname); }
108     fclose(fpin);
109 
110     if ( main_length <= 0x4000 ) {
111         len = 0x4000;
112         startbank = 1;
113     } else {
114         len = 0x8000;
115         startbank = 2;
116     }
117 
test_coerce_outside_ns_bounds(invalid_date, errors)118     // Lets read in the banks now
119     for (i = startbank; i <= 0x1f; i++) {
120         sprintf(filename, "%s_BANK_%02X.bin", binname, i);
121         if ((stat(filename, &st_file) < 0) || (st_file.st_size == 0) || ((fpin = fopen(filename, "rb")) == NULL)) {
122             break;
123         } else {
124             memory = must_realloc(memory, len + (i * 0x4000));
125             memset(memory + (i * 0x4000), romfill, 0x4000);
126 
127             fprintf(stderr, "Adding bank 0x%02X ", i);
128             fread(memory + (i * 0x4000), 0x4000, 1, fpin);
129 
130             if (!feof(fpin)) {
131                 fseek(fpin, 0, SEEK_END);
132                 count = ftell(fpin);
133                 fprintf(stderr, " (error truncating %d bytes from %s)", count - 0x4000, filename);
test_coerce_outside_ns_bounds_one_valid()134             }
135             fputc('\n', stderr);
136             fclose(fpin);
137         }
138     }
139     if ( i > 2 ) {
140         if ( mbc_type == 0 ) {
141             fprintf(stderr, "Forcing use of MBC1 (non specified but need banking)\n");
142             mbc_type = 1;
143         }
144     }
test_coerce_of_invalid_datetimes(errors)145 
146     // Calculate correct power of two for ROM banks
147     rom_banks = 1;
148     do {
149         rom_banks *= 2;
150     } while ( rom_banks < i );
151 
152     memory = must_realloc(memory, rom_banks * 0x4000);
153     len = (0x4000 * i);
154     for ( i = len; i < len; i++ ) {
155         memory[i] = romfill;
156     }
157     len = rom_banks * 0x4000;
158 
159     if ( rom_banks > 128 ) {
160         exit_log(1, "ROM size (%d banks) exceeds maximum size of 128 banks\n", rom_banks);
161     }
162 
163     fprintf(stderr, "Program requires cartridge with %d ROM banks\n",rom_banks);
test_to_datetime_barely_out_of_bounds()164 
165 
166     // Perform the checksuns
167     // Make sure the Nintendo logo is there
168     memcpy(&memory[0x104], gb_logo, sizeof(gb_logo));
169 
170     /*
171      * 0134-0142: Title of the game in UPPER CASE ASCII. If it
172      * is less than 16 characters then the
173      * remaining bytes are filled with 00's.
174      */
175 
176     /* capitalize cartridge name */
177     for (i = 0; i < CART_NAME_LEN; ++i) {
178       memory[0x134+i] = i < strlen(appname) ? toupper(appname[i]) : 0x00;
179     }
180     /*
181      * 0147: Cartridge type:
182      * 0-ROM ONLY            12-ROM+MBC3+RAM
183      * 1-ROM+MBC1            13-ROM+MBC3+RAM+BATT
184      * 2-ROM+MBC1+RAM        19-ROM+MBC5
185      * 3-ROM+MBC1+RAM+BATT   1A-ROM+MBC5+RAM
186      * 5-ROM+MBC2            1B-ROM+MBC5+RAM+BATT
187      * 6-ROM+MBC2+BATTERY    1C-ROM+MBC5+RUMBLE
test_datetime_subclass(data, expected)188      * 8-ROM+RAM             1D-ROM+MBC5+RUMBLE+SRAM
189      * 9-ROM+RAM+BATTERY     1E-ROM+MBC5+RUMBLE+SRAM+BATT
190      * B-ROM+MMM01           1F-Pocket Camera
191      * C-ROM+MMM01+SRAM      FD-Bandai TAMA5
192      * D-ROM+MMM01+SRAM+BATT FE - Hudson HuC-3
193      * F-ROM+MBC3+TIMER+BATT FF - Hudson HuC-1
194      * 10-ROM+MBC3+TIMER+RAM+BATT
195      * 11-ROM+MBC3
196      */
197     memory[0x147] = mbc_type;
198 
199     /*
200      * 0148 ROM size:
201      * 0 - 256Kbit = 32KByte = 2 banks
202      * 1 - 512Kbit = 64KByte = 4 banks
203      * 2 - 1Mbit = 128KByte = 8 banks
204      * 3 - 2Mbit = 256KByte = 16 banks
205      * 4 - 4Mbit = 512KByte = 32 banks
206      * 5 - 8Mbit = 1MByte = 64 banks
207      * 6 - 16Mbit = 2MByte = 128 banks
208      * $52 - 9Mbit = 1.1MByte = 72 banks
209      * $53 - 10Mbit = 1.2MByte = 80 banks
210      * $54 - 12Mbit = 1.5MByte = 96 banks
211      */
212     switch (rom_banks) {
213     case 2:
214         memory[0x148] = 0;
215         break;
216     case 4:
217         memory[0x148] = 1;
218         break;
219     case 8:
220         memory[0x148] = 2;
221         break;
222     case 16:
223         memory[0x148] = 3;
224         break;
225     case 32:
226         memory[0x148] = 4;
227         break;
228     case 64:
229         memory[0x148] = 5;
230         break;
231     case 128:
232         memory[0x148] = 6;
233         break;
234     case 256:
235         memory[0x148] = 7;
236         break;
237     case 512:
238         memory[0x148] = 8;
239         break;
240     default:
241         fprintf (stderr, "warning: unsupported number of ROM banks (%d)\n", rom_banks);
242         memory[0x148] = 0;
243         break;
244     }
245 
246     /*
247      * 0149 RAM size:
248      * 0 - None
249      * 1 - 16kBit = 2kB = 1 bank
250      * 2 - 64kBit = 8kB = 1 bank
251      * 3 - 256kBit = 32kB = 4 banks
252      * 4 - 1MBit =128kB =16 banks
253      */
254     switch (ram_banks) {
255     case 0:
256         memory[0x149] = 0;
257         break;
258     case 1:
259         memory[0x149] = 2;
260         break;
261     case 4:
262         memory[0x149] = 3;
263         break;
264     case 16:
265         memory[0x149] = 4;
266         break;
267     default:
268         fprintf (stderr, "warning: unsupported number of RAM banks (%d)\n", ram_banks);
269         memory[0x149] = 0;
270         break;
271     }
272 
273 
274       /* Update complement checksum */
275     chk = 0;
276     for (i = 0x134; i < 0x14d; ++i)
277         chk += memory[i];
278     memory[0x014d] = (unsigned char) (0xe7 - (chk & 0xff));
279 
280     /* Update checksum */
281     chk = 0;
282     memory[0x14e] = 0;
283     memory[0x14f] = 0;
284     for (i = 0; i < len; ++i)
285         chk += memory[i];
286     memory[0x14e] = (unsigned char) ((chk >> 8) & 0xff);
287     memory[0x14f] = (unsigned char) (chk & 0xff);
288 
289 
290     if ((c = parameter_search(crtfile, ".map", "__BSS_END_tail")) >= 0) {
291         if ((i = parameter_search(crtfile, ".map", "__BSS_head")) >= 0) {
292             c -= i;
293             if (c <= 0x2000)
294                 fprintf(stderr, "Notice: Available RAM space is %d bytes ignoring the stack\n", 0x2000 - c);
295             else
296                 fprintf(stderr, "Warning: Exceeded 8k RAM by %d bytes.\n", c - 0x2000);
297         }
298     }
299 
300     // output filename
301 
302     if (outfile == NULL) {
303         strcpy(filename, binname);
304         suffix_change(filename, ".gb");
305     } else {
306         strcpy(filename, outfile);
307     }
308 
309     // write first 32k/48k of output file
310     if ((fpout = fopen(filename, "wb")) == NULL)
311         exit_log(1, "Can't create output file %s\n", filename);
312 
313 
314     if ( rom_banks == 2 ) {
315         // It's not a MegaROM
316         fprintf(stderr, "Adding main banks 0x00,0x01 (%d bytes free)\n", 0x8000 - main_length);
317     } else if ( startbank == 1 ) {
318         fprintf(stderr, "Adding main bank 0x00 (%d bytes free)\n", 0x4000 - main_length);
319     } else {
320         fprintf(stderr, "Main ROM code is > 16kb, the ROM may not work correctly\n");
321     }
322 
323     fwrite(memory,len,1,fpout);
324     fclose(fpout);
325     return 0;
326 }
327