1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * Copyright (C) 2002-2021 The DOSBox Team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #include "program_boot.h"
22
23 #include <stdio.h>
24
25 #include "bios_disk.h"
26 #include "callback.h"
27 #include "control.h"
28 #include "cross.h"
29 #include "dma.h"
30 #include "drives.h"
31 #include "mapper.h"
32 #include "regs.h"
33 #include "string_utils.h"
34
getFSFile_mounted(char const * filename,Bit32u * ksize,Bit32u * bsize,Bit8u * error)35 FILE *BOOT::getFSFile_mounted(char const *filename, Bit32u *ksize, Bit32u *bsize, Bit8u *error)
36 {
37 // if return NULL then put in error the errormessage code if an error
38 // was requested
39 bool tryload = (*error) ? true : false;
40 *error = 0;
41 Bit8u drive;
42 FILE *tmpfile;
43 char fullname[DOS_PATHLENGTH];
44
45 localDrive *ldp = 0;
46 if (!DOS_MakeName(const_cast<char *>(filename), fullname, &drive))
47 return NULL;
48
49 try {
50 ldp = dynamic_cast<localDrive *>(Drives[drive]);
51 if (!ldp)
52 return NULL;
53
54 tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
55 if (tmpfile == NULL) {
56 if (!tryload)
57 *error = 1;
58 return NULL;
59 }
60
61 // get file size
62 fseek(tmpfile, 0L, SEEK_END);
63 *ksize = (ftell(tmpfile) / 1024);
64 *bsize = ftell(tmpfile);
65 fclose(tmpfile);
66
67 tmpfile = ldp->GetSystemFilePtr(fullname, "rb+");
68 if (tmpfile == NULL) {
69 // if (!tryload) *error=2;
70 // return NULL;
71 WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
72 tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
73 if (tmpfile == NULL) {
74 if (!tryload)
75 *error = 1;
76 return NULL;
77 }
78 }
79
80 return tmpfile;
81 } catch (...) {
82 return NULL;
83 }
84 }
85
getFSFile(char const * filename,Bit32u * ksize,Bit32u * bsize,bool tryload)86 FILE *BOOT::getFSFile(char const *filename, Bit32u *ksize, Bit32u *bsize, bool tryload)
87 {
88 Bit8u error = tryload ? 1 : 0;
89 FILE *tmpfile = getFSFile_mounted(filename, ksize, bsize, &error);
90 if (tmpfile)
91 return tmpfile;
92 // File not found on mounted filesystem. Try regular filesystem
93 std::string filename_s(filename);
94 Cross::ResolveHomedir(filename_s);
95 tmpfile = fopen_wrap(filename_s.c_str(), "rb+");
96 if (!tmpfile) {
97 if ((tmpfile = fopen_wrap(filename_s.c_str(), "rb"))) {
98 // File exists; So can't be opened in correct mode =>
99 // error 2
100 // fclose(tmpfile);
101 // if (tryload) error = 2;
102 WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
103 fseek(tmpfile, 0L, SEEK_END);
104 *ksize = (ftell(tmpfile) / 1024);
105 *bsize = ftell(tmpfile);
106 return tmpfile;
107 }
108 // Give the delayed errormessages from the mounted variant (or
109 // from above)
110 if (error == 1)
111 WriteOut(MSG_Get("PROGRAM_BOOT_NOT_EXIST"));
112 if (error == 2)
113 WriteOut(MSG_Get("PROGRAM_BOOT_NOT_OPEN"));
114 return NULL;
115 }
116 fseek(tmpfile, 0L, SEEK_END);
117 *ksize = (ftell(tmpfile) / 1024);
118 *bsize = ftell(tmpfile);
119 return tmpfile;
120 }
121
printError(void)122 void BOOT::printError(void)
123 {
124 WriteOut(MSG_Get("PROGRAM_BOOT_PRINT_ERROR"), PRIMARY_MOD_NAME);
125 }
126
disable_umb_ems_xms(void)127 void BOOT::disable_umb_ems_xms(void)
128 {
129 Section *dos_sec = control->GetSection("dos");
130 dos_sec->ExecuteDestroy(false);
131 char test[20];
132 safe_strcpy(test, "umb=false");
133 dos_sec->HandleInputline(test);
134 safe_strcpy(test, "xms=false");
135 dos_sec->HandleInputline(test);
136 safe_strcpy(test, "ems=false");
137 dos_sec->HandleInputline(test);
138 dos_sec->ExecuteInit(false);
139 }
140
Run(void)141 void BOOT::Run(void)
142 {
143 // Hack To allow long commandlines
144 ChangeToLongCmd();
145 /* In secure mode don't allow people to boot stuff.
146 * They might try to corrupt the data on it */
147 if (control->SecureMode()) {
148 WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
149 return;
150 }
151
152 FILE *usefile_1 = NULL;
153 FILE *usefile_2 = NULL;
154 Bitu i = 0;
155 Bit32u floppysize = 0;
156 Bit32u rombytesize_1 = 0;
157 Bit32u rombytesize_2 = 0;
158 char drive = 'A';
159 std::string cart_cmd = "";
160
161 if (!cmd->GetCount()) {
162 printError();
163 return;
164 }
165
166 if (cmd->FindExist("/?", false) || cmd->FindExist("-?", false) ||
167 cmd->FindExist("-h", false) || cmd->FindExist("--help", false)) {
168 WriteOut(MSG_Get("SHELL_CMD_BOOT_HELP_LONG"));
169 return;
170 }
171 if (cmd->GetCount() == 1) {
172 if (cmd->FindCommand(1, temp_line)) {
173 if (temp_line.length() == 2 && toupper(temp_line[0]) >= 'A' &&
174 toupper(temp_line[0]) <= 'Z' && temp_line[1] == ':') {
175 drive = toupper(temp_line[0]);
176 if ((drive != 'A') && (drive != 'C') &&
177 (drive != 'D')) {
178 printError();
179 return;
180 }
181 i++;
182 }
183 }
184 }
185 while (i < cmd->GetCount()) {
186 if (cmd->FindCommand(i + 1, temp_line)) {
187 if ((temp_line == "-l") || (temp_line == "-L")) {
188 /* Specifying drive... next argument then is the
189 * drive */
190 i++;
191 if (cmd->FindCommand(i + 1, temp_line)) {
192 drive = toupper(temp_line[0]);
193 if ((drive != 'A') && (drive != 'C') &&
194 (drive != 'D')) {
195 printError();
196 return;
197 }
198
199 } else {
200 printError();
201 return;
202 }
203 i++;
204 continue;
205 }
206
207 if ((temp_line == "-e") || (temp_line == "-E")) {
208 /* Command mode for PCJr cartridges */
209 i++;
210 if (cmd->FindCommand(i + 1, temp_line)) {
211 for (size_t ct = 0;
212 ct < temp_line.size(); ct++)
213 temp_line[ct] = toupper(
214 temp_line[ct]);
215 cart_cmd = temp_line;
216 } else {
217 printError();
218 return;
219 }
220 i++;
221 continue;
222 }
223
224 if (i >= MAX_SWAPPABLE_DISKS) {
225 return; // TODO give a warning.
226 }
227 WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_OPEN"),
228 temp_line.c_str());
229 Bit32u rombytesize;
230 FILE *usefile = getFSFile(temp_line.c_str(),
231 &floppysize, &rombytesize);
232 if (usefile != NULL) {
233 diskSwap[i].reset(
234 new imageDisk(usefile, temp_line.c_str(),
235 floppysize, false));
236 if (usefile_1 == NULL) {
237 usefile_1 = usefile;
238 rombytesize_1 = rombytesize;
239 } else {
240 usefile_2 = usefile;
241 rombytesize_2 = rombytesize;
242 }
243 } else {
244 WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_NOT_OPEN"),
245 temp_line.c_str());
246 return;
247 }
248 }
249 i++;
250 }
251
252 swapInDisks(0);
253
254 if (!imageDiskList[drive_index(drive)]) {
255 WriteOut(MSG_Get("PROGRAM_BOOT_UNABLE"), drive);
256 return;
257 }
258
259 bootSector bootarea;
260 imageDiskList[drive_index(drive)]->Read_Sector(
261 0, 0, 1, reinterpret_cast<uint8_t *>(&bootarea));
262 if ((bootarea.rawdata[0] == 0x50) && (bootarea.rawdata[1] == 0x43) &&
263 (bootarea.rawdata[2] == 0x6a) && (bootarea.rawdata[3] == 0x72)) {
264 if (machine != MCH_PCJR) {
265 WriteOut(MSG_Get("PROGRAM_BOOT_CART_WO_PCJR"));
266 } else {
267 Bit8u rombuf[65536];
268 Bits cfound_at = -1;
269 if (!cart_cmd.empty()) {
270 /* read cartridge data into buffer */
271 fseek(usefile_1, 0x200L, SEEK_SET);
272 if (fread(rombuf, 1, rombytesize_1 - 0x200,
273 usefile_1) < rombytesize_1 - 0x200) {
274 LOG_MSG("Failed to read sufficient cartridge data");
275 fclose(usefile_1);
276 return;
277 }
278 char cmdlist[1024];
279 cmdlist[0] = 0;
280 Bitu ct = 6;
281 Bits clen = rombuf[ct];
282 char buf[257];
283 if (cart_cmd == "?") {
284 while (clen != 0) {
285 strncpy(buf, (char *)&rombuf[ct + 1],
286 clen);
287 buf[clen] = 0;
288 upcase(buf);
289 safe_strcat(cmdlist, " ");
290 safe_strcat(cmdlist, buf);
291 ct += 1 + clen + 3;
292 if (ct > sizeof(cmdlist))
293 break;
294 clen = rombuf[ct];
295 }
296 if (ct > 6) {
297 WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),
298 cmdlist);
299 } else {
300 WriteOut(MSG_Get(
301 "PROGRAM_BOOT_CART_NO_CMDS"));
302 }
303 for (auto &disk : diskSwap)
304 disk.reset();
305 // fclose(usefile_1); //delete diskSwap
306 // closes the file
307 return;
308 } else {
309 while (clen != 0) {
310 strncpy(buf, (char *)&rombuf[ct + 1],
311 clen);
312 buf[clen] = 0;
313 upcase(buf);
314 safe_strcat(cmdlist, " ");
315 safe_strcat(cmdlist, buf);
316 ct += 1 + clen;
317
318 if (cart_cmd == buf) {
319 cfound_at = ct;
320 break;
321 }
322
323 ct += 3;
324 if (ct > sizeof(cmdlist))
325 break;
326 clen = rombuf[ct];
327 }
328 if (cfound_at <= 0) {
329 if (ct > 6) {
330 WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),
331 cmdlist);
332 } else {
333 WriteOut(MSG_Get(
334 "PROGRAM_BOOT_CART_NO_CMDS"));
335 }
336 for (auto &disk : diskSwap)
337 disk.reset();
338 // fclose(usefile_1); //Delete
339 // diskSwap closes the file
340 return;
341 }
342 }
343 }
344
345 disable_umb_ems_xms();
346 MEM_PreparePCJRCartRom();
347
348 if (usefile_1 == NULL)
349 return;
350
351 Bit32u sz1, sz2;
352 FILE *tfile = getFSFile("system.rom", &sz1, &sz2, true);
353 if (tfile != NULL) {
354 fseek(tfile, 0x3000L, SEEK_SET);
355 Bit32u drd = (Bit32u)fread(rombuf, 1, 0xb000, tfile);
356 if (drd == 0xb000) {
357 for (i = 0; i < 0xb000; i++)
358 phys_writeb(0xf3000 + i, rombuf[i]);
359 }
360 fclose(tfile);
361 }
362
363 if (usefile_2 != NULL) {
364 fseek(usefile_2, 0x0L, SEEK_SET);
365 if (fread(rombuf, 1, 0x200, usefile_2) < 0x200) {
366 LOG_MSG("Failed to read sufficient ROM data");
367 fclose(usefile_2);
368 return;
369 }
370
371 PhysPt romseg_pt = host_readw(&rombuf[0x1ce]) << 4;
372
373 /* read cartridge data into buffer */
374 fseek(usefile_2, 0x200L, SEEK_SET);
375 if (fread(rombuf, 1, rombytesize_2 - 0x200,
376 usefile_2) < rombytesize_2 - 0x200) {
377 LOG_MSG("Failed to read sufficient ROM data");
378 fclose(usefile_2);
379 return;
380 }
381
382 // fclose(usefile_2); //usefile_2 is in diskSwap
383 // structure which should be deleted to close
384 // the file
385
386 /* write cartridge data into ROM */
387 for (i = 0; i < rombytesize_2 - 0x200; i++)
388 phys_writeb(romseg_pt + i, rombuf[i]);
389 }
390
391 fseek(usefile_1, 0x0L, SEEK_SET);
392 if (fread(rombuf, 1, 0x200, usefile_1) < 0x200) {
393 LOG_MSG("Failed to read sufficient cartridge data");
394 fclose(usefile_1);
395 return;
396 }
397
398 Bit16u romseg = host_readw(&rombuf[0x1ce]);
399
400 /* read cartridge data into buffer */
401 fseek(usefile_1, 0x200L, SEEK_SET);
402 if (fread(rombuf, 1, rombytesize_1 - 0x200, usefile_1) <
403 rombytesize_1 - 0x200) {
404 LOG_MSG("Failed to read sufficient cartridge data");
405 fclose(usefile_1);
406 return;
407 }
408 // fclose(usefile_1); //usefile_1 is in diskSwap
409 // structure which should be deleted to close the file
410
411 /* write cartridge data into ROM */
412 for (i = 0; i < rombytesize_1 - 0x200; i++)
413 phys_writeb((romseg << 4) + i, rombuf[i]);
414
415 // Close cardridges
416 for (auto &disk : diskSwap)
417 disk.reset();
418
419 if (cart_cmd.empty()) {
420 Bit32u old_int18 = mem_readd(0x60);
421 /* run cartridge setup */
422 SegSet16(ds, romseg);
423 SegSet16(es, romseg);
424 SegSet16(ss, 0x8000);
425 reg_esp = 0xfffe;
426 CALLBACK_RunRealFar(romseg, 0x0003);
427
428 Bit32u new_int18 = mem_readd(0x60);
429 if (old_int18 != new_int18) {
430 /* boot cartridge (int18) */
431 SegSet16(cs, RealSeg(new_int18));
432 reg_ip = RealOff(new_int18);
433 }
434 } else {
435 if (cfound_at > 0) {
436 /* run cartridge setup */
437 SegSet16(ds, dos.psp());
438 SegSet16(es, dos.psp());
439 CALLBACK_RunRealFar(romseg, cfound_at);
440 }
441 }
442 }
443 } else {
444 disable_umb_ems_xms();
445 MEM_RemoveEMSPageFrame();
446 WriteOut(MSG_Get("PROGRAM_BOOT_BOOT"), drive);
447 for (i = 0; i < 512; i++)
448 real_writeb(0, 0x7c00 + i, bootarea.rawdata[i]);
449
450 /* create appearance of floppy drive DMA usage (Demon's Forge) */
451 if (!IS_TANDY_ARCH && floppysize != 0)
452 GetDMAChannel(2)->tcount = true;
453
454 /* revector some dos-allocated interrupts */
455 real_writed(0, 0x01 * 4, 0xf000ff53);
456 real_writed(0, 0x03 * 4, 0xf000ff53);
457
458 SegSet16(cs, 0);
459 reg_ip = 0x7c00;
460 SegSet16(ds, 0);
461 SegSet16(es, 0);
462 /* set up stack at a safe place */
463 SegSet16(ss, 0x7000);
464 reg_esp = 0x100;
465 reg_esi = 0;
466 reg_ecx = 1;
467 reg_ebp = 0;
468 reg_eax = 0;
469 reg_edx = 0; // Head 0 drive 0
470 reg_ebx = 0x7c00; // Real code probably uses bx to load the image
471 }
472 }
473
BOOT_ProgramStart(Program ** make)474 void BOOT_ProgramStart(Program **make)
475 {
476 *make = new BOOT;
477 }
478