Technical info

First a warning: you don't need to read this unless you wish to know more about internal working of SWOS and SWOS++. I'm also assuming that readers have at least some programming knowledge. Even then it might be confusing.


About SWOS
Sprites Map
About SWOS++

About SWOS

I will not discuss much about Linear Executable format. For more info about it see Very useful utility is DumpLX from Tenth Planet Software. For hex editing I used great hex editor HIEW (Hacker's View). Sorry, no link (I got it from some compilation), but Google should find it.

Basic info about executable:
filename: sw(o)s.exe
filesize: 2135087 bytes
file format: Linear Executable
offset to LE header: 0x2c90
fixup section size: 657660
offset to fixup page table: 0x694 (in file 0x3324)
offset to fixup record table: 0xc30 (in file 0x38c0)
number of pages: 358

There are two objects (in LE terms) in file: code and data.

Virtual size of code segment: 658297
Relocation address: 0x10000 <- important
Attributes: readable, executable, has preload pages, nonpermament
Page map index: 1
Page map entries: 0xa1

Virtual size of data segment: 810944
Relocation address: 0xc0000 <- important
Attributes: readable, writeable, has preload pages, nonpermament
Page map index: 0xa2
Page map entries: 0xc5

In file, offset for start of code is 0xa3e00, and offset for data is 0x144e00. From now on, each offset I write will be relative either to code or data start, depending on context.

PC version of SWOS was written as a conversion from Amiga. It can be deduced by analyzing the code, and there are even some left over strings such as "INSERT BLANK DISK IN DF0:". There are 15 memory locations (dwords) that are used as registers. Their start is at 0x3146d. These dwords correspond to Amiga's MC68000 processor registers: data registers D0-D7 and address registers A0-A7. This is not a mistake (15 locations for 16 registers) because register A7 (stack pointer) is mapped directly to Intel's esp register. This means that PC version of SWOS is using so called static emulation, where whole program is translated into host machine language and then executed. Other type of emulation is dynamic emulation, where an interpreter is used that interprets original code run-time (this is the way most emulators operate). Since SWOS is a commercial game, it was more feasibile to use the first approach. I think they did the following: first Amiga's source code (which I believe was mostly written in assembler, or some optimizing C compiler - somebody correct me if I'm wrong) was converted into C code, probably using some third party utility. Than they rewrited some heavily used routines completely in x86 assembler for speed, and rewrited non-portable routines (file handling, input handling, video) to get fully running x86 executable. Compiler used to put all this together was Watcom C - sw(o)s.exe contains whole Watcom run-time library, both 16 and 32-bit versions.

Consequences of this are that the executable is relatively large. It is also missing bss sections commonly found in other executables, and every static array is put in data section filled with zeroes. Heavy use of memory locations instead of processor's registers brings to code size explosion. For example instruction:
mov dword [tmp10], aDataEurocup_tm
is coded in 10 bytes: c7 05 91 14 0f 00 2a 73 0C 00 + fixup record while instruction:
mov eax, aDataEurocup_tm
is coded in only 5 bytes: b8 2a 73 00 00 + fixup record.
Some MC68000 registers could be mapped directly to x86 registers to shrink size a little.

For sound AIL (Audio Interface Library) version 3.03 was used. Unfortunately it is commercial software, publicly unavailable, so I didn't have access to informations about it.

I will enlist some of the most interesting locations in the game, and short descriptions, if available. 0001 means offset is relative to start of code section, and 0002 means relative to data section.
(Note: MC68000 registers are marked as tmp01 - tmp15)

section:offset symbolic name description
0001:00000010 main_ C main() routine - start of program.
0001:00005A0D MainGameLoop Game's main loop.
0001:000074FD ViewHighLightsMain Main routine for viewing highlights.
0001:00007DC3 LoadCommentary Load commentary from CD-ROM.
0001:000092BE Initialization Main program initialization.
0001:00009870 Int9KeyboardHandler Keyboard interrupt handler.
0001:0000C7E2 DrawSprite Draw sprite. In: tmp01 - sprite number, tmp02 - x, tmp03 - y
0001:0000D672 ShowMenu Show menu. In: tmp15 -> menu struct
0001:0000D826 InitMainMenu Main menu initialization
0001:0000F1CE DrawMenuText In: tmp02 - x, tmp03 - y, tmp04 - char color, tmp09 -> text, tmp10 -> chars table
0001:0000F55F ZeroOutStars Overwrite stars in strings with zeros.
0001:0001193D QuitToDosMenu Show quit to DOS menu.
0001:00012E41 EditCustomTeamMenu Show edit custom teams menu.
0001:000229C3 LoadTeamFile Loads requested team file.
In: tmp01 - team file extension number (100 = customs.edt)
Out: tmp01 - 0 = ok, 1 = error
zero flag: set = all ok, clear = error
0001:00023413 LoadHilFile Load highlight file into buffer.
0001:0002F588 ShowPlayMatchMenu Show play match menu.
0001:0003018B ViewOpponentsSelect Called when view opponent is selected.
0001:0003E7E3 ImportTactics Show import tactics menu.
0001:00041FD2 LoadTactics Shows menu and loads tactics.
0001:00059389 CheckControlsInGame /
0001:0005B89E DrawSubstitutesMenu Draws substitutes menu during the game.
0001:0005DE4C not named very important routine, initializes everything before the game
0001:0006EA0C not named probably the most important routine in the game; currently it is only slightly explored, but I suspect it contains AI, players updating, ball updating and a lot more
0002:0003E152 lin_adr_384k Important pointer to memory block that holds virtual screen, title and fill.256 during menus, and pitch during the game
0002:0003E192 working_palette /
0002:0003EA98 key_count number of keys in buffer
0002:0003EA9A key_buffer 10 bytes key buffer
0002:0003EAA6 scan_code scan code of most current key
0002:0003EAD0 converted_key ascii code of pressed key (1 = escape)
0002:0003ECCF play_game play game while this is true
0002:0003ED10 char_tables two pointers to char tables (small and big)
0002:00054B56 game_min_substitutes /
0002:00054B58 game_max_substitutes /
0002:000B119B commentary_table pointers to commentary filenames
0002:000B2A38 teams_country_numbers byte at offset 0 in team is used as index into this table; value from table is added to offset 1 in team, forming team general number
0002:00070AFF pitch_number decides which pitch to load
0002:000BBDC8 club_messages_table table of pointers to messages from club and chairman
0002:000BDA7C game_time game time in BCD format

For Intel 80x86 assembly language proabably the best reference is book "Art of Assembly language", available at For Amiga assembly language, I used "AMIGA MACHINE LANGUAGE - FROM ABACUS BOOKS", available somewhere on the Net (archive name: amigamachine.lha).

Sprites Map

Sprite 1286 contains an error: width is erroneously set to 5 pixels. It should be 16 pixels.

0-162 characters

0-56 small set:
0 - x in a box
1 - '
2 - (
3 - )
4 - ,
5 - -
6 - .
7 - /
8-17 - numbers
18-43 - letters
44 - box (cursor)
45 - *
46 - pound
47 - longer dash
48 - ?
49 - :
50 - +
51 - %
52 - ;
53 - A with two accents above
54 - U         -||-
55 - O         -||-

56 - vertical bar

big set follows, for big character add 57 to small character index (except for number 56, it is unique)

113-122 stars, for player skills

123 - picture of a ball, probably for edit tactics menu

small players with corresponding color and shirt stripes; used for editing tactics (corresponding pictures are copied into them)
124 - white guy
125 - red guy
126 - black guy

small goalkeepers, used for editing tactics
127 - white guy
128 - red guy
129 - black guy

small players with plain shirts
130 - white guy
131 - red guy
132 - black guy

small player with colored sleeves shirts
133 - white guy
134 - red guy
135 - black guy

small players with vertical stripes shirts
136 - white guy
137 - red guy
138 - black guy

small players with horizontal stripes shirts
139 - white guy
140 - red guy
141 - black guy

small goalkeepers
142 - white guy
143 - red guy
144 - black guy

149 - cursor while editing tactics, frame 1
150 -                -||-                 2
151 -                -||-                 3

181, 182 - two empty sprites, they are located in edit teams menu, in invisible entries near the end

183 - up arrow (for scrolling)
184 - down arrow

player faces, during edit tactics (maybe used someplace else too)
186 - white guy
187 - red guy
188 - black guy

189 - empty sprite, also used in edit teams menu as a last, invisible entry

215 - national league champion
216 - invitation tournament winner
217 - national cup winner
218 - unknown trophy
219 - european champions cup
220 - european cup-winners cup
221 - UEFA cup
222 - world championship
223 - european championship

227-234 first scrolling advertisement
235-242 second         -||-
243-250 third          -||-

225-270 numbers + player names, written in upper left corner during the game, marking player with ball, team 1
271-286 numbers + player names for team 2
These sprites are filled before the game, they're intially empty.

307 - First team name
308 These two sprites have special properties, and special drawing function is used

330 - current game time (118. mins at the beginning, for maximum width)

This is still incomplete and under construction. If you find errors or wish to contribute to list, let me know.

About SWOS++

SWOS++ consists of three parts contained in files:, loader.bin and swospp.bin. This partitioning was done because of the nature of SWOS executable itself - it is in Linear Executable format. This means that the program is loaded by DOS extender to some, before loading unknown address. Address varies, and is different almost every execution time. Due to this fact, loader must do a process called "fix up". It consists of adding load address to every reference to data or code, which is invalid - therefore fixing it. To be able to do so, executable besides code and data also contains "fixup records" that are pointing to places that need to be fixed up. Fixup records point to various places scattered around the file, making direct patching by hand extremly labourous - besides patching, fixup records also need to be updated.

First part is program which will patch SWOS executable to allow loading of the rest of the program. After patching, SWOS executable will load loader.bin and execute it. Code itself isn't very interesting.

Second part is loader.bin. It is in some way similar to boot loader. It will load the main part - swospp.bin, fix it up and do patches to SWOS. swospp.bin has a strict structure, which enables loader to do all this and check file correctness.

Third part is swospp.bin, the main part. This file contains acutal code and data, relocation offsets and patch instructions. It is in custom made binary format.

Whole this process is somewhat similar to functioning of dynamic load libraries in Windows, or generaly dynamic libraries in operating systems.

Most of the code was written in Intel x86 assembler using NASM (Net-wide assembler) and some small parts were written using excellent Watcom C compiler (which was by the way used for compiling original PC SWOS). Because of very flexibile nature of build system even C++, Pascal and perhaps some other comilers could be used too.

SWOS++ binary format makes making patches much easier than before - there is absolutely no need for hex editing. All patching is done after the program is loaded into memory and fixed up by loader, so there is no need to worry about fixup records overwritting code.