Grub4dos Internal variables, memory areas, advanced features and function calls

The following is a list of some grub4dos internal variables and functions from a 2013 or later version of a chenall build of grub4dos (as used in RMPrepUSB).

eBook (PDF)

'Getting started with grub4dos' - Latest eBook PDF for 2020!

Complete grub4dos 0.4.6a guide with exercises and full command reference.

Memory areas used by grub4dos (minimum 64MB)

The physical memory address range from 1M to 32M+ is reserved for grub4dos internal code and data. Grub4dos may fail to run on systems with less than 64MB of RAM. Ideally users should not use this range.

Grub4dos and Linux can use RAM above 32MB.

Some areas below 1MB are also used by grub4dos.

Some use of (md)0x300 is relatively safe if low memory is required.

Grub4dos executes grub4dos executables and kernel payloads at 32MB in memory, therefore it is best to use memory areas well above 64MB for any memory workspace if you intend to call grub4dos executables after loading kernel files, etc.

Since grub4dos uses areas above 32MB, a 64MB system is the minimum requirement for grub4dos 0.4.6a.

Note: In grub4dos, batch files and programs run in the same memory buffer space as the 'kernel' command uses. Therefore you cannot use the kernel command directly from a batch file or grub4dos program. To workaround this, either allocate memory to move the buffer by using call Fn.50 0x200000 as the very first line in menu.lst OR use configfile, e.g.

echo kernel /xxx/vmlinuz aaa bbb ccc > (md)0x220+1

echo initrd initrd.gz >> (md)0x220+1

configfile (md)0x220+1

(md) is a memory 'device'. (md)x+y where x is the start sector in memory and y is the length of memory in sectors (e.g. (md)4+2 = start at 2KB in memory for a length of 1KB. Note that the BIOS uses low memory!

Grub4dos use of memory - (md) uses 512-byte 'sectors'

0x0007000  CMD line buffer
0x0008200  320K fixed variables + grldr program   = (md)0x41 to (md)0x300
0x0050000  to the end of low memory is used by gfxmenu
0x0030000  64k sector buffer for current device   = (md)0x180 to (md)0x1FF
0x0040000  unused (user)                          = (md)0x200 to (md)0x227
0x0045000  environment variables                  = (md)0x228 to (md)0x288 approx.
0x0060000  end of grub4dos variables + wkspace    = (md)0x300 to (md)0x3DF
0x007C000  used to load boot code, etc.           = (md)0x3E0...
0x0090000  may be used by BIOS (avoid)            = (md)0x480 to (md)0x4FF
0x00A0000  graphics memory (reserved)             = (md)0x500 to (md)0x5FF
0x00C0000  BIOS area (reserved)                   = (md)0x600 to (md)0x7FF
0x0100000  may be used by BIOS (avoid)            = (md)0x800 best to avoid up to (md)0x880
0x02B0000  2.6MB HMA_ADDR                         = (md)0x1580
0x03E0000  FSYS_BUF                               = (md)0x1F00 to (md)0x1F40
0x0800000  8M for mem array workspace             = (md)0x4000 to (md)0x7FFF
0x0A00000  10M-14M for page map                   = (md)0x5000-(md)0x6FFF
0x0F00000  15M-16M used by some systems (avoid)   = (md)0x7800-(md)0x7FFF
0x1000000  16M-19M used to store background bmp   = (md)0x8000-(md)0xD460 (end of buffer depends on size of bitmap, 1920x1440=0xD460, 1280x800=0x9F40, 1024x768=0x9800, 800x600=0x8EA6)
0x1800000 24MB Unifont area = (md)0xC000-0xFFFF
0x2000000 32MB Reserved memory area = (md)0x10000 (e.g. used by grub4dos for file decompression, etc.)
0x2800000 40MB Linux temp memory area = (md)0x14000

(md)0x880-(md)0x1580 may be usable (untested)??

Avoid 15MB to 16MB as used by some BIOSes/chipsets as echo of 1MB-2MB area! (md)0x7800-7FFF

Easy2Boot use (md)0x3000, (md)0x9F00, (md)0xA000, (md)0xB000.

Avoid below (md)0x300 and (md)0x4000 to (md)0x7FFF area.

Preferably use (md)0x10000 and above (above 32MB). I have also used there areas without problems so far...

Use of around (md)0x8000 to 0x9800 can cause corruption of bitmap background when a menu is displayed in 1024x768 mode (even more used in higher res modes!).

However wenv/insmod seems to load files above 32MB - therefore use above 50MB = (md)0x19000 to be safe!

Many BIOSes use the last part of segment 9000:0 for a scratchpad. All IBM-compatible BIOSes use A000:0 - F000:FFFF.

Black areas are normally unused by grub4dos and IBM PC BIOS and hardware but are not necessarily safe to use!

Suggested:

(md)0x200 - 0x227 (20K) this seems to be used by chenall, et al. and so is probably safe!

(md)0x300 - 0x3DF (114k) 384-600KB approx. (0x3E0 may be used for chainloading by grub4dos)

(md)0x1F40 - 0x3FFF (4M) I have used (md)0x3000-(md)0x3300 without problems

(md)0x9800 - 0xBFFF (5M) Safe if only using graphics mode 1024x768 or lower resolutions E2B used 0xA000+0xA0

(md)0xD800 - 0xFFFFF (can start from 0x9800 as long as not using higher Res than 1024x768)

(md)0x19000+ (50MB+)

As most systems will have over 64MB of memory, use memory after (md)0x20000 to be ultra safe!

Using grub4dos variables

grub4dos environment variables are stored as strings.

Each Grub4dos variable can have a maximum name length 8 characters and each variable holds a maximum size of 511 characters or bytes. The variable name is case sensitive, e.g.

set a=1
set A=2
set
a=1A=2

Environment names of 10 or more characters will cause an error.

Using a 9-character variable name can cause unexpected problems (FRED12345 in this example is shortened by grub4dos to FRED1234) ...

set FRED1234=Hi
set FRED12345=Lo
echo %FRED1234%
echo %FRED12345%
set
LoLoFRED1234=Lo

Note: Variables and their values are stored in memory and may share the same workspace area as modules loaded using the 'insmod' command. You may get crashes if you define too many variables and have lots of large modules loaded into memory (always use delmod to remove the modules from memory after use).

#clear all variables except fred
set * && set fred=%fred%

You can define leading spaces in a variable and have characters like && or ;; by enclosing the argument in double-quotes (must start as "<variable>= with no spaces

set "a=    fred && doris"

Be careful using && or || as the environment is not changed until the whole line has been executed - e.g. the following line will not print HELLO the first time it is run because A is not set until after the whole line has been executed:

set A=HELLO && echo %A%

(grub4dos will replace all %variables% in the whole line with their values first before executing the whole line).

Get the number of characters in any variable - this example prints 5

# set command will return the length
set a=freda
# do stuff here...
# get length of a into @retval
set a=%a%
echo Number of characters in variable a = %@retval%

To display all variables beginning with AB use:

set AB

You can use IF EXIST to test for the existence of a variable

if exist FRED echo Variable FRED exists!

Note: Using ^ anywhere inside % signs will prevent grub4dos from expanding the variable - e.g. %^aa% will be changed by the grub4dos pre-processor to %aa% - it will not be replaced by the value of aa.

Increasing the variable space (not recommended)

Grub4dos has space for only 60 string variables, each one can have a value of up to 511 bytes in size (plus one byte for 'end-of'string').

# check if a variable has been defined

if exist FRED1234 echo FRED1234 is set to %FRED1234% || echo Variable FRED1234 has not been defined!

In the latest versions of grub4dos (15 May 2013 and later) you can extend the variable space using an undocumented @extend command:

You can set up to max 65,536 variables using @extend.

Syntax:

set @extend BASE_ADDR SIZE

The BASE_ADDR is the memory start of extended variable.

SIZE is how many variables it will extended by (MAX is 0xFFFF)

e.g. add 10 variables (so we can have 70 variables in total) at 0x40000.

set @extend 0x40000 10

this needs memory (((10+63)/64) + 10)*512 = 5632. You must confirm the BASE_ADDR has enough space.

setlocal/endlocal will not work correctly however as any variables over the 60 that are set inside a setlocal section will remain after an endlocal!

So if you are using @extend and setlocal/endlocal, be sure to not use more than 60 variables inside the setlocal/endlocal segments!

e.g.

set @extend 0x40000 10 
set *
setlocal
set 70 variables here...
endlocal
set

>>> 10 variables will be listed!

The command:

set @extend

will display:

BASE_ADDR:40000,40200,VARS:10

The first number is the base address in memory where the extra variable names are stored. The second number is the next unused block. The last number is the number of extra variables (above 60) that can be stored (10 variables = 200h*10 = 200 sectors from 0x40000 to 0x401FF).

Strings

You can get a substring of any variable like this: %variablename:~startpos,endpos%

The startpos shows where the substring begins ( + position from the beginning, - position from the end)

The endpos shows where it ends ( + position from the beginning, - position from the end)

e.g

set a=0123456789abcdefghijklmnopqrstuvwxyz

echo %a:~0,3% displays 012

echo %a:~2,3% displays 234

echo %a:~-3,3% displays xyz

echo %a:~3% displays 3456789abcdefghijklmnopqrstuvwxyz

echo %a:~3,-2% displays 3456789abcdefghijklmnopqrstuvwx

echo %a:~,-2% displays 0123456789abcdefghijklmnopqrstuvwx

A header file which may help if you understand C code can be found here.

Example - test string length of KEY is exactly 29 characters

set KL=0

if not "%KEY:~29,1%"=="" set KL=1 && echo TOO LONG!

if "%KEY%"=="%KEY:~0,28%" set KL=2 && echo TOO SHORT!

if not "%KL%"=="0" pause --wait=3 INVALID PRODUCT KEY "%KEY%"! && configfile /menu.lst

Filename strings in batch files (limitations)

Max no lines = 2048 in a batch file, Max. no. labels in a batch file = 128, file must start with !BAT on first line.

In a batch file, %0 is the full path of the batch file itself (if the batch file is in the root then no leading / is output), %~nx0 is the batch files filename + extension, %~n0 is just the filename and %~x0 is just the extension (e.g. .gb4).

n is filename, x is extension, p is the path and d is the drive name.

%0=/xx/fred.g4b (if in root then fred.g4b)

%~pnx0=/xx/fred.g4b (if in root then /fred.g4b)

%~nx0=fred.g4b

%~x0=.g4b

%~n0=fred

%~dp0=(hd0,0)/xx/

%~dpnx0=(hd0,0/xx/fred.g4b

%~p0=/xx/

%~0=parameter without quotes

%~f0=fully path of filename - e.g. if %0=fred.inf then %~f0=(hd1,0)/dir1/dir2/fred.inf if root was set to (hd1,0)/dir1/dir2

If you pass more than one filename to a batch file, %1 will be the second parameter, %2 the third, etc. %* is all parameters.

Batch file to get all parameters as arg1...argn and argc as number of parameters:

!BAT
set argc=0
:LOOP
if "%1"=="" goto :fin
set /a argc=%argc%+1 > nul
set arg%argc%=%1
shift
goto :LOOP
:fin
set

Note: BEWARE! in batch files beware of %0 %1 %2 accidents! e.g.

set f=3
echo %f%2

will not display 32 (it displays %f because %1, %2 %3 special variables are substituted first, so grub4dos translates this to %f+%2) - instead use this...

set f=3
echo %f%%2

%% in a batch file is reduced to %. Since %%2 does not mean anything to grub4dos on the first substitution pass, we get the expected answer.

If you have a filename or full or partial filespec, you can get bits of it like this:

# set current path to /_ISO folder
root ()/_ISO > nul
set file=/test/my.iso
call :show %file%
...
exit
:show
echo %%~d1=%~d1 %%~p1=%~p1 %%~n1=%~n1 %%~x1=%~x1 %%~f1=%~f1
goto :eof

we get:

%~d1=(hd0,0) %~p1=/_ISO/test/ %~n1=my %~x1=.iso %~f1=(hd0,0)/_ISO/test/my.iso

Accessing numbered variables

If we have a number of variables FRED1, FRED2, FRED3, etc, and we want to print out the value of each, we can use a counter like this

!BAT
set FRED1=A
set FRED2=B
set FRED3=C
set N=0
:LOOP
set /a N=%N%+1 > nul
call echo %^FRED%N%%%
if %N%<=2 goto :LOOP

Produces:

ABC

Note that using ^ anywhere inside a variable prevents it from being translated into a value. So %^FRED%N%%% gets translated by 'call' to %FRED1% and then echo displays the FRED1's value.

If not using a batch file use call echo %^FRED%N%% instead (in a batch file, %%=%).

Internal named variables

grub4dos has a few predefined variables - these can be used in a grub4dos command - e.g. echo %@date% or set d=%@date%

These are case sensitive:

@date - e.g. 2012-04-08

@time - e.g. 15:29:11

@random - 0 to 32767 - e.g. set /a num1to10=%@random% / 3277 + 1 or set /a num1to10=%@random:~-1,1% + 1

@root - e.g. (hd0,0)

@path - e.g. (bd)/BOOT/GRUB/

@retval - e.g. 1 - NOTE: This is a 32bit value ranging from -2GB to +2GB

?_BOOT - disk&partition that contained the file \grldr that was used to boot to grub4dos, e.g. (hd0,0) - note: this is NOT the same as (bd) which is set to whatever partition contained the menu.lst file.

?_WENV&;

?_UUID - set after uuid command - e.g. uuid (bd)

? - result of last command - e.g. cat --locate=string --number=1 /myfile &; set offset=%?% &; echo string found at %offset% prints 0x1de or 0x0 if not found, @retval is 0 if not found or <count> if found

You can set the default file extensions of grub4dos executables using command --set-ext= (this is similar to typing just fred in the Windows command shell and Windows would run fred.exe) - for example, we can set the default extensions like this:

command --set-ext=.g4b;.g4x

If you then have a command like fred or /fred in a menu or batch file, grub4dos will try “fred” first and then fred.g4b and then fred.g4x.

Tip: use debug msg=3 to see the search paths used by grub4dos.

Fn calls

You can call an internal function like sprintf within a grub4dos menu or a grub4dos batch file. For example:

set a=aa

set b=bbb

call Fn.0 0 "stringa=%s stringb=%s" %a% %b%

this prints on the display

stringa=aa stringb=bbb

Here is a list of the Fn calls. I don't know all the details for all the parameters, but each parameter should be separated by a space.

Note that Fn. is case sensitive and must have a capital F and a lowercase n.

Some equivalent C library functions for these can be searched for here.

0 grub_sprintf syntax: call Fn.0 <memory location> <printf string> - if <memory location> = 0 then is output to screen, if <memory location>=0x60000 then the string is output to memory at (md)0x300. @retval returns the length of the string - e.g. call Fn.0 0x60000 "%pci%\r\n" = call func sprintf("%pci%") - another example: call Fn.0 0 "id="%s","%1"" %p_hwid% - This function is also useful for stripping quote marks from variables (but ensure there is only one pair or things can go wrong! Also note that Fn.0 0 fred will output 'fred' but Fn.0 0 1234 will not work as it is treated as a number - use Fn.0 0 "1234" instead.) call Fn.0 0 0xFFFF5 | set biosdate= will return the date of the BIOS on most systems - e.g. 06/23/99

1 grub_putstr

2 putchar - e.g. call Fn.2 49 prints '1' (if in the text mode console, call Fn.2 7 will 'beep' the internal speaker if your BIOS and hardware supports it - i.e. if it is a PC and has a 'beeper-speaker' fitted!)

3 get_cmdline_obsolete

4 getxy The return value is ((X << 8) | Y).

5 gotoxy - e.g. call Fn.5 0 11 set cursor pos to beginning of line 11

6 cls

7 wee_skip_to was obsolete setcursor

8 nul_terminate

9 safe_parse_maxint_with_suffix

10 substring

11 grub_strstr - see here - find string in string, returns @retval as position of string start, e.g. call Fn.11 0x6000 "$" || exit or call Fn.11 "%filefind%" "0" && echo found 0 in %filefind%

12 grub_strlen - e.g. call Fn.12 "freddy" ;; echo %@retval% - prints 6

13 grub_strtok - see here for definition e.g. echo 123,456,789 > (md)0x200+1 ;; call Fn.13 0x40000 "," ;; echo First string at %@retval% ;; call Fn.13 0 "," ;; echo Next string at %@retval% ;; cat --skip=262152 (md)0+0x201 (note: memory is changed by each call a 0 is inserted at the delimiting character position -> 123<0>456<0>789

14 grub_strncat - Appends the first num characters of source to destination, plus a terminating null-character

15 grub_strcmp compare two strings - e.g. call Fn.15 XXX XXX ;; echo %@retval% returns 0 (or -1 if no match)

16 grub_strcpy - copies string into destination

17 reserved

18 reserved

19 getkey - gets a kbd key from BIOS kbd buffer

20 checkkey- get kbd key without emptying buffer - e.g. call Fn.20 ;; set /A key=%@retval%&0xff > nul ;; echo %key% prints 0x31 if number 1 key pressed (top 8 bits is scancode, bottom 8 are ASCII code, Function keys are 'special') @retval returns -1 if no key available.

21

22 grub_memcmp

23 grub_memmove

24 grub_memset (32-bit) call Fn.24 0x83562 0xaa 0x100 - fill address 0x83562 in memory with byte 0xaa for size of 256 bytes. Note: address must be 32-bit value max 2GB if signed number (not 64-bit value)

25 grub_memset64 (64-bit) call Fn.25 0x83562 2 0xaa 0x12345678 0xA - fill 64-bit address 0x200083562 in memory (LSW MSW) with byte 0xaa for 64-bit size of 0xA12345678 bytes. Note: 64-bit values as unsigned 32-bit+32-bit (LSW MSW) New 2017-02 and later grldr only

26 grub_open - e.g. directly call grub_open file function it will put filesize at memory 0x8320 - this works well for non-compressed files too. call Fn.26 /myfile.gz ;; set /a filesize=*0x8320

27 grub_read

28 grub_close

29

30

31

32 devread

33 devwrite

34 next_partition

35 open_device

36 real_open_partition

37 set_device

38

39

40

41 parse_string

42 hexdump - e.g. call Fn.42 0x8000 0 3 - list 3 hex bytes at 0x8000

43 skip_to

44 builtin_cmd

45 get_datetime

46 find_command

47

48

49 get_mmap_entry

50 grub_malloc

51 grub_free51

52 list_partitions

53 realmode_run - executes a BIOS interrupt in real mode - e.g. see date.g4b batch file for example.

54 reserved for wee

55 reserved for wee

56 reserved for wee

57 reserved for wee

58 reserved for wee

59 reserved for wee

60 reserved for wee

61 dir - e.g. Fn.61 /dir/

62 print_a_completion

63 print_completions

64 lba_to_chs

65 probe_bpb

66 probe_mbr

67 unicode_to_utf8 e.g. call Fn.67 *0x82d0 0x60000 3 cat (md)0x300+1,1 | set nt_ver=NT

68 rawread

69 rawwrite

70 setcursor x (returns previous cursor state as %@retval%) e.g. call Fn.70 0 to disable cursor and splashimage, call Fn.70 3 && clear will keep the splashscreen visible in console mode (but scrolling doesn't work when the cursor gets to the bottom of the screen so must clear the screen when get to bottom!) - 1=show cursor and disable splashimage, 2=normal splashimage mode,

71 grub_tolower

72 grub_isspace

73 grub_sleep e.g. call Fn.73 3 to sleep for 3 secs

74 mem64

75 envi_cmd

76 strncmpx

77 rectangle (x y w h border_width) - e.g. call Fn.77 180 180 100 100 2 - values in pixels not lines. Can be called multiple times for multiple rectangles on screen.

78 get_cmdline

Internal variable memory locations used by grub4dos

When grub4dos loads into memory, it will store certain internal, local values at defined places within memory. Sometimes it is useful to read or write to these locations.

For instance, the grub4dos version number is held at location 0x8278 in memory. So you can check what version the user is running by adding these lines to your grub4dos menu.lst file:

checkrange 20120201:-1 read 0x8278 || pause --wait=3 Please use grub4dos-0.4.5c-2012-02-01 or later! && exit 1

This checks the value of 0x8278 to ensure it is between 20120201 (the date that the grub4dos version was made) and -1 (which is the largest possible number). If this check fails then the message is displayed with a 3 second countdown and then the menu is exited.

Some variable locations + examples are given in the lists below: Use latest version available or these may not work (they may be subject to change)!

Address Bytes (Type) Description (see source file asm.S for all definitions)

========= ============ ==========================================================================================

0000:8205 1 (BYTE) Control bits - default=0 (bit0=disable PXE, 1=disable kbd during boot, 2=disable unconditional cmd line entrance, 3=disable geometry tune, 4=dis cd-rom startup lookup, 5=enable HLT in checkkey idle loop for less CPU activity/heat)

0000:8208 4 (DWORD) install_partition (the boot partition) (??)

0000:8274 1 (BYTE) 00/01/02=Automatic numbering/boot title number only/number all titles

0000:8275 1 (BYTE) The character between the title number and the title content. Such as: 0x20=' ' 0x2d='-'

0000:8276 1 (BYTE) Current menu item number 0-255

0000:8278 4 (DWORD) grub4dos version - e.g. 20161109 = 0x133A255

0000:8280 4 (DWORD) boot_drive (the boot drive) set /A BD=*0x8280&0xf0 if %BD%==0x80 echo We booted from a hard disk!

0000:8284 4 (DWORD) pxe_yip (your ip)

0000:8288 4 (DWORD) pxe_sip (server ip)

0000:828C 4 (DWORD) pxe_gip (gateway ip)

0000:8290 8 (QWORD) filesize (file size by last "cat --length=0")

0000:8298 4 (DWORD) saved_mem_upper (extended memory size in KB)

0000:829C 4 (DWORD) saved_partition (current root partition) Only 2nd WORD is valid - use set /A PART=*0x829e&0xff (or set /a cur_pri=*0x829C>>16&0xFFFF)

0000:82A0 4 (DWORD) saved_drive (current root drive) set /a cur_drv=*0x82A0&0x7F

0000:82A4 4 (DWORD) no_decompression (no auto gunzip) 1=no decomp

0000:82A8 8 (QWORD) part_start (start sector of last partition)

0000:82B0 8 (QWORD) part_length (total sectors of last partition)

0000:82C0 8 (QWORD) saved_mem_higher (max contiguous mem in KB starting at 4G

0000:82CC 4 (DWORD) rdnum - rd device number

0000:82D0 8 (QWORD) rdbase - sector start address of rd

0000:82D8 8 (QWORD) rdsize - max. size in bytes of rd

0000:8304 4 (DWORD) ptr to current graphics mode - set /a n=23<<2+0x8304&0xffffffff ;; set /A n=%n%&0xffffffff ;; echo Current graphics mode is %n% (0=text mode)

0000:8308 1 (BYTE) left menu marker - e.g. right-arrow (default = 0x10 ►) of highlighted menu entry - 0x8308=0 removes both R and L markers.

grub4dos 0.4.6 only

000:8309 1 (BYTE) right menu marker - e.g. left-arrow for right-to-left aligned menu (default=0x11 ◀, 0 = disable). Note: 0x8308=0=disable.

2 (WORD) unused

0000:8330 4 (DWORD) debug value, e.g. 0, 1, 2, -1 (used for pause command) or 0x7FFFFFFF for fileystem debug mode

0000:8350 1 (BYTE) number of USB drive loaded by usb driver after usb --init command - e.g. 01 - read 0x8350 & 0xff - bit 7 = error

0000:8351 4 (BYTES) USB drive numbers loaded by internal g4d USB driver in order e.g. 0x808182

0000:8355 2 (DWORD) Base address of USB driver data area

0000:8360 Grub4dos code starts here - don't change anything past here!

set /a MEMSIZE=*0x8298 & 0xffffffff >> 10+1

set /a MEMSIZE1=*0x82c0 >> 10+1

set /a TMEM=%MEMSIZE% + %MEMSIZE1%

set /a TMEMG=%TMEM% / 1024

echo Total Memory = %TMEMG%MB

0x8217 current configfile path and name e.g. call Fn.0 0 0x8217

0x826C BSS start address (4 bytes)

0x8274 autonumber boot entries, byte 0 = enable/disable/all, byte 1 = character - e.g. use a hyphen after the number - write --bytes=2 0x8274 0x2d01 (0x2001=default on using space, 0x2000 = do not number menu entries)

0x8276 set /a CURDEF=*0x8276 & 0ff gets current menu item number as a variable CURDEF (0x8277 is not used) - old versions and 2018-08-21 and later

0x8278 check version of grub4dos - e.g. checkrange 20120201:-1 read 0x8278 || pause --wait=3 Please use grub4dos-0.4.5c-2012-02-01 or later! && exit 1

0x8280 Disk type - checkrange 0x80 read 0x8280 && pause --wait=3 I am Hard Disk 0

0x8290 Length of file in bytes -e.g cat --length=0 /myfile.iso ;; set /a LEN=*0x8290 ;; echo Length of file is %LEN% bytes (use if files could be over 4GB)

0x8298 maximum free memory in KB starting at 1M and below 4G (memory may NOT be contiguous) # calculate sizes in MB of iso and available memory - set /a MEMSIZE=*0x8298&0xFFFFFFFF>>10 ;; cat --length=0 /myfile.iso ;; set FSize=*0x8290>>20 ;; if %FSize%>=%MEMSIZE% echo Need More memory!

0x82BC CPU type - iftitle [checkrange 0,1 read 0x82Bc] 32bit system

0x82D0 rd_base (0x82D4 has high word) calc *0x82d0 | set mem=

0x82D8 rd_size

0x8320 to get the expanded length of a compresses .gz file - use: cat --length=1 /myfile.gz &; set /a filesize=*0x8320

# Or directly call grub_open file function it will put filesize at memory 0x8320 - this works well for non-compressed files too. call Fn.26 /myfile.gz &; set /a filesize=*0x8320

0x8328 filepos ptr

set /a IP1 = *0x8284 & 0xFF

set /a IP2 = *0x8285 & 0xFF

set /a IP3 = *0x8286 & 0xFF

set /a IP4 = *0x8287 & 0xFF

set YIP = %IP1%.%IP2%.%IP3%.%IP4%

0x307FF4 can be set to change the address of where grub4dos environment variables are stored (default value 0x45000) - e.g. write 0x307ff4 0x400000 && set * && set ?_BOOT=%?_BOOT%

0x307FF8 If set to 1, configfile command will load embedded menu in grldr instead of last configfile used, write 0x307ff8 1 && configfile

grldr 0.4.6

To patch grldr so that no diagnostic messages are displayed by it whilst it loads, patch byte at 0x2073 (takes affect after reboot)

# Silent startup of grub4dos - no diagnostic/info messages (only works for 0.4.6a) - permanently changes /grldr files

cat --locate=\x02 --replace=\x00 --skip=0x2073 --length=1 /grldr > nul

0x2073 BYTE 0=silent, 2=normal

For silent stage 1.5 booting, use new grubinst.exe and set silent switch.

#get current graphics mode

graphicsmode ;; set n=%@retval% ;; echo Current graphics mode is %n% (0=text mode)

New commands August 2014

write --bytes=1 0x8277 12 - writes a single byte to address 0x8277 (0x8277 is unused in grub4dos)

crc32 ()/fred.iso - calculates crc32 of file

Other internal system variables

Note: these are undocumented and they could change at any time! Use them only if you have to:

e.g.

read VAR.42 ;; set /A color_32=%@retval% ;; # e.g. 0x0B

read VAR.43 ;; set color_fg=%@retval% ;; # e.g. 5636095

VAR.13 - filesystem type ????

VAR.28 - graphics cursor

VAR.29 - menu border character?

VAR.42 - current color BG-FG as byte

VAR.43 - low 32-bits foreground

VAR.44 - low 32-bits background

VAR.45 - cmd line string

VAR.46 - splashimage loaded if not 0

Colours

The 0x8304 below could change - use VAR.xx instead!

Getting the current standard (console) text and background colours from internal system variables:

64-bit background colour - calc 44<<2 + *0x8304 ;; read %@retval% ;; set color_bg=%@retval%

64-bit text colour - calc 43<<2 + *0x8304 ;; read %@retval% ;; set color_fg=%@retval%

8-bit color if in textmode (first 4 bits are background, next 4 bits are text colour) - calc 42<<2 + *0x8304 ;; read %@retval% ;; set color_fb=%@retval%

See PrintMsg.zip for a batch file which prints a message in any colour but preserves the background colour. This works in both text mode and graphics mode and you don't need to know what the current text and background colours are.

echo $[0x04] Hello - prints text in red on black background

echo $[0004] Hello - prints text in red on current background (if in graphicsmode)

echo $[] Hello - prints text in the colours used before any colour change was applied by previous $[xxxx] strings

echo $[0xFF00FF] - prints text in magenta on current background

echo $[0xCCCC1100FF00FF] - prints magenta text on pale yellow background

e.g.

echo abc$[0x04]def$[]ghi

prints...

abcdefghi

Other useful stuff

use latest version of grub4dos for these features!

Debug n

debug 0 Off - No messages

debug 1 Normal - informational messages ouput

debug 2 On - Extra info

debug 3 May display more messages (?)

debug -1 Do not show timer countdown in pause --wait=x command

debug = 0x7FFFFFFF 'internal' debug messages displayed

debug msg=n (0.4.6 only)

0 ==> will disable all messages, even debug 2 level ones

1 ==> show stdmsg to stdout, warning and error messages to stderr

2 ==> show stdmsg and warning to stdout, error messages to stderr

3 ==> show all message to stdout

Single-Step through batch files

debug <batchfile> <args> - Single-Step through a batch file -

e.g. debug /grub/listfiles.g4b (hd0,1)/

[ENTER] - runs next line

Q - quit execution

C - return to the shell (ESC will continue execution where it left off)

S - Skip line (does not execute the current line)

E - End stepping - will run normally until the end of the batch file or until it hits a break point

B - Set Breakpoint - press B then enter a line number. Then press E and it will run and break at the line that was set.

N - Run until next function (e.g. next call command)

calc

If using calc in grub4dos, some operators may not work with spaces and some may work - e.g. calc 44|2 works, but calc 44 | 2 does not.

operators | % >> and << cannot have spaces, but * / - + ^ & work with spaces or without.

Note that ^ is XOR and | is OR and & is AND, << is shift left and >> is shift right, % is modulo (remainder).

key presses

To get a key press and act on it:

pause --test-key --wait=5

:: read BIOS key code location

set /A key=*0x4CB00

:: key has 0x01 if not key else key scan code

:: scancode for p is 0x1970 P is 0x1950

:: if p is pressed wait indefinitely for another key press

if %key%==0x1970 && pause --test-key && set /A key=*0x4CB00

:: Now jump to a routine to deal with key or do nothing if there is no label

debug off

goto :%key% || echo -n

:1

:: do stuff here if no key pressed

:0x3F00 F5

:: do stuff here for F5 keypress

# get the UUID of a volume containing the menu.lst file we used to run grub4dos (note: do NOT use && to join these lines!)

uuid (bd) > nul

set UUID=%?_UUID%

or you can use just one line:

uuid () && call set UUID=%^?%

Note that %^?% is used because %?% would be treated as a literal string and UUID would be set to %?% if ^? was not used!

^ anywhere inside a variable means do not translate to a variable - e.g. set a=%bb^bbb% returns a=%bbbbb%

Remove quotes from a string

set id="fred,doris"

call Fn.0 0 %id% | set id=

The ! operator

! can only be used after a && or || operator to mean 'else'

set e=9

if %e%>=10 if %e%<=90 && echo fred || echo doris Nothing is displayed as result of echo fred was true

if %e%>=10 if %e%<=90 && echo fred ! echo doris 'doris' is displayed as result of if 9>-10 if 9<=90 was false

New operators (chenal grub4dos versions April 2013 and later)

The && and || operators do not affect the environment until the whole line has been executed - e.g.

set a=1 && echo %a%

does not echo '1' because the environment is not updated until after the whole line has been executed. There are now three new operators:

;; - used to separate commands on the same line - e.g.

set a=1 ;; echo %a% ;; # This is a comment ;; set /a b=%a% + 1

&; - as ;; but the next command is only executed if the result of the previous command was true, e.g.

set a=menu.lst ;; if exist /%a% &; echo %a% exists

|; - as ;; but next command is only executed if the previous command was false.

BEWARE - When using the write command!

The syntax of write is:

write [--offset=SKIP] ADDR_OR_FILE INTEGER_OR_STRING

grub4dos will not allow writes to a compressed file. If grub4dos tries to read bytes from a compressed file it will try to decompress the file first.

If you are using an un-initialised memory area, you may find that the write to memory does not work on some systems or on some occasions.

e.g.

write (md)0x3000+1 fred

may not write the string 'fred' to the memory area - this is usually because grub4dos thinks that the memory area being used holds a compressed file (which may just be random bytes of memory!). To overcome this you can either temporarily turn off compressed file (gzip and lzma) support using 0x82a4 or ensure the start of the memory area does not contain random bytes, as follows:

# turn off compressed file support

write 0x82a4 1

write (md)0x3000+1 a string

write 0x82a4 0

or

# initialise memory first

echo ffffffffffffffffffffffffffff > (md)0x3000+1

write (md)0x3000+1 a string

equally cat --hex (md)0x3000+1 may not work if grub4dos thinks that the file is a compressed file and if it cannot decompress it.

cat --hex /fred.gz --- will list the decompressed contents (if fred.gz is a valid compressed file)

cat --hex (md)0x3000+1 --- will try to list memory, but if it thinks the contents contain a compressed file and grub4dos cannot decompress it then it won't list anything! So for listing bytes from memory, always use:

write 0x82a4 1

cat --hex (md)0x3000+1 - will always work!

write 0x82a4 0 - re-enable compressed file support

Note: read and write use 32-bit data values. calc will use 64-bit values - e.g. calc *0x82d0 | set mem=

An example of using @retval

debug -1 ;; pause --wait=10 Press a letter ;; set /A a=%@retval%+0 > nul ;; if %a%>=0x20 echo -e \%a:~1% was pressed (%a%)!

Press a letter (user presses d on keyboard)

d was pressed (0x64)!

(0x1 is returned by pause --wait=10 if no key is pressed within 10 seconds.)

cat --locate=fred (md)0x3000+1 will return @retval = count of number of instances found.

Note that nearly all commands will change the value of @retval, so copy it to a variable if you need to keep it, e.g.

cat --locate=fred (md)0x3000+1

set count=%@retval%

echo fred occurs %count% times

Mapping a new hard disk

(hd) is the next available (free) harddisk number not yet recognised by the BIOS. (hd-1) is the last BIOS harddisk.

e.g. If you have 2 hard disks in the system (hd0 and hd1), then ...

map /fred.img (hd) - maps fred.img to the next available hard drive number (hd2) and increases BIOS harddrive count by 1

ls (hd-1,0)/ - lists files on the last hard drive (hd1,0)

map /doris.img (hd) - maps doris.img to the next available hard drive number (hd3) and increases BIOS harddrive count by 1

ls (hd-2,0)/ - lists files on the first hard drive (hd0,0)

map --hook - make BIOS mappings take effect

root (hd-1,0) - makes the last available hard disk first partition (in doris,img) the root

ls (hd-1,0)/ - lists files in doris.img partition 0

ls (hd3,0)/ - lists files in doris.img partition 0

Equivalent of dd if=/dev/zero to fill a file with zeros or spaces

If you want to fill a file with the same character or 00 byte, you can fill an area of memory using the memset function first and then use it as below:

map (md)0x300+200 (rd) > nul ;; read 0x82d0 > nul ;; call Fn.24 %@retval% 0x00 102400 > nul dd if=(rd)+1 of=()/ABC.xml

# fill (rd) with 0's , 0x82d0 is rd-base mem address, Fn.24 is memset - fill memory <addr> <string> <size> - 200 sectors = 102400 bytes

If you are not sure how large the target file is, then make the (md) area larger than the file.

To fill with spaces, substitute 0x00 with 0x20.

Note: Fn.24 memset takes only 32-bit address and may fail if address is > 32bit (0x82d4 has high word of address - better to use Alternate fill below)

Can get 64-bit value of rd address using calc *0x82d0 | set mem=

but Fn.24 only accepts a 32-bit address.

Fn.25 can be used to fill memory above 4GB if required.

To fill an area of memory (e.g. 10*512=5120 bytes) with 0's the following trick can be used (grub4dos will fill 0xa000 with 0's even though if test fails):

if 1=2 echo never > (md)0xa000+10

# or alternatively...

echo -n > (md)0xa000+10

# Alternative fill with any byte - fill 100K bytes with spaces

echo -n > (md)0x300+200 ;; cat --locate=\x00 --replace=\x20 (md)0x300+200 > nul

# Alternative fill with any byte and set as (rd)

echo -n > (md)0x300+200 ;; cat --locate=\x00 --replace=\x20 (md)0x300+200 > nul ;; map (md)0x300+200 (rd) > nul

# Alternate way to fill memory - (md)0x300+200 is memory at 0x60000 for 102400 bytes

# First fill memory at (md)0x300 with byte 0x32

call Fn.24 0x60000 0x32 102400

# Now copy that memory to (rd) into upper memory

map (md)0x300+200 (rd)

# Memory at (md)0x300 can now be used for something else and memory at (rd) is not changed

# you can use (rd)+1 for all 102400 bytes in (rd)

Checking if directories\folders exist

You cannot use:

if exist /EFI echo Directory EFI Exists

because this does not work on FAT volumes.

Use

ls /EFI/ > nul && echo EFI exists

ls /EFI/ > nul || echo EFI does not exist

Use this to check for folders which exist and are NOT empty:

ls /EFI/ > (md)0x9F00+1 && checkrange 1:-1 read 0x13E0000 > nul && Directory \EFI Exists

ls /EFI/ > (md)0x9F00+1 && checkrange 1:-1 read 0x13E0000 > nul || Directory \EFI does not exist

In a .mnu file we can use this to check for the existence of a folder or non-existence:

iftitle [ls /EFI/ > nul] EFi exists

...

iftitle [ls /EFI/ > nul ;; if %@retval%==0] EFI does not exist

...

Redirection

Sometimes echo will not redirect correctly to a file or memory when it contains a $[xxxx] string which is translated by grub4dos as a colour setting...

echo $[0200]fred > (md)0x300+1

OR

echo -e \$[0200]fred > (md)0x300+1

OR

echo -e \x24\x5b0200]fred > (md)0x300+1

will only put 'fred' in memory and the $[0200] will not be redirected.

To circumvent this do not to use echo - e.g.

pause --wait=0 $[0200]fred > (md)x0300+1

or

call Fn.0 0 $[0200]fred > (md)x0300+1

write (md)0x300+1 $[0200]fred

or

write (md)0x200+1 $[0200]fred\0

cat (md)0x200+1 > (md)0x300+1

or

echo -e \$[0200]fred > (md)0x300+1

Redirection using > and parsing gotchas!

When grub4dos finds a > symbol, it will open a file handle to the specified file, clear it's contents if it already exists and then redirect all standard output into it.

So

echo fred > (md)0xa000+4

will clear 4 sectors in memory (2048 bytes) and then place the text fred in the first 4 bytes.

Also note that due to the way a command line is parsed, the following lines will always fill 2048 bytes of memory at (md)0xa000 with 0's even though fred will never be echo'd

set A=1

if %A%=2 echo fred > (md)0xa000+4

this can be fixed by using a ;; or && command to force the parser to execute each section separately:

if %A%=2 && echo fred > (md)0xa000+4

This also applies to redirection to a file - e.g. if 1==2 echo fred > /myfile.tst will always clear the file even though the test is false.

Tip: To fill a file or memory area with 00's use:

if 1==2 echo never > (md)0x300+0x20

OR

if 1==2 echo never > /myfile.txt

Delays and user input

# user must press a key

pause here is a prompt && echo Always works unless ESC is pressed

# user must press a key

set /p A=here is a prompt && echo always works unless ESC or ENTER is pressed

# user must press a key within 3 seconds - delay shortened if just press ENTER or ESC

set /p:3 A=press a key || echo A has no value - timeout or Enter was pressed

set /p:3 A=press a key && echo A has a value - user entered one or more characters followed by ENTER

# always delay

echo Please wait 3 seconds... && call Fn.73 3 || echo This always prints after 3 seconds

Prompt with timeout in colour (grub4dos batch file)

Here is an example of how to change the text colour temporarily (until echo -n is used).

Instead of set /p:%TIME% ask= you could use password fred and then both the Password: prompt and the ***'s printed would be in the pre-set colour.

ColInput.g4b

!BAT

# Get a string from user after prompting in coloured text

# Syntax: ColPrompt <colour> <"prompt"> [timeout in secs]

# Returns: %ask% = user input string

#

# Usage: ColPrompt.g4b 5 "Please\x20Input\x20Password\x20:\x20" 3

# Usage: ColPrompt.g4b 14 "Please Input Password : "

# Usage: ColPrompt.g4b 1

# Colors = 0=black, 1=blue, 2=green, 3=l cyan, 4=red, 5=magenta, 6=l brown, 7=grey, 8=d grey, 9=l blue, 10=l green, 11=cyan, 12=l red, 13=l magenta ,14=yellow ,15=white

setlocal

set ask=

set TIME=

set COL=%1

if not exist COL set COL=7

set T=%2

if not exist T set T="Password:\x20"

# remove quotes and translate \x20 etc.

call Fn.0 0 %T% | set T=

if not "%3"=="" set TIME=:%3

# set up equivalent 64-bit values for colors 0-F

set color4=0x000000;0x0000AA;0x00AA00;0x00AAAA;0xAA0000;0xAA00AA;0xAA5500;0xAAAAAA;0x555555;0x5555FF;0x55FF55;0x55FFFF;0xFF5555;0xFF55FF;0xFFFF55;0xFFFFFF;

set /a tmp=42<<2+*0x8304&0xffffffff > nul

set /A color32=%tmp% > nul

set /A color64=%tmp%+4 > nul

# color32 is the location to write 32-bit color value - e.g. 0x0d

# color64 is the location to write 64-bit color value - e.g. 0xFF55FF

call :set_color %COL%

set /p:%TIME% ask=%T%

::restore colors with echo -n

echo -n

endlocal && set ask=%ask%

goto :eof

:color4_32

set /A %2=%color4:~%1,8% > nul

goto :eof

:color_8_64

setlocal

set /a b=%1>>4*9 > nul

set /a f=%1&0xf*9 > nul

call :color4_32 %b% b

call :color4_32 %f% f

endlocal && set /A %2=%b%<<32|%f% > nul

goto :eof

:set_color

setlocal

call :color_8_64 %1 c64

write %color32% %1 > nul

write --bytes=8 %color64% %c64% > nul

goto :eof

Get a password

Allows use of coloured text for the prompt and any text (e.g. non-English prompt) and shows cursor.

No timeout feature.

!BAT

:: get password and print * for each character

:: returns password in %pass%

:: Usage: getpass.g4b [prompt]

:: e.g. getpass.g4b $[0104]Please\x20enter\x20password:\x20

:: set cursor on and keep last state

call Fn.70 1 ;; set CUR=%@retval%

if "%1"=="" call :get_pass pass Password:\x20 || call :get_pass pass %1

:: restore cursor state

call Fn.70 %CUR% && set CUR=

:: echo Pass: [%pass%]

goto :eof

:get_pass

setlocal

set baseaddr=0x60000

set offset=%baseaddr%

echo -e -n %2

call :getchar %1

echo

write %offset% 0 > nul

endlocal && call Fn.0 0 %baseaddr% | set %1=

goto :eof

:getchar

call Fn.19

set /A key=%@retval% > nul

if %key%==0x1C0D goto :eof

if %key%==0x11B goto :Esc

::Support backspace

if %key%==0xE08 goto :Back

write --bytes=1 %offset% %key% > nul

set /A offset=%offset%+1 > nul

echo -n *

goto %0

:Back

if %baseaddr%==%offset% goto %0

set /A offset=%offset%-1 > nul

call Fn.4 ;; set /A r=%@retval% > nul

set /a x=%r%>>8 > nul

set /a y=%r%&0xff > nul

#echo %x% %y%

if not %y%<=0 set /a y=%y%-1 > nul

#echo %x% %y%

call Fn.5 %y% %x%

echo -n -e \x20

call Fn.5 %y% %x%

goto %0

:Esc

set offset=%baseaddr%

goto :eof

%pass% has user input

password without echoing anything to screen

With timeout if no user entry

echo -n -e \x09\<- Enter Password && set /p:10 ask=xxx > nul

Check if unifont loaded

location 0x1800820 will be 0 if unifont was never loaded

read 0x1800820 && echo unifont loaded

Vol

grub4dos 0.4.6a 2017-06-xx now supports the vol command

vol - lists all partitions with volume name and filesystem type and ptn type

vol kali linux - sets current root to whatever device has label of kali linux (not case sensitive)

echo %@root% - display current root - e.g. (0xff) or (hd1,0)

vol (hd1,0) ;; echo %?% - get and display volume name and echo volume name

Joliet ISOs/CDs have a Primary Volume Descriptor (32 ASCII) or an Enhanced Volume Descriptor (32 unicode).

Linux and grub2 always use the Primary Volume Descriptor in a ISO file or CD\DVD.

vol --primary (0xff)

For use in linux boot parameter lines (cheat codes), volume names must not contain spaces.

Note: volume names are case insensitive

Get 16-character UUID and full 32-character volume label of ISO

There is no official UUID for ISOs, but linux uses the 'modified date' field as the UUID.

map %ISO% (0xff)

map --hook

root (0xff)

#get UUID from ISO

cat --skip=813 --length=4 (0xff)16+1 | set ID=

cat --skip=817 --length=2 (0xff)16+1 | set ID=%ID%-

cat --skip=819 --length=2 (0xff)16+1 | set ID=%ID%-

cat --skip=821 --length=2 (0xff)16+1 | set ID=%ID%-

cat --skip=823 --length=2 (0xff)16+1 | set ID=%ID%-

cat --skip=825 --length=2 (0xff)16+1 | set ID=%ID%-

cat --skip=827 --length=2 (0xff)16+1 | set ID=%ID%-

echo UUID=%ID%

#get Label from ISO

cat --skip=0x28 --length=32 (0xff)16+1 > (md)0x300+1

cat --locate=\x20\x20 --replace=\x00\x00 (md)0x300+1 > nul

cat --locate=\x20\x00 --replace=\x00\x00 (md)0x300+1 > nul

cat (md)0x300+1 | set ISO_LAB=

echo LABEL=%ISO_LAB%

#use grub4dos 0.4.6a 2017-06-xx or later

uuid (0xff)

set ID=%?%

echo UUID=%ID%

vol --primary (0xff)

set ISO_LAB=%?%

echo LABEL=%ISO_LAB%