--- /dev/null
+!ifdef __UNIX__
+obj = src/dos/main.obj src/dos/video.obj
+!else
+obj = src\dos\main.obj src\dos\video.obj
+!endif
+bin = game.com
+
+opt = -3 -otexan
+dbg = -d3
+
+incpath = -Isrc
+asminc = -i src
+
+AS = nasm
+CC = wcc
+LD = wlink
+ASFLAGS = -fobj $(asminc)
+CFLAGS = $(dbg) $(opt) $(def) -ms -s -zq -bt=com $(incpath)
+
+$(bin): $(obj)
+ %write objects.lnk $(obj)
+ $(LD) name $@ debug all system com option map file { @objects }
+
+.c: src;src/dos
+.asm: src;src/dos
+
+.c.obj: .autodepend
+ $(CC) -fo=$@ $(CFLAGS) $[*
+
+.asm.obj:
+ $(AS) $(ASFLAGS) -o $@ $[*.asm
+
+!ifdef __UNIX__
+clean: .symbolic
+ rm -f $(obj)
+ rm -f $(bin)
+!else
+clean: .symbolic
+ del src\*.obj
+ del src\dos\*.obj
+ del $(bin)
+!endif
--- /dev/null
+ bits 16
+
+segment _TEXT class=CODE
+
+SC_ADDR equ 3c4h ; sequence controller address register
+CRTC_ADDR equ 3d4h ; CRTC address register
+
+ global init_video_
+init_video_:
+ pusha
+ mov ax, 13h
+ int 10h
+
+ ; disable chain-4 (bit 3 of sequencer memory mode register [4])
+ mov dx, SC_ADDR
+ mov ax, 0604h
+ out dx, ax
+ ; disable double-word addressing (bit 6 of CRTC underline location
+ ; register [14h])
+ mov dx, CRTC_ADDR
+ mov ax, 0014h
+ out dx, ax
+ ; enable byte mode address generation (bit 6 of CRTC mode control
+ ; register [17h])
+ mov ax, 0e317h
+ out dx, ax
+
+ ; clear all 256kb of vram
+ mov dx, SC_ADDR
+ mov ax, 0f02h ; map mask reg (2) enable all planes (f)
+ out dx, ax
+ push word 0a000h
+ pop es
+ xor di, di
+ mov ecx, 3fffh
+ xor eax, eax
+ rep stosd
+
+ ; initial back buffer is the second page (far pointer)
+ mov word [_vid_backbuf + 2], 0a400h
+ mov word [_vid_backbuf], 0
+
+ ; set initial scanout address to page 0. if we never pageflip, we
+ ; can just draw to a0000 as usual and it will be visible.
+ ; This also makes sure the low byte is 0, because we're not touching it
+ ; while page flipping; we flip by toggling a bit in the high byte.
+ mov dx, 3dah
+.invb: in al, dx
+ and al, 8
+ jnz .invb
+ mov dx, CRTC_ADDR
+ mov ax, 000ch ; 0ch: start address high register
+ out dx, ax
+ mov ax, 000dh ; 0dh: start address low register
+ out dx, ax
+
+ popa
+ ret
+
+ global close_video_
+close_video_:
+ push ax
+ mov ax, 3
+ int 10h
+ pop ax
+ ret
+
+ global vid_setpalent_
+vid_setpalent_:
+ mov ah, dl
+ mov dx, 3c8h
+ out dx, al
+ inc dx
+ mov al, ah
+ shr al, 2
+ out dx, al
+ mov al, bl
+ shr al, 2
+ out dx, al
+ mov al, cl
+ shr al, 2
+ out dx, al
+ ret
+
+ ; clear the framebuffer 4 pixels at a time
+ global vid_clearfb_
+vid_clearfb_:
+ push eax
+ push ecx
+ push dx
+ push di
+ mov dx, SC_ADDR
+ mov ax, 0f02h ; map mask reg (2) enable all planes (f)
+ out dx, ax
+ mov ax, [_vid_backbuf + 2]
+ mov es, ax
+ mov di, [_vid_backbuf]
+ mov ecx, 4000 ; 4000 dwords * 4 planes * 4 bytes = 64000 pixels
+ xor eax, eax
+ rep stosd
+ pop di
+ pop dx
+ pop ecx
+ pop eax
+ ret
+
+
+ ; vid_backbuf is the far pointer of the back buffer in video RAM
+ ; either a000:0000 or a400:0000. Flipping bit 10 of the segment,
+ ; switches between them, and the low 16bits of the linear address,
+ ; gives the CRTC start address.
+
+ global vid_pgflip_
+vid_pgflip_:
+ push bx
+ push dx
+ push ax
+ ; set the current backbuffer as the new CRTC scanout start address
+ mov ax, [_vid_backbuf + 2]
+ shl ax, 4
+ mov al, 0ch ; CRTC start address high register
+ mov bx, ax
+
+ ; only proceed if we're out of vblank, otherwise we might think we've
+ ; set a new scanout address, but it might not be latched until the next
+ ; vblank, and we'll be drawing over the scanout buffer in the meantime.
+ mov dx, 3dah
+.wait: in al, dx
+ and al, 8
+ jnz .wait
+
+ mov dx, CRTC_ADDR
+ mov ax, bx ; get previously prepared reg addr and value
+ out dx, ax
+
+ pop ax
+ test ax, ax
+ jz .done
+ ; we were already out of vblank, wait until we're in vblank
+ mov dx, 3dah
+.novb: in al, dx
+ and al, 8
+ jz .novb
+.done:
+ ; flip the backbuffer pointer
+ xor word [_vid_backbuf + 2], 400h
+ pop dx
+ pop bx
+ ret
+
+ section .data
+
+ align 4
+ global _vid_backbuf
+_vid_backbuf dd 0
+xyzzy dd 01010101h
--- /dev/null
+#ifndef VIDEO_H_
+#define VIDEO_H_
+
+extern unsigned char far *vid_backbuf;
+
+void init_video(void);
+void close_video(void);
+void vid_setpalent(int idx, int r, int g, int b);
+
+void vid_clearfb(void);
+void vid_clearfb_rect(int x, int y, int w, int h);
+void vid_pgflip(int vsync);
+
+void vid_setmask(unsigned int mask);
+#pragma aux vid_setmask = \
+ "mov dx, 0x3c4" \
+ "mov ah, al" \
+ "mov al, 2" \
+ "out dx, ax" \
+ parm [ax] \
+ modify [ax dx];
+
+void vid_wait_vblank(void);
+#pragma aux vid_wait_vblank = \
+ "mov dx, 0x3da" \
+ "waitvb:" \
+ "in al, dx" \
+ "and al, 8" \
+ "jz waitvb" \
+ modify [al dx];
+
+void vid_vsync(void);
+#pragma aux vid_vsync = \
+ "mov dx, 0x3da" \
+ "invb:" \
+ "in al, dx" \
+ "and al, 8" \
+ "jnz invb" \
+ "waitvb:" \
+ "in al, dx" \
+ "and al, 8" \
+ "jz waitvb" \
+ modify [al dx];
+
+
+#endif /* VIDEO_H_ */