1// ============================================================================================
2//
3//  Copyright (C) 2002 Jeroen Janssen
4//
5//  This library is free software; you can redistribute it and/or
6//  modify it under the terms of the GNU Lesser General Public
7//  License as published by the Free Software Foundation; either
8//  version 2 of the License, or (at your option) any later version.
9//
10//  This library is distributed in the hope that it will be useful,
11//  but WITHOUT ANY WARRANTY; without even the implied warranty of
12//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13//  Lesser General Public License for more details.
14//
15//  You should have received a copy of the GNU Lesser General Public
16//  License along with this library; if not, write to the Free Software
17//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18//
19// ============================================================================================
20//
21//  This VBE is part of the VGA Bios specific to the plex86/bochs Emulated VGA card.
22//  You can NOT drive any physical vga card with it.
23//
24// ============================================================================================
25//
26//  This VBE Bios is based on information taken from :
27//   - VESA BIOS EXTENSION (VBE) Core Functions Standard Version 3.0 located at www.vesa.org
28//
29// ============================================================================================
30
31
32// defines available
33
34// disable VESA/VBE2 check in vbe info
35//#define VBE2_NO_VESA_CHECK
36
37
38#include "vbe.h"
39#include "vbetables.h"
40
41// The current OEM Software Revision of this VBE Bios
42#define VBE_OEM_SOFTWARE_REV 0x0002;
43
44extern char vbebios_copyright;
45extern char vbebios_vendor_name;
46extern char vbebios_product_name;
47extern char vbebios_product_revision;
48
49ASM_START
50// FIXME: 'merge' these (c) etc strings with the vgabios.c strings?
51_vbebios_copyright:
52.ascii       "Bochs/Plex86 VBE(C) 2003 http://savannah.nongnu.org/projects/vgabios/"
53.byte        0x00
54
55_vbebios_vendor_name:
56.ascii       "Bochs/Plex86 Developers"
57.byte        0x00
58
59_vbebios_product_name:
60.ascii       "Bochs/Plex86 VBE Adapter"
61.byte        0x00
62
63_vbebios_product_revision:
64.ascii       "$Id$"
65.byte        0x00
66
67_vbebios_info_string:
68.ascii      "Bochs VBE Display Adapter enabled"
69.byte	0x0a,0x0d
70.byte	0x0a,0x0d
71.byte	0x00
72
73_no_vbebios_info_string:
74.ascii      "NO Bochs VBE Support available!"
75.byte	0x0a,0x0d
76.byte	0x0a,0x0d
77.byte 0x00
78
79#if defined(USE_BX_INFO) || defined(DEBUG)
80msg_vbe_init:
81.ascii      "VBE Bios $Id$"
82.byte	0x0a,0x0d, 0x00
83#endif
84
85  .align 2
86vesa_pm_start:
87  dw vesa_pm_set_window - vesa_pm_start
88  dw vesa_pm_set_display_start - vesa_pm_start
89  dw vesa_pm_unimplemented - vesa_pm_start
90  dw vesa_pm_io_ports_table - vesa_pm_start
91vesa_pm_io_ports_table:
92  dw VBE_DISPI_IOPORT_INDEX
93  dw VBE_DISPI_IOPORT_INDEX + 1
94  dw VBE_DISPI_IOPORT_DATA
95  dw VBE_DISPI_IOPORT_DATA + 1
96  dw 0xffff
97  dw 0xffff
98
99  USE32
100vesa_pm_set_window:
101  cmp  bx, #0x00
102  je  vesa_pm_set_display_window1
103  mov  ax, #0x0100
104  ret
105vesa_pm_set_display_window1:
106  mov  ax, dx
107  push dx
108  push ax
109  mov  dx, # VBE_DISPI_IOPORT_INDEX
110  mov  ax, # VBE_DISPI_INDEX_BANK
111  out  dx, ax
112  pop  ax
113  mov  dx, # VBE_DISPI_IOPORT_DATA
114  out  dx, ax
115  in   ax, dx
116  pop  dx
117  cmp  dx, ax
118  jne  illegal_window
119  mov  ax, #0x004f
120  ret
121illegal_window:
122  mov  ax, #0x014f
123  ret
124
125vesa_pm_set_display_start:
126  cmp  bl, #0x80
127  je   vesa_pm_set_display_start1
128  cmp  bl, #0x00
129  je   vesa_pm_set_display_start1
130  mov  ax, #0x0100
131  ret
132vesa_pm_set_display_start1:
133; convert offset to (X, Y) coordinate
134; (would be simpler to change Bochs VBE API...)
135  push eax
136  push ecx
137  push edx
138  push esi
139  push edi
140  shl edx, #16
141  and ecx, #0xffff
142  or ecx, edx
143  shl ecx, #2
144  mov eax, ecx
145
146  push eax
147  mov  dx, # VBE_DISPI_IOPORT_INDEX
148  mov  ax, # VBE_DISPI_INDEX_VIRT_WIDTH
149  out  dx, ax
150  mov  dx, # VBE_DISPI_IOPORT_DATA
151  in   ax, dx
152  movzx ecx, ax
153
154  mov  dx, # VBE_DISPI_IOPORT_INDEX
155  mov  ax, # VBE_DISPI_INDEX_BPP
156  out  dx, ax
157  mov  dx, # VBE_DISPI_IOPORT_DATA
158  in   ax, dx
159  movzx esi, ax
160  pop  eax
161
162  cmp esi, #4
163  jz bpp4_mode
164  add esi, #7
165  shr esi, #3
166  imul ecx, esi
167  xor edx, edx
168  div ecx
169  mov edi, eax
170  mov eax, edx
171  xor edx, edx
172  div esi
173  jmp set_xy_regs
174
175bpp4_mode:
176  shr ecx, #1
177  xor edx, edx
178  div ecx
179  mov edi, eax
180  mov eax, edx
181  shl eax, #1
182
183set_xy_regs:
184  push dx
185  push ax
186  mov  dx, # VBE_DISPI_IOPORT_INDEX
187  mov  ax, # VBE_DISPI_INDEX_X_OFFSET
188  out  dx, ax
189  pop  ax
190  mov  dx, # VBE_DISPI_IOPORT_DATA
191  out  dx, ax
192  pop  dx
193
194  mov  ax, di
195  push dx
196  push ax
197  mov  dx, # VBE_DISPI_IOPORT_INDEX
198  mov  ax, # VBE_DISPI_INDEX_Y_OFFSET
199  out  dx, ax
200  pop  ax
201  mov  dx, # VBE_DISPI_IOPORT_DATA
202  out  dx, ax
203  pop  dx
204
205  pop edi
206  pop esi
207  pop edx
208  pop ecx
209  pop eax
210  mov  ax, #0x004f
211  ret
212
213vesa_pm_unimplemented:
214  mov ax, #0x014f
215  ret
216  USE16
217vesa_pm_end:
218
219; DISPI ioport functions
220
221dispi_get_id:
222  push dx
223  mov  dx, # VBE_DISPI_IOPORT_INDEX
224  mov  ax, # VBE_DISPI_INDEX_ID
225  out  dx, ax
226  mov  dx, # VBE_DISPI_IOPORT_DATA
227  in   ax, dx
228  pop  dx
229  ret
230
231dispi_set_id:
232  push dx
233  push ax
234  mov  dx, # VBE_DISPI_IOPORT_INDEX
235  mov  ax, # VBE_DISPI_INDEX_ID
236  out  dx, ax
237  pop  ax
238  mov  dx, # VBE_DISPI_IOPORT_DATA
239  out  dx, ax
240  pop  dx
241  ret
242ASM_END
243
244static void dispi_set_xres(xres)
245  Bit16u xres;
246{
247ASM_START
248  push bp
249  mov  bp, sp
250  push ax
251  push dx
252
253  mov  dx, # VBE_DISPI_IOPORT_INDEX
254  mov  ax, # VBE_DISPI_INDEX_XRES
255  out  dx, ax
256  mov  dx, # VBE_DISPI_IOPORT_DATA
257  mov  ax, 4[bp] ; xres
258  out  dx, ax
259
260  pop  dx
261  pop  ax
262  pop  bp
263ASM_END
264}
265
266static void dispi_set_yres(yres)
267  Bit16u yres;
268{
269  outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_YRES);
270  outw(VBE_DISPI_IOPORT_DATA,yres);
271}
272
273static void dispi_set_bpp(bpp)
274  Bit16u bpp;
275{
276  outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_BPP);
277  outw(VBE_DISPI_IOPORT_DATA,bpp);
278}
279
280ASM_START
281; AL = bits per pixel / AH = bytes per pixel
282dispi_get_bpp:
283  push dx
284  mov  dx, # VBE_DISPI_IOPORT_INDEX
285  mov  ax, # VBE_DISPI_INDEX_BPP
286  out  dx, ax
287  mov  dx, # VBE_DISPI_IOPORT_DATA
288  in   ax, dx
289  mov  ah, al
290  shr  ah, 3
291  test al, #0x07
292  jz   get_bpp_noinc
293  inc  ah
294get_bpp_noinc:
295  pop  dx
296  ret
297
298; get display capabilities
299
300_dispi_get_max_xres:
301  push dx
302  push bx
303  call dispi_get_enable
304  mov  bx, ax
305  or   ax, # VBE_DISPI_GETCAPS
306  call _dispi_set_enable
307  mov  dx, # VBE_DISPI_IOPORT_INDEX
308  mov  ax, # VBE_DISPI_INDEX_XRES
309  out  dx, ax
310  mov  dx, # VBE_DISPI_IOPORT_DATA
311  in   ax, dx
312  push ax
313  mov  ax, bx
314  call _dispi_set_enable
315  pop  ax
316  pop  bx
317  pop  dx
318  ret
319
320_dispi_get_max_bpp:
321  push dx
322  push bx
323  call dispi_get_enable
324  mov  bx, ax
325  or   ax, # VBE_DISPI_GETCAPS
326  call _dispi_set_enable
327  mov  dx, # VBE_DISPI_IOPORT_INDEX
328  mov  ax, # VBE_DISPI_INDEX_BPP
329  out  dx, ax
330  mov  dx, # VBE_DISPI_IOPORT_DATA
331  in   ax, dx
332  push ax
333  mov  ax, bx
334  call _dispi_set_enable
335  pop  ax
336  pop  bx
337  pop  dx
338  ret
339
340_dispi_set_enable:
341  push dx
342  push ax
343  mov  dx, # VBE_DISPI_IOPORT_INDEX
344  mov  ax, # VBE_DISPI_INDEX_ENABLE
345  out  dx, ax
346  pop  ax
347  mov  dx, # VBE_DISPI_IOPORT_DATA
348  out  dx, ax
349  pop  dx
350  ret
351
352dispi_get_enable:
353  push dx
354  mov  dx, # VBE_DISPI_IOPORT_INDEX
355  mov  ax, # VBE_DISPI_INDEX_ENABLE
356  out  dx, ax
357  mov  dx, # VBE_DISPI_IOPORT_DATA
358  in   ax, dx
359  pop  dx
360  ret
361
362_dispi_set_bank:
363  push dx
364  push ax
365  mov  dx, # VBE_DISPI_IOPORT_INDEX
366  mov  ax, # VBE_DISPI_INDEX_BANK
367  out  dx, ax
368  pop  ax
369  mov  dx, # VBE_DISPI_IOPORT_DATA
370  out  dx, ax
371  pop  dx
372  ret
373
374dispi_get_bank:
375  push dx
376  mov  dx, # VBE_DISPI_IOPORT_INDEX
377  mov  ax, # VBE_DISPI_INDEX_BANK
378  out  dx, ax
379  mov  dx, # VBE_DISPI_IOPORT_DATA
380  in   ax, dx
381  pop  dx
382  ret
383ASM_END
384
385static void dispi_set_bank_farcall()
386{
387ASM_START
388  cmp bx,#0x0100
389  je dispi_set_bank_farcall_get
390  or bx,bx
391  jnz dispi_set_bank_farcall_error
392  mov ax,dx
393  push dx
394  push ax
395  mov ax,# VBE_DISPI_INDEX_BANK
396  mov dx,# VBE_DISPI_IOPORT_INDEX
397  out dx,ax
398  pop ax
399  mov dx,# VBE_DISPI_IOPORT_DATA
400  out dx,ax
401  in  ax,dx
402  pop dx
403  cmp dx,ax
404  jne dispi_set_bank_farcall_error
405  mov ax, #0x004f
406  retf
407dispi_set_bank_farcall_get:
408  mov ax,# VBE_DISPI_INDEX_BANK
409  mov dx,# VBE_DISPI_IOPORT_INDEX
410  out dx,ax
411  mov dx,# VBE_DISPI_IOPORT_DATA
412  in ax,dx
413  mov dx,ax
414  retf
415dispi_set_bank_farcall_error:
416  mov ax,#0x014F
417  retf
418ASM_END
419}
420
421ASM_START
422dispi_set_x_offset:
423  push dx
424  push ax
425  mov  dx, # VBE_DISPI_IOPORT_INDEX
426  mov  ax, # VBE_DISPI_INDEX_X_OFFSET
427  out  dx, ax
428  pop  ax
429  mov  dx, # VBE_DISPI_IOPORT_DATA
430  out  dx, ax
431  pop  dx
432  ret
433
434dispi_get_x_offset:
435  push dx
436  mov  dx, # VBE_DISPI_IOPORT_INDEX
437  mov  ax, # VBE_DISPI_INDEX_X_OFFSET
438  out  dx, ax
439  mov  dx, # VBE_DISPI_IOPORT_DATA
440  in   ax, dx
441  pop  dx
442  ret
443
444dispi_set_y_offset:
445  push dx
446  push ax
447  mov  dx, # VBE_DISPI_IOPORT_INDEX
448  mov  ax, # VBE_DISPI_INDEX_Y_OFFSET
449  out  dx, ax
450  pop  ax
451  mov  dx, # VBE_DISPI_IOPORT_DATA
452  out  dx, ax
453  pop  dx
454  ret
455
456dispi_get_y_offset:
457  push dx
458  mov  dx, # VBE_DISPI_IOPORT_INDEX
459  mov  ax, # VBE_DISPI_INDEX_Y_OFFSET
460  out  dx, ax
461  mov  dx, # VBE_DISPI_IOPORT_DATA
462  in   ax, dx
463  pop  dx
464  ret
465
466vga_set_virt_width:
467  push ax
468  push bx
469  push dx
470  mov  bx, ax
471  call dispi_get_bpp
472  cmp  al, #0x04
473  ja   set_width_svga
474  shr  bx, #1
475set_width_svga:
476  shr  bx, #3
477  mov  dx, # VGAREG_VGA_CRTC_ADDRESS
478  mov  ah, bl
479  mov  al, #0x13
480  out  dx, ax
481  pop  dx
482  pop  bx
483  pop  ax
484  ret
485
486dispi_set_virt_width:
487  call vga_set_virt_width
488  push dx
489  push ax
490  mov  dx, # VBE_DISPI_IOPORT_INDEX
491  mov  ax, # VBE_DISPI_INDEX_VIRT_WIDTH
492  out  dx, ax
493  pop  ax
494  mov  dx, # VBE_DISPI_IOPORT_DATA
495  out  dx, ax
496  pop  dx
497  ret
498
499dispi_get_virt_width:
500  push dx
501  mov  dx, # VBE_DISPI_IOPORT_INDEX
502  mov  ax, # VBE_DISPI_INDEX_VIRT_WIDTH
503  out  dx, ax
504  mov  dx, # VBE_DISPI_IOPORT_DATA
505  in   ax, dx
506  pop  dx
507  ret
508
509dispi_get_virt_height:
510  push dx
511  mov  dx, # VBE_DISPI_IOPORT_INDEX
512  mov  ax, # VBE_DISPI_INDEX_VIRT_HEIGHT
513  out  dx, ax
514  mov  dx, # VBE_DISPI_IOPORT_DATA
515  in   ax, dx
516  pop  dx
517  ret
518
519_vga_compat_setup:
520  push ax
521  push dx
522
523  ; set CRT X resolution
524  mov  dx, # VBE_DISPI_IOPORT_INDEX
525  mov  ax, # VBE_DISPI_INDEX_XRES
526  out  dx, ax
527  mov  dx, # VBE_DISPI_IOPORT_DATA
528  in   ax, dx
529  push ax
530  mov  dx, # VGAREG_VGA_CRTC_ADDRESS
531  mov  ax, #0x0011
532  out  dx, ax
533  pop  ax
534  push ax
535  shr  ax, #3
536  dec  ax
537  mov  ah, al
538  mov  al, #0x01
539  out  dx, ax
540  pop  ax
541  call vga_set_virt_width
542
543  ; set CRT Y resolution
544  mov  dx, # VBE_DISPI_IOPORT_INDEX
545  mov  ax, # VBE_DISPI_INDEX_YRES
546  out  dx, ax
547  mov  dx, # VBE_DISPI_IOPORT_DATA
548  in   ax, dx
549  dec  ax
550  push ax
551  mov  dx, # VGAREG_VGA_CRTC_ADDRESS
552  mov  ah, al
553  mov  al, #0x12
554  out  dx, ax
555  pop  ax
556  mov  al, #0x07
557  out  dx, al
558  inc  dx
559  in   al, dx
560  and  al, #0xbd
561  test ah, #0x01
562  jz   bit8_clear
563  or   al, #0x02
564bit8_clear:
565  test ah, #0x02
566  jz   bit9_clear
567  or   al, #0x40
568bit9_clear:
569  out  dx, al
570
571  ; other settings
572  mov  dx, # VGAREG_VGA_CRTC_ADDRESS
573  mov  ax, #0x0009
574  out  dx, ax
575  mov  al, #0x17
576  out  dx, al
577  mov  dx, # VGAREG_VGA_CRTC_DATA
578  in   al, dx
579  or   al, #0x03
580  out  dx, al
581  mov  dx, # VGAREG_ACTL_RESET
582  in   al, dx
583  mov  dx, # VGAREG_ACTL_ADDRESS
584  mov  al, #0x10
585  out  dx, al
586  mov  dx, # VGAREG_ACTL_READ_DATA
587  in   al, dx
588  or   al, #0x01
589  mov  dx, # VGAREG_ACTL_ADDRESS
590  out  dx, al
591  mov  al, #0x20
592  out  dx, al
593  mov  dx, # VGAREG_GRDC_ADDRESS
594  mov  ax, #0x0506
595  out  dx, ax
596  mov  dx, # VGAREG_SEQU_ADDRESS
597  mov  ax, #0x0f02
598  out  dx, ax
599
600  ; settings for >= 8bpp
601  mov  dx, # VBE_DISPI_IOPORT_INDEX
602  mov  ax, # VBE_DISPI_INDEX_BPP
603  out  dx, ax
604  mov  dx, # VBE_DISPI_IOPORT_DATA
605  in   ax, dx
606  cmp  al, #0x08
607  jb   vga_compat_end
608  mov  dx, # VGAREG_VGA_CRTC_ADDRESS
609  mov  al, #0x14
610  out  dx, al
611  mov  dx, # VGAREG_VGA_CRTC_DATA
612  in   al, dx
613  or   al, #0x40
614  out  dx, al
615  mov  dx, # VGAREG_ACTL_RESET
616  in   al, dx
617  mov  dx, # VGAREG_ACTL_ADDRESS
618  mov  al, #0x10
619  out  dx, al
620  mov  dx, # VGAREG_ACTL_READ_DATA
621  in   al, dx
622  or   al, #0x40
623  mov  dx, # VGAREG_ACTL_ADDRESS
624  out  dx, al
625  mov  al, #0x20
626  out  dx, al
627  mov  dx, # VGAREG_SEQU_ADDRESS
628  mov  al, #0x04
629  out  dx, al
630  mov  dx, # VGAREG_SEQU_DATA
631  in   al, dx
632  or   al, #0x08
633  out  dx, al
634  mov  dx, # VGAREG_GRDC_ADDRESS
635  mov  al, #0x05
636  out  dx, al
637  mov  dx, # VGAREG_GRDC_DATA
638  in   al, dx
639  and  al, #0x9f
640  or   al, #0x40
641  out  dx, al
642
643vga_compat_end:
644  pop  dx
645  pop  ax
646ASM_END
647
648
649// ModeInfo helper function
650static ModeInfoListItem* mode_info_find_mode(mode, using_lfb)
651  Bit16u mode; Boolean using_lfb;
652{
653  ModeInfoListItem  *cur_info=&mode_info_list;
654
655  while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST)
656  {
657    if (cur_info->mode == mode)
658    {
659      if (!using_lfb)
660      {
661        return cur_info;
662      }
663      else if (cur_info->info.ModeAttributes & VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE)
664      {
665        return cur_info;
666      }
667      else
668      {
669        cur_info++;
670      }
671    }
672    else
673    {
674      cur_info++;
675    }
676  }
677
678  return 0;
679}
680
681ASM_START
682
683; Has VBE display - Returns true if VBE display detected
684
685_vbe_has_vbe_display:
686  push ds
687  push bx
688  mov  ax, # BIOSMEM_SEG
689  mov  ds, ax
690  mov  bx, # BIOSMEM_VBE_FLAG
691  mov  al, [bx]
692  and  al, #0x01
693  xor  ah, ah
694  pop  bx
695  pop  ds
696  ret
697
698; VBE Init - Initialise the Vesa Bios Extension Code
699; This function does a sanity check on the host side display code interface.
700
701vbe_init:
702  mov  ax, # VBE_DISPI_ID0
703  call dispi_set_id
704  call dispi_get_id
705  cmp  ax, # VBE_DISPI_ID0
706  jne  no_vbe_interface
707  push ds
708  push bx
709  mov  ax, # BIOSMEM_SEG
710  mov  ds, ax
711  mov  bx, # BIOSMEM_VBE_FLAG
712  mov  al, #0x01
713  mov  [bx], al
714  pop  bx
715  pop  ds
716  mov  ax, # VBE_DISPI_ID5
717  call dispi_set_id
718no_vbe_interface:
719#if defined(USE_BX_INFO) || defined(DEBUG)
720  mov  bx, #msg_vbe_init
721  push bx
722  call _printf
723  inc  sp
724  inc  sp
725#endif
726  ret
727
728; VBE Display Info - Display information on screen about the VBE
729
730vbe_display_info:
731  call _vbe_has_vbe_display
732  test ax, ax
733  jz   no_vbe_flag
734  mov  ax, #0xc000
735  mov  ds, ax
736  mov  si, #_vbebios_info_string
737  jmp  _display_string
738no_vbe_flag:
739  mov  ax, #0xc000
740  mov  ds, ax
741  mov  si, #_no_vbebios_info_string
742  jmp  _display_string
743
744; helper function for memory size calculation
745
746lmulul:
747  and eax, #0x0000FFFF
748  shl ebx, #16
749  or  eax, ebx
750  SEG SS
751  mul eax, dword ptr [di]
752  mov ebx, eax
753  shr ebx, #16
754  ret
755ASM_END
756
757/** Function 00h - Return VBE Controller Information
758 *
759 * Input:
760 *              AX      = 4F00h
761 *              ES:DI   = Pointer to buffer in which to place VbeInfoBlock structure
762 *                        (VbeSignature should be VBE2 when VBE 2.0 information is desired and
763 *                        the info block is 512 bytes in size)
764 * Output:
765 *              AX      = VBE Return Status
766 *
767 */
768void vbe_biosfn_return_controller_information(AX, ES, DI)
769Bit16u *AX;Bit16u ES;Bit16u DI;
770{
771        Bit16u            ss=get_SS();
772        VbeInfoBlock      vbe_info_block;
773        Bit16u            status;
774        Bit16u            result;
775        Bit16u            vbe2_info;
776        Bit16u            cur_mode=0;
777        Bit16u            cur_ptr=34;
778        Bit16u            size_64k;
779        ModeInfoListItem  *cur_info=&mode_info_list;
780
781        status = read_word(ss, AX);
782
783#ifdef DEBUG
784        printf("VBE vbe_biosfn_return_vbe_info ES%x DI%x AX%x\n",ES,DI,status);
785#endif
786
787        vbe2_info = 0;
788#ifdef VBE2_NO_VESA_CHECK
789#else
790        // get vbe_info_block into local variable
791        memcpyb(ss, &vbe_info_block, ES, DI, sizeof(vbe_info_block));
792
793        // check for VBE2 signature
794        if (((vbe_info_block.VbeSignature[0] == 'V') &&
795             (vbe_info_block.VbeSignature[1] == 'B') &&
796             (vbe_info_block.VbeSignature[2] == 'E') &&
797             (vbe_info_block.VbeSignature[3] == '2')) ||
798
799            ((vbe_info_block.VbeSignature[0] == 'V') &&
800             (vbe_info_block.VbeSignature[1] == 'E') &&
801             (vbe_info_block.VbeSignature[2] == 'S') &&
802             (vbe_info_block.VbeSignature[3] == 'A')) )
803        {
804                vbe2_info = 1;
805#ifdef DEBUG
806                printf("VBE correct VESA/VBE2 signature found\n");
807#endif
808        }
809#endif
810
811        // VBE Signature
812        vbe_info_block.VbeSignature[0] = 'V';
813        vbe_info_block.VbeSignature[1] = 'E';
814        vbe_info_block.VbeSignature[2] = 'S';
815        vbe_info_block.VbeSignature[3] = 'A';
816
817        // VBE Version supported
818        vbe_info_block.VbeVersion = 0x0200;
819
820        // OEM String
821        vbe_info_block.OemStringPtr_Seg = 0xc000;
822        vbe_info_block.OemStringPtr_Off = &vbebios_copyright;
823
824        // Capabilities
825        vbe_info_block.Capabilities[0] = VBE_CAPABILITY_8BIT_DAC;
826        vbe_info_block.Capabilities[1] = 0;
827        vbe_info_block.Capabilities[2] = 0;
828        vbe_info_block.Capabilities[3] = 0;
829
830        // VBE Video Mode Pointer (dynamicly generated from the mode_info_list)
831        vbe_info_block.VideoModePtr_Seg= ES ;
832        vbe_info_block.VideoModePtr_Off= DI + 34;
833
834        // VBE Total Memory (in 64k blocks)
835        outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIDEO_MEMORY_64K);
836        vbe_info_block.TotalMemory = inw(VBE_DISPI_IOPORT_DATA);
837
838        if (vbe2_info)
839        {
840                // OEM Stuff
841                vbe_info_block.OemSoftwareRev = VBE_OEM_SOFTWARE_REV;
842                vbe_info_block.OemVendorNamePtr_Seg = 0xc000;
843                vbe_info_block.OemVendorNamePtr_Off = &vbebios_vendor_name;
844                vbe_info_block.OemProductNamePtr_Seg = 0xc000;
845                vbe_info_block.OemProductNamePtr_Off = &vbebios_product_name;
846                vbe_info_block.OemProductRevPtr_Seg = 0xc000;
847                vbe_info_block.OemProductRevPtr_Off = &vbebios_product_revision;
848
849                // copy updates in vbe_info_block back
850                memcpyb(ES, DI, ss, &vbe_info_block, sizeof(vbe_info_block));
851        }
852        else
853        {
854                // copy updates in vbe_info_block back (VBE 1.x compatibility)
855                memcpyb(ES, DI, ss, &vbe_info_block, 256);
856        }
857
858        do
859        {
860                size_64k = (Bit16u)((Bit32u)cur_info->info.XResolution * cur_info->info.XResolution * cur_info->info.BitsPerPixel) >> 19;
861
862                if ((cur_info->info.XResolution <= dispi_get_max_xres()) &&
863                    (cur_info->info.BitsPerPixel <= dispi_get_max_bpp()) &&
864                    (size_64k <= vbe_info_block.TotalMemory)) {
865#ifdef DEBUG
866                  printf("VBE found mode %x => %x\n", cur_info->mode,cur_mode);
867#endif
868                  write_word(ES, DI + cur_ptr, cur_info->mode);
869                  cur_mode++;
870                  cur_ptr+=2;
871                } else {
872#ifdef DEBUG
873                  printf("VBE mode %x (xres=%x / bpp=%02x) not supported \n", cur_info->mode,cur_info->info.XResolution,cur_info->info.BitsPerPixel);
874#endif
875                }
876                cur_info++;
877        } while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST);
878
879        // Add vesa mode list terminator
880        write_word(ES, DI + cur_ptr, cur_info->mode);
881
882        result = 0x4f;
883
884        write_word(ss, AX, result);
885}
886
887
888/** Function 01h - Return VBE Mode Information
889 *
890 * Input:
891 *              AX      = 4F01h
892 *              CX      = Mode Number
893 *              ES:DI   = Pointer to buffer in which to place ModeInfoBlock structure
894 * Output:
895 *              AX      = VBE Return Status
896 *
897 */
898void vbe_biosfn_return_mode_information(AX, CX, ES, DI)
899Bit16u *AX;Bit16u CX; Bit16u ES;Bit16u DI;
900{
901        Bit16u            result=0x0100;
902        Bit16u            ss=get_SS();
903        ModeInfoBlock     info;
904        ModeInfoListItem  *cur_info;
905        Boolean           using_lfb;
906        Bit16u            lfb_addr;
907
908#ifdef DEBUG
909        printf("VBE vbe_biosfn_return_mode_information ES%x DI%x CX%x\n",ES,DI,CX);
910#endif
911
912        using_lfb=((CX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER);
913
914        CX = (CX & 0x1ff);
915
916        cur_info = mode_info_find_mode(CX, using_lfb, &cur_info);
917
918        if (cur_info != 0)
919        {
920#ifdef DEBUG
921                printf("VBE found mode %x\n",CX);
922#endif
923                memsetb(ss, &info, 0, sizeof(ModeInfoBlock));
924                memcpyb(ss, &info, 0xc000, &(cur_info->info), sizeof(ModeInfoBlockCompact));
925                if (using_lfb) {
926                  info.NumberOfBanks = 1;
927                }
928                lfb_addr = pci_get_lfb_addr(0x1234); // experimental vendor
929                if (lfb_addr > 0) {
930                  info.PhysBasePtr = ((Bit32u)lfb_addr << 16);
931                }
932                if (info.WinAAttributes & VBE_WINDOW_ATTRIBUTE_RELOCATABLE) {
933                  info.WinFuncPtr = 0xC0000000UL;
934                  *(Bit16u *)&(info.WinFuncPtr) = (Bit16u)(dispi_set_bank_farcall);
935                }
936
937                result = 0x4f;
938        }
939        else
940        {
941#ifdef DEBUG
942                printf("VBE *NOT* found mode %x\n",CX);
943#endif
944                result = 0x100;
945        }
946
947        if (result == 0x4f)
948        {
949                // copy updates in mode_info_block back
950                memcpyb(ES, DI, ss, &info, sizeof(info));
951        }
952
953        write_word(ss, AX, result);
954}
955
956/** Function 02h - Set VBE Mode
957 *
958 * Input:
959 *              AX      = 4F02h
960 *              BX      = Desired Mode to set
961 *              ES:DI   = Pointer to CRTCInfoBlock structure
962 * Output:
963 *              AX      = VBE Return Status
964 *
965 */
966void vbe_biosfn_set_mode(AX, BX, ES, DI)
967Bit16u *AX;Bit16u BX; Bit16u ES;Bit16u DI;
968{
969        Bit16u            ss = get_SS();
970        Bit16u            result;
971        ModeInfoListItem  *cur_info;
972        Boolean           using_lfb;
973        Bit8u             no_clear;
974        Bit8u             lfb_flag;
975
976        using_lfb=((BX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER);
977        lfb_flag=using_lfb?VBE_DISPI_LFB_ENABLED:0;
978        no_clear=((BX & VBE_MODE_PRESERVE_DISPLAY_MEMORY) == VBE_MODE_PRESERVE_DISPLAY_MEMORY)?VBE_DISPI_NOCLEARMEM:0;
979
980        BX = (BX & 0x1ff);
981
982        //result=read_word(ss,AX);
983
984        // check for non vesa mode
985        if (BX<VBE_MODE_VESA_DEFINED)
986        {
987                Bit8u   mode;
988
989                dispi_set_enable(VBE_DISPI_DISABLED);
990                // call the vgabios in order to set the video mode
991                // this allows for going back to textmode with a VBE call (some applications expect that to work)
992
993                mode=(BX & 0xff);
994                biosfn_set_video_mode(mode);
995                result = 0x4f;
996        }
997
998        cur_info = mode_info_find_mode(BX, using_lfb, &cur_info);
999
1000        if (cur_info != 0)
1001        {
1002#ifdef DEBUG
1003                printf("VBE found mode %x, setting:\n", BX);
1004                printf("\txres%x yres%x bpp%x\n",
1005                        cur_info->info.XResolution,
1006                        cur_info->info.YResolution,
1007                        cur_info->info.BitsPerPixel);
1008#endif
1009
1010                // first disable current mode (when switching between vesa modi)
1011                dispi_set_enable(VBE_DISPI_DISABLED);
1012
1013                if (cur_info->info.BitsPerPixel == 4)
1014                {
1015                  biosfn_set_video_mode(0x6a);
1016                }
1017
1018                dispi_set_bpp(cur_info->info.BitsPerPixel);
1019                dispi_set_xres(cur_info->info.XResolution);
1020                dispi_set_yres(cur_info->info.YResolution);
1021                dispi_set_bank(0);
1022                dispi_set_enable(VBE_DISPI_ENABLED | no_clear | lfb_flag);
1023                vga_compat_setup();
1024
1025                write_word(BIOSMEM_SEG,BIOSMEM_VBE_MODE,BX);
1026                write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60 | no_clear));
1027
1028                result = 0x4f;
1029        }
1030        else
1031        {
1032#ifdef DEBUG
1033                printf("VBE *NOT* found mode %x\n" , BX);
1034#endif
1035                result = 0x100;
1036
1037                // FIXME: redirect non VBE modi to normal VGA bios operation
1038                //        (switch back to VGA mode
1039                if (BX == 3)
1040                        result = 0x4f;
1041        }
1042
1043        write_word(ss, AX, result);
1044}
1045
1046/** Function 03h - Return Current VBE Mode
1047 *
1048 * Input:
1049 *              AX      = 4F03h
1050 * Output:
1051 *              AX      = VBE Return Status
1052 *              BX      = Current VBE Mode
1053 *
1054 */
1055ASM_START
1056vbe_biosfn_return_current_mode:
1057  push ds
1058  mov  ax, # BIOSMEM_SEG
1059  mov  ds, ax
1060  call dispi_get_enable
1061  and  ax, # VBE_DISPI_ENABLED
1062  jz   no_vbe_mode
1063  mov  bx, # BIOSMEM_VBE_MODE
1064  mov  ax, [bx]
1065  mov  bx, ax
1066  jnz  vbe_03_ok
1067no_vbe_mode:
1068  mov  bx, # BIOSMEM_CURRENT_MODE
1069  mov  al, [bx]
1070  mov  bl, al
1071  xor  bh, bh
1072vbe_03_ok:
1073  mov  ax, #0x004f
1074  pop  ds
1075  ret
1076ASM_END
1077
1078
1079Bit16u vbe_biosfn_read_video_state_size()
1080{
1081    return 9 * 2;
1082}
1083
1084void vbe_biosfn_save_video_state(ES, BX)
1085     Bit16u ES; Bit16u BX;
1086{
1087    Bit16u enable, i;
1088
1089    outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
1090    enable = inw(VBE_DISPI_IOPORT_DATA);
1091    write_word(ES, BX, enable);
1092    BX += 2;
1093    if (!(enable & VBE_DISPI_ENABLED))
1094        return;
1095    for(i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
1096        if (i != VBE_DISPI_INDEX_ENABLE) {
1097            outw(VBE_DISPI_IOPORT_INDEX, i);
1098            write_word(ES, BX, inw(VBE_DISPI_IOPORT_DATA));
1099            BX += 2;
1100        }
1101    }
1102}
1103
1104
1105void vbe_biosfn_restore_video_state(ES, BX)
1106     Bit16u ES; Bit16u BX;
1107{
1108    Bit16u enable, i;
1109
1110    enable = read_word(ES, BX);
1111    BX += 2;
1112
1113    if (!(enable & VBE_DISPI_ENABLED)) {
1114        outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
1115        outw(VBE_DISPI_IOPORT_DATA, enable);
1116    } else {
1117        outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
1118        outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
1119        BX += 2;
1120        outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
1121        outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
1122        BX += 2;
1123        outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
1124        outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
1125        BX += 2;
1126        outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
1127        outw(VBE_DISPI_IOPORT_DATA, enable);
1128
1129        for(i = VBE_DISPI_INDEX_BANK; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
1130            outw(VBE_DISPI_IOPORT_INDEX, i);
1131            outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
1132            BX += 2;
1133        }
1134    }
1135}
1136
1137/** Function 04h - Save/Restore State
1138 *
1139 * Input:
1140 *              AX      = 4F04h
1141 *              DL      = 00h Return Save/Restore State buffer size
1142 *                        01h Save State
1143 *                        02h Restore State
1144 *              CX      = Requested states
1145 *              ES:BX   = Pointer to buffer (if DL <> 00h)
1146 * Output:
1147 *              AX      = VBE Return Status
1148 *              BX      = Number of 64-byte blocks to hold the state buffer (if DL=00h)
1149 *
1150 */
1151void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX)
1152Bit16u *AX; Bit16u CX; Bit16u DX; Bit16u ES; Bit16u *BX;
1153{
1154    Bit16u ss=get_SS();
1155    Bit16u result, val;
1156
1157    result = 0x4f;
1158    switch(GET_DL()) {
1159    case 0x00:
1160        val = biosfn_read_video_state_size2(CX);
1161#ifdef DEBUG
1162        printf("VGA state size=%x\n", val);
1163#endif
1164        if (CX & 8)
1165            val += vbe_biosfn_read_video_state_size();
1166        write_word(ss, BX, val);
1167        break;
1168    case 0x01:
1169        val = read_word(ss, BX);
1170        val = biosfn_save_video_state(CX, ES, val);
1171#ifdef DEBUG
1172        printf("VGA save_state offset=%x\n", val);
1173#endif
1174        if (CX & 8)
1175            vbe_biosfn_save_video_state(ES, val);
1176        break;
1177    case 0x02:
1178        val = read_word(ss, BX);
1179        val = biosfn_restore_video_state(CX, ES, val);
1180#ifdef DEBUG
1181        printf("VGA restore_state offset=%x\n", val);
1182#endif
1183        if (CX & 8)
1184            vbe_biosfn_restore_video_state(ES, val);
1185        break;
1186    default:
1187        // function failed
1188        result = 0x100;
1189        break;
1190    }
1191    write_word(ss, AX, result);
1192}
1193
1194/** Function 05h - Display Window Control
1195 *
1196 * Input:
1197 *              AX      = 4F05h
1198 *     (16-bit) BH      = 00h Set memory window
1199 *                      = 01h Get memory window
1200 *              BL      = Window number
1201 *                      = 00h Window A
1202 *                      = 01h Window B
1203 *              DX      = Window number in video memory in window
1204 *                        granularity units (Set Memory Window only)
1205 * Note:
1206 *              If this function is called while in a linear frame buffer mode,
1207 *              this function must fail with completion code AH=03h
1208 *
1209 * Output:
1210 *              AX      = VBE Return Status
1211 *              DX      = Window number in window granularity units
1212 *                        (Get Memory Window only)
1213 */
1214ASM_START
1215vbe_biosfn_display_window_control:
1216  cmp  bl, #0x00
1217  jne  vbe_05_failed
1218  cmp  bh, #0x01
1219  je   get_display_window
1220  jb   set_display_window
1221  mov  ax, #0x0100
1222  ret
1223set_display_window:
1224  mov  ax, dx
1225  call _dispi_set_bank
1226  call dispi_get_bank
1227  cmp  ax, dx
1228  jne  vbe_05_failed
1229  mov  ax, #0x004f
1230  ret
1231get_display_window:
1232  call dispi_get_bank
1233  mov  dx, ax
1234  mov  ax, #0x004f
1235  ret
1236vbe_05_failed:
1237  mov  ax, #0x014f
1238  ret
1239ASM_END
1240
1241
1242/** Function 06h - Set/Get Logical Scan Line Length
1243 *
1244 * Input:
1245 *              AX      = 4F06h
1246 *              BL      = 00h Set Scan Line Length in Pixels
1247 *                      = 01h Get Scan Line Length
1248 *                      = 02h Set Scan Line Length in Bytes
1249 *                      = 03h Get Maximum Scan Line Length
1250 *              CX      = If BL=00h Desired Width in Pixels
1251 *                        If BL=02h Desired Width in Bytes
1252 *                        (Ignored for Get Functions)
1253 *
1254 * Output:
1255 *              AX      = VBE Return Status
1256 *              BX      = Bytes Per Scan Line
1257 *              CX      = Actual Pixels Per Scan Line
1258 *                        (truncated to nearest complete pixel)
1259 *              DX      = Maximum Number of Scan Lines
1260 */
1261ASM_START
1262vbe_biosfn_set_get_logical_scan_line_length:
1263  mov  ax, cx
1264  cmp  bl, #0x01
1265  je   get_logical_scan_line_length
1266  cmp  bl, #0x02
1267  je   set_logical_scan_line_bytes
1268  jb   set_logical_scan_line_pixels
1269  mov  ax, #0x0100
1270  ret
1271set_logical_scan_line_bytes:
1272  push ax
1273  call dispi_get_bpp
1274  xor  bh, bh
1275  mov  bl, ah
1276  or   bl, bl
1277  jnz  no_4bpp_1
1278  shl  ax, #3
1279  mov  bl, #1
1280no_4bpp_1:
1281  xor  dx, dx
1282  pop  ax
1283  div  bx
1284set_logical_scan_line_pixels:
1285  call dispi_set_virt_width
1286get_logical_scan_line_length:
1287  call dispi_get_bpp
1288  xor  bh, bh
1289  mov  bl, ah
1290  call dispi_get_virt_width
1291  mov  cx, ax
1292  or   bl, bl
1293  jnz  no_4bpp_2
1294  shr  ax, #3
1295  mov  bl, #1
1296no_4bpp_2:
1297  mul  bx
1298  mov  bx, ax
1299  call dispi_get_virt_height
1300  mov  dx, ax
1301  mov  ax, #0x004f
1302  ret
1303ASM_END
1304
1305
1306/** Function 07h - Set/Get Display Start
1307 *
1308 * Input(16-bit):
1309 *              AX      = 4F07h
1310 *              BH      = 00h Reserved and must be 00h
1311 *              BL      = 00h Set Display Start
1312 *                      = 01h Get Display Start
1313 *                      = 02h Schedule Display Start (Alternate)
1314 *                      = 03h Schedule Stereoscopic Display Start
1315 *                      = 04h Get Scheduled Display Start Status
1316 *                      = 05h Enable Stereoscopic Mode
1317 *                      = 06h Disable Stereoscopic Mode
1318 *                      = 80h Set Display Start during Vertical Retrace
1319 *                      = 82h Set Display Start during Vertical Retrace (Alternate)
1320 *                      = 83h Set Stereoscopic Display Start during Vertical Retrace
1321 *              ECX     = If BL=02h/82h Display Start Address in bytes
1322 *                        If BL=03h/83h Left Image Start Address in bytes
1323 *              EDX     = If BL=03h/83h Right Image Start Address in bytes
1324 *              CX      = If BL=00h/80h First Displayed Pixel In Scan Line
1325 *              DX      = If BL=00h/80h First Displayed Scan Line
1326 *
1327 * Output:
1328 *              AX      = VBE Return Status
1329 *              BH      = If BL=01h Reserved and will be 0
1330 *              CX      = If BL=01h First Displayed Pixel In Scan Line
1331 *                        If BL=04h 0 if flip has not occurred, not 0 if it has
1332 *              DX      = If BL=01h First Displayed Scan Line
1333 *
1334 * Input(32-bit):
1335 *              BH      = 00h Reserved and must be 00h
1336 *              BL      = 00h Set Display Start
1337 *                      = 80h Set Display Start during Vertical Retrace
1338 *              CX      = Bits 0-15 of display start address
1339 *              DX      = Bits 16-31 of display start address
1340 *              ES      = Selector for memory mapped registers
1341 */
1342ASM_START
1343vbe_biosfn_set_get_display_start:
1344  cmp  bl, #0x80
1345  je   set_display_start
1346  cmp  bl, #0x01
1347  je   get_display_start
1348  jb   set_display_start
1349  mov  ax, #0x0100
1350  ret
1351set_display_start:
1352  mov  ax, cx
1353  call dispi_set_x_offset
1354  mov  ax, dx
1355  call dispi_set_y_offset
1356  mov  ax, #0x004f
1357  ret
1358get_display_start:
1359  call dispi_get_x_offset
1360  mov  cx, ax
1361  call dispi_get_y_offset
1362  mov  dx, ax
1363  xor  bh, bh
1364  mov  ax, #0x004f
1365  ret
1366ASM_END
1367
1368
1369/** Function 08h - Set/Get Dac Palette Format
1370 *
1371 * Input:
1372 *              AX      = 4F08h
1373 *              BL      = 00h set DAC palette width
1374 *                      = 01h get DAC palette width
1375 *              BH      = If BL=00h: desired number of bits per primary color
1376 * Output:
1377 *              AX      = VBE Return Status
1378 *              BH      = current number of bits per primary color (06h = standard VGA)
1379 */
1380ASM_START
1381vbe_biosfn_set_get_dac_palette_format:
1382  cmp  bl, #0x01
1383  je   get_dac_palette_format
1384  jb   set_dac_palette_format
1385  mov  ax, #0x0100
1386  ret
1387set_dac_palette_format:
1388  call dispi_get_enable
1389  cmp  bh, #0x06
1390  je   set_normal_dac
1391  cmp  bh, #0x08
1392  jne  vbe_08_unsupported
1393  or   ax, # VBE_DISPI_8BIT_DAC
1394  jnz  set_dac_mode
1395set_normal_dac:
1396  and  ax, #~ VBE_DISPI_8BIT_DAC
1397set_dac_mode:
1398  call _dispi_set_enable
1399get_dac_palette_format:
1400  mov  bh, #0x06
1401  call dispi_get_enable
1402  and  ax, # VBE_DISPI_8BIT_DAC
1403  jz   vbe_08_ok
1404  mov  bh, #0x08
1405vbe_08_ok:
1406  mov  ax, #0x004f
1407  ret
1408vbe_08_unsupported:
1409  mov  ax, #0x014f
1410  ret
1411ASM_END
1412
1413
1414/** Function 09h - Set/Get Palette Data
1415 *
1416 * Input:
1417 *              AX      = 4F09h
1418 * Output:
1419 *              AX      = VBE Return Status
1420 *
1421 * FIXME: incomplete API description, Input & Output
1422 */
1423void vbe_biosfn_set_get_palette_data(AX)
1424{
1425}
1426
1427/** Function 0Ah - Return VBE Protected Mode Interface
1428 * Input:    AX   = 4F0Ah   VBE 2.0 Protected Mode Interface
1429 *           BL   = 00h          Return protected mode table
1430 *
1431 *
1432 * Output:   AX   =         Status
1433 *           ES   =         Real Mode Segment of Table
1434 *           DI   =         Offset of Table
1435 *           CX   =         Length of Table including protected mode code
1436 *                          (for copying purposes)
1437 */
1438ASM_START
1439vbe_biosfn_return_protected_mode_interface:
1440  test bl, bl
1441  jnz _fail
1442  mov di, #0xc000
1443  mov es, di
1444  mov di, # vesa_pm_start
1445  mov cx, # vesa_pm_end
1446  sub cx, di
1447  mov ax, #0x004f
1448  ret
1449_fail:
1450  mov ax, #0x014f
1451  ret
1452ASM_END
1453