Putting the 2015 Romero DooM level release to use

or how I learned to love Freedoom.

E1M1

So back in the distant past of 2015, John Romero had released a bunch of assets to DooM, including the source to the maps. And like a fool I never really did anything with them.  But I decided to do something about that.  Obviously just compiling a map isn’t anywhere near good enough, you need graphics, music and sound right?

Enter Freedoom

Now what is great is that the Feeedoom folks have had the whole ‘apple pie’ stance to their project, with everything included.  However they have drifted tools, build processes and other methodologies through the years. But thankfully the full archives are online, so I could go through and piece stuff together to my liking.  Of course this all needs various tools, and oh boy does it ever need tools.  You need:

  • Perl
  • Python
  • a C pre-processor
  • ImageMagick
  • idbsp10
  • deutex-4.4.902
  • midi3mus
  • sox-14.4.2
  • A Unix’y build environment, I used the ancient MSYS

And probably many more I’m forgetting.

The first was to compile the levels.  I used Ron Rossbach’s ancient IDBSP, a C port of iD’s command line Objective C level compiler.  I did run into two slight issues, the first is that Ron’s tool expects their to be a line in the level’s dwd file telling us the name of the wad it’ll compile to, which of course is missing.  The other is that I suck a makefiles, and just cheated forcing the tool to use a .wad extension on whatever .dwg it converts.  And with that out of the way, I had the levels all built.

However that is where I found out the hard way that Freedoom doesn’t target something like my goal of being able to run this wad with the original DooM v1.1 release.  The first problem is that Freedoom uses a different palette set, where it looks like it should be deeper?  I’m not sure, but one thing was for sure everything looked like a rainbow sea of wrong and any vanilla or chocolate engines.  And that is where I got a fun chance to play with ImageMagik.  It really is quite powerful.

The first problem of course is the palette.  Buried in the Python build scripts is a Perl fragment for generating a palette, and the all important playpal.lmp & colormap.lmp. I also got to spend a lot of time trying to extract that palette and do something useful with it to no avail.  But googling led me to VGA_palette_with_black_borders.png, on Wikipedia of all things. So using this as a base I could convert, dither, and re-map the higher color Freedoom artwork into something that would play nice with Vanilla Doom.  The downsite is that I didn’t try to find out exactly what assets a DooM version 1 wad needs, so I converted them all.  On a good Xeon or i7 it takes about 10-20 minutes all the images.  I can’t even think about how slow this would be using a 486, 68040 or MIPS.

ImageMagick-7.0.7-18-Q16/convert ../graphics_freedoom/wia00001.gif -dither Riemersma -remap ..\playpal\VGA_palette_with_black_borders.png wia00001.gif

Imagine running that some 1,558 times.

Vanilla DooM v1.1 only plays audio at 11,000 Hz, 8bit deep.  All the later ones needless to say use better sampling, and once more again Freedoom was at a higher level.  I used sox to re-sample all the audio into the lower frequencies.

sox “../sounds_freedoom/dsslop.wav” -b 8 -r 11025 -c 1 “dsslop.wav”

Later engines also are capable of directly playing MIDI files, and taking advantage of OPL3 chips, instead of v1.1’s OPL2 level. Thankfully Natt’s midi3mus is still online, and I was able to use this to convert Freedoom’s MIDI into the more restricted MUS format.

midi3mus ../musics_freedoom/d_map11.mid d_map11.mus

The last tool is deutex, which is not only capable of building iwads, but also decomposing them.  I ended up having to add the ‘musid’ flag to force it to honor the older sound format.

deutex -v0 -fullsnd -rate accept -rgb 0 255 255 -doom2 bootstrap/ -iwad -musid -textures -lumps -patch -flats -sounds -musics -graphics -sprites -levels -build wadinfo_ult.txt wads/doom.wad

But because I wanted to verify my build against DooM v1.1 I also had to address one other thing, which is the status bar for DooM version 1.1, which I had patched around in my lame source port. I was able to just decompose the shareware 1.0 wad, and extract the following:

  • stabarl.bmp
  • stabarr.bmp
  • starms.bmp
  • stbar.bmp
  • stmbarl.bmp
  • stmbarr.bmp

And injecting them into the final wad gives me something that the Vanilla DooM  v1.1 engine can actually run.

I’ll put up something online later, and a video of me playing, I guess.  It’s a weird Doom, I mean it is the “normal” DooM, just looks different.

As suggested it’s now called zeeDoom. I’ve uploaded the project, some of the 3rd party programs to build the project, and of course the built wad file.

Download zeeDoom

And as mentioned a play through E1M1 video.

Ultimate Christmas! Ultimate DooM! giveaway!

Nothing says Merry Christmas, like shotguns, demons, and a free trip to hell!

I’ve got 5 extra steam keys, so leave a comment with some way to reach you, and the first five people get a copy of Ultimate Doom! Sorry all five have been claimed!

Feel free to play with GCC 1.27, or the x68000, or don’t forget vintage DooM shovelware of the era to go along for the ride!

And for those who like the vintage, don’t for get that you can revert Ultimate DooM back to version 1.1

 

GCC 1.27 & MS-DOS

Inspired by Building and using a 29-year-old compiler on a modern system, i thought I too could get this ancient version of GCC working.  At the time I never had bothered with the older version as I had always assumed that there were many fixes and adaptations to GCC for it to run on MS-DOS via GO32/DJGPP.  However, after doing this, it’s obvious that GO32/DJGPP was rather built around GCC’s stock output, which would sure make a lot more sense.

And it turns out that the target machine being an i386 Sequent running BSD is the best match, both in turns of underscores, and debugging format.  At first, I had tried the AT&T SYSV i386 target, however it couldn’t link anything from the standard libraries that DJGPP has as they all have a leading underscore.  After starting to mess with internal macros to turn things on and off, and re-define how various portions of assembly are emitted, I found the Sequent target and went with that and everything was fine and using the existing build infrastructure for GCC 1.39 I now could actually run hello world!

gcc_v1 -v -E hello.c -o hello.i
gcc version 1.39
cpp_v1 -v -undef -D__GNUC__ -DGNUDOS -Dunix -Di386 -D__unix__ -D__i386__ hello.c -o hello.i
GNU CPP version 1.39
gcc_v1 -v -S hello.i -o hello.s
gcc version 1.39
cc1_v1 hello.i -quiet -version -o hello.s
GNU C version 1.27 (80386, BSD syntax) compiled by GNU C version 5.1.0.
gcc_v1 -v -c hello.s -o hello.o
gcc version 1.39
as -o hello.o hello.s
gcc_v1 -v -o hello hello.o
gcc version 1.39
ld -o hello C:/dos/xdjgpp.v1/lib/crt0.o hello.o -lc

go32 version 1.12.maint3 Copyright (C) 1994 DJ Delorie

hello from DJGPP v1/GCC 1.39!

Wasn’t that great?  Then going through my ‘test’ programs I went to try to build the infocom interpreter, and that is when things went off the rails.

funcs.o: Undefined symbol __udivsi3 referenced from text segment
options.o: Undefined symbol __divsi3 referenced from text segment
options.o: Undefined symbol __divsi3 referenced from text segment
print.o: Undefined symbol __divsi3 referenced from text segment
print.o: Undefined symbol __udivsi3 referenced from text segment
support.o: Undefined symbol __divsi3 referenced from text segment
gcc_v1: Program ld got fatal signal 1.

I’ve had some issues with GCC and these ‘built in’ functions before.  This was an early major stumbling block back in the x68000 GCC days, where after a lot of searching I was able to find 68000 versions of various math routines that were in the native Hudson Soft assembler to link in.  While GCC 1.x does have a libgnu/gnulib to include these functions it warns you over and over to not use GCC to build them, but rather the native CC.  But the problem is that I don’t have a native CC.

But I managed to save myself after googling around by finding srt0.c from 386BSD.  Namely these two:

.globl ___udivsi3
___udivsi3:
	movl 4(%esp),%eax
	xorl %edx,%edx
	divl 8(%esp)
	ret

.globl ___divsi3
___divsi3:
	movl 4(%esp),%eax
	xorl %edx,%edx
	cltd
	idivl 8(%esp)
	ret

I ended up having to removing a single underscore, but now I could link infocom, and even better it runs!

Wanting to try something far more exciting, I went ahead and tried to build DooM.  However, GCC 1.27 has an issue with m_fixed.c  I fired up GDB to at least take a look, although I’m not sure where the fault lies.

FixedMul
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Breakpoint 1, 0x752c5ad5 in msvcrt!_exit () from C:\WINDOWS\System32\msvcrt.dll
(gdb) bt
#0 0x752c5ad5 in msvcrt!_exit () from C:\WINDOWS\System32\msvcrt.dll
#1 0x752bb379 in msvcrt!abort () from C:\WINDOWS\System32\msvcrt.dll
#2 0x0045805c in final (first=0xe066a0, file=0x75312688 <msvcrt!_iob+128>, write_symbols=NO_DEBUG, optimize=0)
at final.c:653
#3 0x00403198 in rest_of_compilation (decl=0x722718) at toplev.c:1296
#4 0x0040fbce in finish_function () at c-decl.c:3272
#5 0x004040c0 in yyparse () at c-parse.y:224
#6 0x0040239d in compile_file (name=0xe00def "C:/dos/xdjgpp.v1/tmp/cca02992.cpp") at toplev.c:837
#7 0x00403a33 in main (argc=11, argv=0xe00f90, envp=0xe01598) at toplev.c:1556

With the code being:

#ifdef REGISTER_CONSTRAINTS
	    if (! constrain_operands (insn_code_number))
	      abort ();
#endif

So I assume some error with constrain operands? Not that it makes it any better.  However, I know this one file compiles fine with 1.39, and since we are on the i386 another alternative is just to use the assembly version that was hiding in the readme..

DooM mostly built by GCC 1.27

And much to my amazement, it works!  Keeping in mind that this is using an a.out tool chain, along with the last DPMI enabled GO32 extender.

Considering the compiler dates back from September 5th, 1988 it’s really great to see it running.

I’ll have to upload source (GCC 1.27/DooM) & binaries later.  But I imagine it should also run on EMX/RSX for a Win32 version.

DooM for the VIC 20

I just found out about this one, it’s DooM! … on an 8bit.  No really!

I was watching this video of the 8bit guy, and (jump to 19:20) he quickly mentions DooM on the VIC 20.

Over on Denial, The Commodore Vic 20 Forum  Kweepa did the seemingly impossible by using cc65 and some assembly for a somewhat cut down (haha!) DooM on the 8bit.  The source is even available on github.

It has cut down the video rendering obviously, but still has some of the C enemy AI logic, it’s a bit more like Wolf in that there is no height in the cut down levels, but wow it’s still amazing to see.  The sound effects were re-cut from the PC speaker stuff, and yes, even reduced versions of the MIDI music make the cut!

Naturally this won’t run on a stock 20, rather you need to get the maximum 37kb of RAM, and it requires a floppy disk drive.

It’s really cool to check out, and yes it runs on VICE.

I’m more so surprised that this didn’t ignite more 6502, or other non 32bit machine versions of DooM.  You can find download information for the d64 image here, and a direct link here.

Started to re-build my MS-DOS machine

DooM!

Good news, it actually works!  I was using the version 1.1 WAD, so honestly weird crashes really aren’t unexpected.  I haven’t looked much at what to do with audio, but I was really impressed compared to the Qauake II wars, it was really surprising to not only see DooM run on the first shot in real metal, but the keyboard works as well.  Well enough for me to pick a level, and get killed.

Naturally it doesn’t work under Windows, however it runs fine with MS-DOS mode.

Null DooM, GCC 1.39, GO32 and DPMI


phew.

DooM via DJGPP v1 GO32

Around the time of the x68000 port of DooM, I was cutting down the DooM source for a null/portable version.  I never could get it to actually run either using EMX or  DJGPP 1.03, as I couldn’t get it to link to save my life with a constant never ending battle of unresolved symbols. After a while I just used what I had towards the x68000 version and concentrated on getting it up and running, and just shelved the null/portable effort.

Later on I wanted to get it running again as part of messing with another cross compiler, as DooM isn’t a trivial application to port and verify correct operation. And in the process of trying to get the null version to build and run on Windows using TDM GCC, I wanted to make sure it at least kept compiling with GCC v1.x.

Once more again I was able to compile individual files but unable to link.  But this time, I just looked at the diffs for binutils, I thought it should be somewhat easy to get hosted on Windows.  Although versions may point to binutils 1.0, I had to use binutils-1.9.tar.gz even though the diffs are against Mar 24 1991, and the source for 1.9 is dated April 17 1991.

My first effort gave me a linker that would happily link, but go32 would either refuse to run the executable, or just crash.  I was going to give up again, but I found mention in another file that DJGPP actually uses the linker from G++, the C++ compiler which was a separate thing in the late ’80s and early’90’s.  This time it worked, and I could link a trivial hello world style application!

Now that I finally had a cross linker actually working, I didn’t want to compile under emulation, so looking at the other diffs, they didn’t look too extensive. I went ahead ,and took DJGPP v1.06 and patched up the compiler & assembler to get a full cross toolchain.  And in no time, I had a null version of DooM running on MS-DOS well at least tested on DOSBox.

This was fun, and all but I didn’t see any easy way to do fun things like hook interrupts so I could get the keyboard & clock like any good MS-DOS program.  DPMI greatly eased this kind of stuff, so looking at the DJGPP history, DJGPP v1 version 1.10 actually adds preliminary DPMI support!  And in the next version, DPMI was much more better supported, however the binary format had changed from a.out to COFF as part of the move to v1.11. I was able to take the memory, and DPMI portions from the final v1.12 libc, and manually build and run them against the v1.06 library / dev tools.

And much to my surprise, it actually worked!  At least having the wrong format didn’t have any effect on how GO32 worked for me.

So feeling lazy, I snagged some of the support code from Maraakate’s revamp of DooM, just to make sure of the timer code, and the keyboard code, and again verified that I can build with the keyboard & timer ISR and I’m able to play the v1.9 shareware & commercial levels fine.  I haven’t done a thing to clean up or update the DooM source itself against all the dozens of bugs and issues with Ultimate DooM, or other games like Chex Quest etc.

I’m sure 99% of people wouldn’t care but you can download it here:

Win32_DJGPPv1_DooM.7z
Download crossdjgppv1

Although I’m using DPMI to drive realtime events, if I looked further at the GO32 v1.06 environments I could either figure out how it operates it’s timer, or modify the extender directly to drive the PIC timer and keyboard as I need.  But overlooking that, the vintage 1991 software is more than capable of running DooM.

Porting DooM to the x68000

Getting started

DooM is without a doubt one of the most popular PC games of all time.  And thanks to it being written in C is also an incredibly portable game.  One platform that mysteriously was lacking DooM was the SHARP x68000.

After a bored day of playing with the source to Mariko’s GCC 1.42 / 1.30 that targets the x68000, I thought I would take a stab at trying to compile DooM.  Since I’m using such an ancient version of GCC the first stumbling block is that DooM is FULL of C++ style comments, which older K&R & ansi based compilers of the late 1980’s simply cannot handle.  So the first phase was to convert all the comments.

In order to convert the comments, I came across this great tool, uncrustify.  The pain is that it doesn’t seem to take wildcards, but you can use make to have it do your work for you, or just a batch file…

uncrustify.exe --replace -c 1.cfg cl_main.h

you get the idea.

The key thing is the configuration file that tells uncrustify what to do.  To convert C++ comments to C is quite simply:

cmt_cpp_to_c = true

And away we go.  Having learned the ‘null’ lesson of Quake 2 the hard way, I started out with a working copy from Windows, via GCC 1.40 for Windows/RSXNT.  I figured that by having a ‘known good’ build with the a very close compiler level would be a good start as I don’t want to fight too much with the compiler.  After it was running with minimal changes, it was time to start the real fun.

Starting the actual port aka platform issues

The first error I hit was:

Error: Couldn’t realloc lumpinfo

For some reason the SHARP/Hudson LIBC has issues doing a realloc.  I have no idea why.  Over on nfggames Neko68k had mentioned that he had a disk image with a working version of GCC, that uses different includes/libraries that was able to get further.  I wasted some time by trying to bypass the Sharp LIBC malloc function by calling the HumanOS’s malloc directly which did get further but ran into issues when switching from usermode to supervisor mode to directly access the hardware.  Once when he shared his disk image, I was able to see how his GCC setup worked, and more importantly linked, so I could alter the GCC cross compiler I was using, and get much further in terms of progress.  I could then get from failing malloc to this:

startup errors

startup errors

And from there after trying different assemblers, flags, and all kinds of other things we could finally get null DooM running on the x68000 via 68030 emulation on XM6 TypeG.

null DooM running on the x68000

null DooM running on the x68000

DooM comes to life

From there, Neko68k was able to do something amazing, add in system support!  Which to be honest would have taken me forever to do, I was more impressed that I was even able to get the null version running, but Neko68k blew me away with this:

There is no correct palette setup at this point, there is all kinds of issues but you can see the startup logo being painted!

Then with a lot of improvements, and an added keyboard driver it was starting to look like DooM!

And then Neko68k had a major breakthrough with the video, timer and keyboard, and we now have a playable port!

Issues while cross compiling

Around this time I had noticed that when I built a cross compiled version the video for me was garbled.  After some investigating it turns out that m_swap was not being compiled correctly but rather the endian order was being reversed!

 .dc.l $00000000,$40f00000

instead of:

.dc.l $40f00000,$00000000

I tried re-building, re-configuring my host setup, and I still had the same issue.  I tried downloading GCC 1.42 and building an i386 SYSV to AT&T 3b1 cross compiler as it too is 68000 based, and I got the same issue.  Maybe it’s a bug in GCC 1.x cross compilers?  I don’t know, but since the procedure is small enough, it was easier to just have the native GCC produce an assembly version which I just assemble and link without issue.

Behold! DooM on the x68030!

Yes, there is no audio, but wow it’s playable!  I do need to map the keyboard better in the emulator, but the key layout in the source is fine.

Downloads

For anyone who cares you can follow more of the porting adventure here:

http://nfggames.com/forum2/index.php?topic=6140.0

Source & binaries are here:

https://sourceforge.net/projects/x68000-doom/

And my cross compiler toolchain is here:

https://sourceforge.net/projects/gcc-1-30-x68000/

DooM 1.2j1.0 on NekoProject II

So the first thing you’ll need is Neko Project II.  It can be a little hard to track down downloads, but there is a whole slew of them here:

http://ux.getuploader.com/emu/index/1/date/desc the site has since moved to here:

http://nenecchi.html.xdomain.jp/

https://simk98.github.io/np21w/download.html (it moved again)

So for now this link, is the latest build, which was last updated on

Extract that, and rn np21nt.exe

You’ll want to configure the sound.

MIDI options

MIDI options

If you choose to use the MIDI you’ll have to map them to a MIDI-OUT port, and I used the default Microsoft GS Wavetable.  Of course you could use MUNT, or any other MIDI mapper or port.  Also you may want to setup the serial port MIDI as a backup plan.

Serial MIDI

Serial MIDI

The sound effect settings work best for the PC-9801-86 audio board.

xx

Select the correct board!

I’ll save installing MS-DOS, and installing DooM for another fun episode, but to configure DooM.

Run setup.exe to setup DooM!

Setup menu

Setup menu

The menu is simply:

  • 1 graphics
  • 2 Background Music
  • 3 sound effects
  • 4 not sure
  • 5 controller

Graphics

Graphics

The PC9821A driver works best from what I’ve done in my limited testing.  I guess if you had a different emulator, or a real PC-98 you’ll get more out of this.

Next is the BGM or music

BGM

BGM

You really have 2 options here, #3 for the PC9801 driver which uses the YM2608 chip.  Or the General MIDI either option 4 or 6. I didn’t notice any difference between the two of them, they both sound kinda slow, but workable.

Now for the audio board, select the PC-98

Doom sound drivers

Doom sound drivers

The PC-9801-86 is what you want here.  Now with either a 100% PC-9801-86 config, or a 50/50 of the MIDI/PC-9801-86 we are ready to run DooM!  Selection option 6 and away we go!

Save settings and run DooM

Save settings and run DooM

And all being well you’ll get the start of DooM!

DooM starting up

DooM starting up

Otherwise, you’ll get this fun error:

DX386 error

DX386 error

In this case I had emm386.sys in my config.sys which conflicts with the dos extender DX386.

Personally, I find it easier to boot off the #1 install diskette which will automatically start DooM!

If you are feeling brave, listen!

Burgertime 7/12/2015 aka DooM 3DO port

I found this talk rather interesting!

I didn’t know that the Jag DooM was considered a reference platform.  It’s an interesting talk about the rush job that was 3DO DooM, along with a small talk on fixed point math and other inside information on software development.  It is a little long, just over an hour.