Batch file DOS systems programming via Peek.com
Posted: 16 Mar 2015 18:02
After I wrote my Batch-Assembler, I reviewed some of my very old 16-bits assembly .COM programs. One of those programs is so special that I decided to "revive" it. PEEK.COM is a versatile program that can provide Batch files with a wide range of useful information, because it allows to have access to specific memory locations, or to execute a limited number of PC BIOS interrupts or MS-DOS functions.
PEEKdoc.bat Batch file below creates PEEK.COM and it is also an example program of its use. It show a description of PEEK.COM using the several video pages that the video card provides for that purpose.
EDIT 2015/04/08: I added the video scroll functions (INT:10:06 and INT:10:07) to PEEK.COM features. The code below generate the new PEEK.COM version.
This is MySet.bat:
This is MCB-Chain.bat:
PEEK.COM program is useful to explore some MS-DOS systems programmings concepts in a very simple way with Batch files, instead of using C or assembly language. For example, this is the output of previous MCB-Chain.bat file:
Below there is a list of some web sites that provide further information about all this stuff:
I suggest you to review the "Batch Control Block" in Tables and Formats of the first link listed above. It represents a whole new research world that is directly related to Batch files! I think that the BCB is the 128-bytes data block of DOSX program that appears at segment D52E in previous MCB-Chain.bat output.
I tested these programs in an old Windows-XP computer and also in a modern Windows 8.1 one. I erroneously thought that old MS-DOS .COM programs will not run in modern Windows versions, but the .COM file format is still supported in any Windows computer that have a 32-bits CPU don't matters how modern it is, and there are a lot of them nowadays. This means that it is not time to abandon MS-DOS .COM programs yet!
Antonio
NOTE: PEEK.COM does not check the values provided to the INT's, so the program may fail if the values are outside of the valid ranges; for example, if you use INT:10 and try to move the cursor or show text outside of the screen limits.
PEEKdoc.bat Batch file below creates PEEK.COM and it is also an example program of its use. It show a description of PEEK.COM using the several video pages that the video card provides for that purpose.
EDIT 2015/04/08: I added the video scroll functions (INT:10:06 and INT:10:07) to PEEK.COM features. The code below generate the new PEEK.COM version.
Code: Select all
@echo off
rem PEEKdoc.bat: Create PEEK.COM program and show a menu-driven description of its use
rem Antonio Perez Ayala
setlocal EnableDelayedExpansion
if "%~1" equ ":getClickPos" goto %1
cd "%~P0"
if not exist PEEK.COM call :CreatePEEK.com
set LF=^
%empty line 1/2 don't remove%
%empty line 2/2 don't remove%
set "spaces="
for /L %%i in (1,1,80) do set "spaces= !spaces!"
set "menu= 1.PEEK 2.Video 3.Mouse 4.DOS 5.PSP 6.SYSVRS 7.MCB exit"
set ESC_KEY=27
set HOME_KEY=71
set LEFT_ARROW=75
set RIGHT_ARROW=77
set END_KEY=79
rem Load the descriptions into 7 video pages of 80x25 characters
title PEEK.COM Program Documentation
mode con: cols=80 lines=25
set /P "=Please wait, loading page: 1 " < NUL
for /F "delims=:" %%a in ('findstr /N /R /C:"^Page 1" "%~F0"') do set "skip=%%a"
set /A page=1, nextPage=page+1
rem Move cursor of page 1 to home
PEEK INT:10:02:1:0000 > NUL
for /F "skip=%skip% tokens=1* delims=:" %%a in ('findstr /N "^" "%~F0"') do (
if "%%b" neq "Page !nextPage!" (
set "line=%%b%spaces%"
PEEK INT:10:09:!page!1F:"!line:~0,80!!LF!"
) else (
rem This page ends: show the menu at bottom with this option selected
set /A "this=page*10-5,rest=this+10"
for /F "tokens=1-2" %%i in ("!this! !rest!") do (
PEEK INT:10:09:!page!1F:"!menu:~0,%%i!":!page!0E:"!menu:~%%i,10!":!page!1F:"!menu:~%%j!"
)
rem Small adjustment in page 3's menu
if !page! equ 3 (
PEEK INT:10:02:03:184C > NUL
PEEK INT:10:09:031F:" "
)
rem Move the cursor at this menu option and leave it there
call :DecToHex this
PEEK INT:10:02:!page!:18!this:~-2! > NUL
set /A page+=1, nextPage+=1
if !page! equ 8 goto :@F
rem Move cursor of next page to home
PEEK INT:10:02:!page!:0000 > NUL
set /P "=!page! " < NUL
)
)
:@F
echo/
rem Change the display page via menu-driven input keys
set page=1
:nextPage
PEEK INT:10:05:%page% > NUL
if %page% neq 3 (
PEEK INT:21:0C:07 > NUL
) else (
call :MouseSelection
)
set key=%errorlevel%
if %key% neq 0 (
if 49 leq %key% if %key% leq 55 set /A page=key-48
) else (
PEEK INT:21:07 > NUL
set key=!errorlevel!
if !key! equ %HOME_KEY% (
set page=1
) else if !key! equ %LEFT_ARROW% (
if %page% gtr 1 set /A page-=1
) else if !key! equ %RIGHT_ARROW% (
if %page% lss 7 set /A page+=1
) else if !key! equ %END_KEY% (
set page=7
)
)
if %key% neq %ESC_KEY% goto nextPage
PEEK INT:10:05:0 > NUL
cls
goto :EOF
rem Example of mouse use
:MouseSelection
setlocal
:getClick
for /F "tokens=1,2 delims=:" %%a in ('cmd /D /Q /C "%~F0" :getClickPos') do (
set /A "col=%%a/8, row=%%b/8, colH=col, rowH=row"
)
rem Get the character at mouse click position
call :DecToHex colH
call :DecToHex rowH
PEEK INT:10:02:03:%rowH:~-2%%colH:~-2% > NUL
for /F "delims=" %%a in ('PEEK INT:10:08:03') do set "char=%%a"
rem Show position and character of mouse click
set "pos=%row%,%col% "
PEEK INT:10:02:03:154A > NUL
PEEK INT:10:09:031F:"%pos:~0,5%"
PEEK INT:10:02:03:164B > NUL
PEEK INT:10:09:031F:"%char%"
if %row% neq 24 goto getClick
if %col% lss 5 goto getClick
if %col% gtr 74 goto getClick
set /A "selection=(col+5)/10+48"
exit /B %selection%
rem Wait for mouse button click and return its position
:getClickPos
PEEK INT:33:00 > NUL
for /L %%# in () do (
ping -n 1 localhost > NUL
PEEK INT:33:03 > NUL
if errorlevel 1 PEEK INT:33:03 /D & exit
)
rem Convert a decimal number to a 4-digits (word) hexadecimal one
:DecToHex var
setlocal EnableDelayedExpansion
set "hexDigit=0123456789ABCDEF"
set "dec=!%1!"
set "hex="
for /L %%i in (1,1,4) do (
set /A "nibble=dec&0x0F, dec>>=4"
for %%h in (!nibble!) do set "hex=!hexDigit:~%%h,1!!hex!"
)
endlocal & set "%1=%hex%"
exit /B
rem Description of PEEK.COM program by pages
Page 1
1. Parameters and switches of PEEK.COM
PEEK seg:ofs {/B|/W|/D}|[n [/L]] [/H]
This program takes information from the memory address given by SEG:OFS params
and show it as a number of /Byte, /Word or /Doubleword size if anyone of these
switches are given; or as a character, or a string of length N, or until find a
zero delimiter if N is zero. If /L switch is also included, the length of the
string is displayed instead of its contents. A /Doubleword number is displayed
in two word parts this way: HIGH:LOW. /H switch show the result in hexadecimal.
The low byte of the result is also returned in ERRORLEVEL value.
SEG parameter may be `BIOS` word. This is a list of some useful BIOS values:
BIOS:4A /W Number of columns in the text screen.
BIOS:62 /B Current active display page (see INT:10:05:p in 2.Video).
BIOS:84 /B Number of rows in the text screen minus one.
BIOS:85 /W Height of the current text font characters in pixels.
If SEG parameter is `INT`, execute an Int this way: PEEK INT:int:fun:sub:data
as described below. Note that all parameters must be given in hexadecimal base.
Press: Home <-- digit --> End Esc
Page 2
2. Screen management via INT:10
INT:10:02:p:rrcc Set cursor position of page P at row RR column CC (base 16).
INT:10:03:p /W Read cursor position of video page P (/H = RRCC).
INT:10:05:p Change active display page (max=3 or 7, see: BIOS:62 /B).
INT:10:06:ttll:bbrr Scroll the window delimited by TTop, LLeft, BBottom, RRight
INT:10:07:ttll:bbrr (base 16) up in function 06, or down in function 07.
INT:10:08:p /W Read character and attribute from video page P (/H = AACC).
INT:10:09:ppaa:`s` Show string `S` with attribute AA in video page PP (base 16)
several ppaa:`text` may be included: 10:09:1E:`One`:3C:`Two`
In functions 06/07 the number of lines may be given after last coordinate; the
attribute for new lines is taken from BBRR (function 06) or TTLL (function 07).
In the string of function 09, back-quote characters (Ascii 96) will be changed
by quotes, and an embedded LF (Ascii 10) will advance the cursor to new line.
Each video page store a complete text screen. The number of available pages
depends on the number of lines in the screen: 25 lines = 8 pages (0-7), 43/50
lines = 4 pages (0-3). A non-visible video page may be filled with text via
INT:10:09 and then displayed via INT:10:05. Don't use video page 0 this way.
Press: Home <-- digit --> End Esc
Page 3
3. Mouse management via INT:33
INT:33:00 /W Initialize mouse, return number of buttons (2 or 3);
the mouse must be initialized before used.
INT:33:03 /W Button pressed status: 1=left, 2=right, 4=center.
INT:33:03 /D Mouse pointer position in X:Y order in pixels (divide by 8).
REM INITIALIZE MOUSE
PEEK INT:33:00 > NUL
REM WAIT FOR A MOUSE BUTTON PRESSED
:WAIT_FOR_CLICK
PEEK INT:33:03 > NUL
IF %ERRORLEVEL% EQU 0 GOTO WAIT_FOR_CLICK
REM GET THE MOUSE POSITION AND SHOW IT
FOR /F `TOKENS=1,2 DELIMS=:` %%A IN ('PEEK INT:33:03 /D') DO (
SET /A `COL=%%A/8, LINE=%%B/8`
)
ECHO Mouse button pressed at line,col: %LINE%,%COL%
For a further example, see the code of this program. Button click:
line,col =
character =
Select the desired option via a left-button mouse click:
Page 4
4. Values returned by DOS via INT:21
INT:21:01 [/B] Keyboard input with echo (0 = first byte of extended key).
INT:21:07 [/B] Direct keyboard input with no echo (0 = same as above).
INT:21:0B /B Keyboard status: 255 = key ready, else = no key or EOF.
INT:21:0C:fun [/B] Flush input buffer, execute function FUN (must be 1 or 7).
INT:21:2A /D Current date in system clock (/H = YYYY:MMDD).
INT:21:2C /D Current time in system clock (/H = HHMM:SSCC).
INT:21:30 /W MS-DOS version (/H = RRVV).
INT:21:44:09:d /W Test disk unit D (1=A,2=B,...): 0 = not valid, else = valid.
INT:21:44:60:d /W Type of disk in unit D. /H = MUDD:
M- Media type: 0 = removable, 1 = fixed.
U- Unit type: 0&1 = 5 1/4, 2,7&8 = 3 1/2, 5 = Hard disk.
DD- Disk type: FD=360K, F9=1.2M/720K, F8=fixed, F0=1.44M.
INT:21:51 /W /H Segment address of PEEK.COM's PSP program.
INT:21:52 /D /H SEG:OFS address of SYSVARS data structure.
INT:21:07 is used in this documentation program to read cursor control keys.
The PSP is described in next page.
SYSVARS is described in page 6.
Press: Home <-- digit --> End Esc
Page 5
5. The Program Segment Prefix (PSP)
The PSP is a 256-byte area placed at beginning of the memory block of all
running programs. It contains several data about the program, like these items:
psp:16 /W /H Segment address of parent's PSP (i.e. COMMAND.COM).
psp:2C /W /H Segment address of the Environment block of this program.
psp:80 /B Length of the parameters given to run this program.
psp:81 len Parameters (command-line) given to run this program.
The Environment block is a memory area that store variables in this format:
var1=value1_
var2=value2_
...
varN=valueN__
Word count of how many strings follows (always 1)
Path where the running program was loaded from_
The underscore indicate a byte with binary zero. MySet.bat file is an example
program that show the environment values of PEEK.COM running program.
Press: Home <-- digit --> End Esc
Page 6
6. The SYSVARS DOS data structure
Function 52H of Int 21H returns the address of SYSVARS (see page 4.DOS). This
is an area used by DOS to store several data, like the address-1 of the first
allocated memory block, that is stored at SYSVARS base address minus 2:
FOR /F `TOKENS=1,2 DELIMS=:` %%A IN ('PEEK INT:21:52 /D /H') DO (
SET SYSVARS_SEG=%%A
SET SYSVARS_OFS=%%B
)
SET /A SYSVARS_OFS_MINUS2=0x%SYSVARS_OFS% - 2
CALL :DECTOHEX SYSVARS_OFS_MINUS2
FOR /F %%A IN ('PEEK %SYSVARS_SEG%:%SYSVARS_OFS_MINUS2% /W /H') DO (
SET /A FIRST_BLOCK=0x%%A + 1
)
CALL :DECTOHEX FIRST_BLOCK
The address of the first allocated block is used to traverse the MCB chain, as
described in next page.
Press: Home <-- digit --> End Esc
Page 7
7. The Memory Control Block (MCB)
In the 16 bytes previous to all memory blocks there is a Memory Control Block
(MCB or `arena header`) that contain information about the block.
All MCB's in the system form a chain that starts at the first allocated block,
previously described. The data in the MCB of any block may be accessed via its
segment and `MCB` word in OFS parameter, plus an additional MCB offset.
seg:MCB:0 Identifier of each block in the chain: `M`=valid, `Z`=last.
seg:MCB:1 /W /H PSP segment of the program owner of this block.
seg:MCB:3 /W Size of this block in paragraphs (16 bytes).
seg:MCB:8 8 Name of program owner of this block (only in PSP segments).
Free memory blocks have 0 as owner, and blocks that belongs to DOS have an 8.
The first block allocated for DOS is further divided in sub-blocks with the
same MCB structure that store the additional data loaded when CONFIG.SYS file
is processed, like device drivers or the area required for FILES=. For further
information see MCB-Chain.bat file, that is an example program that walks
through the MCB chain and display diverse data about the blocks.
Press: Home <-- digit --> End Esc
Page 8
:CreatePEEK.COM
echo Please wait, creating PEEK.COM program
rem Create PutBytes.com auxiliary program, if not exists
if exist PutBytes.com goto :@F
setlocal DisableDelayedExpansion
set LF=^
%empty line 1/2 don't remove%
%empty line 2/2 don't remove%
< NUL (
set /P "=ë0¬<"tZ^<'tV^<0rG^<9wC,0Šàë,Í!€þ,tâë4ŠðŠÔŠç€úÿ"
setlocal EnableDelayedExpansion
set /P "=tì€ìüëç³q€ëd·f€ïd2ä°‚‹ðü뽬<0rØ<9wÔ,0Õ!LF!"
endlocal
set /P "=Šàëï3ÀÍ!¬<,t£ëõŠð¬:Ætò:ÃtêŠÐŠçÍ!ëï"
) > PutBytes.com
endlocal
:@F
for /F "delims=:" %%a in ('findstr /N /R /C:"^PEEK.COM" "%~F0"') do set "skip=%%a"
(for /F "skip=%skip%" %%a in ('findstr "^" "%~F0"') do (
set "lineHex=%%a"
set "lineDec="
for /L %%i in (1,1,32) do if defined lineHex (
set /A "byte=0x!lineHex:~0,2!"
set "lineDec=!lineDec!,!byte!"
set "lineHex=!lineHex:~2!"
)
PutBytes !lineDec:~1!
set /P "=." < NUL > CON
)) > PEEK.com
exit /B
PEEK.COM Object code in hexadecimal
e9f50053796e7461783a205045454b207365673a6f6673205b6e205b2f4c5d5d
20207c20207b2f427c2f577c2f447d20205b2f485d0d0a202020202020202050
45454b20494e543a696e743a66756e3a7375623a6461746124496e76616c6964
205345473a4f465320616464726573730d0a24496e76616c696420494e542070
6172616d65746572730d0a24496e76616c696420706172616d65746572287329
0d0a2400000000000000000100ffff42494f53494e543a4d43423a0000000000
0000000000000000000000000000000000000210031005100610071008100910
00330333012107210b210c212a212c2130214421512152218a0e800032ede30b
8d3e8100b020fcf3ae750d528d160301b409cd215ae97304817dff2f3f74ec4f
418bf78bd732e4ac3c227505f6d4eb0f900ae4750a3c6172063c7a77022c20aa
e2e50ae47403e956018bf28d3eaf01b90400f3a67506acba4000eb318bf28d3e
b301b90400f3a67507800ebb0101eb028bf2e81d0474168d165901f606bb0101
74048d167301b409cd21e9fe038916a5013c3a75e2f606bb010175158bd68d3e
b701b90400f3a67506ff0ea501eb028bf2e8de0375c18916a301f606bb010174
403c3a753ce8ca0375b88916a7013c3a752f803c227514468936be0133c9ac41
3c2275fa49890ebc01eb4ee8a40375928916a9013c3a7509e8970375858916ab
013c0d74343c207403e96bffac3c2074fb3c0d74243c2f7522acb4103c42740c
b4203c577406b4403c4475610826bb01ac3c2074fb3c0d754feb713c3072603c
39775c2c308ad0ac3c30720e3c39770a2c308ae2d50a8ad0ebed32f68916ad01
3c0d74483c207537ac3c2074fb3c0d743b3c2f752aac3c4c7513800ebb0102ac
3c2074fb3c0d74243c2f7513ac3c48750e800ebb0180ac3c2074fb3c0d740d52
8d168c01b409cd215ae9df02f606bb01017418f606bb0170750e833ead01ff75
def606bb010275d7e9e800c43ea301f606bb01707450268b05a3bc01268b4502
a3be018cc0f6c4f0750fb104d3e003c73d710475042680257f8ad0f606bb0170
7439f606bb0140740ca1be01e8a802b23ab402cd21a1bc01f606bb0110740232
e4e89302eb27800ebb01108b0ead0183f9ff751b268a158816bc01f606bb0180
7506b402cd21eb5e8ac2e88502eb570bc97501498bd9f606bb0102741932c0f2
ae7501412bd9891ebc018026bb01ef800ebb0120eb83268a15470ad2741cf606
bb01807506b402cd21eb0d518ac2e84102b220b402cd2159e2dc2bd90aff7402
b3ff881ebc01528d16a001b409cd215ae9d8018a26a501a0a3018d3ed201b913
00f2af75eba3ad013d061074053d07107541b40fcd10b403cd10528b16a90180
3ea3010674048b16a701b402cd10b408cd108adc5ab402cd10b410a0a3018afb
8b0ea7018b16ab018916a7018b16a901e9c6003d091075668b1ea701b403cd10
b901008b36be01833ebc01007421ac3c0a750632d2fec6eb0c3c607502b022b4
09cd10fec2b402cd10ff0ebc0175df46ac3c3a7526e83a0175218916a7013c3a
7519803c227514468936be0133c9ac413c2275fa49890ebc01eb9de90d013d02
10750b8a3ea7018b16a901eb4c903d031075078a3ea701eb40903d081075078a
3ea701eb349080fc337507a3a70132c0eb273d442175228b1ea901833ea70109
7417833ea701607403e9bf00c706a7010d00b960088d16c0018826e3058ae0a0
a701cd007316803eae0121750f803ead0144720833c033c933d2eb78803eae01
10750b803ead010375048bc2eb66803eae0133750f803ead010075040bc07454
8bc3eb50813ead0144217530833ea7010975068bc20c01eb3ba1c2012401b10c
d3e00a26c10150c606c00101b96008b80d44cd2158721da0d101eb18813ead01
512175048bc3eb0c813ead01522175048cc18bd3f606bb01407505a3bc01eb08
890ebe018916bc01e96efda0bc01b44ccd21b9040133d2ac3c3072183c39760a
3c4172103c46770c2c072c30d3e20ad032edebe30aedc3f606bb0180751433c9
bb0a0033d2f7f380c23052410bc075f3eb2cb90010f606bb01107402d0ed8bd0
d3ea80e20f80c23080fa39760380c2075280c1043acd75e6d0e9d0e932ed5ab4
02cd21e2f9c3
This is MySet.bat:
Code: Select all
@echo off
setlocal
rem MySet.bat: Show the values in the environment of PEEK.COM program
rem Antonio Perez Ayala
rem Define local variables with same sizes, so the environment don't change
set PSP=1234
set ENV=1234
set OFS=0000
rem Get the segment of PEEK.COM's PSP
for /F %%a in ('PEEK INT:21:51 /W /H') do set PSP=%%a
rem Get the segment of PEEK.COM's environment
for /F %%a in ('PEEK %PSP%:2C /W /H') do set ENV=%%a
:showVar
rem Show this variable
PEEK %ENV%:%OFS% 0
rem Advance offset to next variable
for /F %%a in ('PEEK %ENV%:%OFS% 0 /L') do set /A OFS=0x%OFS%+%%a+1
call :DecToHex OFS
rem If there is another variable, go back for it
for /F %%a in ('PEEK %ENV%:%OFS% /B') do if %%a neq 0 goto showVar
rem Advance offset to PEEK.COM's path and show it
set /A OFS=0x%OFS%+3
call :DecToHex OFS
echo/
echo PEEK.COM's path:
PEEK %ENV%:%OFS% 0
goto :EOF
rem Convert a decimal number to a 4-digits (word) hexadecimal one
:DecToHex var
setlocal EnableDelayedExpansion
set "hexDigit=0123456789ABCDEF"
set "dec=!%1!"
set "hex="
for /L %%i in (1,1,4) do (
set /A "nibble=dec&0x0F, dec>>=4"
for %%h in (!nibble!) do set "hex=!hexDigit:~%%h,1!!hex!"
)
endlocal & set "%1=%hex%"
exit /B
This is MCB-Chain.bat:
Code: Select all
@echo off
setlocal EnableDelayedExpansion
rem MCB-Chain.bat: Walk through the DOS Memory Control Blocks chain and show data about the blocks
rem Antonio Perez Ayala
rem Initialize the array of sub-block types in DOS Data Segment
set type[B]=BUFFERS
set type[C]=BUFFERS /X
set type[D]=DEVICE
set type[E]=DEVICE Additional
set type[F]=FILES
set type[I]=IFS-Device
set type[L]=LASTDRIVE
set type[Q]=DEVICE (Q type)
set type[S]=STACKS
set type[T]=Temporary Area
set type[X]=FCBS
rem The name of block owner is valid only in sub-blocks type "D" or "I" (or "Q")
set valid_Types=DIQ
rem Initialize/Define all variables with their maximum lengths so several
rem executions of PEEK.COM preserve the Environment in the same place
set SYS_SEG=1234
set SYS_OFS=1234
set Segment=1234
set owner=1234
set owner_Name=12345678
set size=1234
set size_B=12345
set size_K=123
set first=FALSE
set MCB_ID=X
set PSP_ID=20CD
set HAVE_ENV=FALSE
set ENV_SEG=1234
set par_Len=126
set UMB=FALSE
set UMB_ID=SC
set region=0
set dec=65535
set hex=1234
set d=1
set hexDigit=0123456789ABCDEF
rem Get address of SYSVARS-2
for /F "tokens=1,2 delims=:" %%a in ('PEEK INT:21:52 /D /H') do (
set SYS_SEG=%%a
set /A SYS_OFS=0x%%b - 2
call :DecToHex SYS_OFS
)
rem Get segment of first allocated memory block
for /F %%a in ('PEEK %SYS_SEG%:%SYS_OFS% /W /H') do (
set /A Segment=0x%%a + 1
call :DecToHex Segment
)
title DOS Memory Control Blocks chain
cls
:SHOW-HEADER
echo/
echo Seg. Block size Owner Name Type, Environment and Parameters
echo ---- ------------- ---- -------- --------------------------------
echo/
set first= TRUE
:PROC-MCB
rem Take data from this MCB and show they
for /F %%a in ('PEEK %Segment%:MCB:1 /W /H') do set owner=%%a
for /F %%a in ('PEEK %Segment%:MCB:3 /W /H') do set size=%%a
set /A size_B=0x%size%*16, size_K=(0x%size%+32)/64
set "size_B= %size_B%"
set "size_K= (%size_K%)"
for /F "delims=" %%a in ('PEEK %owner%:MCB:8 8') do set "owner_Name=%%a "
rem If the owner is 0 or 8, the block belongs to MS-DOS
if %owner% equ 0000 set "owner_Name=MS-DOS "
if %owner% equ 0008 set "owner_Name=MS-DOS "
set /P "=%Segment% %size_B:~-6% %size_K:~-6% %owner% %owner_Name:~0,8% " < NUL
if %owner_Name% neq MS-DOS goto PROC-PSP
rem Process blocks that belongs to MS-DOS
rem If the owner is 0: the block is free
if %owner% equ 0000 (
echo -- Free Memory --
goto NEXT-MCB
)
rem If is the first block of DOS: is the DOS Data Segment
if %first% neq TRUE goto DOS-CODE
echo DOS Data Segment:
set first=FALSE
:DOS-SUB-BLOCK
rem Get the type of this sub-block
for /F %%a in ('PEEK %Segment%:0') do set MCB_ID=%%a
rem If the type is "M": DOS Data Segment ends (start a normal MCB)
if %MCB_ID% equ M (
set /A Segment=0x%Segment% + 1
call :DecToHex Segment
goto PROC-MCB
)
rem Take data from this DOS sub-block and show they
for /F %%a in ('PEEK %Segment%:3 /W /H') do set size=%%a
set /A size_B=0x%size%*16, size_K=(0x%size%+32)/64
set "size_B= %size_B%"
set "size_K= (%size_K%)"
for /F "delims=" %%a in ('PEEK %Segment%:8 8') do set "owner_Name=%%a "
rem Owner name is valid only in sub-blocks type "D" or "I" (or "Q")
if "!valid_Types:%MCB_ID%=!" equ "%valid_Types%" set "owner_Name= "
echo %size_B:~-6% %size_K:~-6% %owner_Name:~0,8% !TYPE[%MCB_ID%]!
rem Pass to next sub-block and go back to process it
set /A Segment=0x%Segment% + 0x%size% + 1
call :DecToHex Segment
goto DOS-SUB-BLOCK
:DOS-CODE
rem The next DOS blocks are internal programs
echo System Program
goto NEXT-MCB
:PROC-PSP
rem If the block belongs to itself...
if %owner% neq %Segment% goto NOT-PSP
rem And have the "20CD" mark id (INT 20H) at beginning...
for /F %%a in ('PEEK %Segment%:0 /W /H') do set PSP_ID=%%a
if %PSP_ID% neq 20CD goto NOT-PSP
rem Then it is a PSP
rem Check if the PSP have an Environment block:
set HAVE_ENV= TRUE
rem - Get Environment block address from PSP:2C
for /F %%a in ('PEEK %Segment%:2C /W /H') do set ENV_SEG=%%a
rem - Check that the address points to a valid block (MCB id "M" or "Z")
for /F %%a in ('PEEK %ENV_SEG%:MCB:0') do set MCB_ID=%%a
if "%MCB_ID%" neq "M" if "%MCB_ID%" neq "Z" set HAVE_ENV=FALSE
rem - Check that the Env block belongs to the PSP
for /F %%a in ('PEEK %ENV_SEG%:MCB:1 /W /H') do set owner=%%a
if %owner% neq %Segment% set HAVE_ENV=FALSE
if %HAVE_ENV% equ TRUE (
echo Program, Environment at Seg. %ENV_SEG%
) else (
echo Program, don't have Environment
)
rem Check if the PSP have command-line parameters
for /F %%a in ('PEEK %Segment%:80 /B') do set par_Len=%%a
if %par_Len% geq 127 goto NEXT-MCB
if %par_Len% equ 0 goto NEXT-MCB
rem Show the first 38 chars. of command-line parameters
if %par_Len% gtr 38 set par_Len=38
for /F "delims=" %%a in ('PEEK %Segment%:81 %par_Len%') do set PARAM=%%a
echo param: %PARAM%
set "PARAM="
goto NEXT-MCB
:NOT-PSP
rem Check if this block is the Environment of its owner segment
for /F %%a in ('PEEK %owner%:2C /W /H') do set ENV_SEG=%%a
if %ENV_SEG% equ %Segment% (
echo Environment of Seg. %owner%
goto NEXT-MCB
)
rem Otherwise: this block is just PSP's data
echo Data of Seg. %owner%
:NEXT-MCB
rem Check if is there another MCB in the chain
if %size% equ 0 goto END
rem If is the last MCB in the chain (id = "Z"),
rem then an additional MCB chain may start in Upper Memory Blocks
for /F %%a in ('PEEK %Segment%:MCB:0') do set MCB_ID=%%a
if %MCB_ID% equ Z set UMB= TRUE
rem Pass to the next MCB in the chain
set /A Segment=0x%Segment% + 0x%size% + 1
call :DecToHex Segment
rem Check that the next MCB be valid (id = "M" or "Z")
for /F %%a in ('PEEK %Segment%:MCB:0') do set MCB_ID=%%a
if "%MCB_ID%" neq "M" if "%MCB_ID%" neq "Z" (
if %UMB% neq TRUE echo Memory control blocks destroyed
goto END
)
rem If the owner is 8 and the name is "SC" (after the last MCB in the chain),
rem then this MCB specify a "hole" before a new MCB chain in Upper Memory Blocks
for /F %%a in ('PEEK %Segment%:MCB:1 /W /H') do set owner=%%a
for /F %%a in ('PEEK %Segment%:MCB:8 2') do set "UMB_ID=%%a"
if %UMB% equ TRUE if %owner% equ 0008 if "%UMB_ID%" equ "SC" goto UMB-Region
rem Else: go back to process this MCB normally
goto PROC-MCB
:UMB-Region
rem Pass over the "hole" to reach the start of the new MCB chain in UMB
for /F %%a in ('PEEK %Segment%:MCB:3 /W /H') do set size=%%a
set /A Segment=0x%Segment% + 0x%size% + 1
call :DecToHex Segment
rem Show the header of the new MCB chain in UMB
echo/
echo/
set /A region+=1
echo Region # %region% in Upper Memory Blocks:
rem And go back to process it
goto SHOW-HEADER
:END
echo/
goto :EOF
rem Convert a decimal number to a 4-digits (word) hexadecimal one
:DecToHex var
set "dec=!%1!"
set "hex="
for /L %%i in (1,1,4) do (
set /A "d=dec&0x0F, dec>>=4"
for %%d in (!d!) do set "hex=!hexDigit:~%%d,1!!hex!"
)
set "%1=%hex%"
exit /B
PEEK.COM program is useful to explore some MS-DOS systems programmings concepts in a very simple way with Batch files, instead of using C or assembly language. For example, this is the output of previous MCB-Chain.bat file:
Below there is a list of some web sites that provide further information about all this stuff:
Code: Select all
http://stanislavs.org/helppc
http://www.ctyme.com/rbrown.htm
http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/DDJ/1988/8811/8811e/8811e.htm
I suggest you to review the "Batch Control Block" in Tables and Formats of the first link listed above. It represents a whole new research world that is directly related to Batch files! I think that the BCB is the 128-bytes data block of DOSX program that appears at segment D52E in previous MCB-Chain.bat output.
I tested these programs in an old Windows-XP computer and also in a modern Windows 8.1 one. I erroneously thought that old MS-DOS .COM programs will not run in modern Windows versions, but the .COM file format is still supported in any Windows computer that have a 32-bits CPU don't matters how modern it is, and there are a lot of them nowadays. This means that it is not time to abandon MS-DOS .COM programs yet!
Antonio
NOTE: PEEK.COM does not check the values provided to the INT's, so the program may fail if the values are outside of the valid ranges; for example, if you use INT:10 and try to move the cursor or show text outside of the screen limits.