Extensions

BE can be extended with user written "extensions". These are shared libraries with well-defined interfaces. BE can be told to load them at start-up.

Memory extensions

Memory extensions allow BE to view and edit content in places other than in files.

The binary file arguments to BE are normally of the form :-

filename[@address]

This tells BE to load the file and whenever data at a memory address from address to address+filelength is accessed, to supply the data from the file.

However, it is possible to supply binary file arguments of the form :-

extension!args[@address]

Memory extensions may be written to provide either read-only, or read-write access to their data.

BE loads the memory extension DLL or shared library. It then passes the args and address to the memory extension, who does something of its own chosing with them. The memory extension DLL can then supply data to BE on request.

The file bememext.h documents the extension interface.

Caching

Memory extensions can of course cache the data they fetch. They might do this if it is inefficient to make request for the data, every time a new byte is requested. Often a memory extension will request data in blocks, around the specific address requested, taking advantage of the fact that subsequent requests are likely to be to nearby addresses, often in the same block.

When the user presses r (for refresh), BE simply re-requests the data, and it could come from a cache.

When the user presses R (for really refresh), BE tells memory extensions to discard any cache they could be holding, and then re-requests the data, thus ensuring a fresh view is presented.

Memory extensions which cache therefore have to support a function which makes them discard any cached data they might be holding.

Flushing

When editing files, changes to the data are recorded in memory. When BE is closed down, it attempts to write back any changes back into the disk files where the data originally came from. This is referred to as flushing. BE will prompt you as to whether to save the changes back to disk.

If a memory extension is providing the data to BE for display, and the memory extension supports modification of the data, it has a choice :-

As most memory extensions provide a live view of some real-time data, they tend to opt for the first choice.

Memory extensions which don't apply updates immediately must therefore support a function which makes them flush the data from memory to whereever it should be stored.

Addressing

When BE initially accesses an address, it discards address bits which are larger than the addressing mode in use (set via the -A command line argument, default is 32). eg: if we try to access address 0x0123456789abcdef and we are operating in 48 bit addressing mode, BE will truncate the address to 0x0000456789abcdef.

Then if the segmented mode flag is on (set via the -g command line argument), BE computes a new address according to the following formula, which is the basically the Intel 8086 real mode mapping of segment:offset to physical address (with no A20 bit) :-

addr = ( ((addr&0xffff0000)>>12) + (addr&0xffff) ) & 0xfffff;

BE then attempts to satisfy the memory reference using this address by considering all memory sections (files and memory extensions) in turn, until one works.

If the address is greater than 0xffffffff BE will not attempt to satisfy it using a 32 bit memory extension.

Memory extensions which call back into BE

When asked to access data at a given address, sophisticated memory extensions can call back into BE to access data at other address(es). They do this by providing a 'memory access' function, which BE calls at initialisation time to tell the extension the addresses of the callback functions.

The address does not go through the mappings explained in the above Addressing section again.

A memory extension which presents a shadow view of data is a simple use of this feature.

Multiple address decode is another use. Perhaps BE is being used as the data navigation part of a debugger, and the contents of a DRAM chip has been dumped. However, due to incomplete decoding of address bits, the same data appears at several places in the address map.

Summary statistics could be generated via memory extension. For example, when a memory access is made to one range of addresses, it reads another related range and counts how many bits are 1's. It then returns the count. This is an example of a memory extension providing a form of derived data.

Beware: Loops can easily be created. A memory extension that when asked to access a byte at a given address simply calls back into BE for data from the same address is going to recurse forever. BE detects excessive nesting and fails the nested memory access. Don't rely on BE doing this though...

Examples

BEBIG allows BE to edit files that are too large to load into memory. It ought to be noted that the author regularly uses BE on files of several megabytes in size without a problem, but several gigabytes would present a problem! The memory extension opens a file handle and reads bytes demanded by BE upon request. eg:

be big!verybigfile.dat

BEDEV is a memory extension which can allow BE to edit device files. This can be used to edit sectors on a disk. eg:

be dev!/dev/hde

BEEXEC supplies data to BE by invoking a user program and caching the results. eg:

be exec!0x1000,mycommand

BERAWIO is a memory extension which can read and write Windows block devices. Think of this as a Windows equivelent of BEDEV. eg:

be rawio!\\.\PhysicalDrive3

BEMEMU allows the live debug of programs running within the MEMU Memotech retro-computer emulator. eg:

be -A16 -Cz80 -ySpeculator.sym -iSpeculator.ini memu!TOKEN,0x80

The Mosquitto project (Really Small Messaging Broker, supporting the MQTT protocol) uses a BE memory extension to examine its post-mortem dumps. Their git includes rsmb.ini and tools/be/bersmb.cpp.

Another use is the in live-debug of running adapter cards, or other embedded devices. The memory extension can provide data bytes directly from the memory space of the adapter. args could be used to identify the slot the adapter is in. Alternatively, args could identify IO base addresses, memory window addresses, or a device driver to use to access the data. Memory extensions which do this, do exist, and they almost turn BE into a debugger (almost, because there is no run, stop, or single step). Run, stop and single step of an adapter could be driven by the options mechanism, if that were possible and/or desired. When using these, a customised initialisation file is typically also used, which understands all the structure definitions and variables used in the firmware on the adapter.

Yet another use might be providing BE with access to physical or virtual or process specific linear address spaces, perhaps via the use of a device driver. Shared memory windows might give addressibility of datastructures in other programs. A simple example of this is a memory extension which reads bytes from the /dev/kmem special device in the AIX or Linux environment. Using this, kernel device drivers may be debugged.

Perhaps bytes sent down a communications port could be made to appear as a stream of binary data.

Disassembler extensions

Disassembler extensions allow BE to disassemble code in its address space.

The -C dx command line argument is a way of telling BE to load and use a disassembler extension for displaying any code in the data.

The same rules for naming and locating disassembly extensions apply, as for memory extensions.

The file bedisext.h documents the extension interface.

As the disassembler proceeds, it tests each address to see if it is within any field in definition marked with the nocode annotation. If so, it inserts the field instead, and continues disassembly afterwards. Sounds complicated, but this is a way of preventing the disassembly of data.

Examples

BEZ80 is available online, and disassembles Z80 opcodes. You can type :-

be -A 16 -C z80 rom.dat@0x0000 ram.dat@0x4000

BEI86 exists within IBM SSD Hursley, but I have yet to secure its release. It decodes 8086/80186 opcodes.

BEARM exists within IBM SSD Hursley, but I have yet to secure its release. It decodes ARM6 opcodes.

BEPPC exists within IBM SSD Hursley. It decodes PowerPC opcodes.

How to build

Currently extensions may be built for :-

AIX mode #1
BE is compiled with xlC C++, and uses the xlC C++ loadAndInit API to load the extension, named beextension, searching along the directories listed in the PATH and LIBPATH environment variables. The IBM xlC C++ compiler can be used to compile the extension. Unfortunately xlC C++ is expensive and I no longer have access to it. I have binaries for AIX 4.1.5 and AIX 4.3.
AIX mode #2
BE is compiled with xlC C++, and is capable of using loadAndInit or dlopen The IBM xlC C++ compiler can be used to compile the extension. I have no binaries for this mode.
AIX mode #3
BE is compiled with g++, and uses dlopen to load shared libraries named beextension.so, searching along the LIBPATH environment variable. g++ can be used to compile the extension. I have an AIX 5.3 binary, although I think the same source would compile fine on AIX 4.2.1 or later.
Linux
The g++ compiler can be used to compile the extension. BE uses the dlopen API to load the extension, which is named beextension.so. dlopen locates shared libraries by looking along the LD_LIBRARY_PATH, directories listed in the /etc/ld.so.cache file, and in the /usr/lib and /lib directories.
HP-UX
The aCC C++ compiler can be used to compile the extension. BE uses the shl_load API to load the extension, which is named beextension.sl. shl_load locates shared libraries by looking along the SHLIB_PATH. I reserve the right to switch-over-to or add-support-for using dlopen based shared library support.
SunOS
The CC C++ compiler can be used to compile the extension. BE uses the dlopen API to load the extension, which is named beextension.so. dlopen locates shared libraries by looking along the LD_LIBRARY_PATH (it may also look in other places).
Cygwin
The gcc C++ compiler can be used to compile the extension. BE uses the dlopen API to load the extension, which is named beextension.dll.
MacOSX
The gcc C++ 3.3 compiler can be used to compile the extension. BE uses the dlopen API to load the extension, which is named beextension.so.
Windows
The Visual Studio 2013 can be used to compile the extension. BE uses the Win32 LoadLibrary API to load BEextension.DLL.
32 bit OS/2
The IBM VisualAge C++ compiler (version 3.0, with fixpaks CTC306, CTD302 and CTU304, or later recommended) can be used to compile the extension. BE uses the OS/2 DosLoadModule API to load BEextension.DLL, which it finds by looking along the LIBPATH environment variable.
32 bit DOS
The Watcom C/C++ compiler (version 11.0b) and the CauseWay DOS extender (version 1.3 or later) can be used to compile the extension. Despite using the Watcom compiler, I still prefer NMAKE over WMAKE. BE uses the CauseWay provided LoadLibrary API to load the extension, which is named BEextension.DLL, which it seems to find by looking in the current directory or along the PATH.
NetWare
The Watcom C/C++ compiler (version 11.0b) can be used to compile the extension. Despite using the Watcom compiler, I still prefer NMAKE over WMAKE. The DLL mechanism works by the DLL actually being an NLM named with a .ndl file extension. The DLL remains in memory for as long as BE needs it. The .ndl files must be on the search path so BE can find them.