fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/arch/ibmpc/hook.c *
7 * Created: 2003-09-02 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2003-2022 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include "main.h"
24#include "ems.h"
25#include "hook.h"
26#include "ibmpc.h"
27#include "int13.h"
28#include "msg.h"
29#include "xms.h"
30
31#include <string.h>
32#include <time.h>
33
34#ifdef HAVE_SYS_TIME_H
35#include <sys/time.h>
36#endif
37
38#include <cpu/e8086/e8086.h>
39
40#include <devices/memory.h>
41
42#include <lib/log.h>
43
44
45static
46int pc_int_15 (ibmpc_t *pc)
47{
48 unsigned ah;
49 unsigned short seg, ofs;
50 unsigned long size, src, dst;
51
52 ah = e86_get_ah (pc->cpu);
53
54 if (ah == 0x87) {
55 seg = e86_get_es (pc->cpu);
56 ofs = e86_get_si (pc->cpu);
57 size = e86_get_cx (pc->cpu) * 2;
58 src = e86_get_mem16 (pc->cpu, seg, ofs + 0x12);
59 src |= (e86_get_mem8 (pc->cpu, seg, ofs + 0x14) & 0xff) << 16;
60 dst = e86_get_mem16 (pc->cpu, seg, ofs + 0x1a);
61 dst |= (e86_get_mem8 (pc->cpu, seg, ofs + 0x1c) & 0xff) << 16;
62 pce_log (MSG_DEB, "int15: copy %08x -> %08x, %04x\n",
63 src, dst, size
64 );
65 e86_set_cf (pc->cpu, 1);
66 e86_set_ah (pc->cpu, 0x86);
67 return (0);
68 }
69
70 if (ah == 0x88) {
71 /* No extended memory. Only XMS. */
72 e86_set_cf (pc->cpu, 0);
73 e86_set_ax (pc->cpu, 0x0000);
74 e86_set_cf (pc->cpu, 1);
75 return (0);
76 }
77
78 if (ah == 0x90) {
79 e86_set_ah (pc->cpu, 0);
80 e86_set_cf (pc->cpu, 0);
81 if (e86_get_al (pc->cpu) == 0xfd) {
82 /* diskette motor start */
83 e86_set_cf (pc->cpu, 1);
84 }
85 return (0);
86 }
87
88 if (ah == 0x91) {
89 e86_set_ah (pc->cpu, 0);
90 e86_set_cf (pc->cpu, 0);
91 return (0);
92 }
93
94 return (1);
95}
96
97static
98unsigned get_bcd_8 (unsigned n)
99{
100 return ((n % 10) + 16 * ((n / 10) % 10));
101}
102
103static
104int pc_int_1a (ibmpc_t *pc)
105{
106 unsigned ah;
107 time_t tm;
108 struct tm *tval;
109
110 ah = e86_get_ah (pc->cpu);
111
112 if (pc->support_rtc == 0) {
113 return (1);
114 }
115
116 if (ah == 0x02) {
117 tm = time (NULL);
118 tval = localtime (&tm);
119 e86_set_ch (pc->cpu, get_bcd_8 (tval->tm_hour));
120 e86_set_cl (pc->cpu, get_bcd_8 (tval->tm_min));
121 e86_set_dh (pc->cpu, get_bcd_8 (tval->tm_sec));
122 e86_set_cf (pc->cpu, 0);
123
124 return (0);
125 }
126
127 if (ah == 0x03) {
128 e86_set_cf (pc->cpu, 0);
129 return (0);
130 }
131
132 if (ah == 0x04) {
133 tm = time (NULL);
134 tval = localtime (&tm);
135 e86_set_ch (pc->cpu, get_bcd_8 ((1900 + tval->tm_year) / 100));
136 e86_set_cl (pc->cpu, get_bcd_8 (1900 + tval->tm_year));
137 e86_set_dh (pc->cpu, get_bcd_8 (tval->tm_mon + 1));
138 e86_set_dl (pc->cpu, get_bcd_8 (tval->tm_mday));
139 e86_set_cf (pc->cpu, 0);
140
141 return (0);
142 }
143 else if (ah == 0x05) {
144 e86_set_cf (pc->cpu, 0);
145 return (0);
146 }
147
148 return (1);
149}
150
151static
152void pc_hook_log (ibmpc_t *pc)
153{
154 pce_log (MSG_DEB,
155 "pce: hook %04X:%04X AX=%04X BX=%04X CX=%04X DX=%04X DS=%04X ES=%04X\n",
156 e86_get_cs (pc->cpu), e86_get_ip (pc->cpu),
157 e86_get_ax (pc->cpu), e86_get_bx (pc->cpu),
158 e86_get_cx (pc->cpu), e86_get_dx (pc->cpu),
159 e86_get_ds (pc->cpu), e86_get_es (pc->cpu)
160 );
161}
162
163void pc_hook_set_result (ibmpc_t *pc, unsigned err)
164{
165 if ((err & 0xffff) != 0) {
166 e86_set_ax (pc->cpu, err);
167 e86_set_cf (pc->cpu, 1);
168 }
169 else {
170 e86_set_cf (pc->cpu, 0);
171 }
172}
173
174static
175int pc_hook_get_string (ibmpc_t *pc, unsigned char *dst, unsigned max, unsigned seg, unsigned ofs)
176{
177 unsigned i;
178
179 for (i = 0; i < max; i++) {
180 if ((dst[i] = e86_get_mem8 (pc->cpu, seg, ofs + i)) == 0) {
181 return (0);
182 }
183 }
184
185 return (1);
186}
187
188static
189void pc_hook_unknown (ibmpc_t *pc)
190{
191 pc_hook_log (pc);
192 pc_hook_set_result (pc, 0xffff);
193}
194
195static
196void pc_hook_check (ibmpc_t *pc)
197{
198 e86_set_ax (pc->cpu, 0x0fce);
199 e86_set_cf (pc->cpu, 0);
200}
201
202static
203void pc_hook_get_version (ibmpc_t *pc)
204{
205 e86_set_ax (pc->cpu, (PCE_VERSION_MIC << 8) | 0);
206 e86_set_dx (pc->cpu, (PCE_VERSION_MAJ << 8) | PCE_VERSION_MIN);
207 pc_hook_set_result (pc, 0);
208}
209
210static
211void pc_hook_get_version_str (ibmpc_t *pc)
212{
213 unsigned i, n;
214 unsigned short seg, ofs, max;
215 const char *str;
216
217 seg = e86_get_es (pc->cpu);
218 ofs = e86_get_di (pc->cpu);
219 max = e86_get_cx (pc->cpu);
220 str = PCE_VERSION_STR;
221
222 n = strlen (str) + 1;
223
224 if (n > max) {
225 pc_hook_set_result (pc, 0xffff);
226 return;
227 }
228
229 for (i = 0; i < n; i++) {
230 e86_set_mem8 (pc->cpu, seg, ofs + i, str[i]);
231 }
232
233 pc_hook_set_result (pc, 0);
234}
235
236static
237void pc_hook_stop (ibmpc_t *pc)
238{
239 pc->brk = 1;
240 pc_hook_set_result (pc, 0);
241}
242
243static
244void pc_hook_abort (ibmpc_t *pc)
245{
246 pc->brk = 2;
247 pc_hook_set_result (pc, 0);
248}
249
250static
251void pc_hook_set_msg (ibmpc_t *pc)
252{
253 unsigned char msg[256];
254 unsigned char val[256];
255
256 if (pc_hook_get_string (pc, msg, 256, e86_get_ds (pc->cpu), e86_get_si (pc->cpu))) {
257 pc_hook_set_result (pc, 0xffff);
258 return;
259 }
260
261 if (pc_hook_get_string (pc, val, 256, e86_get_es (pc->cpu), e86_get_di (pc->cpu))) {
262 pc_hook_set_result (pc, 0xffff);
263 return;
264 }
265
266 e86_set_ax (pc->cpu, 0);
267
268 if (pc_set_msg (pc, (char *) msg, (char *) val)) {
269 pc_hook_set_result (pc, 0x0001);
270 }
271 else {
272 pc_hook_set_result (pc, 0);
273 }
274}
275
276static
277void pc_hook_get_time_unix (ibmpc_t *pc)
278{
279 unsigned long long tm;
280
281 if (pc->support_rtc == 0) {
282 pc_hook_set_result (pc, 0xffff);
283 return;
284 }
285
286 tm = (unsigned long long) time (NULL);
287
288 e86_set_ax (pc->cpu, tm & 0xffff);
289 tm >>= 16;
290 e86_set_dx (pc->cpu, tm & 0xffff);
291 tm >>= 16;
292 e86_set_cx (pc->cpu, tm & 0xffff);
293 tm >>= 16;
294 e86_set_bx (pc->cpu, tm & 0xffff);
295
296 pc_hook_set_result (pc, 0);
297}
298
299static
300void pc_hook_get_time_bios (ibmpc_t *pc, int local)
301{
302 unsigned long v;
303 time_t tm;
304 struct tm *tv;
305
306 if (pc->support_rtc == 0) {
307 pc_hook_set_result (pc, 0xffff);
308 return;
309 }
310
311 tm = time (NULL);
312
313 if (local) {
314 tv = localtime (&tm);
315 }
316 else {
317 tv = gmtime (&tm);
318 }
319
320 v = (60UL * ((60U * tv->tm_hour) + tv->tm_min)) + tv->tm_sec;
321 v = ((unsigned long long) PCE_IBMPC_CLK2 * v) / 65536;
322
323 e86_set_ax (pc->cpu, v & 0xffff);
324 e86_set_dx (pc->cpu, (v >> 16) & 0xffff);
325
326 pc_hook_set_result (pc, 0);
327}
328
329static
330void pc_hook_get_calendar (ibmpc_t *pc, int local)
331{
332 unsigned cs;
333 time_t tm;
334 struct tm *tv;
335
336 if (pc->support_rtc == 0) {
337 pc_hook_set_result (pc, 0xffff);
338 return;
339 }
340
341#ifdef HAVE_GETTIMEOFDAY
342 {
343 struct timeval tv;
344
345 if (gettimeofday (&tv, NULL)) {
346 tm = time (NULL);
347 cs = 0;
348 }
349 else {
350 tm = tv.tv_sec;
351 cs = tv.tv_usec / 10000;
352 }
353 }
354#else
355 tm = time (NULL);
356 cs = 0;
357#endif
358
359 if (local) {
360 tv = localtime (&tm);
361 }
362 else {
363 tv = gmtime (&tm);
364 }
365
366 e86_set_ax (pc->cpu, 1900 + tv->tm_year);
367 e86_set_bh (pc->cpu, tv->tm_mon);
368 e86_set_bl (pc->cpu, tv->tm_mday - 1);
369 e86_set_ch (pc->cpu, tv->tm_hour);
370 e86_set_cl (pc->cpu, tv->tm_min);
371 e86_set_dh (pc->cpu, tv->tm_sec);
372 e86_set_dl (pc->cpu, cs);
373
374 pc_hook_set_result (pc, 0);
375}
376
377static
378void pc_hook_xms (ibmpc_t *pc)
379{
380 e86_set_ax (pc->cpu, e86_get_bp (pc->cpu));
381 xms_handler (pc->xms, pc->cpu);
382 pc_hook_set_result (pc, 0);
383}
384
385static
386void pc_hook_xms_info (ibmpc_t *pc)
387{
388 xms_info (pc->xms, pc->cpu);
389 pc_hook_set_result (pc, 0);
390}
391
392static
393void pc_hook_ems (ibmpc_t *pc)
394{
395 e86_set_ax (pc->cpu, e86_get_bp (pc->cpu));
396 ems_handler (pc->ems, pc->cpu);
397 pc_hook_set_result (pc, 0);
398}
399
400static
401void pc_hook_ems_info (ibmpc_t *pc)
402{
403 ems_info (pc->ems, pc->cpu);
404 pc_hook_set_result (pc, 0);
405}
406
407static
408void pc_hook_int (ibmpc_t *pc, unsigned n)
409{
410 int exec;
411 unsigned ss, sp, ax;
412
413 ss = e86_get_ss (pc->cpu);
414 sp = e86_get_sp (pc->cpu);
415 ax = e86_get_mem16 (pc->cpu, ss, sp);
416 e86_set_sp (pc->cpu, e86_get_sp (pc->cpu) + 2);
417 e86_set_ax (pc->cpu, ax);
418
419 exec = 0;
420
421 if (n == 0x13) {
422 if (pc_int13_check (pc)) {
423 dsk_int13 (pc->dsk, pc->cpu);
424 exec = 1;
425 }
426 }
427 else if (n == 0x15) {
428 exec = !pc_int_15 (pc);
429 }
430 else if (n == 0x1a) {
431 exec = !pc_int_1a (pc);
432 }
433 else {
434 pc_hook_log (pc);
435 }
436
437 if (exec) {
438 e86_set_ip (pc->cpu, e86_get_ip (pc->cpu) + 2);
439 e86_pq_init (pc->cpu);
440 }
441}
442
443static
444void pc_hook_get_model (ibmpc_t *pc)
445{
446 unsigned model;
447
448 model = 0;
449
450 if (pc->model & PCE_IBMPC_5150) {
451 model |= 1;
452 }
453
454 if (pc->model & PCE_IBMPC_5160) {
455 model |= 2;
456 }
457
458 e86_set_ax (pc->cpu, model);
459
460 pc_hook_set_result (pc, 0);
461}
462
463static
464void pc_hook_get_com (ibmpc_t *pc)
465{
466 e86_set_ax (pc->cpu, (pc->serport[0] != NULL) ? pc->serport[0]->io : 0);
467 e86_set_bx (pc->cpu, (pc->serport[1] != NULL) ? pc->serport[1]->io : 0);
468 e86_set_cx (pc->cpu, (pc->serport[2] != NULL) ? pc->serport[2]->io : 0);
469 e86_set_dx (pc->cpu, (pc->serport[3] != NULL) ? pc->serport[3]->io : 0);
470 pc_hook_set_result (pc, 0);
471}
472
473static
474void pc_hook_get_lpt (ibmpc_t *pc)
475{
476 e86_set_ax (pc->cpu, (pc->parport[0] != NULL) ? pc->parport[0]->io : 0);
477 e86_set_bx (pc->cpu, (pc->parport[1] != NULL) ? pc->parport[1]->io : 0);
478 e86_set_cx (pc->cpu, (pc->parport[2] != NULL) ? pc->parport[2]->io : 0);
479 e86_set_dx (pc->cpu, (pc->parport[3] != NULL) ? pc->parport[3]->io : 0);
480 pc_hook_set_result (pc, 0);
481}
482
483static
484void pc_hook_get_boot (ibmpc_t *pc)
485{
486 e86_set_ax (pc->cpu, pc_get_bootdrive (pc) & 0xff);
487 pc_hook_set_result (pc, 0);
488}
489
490static
491void pc_hook_get_drvcnt (ibmpc_t *pc)
492{
493 e86_set_ah (pc->cpu, pc->hd_cnt);
494 e86_set_al (pc->cpu, pc->fd_cnt);
495 pc_hook_set_result (pc, 0);
496}
497
498int pc_hook (void *ext)
499{
500 unsigned op;
501 ibmpc_t *pc;
502
503 pc = ext;
504
505 op = e86_get_ax (pc->cpu);
506
507 switch (op & 0xff00) {
508 case PCE_HOOK_INT:
509 pc_hook_int (pc, op & 0xff);
510 return (0);
511 }
512
513 switch (op) {
514 case PCE_HOOK_CHECK:
515 pc_hook_check (pc);
516 break;
517
518 case PCE_HOOK_GET_VERSION:
519 pc_hook_get_version (pc);
520 break;
521
522 case PCE_HOOK_GET_VERSION_STR:
523 pc_hook_get_version_str (pc);
524 break;
525
526 case PCE_HOOK_STOP:
527 pc_hook_stop (pc);
528 break;
529
530 case PCE_HOOK_ABORT:
531 pc_hook_abort (pc);
532 break;
533
534 case PCE_HOOK_SET_MSG:
535 pc_hook_set_msg (pc);
536 break;
537
538 case PCE_HOOK_GET_TIME_UNIX:
539 pc_hook_get_time_unix (pc);
540 break;
541
542 case PCE_HOOK_GET_TIME_LOCAL:
543 pc_hook_get_calendar (pc, 1);
544 break;
545
546 case PCE_HOOK_GET_TIME_UTC:
547 pc_hook_get_calendar (pc, 0);
548 break;
549
550 case PCE_HOOK_GET_TIME_BIOS:
551 pc_hook_get_time_bios (pc, 1);
552 break;
553
554 case PCE_HOOK_XMS:
555 pc_hook_xms (pc);
556 break;
557
558 case PCE_HOOK_XMS_INFO:
559 pc_hook_xms_info (pc);
560 break;
561
562 case PCE_HOOK_EMS:
563 pc_hook_ems (pc);
564 break;
565
566 case PCE_HOOK_EMS_INFO:
567 pc_hook_ems_info (pc);
568 break;
569
570 case PCE_HOOK_GET_MODEL:
571 pc_hook_get_model (pc);
572 break;
573
574 case PCE_HOOK_GET_COM:
575 pc_hook_get_com (pc);
576 break;
577
578 case PCE_HOOK_GET_LPT:
579 pc_hook_get_lpt (pc);
580 break;
581
582 case PCE_HOOK_GET_BOOT:
583 pc_hook_get_boot (pc);
584 break;
585
586 case PCE_HOOK_GET_DRVCNT:
587 pc_hook_get_drvcnt (pc);
588 break;
589
590 default:
591 pc_hook_unknown (pc);
592 break;
593 }
594
595 return (0);
596}
597
598static
599void pc_hook_version (ibmpc_t *pc)
600{
601 unsigned short es, di;
602 const char *str;
603
604 es = e86_get_es (pc->cpu);
605 di = e86_get_di (pc->cpu);
606 str = PCE_VERSION_STR;
607
608 e86_set_ax (pc->cpu, (PCE_VERSION_MAJ << 8) | PCE_VERSION_MIN);
609 e86_set_dx (pc->cpu, (PCE_VERSION_MIC << 8));
610
611 if ((es == 0) && (di == 0)) {
612 return;
613 }
614
615 while (*str != 0) {
616 e86_set_mem8 (pc->cpu, es, di, *str);
617 di = (di + 1) & 0xffff;
618 str += 1;
619 }
620
621 e86_set_mem8 (pc->cpu, es, di, 0);
622}
623
624static
625void pc_hook_msg (ibmpc_t *pc)
626{
627 unsigned i, p;
628 unsigned short ds;
629 char msg[256];
630 char val[256];
631
632 e86_set_cf (pc->cpu, 1);
633
634 ds = e86_get_ds (pc->cpu);
635
636 p = e86_get_si (pc->cpu);
637 for (i = 0; i < 256; i++) {
638 msg[i] = e86_get_mem16 (pc->cpu, ds, p++);
639 if (msg[i] == 0) {
640 break;
641 }
642 }
643
644 if (i >= 256) {
645 return;
646 }
647
648 p = e86_get_di (pc->cpu);
649 for (i = 0; i < 256; i++) {
650 val[i] = e86_get_mem16 (pc->cpu, ds, p++);
651 if (val[i] == 0) {
652 break;
653 }
654 }
655
656 if (i >= 256) {
657 return;
658 }
659
660 if (pc_set_msg (pc, msg, val)) {
661 e86_set_cf (pc->cpu, 1);
662 e86_set_ax (pc->cpu, 0x0001);
663 }
664 else {
665 e86_set_cf (pc->cpu, 0);
666 e86_set_ax (pc->cpu, 0x0000);
667 }
668}
669
670void pc_hook_old (void *ext, unsigned char op1, unsigned char op2)
671{
672 ibmpc_t *pc;
673 unsigned long msk;
674
675 pc = (ibmpc_t *) ext;
676
677 pc_hook_log (pc);
678
679 switch (op1) {
680 case (PCEH_INT & 0xff):
681 if (op2 == 0x13) {
682 dsk_int13 (pc->dsk, pc->cpu);
683 return;
684 }
685 else if (op2 == 0x1a) {
686 pc_int_1a (pc);
687 return;
688 }
689 else if (op2 == 0x15) {
690 pc_int_15 (pc);
691 return;
692 }
693 break;
694
695 case (PCEH_CHECK_INT & 0xff):
696 e86_set_ax (pc->cpu, 0);
697
698 if (op2 == 0x13) {
699 dsk_int_13_check (pc);
700 }
701 return;
702 }
703
704 e86_set_cf (pc->cpu, 0);
705
706 switch ((op2 << 8) | op1) {
707 case PCEH_STOP:
708 pc->brk = 1;
709 break;
710
711 case PCEH_ABORT:
712 pc->brk = 2;
713 break;
714
715 case PCEH_SET_BOOT:
716 pc_set_bootdrive (pc, e86_get_al (pc->cpu));
717 break;
718
719 case PCEH_SET_INT28:
720 /* not supported anymore */
721 break;
722
723 case PCEH_SET_CPU:
724 /* not supported anymore */
725 break;
726
727 case PCEH_SET_AMSK:
728 msk = (e86_get_dx (pc->cpu) << 16) + e86_get_ax (pc->cpu);
729 e86_set_addr_mask (pc->cpu, msk);
730 break;
731
732 case PCEH_GET_BOOT:
733 e86_set_al (pc->cpu, pc_get_bootdrive (pc));
734 break;
735
736 case PCEH_GET_COM:
737 e86_set_ax (pc->cpu, (pc->serport[0] != NULL) ? pc->serport[0]->io : 0);
738 e86_set_bx (pc->cpu, (pc->serport[1] != NULL) ? pc->serport[1]->io : 0);
739 e86_set_cx (pc->cpu, (pc->serport[2] != NULL) ? pc->serport[2]->io : 0);
740 e86_set_dx (pc->cpu, (pc->serport[3] != NULL) ? pc->serport[3]->io : 0);
741 break;
742
743 case PCEH_GET_LPT:
744 e86_set_ax (pc->cpu, (pc->parport[0] != NULL) ? pc->parport[0]->io : 0);
745 e86_set_bx (pc->cpu, (pc->parport[1] != NULL) ? pc->parport[1]->io : 0);
746 e86_set_cx (pc->cpu, (pc->parport[2] != NULL) ? pc->parport[2]->io : 0);
747 e86_set_dx (pc->cpu, (pc->parport[3] != NULL) ? pc->parport[3]->io : 0);
748 break;
749
750 case PCEH_GET_VIDEO:
751 /* not supported anymore */
752 e86_set_ax (pc->cpu, 0);
753 break;
754
755 case PCEH_GET_INT28:
756 /* not supported anymore */
757 e86_set_ax (pc->cpu, 0);
758 break;
759
760 case PCEH_GET_CPU:
761 /* not supported anymore */
762 e86_set_ax (pc->cpu, 0);
763 break;
764
765 case PCEH_GET_AMSK:
766 msk = e86_get_addr_mask (pc->cpu);
767 e86_set_ax (pc->cpu, msk & 0xffff);
768 e86_set_dx (pc->cpu, (msk >> 16) & 0xffff);
769 break;
770
771 case PCEH_GET_FDCNT:
772 e86_set_ax (pc->cpu, pc->fd_cnt);
773 break;
774
775 case PCEH_GET_HDCNT:
776 e86_set_ax (pc->cpu, pc->hd_cnt);
777 break;
778
779 case PCEH_GET_VERS:
780 pc_hook_version (pc);
781 break;
782
783 case PCEH_XMS:
784 xms_handler (pc->xms, pc->cpu);
785 break;
786
787 case PCEH_XMS_INFO:
788 xms_info (pc->xms, pc->cpu);
789 break;
790
791 case PCEH_EMS:
792 ems_handler (pc->ems, pc->cpu);
793 break;
794
795 case PCEH_EMS_INFO:
796 ems_info (pc->ems, pc->cpu);
797 break;
798
799 case PCEH_MSG:
800 pc_hook_msg (pc);
801 break;
802
803 default:
804 e86_set_cf (pc->cpu, 1);
805 pc_hook_log (pc);
806 break;
807 }
808}