pulled in and adapted the dos code from eradicate
authorJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 18 May 2021 03:06:23 +0000 (06:06 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 18 May 2021 03:06:23 +0000 (06:06 +0300)
17 files changed:
src/dos/cdpmi.h [new file with mode: 0644]
src/dos/djdpmi.c [new file with mode: 0644]
src/dos/gfx.c [new file with mode: 0644]
src/dos/gfx.h [new file with mode: 0644]
src/dos/logger.c [new file with mode: 0644]
src/dos/logger.h [new file with mode: 0644]
src/dos/main.c
src/dos/pit8254.h [new file with mode: 0644]
src/dos/timer.c [new file with mode: 0644]
src/dos/timer.h [new file with mode: 0644]
src/dos/vbe.c [new file with mode: 0644]
src/dos/vbe.h [new file with mode: 0644]
src/dos/vga.c [new file with mode: 0644]
src/dos/vga.h [new file with mode: 0644]
src/dos/watdpmi.c [new file with mode: 0644]
src/util.c
src/util.h

diff --git a/src/dos/cdpmi.h b/src/dos/cdpmi.h
new file mode 100644 (file)
index 0000000..91dbb67
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef DPMI_H_
+#define DPMI_H_
+
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <sys/nearptr.h>
+
+#define virt_to_phys(v)        ((v) + __djgpp_base_address)
+#define phys_to_virt(p)        ((p) - __djgpp_base_address)
+
+#else  /* not djgpp (basically watcom) */
+
+#define virt_to_phys(v)        (v)
+#define phys_to_virt(p)        (p)
+
+#endif /* __DJGPP__ */
+
+#include "util.h"
+
+#pragma pack (push, 1)
+struct dpmi_regs {
+       uint32_t edi, esi, ebp;
+       uint32_t reserved;
+       uint32_t ebx, edx, ecx, eax;
+       uint16_t flags;
+       uint16_t es, ds, fs, gs;
+       uint16_t ip, cs, sp, ss;
+} PACKED;
+#pragma pack (pop)
+
+uint16_t dpmi_alloc(unsigned int par, uint16_t *sel);
+void dpmi_free(uint16_t sel);
+void dpmi_int(int inum, struct dpmi_regs *regs);
+void *dpmi_mmap(uint32_t phys_addr, unsigned int size);
+void dpmi_munmap(void *addr);
+
+#ifdef __WATCOMC__
+#pragma aux dpmi_alloc = \
+               "mov ax, 0x100" \
+               "int 0x31" \
+               "mov [edi], dx" \
+               "jnc alloc_skip_err" \
+               "xor ax, ax" \
+               "alloc_skip_err:" \
+               value[ax] \
+               parm[ebx][edi] \
+               modify[dx];
+
+#pragma aux dpmi_free = \
+               "mov ax, 0x101" \
+               "int 0x31" \
+               parm[dx] \
+               modify[ax];
+
+#pragma aux dpmi_int = \
+               "mov ax, 0x300" \
+               "xor ecx, ecx" \
+               "int 0x31" \
+               parm[ebx][edi] \
+               modify[ax ecx];
+
+#pragma aux dpmi_mmap = \
+               "mov ax, 0x800" \
+               "mov cx, bx" \
+               "shr ebx, 16" \
+               "mov di, si" \
+               "shr esi, 16" \
+               "int 0x31" \
+               "jnc mmap_skip_err" \
+               "xor bx, bx" \
+               "xor cx, cx" \
+       "mmap_skip_err:" \
+               "mov ax, bx" \
+               "shl eax, 16" \
+               "mov ax, cx" \
+               value[eax] \
+               parm[ebx][esi] \
+               modify[bx cx di esi];
+
+#pragma aux dpmi_munmap = \
+               "mov ax, 0x801" \
+               "mov cx, bx" \
+               "shr ebx, 16" \
+               "int 0x31" \
+               parm[ebx] \
+               modify[ax cx ebx];
+#endif /* __WATCOMC__ */
+
+#ifdef __DJGPP__
+#define dpmi_int(inum, regs) __dpmi_int((inum), (__dpmi_regs*)(regs))
+#endif
+
+#endif /* DPMI_H_ */
diff --git a/src/dos/djdpmi.c b/src/dos/djdpmi.c
new file mode 100644 (file)
index 0000000..ef77a32
--- /dev/null
@@ -0,0 +1,35 @@
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <sys/nearptr.h>
+#include "cdpmi.h"
+#include "util.h"
+
+uint16_t dpmi_alloc(unsigned int par, uint16_t *sel)
+{
+       int tmp;
+       uint16_t seg = __dpmi_allocate_dos_memory(par, &tmp);
+       *sel = tmp;
+       return seg;
+}
+
+void dpmi_free(uint16_t sel)
+{
+       __dpmi_free_dos_memory(sel);
+}
+
+void *dpmi_mmap(uint32_t phys_addr, unsigned int size)
+{
+       __dpmi_meminfo mem;
+       mem.address = phys_addr;
+       mem.size = size;
+       __dpmi_physical_address_mapping(&mem);
+       return (void*)(mem.address - __djgpp_base_address);
+}
+
+void dpmi_munmap(void *addr)
+{
+       __dpmi_meminfo mem;
+       mem.address = (uint32_t)addr + __djgpp_base_address;
+       __dpmi_free_physical_address_mapping(&mem);
+}
+#endif /* __DJGPP__ */
diff --git a/src/dos/gfx.c b/src/dos/gfx.c
new file mode 100644 (file)
index 0000000..c8444aa
--- /dev/null
@@ -0,0 +1,307 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cdpmi.h"
+#include "gfx.h"
+#include "vbe.h"
+#include "vga.h"
+#include "util.h"
+
+#define SAME_BPP(a, b) \
+       ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
+        ((a) == 32 && (b) == 24) || ((a) == 24 && (b) == 32))
+
+void (*blit_frame)(void*, int);
+
+int resizefb(int x, int y, int bpp);
+
+static void blit_frame_lfb(void *pixels, int vsync);
+static void blit_frame_banked(void *pixels, int vsync);
+static uint32_t calc_mask(int sz, int pos);
+
+static struct video_mode *vmodes;
+static int num_vmodes;
+
+static int vbe_init_ver;
+static struct vbe_info vbe;
+
+/* current mode */
+static struct video_mode *curmode;
+static void *vpgaddr[2];
+static int frontidx, backidx;
+static int pgcount, pgsize, fbsize;
+
+
+int init_video(void)
+{
+       int i, num, max_modes;
+       struct video_mode *vmptr;
+
+       if(vbe_info(&vbe) == -1) {
+               fprintf(stderr, "failed to retrieve VBE information\n");
+               return -1;
+       }
+       vbe_print_info(stdout, &vbe);
+
+       num_vmodes = 0;
+       max_modes = 64;
+       if(!(vmodes = malloc(max_modes * sizeof *vmodes))) {
+               fprintf(stderr, "failed to allocate video modes list\n");
+               return -1;
+       }
+
+       num = vbe_num_modes(&vbe);
+       for(i=0; i<num; i++) {
+               struct vbe_mode_info minf;
+
+               if(vbe_mode_info(vbe.modes[i], &minf) == -1) {
+                       continue;
+               }
+
+               if(num_vmodes >= max_modes) {
+                       int newmax = max_modes ? (max_modes << 1) : 16;
+                       if(!(vmptr = realloc(vmodes, newmax * sizeof *vmodes))) {
+                               fprintf(stderr, "failed to grow video mode list (%d)\n", newmax);
+                               free(vmodes);
+                               return -1;
+                       }
+                       vmodes = vmptr;
+                       max_modes = newmax;
+               }
+
+               vmptr = vmodes + num_vmodes++;
+               memset(vmptr, 0, sizeof *vmptr);
+               vmptr->mode = vbe.modes[i];
+               vmptr->xsz = minf.xres;
+               vmptr->ysz = minf.yres;
+               vmptr->bpp = minf.bpp;
+               vmptr->pitch = minf.scanline_bytes;
+               if(minf.mem_model == VBE_TYPE_DIRECT) {
+                       vmptr->rbits = minf.rsize;
+                       vmptr->gbits = minf.gsize;
+                       vmptr->bbits = minf.bsize;
+                       vmptr->rshift = minf.rpos;
+                       vmptr->gshift = minf.gpos;
+                       vmptr->bshift = minf.bpos;
+                       vmptr->rmask = calc_mask(minf.rsize, minf.rpos);
+                       vmptr->gmask = calc_mask(minf.gsize, minf.gpos);
+                       vmptr->bmask = calc_mask(minf.bsize, minf.bpos);
+                       vmptr->bpp = vmptr->rbits + vmptr->gbits + vmptr->bbits;
+               }
+               if(minf.attr & VBE_ATTR_LFB) {
+                       vmptr->fb_addr = minf.fb_addr;
+               } else {
+                       vmptr->bank_size = (uint32_t)minf.bank_size * 1024;
+                       if(!vmptr->bank_size) {
+                               vmptr->bank_size = 65536;
+                       }
+               }
+               vmptr->max_pages = minf.num_img_pages;
+
+               printf("%04x: ", vbe.modes[i]);
+               vbe_print_mode_info(stdout, &minf);
+       }
+       fflush(stdout);
+
+       vbe_init_ver = VBE_VER_MAJOR(vbe.ver);
+       return 0;
+}
+
+void cleanup_video(void)
+{
+       free(vmodes);
+}
+
+struct video_mode *video_modes(void)
+{
+       return vmodes;
+}
+
+int num_video_modes(void)
+{
+       return num_vmodes;
+}
+
+struct video_mode *get_video_mode(int idx)
+{
+       if(idx == VMODE_CURRENT) {
+               return curmode;
+       }
+       return vmodes + idx;
+}
+
+int match_video_mode(int xsz, int ysz, int bpp)
+{
+       int i, best = -1;
+       struct video_mode *vm;
+
+       for(i=0; i<num_vmodes; i++) {
+               vm = vmodes + i;
+               if(vm->xsz != xsz || vm->ysz != ysz) continue;
+               if(SAME_BPP(vm->bpp, bpp)) {
+                       best = i;
+               }
+               if(vm->bpp == bpp) break;
+       }
+
+       if(best == -1) {
+               fprintf(stderr, "failed to find video mode %dx%d %d bpp)\n", xsz, ysz, bpp);
+               return -1;
+       }
+       return best;
+}
+
+int find_video_mode(int mode)
+{
+       int i;
+       struct video_mode *vm;
+
+       vm = vmodes;
+       for(i=0; i<num_vmodes; i++) {
+               if(vm->mode == mode) return i;
+       }
+       return -1;
+}
+
+void *set_video_mode(int idx, int nbuf)
+{
+       unsigned int mode;
+       struct video_mode *vm = vmodes + idx;
+
+       if(curmode == vm) return vpgaddr[0];
+
+       printf("setting video mode %x (%dx%d %d bpp)\n", (unsigned int)vm->mode,
+                       vm->xsz, vm->ysz, vm->bpp);
+       fflush(stdout);
+
+       mode = vm->mode | VBE_MODE_LFB;
+       if(vbe_setmode(mode) == -1) {
+               mode = vm->mode;
+               if(vbe_setmode(mode) == -1) {
+                       fprintf(stderr, "failed to set video mode %x\n", (unsigned int)vm->mode);
+                       return 0;
+               }
+               printf("Warning: failed to get a linear framebuffer. falling back to banked mode\n");
+       }
+
+       /* unmap previous video memory mapping, if there was one (switching modes) */
+       if(vpgaddr[0] && vpgaddr[0] != (void*)0xa0000) {
+               dpmi_munmap(vpgaddr[0]);
+               vpgaddr[0] = vpgaddr[1] = 0;
+       }
+
+       curmode = vm;
+       if(nbuf < 1) nbuf = 1;
+       if(nbuf > 2) nbuf = 2;
+       pgcount = nbuf > vm->max_pages ? vm->max_pages : nbuf;
+       pgsize = vm->ysz * vm->pitch;
+       fbsize = pgcount * pgsize;
+
+       printf("pgcount: %d, pgsize: %d, fbsize: %d\n", pgcount, pgsize, fbsize);
+       printf("phys addr: %p\n", (void*)vm->fb_addr);
+       fflush(stdout);
+
+       if(vm->fb_addr) {
+               vpgaddr[0] = (void*)dpmi_mmap(vm->fb_addr, fbsize);
+               if(!vpgaddr[0]) {
+                       fprintf(stderr, "failed to map framebuffer (phys: %lx, size: %d)\n",
+                                       (unsigned long)vm->fb_addr, fbsize);
+                       set_text_mode();
+                       return 0;
+               }
+               memset(vpgaddr[0], 0xaa, pgsize);
+
+               if(pgcount > 1) {
+                       vpgaddr[1] = (char*)vpgaddr[0] + pgsize;
+                       backidx = 1;
+                       page_flip(FLIP_NOW);    /* start with the second page visible */
+               } else {
+                       frontidx = backidx = 0;
+                       vpgaddr[1] = 0;
+               }
+
+               blit_frame = blit_frame_lfb;
+
+       } else {
+               vpgaddr[0] = (void*)0xa0000;
+               vpgaddr[1] = 0;
+
+               blit_frame = blit_frame_banked;
+       }
+
+       /* allocate main memory framebuffer */
+       if(resizefb(vm->xsz, vm->ysz, vm->bpp) == -1) {
+               fprintf(stderr, "failed to allocate %dx%d (%d bpp) framebuffer\n", vm->xsz,
+                               vm->ysz, vm->bpp);
+               set_text_mode();
+               return 0;
+       }
+
+       return vpgaddr[0];
+}
+
+int set_text_mode(void)
+{
+       /* unmap previous video memory mapping, if there was one (switching modes) */
+       if(vpgaddr[0] && vpgaddr[0] != (void*)0xa0000) {
+               dpmi_munmap(vpgaddr[0]);
+               vpgaddr[0] = vpgaddr[1] = 0;
+       }
+
+       vga_setmode(3);
+       curmode = 0;
+       return 0;
+}
+
+void *page_flip(int vsync)
+{
+       if(!vpgaddr[1]) {
+               /* page flipping not supported */
+               return vpgaddr[0];
+       }
+
+       vbe_swap(backidx ? pgsize : 0, vsync ? VBE_SWAP_VBLANK : VBE_SWAP_NOW);
+       frontidx = backidx;
+       backidx = (backidx + 1) & 1;
+
+       return vpgaddr[backidx];
+}
+
+
+static void blit_frame_lfb(void *pixels, int vsync)
+{
+       if(vsync) wait_vsync();
+       memcpy64(vpgaddr[frontidx], pixels, pgsize >> 3);
+}
+
+static void blit_frame_banked(void *pixels, int vsync)
+{
+       int sz, offs;
+       unsigned int pending;
+       unsigned char *pptr = pixels;
+
+       if(vsync) wait_vsync();
+
+       /* assume initial window offset at 0 */
+       offs = 0;
+       pending = pgsize;
+       while(pending > 0) {
+               sz = pending > curmode->bank_size ? curmode->bank_size : pending;
+               //memcpy64((void*)0xa0000, pptr, sz >> 3);
+               memcpy((void*)0xa0000, pptr, sz);
+               pptr += sz;
+               pending -= sz;
+               vbe_setwin(0, ++offs);
+       }
+
+       vbe_setwin(0, 0);
+}
+
+static uint32_t calc_mask(int sz, int pos)
+{
+       uint32_t mask = 0;
+       while(sz-- > 0) {
+               mask = (mask << 1) | 1;
+       }
+       return mask << pos;
+}
diff --git a/src/dos/gfx.h b/src/dos/gfx.h
new file mode 100644 (file)
index 0000000..2f33db4
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef GFX_H_
+#define GFX_H_
+
+#include "util.h"
+
+struct video_mode {
+       uint16_t mode;
+       short xsz, ysz, bpp, pitch;
+       short rbits, gbits, bbits;
+       short rshift, gshift, bshift;
+       uint32_t rmask, gmask, bmask;
+       uint32_t fb_addr;
+       short max_pages;
+       uint32_t bank_size;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int init_video(void);
+void cleanup_video(void);
+
+struct video_mode *video_modes(void);
+int num_video_modes(void);
+
+#define VMODE_CURRENT  (-1)
+struct video_mode *get_video_mode(int idx);
+
+int match_video_mode(int xsz, int ysz, int bpp);
+int find_video_mode(int mode);
+
+/* argument is the mode list index [0, nmodes-1] */
+void *set_video_mode(int idx, int nbuf);
+int set_text_mode(void);
+
+void set_palette(int idx, int r, int g, int b);
+
+enum {
+       FLIP_NOW,
+       FLIP_VBLANK
+};
+/* page flip and return pointer to the start of the display area (front buffer) */
+void *page_flip(int vsync);
+extern void (*blit_frame)(void *pixels, int vsync);
+
+#ifdef __WATCOMC__
+void wait_vsync(void);
+#pragma aux wait_vsync = \
+       "mov dx, 0x3da" \
+       "l1:" \
+       "in al, dx" \
+       "and al, 0x8" \
+       "jnz l1" \
+       "l2:" \
+       "in al, dx" \
+       "and al, 0x8" \
+       "jz l2" \
+       modify[al dx];
+#endif
+
+#ifdef __DJGPP__
+#define wait_vsync()  asm volatile ( \
+       "mov $0x3da, %%dx\n\t" \
+       "0:\n\t" \
+       "in %%dx, %%al\n\t" \
+       "and $8, %%al\n\t" \
+       "jnz 0b\n\t" \
+       "0:\n\t" \
+       "in %%dx, %%al\n\t" \
+       "and $8, %%al\n\t" \
+       "jz 0b\n\t" \
+       :::"%eax","%edx")
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_H_ */
diff --git a/src/dos/logger.c b/src/dos/logger.c
new file mode 100644 (file)
index 0000000..70489ad
--- /dev/null
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "logger.h"
+
+int init_logger(const char *fname)
+{
+       int fd;
+       if((fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
+               fprintf(stderr, "init_logger: failed to open %s: %s\n", fname, strerror(errno));
+               return -1;
+       }
+
+       close(1);
+       close(2);
+       dup(fd);
+       dup(fd);
+       return 0;
+}
diff --git a/src/dos/logger.h b/src/dos/logger.h
new file mode 100644 (file)
index 0000000..9ef9a85
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int init_logger(const char *fname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LOGGER_H_ */
index 5a1a13a..761dd56 100644 (file)
@@ -1,22 +1,95 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <conio.h>
 #include "rbench.h"
+#include "timer.h"
+#include "gfx.h"
+#include "logger.h"
+#include "cdpmi.h"
 
 static int parse_args(int argc, char **argv);
 
+static struct video_mode *vidmode;
+
 int main(int argc, char **argv)
 {
+       int vmidx;
+       int num_frames = 0;
+       void *vmem;
+
        read_config("rbench.cfg");
 
        if(parse_args(argc, argv) == -1) {
                return 1;
        }
 
-       printf("foo %dx%d %dbpp\n", opt.width, opt.height, opt.bpp);
+#ifdef __DJGPP__
+       __djgpp_nearptr_enable();
+#endif
+
+       init_logger("rbench.log");
+
+       if(init_video() == -1) {
+               return 1;
+       }
+
+       if((vmidx = match_video_mode(opt.width, opt.height, opt.bpp)) == -1) {
+               return 1;
+       }
+       if(!(vmem = set_video_mode(vmidx, 1))) {
+               return 1;
+       }
+       vidmode = get_video_mode(vmidx);
+
+       fb_rmask = vidmode->rmask;
+       fb_gmask = vidmode->gmask;
+       fb_bmask = vidmode->bmask;
+       fb_rshift = vidmode->rshift;
+       fb_gshift = vidmode->gshift;
+       fb_bshift = vidmode->bshift;
+
+       init_timer(100);
+
+       for(;;) {
+               while(kbhit()) {
+                       int c = getch();
+                       if(c == 27) goto end;
+                       key_event(c, 1);
+               }
+
+               time_msec = get_msec();
+               num_frames++;
+               redraw();
+
+               blit_frame(framebuf, 0);
+       }
+
+end:
+       set_text_mode();
+       cleanup_video();
 
+       if(num_frames) {
+               printf("avg framerate: %.1f fps\n", (10000 * num_frames / time_msec) / 10.0f);
+       }
        return 0;
 }
 
+int resizefb(int x, int y, int bpp)
+{
+       free(framebuf);
+
+       fb_width = x;
+       fb_height = y;
+       fb_bpp = bpp;
+       fb_pitch = x * bpp / 8;
+
+       if(!(framebuf = malloc(fb_pitch * fb_height))) {
+               fprintf(stderr, "failed to allocate %dx%d (%dbpp) framebuffer\n",
+                               fb_width, fb_height, fb_bpp);
+               return -1;
+       }
+       return 0;
+}
 
 static const char *usage_str =
        "Usage: %s [options]\n"
diff --git a/src/dos/pit8254.h b/src/dos/pit8254.h
new file mode 100644 (file)
index 0000000..4389883
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+colcycle - color cycling image viewer
+Copyright (C) 2016  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PIT8254_H_
+#define PIT8254_H_
+
+/* frequency of the oscillator driving the 8254 timer */
+#define OSC_FREQ_HZ            1193182
+
+/* I/O ports connected to the 8254 */
+#define PORT_DATA0     0x40
+#define PORT_DATA1     0x41
+#define PORT_DATA2     0x42
+#define PORT_CMD       0x43
+
+/* command bits */
+#define CMD_CHAN0                      0
+#define CMD_CHAN1                      (1 << 6)
+#define CMD_CHAN2                      (2 << 6)
+#define CMD_RDBACK                     (3 << 6)
+
+#define CMD_LATCH                      0
+#define CMD_ACCESS_LOW         (1 << 4)
+#define CMD_ACCESS_HIGH                (2 << 4)
+#define CMD_ACCESS_BOTH                (3 << 4)
+
+#define CMD_OP_INT_TERM                0
+#define CMD_OP_ONESHOT         (1 << 1)
+#define CMD_OP_RATE                    (2 << 1)
+#define CMD_OP_SQWAVE          (3 << 1)
+#define CMD_OP_SW_STROBE       (4 << 1)
+#define CMD_OP_HW_STROBE       (5 << 1)
+
+#define CMD_MODE_BIN           0
+#define CMD_MODE_BCD           1
+
+#endif /* PIT8254_H_ */
diff --git a/src/dos/timer.c b/src/dos/timer.c
new file mode 100644 (file)
index 0000000..0dc4447
--- /dev/null
@@ -0,0 +1,175 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <conio.h>
+#include <dos.h>
+
+#ifdef __WATCOMC__
+#include <i86.h>
+#endif
+
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <go32.h>
+#include <pc.h>
+#endif
+
+#include "pit8254.h"
+#include "util.h"
+
+#define PIT_TIMER_INTR 8
+#define DOS_TIMER_INTR 0x1c
+
+/* macro to divide and round to the nearest integer */
+#define DIV_ROUND(a, b) \
+       ((a) / (b) + ((a) % (b)) / ((b) / 2))
+
+static void set_timer_reload(int reload_val);
+static void cleanup(void);
+
+#ifdef __WATCOMC__
+#define INTERRUPT      __interrupt __far
+
+static void INTERRUPT dos_timer_intr();
+
+static void (INTERRUPT *prev_timer_intr)();
+#endif
+
+#ifdef __DJGPP__
+#define INTERRUPT
+
+static _go32_dpmi_seginfo intr, prev_intr;
+
+#define outp(p, v)     outportb(p, v)
+#endif
+
+static void INTERRUPT timer_irq();
+
+static volatile unsigned long ticks;
+static unsigned long tick_interval, ticks_per_dos_intr;
+static int inum;
+
+void init_timer(int res_hz)
+{
+       _disable();
+
+       if(res_hz > 0) {
+               int reload_val = DIV_ROUND(OSC_FREQ_HZ, res_hz);
+               set_timer_reload(reload_val);
+
+               tick_interval = DIV_ROUND(1000, res_hz);
+               ticks_per_dos_intr = DIV_ROUND(65535L, reload_val);
+
+               inum = PIT_TIMER_INTR;
+#ifdef __WATCOMC__
+               prev_timer_intr = _dos_getvect(inum);
+               _dos_setvect(inum, timer_irq);
+#endif
+#ifdef __DJGPP__
+               _go32_dpmi_get_protected_mode_interrupt_vector(inum, &prev_intr);
+               intr.pm_offset = (intptr_t)timer_irq;
+               intr.pm_selector = _go32_my_cs();
+               _go32_dpmi_allocate_iret_wrapper(&intr);
+               _go32_dpmi_set_protected_mode_interrupt_vector(inum, &intr);
+#endif
+       } else {
+               tick_interval = 55;
+
+               inum = DOS_TIMER_INTR;
+#ifdef __WATCOMC__
+               prev_timer_intr = _dos_getvect(inum);
+               _dos_setvect(inum, dos_timer_intr);
+#endif
+#ifdef __DJGPP__
+               assert(0);
+#endif
+       }
+       _enable();
+
+       atexit(cleanup);
+}
+
+static void cleanup(void)
+{
+       if(!inum) {
+               return; /* init hasn't ran, there's nothing to cleanup */
+       }
+
+       _disable();
+       if(inum == PIT_TIMER_INTR) {
+               /* restore the original timer frequency */
+               set_timer_reload(65535);
+       }
+
+       /* restore the original interrupt handler */
+#ifdef __WATCOMC__
+       _dos_setvect(inum, prev_timer_intr);
+#endif
+#ifdef __DJGPP__
+       _go32_dpmi_set_protected_mode_interrupt_vector(inum, &prev_intr);
+       _go32_dpmi_free_iret_wrapper(&intr);
+#endif
+
+       _enable();
+}
+
+void reset_timer(void)
+{
+       ticks = 0;
+}
+
+unsigned long get_msec(void)
+{
+       return ticks * tick_interval;
+}
+
+void sleep_msec(unsigned long msec)
+{
+       unsigned long wakeup_time = ticks + msec / tick_interval;
+       while(ticks < wakeup_time) {
+#ifdef USE_HLT
+               halt();
+#endif
+       }
+}
+
+static void set_timer_reload(int reload_val)
+{
+       outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
+       outp(PORT_DATA0, reload_val & 0xff);
+       outp(PORT_DATA0, (reload_val >> 8) & 0xff);
+}
+
+#ifdef __WATCOMC__
+static void INTERRUPT dos_timer_intr()
+{
+       ticks++;
+       _chain_intr(prev_timer_intr);   /* DOES NOT RETURN */
+}
+#endif
+
+/* first PIC command port */
+#define PIC1_CMD       0x20
+/* end of interrupt control word */
+#define OCW2_EOI       (1 << 5)
+
+static void INTERRUPT timer_irq()
+{
+       static unsigned long dos_ticks;
+
+       ticks++;
+
+#ifdef __WATCOMC__
+       if(++dos_ticks >= ticks_per_dos_intr) {
+               /* I suppose the dos irq handler does the EOI so I shouldn't
+                * do it if I am to call the previous function
+                */
+               dos_ticks = 0;
+               _chain_intr(prev_timer_intr);   /* XXX DOES NOT RETURN */
+               return; /* just for clarity */
+       }
+#endif
+
+       /* send EOI to the PIC */
+       outp(PIC1_CMD, OCW2_EOI);
+}
diff --git a/src/dos/timer.h b/src/dos/timer.h
new file mode 100644 (file)
index 0000000..1fa83c3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef TIMER_H_
+#define TIMER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* expects the required timer resolution in hertz
+ * if res_hz is 0, the current resolution is retained
+ */
+void init_timer(int res_hz);
+
+void reset_timer(void);
+unsigned long get_msec(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TIMER_H_ */
diff --git a/src/dos/vbe.c b/src/dos/vbe.c
new file mode 100644 (file)
index 0000000..05a5883
--- /dev/null
@@ -0,0 +1,525 @@
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <assert.h>
+#include "vbe.h"
+#include "cdpmi.h"
+
+
+#define FIXPTR(ptr) \
+       do { \
+               uint32_t paddr = (uint32_t)(ptr); \
+               uint16_t pseg = paddr >> 16; \
+               uint16_t poffs = paddr & 0xffff; \
+               if(pseg == seg && poffs < 512) { \
+                       paddr = ((uint32_t)seg << 4) + poffs; \
+               } else { \
+                       paddr = ((uint32_t)pseg << 4) + poffs; \
+               } \
+               (ptr) = (void*)phys_to_virt(paddr); \
+       } while(0)
+
+/* hijack the "VESA" sig field, to pre-cache number of modes */
+#define NMODES(inf) *(uint16_t*)((inf)->sig)
+#define NACCMODES(inf) *(uint16_t*)((inf)->sig + 2)
+
+static int cur_pitch;
+/* TODO update cur_pitch on mode-change and on setscanlen */
+
+
+int vbe_info(struct vbe_info *info)
+{
+       void *lowbuf;
+       uint16_t seg, sel;
+       uint16_t *modeptr;
+       uint32_t offs;
+       struct dpmi_regs regs = {0};
+
+       assert(sizeof *info == 512);
+
+       if(!(seg = dpmi_alloc(sizeof *info / 16, &sel))) {
+               return -1;
+       }
+       lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
+
+       memcpy(lowbuf, "VBE2", 4);
+
+       regs.eax = 0x4f00;
+       regs.es = seg;
+       regs.edi = 0;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               fprintf(stderr, "vbe_get_info (4f00) failed\n");
+               dpmi_free(sel);
+               return -1;
+       }
+
+       memcpy(info, lowbuf, sizeof *info);
+       dpmi_free(sel);
+
+       FIXPTR(info->oem_name);
+       FIXPTR(info->vendor);
+       FIXPTR(info->product);
+       FIXPTR(info->revstr);
+       FIXPTR(info->modes);
+       /* implementations without the accel capability, will use the space taken
+        * by the accel_modes pointer for other data (probably video modes). We
+        * need to check for the capability before "fixing" this pointer, otherwise
+        * we'll shuffle random data.
+        */
+       if(info->caps & VBE_ACCEL) {
+               FIXPTR(info->accel_modes);
+       }
+
+       /* info->modes should be pointing somewhere at the end of the original
+        * low memory buffer. make it point at the same offset in the info
+        * buffer where we copied everything instead.
+        */
+       offs = (char*)info->modes - (char*)lowbuf;
+       if(offs < sizeof *info) {       /* this should always be true */
+               info->modes = (uint16_t*)((char*)info + offs);
+       }
+
+       modeptr = info->modes;
+       while(*modeptr != 0xffff) {
+               if(modeptr - info->modes >= 256) {
+                       modeptr = info->modes;
+                       break;
+               }
+               modeptr++;
+       }
+       NMODES(info) = modeptr - info->modes;
+
+       if(info->caps & VBE_ACCEL) {
+               modeptr = info->accel_modes;
+               while(*modeptr != 0xffff) {
+                       if(modeptr - info->accel_modes >= 256) {
+                               modeptr = info->accel_modes;
+                               break;
+                       }
+                       modeptr++;
+               }
+               NACCMODES(info) = modeptr - info->accel_modes;
+       }
+       return 0;
+}
+
+int vbe_num_modes(struct vbe_info *info)
+{
+       return NMODES(info);
+}
+
+int vbe_mode_info(int mode, struct vbe_mode_info *minf)
+{
+       void *lowbuf;
+       uint16_t seg, sel;
+       struct dpmi_regs regs = {0};
+
+       assert(sizeof *minf == 256);
+       assert(offsetof(struct vbe_mode_info, max_pixel_clock) == 0x3e);
+
+       if(!(seg = dpmi_alloc(sizeof *minf / 16, &sel))) {
+               return -1;
+       }
+       lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
+
+       regs.eax = 0x4f01;
+       regs.ecx = mode;
+       regs.es = seg;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               fprintf(stderr, "vbe_mode_info (4f01) failed\n");
+               dpmi_free(sel);
+               return -1;
+       }
+
+       memcpy(minf, lowbuf, sizeof *minf);
+       dpmi_free(sel);
+       return 0;
+}
+
+void vbe_print_info(FILE *fp, struct vbe_info *vinf)
+{
+       fprintf(fp, "vbe version: %u.%u\n", VBE_VER_MAJOR(vinf->ver), VBE_VER_MINOR(vinf->ver));
+       if(VBE_VER_MAJOR(vinf->ver) >= 2) {
+               fprintf(fp, "%s - %s (%s)\n", vinf->vendor, vinf->product, vinf->revstr);
+               if(vinf->caps & VBE_ACCEL) {
+                       fprintf(fp, "vbe/af %d.%d\n", VBE_VER_MAJOR(vinf->accel_ver), VBE_VER_MINOR(vinf->accel_ver));
+               }
+       } else {
+               fprintf(fp, "oem: %s\n", vinf->oem_name);
+       }
+       fprintf(fp, "video memory: %dkb\n", vinf->vmem_blk * 64);
+
+       if(vinf->caps) {
+               fprintf(fp, "caps:");
+               if(vinf->caps & VBE_8BIT_DAC) fprintf(fp, " dac8");
+               if(vinf->caps & VBE_NON_VGA) fprintf(fp, " non-vga");
+               if(vinf->caps & VBE_DAC_BLANK) fprintf(fp, " dac-blank");
+               if(vinf->caps & VBE_ACCEL) fprintf(fp, " af");
+               if(vinf->caps & VBE_MUSTLOCK) fprintf(fp, " af-lock");
+               if(vinf->caps & VBE_HWCURSOR) fprintf(fp, " af-curs");
+               if(vinf->caps & VBE_HWCLIP) fprintf(fp, " af-clip");
+               if(vinf->caps & VBE_TRANSP_BLT) fprintf(fp, " af-tblt");
+               fprintf(fp, "\n");
+       }
+
+       fprintf(fp, "%d video modes available\n", NMODES(vinf));
+       if(vinf->caps & VBE_ACCEL) {
+               fprintf(fp, "%d accelerated (VBE/AF) modes available\n", NACCMODES(vinf));
+       }
+       fflush(fp);
+}
+
+void vbe_print_mode_info(FILE *fp, struct vbe_mode_info *minf)
+{
+       fprintf(fp, "%dx%d %dbpp", minf->xres, minf->yres, minf->bpp);
+
+       switch(minf->mem_model) {
+       case VBE_TYPE_DIRECT:
+               fprintf(fp, " (rgb");
+               if(0) {
+       case VBE_TYPE_YUV:
+                       fprintf(fp, " (yuv");
+               }
+               fprintf(fp, " %d%d%d)", minf->rsize, minf->gsize, minf->bsize);
+               break;
+       case VBE_TYPE_PLANAR:
+               fprintf(fp, " (%d planes)", minf->num_planes);
+               break;
+       case VBE_TYPE_PACKED:
+               fprintf(fp, " (packed)");
+               break;
+       case VBE_TYPE_TEXT:
+               fprintf(fp, " (%dx%d cells)", minf->xcharsz, minf->ycharsz);
+               break;
+       case VBE_TYPE_CGA:
+               fprintf(fp, " (CGA)");
+               break;
+       case VBE_TYPE_UNCHAIN:
+               fprintf(fp, " (unchained-%d)", minf->num_planes);
+               break;
+       }
+       fprintf(fp, " %dpg", minf->num_img_pages);
+
+       if(minf->attr & VBE_ATTR_LFB) {
+               fprintf(fp, " lfb@%lx", (unsigned long)minf->fb_addr);
+       } else {
+               fprintf(fp, " %xkb/bank", (unsigned int)minf->bank_size);
+       }
+
+       fprintf(fp, " [");
+       if(minf->attr & VBE_ATTR_AVAIL) fprintf(fp, " avail");
+       if(minf->attr & VBE_ATTR_OPTINFO) fprintf(fp, " opt");
+       if(minf->attr & VBE_ATTR_TTY) fprintf(fp, " tty");
+       if(minf->attr & VBE_ATTR_COLOR) fprintf(fp, " color");
+       if(minf->attr & VBE_ATTR_GFX) fprintf(fp, " gfx");
+       if(minf->attr & VBE_ATTR_NOTVGA) fprintf(fp, " non-vga");
+       if(minf->attr & VBE_ATTR_BANKED) fprintf(fp, " banked");
+       if(minf->attr & VBE_ATTR_LFB) fprintf(fp, " lfb");
+       if(minf->attr & VBE_ATTR_DBLSCAN) fprintf(fp, " dblscan");
+       if(minf->attr & VBE_ATTR_ILACE) fprintf(fp, " ilace");
+       if(minf->attr & VBE_ATTR_TRIPLEBUF) fprintf(fp, " trplbuf");
+       if(minf->attr & VBE_ATTR_STEREO) fprintf(fp, " stereo");
+       if(minf->attr & VBE_ATTR_STEREO_2FB) fprintf(fp, " stdual");
+       fprintf(fp, " ]\n");
+       fflush(fp);
+}
+
+int vbe_setmode(uint16_t mode)
+{
+       struct dpmi_regs regs = {0};
+
+       regs.eax = 0x4f02;
+       regs.ebx = mode;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+
+       cur_pitch = vbe_getpitch();
+       return 0;
+}
+
+int vbe_setmode_crtc(uint16_t mode, struct vbe_crtc_info *crtc)
+{
+       void *lowbuf;
+       uint16_t seg, sel;
+       struct dpmi_regs regs = {0};
+
+       assert(sizeof *crtc == 59);
+
+       if(!(seg = dpmi_alloc((sizeof *crtc + 15) / 16, &sel))) {
+               return -1;
+       }
+       lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
+
+       memcpy(lowbuf, crtc, sizeof *crtc);
+
+       regs.eax = 0x4f02;
+       regs.ebx = mode;
+       regs.es = seg;
+       dpmi_int(0x10, &regs);
+
+       dpmi_free(sel);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+
+       cur_pitch = vbe_getpitch();
+       return 0;
+}
+
+int vbe_getmode(void)
+{
+       struct dpmi_regs regs = {0};
+
+       regs.eax = 0x4f03;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return regs.ebx & 0xffff;
+}
+
+int vbe_state_size(unsigned int flags)
+{
+       struct dpmi_regs regs = {0};
+
+       regs.eax = 0x4f04;
+       regs.edx = 0;
+       regs.ecx = flags;
+       dpmi_int(0x10, &regs);
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return (regs.ebx & 0xffff) * 64;
+}
+
+int vbe_save(void *stbuf, int sz, unsigned int flags)
+{
+       void *lowbuf;
+       uint16_t seg, sel;
+       struct dpmi_regs regs = {0};
+
+       if(!(seg = dpmi_alloc((sz + 15) / 16, &sel))) {
+               return -1;
+       }
+       lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
+
+       regs.eax = 0x4f04;
+       regs.edx = 1;   /* save */
+       regs.ecx = flags;
+       regs.es = seg;
+       dpmi_int(0x10, &regs);
+       if((regs.eax & 0xffff) != 0x4f) {
+               dpmi_free(sel);
+               return -1;
+       }
+
+       memcpy(stbuf, lowbuf, sz);
+       dpmi_free(sel);
+       return 0;
+}
+
+int vbe_restore(void *stbuf, int sz, unsigned int flags)
+{
+       void *lowbuf;
+       uint16_t seg, sel;
+       struct dpmi_regs regs = {0};
+
+       if(!(seg = dpmi_alloc((sz + 15) / 16, &sel))) {
+               return -1;
+       }
+       lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
+
+       memcpy(lowbuf, stbuf, sz);
+
+       regs.eax = 0x4f04;
+       regs.edx = 2;   /* restore */
+       regs.ecx = flags;
+       regs.es = seg;
+       dpmi_int(0x10, &regs);
+       dpmi_free(sel);
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return 0;
+}
+
+int vbe_setwin(int wid, int pos)
+{
+       struct dpmi_regs regs;
+
+       if(wid & ~1) return -1;
+
+       regs.eax = 0x4f05;
+       regs.ebx = wid;
+       regs.edx = pos;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return 0;
+}
+
+int vbe_getwin(int wid)
+{
+       struct dpmi_regs regs;
+
+       if(wid & ~1) return -1;
+
+       regs.eax = 0x4f05;
+       regs.ebx = wid;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+
+       return regs.edx & 0xffff;
+}
+
+int vbe_setscanlen(int len_pix)
+{
+       struct dpmi_regs regs;
+
+       regs.eax = 0x4f06;
+       regs.ebx = 0;   /* set scanline length in pixels */
+       regs.ecx = len_pix;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+
+       cur_pitch = vbe_getpitch();
+       return regs.ecx;
+}
+
+int vbe_getscanlen(void)
+{
+       struct dpmi_regs regs;
+
+       regs.eax = 0x4f06;
+       regs.ebx = 1;   /* get scanline length */
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return regs.ecx;
+}
+
+int vbe_getpitch(void)
+{
+       struct dpmi_regs regs;
+
+       regs.eax = 0x4f06;
+       regs.ebx = 1;   /* get scanline length */
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return regs.ebx;
+}
+
+int vbe_scanline_info(struct vbe_scanline_info *sinf)
+{
+       struct dpmi_regs regs;
+
+       regs.eax = 0x4f06;
+       regs.ebx = 1;   /* get scanline length */
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+
+       sinf->size = regs.ebx & 0xffff;
+       sinf->num_pixels = regs.ecx & 0xffff;
+       sinf->max_scanlines = regs.edx & 0xffff;
+       return 0;
+}
+
+enum {
+       SDISP_SET                       = 0x00,
+       SDISP_GET                       = 0x01,
+       SDISP_ALTSET            = 0x02,
+       SDISP_SET_STEREO        = 0x03,
+       SDISP_GETSCHED          = 0x04,
+       SDISP_STEREO_ON         = 0x05,
+       SDISP_STEREO_OFF        = 0x06,
+       SDISP_SET_VBLANK        = 0x80,
+       SDISP_ALTSET_VBLANK     = 0x82,
+       SDISP_SET_STEREO_VBLANK = 0x83
+};
+
+int vbe_setdisp(int x, int y, int when)
+{
+       struct dpmi_regs regs;
+
+       regs.eax = 0x4f07;
+       regs.ebx = (when == VBE_SWAP_VBLANK) ? SDISP_SET_VBLANK : SDISP_SET;
+       regs.ecx = x;
+       regs.edx = y;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return 0;
+}
+
+int vbe_swap(uint32_t voffs, int when)
+{
+       struct dpmi_regs regs;
+       int op;
+
+       switch(when) {
+       case VBE_SWAP_ASYNC:
+               op = SDISP_ALTSET;
+               break;
+
+       case VBE_SWAP_NOW:
+               /* XXX is this the only way? */
+               return vbe_setdisp(voffs % cur_pitch, voffs / cur_pitch, when);
+
+       case VBE_SWAP_VBLANK:
+       default:
+               op = SDISP_ALTSET_VBLANK;
+               break;
+       }
+
+
+       regs.eax = 0x4f07;
+       regs.ebx = op;
+       regs.ecx = voffs;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return 0;
+}
+
+int vbe_swap_pending(void)
+{
+       struct dpmi_regs regs;
+
+       regs.eax = 0x4f07;
+       regs.ebx = SDISP_GETSCHED;
+       dpmi_int(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               return 0;
+       }
+       return regs.ecx;
+}
diff --git a/src/dos/vbe.h b/src/dos/vbe.h
new file mode 100644 (file)
index 0000000..6e15b6a
--- /dev/null
@@ -0,0 +1,241 @@
+#ifndef VBE_H_
+#define VBE_H_
+
+#include <stdio.h>
+#include "util.h"
+
+#pragma pack (push, 1)
+struct vbe_info {
+       char sig[4];
+       uint16_t ver;
+       char *oem_name;
+       uint32_t caps;
+       uint16_t *modes;
+       uint16_t vmem_blk;      /* video memory size in 64k blocks */
+       uint16_t oem_ver;
+       char *vendor;
+       char *product;
+       char *revstr;
+       uint16_t accel_ver;
+       uint16_t *accel_modes;
+       char reserved[216];
+       char oem_data[256];
+} PACKED;
+
+struct vbe_mode_info {
+       uint16_t attr;
+       uint8_t wina_attr, winb_attr;
+       uint16_t win_gran, win_size;
+       uint16_t wina_seg, winb_seg;
+       uint32_t win_func;
+       uint16_t scanline_bytes;
+
+       /* VBE 1.2 and above */
+       uint16_t xres, yres;
+       uint8_t xcharsz, ycharsz;
+       uint8_t num_planes;
+       uint8_t bpp;
+       uint8_t num_banks;
+       uint8_t mem_model;
+       uint8_t bank_size;              /* bank size in KB */
+       uint8_t num_img_pages;
+       uint8_t reserved1;
+
+       /* direct color fields */
+       uint8_t rsize, rpos;
+       uint8_t gsize, gpos;
+       uint8_t bsize, bpos;
+       uint8_t xsize, xpos;
+       uint8_t cmode_info;             /* direct color mode attributes */
+
+       /* VBE 2.0 and above */
+       uint32_t fb_addr;               /* physical address of the linear framebuffer */
+       uint32_t os_addr;               /* phys. address of off-screen memory */
+       uint16_t os_size;               /* size in KB of off-screen memory */
+
+       /* VBE 3.0 and above */
+       uint16_t lfb_scanline_bytes;
+       uint8_t banked_num_img_pages;
+       uint8_t lfb_num_img_pages;
+       uint8_t lfb_rsize, lfb_rpos;
+       uint8_t lfb_gsize, lfb_gpos;
+       uint8_t lfb_bsize, lfb_bpos;
+       uint8_t lfb_xsize, lfb_xpos;
+       uint32_t max_pixel_clock;
+
+       char reserved2[190];
+} PACKED;
+
+struct vbe_crtc_info {
+       uint16_t htotal, hsync_start, hsync_end;
+       uint16_t vtotal, vsync_start, vsync_end;
+       uint8_t flags;
+       uint32_t pixel_clock;
+       uint16_t rate_centihz;  /* refresh rate in 1/100 hz (pck / (htotal * vtotal)) */
+       char reserved[40];
+} PACKED;
+#pragma pack (pop)
+
+/* returned by vbe_scanline_info() */
+struct vbe_scanline_info {
+       int size;
+       int num_pixels;
+       int max_scanlines;
+};
+
+enum {
+       VBE_8BIT_DAC    = 0x01,
+       VBE_NON_VGA             = 0x02,
+       VBE_DAC_BLANK   = 0x04,
+       VBE_STEREO              = 0x08, /* ? */
+       VBE_ACCEL               = 0x08,
+       VBE_STEREO_VESA = 0x10, /* ? */
+       VBE_MUSTLOCK    = 0x10,
+       VBE_HWCURSOR    = 0x20,
+       VBE_HWCLIP              = 0x40,
+       VBE_TRANSP_BLT  = 0x80
+};
+
+#define VBE_VER_MAJOR(v)       (((v) >> 8) & 0xff)
+#define VBE_VER_MINOR(v)       ((v) & 0xff)
+
+/* VBE mode attribute flags (vbe_mode_info.attr) */
+enum {
+       VBE_ATTR_AVAIL          = 0x0001,
+       VBE_ATTR_OPTINFO        = 0x0002,
+       VBE_ATTR_TTY            = 0x0004,
+       VBE_ATTR_COLOR          = 0x0008,
+       VBE_ATTR_GFX            = 0x0010,
+       /* VBE 2.0 */
+       VBE_ATTR_NOTVGA         = 0x0020,
+       VBE_ATTR_BANKED         = 0x0040,
+       VBE_ATTR_LFB            = 0x0080,
+       VBE_ATTR_DBLSCAN        = 0x0100,
+       /* VBE 3.0 */
+       VBE_ATTR_ILACE          = 0x0200,       /* ! */
+       VBE_ATTR_TRIPLEBUF      = 0x0400,
+       VBE_ATTR_STEREO         = 0x0800,
+       VBE_ATTR_STEREO_2FB     = 0x1000,
+       /* VBE/AF */
+       VBE_ATTR_MUSTLOCK       = 0x0200        /* ! */
+};
+
+/* VBE memory model type (vbe_mode_info.mem_model) */
+enum {
+       VBE_TYPE_TEXT,
+       VBE_TYPE_CGA,
+       VBE_TYPE_HERCULES,
+       VBE_TYPE_PLANAR,
+       VBE_TYPE_PACKED,
+       VBE_TYPE_UNCHAIN,
+       VBE_TYPE_DIRECT,
+       VBE_TYPE_YUV
+};
+
+/* VBE window attribute (vbe_mode_info.win(a|b)_attr) */
+enum {
+       VBE_WIN_AVAIL   = 0x01,
+       VBE_WIN_RD              = 0x02,
+       VBE_WIN_WR              = 0x04
+};
+
+/* mode number flags */
+enum {
+       VBE_MODE_RATE           = 0x0800,       /* VBE 3.0+ user-specified refresh rate */
+       VBE_MODE_ACCEL          = 0x2000,       /* VBE/AF */
+       VBE_MODE_LFB            = 0x4000,       /* VBE 2.0+ */
+       VBE_MODE_PRESERVE       = 0x8000
+};
+
+/* standard mode numbers */
+enum {
+       VBE_640X400_8BPP        = 0x100,
+       VBE_640X480_8BPP        = 0x101,
+       VBE_800X600_4BPP        = 0x102,
+       VBE_800X600_8BPP        = 0x103,
+       VBE_1024X768_4BPP       = 0x104,
+       VBE_1024X768_8BPP       = 0x105,
+       VBE_1280X1024_4BPP      = 0x106,
+       VBE_1280X1024_8BPP      = 0x107,
+       VBE_80X60_TEXT          = 0x108,
+       VBE_132X25_TEXT         = 0x109,
+       VBE_132X43_TEXT         = 0x10a,
+       VBE_132X50_TEXT         = 0x10b,
+       VBE_132X60_TEXT         = 0x10c,
+       /* VBE 1.2 */
+       VBE_320X200_15BPP       = 0x10d,
+       VBE_320X200_16BPP       = 0x10e,
+       VBE_320X200_24BPP       = 0x10f,
+       VBE_640X480_15BPP       = 0x110,
+       VBE_640X480_16BPP       = 0x111,
+       VBE_640X480_24BPP       = 0x112,
+       VBE_800X600_15BPP       = 0x113,
+       VBE_800X600_16BPP       = 0x114,
+       VBE_800X600_24BPP       = 0x115,
+       VBE_1024X768_15BPP      = 0x116,
+       VBE_1024X768_16BPP      = 0x117,
+       VBE_1024X768_24BPP      = 0x118,
+       VBE_1280X1024_15BPP     = 0x119,
+       VBE_1280X1024_16BPP     = 0x11a,
+       VBE_1280X1024_24BPP     = 0x11b,
+       /* VBE 2.0 */
+       VBE_1600X1200_8BPP      = 0x120,
+       VBE_1600X1200_15BPP     = 0x121,
+       VBE_1600X1200_16BPP     = 0x122,
+
+       VBE_VMEM_MODE           = 0x81ff
+};
+
+/* VBE CRTC flags (vbe_crtc_info.flags) */
+enum {
+       VBE_CRTC_DBLSCAN        = 0x01,
+       VBE_CRTC_ILACE          = 0x02,
+       VBE_CRTC_HSYNC_NEG      = 0x04,
+       VBE_CRTC_VSYNC_NEG      = 0x08
+};
+
+enum {
+       VBE_STATE_CTRLHW        = 0x01,
+       VBE_STATE_BIOS          = 0x02,
+       VBE_STATE_DAC           = 0x04,
+       VBE_STATE_REGS          = 0x08,
+
+       VBE_STATE_ALL           = 0xffff
+};
+
+enum {
+       VBE_SWAP_NOW,
+       VBE_SWAP_VBLANK,
+       VBE_SWAP_ASYNC  /* schedule swap and return (triple-buffering) */
+};
+
+int vbe_info(struct vbe_info *info);
+int vbe_num_modes(struct vbe_info *info);
+int vbe_mode_info(int mode, struct vbe_mode_info *minf);
+
+void vbe_print_info(FILE *fp, struct vbe_info *info);
+void vbe_print_mode_info(FILE *fp, struct vbe_mode_info *minf);
+
+int vbe_setmode(uint16_t mode);
+int vbe_setmode_crtc(uint16_t mode, struct vbe_crtc_info *crtc);
+int vbe_getmode(void);
+
+int vbe_state_size(unsigned int flags);
+int vbe_save(void *stbuf, int sz, unsigned int flags);
+int vbe_restore(void *stbuf, int sz, unsigned int flags);
+
+int vbe_setwin(int wid, int pos);
+int vbe_getwin(int wid);
+
+/* returns the actual length in pixels, which might not be what was requested */
+int vbe_setscanlen(int len_pix);
+int vbe_getscanlen(void);
+int vbe_getpitch(void);
+int vbe_scanline_info(struct vbe_scanline_info *sinf);
+
+int vbe_setdisp(int x, int y, int when);
+int vbe_swap(uint32_t voffs, int when);
+int vbe_swap_pending(void);    /* 0: not pending (done) or error, 1: pending swap */
+/* TODO add stereo swap */
+
+#endif /* VBE_H_ */
diff --git a/src/dos/vga.c b/src/dos/vga.c
new file mode 100644 (file)
index 0000000..c829684
--- /dev/null
@@ -0,0 +1,11 @@
+#include "vga.h"
+#include "cdpmi.h"
+
+int vga_setmode(int mode)
+{
+       struct dpmi_regs regs = {0};
+
+       regs.eax = mode;        /* func 00 | mode */
+       dpmi_int(0x10, &regs);
+       return 0;
+}
diff --git a/src/dos/vga.h b/src/dos/vga.h
new file mode 100644 (file)
index 0000000..58c6ecb
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef VGA_H_
+#define VGA_H_
+
+#include "util.h"
+
+int vga_setmode(int mode);
+
+#ifdef __WATCOMC__
+void vga_setpal(int16_t idx, uint8_t r, uint8_t g, uint8_t b);
+#pragma aux vga_setpal = \
+       "test ax, 0x8000" \
+       "jnz skip_dacaddr" \
+       "mov dx, 0x3c8" \
+       "out dx, al" \
+       "skip_dacaddr:" \
+       "mov dx, 0x3c9" \
+       "mov al, bl" \
+       "shr al, 2" \
+       "out dx, al" \
+       "mov al, bh" \
+       "shr al, 2" \
+       "out dx, al" \
+       "mov al, cl" \
+       "shr al, 2" \
+       "out dx, al" \
+       parm[ax][bl][bh][cl] \
+       modify[dx];
+#endif /* __WATCOMC__ */
+
+#endif /* VGA_H_ */
diff --git a/src/dos/watdpmi.c b/src/dos/watdpmi.c
new file mode 100644 (file)
index 0000000..04d7b94
--- /dev/null
@@ -0,0 +1,59 @@
+#ifdef __WATCOMC__
+#include "cdpmi.h"
+
+void dpmi_real_int(int inum, struct dpmi_real_regs *regs)
+{
+       unsigned char int_num = (unsigned char)inum;
+       __asm {
+               mov eax, 0x300
+               mov edi, regs
+               mov bl, int_num
+               mov bh, 0
+               xor ecx, ecx
+               int 0x31
+       }
+}
+
+void *dpmi_mmap(uint32_t phys_addr, unsigned int size)
+{
+       uint16_t mem_high, mem_low;
+       uint16_t phys_high = phys_addr >> 16;
+       uint16_t phys_low = phys_addr & 0xffff;
+       uint16_t size_high = size >> 16;
+       uint16_t size_low = size & 0xffff;
+       unsigned int err, res = 0;
+
+       __asm {
+               mov eax, 0x800
+               mov bx, phys_high
+               mov cx, phys_low
+               mov si, size_high
+               mov di, size_low
+               int 0x31
+               add res, 1
+               mov err, eax
+               mov mem_high, bx
+               mov mem_low, cx
+       }
+
+       if(res == 2) {
+               return 0;
+       }
+       return (void*)(((uint32_t)mem_high << 16) | ((uint32_t)mem_low));
+}
+
+void dpmi_munmap(void *addr)
+{
+       uint16_t mem_high = (uint32_t)addr >> 16;
+       uint16_t mem_low = (uint16_t)addr;
+
+       __asm {
+               mov eax, 0x801
+               mov bx, mem_high
+               mov cx, mem_low
+               int 0x31
+       }
+}
+#else
+int stop_gcc_crying_about_empty_translation_units = 0;
+#endif /* __WATCOM__ */
index 82a8bfc..6680c8a 100644 (file)
@@ -1,5 +1,7 @@
 #include "util.h"
 
+uint32_t perf_start_count, perf_interval_count;
+
 int mask_to_shift(unsigned int mask)
 {
        int s = 0;
index d1f81eb..69d9d1e 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef UTIL_H_
 #define UTIL_H_
 
+
 #ifdef NO_STDINT_H
 typedef char int8_t;
 typedef unsigned char uint8_t;
@@ -8,10 +9,32 @@ typedef short int16_t;
 typedef unsigned short uint16_t;
 typedef int int32_t;
 typedef unsigned int uint32_t;
+typedef unsigned long intptr_t;
 #else
 #include <stdint.h>
 #endif
 
+#ifdef __GNUC__
+#define INLINE __inline
+#define PACKED __attribute__((packed))
+
+#elif defined(__WATCOMC__)
+#define INLINE __inline
+#define PACKED
+
+#else
+#define INLINE
+#define PACKED
+#endif
+
+#define BSWAP16(x)     ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
+#define BSWAP32(x)     \
+       ((((x) >> 24) & 0xff) | \
+        (((x) >> 8) & 0xff00) | \
+        (((x) << 8) & 0xff0000) | \
+        ((x) << 24))
+
+
 extern int sinlut[];
 
 #define SIN(x) sinlut[(x) & 0x3ff]
@@ -19,4 +42,198 @@ extern int sinlut[];
 
 int mask_to_shift(unsigned int mask);
 
+#if defined(__i386__) || defined(__x86_64__) || defined(__386__) || defined(MSDOS)
+/* fast conversion of double -> 32bit int
+ * for details see:
+ *  - http://chrishecker.com/images/f/fb/Gdmfp.pdf
+ *  - http://stereopsis.com/FPU.html#convert
+ */
+static INLINE int32_t cround64(double val)
+{
+       val += 6755399441055744.0;
+       return *(int32_t*)&val;
+}
+#else
+#define cround64(x)    ((int32_t)(x))
+#endif
+
+static INLINE float rsqrt(float x)
+{
+       float xhalf = x * 0.5f;
+       int32_t i = *(int32_t*)&x;
+       i = 0x5f3759df - (i >> 1);
+       x = *(float*)&i;
+       x = x * (1.5f - xhalf * x * x);
+       return x;
+}
+
+extern uint32_t perf_start_count, perf_interval_count;
+
+#ifdef __WATCOMC__
+void memset16(void *dest, uint16_t val, int count);
+#pragma aux memset16 = \
+       "cld" \
+       "test ecx, 1" \
+       "jz memset16_dwords" \
+       "rep stosw" \
+       "jmp memset16_done" \
+       "memset16_dwords:" \
+       "shr ecx, 1" \
+       "push ax" \
+       "shl eax, 16" \
+       "pop ax" \
+       "rep stosd" \
+       "memset16_done:" \
+       parm[edi][ax][ecx];
+
+#ifdef USE_MMX
+void memcpy64(void *dest, void *src, int count);
+#pragma aux memcpy64 = \
+       "cploop:" \
+       "movq mm0, [edx]" \
+       "movq [ebx], mm0" \
+       "add edx, 8" \
+       "add ebx, 8" \
+       "dec ecx" \
+       "jnz cploop" \
+       "emms" \
+       parm[ebx][edx][ecx] \
+       modify[8087];
+#else
+#define memcpy64(dest, src, count)     memcpy(dest, src, (count) << 3)
+#endif
+
+void perf_start(void);
+#pragma aux perf_start = \
+       "xor eax, eax" \
+       "cpuid" \
+       "rdtsc" \
+       "mov [perf_start_count], eax" \
+       modify[eax ebx ecx edx];
+
+void perf_end(void);
+#pragma aux perf_end = \
+       "xor eax, eax" \
+       "cpuid" \
+       "rdtsc" \
+       "sub eax, [perf_start_count]" \
+       "mov [perf_interval_count], eax" \
+       modify [eax ebx ecx edx];
+
+void debug_break(void);
+#pragma aux debug_break = "int 3";
+#endif
+
+#ifdef __GNUC__
+#if defined(__i386__) || defined(__x86_64__)
+#define memset16(dest, val, count) asm volatile ( \
+       "cld\n\t" \
+       "test $1, %2\n\t" \
+       "jz 0f\n\t" \
+       "rep stosw\n\t" \
+       "jmp 1f\n\t" \
+       "0:\n\t" \
+       "shr $1, %2\n\t" \
+       "push %%ax\n\t" \
+       "shl $16, %%eax\n\t" \
+       "pop %%ax\n\t" \
+       "rep stosl\n\t" \
+       "1:\n\t"\
+       :: "D"(dest), "a"((uint16_t)(val)), "c"(count) \
+       : "memory")
+#else
+static void INLINE memset16(void *dest, uint16_t val, int count)
+{
+       uint16_t *ptr = dest;
+       while(count--) *ptr++ = val;
+}
+#endif
+
+#ifdef USE_MMX
+#define memcpy64(dest, src, count) asm volatile ( \
+       "0:\n\t" \
+       "movq (%1), %%mm0\n\t" \
+       "movq %%mm0, (%0)\n\t" \
+       "add $8, %1\n\t" \
+       "add $8, %0\n\t" \
+       "dec %2\n\t" \
+       "jnz 0b\n\t" \
+       "emms\n\t" \
+       :: "r"(dest), "r"(src), "r"(count) \
+       : "%mm0")
+#else
+#define memcpy64(dest, src, count)     memcpy(dest, src, (count) << 3)
+#endif
+
+#define perf_start()  asm volatile ( \
+       "xor %%eax, %%eax\n" \
+       "cpuid\n" \
+       "rdtsc\n" \
+       "mov %%eax, %0\n" \
+       : "=m"(perf_start_count) \
+       :: "%eax", "%ebx", "%ecx", "%edx")
+
+#define perf_end() asm volatile ( \
+       "xor %%eax, %%eax\n" \
+       "cpuid\n" \
+       "rdtsc\n" \
+       "sub %1, %%eax\n" \
+       "mov %%eax, %0\n" \
+       : "=m"(perf_interval_count) \
+       : "m"(perf_start_count) \
+       : "%eax", "%ebx", "%ecx", "%edx")
+
+#define debug_break() \
+       asm volatile ("int $3")
+#endif
+
+#ifdef _MSC_VER
+void __inline memset16(void *dest, uint16_t val, int count)
+{
+       __asm {
+               cld
+               mov ax, val
+               mov edi, dest
+               mov ecx, count
+               test ecx, 1
+               jz memset16_dwords
+               rep stosw
+               jmp memset16_done
+               memset16_dwords:
+               shr ecx, 1
+               push ax
+               shl eax, 16
+               pop ax
+               rep stosd
+               memset16_done:
+       }
+}
+
+#define perf_start() \
+       do { \
+               __asm { \
+                       xor eax, eax \
+                       cpuid \
+                       rdtsc \
+                       mov [perf_start_count], eax \
+               } \
+       } while(0)
+
+#define perf_end() \
+       do { \
+               __asm { \
+                       xor eax, eax \
+                       cpuid \
+                       rdtsc \
+                       sub eax, [perf_start_count] \
+                       mov [perf_interval_count], eax \
+               } \
+       } while(0)
+
+#define debug_break() \
+       do { \
+               __asm { int 3 } \
+       } while(0)
+#endif
+
 #endif /* UTIL_H_ */