1 /*
2   Hatari - vdi.c
3 
4   This file is distributed under the GNU General Public License, version 2
5   or at your option any later version. Read the file gpl.txt for details.
6 
7   VDI (Virtual Device Interface) (Trap #2)
8 
9   To get higher resolutions on the Desktop, we intercept the VDI/Line-A calls
10   and set elements in their structures to the higher width/height/cel/planes.
11   We need to intercept the initial Line-A call (which we force into the TOS on
12   boot-up) and also the init calls to the VDI.
13 */
14 const char VDI_fileid[] = "Hatari vdi.c : " __DATE__ " " __TIME__;
15 
16 #include "main.h"
17 #include "configuration.h"
18 #include "file.h"
19 #include "gemdos.h"
20 #include "inffile.h"
21 #include "m68000.h"
22 #include "options.h"
23 #include "screen.h"
24 #include "stMemory.h"
25 #include "tos.h"
26 #include "vdi.h"
27 #include "video.h"
28 
29 #define DEBUG 0
30 
31 Uint32 VDI_OldPC;                  /* When call Trap#2, store off PC */
32 
33 bool bVdiAesIntercept = false;     /* Set to true to trace VDI & AES calls */
34 bool bUseVDIRes = false;           /* Set to true (if want VDI), or false (ie for games) */
35 /* defaults */
36 int VDIRes = 0;                    /* 0,1 or 2 (low, medium, high) */
37 int VDIWidth = 640;                /* 640x480, 800x600 or 1024x768 */
38 int VDIHeight = 480;
39 int VDIPlanes = 4;
40 
41 static Uint32 LineABase;           /* Line-A structure */
42 static Uint32 FontBase;            /* Font base, used for 16-pixel high font */
43 
44 /* Last VDI opcode & vectors */
45 static Uint16 VDIOpCode;
46 static Uint32 VDIControl;
47 static Uint32 VDIIntin;
48 static Uint32 VDIPtsin;
49 static Uint32 VDIIntout;
50 static Uint32 VDIPtsout;
51 #if ENABLE_TRACING
52 /* Last AES opcode & vectors */
53 static Uint32 AESControl;
54 static Uint32 AESGlobal;
55 static Uint32 AESIntin;
56 static Uint32 AESIntout;
57 static Uint32 AESAddrin;
58 static Uint32 AESAddrout;
59 static Uint16 AESOpCode;
60 #endif
61 
62 
63 /*-----------------------------------------------------------------------*/
64 /**
65  * Called to reset VDI variables on reset.
66  */
VDI_Reset(void)67 void VDI_Reset(void)
68 {
69 	/* no VDI calls in progress */
70 	VDI_OldPC = 0;
71 }
72 
73 /*-----------------------------------------------------------------------*/
74 /**
75  * Limit width and height to VDI screen size in bytes, retaining their ratio.
76  * Return true if limiting was done.
77  */
VDI_ByteLimit(int * width,int * height,int planes)78 static bool VDI_ByteLimit(int *width, int *height, int planes)
79 {
80 	double ratio;
81 	int size;
82 
83 	size = (*width)*(*height)*planes/8;
84 	if (size <= MAX_VDI_BYTES)
85 		return false;
86 
87 	ratio = sqrt(MAX_VDI_BYTES) / sqrt(size);
88 	*width = (*width) * ratio;
89 	*height = (*height) * ratio;
90 	if (*width < MIN_VDI_WIDTH || *height < MIN_VDI_HEIGHT)
91 	{
92 		*width = MIN_VDI_WIDTH;
93 		*height = MIN_VDI_HEIGHT;
94 		Log_Printf(LOG_WARN, "Bad VDI screen ratio / too small size -> use smallest valid size.\n");
95 	}
96 	else
97 		Log_Printf(LOG_WARN, "VDI screen size limited to <= %dKB\n", MAX_VDI_BYTES/1024);
98 	return true;
99 }
100 
101 /*-----------------------------------------------------------------------*/
102 /**
103  * Set Width/Height/BitDepth according to passed GEMCOLOR_2/4/16.
104  * Align size when necessary.
105  */
VDI_SetResolution(int GEMColor,int WidthRequest,int HeightRequest)106 void VDI_SetResolution(int GEMColor, int WidthRequest, int HeightRequest)
107 {
108 	int w = WidthRequest;
109 	int h = HeightRequest;
110 
111 	/* Color depth */
112 	switch (GEMColor)
113 	{
114 	 case GEMCOLOR_2:
115 		VDIRes = 2;
116 		VDIPlanes = 1;
117 		break;
118 	 case GEMCOLOR_4:
119 		VDIRes = 1;
120 		VDIPlanes = 2;
121 		break;
122 	 case GEMCOLOR_16:
123 		VDIRes = 0;
124 		VDIPlanes = 4;
125 		break;
126 	}
127 	/* screen size in bytes needs to be below limit */
128 	VDI_ByteLimit(&w, &h, VDIPlanes);
129 
130 #if DEBUG
131 	printf("%s v0x%04x, RAM=%dkB\n", bIsEmuTOS ? "EmuTOS" : "TOS", TosVersion,  ConfigureParams.Memory.STRamSize_KB);
132 #endif
133 	/* width needs to be aligned to 16 bytes */
134 	VDIWidth = Opt_ValueAlignMinMax(w, 128/VDIPlanes, MIN_VDI_WIDTH, MAX_VDI_WIDTH);
135 
136 	/* height needs to be multiple of cell height (either 8 or 16) */
137 	VDIHeight = Opt_ValueAlignMinMax(h, 16, MIN_VDI_HEIGHT, MAX_VDI_HEIGHT);
138 
139 	if (w != VDIWidth || h != VDIHeight)
140 	{
141 		Log_Printf(LOG_WARN, "VDI screen: request = %dx%d@%d, result = %dx%d@%d\n",
142 		       WidthRequest, HeightRequest, VDIPlanes, VDIWidth, VDIHeight, VDIPlanes);
143 	}
144 	else
145 	{
146 		Log_Printf(LOG_DEBUG, "VDI screen: %dx%d@%d\n",
147 			   VDIWidth, VDIHeight, VDIPlanes);
148 	}
149 	if (bUseVDIRes)
150 	{
151 		/* INF file overriding so that (re-)boot uses correct bit-depth */
152 		INF_SetVdiMode(VDIRes);
153 	}
154 }
155 
156 
157 #if ENABLE_TRACING
158 
159 /*-----------------------------------------------------------------------*/
160 
161 /* AES opcodes which have string args */
162 static const struct {
163 	int code;	/* AES opcode */
164 	int count;	/* number of char * args _first_ in addrin[] */
165 } AESStrings[] = {
166 	{ 0x0D, 1 },	/* appl_find() */
167 	{ 0x12, 1 },	/* appl_search() */
168 	{ 0x23, 1 },	/* menu_register() */
169 	{ 0x34, 1 },	/* form_alert() */
170 	{ 0x51, 1 },	/* scrp_write() */
171 	{ 0x5A, 2 },	/* fsel_input() */
172 	{ 0x5B, 3 },	/* fsel_exinput() */
173 	{ 0x6E, 1 },	/* rsrc_load() */
174 	{ 0x7C, 1 }	/* shell_find() */
175 };
176 
177 /* AES opcode -> function name mapping */
178 static const char* AESName_10[] = {
179 	"appl_init",		/* (0x0A) */
180 	"appl_read",		/* (0x0B) */
181 	"appl_write",		/* (0x0C) */
182 	"appl_find",		/* (0x0D) */
183 	"appl_tplay",		/* (0x0E) */
184 	"appl_trecord",		/* (0x0F) */
185 	NULL,			/* (0x10) */
186 	NULL,			/* (0x11) */
187 	"appl_search",		/* (0x12) */
188 	"appl_exit",		/* (0x13) */
189 	"evnt_keybd",		/* (0x14) */
190 	"evnt_button",		/* (0x15) */
191 	"evnt_mesag",		/* (0x16) */
192 	"evnt_mesag",		/* (0x17) */
193 	"evnt_timer",		/* (0x18) */
194 	"evnt_multi",		/* (0x19) */
195 	"evnt_dclick",		/* (0x1A) */
196 	NULL,			/* (0x1b) */
197 	NULL,			/* (0x1c) */
198 	NULL,			/* (0x1d) */
199 	"menu_bar",		/* (0x1E) */
200 	"menu_icheck",		/* (0x1F) */
201 	"menu_ienable",		/* (0x20) */
202 	"menu_tnormal",		/* (0x21) */
203 	"menu_text",		/* (0x22) */
204 	"menu_register",	/* (0x23) */
205 	"menu_popup",		/* (0x24) */
206 	"menu_attach",		/* (0x25) */
207 	"menu_istart",		/* (0x26) */
208 	"menu_settings",	/* (0x27) */
209 	"objc_add",		/* (0x28) */
210 	"objc_delete",		/* (0x29) */
211 	"objc_draw",		/* (0x2A) */
212 	"objc_find",		/* (0x2B) */
213 	"objc_offset",		/* (0x2C) */
214 	"objc_order",		/* (0x2D) */
215 	"objc_edit",		/* (0x2E) */
216 	"objc_change",		/* (0x2F) */
217 	"objc_sysvar",		/* (0x30) */
218 	NULL,			/* (0x31) */
219 	"form_do",		/* (0x32) */
220 	"form_dial",		/* (0x33) */
221 	"form_alert",		/* (0x34) */
222 	"form_error",		/* (0x35) */
223 	"form_center",		/* (0x36) */
224 	"form_keybd",		/* (0x37) */
225 	"form_button",		/* (0x38) */
226 	NULL,			/* (0x39) */
227 	NULL,			/* (0x3a) */
228 	NULL,			/* (0x3b) */
229 	NULL,			/* (0x3c) */
230 	NULL,			/* (0x3d) */
231 	NULL,			/* (0x3e) */
232 	NULL,			/* (0x3f) */
233 	NULL,			/* (0x40) */
234 	NULL,			/* (0x41) */
235 	NULL,			/* (0x42) */
236 	NULL,			/* (0x43) */
237 	NULL,			/* (0x44) */
238 	NULL,			/* (0x45) */
239 	"graf_rubberbox",	/* (0x46) */
240 	"graf_dragbox",		/* (0x47) */
241 	"graf_movebox",		/* (0x48) */
242 	"graf_growbox",		/* (0x49) */
243 	"graf_shrinkbox",	/* (0x4A) */
244 	"graf_watchbox",	/* (0x4B) */
245 	"graf_slidebox",	/* (0x4C) */
246 	"graf_handle",		/* (0x4D) */
247 	"graf_mouse",		/* (0x4E) */
248 	"graf_mkstate",		/* (0x4F) */
249 	"scrp_read",		/* (0x50) */
250 	"scrp_write",		/* (0x51) */
251 	NULL,			/* (0x52) */
252 	NULL,			/* (0x53) */
253 	NULL,			/* (0x54) */
254 	NULL,			/* (0x55) */
255 	NULL,			/* (0x56) */
256 	NULL,			/* (0x57) */
257 	NULL,			/* (0x58) */
258 	NULL,			/* (0x59) */
259 	"fsel_input",		/* (0x5A) */
260 	"fsel_exinput",		/* (0x5B) */
261 	NULL,			/* (0x5c) */
262 	NULL,			/* (0x5d) */
263 	NULL,			/* (0x5e) */
264 	NULL,			/* (0x5f) */
265 	NULL,			/* (0x60) */
266 	NULL,			/* (0x61) */
267 	NULL,			/* (0x62) */
268 	NULL,			/* (0x63) */
269 	"wind_create",		/* (0x64) */
270 	"wind_open",		/* (0x65) */
271 	"wind_close",		/* (0x66) */
272 	"wind_delete",		/* (0x67) */
273 	"wind_get",		/* (0x68) */
274 	"wind_set",		/* (0x69) */
275 	"wind_find",		/* (0x6A) */
276 	"wind_update",		/* (0x6B) */
277 	"wind_calc",		/* (0x6C) */
278 	"wind_new",		/* (0x6D) */
279 	"rsrc_load",		/* (0x6E) */
280 	"rsrc_free",		/* (0x6F) */
281 	"rsrc_gaddr",		/* (0x70) */
282 	"rsrc_saddr",		/* (0x71) */
283 	"rsrc_obfix",		/* (0x72) */
284 	"rsrc_rcfix",		/* (0x73) */
285 	NULL,			/* (0x74) */
286 	NULL,			/* (0x75) */
287 	NULL,			/* (0x76) */
288 	NULL,			/* (0x77) */
289 	"shel_read",		/* (0x78) */
290 	"shel_write",		/* (0x79) */
291 	"shel_get",		/* (0x7A) */
292 	"shel_put",		/* (0x7B) */
293 	"shel_find",		/* (0x7C) */
294 	"shel_envrn",		/* (0x7D) */
295 	NULL,			/* (0x7e) */
296 	NULL,			/* (0x7f) */
297 	NULL,			/* (0x80) */
298 	NULL,			/* (0x81) */
299 	"appl_getinfo"		/* (0x82) */
300 };
301 
302 /**
303  * Map AES call opcode to an AES function name
304  */
AES_Opcode2Name(Uint16 opcode)305 static const char* AES_Opcode2Name(Uint16 opcode)
306 {
307 	int code = opcode - 10;
308 	if (code >= 0 && code < ARRAY_SIZE(AESName_10) && AESName_10[code])
309 		return AESName_10[code];
310 	else
311 		return "???";
312 }
313 
314 /**
315  * Output AES call info, including some of args
316  */
AES_OpcodeInfo(FILE * fp,Uint16 opcode)317 static void AES_OpcodeInfo(FILE *fp, Uint16 opcode)
318 {
319 	int code = opcode - 10;
320 	fprintf(fp, "AES call %3hd ", opcode);
321 	if (code >= 0 && code < ARRAY_SIZE(AESName_10) && AESName_10[code])
322 	{
323 		bool first = true;
324 		int i, items;
325 
326 		fprintf(fp, "%s(", AESName_10[code]);
327 
328 		items = 0;
329 		/* there are so few of these that linear search is fine */
330 		for (i = 0; i < ARRAY_SIZE(AESStrings); i++)
331 		{
332 			/* something that can be shown? */
333 			if (AESStrings[i].code == opcode)
334 			{
335 				items = AESStrings[i].count;
336 				break;
337 			}
338 		}
339 		/* addrin array size in longs enough for items? */
340 		if (items > 0 && items <= STMemory_ReadWord(AESControl+SIZE_WORD*3))
341 		{
342 			const char *str;
343 			fputs("addrin: ", fp);
344 			for (i = 0; i < items; i++)
345 			{
346 				if (first)
347 					first = false;
348 				else
349 					fputs(", ", fp);
350 				str = (const char *)STMemory_STAddrToPointer(STMemory_ReadLong(AESAddrin+SIZE_LONG*i));
351 				fprintf(fp, "\"%s\"", str);
352 			}
353 		}
354 		/* intin array size in words */
355 		items = STMemory_ReadWord(AESControl+SIZE_WORD*1);
356 		if (items > 0)
357 		{
358 			if (!first)
359 			{
360 				fputs(", ", fp);
361 				first = true;
362 			}
363 			fputs("intin: ", fp);
364 			for (i = 0; i < items; i++)
365 			{
366 				if (first)
367 					first = false;
368 				else
369 					fputs(",", fp);
370 				fprintf(fp, "0x%x", STMemory_ReadWord(AESIntin+SIZE_WORD*i));
371 			}
372 		}
373 		fputs(")\n", fp);
374 	}
375 	else
376 		fputs("???\n", fp);
377 	fflush(fp);
378 }
379 
380 /**
381  * If opcodes argument is set, show AES opcode/function name table,
382  * otherwise AES vectors information.
383  */
AES_Info(FILE * fp,Uint32 bShowOpcodes)384 void AES_Info(FILE *fp, Uint32 bShowOpcodes)
385 {
386 	Uint16 opcode;
387 
388 	if (bShowOpcodes)
389 	{
390 		for (opcode = 10; opcode < 0x86; opcode++)
391 		{
392 			fprintf(fp, "%02x %-16s", opcode, AES_Opcode2Name(opcode));
393 			if ((opcode-9) % 4 == 0) fputs("\n", fp);
394 		}
395 		return;
396 	}
397 	if (!bVdiAesIntercept)
398 	{
399 		fputs("VDI/AES interception isn't enabled!\n", fp);
400 		return;
401 	}
402 	if (!AESControl)
403 	{
404 		fputs("No traced AES calls!\n", fp);
405 		return;
406 	}
407 	opcode = STMemory_ReadWord(AESControl);
408 	if (opcode != AESOpCode)
409 	{
410 		fputs("AES parameter block contents changed since last call!\n", fp);
411 		return;
412 	}
413 
414 	fputs("Latest AES Parameter block:\n", fp);
415 	fprintf(fp, "- Opcode: %3hd (%s)\n",
416 		opcode, AES_Opcode2Name(opcode));
417 
418 	fprintf(fp, "- Control: %#8x\n", AESControl);
419 	fprintf(fp, "- Global:  %#8x, %d bytes\n",
420 		AESGlobal, 2+2+2+4+4+4+4+4+4);
421 	fprintf(fp, "- Intin:   %#8x, %d words\n",
422 		AESIntin, STMemory_ReadWord(AESControl+2*1));
423 	fprintf(fp, "- Intout:  %#8x, %d words\n",
424 		AESIntout, STMemory_ReadWord(AESControl+2*2));
425 	fprintf(fp, "- Addrin:  %#8x, %d longs\n",
426 		AESAddrin, STMemory_ReadWord(AESControl+2*3));
427 	fprintf(fp, "- Addrout: %#8x, %d longs\n",
428 		AESAddrout, STMemory_ReadWord(AESControl+2*4));
429 }
430 
431 
432 /*-----------------------------------------------------------------------*/
433 
434 /**
435  * Map VDI call opcode/sub-opcode to a VDI function name
436  */
VDI_Opcode2Name(Uint16 opcode,Uint16 subcode)437 static const char* VDI_Opcode2Name(Uint16 opcode, Uint16 subcode)
438 {
439 	static const char* names_0[] = {
440 		"???",
441 		"v_opnwk",
442 		"v_clswk",
443 		"v_clrwk",
444 		"v_updwk",
445 		"",		/* 5: lots of sub opcodes */
446 		"v_pline",
447 		"v_pmarker",
448 		"v_gtext",
449 		"v_fillarea",	/* sub-opcode 13: v_bez_fill with GDOS */
450 		"v_cellarray",
451 		"",		/* 11: lots of sub opcodes */
452 		"vst_height",
453 		"vst_rotation",
454 		"vs_color",
455 		"vsl_type",
456 		"vsl_width",
457 		"vsl_color",
458 		"vsm_type",
459 		"vsm_height",
460 		"vsm_color",
461 		"vst_font",
462 		"vst_color",
463 		"vsf_interior",
464 		"vsf_style",
465 		"vsf_color",
466 		"vq_color",
467 		"vq_cellarray",
468 		"vrq/sm_locator",
469 		"vrq/sm_valuator",
470 		"vrq/sm_choice",
471 		"vrq/sm_string",
472 		"vswr_mode",
473 		"vsin_mode",
474 		"???", /* 34 */
475 		"vql_attributes",
476 		"vqm_attributes",
477 		"vqf_attributes",
478 		"vqt_attributes",
479 		"vst_alignment"
480 	};
481 	static const char* names_100[] = {
482 		"v_opnvwk",
483 		"v_clsvwk",
484 		"vq_extnd",
485 		"v_contourfill",
486 		"vsf_perimeter",
487 		"v_get_pixel",
488 		"vst_effects",
489 		"vst_point",
490 		"vsl_ends",
491 		"vro_cpyfm",
492 		"vr_trnfm",
493 		"vsc_form",
494 		"vsf_udpat",
495 		"vsl_udsty",
496 		"vr_recfl",
497 		"vqin_mode",
498 		"vqt_extent",
499 		"vqt_width",
500 		"vex_timv",
501 		"vst_load_fonts",
502 		"vst_unload_fonts",
503 		"vrt_cpyfm",
504 		"v_show_c",
505 		"v_hide_c",
506 		"vq_mouse",
507 		"vex_butv",
508 		"vex_motv",
509 		"vex_curv",
510 		"vq_key_s",
511 		"vs_clip",
512 		"vqt_name",
513 		"vqt_fontinfo"
514 		/* 131-233: no known opcodes
515 		 * 234-255: (Speedo) GDOS opcodes
516 		 */
517 	};
518 	static const char* names_opcode5[] = {
519 		"<no subcode>",
520 		"vq_chcells",
521 		"v_exit_cur",
522 		"v_enter_cur",
523 		"v_curup",
524 		"v_curdown",
525 		"v_curright",
526 		"v_curleft",
527 		"v_curhome",
528 		"v_eeos",
529 		"v_eeol",
530 		"vs_curaddress",
531 		"v_curtext",
532 		"v_rvon",
533 		"v_rvoff",
534 		"vq_curaddress",
535 		"vq_tabstatus",
536 		"v_hardcopy",
537 		"v_dspcur",
538 		"v_rmcur",
539 		"v_form_adv",
540 		"v_output_window",
541 		"v_clear_disp_list",
542 		"v_bit_image",
543 		"vq_scan",
544 		"v_alpha_text"
545 	};
546 	static const char* names_opcode5_98[] = {
547 		"v_meta_extents",
548 		"v_write_meta",
549 		"vm_filename",
550 		"???",
551 		"v_fontinit"
552 	};
553 	static const char* names_opcode11[] = {
554 		"<no subcode>",
555 		"v_bar",
556 		"v_arc",
557 		"v_pieslice",
558 		"v_circle",
559 		"v_ellipse",
560 		"v_ellarc",
561 		"v_ellpie",
562 		"v_rbox",
563 		"v_rfbox",
564 		"v_justified"
565 	};
566 
567 	if (opcode == 5)
568 	{
569 		if (subcode < ARRAY_SIZE(names_opcode5)) {
570 			return names_opcode5[subcode];
571 		}
572 		if (subcode >= 98) {
573 			subcode -= 98;
574 			if (subcode < ARRAY_SIZE(names_opcode5_98)) {
575 				return names_opcode5_98[subcode];
576 			}
577 		}
578 	}
579 	else if (opcode == 11)
580 	{
581 		if (subcode < ARRAY_SIZE(names_opcode11)) {
582 			return names_opcode11[subcode];
583 		}
584 	}
585 	else if (opcode < ARRAY_SIZE(names_0))
586 	{
587 		return names_0[opcode];
588 	}
589 	else if (opcode >= 100)
590 	{
591 		opcode -= 100;
592 		if (opcode < ARRAY_SIZE(names_100))
593 		{
594 			return names_100[opcode];
595 		}
596 	}
597 	return "GDOS?";
598 }
599 
600 /**
601  * If opcodes argument is set, show VDI opcode/function name table,
602  * otherwise VDI vectors information.
603  */
VDI_Info(FILE * fp,Uint32 bShowOpcodes)604 void VDI_Info(FILE *fp, Uint32 bShowOpcodes)
605 {
606 	Uint16 opcode, subcode;
607 
608 	if (bShowOpcodes)
609 	{
610 		Uint16 opcode;
611 		for (opcode = 0; opcode < 0x84; )
612 		{
613 			if (opcode == 0x28)
614 			{
615 				fputs("--- GDOS calls? ---\n", fp);
616 				opcode = 0x64;
617 			}
618 			fprintf(fp, "%02x %-16s",
619 				opcode, VDI_Opcode2Name(opcode, 0));
620 			if (++opcode % 4 == 0) fputs("\n", fp);
621 		}
622 		return;
623 	}
624 	if (!bVdiAesIntercept)
625 	{
626 		fputs("VDI/AES interception isn't enabled!\n", fp);
627 		return;
628 	}
629 	if (!VDIControl)
630 	{
631 		fputs("No traced VDI calls!\n", fp);
632 		return;
633 	}
634 	opcode = STMemory_ReadWord(VDIControl);
635 	if (opcode != VDIOpCode)
636 	{
637 		fputs("VDI parameter block contents changed since last call!\n", fp);
638 		return;
639 	}
640 
641 	fputs("Latest VDI Parameter block:\n", fp);
642 	subcode = STMemory_ReadWord(VDIControl+2*5);
643 	fprintf(fp, "- Opcode/Subcode: %hd/%hd (%s)\n",
644 		opcode, subcode, VDI_Opcode2Name(opcode, subcode));
645 	fprintf(fp, "- Device handle: %d\n",
646 		STMemory_ReadWord(VDIControl+2*6));
647 	fprintf(fp, "- Control: %#8x\n", VDIControl);
648 	fprintf(fp, "- Ptsin:   %#8x, %d co-ordinate word pairs\n",
649 		VDIPtsin, STMemory_ReadWord(VDIControl+2*1));
650 	fprintf(fp, "- Ptsout:  %#8x, %d co-ordinate word pairs\n",
651 		VDIPtsout, STMemory_ReadWord(VDIControl+2*2));
652 	fprintf(fp, "- Intin:   %#8x, %d words\n",
653 		VDIIntin, STMemory_ReadWord(VDIControl+2*3));
654 	fprintf(fp, "- Intout:  %#8x, %d words\n",
655 		VDIIntout, STMemory_ReadWord(VDIControl+2*4));
656 }
657 
658 #else /* !ENABLE_TRACING */
AES_Info(FILE * fp,Uint32 bShowOpcodes)659 void AES_Info(FILE *fp, Uint32 bShowOpcodes)
660 {
661 	fputs("Hatari isn't configured with ENABLE_TRACING\n", fp);
662 }
VDI_Info(FILE * fp,Uint32 bShowOpcodes)663 void VDI_Info(FILE *fp, Uint32 bShowOpcodes)
664 {
665 	fputs("Hatari isn't configured with ENABLE_TRACING\n", fp);
666 }
667 #endif /* !ENABLE_TRACING */
668 
669 
670 /*-----------------------------------------------------------------------*/
671 /**
672  * Return true for only VDI opcodes that need to be handled at Trap exit.
673  */
VDI_isWorkstationOpen(Uint16 opcode)674 static inline bool VDI_isWorkstationOpen(Uint16 opcode)
675 {
676 	if (opcode == 1 || opcode == 100)
677 		return true;
678 	else
679 		return false;
680 }
681 
682 /**
683  * Check whether this is VDI/AES call and see if we need to re-direct
684  * it to our own routines. Return true if VDI_Complete() function
685  * needs to be called on OS call exit, otherwise return false.
686  *
687  * We enter here with Trap #2, so D0 tells which OS call it is (VDI/AES)
688  * and D1 is pointer to VDI/AES vectors, i.e. Control, Intin, Ptsin etc...
689  */
VDI_AES_Entry(void)690 bool VDI_AES_Entry(void)
691 {
692 	Uint16 call = Regs[REG_D0];
693 	Uint32 TablePtr = Regs[REG_D1];
694 
695 #if ENABLE_TRACING
696 	/* AES call? */
697 	if (call == 0xC8)
698 	{
699 		if ( !STMemory_CheckAreaType ( TablePtr, 24, ABFLAG_RAM ) )
700 		{
701 			Log_Printf(LOG_WARN, "AES call failed due to invalid parameter block address 0x%x+%i\n", TablePtr, 24);
702 			return false;
703 		}
704 		/* store values for debugger "info aes" command */
705 		AESControl = STMemory_ReadLong(TablePtr);
706 		AESGlobal  = STMemory_ReadLong(TablePtr+4);
707 		AESIntin   = STMemory_ReadLong(TablePtr+8);
708 		AESIntout  = STMemory_ReadLong(TablePtr+12);
709 		AESAddrin  = STMemory_ReadLong(TablePtr+16);
710 		AESAddrout = STMemory_ReadLong(TablePtr+20);
711 		AESOpCode  = STMemory_ReadWord(AESControl);
712 		if (LOG_TRACE_LEVEL(TRACE_OS_AES))
713 		{
714 			AES_OpcodeInfo(TraceFile, AESOpCode);
715 		}
716 		/* using same special opcode trick doesn't work for
717 		 * both VDI & AES as AES functions can be called
718 		 * recursively and VDI calls happen inside AES calls.
719 		 */
720 		return false;
721 	}
722 #endif
723 
724 	/* VDI call? */
725 	if (call == 0x73)
726 	{
727 		if ( !STMemory_CheckAreaType ( TablePtr, 20, ABFLAG_RAM ) )
728 		{
729 			Log_Printf(LOG_WARN, "VDI call failed due to invalid parameter block address 0x%x+%i\n", TablePtr, 20);
730 			return false;
731 		}
732 		/* store values for extended VDI resolution handling
733 		 * and debugger "info vdi" command
734 		 */
735 		VDIControl = STMemory_ReadLong(TablePtr);
736 		VDIIntin   = STMemory_ReadLong(TablePtr+4);
737 		VDIPtsin   = STMemory_ReadLong(TablePtr+8);
738 		VDIIntout  = STMemory_ReadLong(TablePtr+12);
739 		VDIPtsout  = STMemory_ReadLong(TablePtr+16);
740 		VDIOpCode  = STMemory_ReadWord(VDIControl);
741 #if ENABLE_TRACING
742 		{
743 		Uint16 subcode = STMemory_ReadWord(VDIControl+2*5);
744 		LOG_TRACE(TRACE_OS_VDI, "VDI call %3hd/%3hd (%s)\n",
745 			  VDIOpCode, subcode,
746 			  VDI_Opcode2Name(VDIOpCode, subcode));
747 		}
748 #endif
749 		/* Only workstation open needs to be handled at trap return */
750 		return bUseVDIRes && VDI_isWorkstationOpen(VDIOpCode);
751 	}
752 
753 	LOG_TRACE((TRACE_OS_VDI|TRACE_OS_AES), "Trap #2 with D0 = 0x%hX\n", call);
754 	return false;
755 }
756 
757 
758 /*-----------------------------------------------------------------------*/
759 /**
760  * Modify Line-A structure for our VDI resolutions
761  */
VDI_LineA(Uint32 linea,Uint32 fontbase)762 void VDI_LineA(Uint32 linea, Uint32 fontbase)
763 {
764 	Uint32 fontadr, font1, font2;
765 
766 	LineABase = linea;
767 	FontBase = fontbase;
768 
769 	if (bUseVDIRes)
770 	{
771 		int cel_ht, cel_wd;
772 
773 		fontadr = STMemory_ReadLong(linea-0x1cc); /* def_font */
774 		if (fontadr == 0)
775 		{
776 			/* get 8x8 font header */
777 			font1 = STMemory_ReadLong(fontbase + 4);
778 			/* get 8x16 font header */
779 			font2 = STMemory_ReadLong(fontbase + 8);
780 			/* remove DEFAULT flag from 8x8 font */
781 			STMemory_WriteWord(font1 + 66, STMemory_ReadWord(font1 + 66) & ~0x01);
782 			/* remove DEFAULT flag from 8x16 font */
783 			STMemory_WriteWord(font2 + 66, STMemory_ReadWord(font2 + 66) & ~0x01);
784 			/* choose new font */
785 			if (VDIHeight >= 400)
786 			{
787 				fontadr = font2;
788 			} else
789 			{
790 				fontadr = font1;
791 			}
792 			/* make this new default font */
793 			STMemory_WriteLong(linea-0x1cc, fontadr);
794 			/* set DEFAULT flag for chosen font */
795 			STMemory_WriteWord(fontadr + 66, STMemory_ReadWord(fontadr + 66) | 0x01);
796 		}
797 		cel_wd = STMemory_ReadWord(fontadr + 52);
798 		cel_ht = STMemory_ReadWord(fontadr + 82);
799 		if (cel_wd <= 0)
800 		{
801 			Log_Printf(LOG_WARN, "VDI Line-A init failed due to bad cell width!\n");
802 			return;
803 		}
804 		if (cel_ht <= 0)
805 		{
806 			Log_Printf(LOG_WARN, "VDI Line-A init failed due to bad cell height!\n");
807 			return;
808 		}
809 
810 		STMemory_WriteWord(linea-46, cel_ht);                 /* v_cel_ht */
811 		STMemory_WriteWord(linea-44, (VDIWidth/cel_wd)-1);    /* v_cel_mx (cols-1) */
812 		STMemory_WriteWord(linea-42, (VDIHeight/cel_ht)-1);   /* v_cel_my (rows-1) */
813 		STMemory_WriteWord(linea-40, cel_ht*((VDIWidth*VDIPlanes)/8));  /* v_cel_wr */
814 
815 		STMemory_WriteLong(linea-22, STMemory_ReadLong(fontadr + 76)); /* v_fnt_ad */
816 		STMemory_WriteWord(linea-18, STMemory_ReadWord(fontadr + 38)); /* v_fnt_nd */
817 		STMemory_WriteWord(linea-16, STMemory_ReadWord(fontadr + 36)); /* v_fnt_st */
818 		STMemory_WriteWord(linea-14, STMemory_ReadWord(fontadr + 80)); /* v_fnt_wd */
819 		STMemory_WriteWord(linea-12, VDIWidth);               /* v_rez_hz */
820 		STMemory_WriteLong(linea-10, STMemory_ReadLong(fontadr + 72)); /* v_off_ad */
821 		STMemory_WriteWord(linea-4, VDIHeight);               /* v_rez_vt */
822 		STMemory_WriteWord(linea-2, (VDIWidth*VDIPlanes)/8);  /* bytes_lin */
823 		STMemory_WriteWord(linea+0, VDIPlanes);               /* planes */
824 		STMemory_WriteWord(linea+2, (VDIWidth*VDIPlanes)/8);  /* width */
825 	}
826 }
827 
828 
829 /*-----------------------------------------------------------------------*/
830 /**
831  * This is called on completion of a VDI Trap workstation open,
832  * to modify the return structure for extended resolutions.
833  */
VDI_Complete(void)834 void VDI_Complete(void)
835 {
836 	/* right opcode? */
837 	assert(VDI_isWorkstationOpen(VDIOpCode));
838 	/* not changed between entry and completion? */
839 	assert(VDIOpCode == STMemory_ReadWord(VDIControl));
840 
841 	STMemory_WriteWord(VDIIntout, VDIWidth-1);           /* IntOut[0] Width-1 */
842 	STMemory_WriteWord(VDIIntout+1*2, VDIHeight-1);      /* IntOut[1] Height-1 */
843 	STMemory_WriteWord(VDIIntout+13*2, 1 << VDIPlanes);  /* IntOut[13] #colors */
844 	STMemory_WriteWord(VDIIntout+39*2, 512);             /* IntOut[39] #available colors */
845 
846 	STMemory_WriteWord(LineABase-0x15a*2, VDIWidth-1);   /* WKXRez */
847 	STMemory_WriteWord(LineABase-0x159*2, VDIHeight-1);  /* WKYRez */
848 
849 	VDI_LineA(LineABase, FontBase);  /* And modify Line-A structure accordingly */
850 }
851