Start page: ARABART home
← All fifteen programs

WINKEY

1992 · C:\WINKEY\WINKEY.EXE

C:\WINKEY\WINKEY.EXE
WINKEY running in DOS

Loading WINKEY.EXE, 6 KB

Click the screen to type into the program.

The start of his own window toolkit: eight 3-D buttons with icons that press in and pop back out.

The story

WINKEY is the first demo of the window toolkit Yasser began for DOS in 1992. A few bytes of table describe every button style, and a separate file holds the icons. His own assembly paints up to 8 pixels with one write to the graphics card. The same pixel trick appears in his Arabic engine, ARABART: a fingerprint of his hand.

Eight buttons that press themselves

In 1992 Yasser began his own window toolkit for DOS, inspired by the new look of Windows. This is its first demo: a panel of 8 icon buttons, and each one presses itself 11 times.

DOS programs had no buttons with depth, so he drew his own. The bevels, the icons and the press are all his code, talking straight to the EGA graphics card: 640 × 350 pixels in 16 colors. Run the demo, or press a button yourself.

WINKEY.EXE EGA 640 × 350
His demo's last screen: a gray panel with 8 raised icon buttons in a column: the DOS logo, a disk drive, a floppy with an arrow, a page and pen, a second floppy, and three pages with large colored letters.
MAIN.C the call that drew it
button(18, 263, 48, 34, 0x60, 7);
icon(0x0904, 20, 269, 0);

The end of the demo: button 8 of 8, press 11 of 11. Style 60h, raised, and all 8 icons in place.

Every press is two calls in his main: draw the button in its pressed style, then draw its icon 2 pixels lower and 2 pixels to the right. Then the raised style, and the icon back in place. That small shift is the whole trick. The eye reads it as a key going down, the same thing a web page does with :active { translate: 2px 2px }.

The screen above is not a screenshot. Your browser redraws it from his button table and his icon file, and it matches the screen his own program draws, pixel for pixel. 177 buttons in all: the panel, then 8 buttons pressed and raised 11 times each.

Light from the top left

A flat screen shows depth with two colors: light on the top and left edges, shadow on the bottom and right. Swap them, and the button sinks.

BUTTON style 60h, face 7
His raised button at 6 times its size: a black outline, two rings of white on the top and left and dark gray on the bottom and right, a gray face and the DOS logo.

Style 60h, raised: a black outline, then light on the top and left. Press it.

STYLES his style table

His button routine reads a small table stored inside WINKEY.EXE. It holds 6 styles. Each style is a list of rings from the outside in. Each ring is 2 bytes: the color for the top and left edges, then the color for the bottom and right. A byte of 10h ends the list, and one fill paints the face. A whole look is a few bytes of data, like a CSS rule: border-style: outset raises a box and inset sinks it.

Pick another face color and the light follows. His routine moves every ring color except black and dark gray by the same step as the face. So white turns into the bright version of the face color. One table serves a gray button, a blue one or a green one.

A toolkit in 15 routines

WINKEY is a C program, built with Turbo C++. Its library is 15 small C functions whose insides are his own assembly, written inline. The compiler adds the first and last lines of each one, and everything in between is Yasser talking to the card. The library already holds routines for menus and selections that this demo does not need yet. It can save the screen under a menu and put it back. It can draw an XOR highlight, fill a checkerboard, outline a rectangle and work out the size of a saved area.

His library, and the same jobs in a browser
19922026Why it is the same idea
bar: fill a rectangle with one color ctx.fillRect() on a canvas Set the color once, then paint an area.
save and restore an area of the screen getImageData() and putImageData() Keep the pixels under a menu, put them back when it closes.
icon: a picture from MENU2.DAT drawImage() from a sprite sheet The art lives in its own file, loaded once.
XOR highlight globalCompositeOperation = "difference" Draw it twice and the screen is back as it was.
button: rings of light and shadow, then the face CSS border-style: outset and inset The same light-and-shadow idea, now one line.
For engineers: the 15 routines
WINKEY.EXE, his library (offsets in the code segment)
0239 load MENU2.DAT ; DOS open, read 4,000 bytes, close
027C icon ; runs from MENU2.DAT, one write per byte
0353 reset the Graphics Controller ; Set/Reset off, Bit Mask FFh
0363 color ; Set/Reset = the color, all 4 planes
037B area size ; bytes to save a rectangle: w × h ÷ 2 + 10
03BE allocate ; DOS memory, bytes rounded up to 16
03EA save area ; copies each of the 4 planes out
047B restore area ; writes them back, plane by plane
04D7 free ; DOS memory
04E3 XOR highlight ; flips the brightness plane
05BA bar ; a filled rectangle
066F checkerboard ; every other pixel
0765 line ; horizontal or vertical
085C rectangle ; four lines
08A3 button ; rings from the table at 09B2, then a bar
The names are plain descriptions of what each routine does. The style table at 09B2 holds 16 bytes per style: 10h flat, 20h sunken, 30h raised, 40h panel, 50h pressed, 60h released.

One write, up to eight pixels

On the EGA card a pixel lives in four places at once. Yasser makes the card do the four-way work for him, eight pixels at a time.

The card keeps the screen in 4 layers called bit planes: one for blue, one for green, one for red and one for brightness. Think of 4 sheets of tracing paper over one drawing. A pixel's color is one bit from each sheet, and each byte of a sheet covers 8 pixels side by side.

So he sets the card up once. Its Set/Reset setting holds the color for all 4 sheets. Its Bit Mask says which of the 8 pixels may change, like a stencil. Then he reads the byte, so the card keeps a copy of what is already there in its latches, and writes it back. Every pixel inside the stencil takes the color, on all 4 sheets, and the rest stay as they were. Try both ways below.

ONE BYTE = 8 PIXELS row 44, x 24 to 39
One pixel at a time
9 writes
His way
2 writes

9 pixels picked, across 2 bytes: 9 writes one pixel at a time, 2 writes his way. Tap pixels to change the pick.

His loop, line by line

This is the loop that paints every icon. Here it runs on the first run of the DOS logo: skip 25 pixels from the right edge, then paint 9 in light red. It walks left, adds each pixel to the mask, and writes only when it reaches the left end of a byte. Step through it, or press Run.

  1. 24
  2. 25
  3. 26
  4. 27
  5. 28
  6. 29
  7. 30
  8. 31
  9. 32
  10. 33
  11. 34
  12. 35
  13. 36
  14. 37
  15. 38
  16. 39

Bit Mask 00000000 Writes 2 Pixels painted 9

WINKEY.EXE the run loop
  1. 02D0up:dec bx; one row up
  2. 02D1xor ah,ah
  3. 02D3run:mov dx,[bp+6]; the right edge
  4. 02D6lodsb; next byte
  5. 02D7cmp al,0EFh / je up; EFh: row up
  6. 02DBcmp al,0FCh / je color; FCh: new color
  7. 02DFja done; FDh+: the end
  8. 02E1sub dx,ax; skip pixels
  9. 02E3lodsb; how many
  10. 02E7mov ax,bx; the row
  11. 02EBmov bx,50h / mul bx; × 80 bytes
  12. 02F0mov di,ax; row start
  13. 02F4mov cx,3 / xor bx,bx; 3 times:
  14. 02F9rev:shr dx,1 / rcl bx,1; a bit into BX
  15. 02FDloop rev
  16. 02FFadd di,dx; the byte
  17. 0301mov cl,cs:[bx+033Dh]; x mod 8
  18. 0308mov bx,cx / mov cx,ax; pixel, count
  19. 030Cmov dx,3CEh / mov al,8; the card
  20. 0311out dx,al; pick Bit Mask
  21. 0312inc dx / xor ax,ax; empty mask
  22. 0315pix:or al,cs:[bx+0345h]; add the pixel
  23. 031Acmp bx,0 / jne more; left end?
  24. 031Fout dx,al; set the mask
  25. 0320mov al,es:[di]; read: latches
  26. 0323mov es:[di],al; write: paint
  27. 0326dec di; byte to the left
  28. 0327mov bx,8 / xor al,al; new mask
  29. 032Cmore:dec bx / loop pix; next pixel
  30. 032Fout dx,al; the last mask
  31. 0330mov al,es:[di]; read
  32. 0333mov es:[di],al; write
  33. 0336pop bx / jmp 02D1; next run
The same in C
  1. for (;;) {
  2. int b = *p++;// next byte
  3. if (b == 0xEF) { row--; continue; }
  4. if (b == 0xFC) newColor();
  5. if (b > 0xFC) break;// the end
  6. int x = right - b;// skip b
  7. int n = *p++;// paint n
  8. char *v = vga + row*80 + x/8;
  9. int bit = x % 8;
  10. unsigned char mask = 0;
  11. while (n--) {
  12. mask |= 0x80 >> bit;
  13. if (bit == 0) {// left end
  14. bitMask(mask);
  15. *v = *v;// read, write
  16. v--; bit = 8; mask = 0;
  17. }
  18. bit--;
  19. }
  20. bitMask(mask); *v = *v;
  21. }
Watches
BX
44 the row, back from the stack
Help

On to the next byte of the file: the next run.

For engineers: setting up the card
WINKEY.EXE, the icon routine (0000:02AC), on every new color
02AC mov al,[si] / inc si ; the color byte from MENU2.DAT
02B2 mov dx,3CEh ; the Graphics Controller
02B5 mov al,1 / out dx,al ; register 1: Enable Set/Reset
02B8 inc dx / mov al,0Fh / out dx,al ; all 4 planes take the Set/Reset color
02BC dec dx / xor al,al / out dx,al ; register 0: Set/Reset
02C0 inc dx / mov al,[bp-1] / out dx,al ; the color itself
030C mov dx,3CEh / mov al,8 / out dx,al ; register 8: the Bit Mask, set once per byte
Port 3CEh picks a register and 3CFh takes its value. After this, the bytes the CPU writes no longer matter: the card paints the Set/Reset color through the mask. His ARABART engine sets up the card the same way.

Eight icons, 3,005 bytes

The pictures on the buttons are not inside the program. They live in MENU2.DAT, a separate file, so new icons need no new code.

The file opens with a table of 8 addresses, one per icon, like the index of a sprite sheet. Each icon is a list of runs: skip this many pixels from the right edge, then paint that many to the left. The byte EF moves up one row, FC starts a new color, and FD or more ends the icon. Pick an icon and watch his bytes paint it.

MENU2.DAT pick an icon
ICON his bytes, one run at a time
The DOS logo from MENU2.DAT: the letters D, O and S in light red, magenta and yellow, on the gray button face.

2A 1A0C 0619 09EF18 0AEF18 0AEF17 041F 03EF17 031F 03EF16 031F 03EF16 031F 03EF16 031F 03EF16 031F 03EF16 031F 03EF16 031F 03EF16 031F 03EF16 021F 03EF16 021F 03EF16 011F 03EF1A 011F 03EF19 09EF18 0AEF19 09FC05 0612 04EF11 07EF12 06EF12 0115 02EF0D 0210 01EF0D 0319 02EF0C 0419 03EF0C 0319 03EF0C 0319 03EF0C 0319 03EF0C 0319 03EF0C 0319 03EF0C 0418 04EF0D 0318 03EF0D 0217 04EF11 0115 05EF12 07EF11 07EF12 04FC0E 060A 05EF08 09EF07 0BEF07 040E 04EF06 040F 04EF06 0310 03EF06 0310 03EF07 04EF08 04EF09 030F 01EF0B 010F 02EF0F 03EF06 0310 03EF06 0310 03EF06 040F 04EF07 040E 04EF07 0BEF08 09EF0A 05FF

The DOS logo: 251 bytes, 93 runs. Press Run to watch his loop paint it.

The DOS logo takes 251 bytes. As a plain bitmap, 42 × 26 pixels at 4 bits each, it would take 546. The program loads the whole file with three DOS calls, open, read and close, and draws every icon from memory. On the web the same idea is an SVG sprite sheet: one file of pictures, loaded once, each picture found by name.

His Arabic engine stores its letters the same way: runs counted from the right edge, rows from the bottom up, and the same EF for one row up. Watch a letter paint itself on the ARABART page.

A fingerprint of his hand

To find a pixel, a program needs two numbers from its x: which byte holds it, x ÷ 8, and which bit inside that byte, x mod 8. Yasser gets both from six instructions of his own.

x = 300: byte 37 of the row, pixel 4 inside it. In JavaScript: x >> 3 and x & 7.

WINKEY.EXE x ÷ 8 and x mod 8
  1. 02F4mov cx,3; 3 times
  2. 02F7xor bx,bx; BX = 0
  3. 02F9rev:shr dx,1; a bit falls out
  4. 02FBrcl bx,1; into BX
  5. 02FDloop rev
  6. 02FFadd di,dx; x ÷ 8: the byte
  7. 0301mov cl,cs:[bx+033Dh]; x mod 8
  8. 033Ddb 00,04,02,06, 01,05,03,07; his table
The same in C
  1. unsigned r = 0, carry;
  2. for (int i = 0; i < 3; i++) {
  3. carry = x & 1; x >>= 1;// shr
  4. r = (r << 1) | carry;// rcl
  5. }
  6. int byte = x;// now x / 8
  7. int bit = TABLE[r];// his table
  8. // the textbook way: x >> 3, x & 7
Watches
CL
4 300 mod 8
Help

His little table turns 001 back: entry 1 holds 4. That is x mod 8, the pixel inside the byte.

A textbook writes and bx,7, the x & 7 of JavaScript. Yasser shifts the 3 low bits out of x, one at a time. Each one falls into the carry flag and slides into a second register, so they arrive in reverse order. His 8-byte table, 00 04 02 06 01 05 03 07, turns them back. The same three shifts leave x ÷ 8 behind, so one small loop gives both answers.

WINKEY uses it in 5 of its routines: the icon, the bar, the line, the checkerboard and the highlight. The same bytes appear in his Arabic engine, ARABART 4.15, in ARAB-ART 7.15 and in PESM, and in no other program in this archive. Carried from project to project, it marks his code as surely as a signature. See it at work in ARABART.

Every Byte of WINKEY.EXE

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.

C:\WINKEY\WINKEY.EXE

4,220 bytes on the disk, packed by DIET

7,056 bytes in memory, 3,507 of them his own code

  • Code50%
  • Borland library30%
  • Data10%
  • Start-up code8.1%
  • Stack1.8%
  • Header0.4%
  • Fix-up list0.2%
  • Empty space0.1%

The biggest parts, his first

  1. His drawing library2,013 bytesYasser
  2. main1,494 bytesYasser
  3. Turbo C runtime library2,084 bytesBorland’s compiler
  4. Runtime data720 bytesBorland’s compiler
  5. Turbo C++ start-up (c0)569 bytesBorland’s compiler
  6. Initial stack128 bytesBorland’s compiler

Mapped by hand from the program’s own bytes.

Open in the byte mapSee the packed file