VOGONS

Common searches


Convert PC Booter to EXE [MULE]

Topic actions

First post, by spieler8

User metadata
Rank Newbie
Rank
Newbie

MULE is a very interesting, ancient multi-player game (economic simulation) with ports to various systems.

The formula to derive the economics vary from platform to platform. Some years ago a DOS-port was rediscovered, so I wanted to take a look at that DOS port. Thinking that would be probably easy (well at least easier than debugging say pure assembler) with Ghidra.

However I then discovered that it is one of those ancient PC Booters. There is a 180kb disk image which is directly bootable (e.g. in DOSBox), but it's not possible to mount the image. Here is what I've tried so far:

- Dirctly throw the disk image into Ghidra or IdaPro. They can't handle this obviously
- Load the game, break into with the Dosbox debugger and create a memdump. However this is also (obviously) not an executable and again Ghidra and IdaPro fail
- I've looked at the strings of the binary. There are some strings which provide hints:

EA Mini-DOS
MAIN2 COM
MULET EXE
EWAMP EXE
OWAMP EXE
VENDOR-#DA1

I first thought: Great, I just need to look for .EXE MZ headers for start offsets and just dump the binaries on disk. However a search for 4D 5A (MZ) did not yield any results.

Thus I conclude that this is maybe some EA internal mini operating systems. Which probably does not much more than loading files from disk into memory and maybe does some relocation... but this is all so ancient technology that is older than me that I am just lost.

Does anybody have a clue or advice how to approach this? That is either
- any kind of informatino w.r.t. that EA Mini-DOS
- some tools/tutorial/documentation to convert booters into .EXE or .COM files
- general approach of how to turn this into something that Ghidra understands?

Thing is once Ghidra loads the whole thing up, it might not be even too difficult to statically analyze the game logic. After all, it's just 180kb of code...

Reply 2 of 24, by jakethompson1

User metadata
Rank Oldbie
Rank
Oldbie

In theory, if you load it into IDA as a binary file, you can disassemble the first 512 bytes as 8086 real mode code. That is the boot sector, and it should make int 13h calls to the BIOS to load the next stage from somewhere on the disk. If you look at the inputs and outputs of the various int 13h calls you should be able to figure out what it's doing. It probably doesn't follow any defined format.

Reply 3 of 24, by retardware

User metadata
Rank Oldbie
Rank
Oldbie

Just a general question:
Doesn't there exist a TSR which loads a disk image and takes over INT 13 for the A: floppy, so it fetches the data from the image file and feeds it to the booter game?
Maybe with extra hotkeys to switch between multiple disks if it is a booter with multiple disks the user has to swap on programs' demand?

Such a thing should work in case the bootstrap code does not depend on loading into a fixed memory area like 0000:7c00 (probably not very likely), or in cases with well-configured DOS so that 0000:7c00 and up is available for use.

Reply 5 of 24, by matze79

User metadata
Rank l33t
Rank
l33t

you can bootstrap with memdisk via grub4dos

so you can start any Floppy Image you want and even Option ROMs via https://github.com/rvalles/optromloader
You also can load HDD Images to Ramdisk, and boot from then.

The Limitation is you would need enough ram to fit your image inside.

https://dosreloaded.de - The German Retro DOS PC Community
https://www.retroianer.de - under constructing since ever

Co2 - for a endless Summer

Reply 6 of 24, by Zup

User metadata
Rank Oldbie
Rank
Oldbie
jakethompson1 wrote on 2021-09-19, 22:14:

In theory, if you load it into IDA as a binary file, you can disassemble the first 512 bytes as 8086 real mode code. That is the boot sector, and it should make int 13h calls to the BIOS to load the next stage from somewhere on the disk. If you look at the inputs and outputs of the various int 13h calls you should be able to figure out what it's doing. It probably doesn't follow any defined format.

Keep in mind:
- Some booter games didn't even use int 13h, but access the FDC directly.
- Part of your loader disk accesses will be protection checks, some data are expected to NOT be read/located.
- As stated, boot games (usually) don't follow any filesystem... they don't read file xxx but track x, sector y. The loader expects valid data on known disk locations.

So maybe you can be able to make a EXE loader for that game that replicates the behaviour of original loader but reading from a disk file... but don't expect that to be easy.

I have traveled across the universe and through the years to find Her.
Sometimes going all the way is just a start...

I'm selling some stuff!

Reply 7 of 24, by llm

User metadata
Rank Member
Rank
Member

first: what game exact? this one: https://www.mobygames.com/game/pc-booter/mule

However a search for 4D 5A (MZ) did not yield any results.

missing MZ header does not mean that there isn't a EXE layout around 😀 but i don't think that the booter games follow dos standards very near - there is just no need for that

the question is: how good are your with reverse engineering, programming at all?

1. you could start with disassembling the boot-sector

here is a small tutorial how a 16bit x86 bootsector works - so you're able to point IDA/Ghidra to the correct bytes to disassemble (auto detection will not work for binary images, but its still not that hard)
https://appusajeev.wordpress.com/2011/01/27/w … l-mode-os-nasm/ (source: https://github.com/appusajeev/os-dev-16)

another "tutorial" disection old dos boot-sector
https://www.pagetable.com/?p=165

2. understand the games IO needs:
-you could patch dosbox a little to print port/interrupt accesses and sector reads

i used https://github.com/mikechambers84/xtulator (not that perfect but smaller emulator) that is easier to patch (put in some printfs)
to get information from a game

or maybe this one as a base: https://github.com/morphx666/x8086NetEmu (VB.Net based emulator) i you're more firm with .Net Stuff

its more clear how big the project will get - after these 2 steps

Reply 8 of 24, by spieler8

User metadata
Rank Newbie
Rank
Newbie

Well I was indeed able to debug the boot sector and could trace it to the point where it jumps out of the boot loader. It reads some sectors from disk and then jumps to 0000:f000 starting the program execution -> so unfortunately it cannot be simulated by just dumping the memory into a binary I guess (as far as I understand, .com files are mapped to 0x100). I would probably need to create a .exe header with relocation tables?!
Also it seems at that time to still not have finished loading, as there are more disk accesses. But at least I have an entry address and hope I can continue with Ida or Ghidra from there...

thx for the links, I was not aware of any other emulator than Dosbox...

Reply 9 of 24, by llm

User metadata
Rank Member
Rank
Member

Also it seems at that time to still not have finished loading, as there are more disk accesses. But at least I have an entry address and hope I can continue with Ida or Ghidra from there...

i've done something more or less similar (yours is harder) with the XTulator emulator

XTulator only emulates Hardware so BIOS and DOS etc. needs to come from the ROM + floppy or HD image (Dosbox for example also emulates the DOS API)
i changed that in a way that the game i was reversing was hard loaded at a specific address (just a binary file load), and faked the DOS/BIOS interrupts to get the game running
you're game could only rely on BIOS interrupts or direct hardware access (memory management etc. needs to come from the game itself)- so faking the BIOS interrupts wouldn't be to hard

what you need to find out:
-how the game is loading the sectors - is it using the BIOS 13h Interrupt API or direct hardware reads?
-how are the image-bytes mapped in RAM - hopefully linear but also not bad if clustered a litte

so:
you could disassemble the whole thing - 180kbytes code and data isn't that much (i don't think that the code part is a big part of it)
use nasm or UASM as assembler to get to the very same binary image - its just some work but then you're 100% clear that you
got the disassembling correct - and then you are more free to changes/analysis

or:
if you're familiar with C/C++ you could change the dosbox source-code (im using "dosbox staging" for that cause it comes out of the box with VS2019 support)
or extend one of the other emulators to print the called interrupts and dump the registers out - would be just a few lines of prints nothing more
( i would add a print to the interrupt opcode and print al/ah - to get an overview whats used)

logs like:
-int 13h: Read Sector(Sectors-to-read: 23, Cylinder=23, Sector=1, Head=23, Drive=0, OutBuffer=0x1234:5678) -> Result(Error: 0, Error-code: 0, Read-Sectors: 23)
-int 13h: Read Sector(Sectors-to-read: 5, Cylinder=254, Sector=1, Head=243, Drive=0, OutBuffer=0x1234:4567) -> Result(Error: 0, Error-code: 0, Read-Sectors: 5)
-int 13h: Read Sector(Sectors-to-read: 3, Cylinder=22, Sector=1, Head=5, Drive=0, OutBuffer=0x1234:5234) -> Result(Error: 0, Error-code: 0, Read-Sectors: 3)

then you know exactly what the game is doing with the image at start (without disassembling the whole thing) - hopefully the sectors accesses are only part of the loading process

you could also map the image to a local file and re-map the Read-Sector accesses to it (you know what Cylinders, Heads etc. target which part of the image) etc. - there a million+1 way - based on your abilities and interest 😀

it could be that you just need to disable the sector reading, pushing a big blob into ram an execute it - at least in an emulator (for the beginning) you
could load the game at the very same position at the loader would do it (no relocation needed) and then go further (maybe the game code is just under 64kb and then there is no need for relocation ds/cs would point to everything)

Reply 10 of 24, by zyzzle

User metadata
Rank Newbie
Rank
Newbie

The key would be getting this "EA Mini-DOS" to run, as there seem to be files right there in the FAT of that OS directory which could at the least be copied, which would then enable you to have the full data intact, in sequential order, and not have to worry about tracing direct floppy access. There were other games using this "DOS" but I could find any reader or way to access the file system directly. No image readers will recognize it that "Mini-DOS" is certainly not compatible with MS-DOS file structures. If you could get the files intact, you'd just need to patch them to use DOS calls, probably difficult. I'd like to see other experts of the past (trixter, for example, of oldschool.org) chime in, as he made so many PC-Booter games of the past usable in DOS. There used to be a whole bunch of them available on the oldschool.org site, but they've been taken down and trixter is nowhere to be found. He probably could make MULE playable in DOS with very little effort.

Another game I've been trying to make playable directly in DOS is MasterType, by Bruce Zweig. An IBM version was produced and even supports 16-color CGA graphics, but I can't get it to boot, even using flopper in real DOS.

Reply 11 of 24, by llm

User metadata
Rank Member
Rank
Member
zyzzle wrote on 2021-09-23, 23:10:

The key would be getting this "EA Mini-DOS" to run, as there seem to be files right there in the FAT of that OS directory which could at the least be copied, which would then enable you to have the full data intact, in sequential order, and not have to worry about tracing direct floppy access.

its very easy (for a software developer) to trace the floppy accesses and what you talkin about only works if the loader is handling the game files completely at start, it won't help to have the complete files if the sector reads are cluttered over the game runtime - logging of the sector accesses will give a clear overview if that happens all the time - AND then can one decide how to go further

and patching/replacing the reads would be also not easy - the BIOS sector reads are sector oriented and need head,cylinder,sector-nr, DOS file reads only rely on file handle and offset (so he needs to patch also some sort of mapping into
the game) - so he needs way more place as the current BIOS calls need - which makes the patching complex (a full disassembly would be easier here)

zyzzle wrote on 2021-09-23, 23:10:

He probably could make MULE playable in DOS with very little effort.

as usual, it depends on the game code complexity and the knowledge of the person doing it

spieler8 wrote on 2021-09-21, 21:53:

It reads some sectors from disk and then jumps to 0000:f000 starting the program execution -> so unfortunately it cannot be simulated by just dumping the memory into a binary I guess (as far as I understand, .com files are mapped to 0x100).

yeah that will not work out of the box - but you need to understand cleary what the 0x100 means)

how gets a DOS COM program loaded:
DOS allocates memory (all available) and puts a Program segment prefix (PSP: https://en.wikipedia.org/wiki/Program_Segment_Prefix) of size 100h bytes in front and the COM-File image behind
the CS/IP register will target the first byte of the COM image (in our case 0xBA part of the "mov dx, msg" command) and the DS register will be set to the first byte of the PSP
thats the reason for this org 100h need in a COM program - or else the variable offsets will target the PSP space (which for example contains the command line parameters etc.)
that is the easy "relocation" of COM programs

; nasm hellocom.asm -fbin -o hellocom.com
org 100h ; sizeof(PSP)
mov dx, msg
mov ah, 09h
int 0x21
int 20h
msg db 'Hello', 0x0d, 0x0a, '$'

at runtime

<-- DS points to the start of the PSP (DS = PSP segment)
ProgramSegmentPrefix (100h bytes)
<-- CS/IP points here (the first byte 0xBA) (CS/IP = PSP+100h)
0x0000: BA 09 01 mov dx,0x100+9 <-- DS:[100h+9] at runtime
0x0003: B4 09 mov ah, 9
0x0005: CD 21 int 0x21
0x0007: CD 20 int 0x20
0x0009: 48 65 6C 6C 6F 0D 0A 24 Hello world\r\n$ <-- byte 9 in COM-file

but maybe its only <64kB code - then you could save it as a COM-File and change DS on start with your runtime relativ offset (that keeps the offsets in game code intact but the DS register moves them to another place)

spieler8 wrote on 2021-09-21, 21:53:

I would probably need to create a .exe header with relocation tables?!

will be more complex if there a multiple segments (but i don't think thats the case)
then you need to know all offsets that needs relocation - dependening on the game code size/complexity that could be a huge workload - but doable

i would first try to get an enviroment in which the analysis is more easy - for example XTulator or the VB.Net thing
its way more easy to try out things in such an environment - like runtime-patching, replacing bios sector reads with DOS api calls etc.

Reply 12 of 24, by Akuma

User metadata
Rank Member
Rank
Member
retardware wrote on 2021-09-19, 22:43:

Such a thing should work in case the bootstrap code does not depend on loading into a fixed memory area like 0000:7c00 (probably not very likely), or in cases with well-configured DOS so that 0000:7c00 and up is available for use.

It follows this route, the first being the one you pointed out.
0000:7C00 <--
0000:F000
0050:0100

Reply 13 of 24, by llm

User metadata
Rank Member
Rank
Member

a take a quick look at the boot-sector with IDA - i always love to understand what old-school bytes doing 😀

seg000:7C00 seg000          segment byte public 'CODE'
seg000:7C00 assume cs:seg000
seg000:7C00 ;org 7C00h
seg000:7C00 assume es:nothing, ss:nothing, ds:nothing
seg000:7C00 cli
seg000:7C01 xor ax, ax
seg000:7C03 mov ss, ax
seg000:7C05 mov sp, 7C00h
seg000:7C08 mov ds, ax
seg000:7C0A mov es, ax
seg000:7C0C cld
seg000:7C0D mov di, 28h
seg000:7C10 mov ax, 0FF53h
seg000:7C13 mov cx, 4
seg000:7C16 call sub_7C27
seg000:7C19 mov di, 2Ah
seg000:7C1C mov ax, 0F000h
seg000:7C1F mov cx, 4
seg000:7C22 call sub_7C27
seg000:7C25 jmp short loc_7C32
seg000:7C27
seg000:7C27 ; =============== S U B R O U T I N E =======================================
seg000:7C27
seg000:7C27
seg000:7C27 sub_7C27 proc near ; CODE XREF: seg000:7C16p
seg000:7C27 ; seg000:7C22p ...
seg000:7C27 stosw
seg000:7C28 add di, 2
seg000:7C2B loop sub_7C27
seg000:7C2D add di, 4
seg000:7C30 stosw
seg000:7C31 retn
seg000:7C31 sub_7C27 endp
seg000:7C31
seg000:7C32 ; ---------------------------------------------------------------------------
seg000:7C32
seg000:7C32 loc_7C32: ; CODE XREF: seg000:7C25j
seg000:7C32 mov bx, 78h
seg000:7C35 mov si, [bx]
seg000:7C37 mov bx, 7Ah
seg000:7C3A mov ds, word ptr [bx]
seg000:7C3C mov di, 80h
seg000:7C3F mov cx, 0Bh
seg000:7C42 rep movsb
seg000:7C44 mov di, 84h
seg000:7C47 mov ax, 0
seg000:7C4A stosb
seg000:7C4B mov ds, ax
seg000:7C4D mov ax, 80h
seg000:7C50 mov bx, 78h
seg000:7C53 mov [bx], ax
seg000:7C55 mov ax, 0
seg000:7C58 mov bx, 7Ah
seg000:7C5B mov [bx], ax
seg000:7C5D sti
seg000:7C5E mov ax, 0
seg000:7C61 int 13h ; DISK - RESET DISK SYSTEM
seg000:7C61 ; DL = drive (if bit 7 is set both hard disks and floppy disks reset)
seg000:7C63 jnb short loc_7C68
seg000:7C65 jmp loc_7D4C
Show last 241 lines
seg000:7C68 ; ---------------------------------------------------------------------------
seg000:7C68
seg000:7C68 loc_7C68: ; CODE XREF: seg000:7C63j
seg000:7C68 mov bx, 40h
seg000:7C6B mov es, bx
seg000:7C6D assume es:nothing
seg000:7C6D mov bx, es:10h
seg000:7C72 and bx, 30h
seg000:7C76 cmp bx, 30h
seg000:7C79 jnz short loc_7C7E
seg000:7C7B call sub_7D4E
seg000:7C7E
seg000:7C7E loc_7C7E: ; CODE XREF: seg000:7C79j
seg000:7C7E mov ah, 0
seg000:7C80 mov al, 4
seg000:7C82 int 10h ; - VIDEO - SET VIDEO MODE
seg000:7C82 ; AL = mode
seg000:7C84 xor bp, bp
seg000:7C86 mov ax, 0F000h
seg000:7C89 mov ds, ax
seg000:7C8B assume ds:nothing
seg000:7C8B mov si, 0FFFEh
seg000:7C8E lodsb
seg000:7C8F cmp al, 0FDh
seg000:7C91 jnz short loc_7C98
seg000:7C93 mov bp, 1
seg000:7C96 jmp short loc_7CA0
seg000:7C98 ; ---------------------------------------------------------------------------
seg000:7C98
seg000:7C98 loc_7C98: ; CODE XREF: seg000:7C91j
seg000:7C98 mov dx, 3D8h
seg000:7C9B mov al, 2
seg000:7C9D out dx, al
seg000:7C9E jmp short loc_7CAA
seg000:7CA0 ; ---------------------------------------------------------------------------
seg000:7CA0
seg000:7CA0 loc_7CA0: ; CODE XREF: seg000:7C96j
seg000:7CA0 mov dx, 3DAh
seg000:7CA3 in al, dx ; Video status bits:
seg000:7CA3 ; 0: retrace. 1=display is in vert or horiz retrace.
seg000:7CA3 ; 1: 1=light pen is triggered; 0=armed
seg000:7CA3 ; 2: 1=light pen switch is open; 0=closed
seg000:7CA3 ; 3: 1=vertical sync pulse is occurring.
seg000:7CA4 xor al, al
seg000:7CA6 out dx, al ; Video: bits 0-1 control
seg000:7CA6 ; Feature Control outputs FC0 and FC1
seg000:7CA7 mov al, 2
seg000:7CA9 out dx, al ; Video: bits 0-1 control
seg000:7CA9 ; Feature Control outputs FC0 and FC1
seg000:7CAA
seg000:7CAA loc_7CAA: ; CODE XREF: seg000:7C9Ej
seg000:7CAA mov ax, 0B800h
seg000:7CAD mov es, ax
seg000:7CAF assume es:nothing
seg000:7CAF mov bx, 0
seg000:7CB2 mov dx, 0
seg000:7CB5 mov ch, 1
seg000:7CB7 mov cl, 1
seg000:7CB9 mov al, 9
seg000:7CBB mov ah, 2
seg000:7CBD int 13h ; DISK - READ SECTORS INTO MEMORY
seg000:7CBD ; AL = number of sectors to read, CH = track, CL = sector
seg000:7CBD ; DH = head, DL = drive, ES:BX -> buffer to fill
seg000:7CBD ; Return: CF set on error, AH = status, AL = number of sectors read
seg000:7CBF jb short loc_7CDB
seg000:7CC1 mov bx, 1200h
seg000:7CC4 mov al, 9
seg000:7CC6 mov ch, 2
seg000:7CC8 mov cl, 1
seg000:7CCA mov ah, 2
seg000:7CCC int 13h ; DISK - READ SECTORS INTO MEMORY
seg000:7CCC ; AL = number of sectors to read, CH = track, CL = sector
seg000:7CCC ; DH = head, DL = drive, ES:BX -> buffer to fill
seg000:7CCC ; Return: CF set on error, AH = status, AL = number of sectors read
seg000:7CCE jb short loc_7CDB
seg000:7CD0 mov bx, 2400h
seg000:7CD3 mov ch, 3
seg000:7CD5 mov al, 9
seg000:7CD7 mov ah, 2
seg000:7CD9 int 13h ; DISK - READ SECTORS INTO MEMORY
seg000:7CD9 ; AL = number of sectors to read, CH = track, CL = sector
seg000:7CD9 ; DH = head, DL = drive, ES:BX -> buffer to fill
seg000:7CD9 ; Return: CF set on error, AH = status, AL = number of sectors read
seg000:7CDB
seg000:7CDB loc_7CDB: ; CODE XREF: seg000:7CBFj
seg000:7CDB ; seg000:7CCEj
seg000:7CDB jb short loc_7D4C
seg000:7CDD mov bx, 3600h
seg000:7CE0 mov ch, 4
seg000:7CE2 mov al, 5
seg000:7CE4 mov ah, 2
seg000:7CE6 int 13h ; DISK - READ SECTORS INTO MEMORY
seg000:7CE6 ; AL = number of sectors to read, CH = track, CL = sector
seg000:7CE6 ; DH = head, DL = drive, ES:BX -> buffer to fill
seg000:7CE6 ; Return: CF set on error, AH = status, AL = number of sectors read
seg000:7CE8 jb short loc_7D4C
seg000:7CEA test bp, bp
seg000:7CEC jnz short loc_7CF6
seg000:7CEE mov dx, 3D9h
seg000:7CF1 mov al, 21h
seg000:7CF3 out dx, al
seg000:7CF4 jmp short loc_7D06
seg000:7CF6 ; ---------------------------------------------------------------------------
seg000:7CF6
seg000:7CF6 loc_7CF6: ; CODE XREF: seg000:7CECj
seg000:7CF6 mov dx, 3DAh
seg000:7CF9 in al, dx ; Video status bits:
seg000:7CF9 ; 0: retrace. 1=display is in vert or horiz retrace.
seg000:7CF9 ; 1: 1=light pen is triggered; 0=armed
seg000:7CF9 ; 2: 1=light pen switch is open; 0=closed
seg000:7CF9 ; 3: 1=vertical sync pulse is occurring.
seg000:7CFA mov al, 2
seg000:7CFC out dx, al ; Video: bits 0-1 control
seg000:7CFC ; Feature Control outputs FC0 and FC1
seg000:7CFD mov al, 1
seg000:7CFF out dx, al ; Video: bits 0-1 control
seg000:7CFF ; Feature Control outputs FC0 and FC1
seg000:7D00 mov al, 10h
seg000:7D02 out dx, al ; Video: bits 0-1 control
seg000:7D02 ; Feature Control outputs FC0 and FC1
seg000:7D03 mov al, 1
seg000:7D05 out dx, al ; Video: bits 0-1 control
seg000:7D05 ; Feature Control outputs FC0 and FC1
seg000:7D06
seg000:7D06 loc_7D06: ; CODE XREF: seg000:7CF4j
seg000:7D06 test bp, bp
seg000:7D08 jnz short loc_7D12
seg000:7D0A mov dx, 3D8h
seg000:7D0D mov al, 0Ah
seg000:7D0F out dx, al
seg000:7D10 jmp short loc_7D1C
seg000:7D12 ; ---------------------------------------------------------------------------
seg000:7D12
seg000:7D12 loc_7D12: ; CODE XREF: seg000:7D08j
seg000:7D12 mov dx, 3DAh
seg000:7D15 in al, dx ; Video status bits:
seg000:7D15 ; 0: retrace. 1=display is in vert or horiz retrace.
seg000:7D15 ; 1: 1=light pen is triggered; 0=armed
seg000:7D15 ; 2: 1=light pen switch is open; 0=closed
seg000:7D15 ; 3: 1=vertical sync pulse is occurring.
seg000:7D16 xor al, al
seg000:7D18 out dx, al ; Video: bits 0-1 control
seg000:7D18 ; Feature Control outputs FC0 and FC1
seg000:7D19 mov al, 0Ah
seg000:7D1B out dx, al ; Video: bits 0-1 control
seg000:7D1B ; Feature Control outputs FC0 and FC1
seg000:7D1C
seg000:7D1C loc_7D1C: ; CODE XREF: seg000:7D10j
seg000:7D1C xor ax, ax
seg000:7D1E mov es, ax
seg000:7D20 assume es:nothing
seg000:7D20 mov bx, 0F000h
seg000:7D23 mov cx, 270Ah
seg000:7D26 mov dx, 0
seg000:7D29
seg000:7D29 loc_7D29: ; CODE XREF: seg000:7D36j
seg000:7D29 mov ax, 201h
seg000:7D2C int 13h ; DISK - READ SECTORS INTO MEMORY
seg000:7D2C ; AL = number of sectors to read, CH = track, CL = sector
seg000:7D2C ; DH = head, DL = drive, ES:BX -> buffer to fill
seg000:7D2C ; Return: CF set on error, AH = status, AL = number of sectors read
seg000:7D2E jb short loc_7D4C
seg000:7D30 add cl, 0Ah
seg000:7D33 cmp cl, 5Ah
seg000:7D36 jbe short loc_7D29
seg000:7D38 mov cl, 6
seg000:7D3A mov ch, 4
seg000:7D3C mov dx, 0
seg000:7D3F mov al, 4
seg000:7D41 mov ah, 2
seg000:7D43 int 13h ; DISK - READ SECTORS INTO MEMORY
seg000:7D43 ; AL = number of sectors to read, CH = track, CL = sector
seg000:7D43 ; DH = head, DL = drive, ES:BX -> buffer to fill
seg000:7D43 ; Return: CF set on error, AH = status, AL = number of sectors read
seg000:7D45 jb short loc_7D4C
seg000:7D47 mov ax, 0F000h
seg000:7D4A push ax
seg000:7D4B retn
seg000:7D4C ; ---------------------------------------------------------------------------
seg000:7D4C
seg000:7D4C loc_7D4C: ; CODE XREF: seg000:7C65j
seg000:7D4C ; seg000:loc_7CDBj ...
seg000:7D4C int 19h ; DISK BOOT
seg000:7D4C ; causes reboot of disk system
seg000:7D4E
seg000:7D4E ; =============== S U B R O U T I N E =======================================
seg000:7D4E
seg000:7D4E
seg000:7D4E sub_7D4E proc near ; CODE XREF: seg000:7C7Bp
seg000:7D4E mov ah, 0
seg000:7D50 mov al, 2
seg000:7D52 int 10h ; - VIDEO - SET VIDEO MODE
seg000:7D52 ; AL = mode
seg000:7D54 mov cx, 0F0Fh
seg000:7D57 mov ah, 1
seg000:7D59 int 10h ; - VIDEO - SET CURSOR CHARACTERISTICS
seg000:7D59 ; CH bits 0-4 = start line for cursor in character cell
seg000:7D59 ; bits 5-6 = blink attribute
seg000:7D59 ; CL bits 0-4 = end line for cursor in character cell
seg000:7D5B mov dh, 0Ch
seg000:7D5D mov dl, 0
seg000:7D5F mov bh, 0
seg000:7D61 mov ah, 2
seg000:7D63 int 10h ; - VIDEO - SET CURSOR POSITION
seg000:7D63 ; DH,DL = row, column (0,0 = upper left)
seg000:7D63 ; BH = page number
seg000:7D65 push cs
seg000:7D66 pop ds
seg000:7D67 assume ds:nothing
seg000:7D67 lea si, aThisProductReq ; " This product requires an "...
seg000:7D6B
seg000:7D6B loc_7D6B: ; CODE XREF: sub_7D4E+29j
seg000:7D6B lodsb
seg000:7D6C and al, 7Fh
seg000:7D6E jz short loc_7D79
seg000:7D70 mov ah, 0Eh
seg000:7D72 mov bx, 7
seg000:7D75 int 10h ; - VIDEO - WRITE CHARACTER AND ADVANCE CURSOR (TTY WRITE)
seg000:7D75 ; AL = character, BH = display page (alpha modes)
seg000:7D75 ; BL = foreground color (graphics modes)
seg000:7D77 jmp short loc_7D6B
seg000:7D79 ; ---------------------------------------------------------------------------
seg000:7D79
seg000:7D79 loc_7D79: ; CODE XREF: sub_7D4E+20j
seg000:7D79 mov bx, 40h ; BIOS data: https://stanislavs.org/helppc/bios_data_area.html
seg000:7D7C mov es, bx
seg000:7D7E assume es:nothing
seg000:7D7E and byte ptr es:10h, 0CFh ; 40:10 2 bytes Equipment list flags
seg000:7D84 or byte ptr es:10h, 20h
seg000:7D8A retn
seg000:7D8A sub_7D4E endp
seg000:7D8A
seg000:7D8A ; ---------------------------------------------------------------------------
seg000:7D8B db 11h
seg000:7D8C db 11h
seg000:7D8D aThisProductReq db ' This product requires an IBM Color/Graphics Adapter'
seg000:7D8D ; DATA XREF: sub_7D4E+19o
seg000:7D8D db 0
seg000:7DCF db 31h dup(0) ; fill to reach 512 bytes
seg000:7DCF seg000 ends

and the overview with the EXE names seems to be exact 32 byte per File - like a FAT directory entry

Reply 14 of 24, by llm

User metadata
Rank Member
Rank
Member

the boot-sector set the CGA 320x200x4 graphics mode (+some additional ports settings - special CGA mode?)

so the memory layout would be like that

320x200x4 colors (each 2bits) means 4 pixel per byte
320 pixel/4 = 80 bytes per line

bits  7 6 5 4 3 2 1 0
pixel 0 0 1 1 2 2 3 3

https://moddingwiki.shikadi.net/wiki/Raw_CGA_Data
even lines starting at 0xB800:0
+1F40 byte 100*80 bytes
+C0 padding (unused)
odd lines starting at 0xB800:0x2000
+1F40 byte 100*80 bytes
+C0 padding (unused)

so the even lines of the picture and then the odd lines

example: https://mobile.twitter.com/Foone/status/1133821744723062784

Reply 15 of 24, by Akuma

User metadata
Rank Member
Rank
Member
llm wrote on 2021-09-25, 08:52:

and the overview with the EXE names seems to be exact 32 byte per File - like a FAT directory entry

I wonder if that was just used for development? (maybe it is not used by the game at all)

Reply 16 of 24, by llm

User metadata
Rank Member
Rank
Member

these are the sector reads on start (the game play does not seem to read further sectors) but could

Read sectors (Head and Drive are always 0)

Sector-Count(Bytes)  Cylinder  Sector  Buffer(Seg:Ofs)

9(4608) 1 1 0xB800:0000
9(4608) 2 1 0xB800:1200
9(4608) 3 1 0xB800:2400
5(2560) 4 1 0xB800:3600
1(512) 39 10 0x0000:F000
1(512) 39 20 0x0000:F000
1(512) 39 30 0x0000:F000
1(512) 39 40 0x0000:F000
1(512) 39 50 0x0000:F000
1(512) 39 60 0x0000:F000
1(512) 39 70 0x0000:F000
1(512) 39 80 0x0000:F000
1(512) 39 90 0x0000:F000
4(2048) 4 6 0x0000:F000
9(4608) 5 1 0x0000:0600
9(4608) 6 1 0x0000:1800
9(4608) 7 1 0x0000:2A00
9(4608) 8 1 0x0000:3C00
9(4608) 9 1 0x0000:4E00
9(4608) 10 1 0x0000:6000
9(4608) 11 1 0x0000:7200
9(4608) 12 1 0x0000:8400
9(4608) 13 1 0x0000:9600
9(4608) 14 1 0x0000:A800
9(4608) 15 1 0x0000:BA00
9(4608) 16 1 0x0000:CC00
9(4608) 17 1 0x0000:DE00
2(1024) 0 2 0x0C07:0000
2(1024) 0 6 0x0C07:0400
2(1024) 0 8 0x0C07:0800
2(1024) 18 1 0x0F00:0000
2(1024) 18 3 0x0F00:0400
2(1024) 18 5 0x0F00:0800
2(1024) 18 7 0x0F00:0C00
2(1024) 19 1 0x0F00:1000
2(1024) 19 3 0x0F00:1400
2(1024) 19 5 0x0F00:1800
2(1024) 19 7 0x0F00:1C00
2(1024) 20 1 0x0F00:2000
2(1024) 20 3 0x0F00:2400
2(1024) 20 5 0x0F00:2800
2(1024) 20 7 0x0F00:2C00
2(1024) 21 1 0x0F00:3000
2(1024) 21 3 0x0F00:3400
2(1024) 21 5 0x0F00:3800
2(1024) 21 7 0x0F00:3C00
2(1024) 22 1 0x0F00:4000
2(1024) 22 3 0x0F00:4400
2(1024) 22 5 0x0F00:4800
2(1024) 22 7 0x0F00:4C00
2(1024) 23 1 0x0F00:5000
2(1024) 23 3 0x0F00:5400
2(1024) 23 5 0x0F00:5800
2(1024) 23 7 0x0F00:5C00
2(1024) 24 1 0x0F00:6000
2(1024) 24 3 0x0F00:6400
2(1024) 24 5 0x0F00:6800
2(1024) 24 7 0x0F00:6C00
Show last 43 lines
2(1024)  25  1   0x0F00:7000
2(1024) 25 3 0x0F00:7400
2(1024) 25 5 0x0F00:7800
2(1024) 25 7 0x0F00:7C00
2(1024) 26 1 0x0F00:8000
2(1024) 26 3 0x0F00:8400
2(1024) 26 5 0x0F00:8800
2(1024) 26 7 0x0F00:8C00
2(1024) 27 1 0x0F00:9000
2(1024) 27 3 0x0F00:9400
2(1024) 27 5 0x0F00:9800
2(1024) 27 7 0x0F00:9C00
2(1024) 28 1 0x0F00:A000
2(1024) 28 3 0x0F00:A400
2(1024) 28 5 0x0F00:A800
2(1024) 28 7 0x0F00:AC00
2(1024) 29 1 0x0F00:B000
2(1024) 29 3 0x0F00:B400
2(1024) 29 5 0x0F00:B800
2(1024) 29 7 0x0F00:BC00
2(1024) 30 1 0x0F00:C000
2(1024) 30 3 0x0F00:C400
2(1024) 30 5 0x0F00:C800
2(1024) 0 2 0x0C07:0000
2(1024) 0 6 0x0C07:0400
2(1024) 0 8 0x0C07:0800
2(1024) 30 7 0xB800:0000
2(1024) 31 1 0xB800:0400
2(1024) 31 3 0xB800:0800
2(1024) 31 5 0xB800:0C00
2(1024) 31 7 0xB800:1000
2(1024) 32 1 0xB800:1400
2(1024) 32 3 0xB800:1800
2(1024) 32 5 0xB800:1C00
2(1024) 32 7 0xB800:2000
2(1024) 33 1 0xB800:2400
2(1024) 33 3 0xB800:2800
2(1024) 33 5 0xB800:2C00
2(1024) 33 7 0xB800:3000
2(1024) 34 1 0xB800:3400
2(1024) 34 3 0xB800:3800
2(1024) 34 5 0xB800:3C00

Reply 17 of 24, by llm

User metadata
Rank Member
Rank
Member

i still have no idea what the sector/cylinder count of the mule image is

printing the bios floppy info from dosbox gives strange results (or are these correct?)

#include <stdio.h>

int main()
{
// info from: http://vitaly_filatov.tripod.com/ng/asm/asm_024.9.html

unsigned int ax_val = 0;
unsigned int cx_val = 0;
unsigned char dh_val = 0;
unsigned char dl_val = 0;
unsigned char bl_val = 0;
unsigned char bh_val = 0;
unsigned int es_val = 0;
unsigned int di_val = 0;

printf("BIOS-func: 13h/08h, Get Current Drive Parameters for drive 0\n");
asm mov ah,08h
asm mov dl,0
asm int 13h

asm mov [ax_val],ax
asm mov [cx_val],cx
asm mov [dh_val],dh
asm mov [dl_val],dl
asm mov [bl_val],bl
asm mov [bh_val],bh
asm mov [es_val],es
asm mov [di_val],di

printf("ax_val: %u\n", ax_val);
printf("cx_val: %u\n", cx_val);
//ccccccccccssssss
printf(" max cylinder: %u\n", (cx_val>>6));
printf(" max sectors: %u\n", (cx_val&0x3F));
printf("max heads: %u\n", dh_val);
printf("nr of disketts: %u\n", dl_val);
printf("bl_val: %u\n", bl_val);
printf(" cmos_drive_type: %u\n", (bl_val&0xF));
printf("bh_val: %u\n", bh_val);

return 0;
}

but giving max cylinder count of 156 and max sector size of 9

i think it should print something near to this floppy format

360 KB 5.25"
Tracks (Cylinders): 40
Sectors Per Track/Cylinder: 9
Total Sectors Per Disk: 720

im mount the image in dosbox with

mount c d:\temp
imgmount 0 c:\mule.img -t floppy -fs none

Reply 18 of 24, by llm

User metadata
Rank Member
Rank
Member

according to ripsaw8080 its a single side DOS 1.x floppy image (and could be mounted by Dosbox if it were a full double side image of size 360kb)

Re: IMG file - corrupt or...?

here the source of DOS 1.25 describing the format: https://github.com/microsoft/MS-DOS/blob/04a3 … e/MSDOS.ASM#L99
i don't know if its a FAT12 or something earlier

but writing a program that extracts the files should be very doable if it is a FAT like file-system

Reply 19 of 24, by llm

User metadata
Rank Member
Rank
Member

its actually a FAT12 (Legacy) - i've wrote a small exporter for it (not finished so far) - these com and exe files are really there and exportable

but it seems that these files do not contain the game code - there are a bunch of bad-clusters in the FAT that seems to be occupied by the real game code and data

i will try to combined the sector load info with the FAT system managed sectors - this way i can get a feeling how the game gets loaded and what the relevance of the FAT files is