;; ============================================================================================ ;; ;; Copyright (C) 2002 Jeroen Janssen ;; ;; This library is free software; you can redistribute it and/or ;; modify it under the terms of the GNU Lesser General Public ;; License as published by the Free Software Foundation; either ;; version 2 of the License, or (at your option) any later version. ;; ;; This library is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; Lesser General Public License for more details. ;; ;; You should have received a copy of the GNU Lesser General Public ;; License along with this library; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ;; ;; ============================================================================================ ;; ;; This VBE is part of the VGA Bios specific to the plex86/bochs Emulated VGA card. ;; You can NOT drive any physical vga card with it. ;; ;; ============================================================================================ ;; ;; This VBE Bios is based on information taken from : ;; - VESA BIOS EXTENSION (VBE) Core Functions Standard Version 3.0 located at www.vesa.org ;; ;; ============================================================================================ ; Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice ; other than GPL or LGPL is available it will apply instead, Oracle elects to use only ; the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where ; a choice of LGPL license versions is made available with the language indicating ; that LGPLv2 or any later version may be used, or where a choice of which version ; of the LGPL is applied is otherwise unspecified. include vgadefs.inc include commondefs.inc public _vga_compat_setup public dispi_set_enable_ public dispi_set_bank_ public _dispi_set_bank_farcall public _dispi_get_max_bpp public _vbe_has_vbe_display public vbe_biosfn_return_current_mode public vbe_biosfn_display_window_control public vbe_biosfn_set_get_logical_scan_line_length public vbe_biosfn_set_get_display_start public vbe_biosfn_set_get_dac_palette_format public vbe_biosfn_set_get_palette_data public vbe_biosfn_return_protected_mode_interface VGAROM segment public 'CODE' SET_DEFAULT_CPU VBE_BYTEWISE_IO EQU 1 ;; Bytewise in/out ifdef VBE_BYTEWISE_IO public do_out_dx_ax public do_in_ax_dx do_out_dx_ax: xchg ah, al out dx, al xchg ah, al out dx, al ret do_in_ax_dx: in al, dx xchg ah, al in al, dx ret out_dx_ax EQU call do_out_dx_ax in_ax_dx EQU call do_in_ax_dx else out_dx_ax EQU out dx, ax in_ax_dx EQU in ax, dx endif ;; Vertical retrace waiting wait_vsync: push ax push dx mov dx, 03DAh ; @todo use a symbolic constant! wv_loop: in al, dx test al, 8 jz wv_loop pop dx pop ax ret wait_not_vsync: push ax push dx mov dx, 03DAh ; @todo use a symbolic constant! wnv_loop: in al, dx test al, 8 jnz wnv_loop pop dx pop ax ret ; AL = bits per pixel / AH = bytes per pixel dispi_get_bpp: push dx mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_BPP out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx cmp al, 4 jbe get_bpp_noinc mov ah, al if VBOX_BIOS_CPU gt 8086 shr ah, 3 else shr ah, 1 shr ah, 1 shr ah, 1 endif test al, 07 jz get_bpp_noinc inc ah get_bpp_noinc: pop dx ret ; get display capabilities _dispi_get_max_bpp: push dx push bx call dispi_get_enable mov bx, ax or ax, VBE_DISPI_GETCAPS call dispi_set_enable_ mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_BPP out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx push ax mov ax, bx call dispi_set_enable_ pop ax pop bx pop dx ret dispi_set_enable_: push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_ENABLE out_dx_ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out_dx_ax pop dx ret dispi_get_enable: push dx mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_ENABLE out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx pop dx ret dispi_set_bank_: push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_BANK out_dx_ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out_dx_ax pop dx ret dispi_get_bank: push dx mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_BANK out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx pop dx ret _dispi_set_bank_farcall: cmp bx, 0100h je dispi_set_bank_farcall_get or bx,bx jnz dispi_set_bank_farcall_error mov ax, dx push dx push ax mov ax, VBE_DISPI_INDEX_BANK mov dx, VBE_DISPI_IOPORT_INDEX out_dx_ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out_dx_ax in_ax_dx pop dx cmp dx,ax jne dispi_set_bank_farcall_error mov ax, 004Fh retf dispi_set_bank_farcall_get: mov ax, VBE_DISPI_INDEX_BANK mov dx, VBE_DISPI_IOPORT_INDEX out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx mov dx,ax retf dispi_set_bank_farcall_error: mov ax, 014Fh retf dispi_set_x_offset: push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_X_OFFSET out_dx_ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out_dx_ax pop dx ret dispi_get_x_offset: push dx mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_X_OFFSET out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx pop dx ret dispi_set_y_offset: push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_Y_OFFSET out_dx_ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out_dx_ax pop dx ret dispi_get_y_offset: push dx mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_Y_OFFSET out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx pop dx ret vga_set_virt_width: push ax push bx push dx mov bx, ax call dispi_get_bpp cmp al, 4 ja set_width_svga shr bx, 1 set_width_svga: if VBOX_BIOS_CPU gt 8086 shr bx, 3 else shr bx, 1 shr bx, 1 shr bx, 1 endif mov dx, VGAREG_VGA_CRTC_ADDRESS mov ah, bl mov al, 13h out dx, ax pop dx pop bx pop ax ret dispi_set_virt_width: call vga_set_virt_width push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_VIRT_WIDTH out_dx_ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out_dx_ax pop dx ret dispi_get_virt_width: push dx mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_VIRT_WIDTH out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx pop dx ret dispi_get_virt_height: push dx mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_VIRT_HEIGHT out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx pop dx ret _vga_compat_setup: push ax push dx ; set CRT X resolution mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_XRES out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx push ax mov dx, VGAREG_VGA_CRTC_ADDRESS mov ax, 0011h out dx, ax pop ax push ax if VBOX_BIOS_CPU gt 8086 shr ax, 3 else shr ax, 1 shr ax, 1 shr ax, 1 endif dec ax mov ah, al mov al, 01 out dx, ax pop ax call vga_set_virt_width ; set CRT Y resolution mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_YRES out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx dec ax push ax mov dx, VGAREG_VGA_CRTC_ADDRESS mov ah, al mov al, 12h out dx, ax pop ax mov al, 07 out dx, al inc dx in al, dx and al, 0BDh test ah, 01 jz bit8_clear or al, 02 bit8_clear: test ah, 02 jz bit9_clear or al, 40h bit9_clear: out dx, al ; other settings mov dx, VGAREG_VGA_CRTC_ADDRESS mov ax, 0009 out dx, al mov dx, VGAREG_VGA_CRTC_DATA in al, dx and al, 60h ; clear double scan bit and cell height out dx, al mov dx, VGAREG_VGA_CRTC_ADDRESS mov al, 17h out dx, al mov dx, VGAREG_VGA_CRTC_DATA in al, dx or al, 03 out dx, al mov dx, VGAREG_ACTL_RESET in al, dx mov dx, VGAREG_ACTL_ADDRESS mov al, 10h out dx, al mov dx, VGAREG_ACTL_READ_DATA in al, dx or al, 01 mov dx, VGAREG_ACTL_ADDRESS out dx, al mov al, 20h out dx, al mov dx, VGAREG_GRDC_ADDRESS mov ax, 0506h out dx, ax mov dx, VGAREG_SEQU_ADDRESS mov ax, 0F02h out dx, ax ; settings for >= 8bpp mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_BPP out_dx_ax mov dx, VBE_DISPI_IOPORT_DATA in_ax_dx cmp al, 08 jb vga_compat_end mov dx, VGAREG_VGA_CRTC_ADDRESS mov al, 14h out dx, al mov dx, VGAREG_VGA_CRTC_DATA in al, dx or al, 40h out dx, al mov dx, VGAREG_ACTL_RESET in al, dx mov dx, VGAREG_ACTL_ADDRESS mov al, 10h out dx, al mov dx, VGAREG_ACTL_READ_DATA in al, dx or al, 40h mov dx, VGAREG_ACTL_ADDRESS out dx, al mov al, 20h out dx, al mov dx, VGAREG_SEQU_ADDRESS mov al, 04 out dx, al mov dx, VGAREG_SEQU_DATA in al, dx or al, 08 out dx, al mov dx, VGAREG_GRDC_ADDRESS mov al, 05 out dx, al mov dx, VGAREG_GRDC_DATA in al, dx and al, 9Fh or al, 40h out dx, al vga_compat_end: pop dx pop ax ; Has VBE display - Returns true if VBE display detected _vbe_has_vbe_display: push ds push bx mov ax, BIOSMEM_SEG mov ds, ax mov bx, BIOSMEM_VBE_FLAG mov al, [bx] and al, 01 xor ah, ah pop bx pop ds ret ; Function 03h - Return Current VBE Mode ; ; Input: ; AX = 4F03h ; Output: ; AX = VBE Return Status ; BX = Current VBE Mode ; ; vbe_biosfn_return_current_mode: push ds mov ax, BIOSMEM_SEG mov ds, ax call dispi_get_enable and ax, VBE_DISPI_ENABLED jz no_vbe_mode mov bx, BIOSMEM_VBE_MODE mov ax, [bx] mov bx, ax jnz vbe_03_ok no_vbe_mode: mov bx, BIOSMEM_CURRENT_MODE mov al, [bx] mov bl, al xor bh, bh vbe_03_ok: mov ax, 004Fh pop ds ret ; Function 05h - Display Window Control ; ; Input: ; AX = 4F05h ; (16-bit) BH = 00h Set memory window ; = 01h Get memory window ; BL = Window number ; = 00h Window A ; = 01h Window B ; DX = Window number in video memory in window ; granularity units (Set Memory Window only) ; Note: ; If this function is called while in a linear frame buffer mode, ; this function must fail with completion code AH=03h ; ; Output: ; AX = VBE Return Status ; DX = Window number in window granularity units ; (Get Memory Window only) vbe_biosfn_display_window_control: cmp bl, 0 jne vbe_05_failed cmp bh, 1 je get_display_window jb set_display_window mov ax, 0100h ret set_display_window: mov ax, dx call dispi_set_bank_ call dispi_get_bank cmp ax, dx jne vbe_05_failed mov ax, 004Fh ret get_display_window: call dispi_get_bank mov dx, ax mov ax, 004Fh ret vbe_05_failed: mov ax, 014Fh ret ; Function 06h - Set/Get Logical Scan Line Length ; ; Input: ; AX = 4F06h ; BL = 00h Set Scan Line Length in Pixels ; = 01h Get Scan Line Length ; = 02h Set Scan Line Length in Bytes ; = 03h Get Maximum Scan Line Length ; CX = If BL=00h Desired Width in Pixels ; If BL=02h Desired Width in Bytes ; (Ignored for Get Functions) ; ; Output: ; AX = VBE Return Status ; BX = Bytes Per Scan Line ; CX = Actual Pixels Per Scan Line ; (truncated to nearest complete pixel) ; DX = Maximum Number of Scan Lines ; vbe_biosfn_set_get_logical_scan_line_length: mov ax, cx cmp bl, 1 je get_logical_scan_line_length cmp bl, 2 je set_logical_scan_line_bytes jb set_logical_scan_line_pixels mov ax, 0100h ret set_logical_scan_line_bytes: push ax call dispi_get_bpp xor bh, bh mov bl, ah or bl, bl pop ax jnz no_4bpp_1 if VBOX_BIOS_CPU gt 8086 shl ax, 3 else shl ax, 1 shl ax, 1 shl ax, 1 endif mov bl, 1 no_4bpp_1: xor dx, dx div bx set_logical_scan_line_pixels: call dispi_set_virt_width get_logical_scan_line_length: call dispi_get_bpp xor bh, bh mov bl, ah call dispi_get_virt_width mov cx, ax or bl, bl jnz no_4bpp_2 if VBOX_BIOS_CPU gt 8086 shr ax, 3 else shr ax, 1 shr ax, 1 shr ax, 1 endif mov bl, 1 no_4bpp_2: mul bx mov bx, ax call dispi_get_virt_height mov dx, ax mov ax, 004Fh ret ; Function 07h - Set/Get Display Start ; ; Input(16-bit): ; AX = 4F07h ; BH = 00h Reserved and must be 00h ; BL = 00h Set Display Start ; = 01h Get Display Start ; = 02h Schedule Display Start (Alternate) ; = 03h Schedule Stereoscopic Display Start ; = 04h Get Scheduled Display Start Status ; = 05h Enable Stereoscopic Mode ; = 06h Disable Stereoscopic Mode ; = 80h Set Display Start during Vertical Retrace ; = 82h Set Display Start during Vertical Retrace (Alternate) ; = 83h Set Stereoscopic Display Start during Vertical Retrace ; ECX = If BL=02h/82h Display Start Address in bytes ; If BL=03h/83h Left Image Start Address in bytes ; EDX = If BL=03h/83h Right Image Start Address in bytes ; CX = If BL=00h/80h First Displayed Pixel In Scan Line ; DX = If BL=00h/80h First Displayed Scan Line ; ; Output: ; AX = VBE Return Status ; BH = If BL=01h Reserved and will be 0 ; CX = If BL=01h First Displayed Pixel In Scan Line ; If BL=04h 0 if flip has not occurred, not 0 if it has ; DX = If BL=01h First Displayed Scan Line ; ; Input(32-bit): ; BH = 00h Reserved and must be 00h ; BL = 00h Set Display Start ; = 80h Set Display Start during Vertical Retrace ; CX = Bits 0-15 of display start address ; DX = Bits 16-31 of display start address ; ES = Selector for memory mapped registers ; vbe_biosfn_set_get_display_start: cmp bl, 80h je set_display_start_wait cmp bl, 1 je get_display_start jb set_display_start mov ax, 0100h ret set_display_start_wait: call wait_not_vsync call wait_vsync set_display_start: mov ax, cx call dispi_set_x_offset mov ax, dx call dispi_set_y_offset mov ax, 004Fh ret get_display_start: call dispi_get_x_offset mov cx, ax call dispi_get_y_offset mov dx, ax xor bh, bh mov ax, 004Fh ret ; Function 08h - Set/Get Dac Palette Format ; ; Input: ; AX = 4F08h ; BL = 00h set DAC palette width ; = 01h get DAC palette width ; BH = If BL=00h: desired number of bits per primary color ; Output: ; AX = VBE Return Status ; BH = current number of bits per primary color (06h = standard VGA) ; vbe_biosfn_set_get_dac_palette_format: cmp bl, 1 je get_dac_palette_format jb set_dac_palette_format mov ax, 0100h ret set_dac_palette_format: call dispi_get_enable cmp bh, 6 je set_normal_dac cmp bh, 8 jne vbe_08_unsupported or ax, VBE_DISPI_8BIT_DAC jnz set_dac_mode set_normal_dac: and ax, NOT VBE_DISPI_8BIT_DAC set_dac_mode: call dispi_set_enable_ get_dac_palette_format: mov bh, 6 call dispi_get_enable and ax, VBE_DISPI_8BIT_DAC jz vbe_08_ok mov bh, 8 vbe_08_ok: mov ax, 004Fh ret vbe_08_unsupported: mov ax, 014Fh ret ; Function 09h - Set/Get Palette Data ; ; Input: ; AX = 4F09h ; (16-bit) BL = 00h Set palette data ; = 01h Get palette data ; = 02h Set secondary palette data ; = 03h Get secondary palette data ; = 80h Set palette data during VRetrace ; CX = Number of entries to update (<= 256) ; DX = First entry to update ; ES:DI = Table of palette values ; Output: ; AX = VBE Return Status ; ; Notes: ; Secondary palette support is a "future extension". ; Attempts to set/get it should return status 02h. ; ; In VBE 3.0, reading palette data is optional and ; subfunctions 01h and 03h may return failure. ; ; The format of palette entries is as follows: ; ; PaletteEntry struc ; Blue db ? ; Blue channel value (6 or 8 bits) ; Green db ? ; Green channel value (6 or 8 bits) ; Red db ? ; Red channel value (6 or 8 bits) ; Padding db ? ; DWORD alignment byte (unused) ; PaletteEntry ends ; ; Most applications use VGA DAC registers directly to ; set/get palette in VBE modes. However, subfn 4F09h is ; required for NonVGA controllers (eg. XGA). ; vbe_biosfn_set_get_palette_data: test bl, bl jz set_palette_data cmp bl, 01 je get_palette_data cmp bl, 03 jbe vbe_09_nohw cmp bl, 80h jne vbe_09_unsupported if 0 ; this is where we could wait for vertical retrace endif set_palette_data: DO_pushad push ds push es pop ds mov al, dl mov dx, VGAREG_DAC_WRITE_ADDRESS out dx, al inc dx mov si, di set_pal_loop: if VBOX_BIOS_CPU ge 80386 lodsd ror eax, 16 out dx, al rol eax, 8 out dx, al rol eax, 8 out dx, al else lodsw mov bx, ax lodsw out dx, al mov al, bh out dx, al mov al, bl out dx, al endif loop set_pal_loop pop ds DO_popad vbe_09_ok: mov ax, 004Fh ret get_palette_data: DO_pushad mov al, dl mov dx, VGAREG_DAC_READ_ADDRESS out dx, al add dl, 2 if VBOX_BIOS_CPU ge 80386 get_pal_loop: xor eax, eax in al, dx shl eax, 8 in al, dx shl eax, 8 in al, dx stosd else xor bx, bx get_pal_loop: in al, dx mov bl, al in al, dx mov ah, al in al, dx stosw mov ax, bx stosw endif loop get_pal_loop DO_popad jmp vbe_09_ok vbe_09_unsupported: mov ax, 014Fh ret vbe_09_nohw: mov ax, 024Fh ret ; Function 0Ah - Return VBE Protected Mode Interface ; ; Input: AX = 4F0Ah VBE 2.0 Protected Mode Interface ; BL = 00h Return protected mode table ; Output: AX = Status ; ES = Real Mode Segment of Table ; DI = Offset of Table ; CX = Length of Table including protected mode code ; (for copying purposes) ; vbe_biosfn_return_protected_mode_interface: test bl, bl jnz _fail mov di, 0C000h mov es, di mov di, offset vesa_pm_start mov cx, vesa_pm_end - vesa_pm_start mov ax, 004Fh ret _fail: mov ax, 014fh ret VGAROM ends ;; ;; 32-bit VBE interface ;; .386 public vesa_pm_start public vesa_pm_end VBE32 segment public use32 'CODE' align 2 vesa_pm_start: dw vesa_pm_set_window - vesa_pm_start dw vesa_pm_set_display_start - vesa_pm_start dw vesa_pm_unimplemented - vesa_pm_start dw vesa_pm_io_ports_table - vesa_pm_start vesa_pm_io_ports_table: dw VBE_DISPI_IOPORT_INDEX dw VBE_DISPI_IOPORT_INDEX + 1 dw VBE_DISPI_IOPORT_DATA dw VBE_DISPI_IOPORT_DATA + 1 dw 3B6h dw 3B7h dw 0FFFFh dw 0FFFFh vesa_pm_set_window: cmp bx, 0 je vesa_pm_set_display_window1 mov ax, 0100h ret vesa_pm_set_display_window1: mov ax, dx push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_BANK out dx, ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out dx, ax in ax, dx pop dx cmp dx, ax jne illegal_window mov ax, 004Fh ret illegal_window: mov ax, 014Fh ret vesa_pm_set_display_start: cmp bl, 80h je vesa_pm_set_display_start1_wait cmp bl, 00 je vesa_pm_set_display_start1 mov ax, 0100h ret vesa_pm_set_display_start1_wait: push edx mov dx, 03DAh ; @todo: use symbolic constant wnv_loop_32: in al, dx test al, 8 jnz wnv_loop_32 wv_loop_32: in al, dx test al, 8 jz wv_loop_32 pop edx vesa_pm_set_display_start1: ; convert offset to (X, Y) coordinate ; (would be simpler to change Bochs VBE API...) push eax push ecx push edx push esi push edi shl edx, 16 and ecx, 0FFFFh or ecx, edx shl ecx, 2 mov eax, ecx push eax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_VIRT_WIDTH out dx, ax mov dx, VBE_DISPI_IOPORT_DATA in ax, dx movzx ecx, ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_BPP out dx, ax mov dx, VBE_DISPI_IOPORT_DATA in ax, dx movzx esi, ax pop eax cmp esi, 4 jz bpp4_mode add esi, 7 shr esi, 3 imul ecx, esi xor edx, edx div ecx mov edi, eax mov eax, edx xor edx, edx div esi jmp set_xy_regs bpp4_mode: shr ecx, 1 xor edx, edx div ecx mov edi, eax mov eax, edx shl eax, 1 set_xy_regs: push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_X_OFFSET out dx, ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out dx, ax pop dx mov ax, di push dx push ax mov dx, VBE_DISPI_IOPORT_INDEX mov ax, VBE_DISPI_INDEX_Y_OFFSET out dx, ax pop ax mov dx, VBE_DISPI_IOPORT_DATA out dx, ax pop dx pop edi pop esi pop edx pop ecx pop eax mov ax, 004fh ret vesa_pm_unimplemented: mov ax, 014Fh ret vesa_pm_end: VBE32 ends end