5.00 avg. rating (94% score) - 1 vote
I have always been a fan of Super Mario game (and its variants) ever since the first time I touched the computer keyboard. I remember the first time playing it on my old 80386 computer and could not get passed the canal in the middle of level 1:
After I managed to get past the canal and proceeded to higher levels, it seemed that I could not get through level 4:
I decided to give up and did not attempt the game until years later when I had an Internet connection at home and soon figured out that I was playing on an uncompleted version of the game. By then (around the year 2000), Mike Wiering, the original author of the Mario game for MS-DOS, has released the source code on his website. Unlike my version, which proceeds directly to level 1 upon startup, the full version supports 2 players (MARIO and LUIGI) and has a menu with some other options:
Compiling the source
The game will not run on modern computers – it stopped at a black screen upon startup, perhaps due to some illegal VGA function calls. It also cannot run on Windows Vista and above, or 64-bit version of Windows, due to a lack of 16-bit compatibility as well as full-screen support. These days, DosBox is the only option if I want to play the game. Interestingly, this MARIO game, and similar games by Wiering Software such as Charlie II, Charlie the Duck or Super Angelo play fine on DosBox but seem to have timing issue (the speed is very fast) when run from inside a virtual machine such as Microsoft Virtual PC, VmWare or Sun VirtualBox.
With some Pascal programming knowledge and time at hand, I decided to have a closer look at the source code, to figure out how Pascal is used in game programming, and this article will discuss some interesting facts that I have found.
The first thing I learned was that the released executable was packed (as described in the README.TXT provided with the source code) to reduce file size to 57KB, perhaps with some MS-DOS packing utility. The compiled executable can be as big as several hundreds KB. In those days with 360KB floppy disk, this was probably a huge concern.
Source code organization
The source code is quite well organized into several Pascal unit files (*.PAS) and sprite include files (*.00?). Variables are well named and procedures are well structured. Although there are few inline comments provided since the code was never meant to release to public, the code can be understood and modified by anyone willing to do so.
The description of the main source code files can be found below:
MARIO.PAS: The main application
WORLDS.PAS: All level data are hard-coded here
BACKGR.PAS: Unit to support drawing the game background such as skylines.
BLOCKS.PAS: Assists in the drawing of animation
BUFFERS.PAS: Support reading of level and sprite data into buffers
CPU286.PAS: Halt the program if a CPU older than 286 is detected
ENEMIES.PAS: Define Mario’s enemies, such as turtle, fish or moving objects
FIGURES.PAS: Define behavior of objects along MARIO’s way, except for enemies
GLITTER.PAS and TMPOBJ.PAS: Display glitters such as stars that show when Mario hits coins or an object.
JOYSTICK.PAS: Support the use of a joystick
KEYBOARD.PAS: Process keyboard input
MUSIC.PAS: Play sound using PC speaker
PALETTES.PAS: The color palette used to draw the game
PLAY.PAS: Main game logic, e.g. how MARIO interacts with enemies, objects, earn coins, etc.
PLAYERS.PAS: Define the behavior of MARIO and LUJI.
STARS.PAS: Draw the stars on the sky
STATUS.PAS: The game status line
TXT.PAS: Text processing unit
VGA256.PAS: Custom Turbo Pascal VGA unit (Mode 13h, 320×200 256 colors)
Creating sprites
Sprites are first created using GRED.EXE (see GRED.TXT included in the source code):
It will be saved as a binary file (*.000, e.g: TREE.000), and then exported to a Pascal file that looks like the following:
The Pascal file is named TREE.$00. If a sprite has multiple states, as is the case for animated object, the extension is incremented, e.g. TREE.001 and TREE.$01. Sprites will be included as an include file in FIGURES.PAS:
{$I Tree.$00} {$I Tree.$01} {$I Tree.$02} {$I Tree.$03}
The point here is to store all sprites and level information into the code section, not the data section, of the program. The data segment in Pascal program can only contain up to 64K of data, and the game may grow beyond that. If slow read speed (earlier games ran on floppy disk) and having the game in multiple files was not an issue, an alternative would have been to store the data as external file.
Code would then be written to access the disguised data stored in the code segment by means of procedures consisting entirely of DB directives. The following will draw TREE000 at the specified location:
PutImage (XPos, YPos, W, H, TREE000^);
PutImage is defined in VGA256.PAS:
procedure PutImage (XPos, YPos, Width, Height: Integer; var BitMap);
Level data
As mentioned in README.TXT, there is no level editor for this game. All levels are coded in WORLDS.PAS:
A typical level consists of 2 procedures, a level data file (Level_1a) and an option file (Options_1a). Similar to sprites, they are just assembler procedures having only DB directives to store data. The option file will define how the level data will be interpreted. Take a look at Intro_0 and Options_0, for the ‘intro’ level, which is the background shown behind the selection menu:
Each assembler directive defines each vertical portion of the screen. One DB is a string of 13 characters. Each character defines an object on the screen, from bottom to top. The character 0 marks the end of a level (e.g. DB 0). The same character may be interpreted differently in different levels if the level options are different – see function ReDraw() in FIGURES.PAS. All level data will be loaded into variable WorldMap (found in BUFFERS.PAS) using ReadWorld(). Some levels may have certain pipes where Mario can dive in to enter a different area – these are defined as sub levels, for example, see Level_1b.
A modern approach
With some free time at hand, I decided to try out and see how the level data can be re-used to display an overview of each level, without re-writing everything from scratch. My first task is to save the sprites as an image file, which was easy since the GRED file format is documented. It wasn’t long before I managed to write a tool in VB.NET that loads a sprite binary file and display in a PictureBox:
All sprites will then be converted to VB.NET resources. The next challenge would be to export the level data. Based on function PlayWorld in WORLDS.PAS, I wrote my LVL2BIN.PAS which exports all level data to a binary file (*.LVL):
It is used as follows:
WriteLevelToBin(@Level_1a^, ‘Level1a.LVL’);
For the level options, to facilitate modifications, I did not export it to binary file, but instead convert to an XML file:
Most of the code is available from function ReadWorld() in BUFFERS.PAS. I adapted them to VB.NET with some minor modifications to cater for zero-based array index in VB.NET (Pascal supports non-zero-based array) and the lack of
set data types in VB.NET. For example, the following simple Pascal code:
var Ch: Set of Char;
….
Ch = [C] + [#1 .. #13];
turns complicated and probably more expensive in VB.NET – a
List has to be used to emulate a set:
Dim Ch As New List(Of Char)
Ch.Add(C)
For i As Integer = 1 To 13
If Not Ch.Contains(Chr(i)) Then Ch.Add(Chr(i))
Next
The following shows the level viewer in actions. it reads level data (.LVL) and options (.XML) and display it on screen:
(For simplicity, enemies and background are not yet drawn)
When I was writing the code, there was something which surprised me. Despite the different look of the bricks between intro level (level 0) and level 2 (see image below), they actually come from the same sprite (BRICK0.000).
The tricks are in the following 2 functions in FIGURES.PAS:
procedure ReColor (P1, P2: Pointer; C: Byte);
procedure ReColor2 (P1, P2: Pointer; C1, C2: Byte);
Both were written in assembly:
All that the seeming complicated assembler code above will do is to loop through every pixel in the image and modify its color by 1 (for ReColor) or 2 (for ReColor2) constants to make the new image look different. This allows the same image to be used for 2 different levels yet still look different. I converted both of them to VB.NET:
Private Function ReColor(ByVal fig As Bitmap, ByVal factor As Integer) As Bitmap
Private Function ReColor2(ByVal fig As Bitmap, ByVal factor1 As Integer, ByVal factor2 As Integer) As Bitmap
However, despite using the exact same constant and same color palette, my resulting display of level 2 does not look exactly the same:
I am unable to locate the exact problem and can only assume it’s due to something I might have overlooked.
At this point I can proceed to draw the background, animated sprites (turtles, fish, …) and implement the proper game if I want.
Easter eggs
The game has some Easter eggs which can be found in PLAY.PAS. To activate cheat mode, press P to pause the game, then press TAB. Pressing 2305 while in cheat mode will get you through the next level. Also if you prefer to play in grayscale, press MONO:
Similar games: Charlie the Duck, Charlie II and Super Angelo
According to Wiering Software, these 3 games are developed based on the original Mario source code; however, their source code was never released. Charlie II (my favorite game) also has a Windows version which was perhaps written in C++. As of 2011, it’s pretty clear that no future DOS versions of these games will ever be created, and any future version will perhaps only be in Flash. Click
here and
here if you want to try out. Nevertheless, I hope this article will be useful for those who want to port the game to other platforms, or to learn something about game development in Pascal.
By the way, if you’re playing Charlie II and receive an error “Unexpected error no XXXX, please contact Wiering Software”, don’t think it’s a bug with the game. Most likely you’re using an activation key generated by a crack tool which does not satisfy all the requirements. In this discussion, Mike Wiering said that several checkings of the activation code are done at various places using various different algorithms in the game and a cracker may not have located all those places. So if you got the error, simple try a different key.
The full .NET source code to convert sprites and view the levels can be downloaded here.
See also:
The good old days: cracking 16-bit DOS games
Experimenting with DOSBox SVN-Daum, a custom DOSBox build by ykhwong
References:
1. Open Game Source: Mario Clone
2. Wiering Software – Creating Games
3. Using haXe for Platform Games
5.00 avg. rating (94% score) - 1 vote
thank you cool info
Hello; if you don't mind, could I ask you a few things regarding this game?
Justin, just type your questions here![Smile :)]()
HEY, I HAVE A DIFFERENT PROBLEM. YOU SAID IS VERY FAST BUT FOR ME ITS REALLY SLOW…. LIKE EVEN USING CONTROL IS DAMN SLOW. IM USING DOSBOX TOO, WINDOWS 7. WHAT SHOULD IDO
Thanks a lot! I was using DrawImage function instead of PutImage, and was getting double buffered error. Thanks! The function PutImage solved the problem.
Press S. The HUD will dissapear, and it'll run faster. It worked for me.
Press Ctrl + F12 like 12 times, then you get a reasonable speed
Could I get a compiled version of your viewer please![Smile :)]()
Hi.
You can download the source code of the level viewer from the link at the bottom of the article. From there you can compile it using Visual Studio 2008 or newer (VB.NET). I may be able to upload the compile executable when I have the time to.
Thanks – Im looking to do a conversion to a retro machine and im abit behind the times on VB.net
I think i've got it and cool Blog
– If I do use ill be sure to credit you
Great! Let me know if you need other information. Looking forward to any progress updates on your retro machine project.
Hi MD,
maybe you can help me a little bit and thanks for sharing your knowledge.
thanks for this great articel. I want to compile the source for TP 5.5 but i get an error. There is an file not found in the palettes.pas. (MPAL256.). do you have an idea what can be the problem? and another question: is it possible to convert a png sprite back to the GRED fileformat? i know its an old sourcecode in an older language like pascal but i'm very interested in to learn the basics.
Axel//germany
Hi Axel,
The error you saw may be because all units have not yet been compiled so it could not find CPU256.TPU when compiling MARIO.EXE. To solve that you need to open a DOS prompt, change directory to the Mario source code folder and type the following command:
C:\TP55\TPC /M /L MARIO
where C:\TP55\ is the path to your Turbo Pascal 5.5 folder and TPC is the executable name for the Turbo Pascal Compiler. Try to store the source code and Turbo Pascal in the root folder of the drive and keep the directory name short as DOS programs doesn't like long file names.
Make sure that you have Turbo Pascal version 5.5, 6.0 or 7.0. You can download v5.5 (free) from http://progopedia.com/version/turbo-pascal-5.5/ . The source code for Turbo 5.5 can be downloaded from this site http://www.wieringsoftware.nl/mario/source.html and extract it.
As for GRED, it supports saving sprites into a proprietary binary format or Pascal/C source code. It only supports reading sprites from its own proprietary binary format so it's not possible to tell GRED to load a PNG file. What you can do, however, is to write a Windows program (in Java, C# or VB.NET), load a low-res PNG file and save it in the GRED file format. The file format is documented in the GRED.TXT file, located in the source code folder.
You can refer to the .NET sample code downloadable at the end of the article for some hints.
Let me know your progress and if you have any questions.