Analysis model: gpt-5.5 xhigh
RQ Demo 0 - Renaissance
Subject
RQ Demo 0 is a 1992 MS-DOS VGA quickie by Renaissance. The Hornet 1992
index lists it as:
rqdemo0.zip 93169 **+ RQ Demo 0 by Renaissance
Release metadata:
- Release year: 1992
- Code: Tran
- Group: Renaissance
- Package examined:
rqdemo0.zipfrom the Hornet 1992 mirror
Sources:
This is not a large multi-part production. It is closer to a focused engine test: text-mode warning, one VGA effect, a text-page sequencer, keyboard exit, and a Sound Blaster-style music/player subsystem. The visible identity is simple and very Renaissance: hypnotic vertical colour bars, blue proportional text, palette changes, and self-aware commentary about the code.
No old BBS/contact details are reproduced here. The executable contains one old distro/contact line; it is deliberately omitted from the text and screenshots.
Runtime Capture


Capture details:
- Emulator: DOSBox-X
2026.01.02 - Capture date: 2026-06-19
- Capture command:
dx-capture /v RQDEMO0.EXE - Capture format: MPEG-TS/H.264
- Config:
machine=svga_s3,core=normal,cycles=30000,xms=false,ems=false,umb=false, no emulated sound card - Timing zero for the runtime frames below: the automated capture stream, which includes roughly the first prompt/keypress interval before graphics mode
- Runtime capture duration:
109.550566seconds
The first capture stopped at the intro prompt because the demo waits with BIOS keyboard input. A second capture queued an Enter key after startup. The queued Esc command did not stop the run, so the capture was externally killed after a long enough sequence. The MPEG-TS tail has decode warnings from the forced stop, but the selected frames were extracted with accurate seeking.
| Time | Frame | What Is Happening |
|---|---|---|
00:00:01.000 |
![]() |
DOS text-mode warning and "press any key" gate. The demo also documents an N command-line flag for running only the flashing bars without text pages. |
00:00:03.000 |
![]() |
First stable graphics-mode frame after the keypress: unchained VGA memory is filled with vertical blue bars. |
00:00:09.000 |
![]() |
First centered text page over the same bar field. The bars are not a bitmap backdrop; their colours are being updated through DAC palette writes. |
00:00:24.000 |
![]() |
The self-commentary page about old/new code and IRQ detection. The red bars are a palette phase of the same background. |
00:00:39.000 |
![]() |
A mid-transition frame where the offscreen text/page transfer and CRTC page scroll make the old text look broken. This corresponds to the code that slides/copies the rendered page. |
00:00:42.000 |
![]() |
Next readable page after the transition. The same proportional text renderer and palette pump are still active. |
The sequence loops through four text blocks. With N on the command line, the
main path skips the text pages and stays in the pure bar/palette loop.
The GIF above is a sparse 720x400 progression assembled directly from the five graphics-mode DOSBox-X frames in the table. It shows the palette-bar field, readable text cards, a transition tear, and the next page without mixing in the text-mode warning/key-gate frame.
Runtime-To-Code Concordance
The runtime media is a direct frame sample of the program's two-layer design: generated unchained-VGA bars stay alive underneath, while proportional text pages are prepared offscreen, revealed with CRTC state, and copied back.
| Runtime evidence | Code path / data path | What it proves |
|---|---|---|
Warning/key-gate frame at 00:00:01.000 |
Entry code at 0689h, VGA display-combination check at 0668h, DOS AH=09h warning print, and BIOS INT 16h key wait. |
The text-mode warning is an explicit startup gate before VGA runtime setup. |
N command-line hint on the warning screen |
PSP command-tail scanner at 4914h; normal path starts text pages at 4973h, N mode loops on 4699h at 49A5h. |
The executable has a documented pure-bars mode that skips the text-page sequencer. |
First graphics frame at 00:00:03.000 |
VGA setup at 457Ah, chain-4 disable, four-plane clear, triangle-wave buffer fill at 45DFh, and plane deinterleave uploads at 45FBh. |
The vertical bar field is generated into memory and split across VGA planes, not loaded as a bitmap. |
| Blue/red/green bar phase changes across the GIF | Per-frame service 4699h, 03DAh retrace wait, and DAC upload of 16 RGB triples starting at palette index 11h. |
Most visible motion is DAC palette movement over stable planar bytes. |
Centered blue text page at 00:00:09.000 |
Text routine 4708h, font trim setup 4658h, width tables, temporary line buffer at 3483h, and transparent nonzero glyph writes. |
The text is a proportional software renderer layered over a copied bar background. |
Self-commentary/code note at 00:00:24.000 |
Message pointer sequence 2EDEh, 2F94h, 31CDh, 3340h in the main runtime loop, each followed by counted waits through 490Eh. |
The text pages are scripted blocks with fixed dwell times, not ad-hoc prints from DOS. |
Transition tear at 00:00:39.000 |
CRTC register 09h ramp, start-address scroll through registers 0Ch/0Dh, and final A000h copy from 3E80h to 0000h. |
The broken-looking mid-frame is a real page-reveal/page-transfer moment caught by the capture, not a bad screenshot. |
Next readable page at 00:00:42.000 |
Same 4708h text-page renderer plus continuing palette service calls between copy chunks. |
The renderer deliberately keeps the palette pump alive while building a new offscreen page. |
| Runtime capture with no emulated sound card | Sound setup around 2D18h, player start at 2E4Eh, DMA buffer at 2078h, and IRQ mixer variants at 2F75h/315Ah. |
The published visuals are silent, but the binary still contains a substantial Sound Blaster-style four-channel mixer. |
| External capture stop and Esc mismatch | Keyboard ISR at 004D:3E8Ah sets CS:[2D81h] on Escape; cleanup path starts at 49AAh. |
Exit is frame-service driven; the long capture was externally killed after enough visual evidence. |
This is why the page's simple screenshots are technically dense: one frame contains raw IVT/PIT ownership, unchained VGA planes, retrace-timed DAC animation, CRTC page movement, proportional text composition, and an audio subsystem that is larger than the visible effect suggests.
Archive Contents
The examined archive:
280bac5f3e4c772ea226663abebec2391dfc8841c19bf2776bcde45559bcb746 rqdemo0.zip
Contents:
ren-92.nfo 3534 bytes 1992-12-04 15:08
rqdemo0.exe 95620 bytes 1992-02-22 20:24
File hashes:
d810911886836212e87f631d22afebaf79b0b4fc4b831f461f15119804ac06c5 ren-92.nfo
654ca6af704b55da31e7287e3e1326affa7d0f377949693384ee9e8c1e4d9679 rqdemo0.exe
REN-92.NFO places the demo in Renaissance's Sound Blaster / Sound Blaster Pro
/ AdLib era and lists the then-current Renaissance staff. It also contains
historical boards and numbers; those details are intentionally not reproduced.
Packed And Unpacked Shape
RQDEMO0.EXE is a PKLITE-packed MZ program:
!PKLITE Copr. 1990-91 PKWARE Inc. All Rights Reserved
Packed MZ header:
file size 95620 bytes
MZ image size 95620 bytes
relocations 1
header bytes 160
min extra 0x192b
max extra 0xffff
entry CS:IP fff0:0100
stack SS:SP 1759:0200
I expanded it with UNP under DOSBox-X. The expanded executable:
3f4b15094fef1857863043ca0e1e158b2f9ba49c7d191aa29cc0ce0455406314 RQDEMO0.EXE
Expanded MZ header:
file size 193792 bytes
MZ image size 193792 bytes
relocations 8
header bytes 512
load bytes 193280
min extra 0x0000
max extra 0xffff
entry CS:IP 0000:0489
stack SS:SP 0000:0000
entry file off 0x0689
Offsets below are file offsets in the UNP-expanded executable unless explicitly
called a load offset. Since the expanded MZ header is 0x200 bytes, a normal
near code target displayed at file offset X is load offset X - 0x200.
Absolute data operands inside the code are loaded offsets and therefore live at
file offset operand + 0x200.
Startup Gate
The real entry is tiny and readable after unpacking:
0689: fc cld
068a: 8c c8 mov ax,cs
068c: 8e d8 mov ds,ax
068e: 8e d0 mov ss,ax
0690: bc 00 02 mov sp,0200h
0693: e8 d2 ff call 0668h ; VGA check
0696: 0b c0 or ax,ax
0698: 75 05 jne 069fh
069a: ba 00 02 mov dx,0200h ; "must have VGA"
069d: eb e1 jmp 0680h ; print and exit
069f: ba 2d 02 mov dx,022dh ; warning / press-key text
06a2: b4 09 mov ah,09h
06a4: cd 21 int 21h
06a6: 33 c0 xor ax,ax
06a8: cd 16 int 16h ; wait for a key
06aa: e8 43 ff call 05f0h ; hook timer/keyboard
06ad: 9a 69 42 4d 00 call 004dh:4269h ; main runtime
06b2: 8c c8 mov ax,cs
06b4: 8e d8 mov ds,ax
06b6: e8 82 ff call 063bh ; restore hooks
06b9: b8 03 00 mov ax,0003h
06bc: cd 10 int 10h
06be: ba 26 02 mov dx,0226h ; "Bye..."
06c1: eb bd jmp 0680h
The VGA check at 0668h uses the VGA BIOS display-combination call:
0668: 33 db xor bx,bx
066a: b8 00 1a mov ax,1a00h
066d: cd 10 int 10h
066f: 80 fb 07 cmp bl,07h
0672: 72 09 jb 067dh
0674: 80 fb 0c cmp bl,0ch
0677: 77 04 ja 067dh
0679: b8 01 00 mov ax,0001h
067c: c3 ret
067d: 33 c0 xor ax,ax
067f: c3 ret
So the acceptance test is not "did INT 10h exist"; it specifically wants BL in
the VGA display-combination range 07h..0Ch.
Timer And Keyboard Hooks
The hook installer at 05f0h rewrites vectors 08h and 09h directly in the
interrupt vector table:
05f0: fa cli
05f1: 33 c0 xor ax,ax
05f3: 8e c0 mov es,ax
05f5: 26 a1 20 00 mov ax,es:[0020h] ; old INT 08 offset
05f9: a3 e8 03 mov [03e8h],ax
05fc: 26 a1 22 00 mov ax,es:[0022h] ; old INT 08 segment
0600: a3 ea 03 mov [03eah],ax
0603: 26 c7 06 20 00 83 3e mov word es:[0020h],3e83h
060a: 26 c7 06 22 00 4d 00 mov word es:[0022h],004dh
0611: 26 a1 24 00 mov ax,es:[0024h] ; old INT 09 offset
0615: a3 ec 03 mov [03ech],ax
0618: 26 a1 26 00 mov ax,es:[0026h] ; old INT 09 segment
061c: a3 ee 03 mov [03eeh],ax
061f: 26 c7 06 24 00 8a 3e mov word es:[0024h],3e8ah
0626: 26 c7 06 26 00 4d 00 mov word es:[0026h],004dh
062d: b0 36 mov al,36h
062f: e6 43 out 43h,al
0631: b0 ae mov al,0aeh
0633: e6 40 out 40h,al
0635: b0 4d mov al,04dh
0637: e6 40 out 40h,al
0639: fb sti
063a: c3 ret
The PIT divisor is 0x4dae, roughly 1193180 / 19886 = 60.0 Hz. The timer
handler reached through 004d:3e83 is intentionally minimal:
4553: 50 push ax
4554: b0 20 mov al,20h
4556: e6 20 out 20h,al ; EOI
4558: 58 pop ax
4559: cf iret
The keyboard handler at 004d:3e8a reads the raw controller port, acknowledges
the 8042 latch, and only cares about Escape make-code 01h:
455a: 50 push ax
455b: e4 60 in al,60h
455d: 8a e0 mov ah,al
455f: e4 61 in al,61h
4561: 0c 80 or al,80h
4563: e6 61 out 61h,al
4565: 34 80 xor al,80h
4567: e6 61 out 61h,al
4569: 80 fc 01 cmp ah,01h
456c: 75 06 jne 4574h
456e: 2e c6 06 81 2d 01
mov byte cs:[2d81h],01h
4574: b0 20 mov al,20h
4576: e6 20 out 20h,al
4578: 58 pop ax
4579: cf iret
The render loop polls cs:[2d81h] once per palette frame. If the flag is set,
it jumps to the cleanup path at 49aah.
Main Runtime Entry
The far call 004d:4269 lands at file offset 4939h:
4939: fb sti
493a: 8c d8 mov ax,ds
493c: 2d 10 00 sub ax,0010h
493f: 8e c0 mov es,ax ; PSP segment for command tail
4941: 8c c8 mov ax,cs
4943: 8e d8 mov ds,ax
4945: 89 26 83 2d mov [2d83h],sp ; saved stack for forced exit
4949: e4 21 in al,21h
494b: a2 82 2d mov [2d82h],al ; save PIC mask
494e: 0c 01 or al,01h
4950: e6 21 out 21h,al ; mask IRQ0 while runtime owns timing
4952: e8 bf ff call 4914h ; command-line N/n check
4955: 55 push bp
4956: e8 ff fc call 4658h ; font width/trim preparation
4959: e8 1e fc call 457ah ; VGA setup/background upload
495c: 0e push cs
495d: e8 b8 e3 call 2d18h ; sound/IRQ/DMA init
4960: b8 7c 04 mov ax,047ch
4963: 50 push ax
4964: 33 c0 xor ax,ax
4966: 50 push ax
4967: 0e push cs
4968: e8 e3 e4 call 2e4eh ; start player
496b: 83 c4 04 add sp,0004h
496e: 5d pop bp
496f: 0b ed or bp,bp
4971: 75 32 jne 49a5h ; N mode: only bars
The N flag scanner is literal and forgiving: it reads ES:0080h, scans the
PSP command tail from ES:0081h, and accepts either n or N:
4914: 33 ed xor bp,bp
4916: 32 ed xor ch,ch
4918: 26 8a 0e 80 00 mov cl,es:[0080h]
491d: 0a c9 or cl,cl
491f: 75 01 jne 4922h
4921: c3 ret
4922: bf 81 00 mov di,0081h
4925: 57 push di
4926: 51 push cx
4927: b0 6e mov al,'n'
4929: f2 ae repne scasb
492b: 59 pop cx
492c: 5f pop di
492d: 74 06 je 4935h
492f: b0 4e mov al,'N'
4931: f2 ae repne scasb
4933: 75 ec jne 4921h
4935: bd 01 00 mov bp,0001h
4938: c3 ret
Normal mode cycles four text blocks with long waits:
4973: be de 2e mov si,2edeh
4976: e8 8f fd call 4708h
4979: b9 c0 00 mov cx,00c0h
497c: e8 8f ff call 490eh
497f: be 94 2f mov si,2f94h
4982: e8 83 fd call 4708h
4985: b9 00 03 mov cx,0300h
4988: e8 83 ff call 490eh
498b: be cd 31 mov si,31cdh
498e: e8 77 fd call 4708h
4991: b9 00 03 mov cx,0300h
4994: e8 77 ff call 490eh
4997: be 40 33 mov si,3340h
499a: e8 6b fd call 4708h
499d: b9 00 03 mov cx,0300h
49a0: e8 6b ff call 490eh
49a3: eb ce jmp 4973h
No-text mode is the entire concept reduced to the palette loop:
49a5: e8 f1 fc call 4699h
49a8: eb fb jmp 49a5h
490eh is just a counted wait that calls the palette/vblank routine CX
times:
490e: e8 88 fd call 4699h
4911: e2 fb loop 490eh
4913: c3 ret
VGA Register Setup
The visual runtime starts from BIOS mode 13h, then immediately turns the mode
into an unchained planar layout:
457a: b8 13 00 mov ax,0013h
457d: cd 10 int 10h
457f: ba c4 03 mov dx,03c4h ; sequencer
4582: b0 04 mov al,04h ; memory mode register
4584: ee out dx,al
4585: 42 inc dx
4586: ec in al,dx
4587: 24 f7 and al,0f7h ; clear chain-4
4589: 0c 04 or al,04h ; odd/even disable
458b: ee out dx,al
458c: 4a dec dx
458d: b0 02 mov al,02h ; map mask index
458f: ee out dx,al
4590: ba ce 03 mov dx,03ceh ; graphics controller
4593: b8 05 40 mov ax,4005h
4596: ef out dx,ax ; index 5, 256-colour shift
4597: b0 06 mov al,06h
4599: ee out dx,al
459a: 42 inc dx
459b: ec in al,dx
459c: 24 fd and al,0fdh
459e: ee out dx,al
459f: ba d4 03 mov dx,03d4h ; CRTC
45a2: b0 14 mov al,14h
45a4: ee out dx,al
45a5: 42 inc dx
45a6: ec in al,dx
45a7: 24 bf and al,0bfh
45a9: ee out dx,al
45aa: 4a dec dx
45ab: b0 17 mov al,17h
45ad: ee out dx,al
45ae: 42 inc dx
45af: ec in al,dx
45b0: 0c 40 or al,40h
45b2: ee out dx,al
It selects Sequencer Map Mask data 0fh, points ES at A000h, and clears
0x7d00 words:
45b3: ba c5 03 mov dx,03c5h
45b6: b0 0f mov al,0fh
45b8: ee out dx,al
45b9: b8 00 a0 mov ax,0a000h
45bc: 8e c0 mov es,ax
45be: 33 c0 xor ax,ax
45c0: 33 ff xor di,di
45c2: b9 00 7d mov cx,7d00h
45c5: f3 ab rep stosw
With all four planes enabled, this clears the visible planar aperture in one
pass. The later drawing routines select plane masks 1, 2, 4, and 8
explicitly and write each plane separately.
The initial DAC setup writes 16 RGB triples starting at colour 0, then blanks a run of later entries:
45c7: be 46 2e mov si,2e46h
45ca: b9 30 00 mov cx,0030h ; 16 RGB triples
45cd: ba c8 03 mov dx,03c8h
45d0: 32 c0 xor al,al
45d2: ee out dx,al ; DAC index 0
45d3: 42 inc dx ; 03c9h
45d4: ac lodsb
45d5: ee out dx,al
45d6: e2 fc loop 45d4h
45d8: 32 c0 xor al,al
45da: b1 33 mov cl,33h
45dc: ee out dx,al
45dd: e2 fd loop 45dch
Bar Field Generator
The background is not stored as an image. It is generated into conventional
memory at segment 1f90h, then deinterleaved into the four VGA planes.
The generator is a 64,000-byte triangle-wave fill:
45df: b8 90 1f mov ax,1f90h
45e2: 8e c0 mov es,ax
45e4: 33 ff xor di,di
45e6: b9 00 fa mov cx,0fa00h ; 64000 bytes
45e9: b8 14 01 mov ax,0114h ; AL=value, AH=delta
45ec: aa stosb
45ed: 02 c4 add al,ah
45ef: 3c 10 cmp al,10h
45f1: 74 04 je 45f7h
45f3: 3c 20 cmp al,20h
45f5: 75 02 jne 45f9h
45f7: f6 dc neg ah
45f9: e2 f1 loop 45ech
That produces a repeating byte ramp between 10h and 20h. The vertical look
comes from the following four plane uploads. Each plane copies every fourth
byte from the 64,000-byte generated buffer:
45fb: 8c c0 mov ax,es
45fd: 8e d8 mov ds,ax ; DS = generated buffer
45ff: b8 00 a0 mov ax,0a000h
4602: 8e c0 mov es,ax ; ES = VGA
4604: 33 f6 xor si,si
4606: 33 ff xor di,di
4608: ba c5 03 mov dx,03c5h ; map-mask data port
460b: b0 01 mov al,01h ; plane 0
460d: ee out dx,al
460e: b9 80 3e mov cx,3e80h ; 16000 plane bytes
4611: a4 movsb
4612: 83 c6 03 add si,0003h ; skip to next same-plane pixel
4615: e2 fa loop 4611h
4617: 81 ee ff f9 sub si,0f9ffh
461b: 81 ef 80 3e sub di,3e80h
461f: b0 02 mov al,02h ; plane 1
4621: ee out dx,al
4622: b9 80 3e mov cx,3e80h
4625: a4 movsb
4626: 83 c6 03 add si,0003h
4629: e2 fa loop 4625h
The same loop repeats for masks 04h and 08h. The weird-looking
sub si,0f9ffh is the reset for the deinterleave pattern: after 16000
iterations of movsb plus add si,3, SI has advanced by 64000, so the
subtraction brings it back near the next plane's starting byte. DI is similarly
rewound to draw into the same visible plane offset with a different VGA map
mask.
This is the core static picture. Animation mostly comes from changing the DAC entries and scrolling/copying between planar pages.
Per-Frame Palette Pump
Routine 4699h is the common "one frame" service. It first checks the Escape
flag:
4699: push ax/bx/cx/dx/si/di/ds
46a0: mov ax,cs
46a2: mov ds,ax
46a4: cmp byte [2d81h],00h
46a9: je 46aeh
46ab: jmp 49aah ; cleanup
Then it selects a palette subtable and waits for a full vertical retrace edge:
46ae: be 76 2e mov si,2e76h
46b1: 03 36 d8 2e add si,[2ed8h] ; phase
46b5: 03 36 dc 2e add si,[2edch] ; secondary offset
46b9: ba da 03 mov dx,03dah
46bc: ec in al,dx
46bd: a8 08 test al,08h
46bf: 75 fb jne 46bch ; wait until not in retrace
46c1: ec in al,dx
46c2: a8 08 test al,08h
46c4: 74 fb je 46c1h ; wait until retrace starts
During retrace it rewrites 16 RGB triples starting at DAC entry 11h:
46c6: ba c8 03 mov dx,03c8h
46c9: b0 11 mov al,11h
46cb: ee out dx,al
46cc: 42 inc dx ; DAC data port 03c9h
46cd: b9 30 00 mov cx,0030h
46d0: ac lodsb
46d1: ee out dx,al
46d2: e2 fc loop 46d0h
Finally it bounces the palette phase between 0 and 0x30, negating the
signed step when either edge is hit:
46d4: a1 da 2e mov ax,[2edah] ; signed delta
46d7: 8b 1e d8 2e mov bx,[2ed8h]
46db: 03 d8 add bx,ax
46dd: 83 fb 30 cmp bx,0030h
46e0: 74 04 je 46e6h
46e2: 0b db or bx,bx
46e4: 75 16 jne 46fch
46e6: f7 d8 neg ax
46e8: a3 da 2e mov [2edah],ax
46eb: 83 3e dc 2e 02 cmp word [2edch],0002h
46f0: 72 06 jb 46f8h
46f2: c7 06 dc 2e ff ff
mov word [2edch],0ffffh
46f8: ff 06 dc 2e inc word [2edch]
46fc: 89 1e d8 2e mov [2ed8h],bx
4700: pop ds/di/si/dx/cx/bx/ax
4707: ret
This explains why the screenshots show the same bars in blue, red, and green: the bytes in VGA memory are mostly stable; the colours behind those indices are moving.
Text Page Renderer
The text page routine starts at 4708h. Its first job is to copy the generated
bar field to an offscreen VGA page at segment A3E8h, again plane by plane:
4708: 8b ee mov bp,si ; remember message block
470a: b8 90 1f mov ax,1f90h
470d: 8e d8 mov ds,ax ; generated bars
470f: b8 e8 a3 mov ax,0a3e8h
4712: 8e c0 mov es,ax ; offscreen VGA page
4714: 33 f6 xor si,si
4716: 33 ff xor di,di
4718: ba c5 03 mov dx,03c5h
471b: b0 01 mov al,01h
471d: b9 04 00 mov cx,0004h ; four planes
4720: ee out dx,al ; select plane
4721: d0 e0 shl al,1
4723: push ax
4724: push cx
4725: b9 08 00 mov cx,0008h ; eight chunks per plane
4728: push cx
4729: b9 d0 07 mov cx,07d0h ; 2000 bytes per chunk
472c: a4 movsb
472d: 83 c6 03 add si,0003h
4730: e2 fa loop 472ch
4732: pop cx
4733: call 4699h ; keep palette alive during copy
4736: loop 4728h
4738: pop cx
4739: pop ax
473a: sub si,0f9ffh
473e: sub di,3e80h
4742: loop 4720h
So a full text-page redraw is deliberately amortized: copy 4 * 8 * 2000
bytes, but call the palette/vblank service between chunks. That prevents the
bars from freezing during the offscreen preparation.
After the background copy, it builds proportional text. The width preparation
at 4658h scans an 8-row font table and trims leading empty columns:
4658: push ds
4659: mov ax,137ch
465c: mov bx,0afc0h
465f: mov cl,04h
4661: shr bx,cl
4663: add ax,bx ; font segment
4665: mov ds,ax
4667: mov si,000fh
466a: mov di,2d86h ; width/trim table in CS
466d: mov cx,0045h ; 69 glyphs
4670: push cx
4671: push si
4672: push si
4673: xor dx,dx
4675: mov cx,0008h
4678: mov al,[si]
467a: or al,al
467c: jne 468bh ; non-empty column found
467e: add si,0230h ; next row of same glyph column
4682: loop 4678h
4684: inc dl ; another empty leading column
4686: pop si
4687: dec si
4688: push si
4689: jmp 4675h
468b: pop si
468c: pop si
468d: pop cx
468e: sub cs:[di],dl
4691: inc di
4692: add si,0008h ; next glyph
4695: loop 4670h
4697: pop ds
4698: ret
The actual glyph compositor uses a temporary line bitmap around load offset
3483h. It first measures a zero-terminated text line through translation and
width tables:
4752: xor dx,dx
4754: xor ah,ah
4756: lodsb
4757: or al,al
4759: je 4768h
475b: mov bx,2dcbh
475e: xlat
475f: mov bx,2d85h
4762: xlat
4763: stc
4764: adc dx,ax
4766: jmp 4756h
Then it expands each character into the temporary line buffer. The glyph source
rows are separated by 0x230, which is why the loop jumps by that much after
each row:
4773: mov di,3483h ; temporary line buffer
4776: lodsb
4777: or al,al
4779: je 47c6h ; end of line
477b: push si
477c: xor ah,ah
477e: mov bx,2dcbh
4781: xlat ; character remap
4782: mov bp,ax
4784: mov bx,2d85h
4787: xlat ; width
4788: mov dx,ax
478a: add ax,di
478c: push ax
478d: mov si,bp
478f: shl si,1
4791: shl si,1
4793: shl si,1 ; glyph * 8
4795: add si,0afc0h
4799: mov ax,137ch
479c: mov ds,ax
479e: mov cx,0008h
47a1: push cx
47a2: mov cx,dx ; glyph width
47a4: rep movsb ; one row
47a6: xor al,al
47a8: mov es:[di],al ; terminator/spacing
47ab: add si,0230h ; next glyph row
47af: sub si,dx
47b1: add di,0140h ; next line-buffer row
47b5: sub di,dx
47b7: pop cx
47b8: loop 47a1h
Once a line is in the temporary buffer, it is centered and copied to the offscreen VGA page with transparent zero suppression. The routine repeats the same 8-row loop four times, once per plane mask:
47cf: mov si,3483h
47d2: sub cx,0003h
47d5: and cx,0fffch
47d8: mov bx,cx
47da: mov di,0140h
47dd: sub di,cx
47df: shr di,1
47e1: shr di,1
47e3: shr di,1 ; horizontal centering in planar bytes
47e5: mov dx,0018h
47e8: sub dx,bp
47ea: mov ax,0280h ; 8 rows * 80 bytes/plane row
47ed: mul dx
47ef: add di,ax ; vertical placement
...
4802: mov dx,03c5h
4805: mov al,01h
4807: out dx,al
4809: mov cx,0008h
480c: push cx
480d: mov cx,bx
480f: lodsb
4810: add si,0003h ; deinterleave source pixels
4813: or al,al
4815: je 481ah
4817: mov es:[di],al ; transparent write
481a: inc di
481b: loop 480fh
481d: add si,bp ; next glyph row stride
481f: add di,dx ; next screen row stride
4822: loop 480ch
The masks 02h, 04h, and 08h follow at 482dh, 4855h, and 487bh.
The page reveal is a CRTC trick. First it ramps CRTC register 09h, then it
scrolls the CRTC start address from 0 to 0x3e80 in 0x50-byte steps while
still calling 4699h between steps:
48a3: mov dx,03d4h
48a6: mov ax,0109h
48a9: call 4699h
48ac: out dx,ax
48ad: inc ah
48af: cmp ah,10h
48b2: jb 48a9h
48b4: mov dx,03d4h
48b7: xor bx,bx
48b9: add bx,0050h
48bc: mov ah,bh
48be: mov al,0ch
48c0: out dx,ax
48c1: mov ah,bl
48c3: mov al,0dh
48c5: out dx,ax
48c6: call 4699h
48c9: cmp bx,3e80h
48cd: jb 48b9h
After the scroll, it copies the offscreen page back down over the visible page:
48cf: mov dx,03c5h
48d2: mov al,0fh
48d4: out dx,al
48d5: mov dx,03ceh
48d8: mov ax,4105h
48db: out dx,ax
48dc: mov ax,0a000h
48df: mov ds,ax
48e1: mov es,ax
48e3: mov si,3e80h
48e6: xor di,di
48e8: mov cx,4000h
48eb: rep movsb
48ed: mov ax,4005h
48f0: out dx,ax
This code explains the 00:00:39.000 capture: it catches a CRTC/page-transfer
moment where the old and new text states are not yet visually settled.
Sound Blaster-Style Player
The capture used no emulated sound card, but the binary still contains a substantial player. The user-facing text says the tune is a ripped MOD, and the code matches a small Sound Blaster DMA/IRQ mixer rather than an AdLib path.
The setup routine around 2d18h:
- calls a detection/helper path;
- chooses an IRQ vector from a detected IRQ number stored near
23e2h; - saves the old vector;
- unmasks that IRQ in PIC port
21h; - programs DMA channel 1 through ports
0ah,0ch,0bh,02h,03h, and page register83h; - writes DSP command bytes through the detected DSP port held near
23e0h.
The important shape:
2d41: mov bl,[23e2h] ; IRQ number
2d45: add bl,08h ; vector = IRQ + 8
2d4a: shl bx,1
2d4c: shl bx,1
2d4e: mov ax,es:[bx] ; save old vector
2d51: mov [2421h],ax
2d54: mov ax,es:[bx+2]
2d58: mov [2423h],ax
...
2d63: in al,21h
2d65: and al,ah ; unmask selected IRQ
2d67: out 21h,al
2d69: mov al,00h
2d6b: out 0ch,al ; DMA flip-flop clear
2d6d: mov al,49h
2d6f: out 0bh,al ; DMA mode
...
2d9c: in al,dx ; DSP write-ready polling
2d9f: js 2d9ch
2da1: mov al,0d1h
2da3: out dx,al ; speaker on / DSP command
The playback initializer at 2e4eh builds channel state, installs the chosen
IRQ handler, clears a double buffer at load offset 2078h, and starts a DMA
transfer:
2f1e: mov ax,2078h
2f21: mov [2398h],ax
2f24: mov word [239eh],0000h
2f2e: mov di,2078h
2f31: mov ax,8080h
2f34: mov cx,0190h
2f37: rep stosw ; neutral 8-bit unsigned sample buffer
...
2f3d: mov al,05h
2f3f: out 0ah,al ; mask DMA channel 1
2f41: mov ax,[239ah]
2f44: out 02h,al ; DMA address low/high
2f46: mov al,ah
2f48: out 02h,al
2f4a: mov ax,0010h
2f4d: out 03h,al ; DMA count low/high
2f4f: mov al,ah
2f51: out 03h,al
2f53: mov al,01h
2f55: out 0ah,al ; unmask channel
The IRQ handlers at 2f75h and 315ah are two mixer variants. Both flip the
buffer pointer and then run four nearly identical channel loops. The first
channel stores into the buffer; the next three add into it.
First-channel mixer core:
2fe4: mov cs:[240fh],sp ; save real stack
2fe9: xor bl,bl
2feb: mov bh,cs:[23aeh] ; volume/table selector
2ff0: mov di,cs:[2398h] ; current output buffer
2ff5: lds si,cs:[23a0h] ; channel sample pointer
2ffa: mov cx,0064h ; 100 words per channel block
2ffd: mov bp,cs:[23aah] ; fixed-point step/carry state
3002: mov dx,cs:[23ach]
3007: mov sp,cs:[23a4h] ; channel loop/end threshold
300c: mov ax,2020h ; neutral fallback
300f: cmp si,cs:[23a6h]
3014: jae 3037h ; wrap/reload pointer
3016: cmp si,sp
3018: jae 3027h
301a: lodsw ; two 8-bit samples
301b: add dh,dl
301d: adc si,bp ; fixed-point sample stepping
301f: xlat es:[bx] ; translate low byte through volume table
3021: xchg ah,al
3023: xlat es:[bx] ; translate high byte
3025: xchg ah,al
3027: stosw ; write mixed pair
3028: loop 300ch
Additive channels use the same stepping and translation but combine with existing buffer words:
306d: lodsw
306e: add dh,dl
3070: adc si,bp
3072: xlat es:[bx]
3074: xchg ah,al
3076: xlat es:[bx]
3078: xchg ah,al
307a: add es:[di],ax
307d: add di,0002h
3080: loop 305fh
So the player is doing a four-channel software mix into an unsigned 8-bit DMA
buffer, with per-channel fixed-point stepping and table-based volume
translation. The sequencer at 335fh decrements a tick counter, reads compact
event bytes from the module stream, dispatches through a jump table near
23f9h, and updates one of four channel state blocks at 23a0h, 23b0h,
23c0h, or 23d0h.
This player is much more involved than the visual quickie around it. That matches the Renaissance context: early 1992 Tran code often carries serious audio and interrupt machinery even when the visible demo is intentionally small.
Cleanup
Escape or normal return reaches 49aah:
49aa: 8b 26 83 2d mov sp,[2d83h] ; restore saved stack
49ae: 0e push cs
49af: e8 fa e3 call 2dach ; stop/restore player IRQ
49b2: a0 82 2d mov al,[2d82h]
49b5: e6 21 out 21h,al ; restore PIC mask
49b7: cb retf
The outer startup code then calls 063bh, which restores INT 08h and INT
09h from the saved words and resets PIT channel 0 to the BIOS-style divisor:
063b: fa cli
063c: xor ax,ax
063e: mov es,ax
0640: mov ax,[03e8h]
0643: mov es:[0020h],ax
0647: mov ax,[03eah]
064a: mov es:[0022h],ax
064e: mov ax,[03ech]
0651: mov es:[0024h],ax
0655: mov ax,[03eeh]
0658: mov es:[0026h],ax
065c: mov al,36h
065e: out 43h,al
0660: xor al,al
0662: out 40h,al
0664: out 40h,al
0666: sti
0667: ret
What Each Part Does
- Text-mode shell: checks VGA, prints the warning/
N-mode instructions, and waits for a BIOS keypress before touching graphics. - Hook layer: installs raw timer and keyboard vectors, sets PIT to about 60 Hz,
and records Escape through a direct port
60hscan-code handler. - Main runtime: scans the PSP command tail for
N, initializes font metrics, sets planar VGA mode, starts the audio path, then chooses either text-page mode or pure palette-bar mode. - Bar generator: creates a 64,000-byte triangle-wave field and deinterleaves it into the four VGA planes.
- Palette pump: waits on
03DAhvertical retrace and rewrites 16 DAC entries every frame, producing the blue/green/red bar phases without rewriting the whole screen. - Text renderer: builds proportional text lines from a compact font, writes only non-zero glyph bytes to an offscreen planar page, reveals the page using CRTC start-address changes, then copies the offscreen page back to the visible origin.
- Sound player: detects/sets up a Sound Blaster-like DSP, IRQ, and DMA path, then mixes four software channels into an unsigned 8-bit DMA buffer using fixed-point sample stepping and volume translation tables.
- Exit path: Escape sets a byte flag in the keyboard ISR; the next frame service jumps to cleanup, stops/restores the player, restores the PIC mask, restores timer/keyboard vectors, returns to text mode, and prints the short farewell.
The demo looks simple because the visual vocabulary is narrow. Internally it is still a good early-Renaissance example: raw IVT/PIT/PIC ownership, unchained VGA planes, CRTC page movement, retrace-timed DAC animation, proportional software text, and a real interrupt/DMA software mixer all packed into a "quickie".





