Arabic Commercial
1991 · C:\ARABIC\DEMO.COM
Loading DEMO.COM, 5 KB
A 15-slide Arabic advert for a computer services center in Port Said, played by a 152-byte player he wrote.
The story
This slide show is the step before ARABART. Every line of Arabic travels as short vertical pen strokes, 9,023 of them for 15 slides. A player of just 152 bytes paints them back one pixel at a time. Before each line, the player rewrites five bytes of its own code to pick the direction, the shadow and both colors. Player and pictures together fit in 4,864 bytes on disk. His signature rides inside, scrambled, and comes back only when the program runs.
Fifteen slides, painted stroke by stroke
In 1991 Yasser made a slide show for the Port Said Center for Computer Services: fifteen slides of Arabic in color, with 3-D shadows. A player of 152 bytes shows all of it.
Here the Arabic travels as pixels. Every line of text is a list of short vertical strokes, and his player paints them back one pixel at a time. For a 3-D line it paints each pixel twice: once in the letters’ color, then two pixels right and two down in the shadow’s. Press Run to play the whole show from his own bytes, or step slide by slide.
dec bx x: one column to the left inc bx w: one column to the right mov ax,0C07h the letters’ color jmp +0 draw the shadow mov ax,0C04h the shadow’s color Watch the window beside the screen. Before each line of text, the player reads five bytes and writes them straight into its own code: which way “x” moves, whether to draw the shadow, and both colors. One loop, rewritten on the fly, draws all 23 lines of the show. This is self-modifying code.
The commercial is the step before ARABART. Its letters stand 34 pixels tall, the height of ARABART’s font 3, with the same two-pixel shadow. In ARABART that look became a single call: one line of code draws 3-D Arabic from any letters you type.
Inside the player
78 instructions, 152 bytes, draw the whole show. Step through the first strokes of slide 1 and watch his script bytes turn into pixels.
- 0113
mov si,01ECh; the script - 0116
mov ax,0010h; 640 x 350 - 0119
int 10h; and a clean screen - 011E
mov [0149h],al; write: step for x - 0124
mov [0151h],al; write: step for w - 012A
mov [0174h],al; write: shadow jump - 0130
mov [016Dh],al; write: letter color - 0136
mov [0176h],al; write: shadow color - 0139
mov di,[si]; start row - 013D
mov bx,[si]; start column - 0141next:
mov dl,[si]; one script byte - 0144
cmp dl,78h; x? - 0149
inc bx; rewritten: dec bx - 014C
cmp dl,77h; w? - 0154
cmp dl,79h; y: next line - 0159
cmp dl,7Ah; z: the end - 015E
cmp dl,88h; next slide - 0165
add dx,di; the row - 0167
mov cl,[si]; how many pixels - 016Cpx:
mov ax,0C00h; rewritten: color - 016F
xchg bx,cx - 0171
int 10h; one pixel - 0173
jmp +0; rewritten: 0 or 0Dh - 0175
mov ax,0C00h; rewritten: shadow - 0178
inc cx / inc cx; x + 2 - 017A
inc dx / inc dx; y + 2 - 017C
int 10h; its shadow - 017E
dec cx / dec cx; back again - 0182
inc dx; one row down - 0185
loop px; the rest - 0187
jmp next
-
byte *s = script; -
set_mode(0x10);// and clears -
code[0x149] = *s++;// his code -
code[0x151] = *s++; -
code[0x174] = *s++; -
code[0x16D] = *s++; -
code[0x176] = *s++; -
row0 = word(s); s += 2; -
col = word(s); s += 2; -
for (;;) { -
b = *s++; -
if (b == 'x') { col--; continue; } -
if (b == 'w') { col++; continue; } -
if (b == 'y' || b == 'z' || b == 0x88) -
break; -
row = row0 + b; n = *s++; -
while (n--) { -
plot(col, row, color); -
if (shadow) -
plot(col + 2, row + 2, shade); -
row++; -
} -
}
- SI
- 01ECh where it is reading
- BX
- 0 the column
- DX
- 0 the row
His read pointer starts at the script: 25,653 bytes that hold all fifteen slides.
| 1991 to 1993 | 2026 | Why it is the same idea |
|---|---|---|
| Five bytes written into his own loop before each line | Setting shader uniforms before a GPU draw call | One drawing program, with new settings for every draw. |
| A jump of 0 or 13 bytes decides whether the shadow is drawn | A boolean flag passed to the shader | One switch turns a whole step on or off. |
| Strokes stored as a row and a length | Run-length encoding, as in fax images and sprite formats | Store where the ink is, not every empty pixel. |
For engineers: the whole player
mov ax,0010h / int 10h ; EGA 640×350, 16 colors; a clean screen mov al,[si] / inc si / mov [0149h],al ; five times: 0149, 0151, 0174, 016D, 0176 mov di,[si] / mov bx,[si+2] ; start row, start column mov dl,[si] / inc si ; the next script byte cmp dl,"x" / jne / inc bx ; the step he rewrote cmp dl,"y" / "z" / 88h ; next line, the end, next slide mov dh,0 / add dx,di / mov cl,[si] ; row, stroke length xchg bx,cx / int 10h ; CX = column, and BH = 0, the page inc cx / inc cx / inc dx / inc dx / int 10h ; the shadow at +2, +2 loop 016Ch ; down the stroke mov cx,1000h / mov ah,0Bh / int 21h / loop ; a pause between slides mov ax,3 / int 10h / mov ah,9 / int 21h ; text mode, print the credit One small trick shows how carefully he packed it. xchg bx,cx moves the stroke counter into BX. The counter is always below 256, so BH is 0, which is exactly the video page number the BIOS pixel call needs. One instruction does two jobs. The pause between slides asks DOS 4,096 times whether a key was pressed, then setting the graphics mode again wipes the screen for the next slide. See the player in Every Byte.
4,864 bytes for fifteen slides
The whole commercial, player and pictures, is 4,864 bytes on disk. One plain screenshot of a single slide would take 112,000.
The secret is to store only the ink. A plain 16-color picture spends half a byte on every pixel, black or not. His script spends two bytes on each stroke, where it starts and how long it runs, plus one byte to move to the next column. Scan one word and watch the two counts.
- His script so far
- 0 bytes
- The same pixels as a plain picture
- 0 bytes
Sizes in units of his player, the whole drawing program. Tap any bar to make it the unit.
In memory the program is 25,899 bytes. He shipped it packed with DIET, a compressor of the day, and it unpacks itself when it starts, like a self-extracting zip. That leaves 4,864 bytes on the floppy disk for fifteen slides of animated Arabic.
His name, hidden in the bytes
Open DEMO.COM in a file viewer and his name is nowhere to be found. It is there, scrambled, and the first 19 bytes of the program put it back together.
Each byte of the credit is stored a little higher than it should be. The first is 4 too high, the next 5, the next 6, and so on. His loop subtracts the same keys, one by one, and when the show ends the program prints the result. Step through it, then try it on your own name.
- 0100
mov si,01ABh; the 65 bytes - 0103
mov al,4; the first key - 0105
mov cx,41h; 65 times - 0108next:
mov ah,[si]; one byte - 010A
sub ah,al; minus the key - 010C
mov [si],ah; written back - 010E
inc si; the next byte - 010F
inc al; the next key - 0111
loop next
-
byte *s = credit;// 01ABh -
byte key = 4; -
for (n = 65; n > 0; n--) { -
*s = *s - key;// unscramble -
s++; -
key++; -
}
- SI
- 01ABh byte 1 of 65
- AL
- 0 the key
- CX
- 0 bytes still to go
His read pointer starts at the 65 scrambled bytes. In a file viewer they read as nonsense.
After his loop The last slide signs the show in Arabic for every viewer: برمجة ياسر محمد على, “Programmed by Yasser Mohamed Ali”. The scrambled lines are the signature inside the code: KEMYAM Software Corp., <c>1991 YASSER MOHAMED ALI.
In paid work he hid his name this way so that nobody could remove it easily. Today we call this obfuscation. A search of the file finds nothing, and a byte editor cannot change the name without knowing his rule.
Every Byte of DEMO.COM
Each square is one byte of the program in memory, colored by what it is. Tap a part to open it in the full map, down to the single instruction.
4,864 bytes on the disk, packed by DIET
25,899 bytes in memory, 25,899 of them his own code
- Picture99%
- Code0.7%
- Text0.3%
- Empty space<0.1%
The biggest parts, his first
- Slide script25,653 bytesYasser
- Slide player152 bytesYasser
- Scrambled credit65 bytesYasser
- Credit decoder19 bytesYasser
- Tail10 bytesYasser
Mapped by hand from the program’s own bytes.