Mortal Kombat Trilogy is a 1998 reskin of Cony Soft's Mortal Kombat 5, itself an unlicensed NES adaptation of Mortal Kombat 3. This version adds a Trilogy introduction screen and a "HOSEKN" title-screen credit, and doubles the original eight-fighter roster through duplicates rather than eight new characters.1
Hosenkan Electronics has been suggested as the publisher because of the "HOSEKN" credit, but that identification is unconfirmed.
Hidden Two-Player Join#
During a one-player fight against the computer, press A + B simultaneously on Controller 2. This interrupts the fight with the following message:
PLAY TWO HAS
ENTERED TOURNMENT
Both mistakes are in the game's text: "PLAY" instead of "PLAYER", and "TOURNMENT" instead of "TOURNAMENT".
After a short delay, the game returns to the character-selection flow with both players active. The feature is already enabled; no cheat device or ROM patch is required. It provides an arcade-style way to interrupt a single-player match and admit a second player.
The input check at CPU address C1F4 tests whether the opponent is computer-controlled, then checks the A and B bits in Controller 2's held-button state. Holding both buttons together works; there is no longer sequence to enter.
16-bit x86 Remnants#
Several areas of the PRG-ROM contain 16-bit x86 machine code, alongside the game's native 6502 code. The surviving routines include text-record generation, checksum calculations, string copying, and buffered data output. They use x86 stack frames, segmented memory pointers, far calls, and far returns.
Locations#
The table below lists all 16 likely locations. These are extraction windows containing the remnants and adjacent ROM bytes; individual routine locations are given in the sections below.
All offsets are relative to the start of PRG-ROM, and the end offsets are exclusive. For the examined ROM, add 10 to obtain an offset in the complete .nes file.
Window |
PRG start |
PRG end (exclusive) |
Length |
Contents and matching regions |
|---|---|---|---|---|
00 |
002C49 |
00405C |
1413 |
Text-record generation, nibble conversion, checksum calculations, and buffer-transfer calls. |
01 |
006C49 |
008021 |
13D8 |
Contains the same 738-byte checksum and nibble-output tail as window 00, at 0078C8–008000. |
02 |
00AC49 |
00B01D |
03D4 |
Repeated block A. This entire extraction window matches windows 04, 06, 08, and 10 byte-for-byte. |
03 |
00B31F |
00C2B8 |
0F99 |
Contains the same 738-byte checksum and nibble-output tail as windows 00 and 01, at 00B8C8–00C000. |
04 |
00EC49 |
00F01D |
03D4 |
Repeated block A; identical to window 02. |
05 |
00F8C8 |
010021 |
0759 |
Repeated block B. Its 00F8C8–010000 bank-tail sample matches the corresponding samples in windows 07, 09, and 11. |
06 |
012C49 |
01301D |
03D4 |
Repeated block A; identical to window 02. |
07 |
0138C8 |
014021 |
0759 |
Repeated block B; matching sample at 0138C8–014000. |
08 |
016C49 |
01701D |
03D4 |
Repeated block A; identical to window 02. |
09 |
0178C8 |
018021 |
0759 |
Repeated block B; matching sample at 0178C8–018000. |
10 |
01AC49 |
01B01D |
03D4 |
Repeated block A; identical to window 02. |
11 |
01B8C8 |
01C01A |
0752 |
Repeated block B; matching sample at 01B8C8–01C000. |
12 |
01D995 |
01E01C |
0687 |
Formatted text output, a 78-asterisk separator, block-buffer reading and writing, and the 87-byte nibble-conversion routine. |
13 |
01EB02 |
01F020 |
051E |
Two- and four-byte ordering, numeric-field processing, line-buffer output, and a three-byte block-transfer call. |
14 |
01F254 |
01F317 |
00C3 |
String-copying loops, zero termination, and a call passing the completed buffer. |
15 |
01F8F3 |
01FFF7 |
0704 |
Record traversal, buffered output, another nibble-conversion routine, and a literal 2D 4C ("-L") comparison. |
The ROM contains several exact duplicates. Windows 02, 04, 06, 08, and 10 sit inside a larger 594-byte sequence repeated at PRG offsets 00AB69, 00EB69, 012B69, 016B69, and 01AB69.
The 738-byte bank-tail samples form two further sets: identical copies at 0038C8, 0078C8, and 00B8C8; and another version at 00F8C8, 0138C8, 0178C8, and 01B8C8. Each sample ends at its next 4000-byte boundary.
Byte-to-Pair Conversion#
A complete 87-byte routine occupies PRG offsets 01DF43–01DF99 inclusive. It splits an input byte into its upper and lower four-bit halves, or nibbles. Each half is passed to the helper at 0C9E:13A3, upper half first. The two returned bytes are written consecutively to a buffer.
Its operation can be summarized using descriptive function names:
output_byte(helper_0C9E_13A3((input_byte >> 4) & 0x0F));
output_byte(helper_0C9E_13A3( input_byte & 0x0F));
For example, input AB supplies 0A and 0B to the helper in that order. The same helper is used throughout the text-record generation code to encode numeric fields.
Selected instructions from this routine show its x86 structure:
Bytes Instruction
55 push bp
83 EC 06 sub sp,6
8B EC mov bp,sp
C4 36 06 C4 les si,[0C406h]
...
25 0F 00 and ax,000Fh
...
9A A3 13 9E 0C call far 0C9Eh:13A3h
...
26 88 04 mov es:[si],al
...
83 C4 06 add sp,6
5D pop bp
CB retf
The buffer position is held in a segment-and-offset pointer, and the routine advances it once for each output byte. 0C9E:13A3 is the x86 address encoded in the far call, rather than an NES CPU address.
Text Records and Checksums#
Window 00 includes several paths that construct text records one character at a time. The code at PRG offset 002ECD writes the prefix :02000002. Other paths begin with S903 at 003381, S804 at 00358C, and S705 at 00380E.
These paths append numeric fields through the nibble-conversion helper and calculate check bytes. The colon-prefixed path adds field values and negates the accumulated sum. The S-prefixed paths add their field values and invert the result with NOT AL. The resulting check byte is also passed through the conversion helper.
The records end with carriage return and line feed, 0D 0A. The code then calculates the number of bytes in the output buffer and passes the buffer and its length to a transfer helper.
Formatted Text and Separators#
Window 12 includes code that builds formatted text using spaces, colons, asterisks, and line breaks. A loop at PRG offset 01DE2B writes exactly 78 asterisks, followed by two line-feed bytes and a zero terminator. The surrounding code also writes space-padded fields and converts individual bytes through the 87-byte routine.
Window 14 contains string-copying loops. One reads an indexed entry from a table with a 32-byte stride; another appends bytes from the string at x86 data offset 14C0 until it reaches a zero byte. The completed output buffer is zero-terminated and passed to the helper at 0C9E:025B.
Buffered Data Output#
The remnants contain several buffer-management routines:
PRG range (inclusive) |
Operation |
|---|---|
01DE96–01DEFE |
Writes a byte through a segmented buffer pointer and manages blocks with a 1000-byte capacity. |
01DEFF–01DF42 |
Reads a byte, advances the buffer pointer, and selects the next block when the remaining count reaches zero. |
01FE6B–01FED7 |
Appends a byte to an output buffer; when the buffer is full, passes 1000 bytes to the transfer helper and checks the returned count. |
01FED8–01FF2F |
Calculates the occupied buffer length and passes that many bytes to the transfer helper. |
01FF30–01FF6C |
Splits a byte into two nibbles, converts each through 0C9E:13A3, and sends both results through the buffered writer. |
1000 bytes is 4 KiB. The two routines that transfer buffered output use the same far-call target, 22CA:000A.
Other Data Processing#
Window 13 includes paths that process data in two- and four-byte groups, passing bytes to helpers in different orders. Another part extracts three bytes from a value stored across two 16-bit words and passes the resulting three-byte block to the transfer helper.
Window 15 also contains code that traverses records in 3D-byte steps, tests flag bit 40, and processes 20-byte fields through a character helper. Near the end of the ROM, a separate fragment compares two input bytes with 2D and 4C — the characters -L — and advances the pointer by two on a match.
References#
- Mortal Kombat 5 — Bootleg Games Wiki, introduction, "Overview", and "Reskins".↩︎