| ; **************************************************************************** |
| ; |
| ; gfxboot.asm |
| ; |
| ; Copyright 2008-2009 Sebastian Herbszt |
| ; |
| ; This module is based on the gfxboot integration patch by Steffen Winterfeldt: |
| ; |
| ; Copyright 2001-2008 Steffen Winterfeldt |
| ; |
| ; Some parts borrowed from Syslinux core: |
| ; |
| ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved |
| ; |
| ; This program is free software; you can redistribute it and/or modify |
| ; it under the terms of the GNU General Public License as published by |
| ; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
| ; Boston MA 02111-1307, USA; either version 2 of the License, or |
| ; (at your option) any later version; incorporated herein by reference. |
| ; |
| ; **************************************************************************** |
| |
| absolute 0 |
| pspInt20: resw 1 |
| pspNextP: resw 1 |
| resb 124 |
| pspCmdLen: resb 1 |
| pspCmdArg: resb 127 |
| |
| ;%define DEBUG |
| |
| section .text |
| org 100h |
| |
| _start: |
| mov ax,2 |
| mov bx, msg_progname |
| int 22h |
| |
| mov ax,2 |
| mov bx, msg_crlf |
| int 22h |
| |
| push es |
| mov ax,0ah |
| mov cl,9 |
| int 22h |
| pop es |
| cmp al,32h |
| jnz not_pxelinux |
| |
| mov dl,0 ; fake drive number |
| mov cl,11 ; fake sector size 2048 bytes |
| |
| not_pxelinux: |
| mov [derivative_id],al |
| mov [drivenumber],dl |
| mov [sectorshift],cl |
| mov ax,1 |
| shl ax,cl |
| mov [sectorsize],ax |
| mov ax,trackbufsize |
| shr ax,cl |
| mov [BufSafe],ax |
| |
| xor cx,cx |
| mov cl,[pspCmdLen] |
| dec cx |
| and cx,cx |
| jne continue |
| |
| mov ax,2 |
| mov bx, msg_usage |
| int 22h |
| ret |
| continue: |
| mov di,pspCmdArg+1 |
| add di,cx |
| dec di |
| std |
| mov al,' ' |
| repe scasb |
| inc cx |
| cld |
| mov [pspCmdLen],cl |
| mov si,pspCmdArg+1 |
| mov di,si |
| add di,cx |
| xor al,al |
| stosb |
| mov si,pspCmdArg+1 |
| |
| ; get config file name |
| mov ax,0eh |
| int 22h |
| |
| ; open config file |
| mov si,bx ; es:bx config file name |
| mov ax,6 |
| int 22h |
| jc no_config_file |
| and eax,eax |
| jz no_config_file |
| jmp got_config_file |
| no_config_file: |
| push es |
| push bx |
| push cs |
| pop es |
| mov bx, msg_config_file |
| mov ax,2 |
| int 22h |
| mov bx, msg_space |
| mov ax,2 |
| int 22h |
| pop bx |
| pop es |
| mov ax,2 |
| int 22h |
| push cs |
| pop es |
| mov bx, msg_space |
| mov ax,2 |
| int 22h |
| mov bx, msg_missing |
| mov ax,2 |
| int 22h |
| mov ax,2 |
| mov bx, msg_crlf |
| int 22h |
| ret |
| got_config_file: |
| push cs |
| pop es |
| call parse_config |
| |
| ; get_gfx_file |
| mov ax,cs |
| add ax,2000h |
| mov word [gfx_mem_start_seg],ax |
| mov ax,[pspNextP] |
| mov word [gfx_mem_end_seg],ax |
| |
| call gfx_init |
| jc error |
| |
| call gfx_setup_menu |
| jc exit |
| |
| input: |
| call gfx_input |
| jc exit |
| |
| cmp eax,1 |
| jz exit |
| |
| cmp eax,2 |
| jz boot |
| |
| jmp input |
| |
| boot: |
| call far [gfx_bc_done] |
| mov ax,cs |
| mov es,ax |
| mov bx,command_line |
| mov ax,3 |
| int 22h |
| exit: |
| call far [gfx_bc_done] |
| error: |
| ret |
| |
| cb_table dw cb_status ; 0 |
| dw cb_fopen ; 1 |
| dw cb_fread ; 2 |
| dw cb_getcwd ; 3 |
| dw cb_chdir ; 4 |
| dw cb_readsector ; 5 |
| cb_len equ ($-cb_table)/2 |
| |
| gfx_cb: |
| push cs |
| pop ds |
| |
| cmp al,cb_len |
| jae gfx_cb_error |
| |
| movzx bx,al |
| add bx,bx |
| call word [bx+cb_table] |
| jmp gfx_cb_end |
| gfx_cb_error: |
| mov al,0ffh |
| gfx_cb_end: |
| retf |
| |
| ; Return status info |
| ; |
| ; return: |
| ; edx filename buffer (64 bytes) |
| ; |
| cb_status: |
| mov edx,cs |
| shl edx,4 |
| add edx,fname_buf |
| |
| xor al,al |
| ret |
| |
| ; Open file |
| ; |
| ; return: |
| ; al 0: ok, 1: file not found |
| ; ecx file length (al = 0) |
| ; |
| cb_fopen: |
| push ds |
| pop es |
| mov ax,6 |
| mov si,fname_buf |
| int 22h |
| jnc cb_fopen_ok |
| cb_fopen_failed: |
| mov al,1 |
| jmp cb_fopen_end |
| cb_fopen_ok: |
| mov ecx,eax |
| mov [f_handle],si |
| mov [f_size],ecx |
| xor al,al |
| cb_fopen_end: |
| ret |
| |
| ; Read next chunk |
| ; |
| ; return: |
| ; edx buffer address (linear) |
| ; ecx data length (< 64k) |
| ; |
| cb_fread: |
| cmp dword [f_size],0 |
| jz cb_fread_eof |
| push ds |
| pop es |
| mov ax,7 |
| mov si,[f_handle] |
| mov bx,trackbuf |
| mov cx,[BufSafe] |
| int 22h |
| mov al,1 |
| jc cb_fread_end |
| sub [f_size], ecx |
| or si,si |
| jnz cb_fread_noeof |
| and dword [f_size],0 |
| cb_fread_noeof: |
| mov edx,cs |
| shl edx,4 |
| add edx,trackbuf |
| jmp cb_fread_ok |
| cb_fread_eof: |
| xor ecx,ecx |
| cb_fread_ok: |
| xor al,al |
| cb_fread_end: |
| ret |
| |
| ; Return current working directory |
| ; |
| ; return: |
| ; edx filename |
| ; |
| cb_getcwd: |
| mov edx,cs |
| shl edx,4 |
| add edx,gfx_slash |
| xor al,al |
| ret |
| |
| ; Set current working directory |
| ; |
| cb_chdir: |
| xor al,al |
| ret |
| |
| ; Read sector |
| ; |
| ; edx sector |
| ; |
| ; return: |
| ; edx buffer (linear address) |
| ; |
| ; Note: does not return on error! |
| ; |
| cb_readsector: |
| push esi |
| push edi |
| push ds |
| pop es |
| mov ax,19h |
| xor esi,esi |
| xor edi,edi |
| mov cx,1 |
| mov bx,trackbuf |
| int 22h |
| pop edi |
| pop esi |
| mov al,1 |
| jc cb_readsector_end |
| mov edx,ds |
| shl dx,4 |
| add edx,trackbuf |
| xor al,al |
| cb_readsector_end: |
| ret |
| |
| gfx_init: |
| mov ax,0e801h |
| xor bx,bx |
| xor cx,cx |
| xor dx,dx |
| int 15h |
| jnc got_e801 |
| |
| mov ax,2 |
| mov bx, msg_memory |
| int 22h |
| stc |
| ret |
| |
| got_e801: |
| cmp ax,3c00h |
| jb mem_below_16mb |
| shl ebx,6 |
| add eax,ebx |
| |
| mem_below_16mb: |
| shl eax,10 |
| mov [gfx_bios_mem_size],eax |
| shr eax,20 |
| cmp ax,16 |
| jb skip_extended |
| |
| mov word [gfx_xmem_0],88h ; 8MB at 8MB |
| mov dword [gfx_save_area1],7f0000h ; 8MB-64k |
| |
| skip_extended: |
| movzx ebx,word [gfx_mem_start_seg] |
| shl ebx,4 |
| |
| movzx ecx,word [gfx_mem_end_seg] |
| shl ecx,4 |
| |
| mov dword [gfx_mem],ebx |
| mov dword [gfx_mem0_start],ebx |
| mov dword [gfx_mem0_end],ecx |
| |
| call gfx_read_file |
| jc gfx_init_end |
| |
| call gfx_get_sysconfig |
| |
| ; align 4 |
| mov eax,[gfx_mem0_start] |
| add eax,3 |
| and eax,~3 |
| mov [gfx_mem0_start],eax |
| |
| ; setup jump table |
| les bx,[gfx_bc_jt] |
| |
| mov ax,[es:bx] |
| mov [gfx_bc_init],ax |
| mov [gfx_bc_init+2],es |
| |
| mov ax,[es:bx+2] |
| mov [gfx_bc_done],ax |
| mov [gfx_bc_done+2],es |
| |
| mov ax,[es:bx+4] |
| mov [gfx_bc_input],ax |
| mov [gfx_bc_input+2],es |
| |
| mov ax,[es:bx+6] |
| mov [gfx_bc_menu_init],ax |
| mov [gfx_bc_menu_init+2],es |
| |
| ; ... |
| |
| mov esi,cs |
| shl esi,4 |
| add esi,gfx_sysconfig |
| call far [gfx_bc_init] |
| |
| gfx_init_end: |
| ret |
| |
| gfx_read_file: |
| ; open file |
| ; es:si - file name |
| |
| push cs |
| pop es |
| mov ax,6 |
| mov si,pspCmdArg+1 |
| int 22h |
| jnc gfx_file_read |
| stc |
| ret |
| |
| gfx_file_read: |
| ; si - file handle |
| ; eax - length of file in bytes, or -1 |
| ; cx - file block size |
| |
| mov edx,[gfx_mem0_end] |
| sub edx,[gfx_mem0_start] |
| sub edx,0fh ; space to allow for aligning later |
| ; edx: max allowed size |
| |
| cmp eax,-1 ; unknown file size -> set to max allowed size |
| jnz .has_size |
| mov eax,edx |
| .has_size: |
| cmp eax,edx |
| jbe read_bootlogo |
| |
| gfx_file_too_big: |
| mov ax,2 |
| mov bx,msg_bootlogo_toobig |
| int 22h |
| stc |
| ret |
| |
| read_bootlogo: |
| mov [file_length],eax |
| mov edi,[gfx_mem] |
| |
| ; read file |
| ; si - file handle |
| ; es:bx - buffer |
| ; cx - number of blocks to read |
| |
| read: |
| push eax |
| mov ax,7 |
| mov bx,trackbuf |
| mov cx,[BufSafe] |
| int 22h |
| |
| push edi |
| push ecx |
| push si |
| push es |
| |
| mov si,trackbuf |
| push edi |
| call gfx_l2so |
| pop di |
| pop es |
| |
| rep movsb ; move ds:si -> es:di, length ecx |
| pop es |
| pop si |
| pop ecx |
| pop edi |
| |
| pop eax |
| |
| ; si == 0: EOF |
| or si,si |
| jz gfx_read_done |
| add edi,ecx |
| sub eax,ecx |
| ja read |
| jmp gfx_file_too_big |
| gfx_read_done: |
| sub eax,ecx |
| mov edx,[file_length] |
| sub edx,eax |
| ; edx = real file size |
| mov [gfx_archive_end],edx |
| add edx,[gfx_mem0_start] |
| add edx,0fh ; for alignment |
| mov [gfx_mem0_start],edx |
| |
| bootlogo_read_done: |
| call find_file |
| or eax,eax |
| jnz found_bootlogo |
| stc |
| ret |
| |
| found_bootlogo: |
| push edi |
| push eax |
| add eax,edi |
| push dword [gfx_mem] |
| pop dword [gfx_archive_start] |
| neg al |
| and eax,byte 0fh |
| jz no_align |
| add [gfx_archive_start],eax |
| |
| no_align: |
| pop eax |
| pop edi |
| sub edi,[gfx_mem] |
| mov ecx,[gfx_archive_start] |
| add edi,ecx |
| mov [gfx_file],edi |
| add [gfx_archive_end],ecx |
| add eax,edi |
| shr eax,4 |
| mov [gfx_bc_jt+2],ax |
| ret |
| |
| gfx_get_sysconfig: |
| mov ah,0 |
| cmp byte [derivative_id],33h |
| jnz not_isolinux |
| mov ah,2 |
| not_isolinux: |
| mov al,[drivenumber] |
| mov [gfx_boot_drive],al |
| cmp al,80h ; floppy ? |
| jae not_floppy |
| mov ah,1 |
| not_floppy: |
| mov byte [gfx_media_type],ah |
| mov ah,[sectorshift] |
| mov byte [gfx_sector_shift],ah |
| mov ax,cs |
| mov [gfx_bootloader_seg],ax |
| ret |
| |
| gfx_setup_menu: |
| push es |
| push ds |
| pop es |
| |
| mov word [menu_desc+menu_ent_list],0 |
| mov di,[menu_seg] |
| mov [menu_desc+menu_ent_list+2],di |
| |
| mov word [menu_desc+menu_default],dentry_buf |
| mov [menu_desc+menu_default+2],cs |
| |
| mov di,256 |
| mov [menu_desc+menu_arg_list],di |
| mov di,[menu_seg] |
| mov [menu_desc+menu_arg_list+2],di |
| |
| mov cx,[label_cnt] |
| mov [menu_desc+menu_entries],cx |
| |
| mov cx,256*2 |
| mov [menu_desc+menu_ent_size],cx |
| mov [menu_desc+menu_arg_size],cx |
| |
| mov esi,ds |
| shl esi,4 |
| add esi,menu_desc |
| |
| call far [gfx_bc_menu_init] |
| pop es |
| ret |
| |
| magic_ok: |
| xor eax,eax |
| cmp dword [es:bx],0b2d97f00h ; header.magic_id |
| jnz magic_ok_end |
| cmp byte [es:bx+4],8 ; header.version |
| jnz magic_ok_end |
| mov eax,[es:bx+8] |
| magic_ok_end: |
| ret |
| |
| find_file: |
| mov edi,[gfx_mem] |
| push edi |
| call gfx_l2so |
| pop bx |
| pop es |
| call magic_ok |
| or eax,eax |
| jnz find_file_end |
| |
| find_file_loop: |
| mov ecx,[gfx_mem0_start] |
| sub ecx,26 + 12 ; min cpio header + gfx header |
| cmp edi,ecx |
| jae find_file_end |
| push edi |
| call gfx_l2so |
| pop bx |
| pop es |
| cmp word [es:bx],71c7h |
| jnz find_file_end |
| mov ax,[es:bx+20] ; file name size |
| movzx esi,ax |
| |
| inc si |
| and si,~1 ; align |
| |
| mov eax,[es:bx+22] ; data size |
| rol eax,16 ; get word order right |
| mov ecx,eax |
| |
| inc ecx |
| and ecx,byte ~1 ; align |
| |
| add si,26 ; skip header |
| |
| add edi,esi |
| add bx,si |
| call magic_ok |
| or eax,eax |
| jnz find_file_end |
| |
| add edi,ecx |
| jmp find_file_loop |
| |
| find_file_end: |
| ret |
| |
| gfx_input: |
| mov edi,cs |
| shl edi,4 |
| add edi, command_line ; buffer (0: no buffer) |
| mov ecx, max_cmd_len ; buffer size |
| ; xor eax,eax ; timeout value (0: no timeout) |
| mov eax,100 ; timeout value (0: no timeout) |
| |
| call far [gfx_bc_input] |
| ret |
| |
| gfx_l2so: |
| push eax |
| mov eax,[esp + 6] |
| shr eax,4 |
| mov [esp + 8],ax |
| and word [esp + 6],byte 0fh |
| pop eax |
| ret |
| |
| parse_config: |
| mov [f_handle],si |
| push es |
| mov ax,cs |
| add ax,1000h |
| mov es,ax |
| mov word [menu_seg],ax |
| xor eax,eax |
| mov cx,4000h |
| mov di,0 |
| rep stosd |
| pop es |
| .read: |
| call skipspace |
| jz .eof |
| jc .read |
| cmp al,'#' |
| je .nextline |
| or al,20h ; convert to lower case |
| mov di,configbuf |
| stosb |
| .read_loop: |
| call getc |
| jc .eof |
| cmp al,' ' |
| jbe .done |
| or al,20h ; convert to lower case |
| stosb |
| jmp .read_loop |
| .done: |
| call ungetc |
| |
| xor ax,ax |
| stosb |
| %ifdef DEBUG |
| mov ax,2 |
| mov bx, configbuf |
| int 22h |
| |
| mov ax,2 |
| mov bx, msg_crlf |
| int 22h |
| %endif |
| push si |
| push di |
| xor ecx,ecx |
| mov si,configbuf |
| mov di,label_keyword+1 |
| mov cl, byte [label_keyword] |
| call memcmp |
| pop di |
| pop si |
| jz .do_label |
| |
| push si |
| push di |
| xor ecx,ecx |
| mov si,configbuf |
| mov di,default_keyword+1 |
| mov cl, byte [default_keyword] |
| call memcmp |
| pop di |
| pop si |
| jz .do_default |
| |
| .nextline: |
| call skipline |
| jmp .read |
| |
| .do_label: |
| call skipspace |
| jz .eof |
| jc .noparm |
| call ungetc |
| push es |
| push di |
| mov ax,[menu_seg] |
| mov es,ax |
| mov di,[menu_off] |
| call getline |
| mov di,[menu_off] |
| add di,512 |
| mov [menu_off],di |
| pop di |
| pop es |
| inc word [label_cnt] |
| |
| jmp .read |
| |
| .do_default: |
| call skipspace |
| jz .eof |
| jc .noparm |
| call ungetc |
| push es |
| push di |
| push cs |
| pop es |
| mov di,dentry_buf |
| call getline |
| pop di |
| pop es |
| |
| jmp .read |
| |
| .eof: |
| .noparm: |
| ret |
| |
| skipline: |
| cmp al,10 |
| je .end |
| call getc |
| jc .end |
| jmp skipline |
| .end: |
| ret |
| |
| skipspace: |
| .loop: |
| call getc |
| jc .eof |
| cmp al,0Ah |
| je .eoln |
| cmp al,' ' |
| jbe .loop |
| ret |
| .eof: |
| cmp al,al |
| stc |
| ret |
| .eoln: |
| add al,0FFh |
| ret |
| |
| ungetc: |
| mov byte [ungetc_cnt],1 |
| mov byte [ungetcdata],al |
| ret |
| |
| getc: |
| cmp byte [ungetc_cnt],1 |
| jne .noungetc |
| mov byte [ungetc_cnt],0 |
| mov al,[ungetcdata] |
| clc |
| ret |
| .noungetc: |
| sub word [bufbytes],1 |
| jc .get_data |
| mov si,trackbuf |
| add si,[bufdata] |
| mov al,[si] |
| inc word [bufdata] |
| clc |
| ret |
| .get_data: |
| mov si,[f_handle] |
| and si,si |
| jz .empty |
| mov ax,7 |
| mov bx,trackbuf |
| mov cx,[BufSafe] |
| int 22h |
| mov word [bufdata],0 |
| jc .empty |
| mov [f_handle],si |
| mov [bufbytes],cx |
| jmp getc |
| .empty: |
| mov word [f_handle],0 |
| mov word [bufbytes],0 |
| stc |
| ret |
| |
| getline: |
| call skipspace |
| jz .eof |
| jc .eoln |
| call ungetc |
| .loop: |
| call getc |
| jc .ret |
| cmp al,' ' |
| jna .ctrl |
| .store: |
| stosb |
| jmp .loop |
| .ctrl: |
| cmp al,10 |
| je .ret |
| mov al,' ' |
| jmp .store |
| .eoln: |
| clc |
| jmp .ret |
| .eof: |
| stc |
| .ret: |
| xor al,al |
| stosb |
| ret |
| |
| |
| memcmp: |
| push si |
| push di |
| push ax |
| .loop: |
| mov al,[si] |
| mov ah,[di] |
| inc si |
| inc di |
| cmp al,ah |
| loope .loop |
| pop ax |
| pop di |
| pop si |
| ret |
| |
| section .data |
| derivative_id db 0 |
| drivenumber db 0 |
| sectorshift db 0 |
| sectorsize dw 0 |
| trackbufsize equ 16384 |
| trackbuf times trackbufsize db 0 |
| BufSafe dw 0 |
| file_length dd 0 |
| |
| bufbytes dw 0 |
| bufdata dw 0 |
| configbuf times trackbufsize db 0 |
| ungetc_cnt db 0 |
| ungetcdata db 0 |
| label_keyword db 6,'label',0 |
| default_keyword db 7,'default',0 |
| label_cnt dw 0 |
| |
| msg_progname db 'gfxboot: ',0 |
| msg_config_file db 'Configuration file',0 |
| msg_missing db 'missing',0 |
| msg_usage db 'Usage: gfxboot.com <bootlogo>',0dh,0ah,0 |
| msg_memory db 'Could not detect available memory size',0dh,0ah,0 |
| msg_bootlogo_toobig db 'bootlogo file too big',0dh,0ah,0 |
| msg_pxelinux db 'pxelinux is not supported',0dh,0ah,0 |
| msg_unknown_file_size db 'unknown file size',0dh,0ah,0 |
| msg_space db ' ',0 |
| msg_crlf db 0dh,0ah,0 |
| |
| f_handle dw 0 |
| f_size dd 0 |
| fname_buf times 64 db 0 |
| fname_buf_len equ $ - fname_buf |
| gfx_slash db '/', 0 |
| db0 db 0 |
| max_cmd_len equ 2047 |
| command_line times max_cmd_len+2 db 0 |
| dentry_buf times 512 db 0 |
| dentry_buf_len equ $ - dentry_buf |
| |
| ; menu entry descriptor |
| menu_entries equ 0 |
| menu_default equ 2 ; seg:ofs |
| menu_ent_list equ 6 ; seg:ofs |
| menu_ent_size equ 10 |
| menu_arg_list equ 12 ; seg:ofs |
| menu_arg_size equ 16 |
| sizeof_menu_desc equ 18 |
| |
| menu_desc times sizeof_menu_desc db 0 |
| menu_seg dw 0 |
| menu_off dw 0 |
| |
| gfx_mem_start_seg dw 0 |
| gfx_mem_end_seg dw 0 |
| |
| align 4, db 0 |
| gfx_mem dd 0 ; linear address |
| gfx_save_area1 dd 0 ; 64k |
| gfx_save_area1_used db 0 ; != 0 if area1 is in use |
| |
| ; interface to loadable gfx extension (seg:ofs values) |
| gfx_bc_jt dd 0 |
| |
| gfx_bc_init dd 0 |
| gfx_bc_done dd 0 |
| gfx_bc_input dd 0 |
| gfx_bc_menu_init dd 0 |
| |
| ; system config data (52 bytes) |
| gfx_sysconfig equ $ |
| gfx_bootloader db 1 ; 0: boot loader type (0: lilo, 1: syslinux, 2: grub) |
| gfx_sector_shift db 9 ; 1: sector shift |
| gfx_media_type db 0 ; 2: media type (0: disk, 1: floppy, 2: cdrom) |
| gfx_failsafe db 0 ; 3: turn on failsafe mode (bitmask) |
| ; 0: SHIFT pressed |
| ; 1: skip gfxboot |
| ; 2: skip monitor detection |
| gfx_sysconfig_size db gfx_sysconfig_end-gfx_sysconfig ; 4: size of sysconfig data |
| gfx_boot_drive db 0 ; 5: BIOS boot drive |
| gfx_callback dw gfx_cb ; 6: offset to callback handler |
| gfx_bootloader_seg dw 0 ; 8: code/data segment used by bootloader; must follow gfx_callback |
| gfx_reserved_1 dw 0 ; 10 |
| gfx_user_info_0 dd 0 ; 12: data for info box |
| gfx_user_info_1 dd 0 ; 16: data for info box |
| gfx_bios_mem_size dd 0 ; 20: BIOS memory size (in bytes) |
| gfx_xmem_0 dw 0 ; 24: extended mem area 0 (start:size in MB; 12:4 bits) |
| gfx_xmem_1 dw 0 ; 26: extended mem area 1 |
| gfx_xmem_2 dw 0 ; 28: extended mem area 2 |
| gfx_xmem_3 dw 0 ; 20: extended mem area 3 |
| gfx_file dd 0 ; 32: start of gfx file |
| gfx_archive_start dd 0 ; 36: start of cpio archive |
| gfx_archive_end dd 0 ; 40: end of cpio archive |
| gfx_mem0_start dd 0 ; 44: low free memory start |
| gfx_mem0_end dd 0 ; 48: low free memory end |
| gfx_sysconfig_end equ $ |