!ifdef __UNIX__
obj = src/main.obj src/video.obj src/3dgfx.obj src/3dgfx_s.obj &
- src/polyfill.obj src/lut.obj
+ src/polyfill.obj src/lut.obj src/vmath.obj src/vmath_s.obj
!else
obj = src\main.obj src\video.obj src\3dgfx.obj src\3dgfx_s.obj &
- src\polyfill.obj src\lut.obj
+ src\polyfill.obj src\lut.obj src\vmath.obj src\vmath_s.obj
!endif
bin = low3d.exe
+#include "config.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int32_t mvmat[16];
static int32_t pmat[16];
static int vp[4];
+static int dirty_x0, dirty_x1, dirty_y0, dirty_y1;
unsigned char *g3d_fbpixels;
int g3d_width, g3d_height;
int g3d_curcidx;
+
int g3d_init(void)
{
memset(mvmat, 0, sizeof mvmat);
vp[0] = vp[1] = 0;
vp[2] = width;
vp[3] = height;
+
+ g3d_reset_dirty();
+}
+
+void g3d_reset_dirty(void)
+{
+ dirty_x0 = XRES;
+ dirty_y0 = YRES;
+ dirty_x1 = 0;
+ dirty_y1 = 0;
+}
+
+void blkclear(void *p, int len, int col);
+#pragma aux blkclear = \
+ "mov ah, al" \
+ "shl eax, 8" \
+ "mov al, ah" \
+ "shl eax, 8" \
+ "mov al, ah" \
+ "shr ecx, 2" \
+ "rep stosd" \
+ parm [edi] [ecx] [eax] \
+ modify [eax ecx edi];
+
+extern volatile long nticks;
+#define COL 0
+
+void g3d_clear_dirty(void)
+{
+ unsigned char *ptr;
+ int i, count, nlines;
+
+ if(dirty_x0 < 0) dirty_x0 = 0;
+ if(dirty_y0 < 0) dirty_y0 = 0;
+ if(dirty_x1 >= XRES) dirty_x1 = XRES - 1;
+ if(dirty_y1 >= YRES) dirty_y1 = YRES - 1;
+
+ nlines = dirty_y1 - dirty_y0;
+ if(dirty_y1 <= 0 || nlines >= YRES) {
+ blkclear(g3d_fbpixels, XRES * YRES, COL);
+ goto end;
+ }
+
+ ptr = g3d_fbpixels + dirty_y0 * XRES;
+ if(dirty_x1 > XRES - 4) {
+ blkclear(ptr, nlines * XRES, COL);
+ goto end;
+ }
+
+ ptr = (unsigned char*)((uintptr_t)(ptr + dirty_x0) & 0xfffffffc);
+ count = dirty_x1 + 3 - dirty_x0;
+ for(i=0; i<nlines; i++) {
+ blkclear(ptr, count, COL);
+ ptr += XRES;
+ }
+end:
+ g3d_reset_dirty();
+}
+
+
+void vmemcopy(long fboffs, void *p, int len);
+#pragma aux vmemcopy = \
+ "mov edi, 0xa0000" \
+ "add edi, eax" \
+ "shr ecx, 2" \
+ "rep movsd" \
+ parm [eax] [esi] [ecx] \
+ modify [ecx edi esi];
+
+void g3d_copy_dirty(void)
+{
+ int i, count, nlines;
+ unsigned long fboffs;
+
+ if(dirty_x0 < 0) dirty_x0 = 0;
+ if(dirty_y0 < 0) dirty_y0 = 0;
+ if(dirty_x1 >= XRES) dirty_x1 = XRES - 1;
+ if(dirty_y1 >= YRES) dirty_y1 = YRES - 1;
+
+ nlines = dirty_y1 - dirty_y0;
+ if(dirty_y1 <= 0 || nlines >= YRES) {
+ vmemcopy(0, g3d_fbpixels, XRES * YRES);
+ return;
+ }
+
+ fboffs = dirty_y0 * XRES;
+ if(dirty_x1 > XRES - 4) {
+ vmemcopy(fboffs, g3d_fbpixels + fboffs, nlines * XRES);
+ return;
+ }
+
+ fboffs += dirty_x0 & 0xfffffffc;
+ count = dirty_x1 + 3 - dirty_x0;
+ for(i=0; i<nlines; i++) {
+ vmemcopy(fboffs, g3d_fbpixels + fboffs, count);
+ fboffs += XRES;
+ }
}
void g3d_modelview(const int32_t *m)
/* viewport transform */
v[i].x = vpscale(v[i].x, vp[2], 1) + (vp[0] << 8);
v[i].y = vpscale(-v[i].y, vp[3], 1) + (vp[1] << 8);
+
+#if defined(USE_DIRTY_CLEAR) || defined(USE_DIRTY_COPY)
+ x = v[i].x >> 8;
+ y = v[i].y >> 8;
+ if(x - 4 < dirty_x0) dirty_x0 = x - 4;
+ if(y - 4 < dirty_y0) dirty_y0 = y - 4;
+ if(x + 8 > dirty_x1) dirty_x1 = x + 8;
+ if(y + 8 > dirty_y1) dirty_y1 = y + 8;
+#endif
}
switch(prim) {
extern int g3d_width, g3d_height;
extern int g3d_curcidx;
+extern int g3d_bbox[4];
+
int g3d_init(void);
void g3d_shutdown(void);
void g3d_framebuffer(int width, int height, void *fb);
+void g3d_reset_dirty(void);
+void g3d_clear_dirty(void);
+void g3d_copy_dirty(void);
+
void g3d_modelview(const int32_t *m);
void g3d_projection(const int32_t *m);
--- /dev/null
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#undef USE_VSYNC
+#define USE_DIRTY_CLEAR
+#define USE_DIRTY_COPY
+
+#endif /* CONFIG_H_ */
+#include "config.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include "video.h"
#include "3dgfx.h"
+#include "vmath.h"
#include "util.h"
void update(void);
static int quit;
static unsigned char *fb;
+static long nframes;
-static volatile unsigned long nticks;
+volatile unsigned long nticks;
static void interrupt (*prev_timer_intr)();
int main(void)
{
- long rate, nframes = 0;
- long tstart, tdur;
+ int32_t proj[16];
+ long rate;
- if(!(fb = malloc(64000))) {
+ if(!(fb = calloc(1, 64000))) {
fprintf(stderr, "failed to allocate framebuffer\n");
return 1;
}
g3d_init();
g3d_framebuffer(320, 200, fb);
+ mat_perspective(proj, 50, (4 << 16) / 3, 0x8000, 0x100000);
+ g3d_projection(proj);
+
prev_timer_intr = _dos_getvect(0x1c);
_dos_setvect(0x1c, timer_intr);
- _disable();
- tstart = nticks;
- _enable();
for(;;) {
while(kbhit()) {
}
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 = nframes * 100 * 18 / nticks;
+ printf("%ld frames in %ld sec, rate: %ld.%ld\n", nframes, nticks / 18,
rate / 100, rate % 100);
return 0;
}
+#define VERT(x, y, z) { x << 16, y << 16, z << 16, 0x10000 }
struct g3d_vertex varr[] = {
- {0, 0x8000, 0, 0x10000},
- {-0x8f00, -0x8000, 0, 0x10000},
- {0x8c00, -0x6000, 0, 0x10000}
+ VERT(-1, -1, 1), VERT(1, -1, 1), VERT(1, 1, 1), VERT(-1, 1, 1),
+ VERT(1, -1, 1), VERT(1, -1, -1), VERT(1, 1, -1), VERT(1, 1, 1),
+ VERT(1, -1, -1), VERT(-1, -1, -1), VERT(-1, 1, -1), VERT(1, 1, -1),
+ VERT(-1, -1, -1), VERT(-1, -1, 1), VERT(-1, 1, 1), VERT(-1, 1, -1),
+ VERT(-1, 1, 1), VERT(1, 1, 1), VERT(1, 1, -1), VERT(-1, 1, -1),
+ VERT(-1, -1, -1), VERT(1, -1, -1), VERT(1, -1, 1), VERT(-1, -1, 1)
};
-void mat_rotz(int32_t *m, int theta)
-{
- m[0] = XCOS(theta);
- m[1] = XSIN(theta);
- m[4] = -XSIN(theta);
- m[5] = XCOS(theta);
- m[10] = 0x10000;
- m[15] = 0x10000;
- m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[12] = m[13] = m[14] = 0;
-}
-
void update(void)
{
int32_t xform[16];
+#ifdef USE_DIRTY_CLEAR
+ g3d_clear_dirty();
+#else
vid_clearfb(fb);
+#endif
- mat_rotz(xform, nticks);
+ mat_trans(xform, 0, 0, -0x40000);
+ mat_mul_rotx(xform, nframes);
+ mat_mul_roty(xform, nframes);
g3d_modelview(xform);
- g3d_color(15);
- g3d_draw(G3D_TRIANGLES, varr, sizeof varr / sizeof *varr);
-
- /*wait_vsync();*/
+ g3d_color(9);
+ g3d_draw(G3D_QUADS, varr, 4);
+ g3d_color(10);
+ g3d_draw(G3D_QUADS, varr + 4, 4);
+ g3d_color(11);
+ g3d_draw(G3D_QUADS, varr + 8, 4);
+ g3d_color(12);
+ g3d_draw(G3D_QUADS, varr + 12, 4);
+ g3d_color(13);
+ g3d_draw(G3D_QUADS, varr + 16, 4);
+ g3d_color(14);
+ g3d_draw(G3D_QUADS, varr + 20, 4);
+
+#ifdef USE_VSYNC
+ wait_vsync();
+#endif
+#ifdef USE_DIRTY_COPY
+ g3d_copy_dirty();
+#else
vid_copyfb(fb);
+#endif
}
void handle_key(int key)
{
int i, topidx, botidx, mididx;
int32_t dx, dy, dym;
+ int32_t v01x, v01y, v02x, v02y;
struct g3d_vertex vtmp;
+ /* calculate winding */
+ v01x = verts[1].x - verts[0].x;
+ v01y = verts[1].y - verts[0].y;
+ v02x = verts[2].x - verts[0].x;
+ v02y = verts[2].y - verts[0].y;
+ if(!((v01x * v02y - v02x * v01y) & 0x80000000)) {
+ return;
+ }
+
topidx = botidx = 0;
for(i=1; i<3; i++) {
if(verts[i].y < verts[topidx].y) {
botidx = i;
}
}
- mididx = (topidx + 1) % 3;
- if(mididx == botidx) mididx = (mididx + 1) % 3;
+ mididx = topidx + 1; if(mididx > 2) mididx = 0;
+ if(mididx == botidx) {
+ if(++mididx > 2) mididx = 0;
+ }
dy = verts[botidx].y - verts[topidx].y;
if(dy == 0) return;
static void filltop(struct g3d_vertex *v0, struct g3d_vertex *v1, struct g3d_vertex *v2)
{
struct g3d_vertex *vtmp;
- int x, xn, y, endy, len;
- int32_t xl, xr, dxl, dxr, dxldy, dxrdy, dy;
+ int x, xn, line, lasty, len;
+ int32_t xl, xr, dxl, dxr, slopel, sloper, dy;
+ int32_t y0, y1, yoffs;
unsigned char *fbptr;
if(v1->x > v2->x) {
v2 = vtmp;
}
- y = v0->y >> 8;
- endy = v1->y >> 8;
- if(endy > YRES) endy = YRES;
- x = v0->x >> 8;
-
- fbptr = g3d_fbpixels + y * XRES + x;
-
- xl = xr = v0->x;
dy = v1->y - v0->y;
dxl = v1->x - v0->x;
dxr = v2->x - v0->x;
- dxldy = (dxl << 8) / dy;
- dxrdy = (dxr << 8) / dy;
+ slopel = (dxl << 8) / dy;
+ sloper = (dxr << 8) / dy;
+
+ y0 = (v0->y + 0x100) & 0xffffff00; /* start from the next scanline */
+ yoffs = y0 - v0->y; /* offset of the next scanline */
+ xl = v0->x + ((yoffs * slopel) >> 8);
+ xr = v0->x + ((yoffs * sloper) >> 8);
+
+ line = y0 >> 8;
+ lasty = v1->y >> 8;
+ if(lasty >= YRES) lasty = YRES - 1;
+ x = xl >> 8;
- while(y++ < endy) {
- if(y > 0) {
- len = (xr - xl) >> 8;
+ fbptr = g3d_fbpixels + line * XRES + x;
+
+ while(line <= lasty) {
+ if(line >= 0) {
+ len = ((xr + 0x100) >> 8) - (xl >> 8);
if(len > 0) memset(fbptr, g3d_curcidx, len);
}
- xl += dxldy;
- xr += dxrdy;
+ xl += slopel;
+ xr += sloper;
xn = xl >> 8;
fbptr += XRES + (xn - x);
x = xn;
+ line++;
}
}
static void fillbot(struct g3d_vertex *v0, struct g3d_vertex *v1, struct g3d_vertex *v2)
{
struct g3d_vertex *vtmp;
- int x, xn, y, endy, len;
- int32_t xl, xr, dxl, dxr, dxldy, dxrdy, dy;
+ int x, xn, line, lasty, len;
+ int32_t xl, xr, dxl, dxr, slopel, sloper, dy;
+ int32_t y0, y1, yoffs;
unsigned char *fbptr;
if(v0->x > v1->x) {
v1 = vtmp;
}
- y = v0->y >> 8;
- endy = v2->y >> 8;
- if(endy > YRES) endy = YRES;
- x = v0->x >> 8;
-
- fbptr = g3d_fbpixels + y * XRES + x;
-
- xl = v0->x;
- xr = v1->x;
dy = v2->y - v0->y;
dxl = v2->x - v0->x;
dxr = v2->x - v1->x;
- dxldy = (dxl << 8) / dy;
- dxrdy = (dxr << 8) / dy;
+ slopel = (dxl << 8) / dy;
+ sloper = (dxr << 8) / dy;
+
+ y0 = (v0->y + 0x100) & 0xffffff00; /* start from the next scanline */
+ yoffs = y0 - v0->y; /* offset of the next scanline */
+ xl = v0->x + ((yoffs * slopel) >> 8);
+ xr = v1->x + ((yoffs * sloper) >> 8);
+
+ line = y0 >> 8;
+ lasty = v2->y >> 8;
+ if(lasty >= YRES) lasty = YRES - 1;
+ x = xl >> 8;
+
+ fbptr = g3d_fbpixels + line * XRES + x;
- while(y++ < endy) {
- if(y > 0) {
- len = (xr - xl) >> 8;
+ while(line <= lasty) {
+ if(line >= 0) {
+ len = ((xr + 0x100) >> 8) - (xl >> 8);
if(len > 0) memset(fbptr, g3d_curcidx, len);
}
- xl += dxldy;
- xr += dxrdy;
+ xl += slopel;
+ xr += sloper;
xn = xl >> 8;
fbptr += XRES + (xn - x);
x = xn;
+ line++;
}
}
--- /dev/null
+#include <string.h>
+#include <math.h>
+#include "vmath.h"
+#include "util.h"
+
+static const int32_t idmat[] = {
+ 0x10000, 0, 0, 0,
+ 0, 0x10000, 0, 0,
+ 0, 0, 0x10000, 0,
+ 0, 0, 0, 0x10000
+};
+
+void mat_identity(int32_t *m)
+{
+ memcpy(m, idmat, sizeof idmat);
+}
+
+void mat_trans(int32_t *m, int32_t x, int32_t y, int32_t z)
+{
+ memcpy(m, idmat, sizeof idmat);
+ m[3] = x;
+ m[7] = y;
+ m[11] = z;
+}
+
+void mat_rotx(int32_t *m, int32_t theta)
+{
+ memcpy(m, idmat, sizeof idmat);
+ m[5] = XCOS(theta);
+ m[6] = XSIN(theta);
+ m[9] = -XSIN(theta);
+ m[10] = XCOS(theta);
+}
+
+void mat_roty(int32_t *m, int32_t theta)
+{
+ memcpy(m, idmat, sizeof idmat);
+ m[0] = XCOS(theta);
+ m[2] = -XSIN(theta);
+ m[8] = XSIN(theta);
+ m[10] = XCOS(theta);
+}
+
+void mat_rotz(int32_t *m, int32_t theta)
+{
+ memcpy(m, idmat, sizeof idmat);
+ m[0] = XCOS(theta);
+ m[1] = XSIN(theta);
+ m[4] = -XSIN(theta);
+ m[5] = XCOS(theta);
+}
+
+void mat_mul_trans(int32_t *m, int32_t x, int32_t y, int32_t z)
+{
+ int32_t tmp[16];
+ mat_trans(tmp, x, y, z);
+ mat_mult(m, tmp);
+}
+
+void mat_mul_rotx(int32_t *m, int32_t theta)
+{
+ int32_t tmp[16];
+ mat_rotx(tmp, theta);
+ mat_mult(m, tmp);
+}
+
+void mat_mul_rotz(int32_t *m, int32_t theta)
+{
+ int32_t tmp[16];
+ mat_rotz(tmp, theta);
+ mat_mult(m, tmp);
+}
+
+void mat_mul_roty(int32_t *m, int32_t theta)
+{
+ int32_t tmp[16];
+ mat_roty(tmp, theta);
+ mat_mult(m, tmp);
+}
+
+void mat_perspective(int32_t *m, int vfov, int32_t aspect, int32_t znear, int32_t zfar)
+{
+ float halffov = (float)vfov * 3.1415926536f / 360.0f;
+ float s = 1.0f / (float)tan(halffov);
+ float zn = (float)znear / 65536.0f;
+ float zf = (float)zfar / 65536.0f;
+ float dz = zn - zf;
+
+ memset(m, 0, 16 * sizeof *m);
+ m[0] = (int32_t)((s / ((float)aspect / 65536.0f)) * 65536.0f);
+ m[5] = (int32_t)(s * 65536.0f);
+ m[10] = (int32_t)((zn + zf) / dz * 65536.0f);
+ m[11] = (int32_t)(2.0f * zn * zf / dz * 65536.0f);
+ m[14] = -0x10000;
+ m[15] = 0x10000;
+}
--- /dev/null
+#ifndef VMATH_H_
+#define VMATH_H_
+
+#include "types.h"
+
+void mat_trans(int32_t *m, int32_t x, int32_t y, int32_t z);
+void mat_rotx(int32_t *m, int32_t theta);
+void mat_roty(int32_t *m, int32_t theta);
+void mat_rotz(int32_t *m, int32_t theta);
+
+void mat_mul_trans(int32_t *m, int32_t x, int32_t y, int32_t z);
+void mat_mul_rotx(int32_t *m, int32_t theta);
+void mat_mul_roty(int32_t *m, int32_t theta);
+void mat_mul_rotz(int32_t *m, int32_t theta);
+
+void mat_perspective(int32_t *m, int vfov, int32_t aspect, int32_t znear, int32_t zfar);
+
+void mat_mult(int32_t *ma, int32_t *mb);
+
+#endif /* VMATH_H_ */
--- /dev/null
+ bits 32
+ section .text USE32
+
+ ; eax: ma ptr, edx: mb ptr
+ global mat_mult_
+mat_mult_:
+ push ebp
+ mov ebp, esp
+ sub esp, 64
+ push ebx
+ push ecx
+ push esi
+ push edi
+
+ mov ebx, edx ; mb to ebx
+ mov edi, eax ; ma to edi
+
+%macro MULROWCOL 0
+ mov eax, [edi] ; eax <- row[0]
+ imul dword [ebx] ; mul col[0]
+ shrd eax, edx, 16
+ mov ecx, eax
+ mov eax, [edi + 4] ; eax <- row[1]
+ imul dword [ebx + 16] ; mul col[1]
+ shrd eax, edx, 16
+ add ecx, eax
+ mov eax, [edi + 8] ; eax <- row[2]
+ imul dword [ebx + 32] ; mul col[2]
+ shrd eax, edx, 16
+ add ecx, eax
+ mov eax, [edi + 12] ; eax <- row[3]
+ imul dword [ebx + 48] ; mul col[3]
+ shrd eax, edx, 16
+ add eax, ecx ; dot product in eax
+%endmacro
+
+ ; row 0
+ MULROWCOL
+ mov [ebp - 64], eax
+ add ebx, 4 ; next matrix b column
+ MULROWCOL
+ mov [ebp - 60], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 56], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 52], eax
+ sub ebx, 12 ; return to mb column 0
+ add edi, 16 ; next matrix a row
+
+ ; row 1
+ MULROWCOL
+ mov [ebp - 48], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 44], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 40], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 36], eax
+ sub ebx, 12 ; return to mb column 0
+ add edi, 16 ; next matrix a row
+
+ ; row 2
+ MULROWCOL
+ mov [ebp - 32], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 28], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 24], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 20], eax
+ sub ebx, 12 ; return to mb column 0
+ add edi, 16 ; next matrix a row
+
+ ; row 3
+ MULROWCOL
+ mov [ebp - 16], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 12], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 8], eax
+ add ebx, 4
+ MULROWCOL
+ mov [ebp - 4], eax
+
+ sub edi, 48 ; return edi to the start of matrix a
+ lea esi, [ebp - 64] ; esi to the temp matrix on stack
+ mov ecx, 16
+ rep movsd
+
+ pop edi
+ pop esi
+ pop ecx
+ pop ebx
+ mov esp, ebp
+ pop ebp
+ ret
+
+; vi:ft=nasm ts=8 sts=8 sw=8:
+ ret
-#include <stdio.h>\r
-#include <math.h>\r
-\r
-#define SINTAB_SIZE 512\r
-#define SINTAB_SCALE 8192\r
-\r
-int main(void)\r
-{\r
- int i, len;\r
-\r
- printf("\tbits 32\n");\r
- printf("\tsection .rodata\n\n");\r
-\r
- printf("\tglobal _sintab\n");\r
- printf("_sintab:\n");\r
-\r
- fputs("\tdw", stdout);\r
- len = 10;\r
- for(i=0; i<SINTAB_SIZE; i++) {\r
- double theta = (double)i / (double)SINTAB_SIZE * 6.28318530718;\r
- double sf = sin(theta);\r
- int sx = (int)(sf * SINTAB_SCALE);\r
- len += printf(" %d", sx);\r
- if(len >= 72) {\r
- putchar('\n');\r
- if(i < SINTAB_SIZE - 1) {\r
- fputs("\tdw", stdout);\r
- len = 10;\r
- }\r
- } else {\r
- putchar(',');\r
- len++;\r
- }\r
- }\r
- printf("\n\n; vi:ft=nasm ts=8 sts=8 sw=8:\n");\r
- return 0;\r
-}\r
+#include <stdio.h>
+#include <math.h>
+
+#define SINTAB_SIZE 512
+#define SINTAB_SCALE 8192
+
+int main(void)
+{
+ int i, len;
+
+ printf("\tbits 32\n");
+ printf("\tsection .rodata\n\n");
+
+ printf("\tglobal _sintab\n");
+ printf("_sintab:\n");
+
+ fputs("\tdw", stdout);
+ len = 10;
+ for(i=0; i<SINTAB_SIZE; i++) {
+ double theta = (double)i / (double)SINTAB_SIZE * 6.28318530718;
+ double sf = sin(theta);
+ int sx = (int)(sf * SINTAB_SCALE);
+ len += printf(" %d", sx);
+ if(len >= 72) {
+ putchar('\n');
+ if(i < SINTAB_SIZE - 1) {
+ fputs("\tdw", stdout);
+ len = 10;
+ }
+ } else {
+ putchar(',');
+ len++;
+ }
+ }
+ printf("\n\n; vi:ft=nasm ts=8 sts=8 sw=8:\n");
+ return 0;
+}