FreeBASIC  0.91.0
gfx_dos.c
Go to the documentation of this file.
1 /* list of dos gfx drivers and common code */
2 
3 #include "../fb_gfx.h"
4 #include "fb_gfx_dos.h"
5 #include <pc.h>
6 #include <sys/nearptr.h>
7 
8 /* timer ticks per second */
9 #define TIMER_HZ 1000
10 
11 /* driver list */
17 
24  NULL
25 };
26 
28 
29 volatile int __fb_dos_junk;
30 volatile int __fb_dos_update_ticks = 0;
32 
33 static void fb_dos_save_video_mode(void);
34 static void fb_dos_restore_video_mode(void);
35 
36 static __dpmi_regs fb_dos_mouse_regs;
37 static __dpmi_raddr fb_dos_mouse_isr_rmcb;
38 
39 /* layout from gfx_mouse.s */
40 extern void fb_dos_mouse_isr_start(void);
41 extern short fb_dos_mouse_x;
42 extern short fb_dos_mouse_y;
43 extern short fb_dos_mouse_z;
44 extern char fb_dos_mouse_buttons;
45 extern void fb_dos_mouse_isr(void);
46 extern void fb_dos_mouse_isr_end(void);
47 
48 /* from gfx_softcursor.c */
49 extern char fb_hSoftCursor_data_start;
50 extern char fb_hSoftCursor_data_end;
51 extern void fb_hSoftCursor_code_start(void);
52 extern void fb_hSoftCursor_code_end(void);
53 
54 /* from gfx_blitter.c */
55 extern void fb_hBlit_code_start(void);
56 extern void fb_hBlit_code_end(void);
57 
58 /* from gfx_blitter_mmx.s */
59 extern char fb_hBlitMMX_data_start;
60 extern char fb_hBlitMMX_data_end;
61 extern void fb_hBlitMMX_code_start(void);
62 extern void fb_hBlitMMX_code_end(void);
63 
64 /* from gfx_mmx.s */
65 extern void fb_MMX_code_start(void);
66 extern void fb_MMX_code_end(void);
67 
68 /* from gfx_core.c */
69 void fb_hPostEvent_code_start(void);
70 void fb_hPostEvent_code_end(void);
71 
72 static const unsigned char kb_scan_to_ascii[128][3] = {
73  /*
74  normal
75  +shift
76  +ctrl
77  */
78  { 0, 0, 0},
79  { 27, 27, 0}, /* esc */
80  { 49, 33, 0}, /* 1 ! */
81  { 50, 64, 0}, /* 2 @ */
82  { 51, 35, 0}, /* 3 # */
83  { 52, 36, 0}, /* 4 $ */
84  { 53, 37, 0}, /* 5 % */
85  { 54, 94, 0}, /* 6 ^ */
86  { 55, 38, 0}, /* 7 & */
87  { 56, 42, 0}, /* 8 * */
88  { 57, 40, 0}, /* 9 ( */
89  { 48, 41, 0}, /* 0 ) */
90  { 45, 95, 0}, /* - _ */
91  { 61, 43, 0}, /* = + */
92  { 8, 8, 127}, /* backspace */
93  { 9, 9, 0}, /* tab */
94  { 113, 81, 17}, /* q Q */
95  { 119, 87, 23}, /* w W */
96  { 101, 69, 5}, /* e E */
97  { 114, 82, 18}, /* r R */
98  { 116, 84, 20}, /* t T */
99  { 121, 89, 25}, /* y Y */
100  { 117, 85, 21}, /* u U */
101  { 105, 73, 9}, /* i I */
102  { 111, 79, 15}, /* o O */
103  { 112, 80, 16}, /* p P */
104  { 91, 123, 27}, /* [ { */
105  { 93, 125, 29}, /* ] } */
106  { 13, 13, 10}, /* enter */
107  { 0, 0, 0}, /* ctrl */
108  { 97, 65, 1}, /* a A */
109  { 115, 83, 0}, /* s S */
110  { 100, 68, 4}, /* d D */
111  { 102, 70, 6}, /* f F */
112  { 103, 71, 7}, /* g G */
113  { 104, 72, 8}, /* h H */
114  { 106, 74, 10}, /* j J */
115  { 107, 75, 11}, /* k K */
116  { 108, 76, 12}, /* l L */
117  { 59, 58, 0}, /* ; : */
118  { 39, 34, 0}, /* ' " */
119  { 96, 126, 0}, /* ` ~ */
120  { 0, 0, 0}, /* lshift */
121  { 92, 124, 28}, /* \ | */
122  { 122, 90, 26}, /* z Z */
123  { 120, 88, 24}, /* x X */
124  { 99, 67, 0}, /* c C */
125  { 118, 86, 22}, /* v V */
126  { 98, 66, 2}, /* b B */
127  { 110, 78, 14}, /* n N */
128  { 109, 77, 13}, /* m M */
129  { 44, 60, 0}, /* , < */
130  { 46, 62, 0}, /* . > */
131  { 47, 63, 0}, /* / ? */
132  { 0, 0, 0}, /* rshift */
133  { 42, 42, 0}, /* numpad * */
134  { 0, 0, 0}, /* alt */
135  { 32, 32, 32}, /* space */
136  { 0, 0, 0}, /* caps lock */
137  { 0, 0, 0}, /* F1 */
138  { 0, 0, 0}, /* F2 */
139  { 0, 0, 0}, /* F3 */
140  { 0, 0, 0}, /* F4 */
141  { 0, 0, 0}, /* F5 */
142  { 0, 0, 0}, /* F6 */
143  { 0, 0, 0}, /* F7 */
144  { 0, 0, 0}, /* F8 */
145  { 0, 0, 0}, /* F9 */
146  { 0, 0, 0}, /* F10 */
147  { 0, 0, 0}, /* num lock */
148  { 0, 0, 0}, /* scroll lock */
149  { 0, 0, 0}, /* home */
150  { 0, 0, 0}, /* up */
151  { 0, 0, 0}, /* page up */
152  { 45, 45, 0}, /* numpad - */
153  { 0, 0, 0}, /* left */
154  { 0, 0, 0}, /* numpad 5 */
155  { 0, 0, 0}, /* right */
156  { 43, 43, 0}, /* numpad + */
157  { 0, 0, 0}, /* end */
158  { 0, 0, 0}, /* down */
159  { 0, 0, 0}, /* page down */
160  { 0, 0, 0}, /* insert */
161  { 0, 0, 0}, /* delete */
162  { 0, 0, 0}, /* F11 */
163  { 0, 0, 0} /* F12 */
164 };
165 
166 static const char kb_numpad_to_ascii[13] = {
167  '7', '8', '9', 0,
168  '4', '5', '6', '+',
169  '1', '2', '3',
170  '0', '.'
171 };
172 
173 static void fb_dos_multikey_hook(int scancode, int flags);
174 
175 static void fb_dos_kb_init(void)
176 {
182 
184  (void)fb_ConsoleMultikey(0); /* ensure the ISR is installed */
185 
186  return;
187 }
188 
189 static void fb_dos_kb_exit(void)
190 {
192 }
193 
194 static void fb_dos_multikey_hook(int scancode, int flags)
195 {
196  EVENT e;
197 
198  e.type = (flags & KB_PRESS) ? ((flags & KB_REPEAT) ? EVENT_KEY_REPEAT : EVENT_KEY_PRESS) : EVENT_KEY_RELEASE;
199 
200  e.scancode = scancode;
201 
202  if ( flags & KB_ALT )
203  {
204  e.ascii = 0;
205  }
206  else
207  {
208  e.ascii = kb_scan_to_ascii[scancode][(flags & KB_CTRL) ? 2 : (flags & KB_SHIFT) ? 1 : 0];
209 
210  if ( flags & KB_CAPSLOCK )
211  {
212  if ( (flags & KB_SHIFT) && ((e.ascii >= 'A') && (e.ascii <= 'Z') ) )
213  e.ascii += ('a' - 'A');
214  else if ( ((e.ascii >= 'a') && (e.ascii <= 'z') ) )
215  e.ascii -= ('a' - 'A');
216  }
217 
218  if ( (flags & KB_NUMLOCK) && !(flags & (KB_EXTENDED | KB_CTRL)) && !(flags & (KB_SHIFT )) )
219  {
220  if ( scancode >= 71 && scancode <= 83 )
221  {
222  e.ascii = kb_numpad_to_ascii[scancode - 71];
223  }
224  }
225  }
226 
227  fb_hPostEvent(&e);
228 }
229 
230 static int fb_dos_mouse_init(void)
231 {
232  if (!fb_dos.mouse_ok) return -1; /* set in fb_dos_detect(); return success if no mouse */
233 
234  /* set horizontal range */
235  fb_dos.regs.x.ax = 0x7;
236  fb_dos.regs.x.cx = 0;
237  fb_dos.regs.x.dx = fb_dos.w - 1;
238  __dpmi_int(0x33, &fb_dos.regs);
239 
240  /* set vertical range */
241  fb_dos.regs.x.ax = 0x8;
242  fb_dos.regs.x.cx = 0;
243  fb_dos.regs.x.dx = fb_dos.h - 1;
244  __dpmi_int(0x33, &fb_dos.regs);
245 
246  /* ensure that the mouse isn't drawn by the mouse driver */
247  fb_dos.regs.x.ax = 0x2;
248  __dpmi_int(0x33, &fb_dos.regs);
249 
251 
252  /* allocate real-mode callback */
253  if (__dpmi_allocate_real_mode_callback(fb_dos_mouse_isr, &fb_dos_mouse_regs, &fb_dos_mouse_isr_rmcb))
254  return 0; /* failure */
255 
256  /* set user interrupt routine */
257  fb_dos.regs.x.ax = 0x0C;
258  fb_dos.regs.x.cx = 0xFF;
259  fb_dos.regs.x.es = fb_dos_mouse_isr_rmcb.segment;
260  fb_dos.regs.x.dx = fb_dos_mouse_isr_rmcb.offset16;
261  __dpmi_int(0x33, &fb_dos.regs);
262 
263  return -1; /* success */
264 }
265 
266 int fb_dos_get_mouse(int *x, int *y, int *z, int *buttons, int *clip)
267 {
268  if (!fb_dos.mouse_ok) return -1;
269 
270  *x = fb_dos_mouse_x;
271  *y = fb_dos_mouse_y;
272  *z = (fb_dos.mouse_wheel_ok ? fb_dos_mouse_z : 0);
273  *buttons = fb_dos_mouse_buttons;
274  *clip = fb_dos.mouse_clip;
275 
276  return 0;
277 }
278 
279 void fb_dos_set_mouse(int x, int y, int cursor, int clip)
280 {
281  int new_x, new_y;
282 
283  if (!fb_dos.mouse_ok) return;
284 
285  new_x = ((x >= 0) ? x : fb_dos_mouse_x);
286  new_y = ((y >= 0) ? y : fb_dos_mouse_y);
287  fb_dos.mouse_cursor = cursor;
288 
289  fb_dos.regs.x.ax = 0x4;
290  fb_dos.regs.x.cx = new_x;
291  fb_dos.regs.x.dx = new_y;
292  __dpmi_int(0x33, &fb_dos.regs);
293 
294  fb_dos_mouse_x = new_x;
295  fb_dos_mouse_y = new_y;
296 
297  if (clip == 0)
298  fb_dos.mouse_clip = FALSE;
299  else if (clip > 0)
300  fb_dos.mouse_clip = TRUE;
301 }
302 
303 static void fb_dos_mouse_exit(void)
304 {
305  if (!fb_dos.mouse_ok) return;
306 
308 
309  fb_dos.regs.x.ax = 0x0;
310  __dpmi_int(0x33, &fb_dos.regs);
311 
312  __dpmi_free_real_mode_callback(&fb_dos_mouse_isr_rmcb);
313 }
314 
315 static int fb_dos_timer_handler(unsigned irq)
316 {
317  int do_abort;
318  int mouse_x = 0, mouse_y = 0;
319  int buttons;
320  EVENT e;
321 
322  fb_dos.timer_ticks += fb_dos.timer_step;
323  if( (do_abort = fb_dos.timer_ticks < 65536)==FALSE )
324  fb_dos.timer_ticks -= 65536;
325 
327 
329  return do_abort;
330 
331  fb_dos.in_interrupt = TRUE;
332 
334 
335 #if 0 /* Set to 1 if you want to debug a display driver */
336  outportb(0x20, 0x20);
337  fb_dos_sti();
338 #endif
339 
340  if( fb_dos.depth <= 8 && fb_dos.set_palette && fb_dos.pal_dirty )
341  {
342  fb_dos.set_palette( );
343  if( fb_dos.mouse_ok ) fb_hSoftCursorPaletteChanged( );
344  }
345 
346  mouse_x = fb_dos_mouse_x;
348  if ( fb_dos.mouse_ok && fb_dos.mouse_cursor ) {
349  fb_hSoftCursorPut(mouse_x, mouse_y);
350  }
351 
352  fb_dos.update();
353  fb_hMemSet(__fb_gfx->dirty, FALSE, fb_dos.h);
354 
355  if ( fb_dos.mouse_ok && fb_dos.mouse_cursor ) {
356  fb_hSoftCursorUnput(mouse_x, mouse_y);
357  }
358 
359  e.type = 0;
360 
361  if ( fb_dos.mouse_ok ) {
362 
363  if ( (fb_dos.mouse_x_old != mouse_x) || (fb_dos.mouse_y_old != mouse_y) ) {
365  e.x = mouse_x;
366  e.y = mouse_y;
367  e.dx = mouse_x - fb_dos.mouse_x_old;
368  e.dy = mouse_y - fb_dos.mouse_y_old;
369  fb_hPostEvent(&e);
370  }
371 
372  if ( fb_dos.mouse_z_old != fb_dos_mouse_z ) {
374  e.z = fb_dos_mouse_z;
375  fb_hPostEvent(&e);
376  }
377 
378  if (fb_dos_mouse_buttons != fb_dos.mouse_buttons_old) {
379  buttons = (fb_dos_mouse_buttons ^ fb_dos.mouse_buttons_old) & 0x7;
380  for (e.button = 0x4; e.button; e.button >>= 1) {
381  if (buttons & e.button) {
384  else
386  fb_hPostEvent(&e);
387  }
388  }
389  }
390 
391  fb_dos.mouse_x_old = mouse_x;
392  fb_dos.mouse_y_old = mouse_y;
393  fb_dos.mouse_z_old = fb_dos_mouse_z;
395  }
396 
397  fb_dos.in_interrupt = FALSE;
398 
399  return do_abort;
400 }
401 static void end_fb_dos_timer_handler(void) { /* do not remove */ }
402 
403 static int fb_dos_timer_set_rate(int rate)
404 {
405  int i;
406  while (rate > 65536 )
407  rate /= 2;
408  for (i = 0; i != 4; ++i) {
409  outportb(0x43, 0x34);
410  outportb(0x40, rate & 0xff);
411  outportb(0x40, rate >> 8);
412  }
413  return rate;
414 }
415 
416 static int fb_dos_timer_set_freq(int freq)
417 {
418  return fb_dos_timer_set_rate( 1193181 / freq );
419 }
420 
421 static int fb_dos_timer_init(int freq)
422 {
423  fb_dos.timer_ticks = 0;
424  fb_dos.timer_step = fb_dos_timer_set_freq( freq );
425  return fb_isr_set( 0, fb_dos_timer_handler, 0, 16384 );
426 }
427 
428 static void fb_dos_timer_exit(void)
429 {
430  fb_dos_cli();
431  fb_isr_reset( 0 );
433  fb_dos_sti();
434 }
435 
436 void fb_dos_set_palette(int idx, int r, int g, int b)
437 {
438  fb_dos.pal_dirty = TRUE;
439  fb_dos.pal[idx].r = r >> 2;
440  fb_dos.pal[idx].g = g >> 2;
441  fb_dos.pal[idx].b = b >> 2;
442  fb_dos.pal_first = MIN(fb_dos.pal_first, idx);
443  fb_dos.pal_last = MAX(fb_dos.pal_last, idx);
444 }
445 
447 {
448  int i, color_count;
449 
450  color_count = MIN( (1 << fb_dos.depth), fb_dos.pal_last + 1 );
451 
452  outportb(0x3C8, fb_dos.pal_first);
453  for (i = fb_dos.pal_first; i < color_count; i++) {
454  outportb(0x3C9, fb_dos.pal[i].r);
455  outportb(0x3C9, fb_dos.pal[i].g);
456  outportb(0x3C9, fb_dos.pal[i].b);
457  }
458 
459  fb_dos.pal_dirty = FALSE;
460  fb_dos.pal_last = 0;
461  fb_dos.pal_first = 256;
462 }
463 
465 {
466  while ((inportb(0x3DA) & 8) != 0);
467  while ((inportb(0x3DA) & 8) == 0);
468 }
469 
470 void fb_dos_detect(void)
471 {
472  if (!fb_dos.detected) {
473  fb_dos.detected = TRUE;
474 
475  /* detect mouse */
476  fb_dos.regs.x.ax = 0x0;
477  __dpmi_int(0x33, &fb_dos.regs);
478  fb_dos.mouse_ok = (fb_dos.regs.x.ax == 0) ? FALSE : TRUE;
479 
480  fb_dos.regs.x.ax = 0x11;
481  __dpmi_int(0x33, &fb_dos.regs);
482  fb_dos.mouse_wheel_ok = ((fb_dos.regs.x.ax == 0x574D) && (fb_dos.regs.x.cx & 1)) ? TRUE : FALSE;
483 
484  /* detect nearptr */
485  fb_dos.nearptr_ok = __djgpp_nearptr_enable();
486  if ( fb_dos.nearptr_ok )
487  __djgpp_nearptr_disable();
488  }
489 
490  /* save current video mode */
492 }
493 
494 int fb_dos_init(char *title, int w, int h, int depth, int refresh_rate, int flags)
495 {
496  int i;
497 
498  fb_dos.inited = TRUE;
499 
500  /* lock code and data accessed in int handlers */
501 
503  lock_var(*__fb_gfx);
504  fb_dos_lock_data(__fb_gfx->page, sizeof(unsigned char *) * __fb_gfx->num_pages);
505  for (i = 0; i < __fb_gfx->num_pages; i++)
508  fb_dos_lock_data(__fb_gfx->device_palette, sizeof(int) * 256);
509  fb_dos_lock_data(__fb_gfx->palette, sizeof(int) * 256);
510  fb_dos_lock_data(__fb_color_conv_16to32, sizeof(int) * 512);
511  lock_var(fb_dos);
512  fb_dos_lock_data(&__fb_gfx->key, 128);
515  fb_dos_lock_code(fb_dos.update, fb_dos.update_len);
523  lock_code(fb_MMX_code_start, fb_MMX_code_end); /* hMemCpyMMX, hMemSetMMX */
526  fb_dos_lock_code(memcpy, 1024); /* we don't know how big memcpy and memset are, */
527  fb_dos_lock_code(memset, 1024); /* so we guess 1k each... */
535 
536  fb_dos.w = w;
537  fb_dos.h = h;
538  fb_dos.depth = depth;
539  fb_dos.Bpp = (depth + 7) / 8;
540  __fb_gfx->refresh_rate = fb_dos.refresh = refresh_rate;
541 
542  fb_dos_kb_init();
543 
544  if (!fb_dos_mouse_init())
545  return -1;
546 
547  if (fb_dos.mouse_ok)
548  {
549  fb_dos_set_mouse(fb_dos.w / 2, fb_dos.h / 2, TRUE, 0);
550  fb_dos.mouse_x_old = fb_dos.w / 2;
551  fb_dos.mouse_y_old = fb_dos.h / 2;
552  }
553  else
554  {
555  fb_dos.mouse_cursor = FALSE;
556  }
557 
558  __fb_dos_ticks_per_update = TIMER_HZ / refresh_rate;
559 
561  return -1;
562 
564 
565  fb_dos.locked = 0;
566 
567  return 0;
568 }
569 
570 void fb_dos_exit(void)
571 {
572  int i;
573 
574  if (!fb_dos.inited) return;
575 
576  fb_dos_cli();
577 
580  fb_dos_kb_exit();
581 
582  fb_dos_sti();
583 
584  fb_dos.w = fb_dos.h = fb_dos.depth = fb_dos.refresh = 0;
585 
586  if( fb_dos.palbuf_sel )
587  {
588  __dpmi_free_dos_memory( fb_dos.palbuf_sel );
589  fb_dos.palbuf_sel = fb_dos.palbuf_seg = 0;
590  }
591 
592  /* unlock code and data */
593 
596  fb_dos_unlock_data(__fb_gfx->page, sizeof(unsigned char *) * __fb_gfx->num_pages);
597  for (i = 0; i < __fb_gfx->num_pages; i++)
600  fb_dos_unlock_data(__fb_gfx->device_palette, sizeof(int) * 256);
601  fb_dos_unlock_data(__fb_gfx->palette, sizeof(int) * 256);
602  fb_dos_unlock_data(__fb_color_conv_16to32, sizeof(int) * 512);
603  unlock_var(fb_dos);
607  fb_dos_unlock_code(fb_dos.update, fb_dos.update_len);
618  fb_dos_unlock_code(memcpy, 1024); /* we don't know how big memcpy and memset are, */
619  fb_dos_unlock_code(memset, 1024); /* so we guess 1k each... */
627 
629 
630  fb_dos.inited = FALSE;
631 }
632 
633 void fb_dos_lock(void)
634 {
635  if (!fb_dos.locked)
637  __fb_dos_junk = 0; /* just something to make sure the loop is not optimized away */
638  fb_dos.locked++;
639 }
640 
641 void fb_dos_unlock(void)
642 {
643  fb_dos.locked--;
644 }
645 
646 void fb_dos_set_window_title(char *title)
647 {
648 }
649 
650 static void fb_dos_save_video_mode(void)
651 {
652  int n = fb_ConsoleWidth(0, 0);
653  fb_dos.old_rows = n >> 16;
654  fb_dos.old_cols = n & 0xFFFF;
655 }
656 
657 static void fb_dos_restore_video_mode(void)
658 {
659  if (fb_dos.old_rows == 0)
660  return;
661  fb_ConsoleWidth(fb_dos.old_rows, fb_dos.old_cols);
662  fb_dos.old_rows = fb_dos.old_cols = 0;
663 }
664 
665 void fb_hScreenInfo(ssize_t *width, ssize_t *height, ssize_t *depth, ssize_t *refresh)
666 {
667  *width = fb_dos.w;
668  *height = fb_dos.h;
669  *depth = fb_dos.depth;
670  *refresh = fb_dos.refresh;
671 }
672 
673 ssize_t fb_hGetWindowHandle(void)
674 {
675  return 0;
676 }