--- /dev/null
+*.o
+*.swp
+*.obj
+*.OBJ
+*.exe
+*.EXE
+*.map
+*.MAP
+*.lnk
+*.LNK
--- /dev/null
+
+!ifdef __UNIX__
+obj = src/main.obj src/video.obj src/3dgfx.obj src/3dgfx_s.obj src/polyfill.obj
+!else
+obj = src\main.obj src\video.obj src\3dgfx.obj src\3dgfx_s.obj src\polyfill.obj
+!endif
+bin = low3d.exe
+
+opt = -3 -otexan
+dbg = -d3
+
+incpath = -Isrc
+
+AS = nasm
+CC = wcc386
+ASFLAGS = -fobj
+CFLAGS = $(dbg) $(opt) $(def) -zq -bt=dos $(incpath)
+LDFLAGS = option map
+LD = wlink
+
+$(bin): $(obj)
+ %write objlist.lnk $(obj)
+ $(LD) debug all name $@ system dos4g file { @objlist } $(LDFLAGS)
+
+.c: src
+.asm: src
+
+.c.obj: .autodepend
+ $(CC) -fo=$@ $(CFLAGS) $[*
+
+.asm.obj:
+ $(AS) $(ASFLAGS) -o $@ $[*.asm
+
+!ifdef __UNIX__
+clean: .symbolic
+ rm -f $(obj) $(bin)
+!else
+clean: .symbolic
+ del src\*.obj
+ del *.lnk
+ del *.map
+ del $(bin)
+!endif
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include "3dgfx.h"
+
+static int32_t mvmat[16];
+static int32_t pmat[16];
+
+int g3d_init(void)
+{
+ memset(mvmat, 0, sizeof mvmat);
+ memset(pmat, 0, sizeof pmat);
+ mvmat[0] = mvmat[5] = mvmat[10] = mvmat[15] = 0;
+ pmat[0] = pmat[5] = pmat[10] = pmat[15] = 0;
+ return 0;
+}
+
+void g3d_shutdown(void)
+{
+}
+
+void g3d_framebuf(int width, int height, void *fb)
+{
+}
+
+void g3d_modelview(const int32_t *m)
+{
+ memcpy(mvmat, m, sizeof mvmat);
+}
+
+void g3d_projection(const int32_t *m)
+{
+ memcpy(pmat, m, sizeof pmat);
+}
+
+void g3d_draw(int prim, struct g3d_vertex *varr, int vcount)
+{
+}
--- /dev/null
+#ifndef GFX3D_H_
+#define GFX3D_H_
+
+#include "types.h"
+
+struct g3d_vertex {
+ int32_t x, y, z;
+};
+
+enum {
+ G3D_POINTS,
+ G3D_LINES,
+ G3D_TRIANGLES
+};
+
+int g3d_init(void);
+void g3d_shutdown(void);
+
+void g3d_framebuf(int width, int height, void *fb);
+
+void g3d_modelview(const int32_t *m);
+void g3d_projection(const int32_t *m);
+
+void g3d_xform(struct g3d_vertex *v, const int32_t *m);
+
+void g3d_draw(int prim, struct g3d_vertex *varr, int vcount);
+
+#endif /* GFX3D_H_ */
--- /dev/null
+ bits 32
+ section .text
+
+ ; eax: vertex ptr, edx: matrix ptr
+ global g3d_xform_
+g3d_xform_:
+ push ebp
+ mov ebp, esp
+ sub esp, 8
+ push ebx
+ push esi
+ push edi
+
+ mov ebx, edx ; matrix to ebx
+ mov edi, eax ; vertex to edi
+
+%macro MULROW 0
+ mov eax, [edi] ; eax <- X
+ imul dword [ebx]
+ mov ecx, eax
+ mov eax, [edi + 4] ; eax <- Y
+ imul dword [ebx + 4]
+ add ecx, eax
+ mov eax, [edi + 8] ; eax <- Z
+ imul dword [ebx + 8]
+ add ecx, eax
+ mov eax, [edi + 12] ; eax <- W
+ imul dword [ebx + 12]
+ add eax, ecx
+%endmacro
+
+ MULROW
+ mov [ebp - 12], eax
+ add ebx, 16 ; next matrix row
+ MULROW
+ mov [ebp - 8], eax
+ add ebx, 16
+ MULROW
+ mov [ebp - 4], eax
+ add ebx, 16
+ MULROW
+ mov [edi + 12], eax ; move W into place
+ ; move XYZ into place
+ mov esi, [ebp - 12]
+ movsd
+ movsd
+ movsd
+
+ pop edi
+ pop esi
+ pop ebx
+ mov esp, ebp
+ pop ebp
+ ret
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <conio.h>
+#include <dos.h>
+#include "video.h"
+#include "3dgfx.h"
+
+void update(void);
+void wait_vsync(void);
+void handle_key(int key);
+void interrupt timer_intr();
+
+static int quit;
+static unsigned char *fb;
+
+static volatile unsigned long nticks;
+
+static void interrupt (*prev_timer_intr)();
+
+int main(void)
+{
+ long rate, nframes = 0;
+ long tstart, tdur;
+
+ if(!(fb = malloc(64000))) {
+ fprintf(stderr, "failed to allocate framebuffer\n");
+ return 1;
+ }
+
+ init_video();
+
+ prev_timer_intr = _dos_getvect(0x1c);
+ _dos_setvect(0x1c, timer_intr);
+ _disable();
+ tstart = nticks;
+ _enable();
+
+ for(;;) {
+ while(kbhit()) {
+ int c = getch();
+ handle_key(c);
+ if(quit) goto end;
+ }
+
+ update();
+ nframes++;
+ }
+
+end:
+ _disable();
+ tdur = nticks - tstart;
+ _enable();
+ _dos_setvect(0x1c, prev_timer_intr);
+
+ close_video();
+ free(fb);
+
+ rate = nframes * 100 * 18 / tdur;
+ printf("%ld frames in %ld sec, rate: %ld.%ld\n", nframes, tdur / 18,
+ rate / 100, rate % 100);
+ return 0;
+}
+
+void update(void)
+{
+ memset(fb, 0, 64000);
+
+ wait_vsync();
+ memcpy((void*)0xa0000, fb, 64000);
+}
+
+void handle_key(int key)
+{
+ switch(key) {
+ case 27:
+ quit = 1;
+ break;
+ }
+}
+
+void interrupt timer_intr()
+{
+ nticks++;
+ _chain_intr(prev_timer_intr);
+}
--- /dev/null
+ bits 32
+ section .text
+
+ global polyfill_
+polyfill_:
+ ret
+
+; vi:ft=nasm ts=8 sts=8 sw=8:
--- /dev/null
+#ifndef TYPES_H_
+#define TYPES_H_
+
+typedef signed char int8_t;
+typedef short int16_t;
+typedef long int32_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+
+#endif /* TYPES_H_ */
--- /dev/null
+#include <dos.h>
+#include "video.h"
+
+void init_video(void)
+{
+ union REGS regs = {0};
+
+ regs.w.ax = 0x13;
+ int386(0x10, ®s, ®s);
+}
+
+void close_video(void)
+{
+ union REGS regs = {0};
+
+ regs.w.ax = 3;
+ int386(0x10, ®s, ®s);
+}
--- /dev/null
+#ifndef VIDEO_H_
+#define VIDEO_H_
+
+void init_video(void);
+void close_video(void);
+
+void wait_vsync(void);
+#pragma aux wait_vsync = \
+ "mov dx, 0x3da" \
+ "invb:" \
+ "in al, dx" \
+ "and al, 8" \
+ "jnz invb" \
+ "waitvb:" \
+ "in al, dx" \
+ "and al, 8" \
+ "jz waitvb" \
+ modify [eax edx];
+
+#endif /* VIDEO_H_ */