Analysis model: gpt-5.5 xhigh
The Firm Loader (1) by The FiRM - Technical Dissection
The Firm Loader (1) is an August 1989 MS-DOS cracktro / loader by The
FiRM. Pouet lists it as a cracktro for MS-DOS, and the preserved Scene.org
package notes that this loader was spread with Ancient Land of Ys.
Release year: 1989
This is a much more interesting specimen than a plain TheDraw screen saver.
The visible page is text mode, but the file is a custom .COM loader shell:
it saves the machine state, installs keyboard/timer/Ctrl-Break hooks, detects
the display target, draws a table-driven CP437 logo, blinks one outline for a
short wait period, decodes an obfuscated child filename, runs that child through
DOS EXEC, then restores the interrupt table and video state before exiting.
The inner loops are compact but dense. The drawing stream is not a bitmap. Each
logo cell is stored as a packed text-memory byte offset plus a 4-bit character
index. The filename is not stored plainly either: a symmetric overlapping
word-XOR pass turns the buffer at 05B0h into gitum.000 immediately before
the INT 21h AX=4B00h handoff, then runs the same pass again to hide it.
Sources
- Pouet production page: https://www.pouet.net/prod.php?which=84644
- Scene.org file page: https://files.scene.org/view/demos/compilations/lost_found_and_more/cracktro/the_firm_loader.zip
- Scene.org archive: https://files.scene.org/get/demos/compilations/lost_found_and_more/cracktro/the_firm_loader.zip
Pouet records:
Title: the firm loader (1)
Group: The Firm
Type: Cracktro
Platform: MS-Dos
Released: August 1989
The Scene.org package contains a short info.txt which says the loader was
spread with Ancient Land of Ys.
Visual References
The cracktro has one main visual state plus a short blink/wait phase. The
00:00.000 timestamp below means "after the table render has produced the
page", not a captured DOSBox timeline.
| Time | Still | What is happening |
|---|---|---|
00:00.000 |
![]() |
Public Pouet screenshot of the text-mode loader page. |
00:00.000 |
![]() |
Direct reconstruction from the two coordinate streams, the 16-byte CP437 character table, the texture stream, and the bottom text stream. |
Generated runtime map:

Visual/Archive-To-Code Concordance
This page does not have a timed DOSBox video sequence. The useful visual evidence is the public Pouet GIF screenshot, the decoded text-mode reconstruction, and the generated runtime map. Those are enough to tie the visible loader page to the recovered code without inventing a motion capture.
The public screenshot and decoded reconstruction both correspond to the page
builder at 01B5h. The startup path normalizes the segment registers, saves
machine state, installs interrupt wrappers, detects the display target, and then
calls the visible-page builder:
0140 call 056Eh ; normalize DS/ES/SS and SP
014F call 0339h ; save video, cursor, ports, Ctrl-Break, full IVT
0152 call 03D3h ; install INT 23h, INT 09h, INT 08h wrappers
0155 call 04DAh ; detect display, set mode, choose text segment
0158 call 01B5h ; draw visible page and wait/blink
Inside 01B5h, the top-left THE logo and the large FiRM outline are not a
stored 4000-byte text page. They are two coordinate streams rendered through
0235h, using different base delay/attribute values:
01C6 mov bp,3F00h
01C9 mov si,079Eh
01CC mov dl,[05E3h]
01D0 call 0235h ; draw first coordinate stream
01D3 mov bp,3400h
01D6 mov si,085Ch
01D9 mov dl,[05E4h]
01DD call 0235h ; draw second coordinate stream
The decoded screen proves that interpretation: the renderer's packed words
become text-memory byte offsets plus CP437 character indexes. The inner loop
extracts DI = AX >> 4, uses the low nibble as an index into the 16-byte table
at 0792h, and writes an attribute/character word:
0245 lodsw ; AX = packed coordinate/char-index word
024B mov di,ax
024D shr di,1
024F shr di,1
0251 shr di,1
0253 shr di,1 ; DI = AX >> 4
0255 and al,0Fh ; character-table index
0257 xlat
0258 mov cl,al
026E call 0322h ; final cell write
The small visual difference between the static screenshot and the runtime logic is the wait/blink phase. After the initial draw, the loader performs up to 64 passes where it redraws only the second outline stream with alternating attributes:
01EC mov bx,0040h ; 64 passes
01EF mov bp,0001h ; fast redraw mode
01F2 mov dl,[05E4h]
01F6 mov dh,[05E5h]
020B xchg dh,dl
020D call 0235h ; redraw second outline stream
Because BP=0001h, the renderer skips the temporary flash cell and writes the
final CP437 cell directly. That is the loader's only intended animation-like
visual state: an attribute blink of the large FiRM outline while waiting for a
key or timeout.
The generated runtime map belongs to the non-visual loader shell around the
page. After the wait, 01B5h clears the text screen, the filename mutator at
0590h decodes 05B0h into gitum.000, and the standard DOS EXEC parameter
block at 05D0h is patched to point into the current CS:
015B call 0590h ; decode child filename buffer
015E mov ax,cs
0160 mov [05D4h],ax
0163 mov [05D8h],ax
0166 mov [05DCh],ax
0169 mov bx,05D0h
016C mov dx,05B0h
016F mov ax,4B00h
0172 int 21h ; EXEC child
The same map also explains why the loader saves and restores so much state for a simple-looking text page. The visible page is only the front end; the program also installs Ctrl-Break, keyboard, and timer wrappers, supervises the keyboard hook while the child runs, restores the IVT and hardware ports, re-obfuscates the child filename, and only then prints a success or DOS error message.
So the visual evidence is organized like this: the public GIF is the preserved
screen state, the decoded screen proves the packed coordinate streams and CP437
table, and the runtime map ties that page to the surrounding loader lifecycle:
state save, interrupt hooks, CGA-safe cell writer, blink wait, hidden child
EXEC, restore, and status output.
Archive
The examined ZIP:
8ffff321b587272febbc42ffa3cc685b6dac21cab26c9b0b5e650ab04113f21a the_firm_loader.zip
ZIP central directory:
info.txt 48 bytes stored 2020-01-12 14:59
YS.COM 2502 bytes deflated 1989-08-15 23:52
Extracted files:
d988a5703b5680b1a1859235f327ffb04eb2cb467ebd312cbad2ec371eaeaf66 info.txt
8832bcf7bce8022b92a481c225c25ebb470f746c958876288c7ff7105db4e747 YS.COM
Public screenshot:
934969b72f4f89d00b1d6b5af207951671e34be3b0e5dacfa6a5b95ec423cccd the-firm-loader-1-pouet.gif
The archive does not contain the eventual child program. The loader decodes and
tries to EXEC gitum.000, but the package only includes YS.COM and
info.txt.
COM Layout
For a DOS .COM file:
file offset 0000h -> runtime CS:0100h
runtime offset = file offset + 0100h
file offset = runtime offset - 0100h
The top-level runtime layout:
0100-013F jump plus ASCII author tag
0140-01B4 startup, save/install/setup, EXEC handoff, status messages
01B5-0234 visible-page builder and 64-pass wait/blink loop
0235-0338 coordinate renderer, text writer, CGA-safe cell writer
0339-03D2 save and restore video/ports/IVT/control-break state
03D3-04D9 INT 23h, INT 09h, and INT 08h hook installation and handlers
04DA-056D display detector and B800h/B000h target selection
056E-05AE segment/stack normalizer and filename mutator
05AF-06A0 obfuscated filename, EXEC parameter block, messages, text streams
06A1-0791 texture/twinkle seed table
0792-079D 16-byte CP437 drawing-character table
079E-085B first logo coordinate stream
085C-0AC5 second logo / outline coordinate stream
0AC6-0AEF saved flags, ports, vectors, return thunk
0AF0-0EEF 1024-byte saved interrupt-vector-table copy
The file starts with a jump over a plain author banner:
0100 EB 3E jmp 0140h
0102 " FIRMLOADER < was written by The Software Surgeon"
That banner is data only; execution begins at 0140h.
Startup Flow
The main routine is compact:
0140 call 056Eh ; normalize DS/ES/SS and SP
0143 mov bx,010Fh
0147 mov ah,4Ah
0149 int 21h ; shrink DOS memory block
014B mov al,00h ; error code for resize failure
014D jc 0174h
014F call 0339h ; save video, cursor, ports, Ctrl-Break, full IVT
0152 call 03D3h ; install INT 23h, INT 09h, INT 08h wrappers
0155 call 04DAh ; detect display, set mode, choose text segment
0158 call 01B5h ; draw visible page and wait/blink
015B call 0590h ; decode child filename buffer
015E mov ax,cs
0160 mov [05D4h],ax
0163 mov [05D8h],ax
0166 mov [05DCh],ax ; patch EXEC parameter block segments to CS
0169 mov bx,05D0h ; EXEC parameter block
016C mov dx,05B0h ; ASCIIZ child filename
016F mov ax,4B00h
0172 int 21h ; EXEC child
0174 lahf ; save EXEC/resize flags across cleanup
0175 call 056Eh ; normalize again after child/interrupt path
0178 call 0381h ; restore IVT, ports, mode, cursor, Ctrl-Break
017B call 0590h ; re-obfuscate filename buffer
017E sahf ; recover original carry/error state
017F jae 01A3h ; success path
The LAHF / SAHF pair is important. The loader wants to restore machine
state and re-hide the filename before it decides which message to print, but it
also wants the carry flag and AL error code from the earlier DOS call.
Error dispatch:
0181 mov dx,05E8h ; memory allocation error
0184 cmp al,00h
0186 je 01ACh
0188 mov dx,0605h ; file not found
018B cmp al,02h
018D je 01ACh
018F mov dx,0619h ; insufficient memory
0192 cmp al,08h
0194 je 01ACh
0196 mov dx,0631h ; DOS error-code (00) detected
0199 aam 0Ah
019B xchg ah,al
019D add [0643h],ax ; patch two ASCII digit slots
01A1 jmp 01ACh
AL=00h is not a normal DOS EXEC error. It is preloaded before the memory
resize branch, so a failed INT 21h AH=4Ah prints the memory-allocation
message through the same cleanup path.
On success:
01A3 mov si,067Ch
01A6 call 02FBh ; draw "Thanks for supporting THE FiRM!"
01A9 mov dx,0653h ; CR/LF/LF "$"
01AC mov ah,09h
01AE int 21h
01B0 mov ax,4C00h
01B3 int 21h
So the loader never returns to the caller. It exits through DOS after the child program returns or after an error.
Segment Normalizer
Routine 056Eh is called at entry, after EXEC, and from interrupt-exit
paths. It behaves like a local far/near thunk:
056E pop cs:[0ACBh] ; save return IP
0573 mov cs:[0AC9h],cs
0578 mov ds,cs:[0AC9h]
057D mov es,[0AC9h]
0581 cli
0582 mov ss,[0AC9h]
0586 mov sp,10F0h
0589 sti
058A jmp [0ACBh] ; continue after the original call
This makes the whole program independent of whatever segment registers the
child or interrupt path left behind. It also discards the caller's stack and
uses a fixed stack at CS:10F0h, safely above the 2502-byte file body and its
state buffers.
Saved State
Routine 0339h snapshots more than the visible text page needs:
033F mov ah,0Fh
0341 int 10h
0343 mov [0ACDh],al ; current video mode
0346 mov ah,03h
0348 int 10h
034A mov [0ACEh],cx ; cursor shape
034E mov ax,3300h
0351 int 21h
0353 mov [0AD0h],dl ; Ctrl-Break flag
0357 in al,61h
0359 mov [0AD1h],al
035C in al,21h
035E mov [0AD2h],al
0361 in al,0A1h
0363 mov [0AD3h],al ; speaker/PIC masks
0368 xor si,si
036A mov ds,si
0370 mov di,0AF0h
0373 mov cx,0200h
0376 rep movsw ; copy 1024-byte IVT from 0000:0000
The IVT copy is full table state, not just the vectors this program touches.
The restore routine 0381h copies those 1024 bytes back under CLI/STI, then
restores ports 61h, 21h, A1h, reprograms PIT channel 0 to mode 3 with a
zero count, restores the DOS Ctrl-Break flag, resets the old video mode, and
restores the cursor shape.
The PIT reset is:
03A6 mov al,36h
03A8 out 43h,al
03AA mov al,00h
03AC out 40h,al
03AE mov al,00h
03B0 out 40h,al
That puts channel 0 back into the standard square-wave timer mode with count
0000h, meaning 65536.
Display Setup
Routine 04DAh calls the detector at 0515h and stores its return value in
0AC6h. The rest of the renderer uses 0AC6h == 1 as the "wait on port
03DAh before touching text memory" case.
Display setup:
04DE call 0515h
04E1 mov [0AC6h],al
04E4 cmp al,00h
04E6 je 04F3h
04E8 mov al,03h
04EA mov ah,00h
04EC int 10h ; colour 80x25 text
04EE mov ax,0B800h
04F1 jmp 050Dh
04F3 mov al,02h
04F5 mov ah,00h
04F7 int 10h ; loader's monochrome branch
04F9 mov word [05E3h],0707h
04FF mov word [05E5h],0F0Fh
0505 mov byte [05E7h],00h
050A mov ax,0B000h
050D mov [0AC7h],ax ; text memory segment for all writers
The nonzero path sets BIOS mode 3 and writes to B800h. The zero path is the
loader's monochrome branch: it changes the attributes and writes to B000h.
The detector itself first tries INT 10h AX=1A00h display-combination state,
then INT 10h AX=1200h BL=10h, then a BIOS data-area check at 0040:0063h.
The returned value is not used as a rich adapter enum. In this file it only
selects colour versus monochrome attributes and whether text writes should wait
for the CGA status bit.
Page Builder
Routine 01B5h builds the visible page:
01BB mov cx,[0ACEh]
01BF or ch,20h
01C2 mov ah,01h
01C4 int 10h ; hide cursor
01C6 mov bp,3F00h
01C9 mov si,079Eh
01CC mov dl,[05E3h] ; colour path: 0Fh
01D0 call 0235h ; draw first coordinate stream
01D3 mov bp,3400h
01D6 mov si,085Ch
01D9 mov dl,[05E4h] ; colour path: 09h
01DD call 0235h ; draw second coordinate stream
01E0 mov si,06A1h
01E3 call 0290h ; XOR/rotate texture patch
01E6 mov si,0657h
01E9 call 02FBh ; bottom "* * * PRESENTS * * *"
The first coordinate stream draws the THE logo at the top-left. The second
stream draws the large FiRM outline. The text stream at 0657h writes the
bottom centered presentation line.
The reconstruction generated from those tables produced these nonblank rows:
02: THE Software Surgeon Bilbo Baggins
03: Maestro Barimor
04: Mr. Camaro The Guch
05: Lord Blix Wild Patrick
06: Chancelor Hogmeat Chancelor Hogmeat
07: Electric Rat Da Viper
09-21: large FiRM outline
23: * * * P R E S E N T S * * *
The image also contains line-drawing characters; the simplified text above is only a row index, not a replacement for the decoded screenshot.
Coordinate Renderer
Routine 0235h is the core logo renderer. Inputs:
SI = coordinate stream
DL = attribute byte
BP = delay / special mode
It begins by loading the destination text segment and the 16-byte character table:
023C mov es,[0AC7h]
0240 mov bx,0792h ; 16-byte CP437 character table
0243 mov ch,dl ; attribute in high byte of screen word
Inner loop:
0245 lodsw ; AX = packed coordinate/char-index word
0246 cmp ax,0FFFFh
0249 je 0288h
024B mov di,ax
024D shr di,1
024F shr di,1
0251 shr di,1
0253 shr di,1 ; DI = AX >> 4 = byte offset in text memory
0255 and al,0Fh ; low nibble = character table index
0257 xlat ; AL = [0792h + AL]
0258 mov cl,al ; screen word = CH:CL = attribute:character
Each stream word is therefore:
bits 15..4 text-memory byte offset
bits 3..0 CP437 character-table index
This is why the stream can draw boxes and diagonal outline pieces without storing full character/attribute words for every point.
The optional delayed flash write:
025A cmp bp,0001h
025D je 026Eh
025F push cx
0260 mov ch,[05E6h] ; colour path: 07h
0264 mov cl,04h ; temporary diamond/control glyph
0266 call 0322h
0269 mov cx,bp
026B loop 026Bh ; busy delay
026D pop cx
026E call 0322h ; final cell
The first two calls use BP=3F00h and BP=3400h, so they draw a temporary
character, wait, then draw the real CP437 line piece. The animation loop later
uses BP=0001h, which skips the temporary flash and just redraws the second
outline quickly.
The key-skip path:
0271 cmp bp,0B00h
0275 jb 0245h
0277 je 0282h
0279 mov ah,01h
027B int 16h
027D je 0282h
027F mov bp,0B00h
0282 mov ah,0Bh
0284 int 21h
0286 jmp 0245h
For the initial slow draw, a pending key shortens later delays by forcing
BP=0B00h. The DOS AH=0Bh call is a standard input-status poll whose return
value is ignored here; it just gives DOS/BIOS a chance to observe console
status while the renderer continues.
Cell Writer
Every renderer path eventually calls 0322h:
0324 cmp byte [0AC6h],01h
0329 jne 0333h
032B mov dx,03DAh
032E in al,dx
032F shr al,1
0331 jae 032Eh ; wait until status bit 0 was set
0333 mov es:[di],cx
0336 pop dx
0337 pop ax
0338 ret
When the display detector returned 1, the write waits on CGA status port
03DAh before storing the character/attribute word. On the other paths it
writes directly to B800h or B000h.
Texture / Twinkle Patch
Routine 0290h draws a 12-row by 20-column patch beginning at text-memory byte
offset 0184h. It is not a plain bitmap. It picks one of twelve 20-byte
windows from the table at 06A1h using the BIOS tick count, then XORs and
rotates characters already present on the screen.
Selection:
029B mov ah,00h
029D int 1Ah ; CX:DX = BIOS ticks since midnight
029F xor ax,ax
02A1 xchg ax,dx
02A2 shr ax,1
02A4 mov cx,000Ch
02A8 div cx ; remainder in DX selects 0..11
02AA mov al,14h
02AC mul dl ; AL = selected row * 20
02AE mov bx,si ; base = 06A1h
02B0 add si,ax
02B2 mov di,0184h
Inner 20-column loop:
02B9 mov cx,0014h
02BC mov ah,[05E7h] ; colour path: 0Ch
02C0 cmp byte [si],0FFh
02C3 jne 02C7h
02C5 mov si,bx ; FF wraps back to base table
02C7 lodsb
02C8 mov dx,si
02CA xor al,dl ; character byte depends on current stream address
02DD xor es:[di],ax ; XOR char/attr into existing screen word
02E0 rol byte es:[di],1
02E3 inc di
02E4 inc di
02E5 loop 02C7h
At the row boundary:
02E7 pop cx
02E8 test cx,0001h
02EC je 02F1h
02EE add di,0050h ; skip one text row on alternating passes
02F1 loop 02B8h
The patch therefore adds a deterministic but tick-selected disturbance to part of the page. It uses only 240 byte reads per call and reuses the text page as its own destination buffer.
Text Writer
Routine 02FBh handles the bottom line and the success message. Stream format:
word destination byte offset in text memory
byte initial attribute
byte character data...
00h attribute switch marker; next byte becomes the new attribute
FFh end of stream
The loop:
0304 lodsw
0305 mov di,ax
0307 lodsb
0308 mov ch,al ; attribute
030A lodsb
030B mov cl,al ; character
030D cmp al,00h
030F je 0307h ; zero means read a new attribute
0311 cmp al,0FFh
0313 je 031Ch
0315 call 0322h
0318 inc di
0319 inc di
031A jmp 030Ah
The stream at 0657h decodes as:
destination: 0E8Eh
attribute: F0h
text: * * * P R E S E N T S * * *
The success stream at 067Ch starts at 00A0h, writes Thanks for supporting
in attribute 07h, sees a zero marker, switches to attribute 0Fh, and then
writes THE FiRM!.
Wait / Blink Loop
After the first render, the loader waits up to 64 redraw passes:
01EC mov bx,0040h ; 64 passes
01EF mov bp,0001h ; fast redraw mode for renderer
01F2 mov dl,[05E4h] ; colour path: 09h
01F6 mov dh,[05E5h] ; colour path: 0Ch
01FA xor cx,cx
01FC loop 01FCh ; 65536-loop busy delay
01FE mov ah,01h
0200 int 16h
0202 jne 0215h ; key ready -> consume and exit wait
0204 mov ah,0Bh
0206 int 21h ; DOS stdin status, return unused
0208 mov si,085Ch
020B xchg dh,dl ; alternate 09h and 0Ch
020D call 0235h ; redraw second outline stream
0210 dec bx
0211 je 0219h
0213 jmp 01FAh
Because BP=1, the renderer skips the temporary flash cell and writes the
second coordinate stream directly. The only visual change during this phase is
the big FiRM outline alternating between the two colour attributes.
The wait exits either when a key is pending or after all 64 passes:
0215 mov ah,00h
0217 int 16h ; consume key
0219 mov cx,0000h
021C mov dx,184Fh
021F mov bh,07h
0221 mov ax,0600h
0224 int 10h ; clear full 80x25 page
0226 mov cx,[0ACEh]
022A mov ah,01h
022C int 10h ; restore saved cursor shape
The page clear happens before the child EXEC, so the loaded game or payload
starts with a clean text page.
Filename Mutator
Routine 0590h is symmetric: one call decodes, a second call restores the
original obfuscated bytes. It operates on overlapping words, so each iteration
can read one byte that the previous iteration just wrote.
The actual loop:
0590 push ax
0591 push bx
0592 push cx
0593 mov cx,001Fh
0596 mov bx,0619h
0599 mov ax,[bx-0069h] ; starts at 05B0h
059C add bx,bx
059E inc bx
059F xor al,bh
05A1 inc bx
05A2 xor ah,bl
05A4 shr bx,1
05A6 mov [bx-006Ah],ax ; starts at 05B0h
05A9 loop 0599h
Emulating that exact byte order on the preserved file gives:
before 05B0h: 6B 51 4E 41 5B 1E 02 7C 7E 48 B5 BB ...
after 05B0h: 67 69 74 75 6D 2E 30 30 30 00 FF FF ...
ASCII: g i t u m . 0 0 0 \0
So the decoded child program name is:
gitum.000
A second call produces the original obfuscated bytes again. That is why the
loader can hide the child name in the file, reveal it for EXEC, then hide it
again before printing status and exiting.
DOS EXEC Parameter Block
Immediately before EXEC, the code patches three segment fields in the block
at 05D0h:
015E mov ax,cs
0160 mov [05D4h],ax
0163 mov [05D8h],ax
0166 mov [05DCh],ax
0169 mov bx,05D0h
016C mov dx,05B0h
016F mov ax,4B00h
0172 int 21h
The parameter block bytes before segment patching:
05D0: 00 00 environment segment = inherit parent environment
05D2: 80 00 command tail offset = 0080h
05D4: 00 00 command tail segment, patched to CS
05D6: 5C 00 first FCB offset = 005Ch
05D8: 00 00 first FCB segment, patched to CS
05DA: 6C 00 second FCB offset = 006Ch
05DC: 00 00 second FCB segment, patched to CS
That is the standard DOS EXEC parameter block for AH=4Bh: inherit the
environment, pass the PSP command tail, and pass the two default FCBs from the
loader's PSP area.
Interrupt Hooks
The loader installs three interrupt paths.
INT 23h is the Ctrl-C / Ctrl-Break handler:
041D clc
041E jmp 0174h
That lands in the normal cleanup path. It deliberately clears carry, so a Ctrl-Break exit is treated like a non-error cleanup rather than a DOS failure.
INT 09h first points to a far jump through 0AD5h:
0421 jmp far cs:[0AD5h]
At install time, 0AD5h:0AD7h points to the local keyboard handler at 042Eh.
The old BIOS keyboard vector is saved at 0AD9h:0ADBh.
The local handler:
042E push ax
042F in al,60h
0431 mov ah,al
0433 xchg al,cs:[05E2h] ; AL = previous scan byte, [05E2] = current
0438 cmp ax,cs:[05E0h] ; bytes 1Dh,4Eh
043D pop ax
043E jne 0454h
0440 in al,61h
0442 mov ah,al
0444 or al,80h
0446 out 61h,al
0448 mov al,ah
044A out 61h,al
044C mov al,20h
044E out 20h,al
0450 clc
0451 jmp 0174h
0454 jmp far cs:[0AD9h] ; chain old INT 09h
The hotkey comparison is a two-scan-byte sequence, not an ASCII key. The stored
word is 4E1Dh, meaning previous scan byte 1Dh followed by current scan byte
4Eh. On an IBM PC keyboard that is the Ctrl make code followed by keypad
plus. If the sequence is not seen, the handler chains to the old keyboard ISR.
INT 08h is a timer wrapper:
0426 call 0459h
0429 jmp far cs:[0ADDh] ; old INT 08h
Routine 0459h watches the real IVT entry at 0000:0024h, the keyboard
interrupt vector. Its job is to keep the loader's keyboard wrapper and the
saved old keyboard vector coherent while the child program is running:
045D xor ax,ax
045F mov es,ax
0461 mov ax,cs
0465 mov bx,0024h ; IVT slot for INT 09h
0468 cmp byte cs:[0AD4h],00h
046E jne 04A1h
...
0476 mov byte cs:[0AD4h],0FFh
047C mov ax,[0AD9h] ; rotate old vector into wrapper slot
047F mov [0AD5h],ax
...
0495 mov ax,042Eh
0498 mov es:[bx],ax
049B mov es:[bx+2],cs ; install local handler directly
The reverse branch notices when the IVT again points at the wrapper and swaps the saved pointers back:
04AF mov byte cs:[0AD4h],00h
04B5 mov ax,[0AD5h]
04B8 mov [0AD9h],ax
...
04C1 mov ax,042Eh
04C4 mov [0AD5h],ax
04C7 mov [0AD7h],cs
04CB mov ax,0421h
04CE mov es:[bx],ax
04D1 mov es:[bx+2],cs
This is more elaborate than the visible page requires. It is loader-shell infrastructure: keep a bailout key alive while the child runs, then restore the machine exactly.
What Makes It Distinctive
For an August 1989 PC cracktro, this is not visually huge, but it is technically busy:
- The visible logo is a compact vector-like CP437 cell stream, not a raw 4000-byte text page.
- The first draw uses slow flash writes, while the wait phase redraws only the second stream with alternating attributes.
- The write path has a CGA status wait branch selected by the display detector.
- The loader saves the entire interrupt vector table and restores it after the child returns.
- The timer hook supervises the keyboard hook instead of installing only a
one-shot
INT 09hhandler. - The child filename is decoded only around the
EXECcall, using an overlapping symmetric XOR transform.
The practical runtime sequence is:
normalize segments and stack
shrink DOS memory block
save video, cursor, Ctrl-Break, PIC/speaker ports, and full IVT
install Ctrl-Break, keyboard, and timer wrappers
detect text display target
draw THE + FiRM page from coordinate streams
blink the FiRM outline for up to 64 passes or until keypress
clear text page
decode 05B0h -> "gitum.000"
EXEC gitum.000 with inherited environment and PSP command tail/FCBs
restore IVT, ports, timer, video mode, cursor, and Ctrl-Break state
re-obfuscate filename buffer
print success or DOS error message
exit via INT 21h AX=4C00h
That combination of tiny visual data, defensive state restoration, and a hidden child handoff is exactly the profile of a reusable cracktro loader rather than a standalone demo effect.

