The two programs

Technical appendix · Home · Français

Summary: who does what

The game is two programs sharing one DOS session. MAUPITI1.EXE is the game; maup.exe is its engine.

MAUPITI1.EXE maup.exe
Written in Borland Pascal Turbo C and assembly
Role Decides what happens Does what touches the hardware or the data files
Handles Commands, time, schedules, conversations, puzzles, the quiz, sympathy; which layers make up a scene and which line to say Files and archives, image and text decoding, video, palettes and fades, music, speech playback, mouse, saves
Data Interprets the game tables in record 0x2A and the state from SAUP.MAU, once loaded Loads all files: .CON archives, MUS.HUF, SND.HUF, .MAU

How they run together:

  1. MAUPITI.BAT starts MAUPITI1.EXE, which goes resident and returns to DOS without playing anything.
  2. The batch file then starts maup.exe, which installs its services on INT 0FEh, probes the sound card and runs the intro.
  3. maup.exe calls back into MAUPITI1.EXE, whose main loop then runs the game until the end.
  4. From then on, MAUPITI1.EXE is in control and calls maup.exe through 51 INT 0FEh services.

So MAUPITI1.EXE loads first and maup.exe is the process that stays running, but MAUPITI1.EXE drives the game. For example, to say a line, MAUPITI1.EXE picks the phrase and the character, and maup.exe builds and plays the sound.

The details and evidence follow.

Files

File Size Notes
maup.exe 56,577 B 16-bit MZ, Turbo C 1988 runtime. Owns files, video, sound and mouse.
MAUPITI1.EXE 76,544 B Borland Pascal game logic with 51 INT 0FEh client stubs, run first by MAUPITI.BAT.
MAUPITI1.olz 32,717 B LZEXE LZ91 packing of the same program: it unpacks to an identical 70,912-byte image.
INSTALLE.EXE 12,684 B Turbo C installer; contains md c:maupiti, copy *.* c:maupiti and INSERT DISK No %d.

MAUPITI.BAT: echo off, maupiti1, maup %1.

maup.exe layout (verified)

  • e_cparhdr = 0x80, so the image starts at file 0x800 = EA 0x10000. CS:IP = 0000:0000, SS:SP = 11C4:00E6, 444 relocations.
  • dseg is image paragraph 0xA42 (EA 0x1A420); the stack is paragraph 0x11C4 (EA 0x21C40).
  • There are 24 relocated code segments: 0, 53, 84, 159, 2DB, 493, 5FA, 6F0, 742, 7AB, 805, 8E8, 968, 96C, 97F, 9DF, 9E3, 9E9, A1A, A2F, A31, A34, A3B, A3E. They are ordinary multi-segment program segments. Nothing in the file shows an overlay manager or overlay data.
  • The header's image size is 56,548 B. The last 29 bytes are a release-group trailer, letters shifted by -1 and parts separated by NUL: The X Web, - www.lankhor.net.
  • Functions commonly use push bp / mov bp,sp: 160 raw 55 8B EC matches.
  • INT 21h: 67 CD 21 sites (verified). The per-AH breakdown from earlier work is unverified.

Startup (verified)

  • 0x10102: C startup calls main at 0x53:0 = _main EA 0x10530, which calls 0x53:6 = Game_Main EA 0x10536.
  • Game_Main first probes ports 0x210-0x260 for a Sound Blaster with Snd_ProbeSbDsp EA 0x107FC (reset at base+6, expects 0xAA at base+0xA). It then stores the result in bSoundCard (DS:F6).
  • 0x105C0-0x105F2: stores ds:0xDA = 0x5000, then makes six far init calls. The last is 0x6F0:4 = Font_LoadCharset EA 0x16F04, which runs the intro.
  • 0x105F7: publishes maup.exe's DS at 0000:03C4.
  • 0x10603-0x10617: sets IVT[0xFE] to 0x53:0xF8 with direct stores. Segment 0x53 is relocated.
  • 0x10618: loads DS from 0000:03C6 and jumps to 0x84:0x3CB = EA 0x10C0B, which does call far [0000:03C8] into MAUPITI1.

INT 0FEh handler (verified)

The handler is in maup.exe at 0x53:0xF8 = EA 0x10628, just after a push bp / mov bp,sp at 0x10625.

  • AX = service number; CX = stack offset of a word argument block.
  • It saves registers, loads DS from 0000:03C4, sets bInInt0FE, and jumps to 0x10653 + (AX-1)*8.
  • Each 8-byte slot is call far target / jmp common_exit. Exit at 0x107E8, then iret at 0x107FB.
Svc Target EA Name Svc Target EA Name
02 0x15A38 Vid_DrawTextLines 1A 0x123F9 Con_LoadRec22Images
09 0x157C3 Txt_DrawIndexed 1C 0x1135D Vid_ArenaAlloc
0A 0x11A20 Ui_DrawWidget 23 0x11F39 Save_WriteGame
0B 0x11E12 Save_LoadGame 28 0x141D9 Mau_LoadFiles
17 0x1428C Ui_CaptureSpriteSheet 2B 0x148BC Demo_Init
19 0x123D4 Con_LoadRecordPlus1E others see symbols.tsv SvcNN if unidentified

The main groups read from their code:

Services Names What they do
03, 04, 05 Mem_PeekByte, Mem_PeekBE16, Mem_PeekBE32 read through a far pointer, swapping from big-endian
1F, 20, 21 Mem_PokeByte, Mem_PokeBE16, Mem_PokeBE32 the same for writes
01, 06 Mem_SumBytes, Mem_SumBE16 sum a table's first N entries
26 Table_EntrySize difference of two adjacent big-endian words
0F Table_GetEntry table lookup; MAUPITI1 uses it on the rec 0x2A buffer
15 Rand_Range value between two bounds, from counter DS:63F
0D, 0E, 1D, 27 Vid_ClearRect, Img_Draw, Vid_CopyRect, Vid_DrawLine drawing (the rectangle and line roles are inferred)
18, 1A, 1B Con_LoadRoom, Con_LoadRec22Images, Con_LoadRec23 load a room, a portrait, the map and small portraits
1E Speech_Say compiles and plays a spoken line: see Speech
14 Svc14 magnifying glass: redraws an area at double size with sprite 0x27 sub 7 (inferred)
22 Svc22 recolours the frame for the room, with table 0xC9 (inferred)
2C, 2D Snd_PlayMusic, Snd_Stop music (Snd_Stop is inferred)
31, 33 Time_DelayShort, Time_Delay delay loops

Con_LoadRoom is service 0x18. Pal_TimeOfDayB (service 0x2A) picks a room palette by hour; File formats, scenes and palettes has the whole palette system. Pal_TimeOfDayA (service 0x29) has the same hour test.

So MAUPITI1, a little-endian Pascal program, reads the game's big-endian data through these services.

maup.exe calling itself through INT 0FEh

CD FE occurs seven times, but 0x13B8F is the operand of the jmp at 0x13B8E. The six real sites:

EA Service Block size
0x12194 0x24 0x1C
0x121A9 0x0A 0x18
0x12269 0x09 0x38
0x12363 0x08 0x10
0x1238A 0x1D 0x10
0x123CB 0x24 0x1C

Low-memory mailbox

Slot Writer Contents
0000:03C4 maup.exe maup.exe DS
0000:03C6/03C8/03CA MAUPITI1 EA 0x20B80 its DS, then return IP+9 and CS
0000:03F8/03FA maup.exe IVT[0xFE]

MAUPITI1.EXE (verified with ndisasm)

EA = file + 0xEA00; header 0x1600 B; 1,378 relocations; entry EA 0x1EF37. Segments: 0 (main code), 0xF21 (one Keep routine), 0xF23 (library and service stubs), 0x10B8, 0x10F1 (Borland runtime), 0x114C (data).

How the two programs join

  1. Main EA 0x1EF37 initialises the runtime, then calls Mailbox_Publish EA 0x20B80. That stores its DS, return IP + 9 and CS at 0000:03C6-03CB.
  2. The next instructions call Dos_Keep(3) at EA 0x1F210 (INT 21h AH=31h). The program stays resident and the batch file starts maup.exe.
  3. maup.exe sets its services up, then does call far [0000:03C8]. Return IP + 9 is EA 0x1EF4D, the instruction after Dos_Keep, so Main carries on inside maup.exe's process.
  4. Main computes buffer pointers, then calls Game_LoadAssets EA 0x12722 and Game_InitOnce EA 0x186D9, then repeats Game_MainLoop EA 0x1EBC8 until DS:131 is non-zero.

So MAUPITI1.EXE holds the game logic and calls maup.exe for files, video, sound and input.

Findings

  • Stubs: there are 51, each mov ax,N / mov cx,bp / add cx,6 / int 0FEh, numbered 1..51. The other five CD FE hits are displacements of mov [bp+di-0x133],ax, starting at 0x17D6E. Call counts per service are in symbols-maupiti1.tsv; the IDA name Maup_Svc13 (7 callers) is decimal, so service 0x0D.
  • INT 21h: 21 sites. One is Keep; the rest are in the Borland runtime segment. Its vector calls target 00h, 23h, 24h and 3Fh, so none set vector 0FEh.
  • File runtime: the only far-called routines in the runtime are at 0x20F10, 0x20FE8, 0x21154, 0x2116D, 0x21189 and 0x211A4. The file INT 21h code at 0x2136E and up has no far caller, so game code doesn't use it. All game file access goes through services.
  • Hardware: a linear sweep finds no real in, out, INT 10h or INT 33h, and one iret at 0x20FD5. Its three in/ins hits and the CD 33 bytes are misaligned decodes or operands. The two rep movsb sites are runtime code, so nothing copies a handler.
  • Text: the seven printable runs in the file are runtime fragments, as earlier work found. The game text is in G3.CON.

Game state

Service 7 returns a far pointer to MAUPITI1's DS:166, the base of a 0x99B-byte state block. Game_MainLoop EA 0x1EBC8 fills it from SAUP.MAU (service 0x28), and restores from a save with service 0x0B. So SAUP.MAU is the starting game state, followed by 8 more bytes.

DS: SAUP.MAU offset Start value Role
166 0 26 current room number; its .CON record is this minus 1
168 2 20 time in half-hours, so 10:00: the palette code uses it as hours (File formats, scenes and palettes)
16A 4 0 passed with the time; high word or day count (inferred)
16C 6 79 condition, 0-99; 20-point steps choose "Lessivé" to "La pêche"
16E 8 50 hidden sympathy score, lowered by Game_AddSympathy (see Game logic)

Turn structure

Game_MainLoop loads the state and calls Room_Enter EA 0x141CB, then runs Game_Turn EA 0x1E220 until DS:132 or DS:134 is set. If DS:134 was set, it runs a closing sequence that draws a class 6 string. Main_Body repeats Game_MainLoop until DS:131 is set.

Loading: Game_LoadAssets EA 0x12722

Con_LoadRecord EA 0x20691 calls service 0x19 with record - 0x1E:

Record Sub Pointer (DS:) Size used in layout
0x28 0 A8 banks 1-7 of string sizes, 3,178 B
0x29 0 A4 banks 1-7 of text, 45,410 B
0x2A 0 AC 0xA39E = 41,886 B
0x27 0 E4
0x27 7, 8 F8, 104
0x27 5, 1 BC loaded for further processing
0x27 6 100

Next: Game logic · Technical appendix · Home