In this post, I revisit Mike Wiering’s Mario game which was written in Pascal.
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 past 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, had 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 be released to the 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:
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 displays it on the 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 two 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 three 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.
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.
The full .NET source code to convert sprites and view the levels can be downloaded here.
See Also
References
- Open Game Source: Mario Clone