From cfe889ac035848dbe4c3a89afb7c8c37a574163e Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Tue, 3 Mar 2020 06:48:25 +0200 Subject: [PATCH] initial commit --- .gitignore | 36 ++++ Makefile | 45 +++++ src/dos/cdpmi.h | 94 ++++++++++ src/dos/djdpmi.c | 35 ++++ src/dos/gfx.c | 207 +++++++++++++++++++++ src/dos/gfx.h | 74 ++++++++ src/dos/inttypes.h | 37 ++++ src/dos/keyb.c | 255 ++++++++++++++++++++++++++ src/dos/keyb.h | 55 ++++++ src/dos/logger.c | 21 +++ src/dos/logger.h | 14 ++ src/dos/main.c | 77 ++++++++ src/dos/mouse.asm | 193 ++++++++++++++++++++ src/dos/mouse.h | 25 +++ src/dos/pit8254.h | 51 ++++++ src/dos/scancode.h | 47 +++++ src/dos/timer.c | 176 ++++++++++++++++++ src/dos/vbe.c | 508 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dos/vbe.h | 242 +++++++++++++++++++++++++ src/dos/vga.c | 11 ++ src/dos/vga.h | 30 ++++ src/dos/watdpmi.c | 59 ++++++ src/game.c | 24 +++ src/game.h | 40 +++++ src/timer.h | 20 +++ src/util.c | 3 + src/util.h | 105 +++++++++++ 27 files changed, 2484 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/dos/cdpmi.h create mode 100644 src/dos/djdpmi.c create mode 100644 src/dos/gfx.c create mode 100644 src/dos/gfx.h create mode 100644 src/dos/inttypes.h create mode 100644 src/dos/keyb.c create mode 100644 src/dos/keyb.h create mode 100644 src/dos/logger.c create mode 100644 src/dos/logger.h create mode 100644 src/dos/main.c create mode 100644 src/dos/mouse.asm create mode 100644 src/dos/mouse.h create mode 100644 src/dos/pit8254.h create mode 100644 src/dos/scancode.h create mode 100644 src/dos/timer.c create mode 100644 src/dos/vbe.c create mode 100644 src/dos/vbe.h create mode 100644 src/dos/vga.c create mode 100644 src/dos/vga.h create mode 100644 src/dos/watdpmi.c create mode 100644 src/game.c create mode 100644 src/game.h create mode 100644 src/timer.h create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04f87b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +*.swp +*.o +*.d +*.obj +*.OBJ +*.exe +*.EXE +*.lnk +*.LNK +*.lbc +*.LBC +*.lib +*.LIB +*.err +*.ERR +*.log +*.LOG +*sdf +game +*.zip +data +*.a +*.dll +*.occ +*.OCC +*.map +*.MAP +*.CFG +*.cfg +*.$$$ +*.dep +*.DEP +*.odj +*.COF +*.dja +*.DJA diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ece428f --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +!ifdef __UNIX__ +dosobj = src/dos/main.obj src/dos/gfx.obj src/dos/timer.obj src/dos/watdpmi.obj & + src/dos/vbe.obj src/dos/vga.obj src/dos/keyb.obj src/dos/mouse.obj & + src/dos/logger.obj +gameobj = src/game.obj src/util.obj +incpath = -Isrc -Isrc/dos -Ilibs/imago/src +libpath = libpath libs/imago +RM = rm -f +!else +dosobj = src\dos\main.obj src\dos\gfx.obj src\dos\timer.obj src\dos\watdpmi.obj & + src\dos\vbe.obj src\dos\vga.obj src\dos\keyb.obj src\dos\mouse.obj & + src\dos\logger.obj +gameobj = src\game.obj src\util.obj +incpath = -Isrc -Isrc\dos -Ilibs\imago\src +libpath = libpath libs\imago +RM = del +!endif + +obj = $(dosobj) $(gameobj) +bin = game.exe + +CC = wcc386 +LD = wlink +CFLAGS = -d3 -5 -fp5 -otebmileran -s -zq -bt=dos $(incpath) +LDFLAGS = option map $(libpath) +# library { $(libs) } + +$(bin): $(obj) + %write objects.lnk $(obj) + $(LD) debug all name $@ system dos4g file { @objects } $(LDFLAGS) + +.c: src;src/dos +.asm: src;src/dos + +.c.obj: .autodepend + $(CC) -fo=$@ $(CFLAGS) $[* + +.asm.obj: + nasm -f obj -o $@ $[*.asm + +clean: .symbolic + $(RM) src\*.obj + $(RM) src\dos\*.obj + $(RM) objects.lnk + $(RM) $(bin) diff --git a/src/dos/cdpmi.h b/src/dos/cdpmi.h new file mode 100644 index 0000000..71138b6 --- /dev/null +++ b/src/dos/cdpmi.h @@ -0,0 +1,94 @@ +#ifndef DPMI_H_ +#define DPMI_H_ + +#ifdef __DJGPP__ +#include +#include + +#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 "inttypes.h" +#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 index 0000000..f3a06b9 --- /dev/null +++ b/src/dos/djdpmi.c @@ -0,0 +1,35 @@ +#ifdef __DJGPP__ +#include +#include +#include "cdpmi.h" +#include "inttypes.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 index 0000000..b4e4add --- /dev/null +++ b/src/dos/gfx.c @@ -0,0 +1,207 @@ +#include +#include +#include "gfx.h" +#include "vbe.h" +#include "vga.h" + +#define SAME_BPP(a, b) \ + ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \ + ((a) == 32 && (b) == 24) || ((a) == 24 && (b) == 32)) + +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 fbidx; +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 = 4; /* TODO change */ + 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= 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; + 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); + } + + 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; +} + +int match_video_mode(int xsz, int ysz, int bpp) +{ + int i, best = -1; + struct video_mode *vm; + + for(i=0; ixsz != 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; imode == mode) return i; + } + return -1; +} + +void *set_video_mode(int idx, int nbuf) +{ + unsigned int mode; + struct vbe_mode_info minf; + struct video_mode *vm = vmodes + idx; + + printf("setting video mode %x (%dx%d %d bpp)\n", (unsigned int)vm->mode, + vm->xsz, vm->ysz, vm->bpp); + + 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"); + } + + vbe_mode_info(mode, &minf); + + curmode = vm; + if(nbuf < 1) nbuf = 1; + if(nbuf > 2) nbuf = 2; + pgcount = nbuf > minf.num_img_pages ? minf.num_img_pages : nbuf; + pgsize = (vm->xsz * vm->bpp / 8) * vm->ysz; + fbsize = pgcount * pgsize; + + vpgaddr[0] = (void*)dpmi_mmap(minf.fb_addr, fbsize); + memset(vpgaddr[0], 0xaa, fbsize); + + if(pgcount > 1) { + vpgaddr[1] = (char*)vpgaddr[0] + pgsize; + fbidx = 1; + page_flip(FLIP_NOW); /* start with the second page visible */ + } else { + fbidx = 0; + vpgaddr[1] = 0; + } + return vpgaddr[0]; +} + +int set_text_mode(void) +{ + vga_setmode(3); + curmode = 0; + return 0; +} + +void *page_flip(int vsync) +{ + if(!vpgaddr[1]) { + /* page flipping not supported */ + return vpgaddr[0]; + } + + vbe_swap(fbidx ? pgsize : 0, vsync ? VBE_SWAP_VBLANK : VBE_SWAP_NOW); + fbidx = (fbidx + 1) & 1; + + return vpgaddr[fbidx]; +} + +static uint32_t calc_mask(int sz, int pos) +{ + int i; + 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 index 0000000..fe4a499 --- /dev/null +++ b/src/dos/gfx.h @@ -0,0 +1,74 @@ +#ifndef GFX_H_ +#define GFX_H_ + +#include "inttypes.h" + +struct video_mode { + uint16_t mode; + short xsz, ysz, bpp; + short rbits, gbits, bbits; + short rshift, gshift, bshift; + uint32_t rmask, gmask, bmask; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int init_video(void); +void cleanup_video(void); + +struct video_mode *video_modes(void); +int num_video_modes(void); + +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); + +#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/inttypes.h b/src/dos/inttypes.h new file mode 100644 index 0000000..4d40ee2 --- /dev/null +++ b/src/dos/inttypes.h @@ -0,0 +1,37 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016 John Tsiombikas + +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 . +*/ +#ifndef INT_TYPES_H_ +#define INT_TYPES_H_ + +/*#if defined(__WATCOMC__) && __WATCOMC__ < 0x1200*/ +#if defined(__DOS__) || defined(__MSDOS__) +typedef 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; +#else +#include +#endif + +#endif /* INT_TYPES_H_ */ diff --git a/src/dos/keyb.c b/src/dos/keyb.c new file mode 100644 index 0000000..55b9013 --- /dev/null +++ b/src/dos/keyb.c @@ -0,0 +1,255 @@ +/* +DOS interrupt-based keyboard driver. +Copyright (C) 2013 John Tsiombikas + +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 the program. If not, see +*/ +#define KEYB_C_ + +#include +#include +#include +#include +#include +#include + +#ifdef __WATCOMC__ +#include +#endif +#ifdef __DJGPP__ +#include +#include +#include +#endif + +#include "keyb.h" +#include "scancode.h" +#include "inttypes.h" + +#define KB_INTR 0x9 +#define KB_PORT 0x60 + +#define PIC1_CMD_PORT 0x20 +#define OCW2_EOI (1 << 5) + +#ifdef __WATCOMC__ +#define INTERRUPT __interrupt __far + +#define DONE_INIT (prev_handler) +static void (INTERRUPT *prev_handler)(); +#endif + +#ifdef __DJGPP__ +#define INTERRUPT + +#define DONE_INIT prev_intr.pm_offset +static _go32_dpmi_seginfo intr, prev_intr; + +#define outp(p, v) outportb(p, v) +#define inp(p) inportb(p) +#endif + +static void INTERRUPT kbintr(); + +static int *buffer; +static int buffer_size, buf_ridx, buf_widx; +static int last_key; + +static unsigned int num_pressed; +static unsigned char keystate[256]; + +#define ADVANCE(x) ((x) = ((x) + 1) % buffer_size) + +int kb_init(int bufsz) +{ + if(DONE_INIT) { + fprintf(stderr, "keyboard driver already initialized!\n"); + return 0; + } + + buffer_size = bufsz; + if(buffer_size && !(buffer = malloc(buffer_size * sizeof *buffer))) { + fprintf(stderr, "failed to allocate input buffer, continuing without\n"); + buffer_size = 0; + } + buf_ridx = buf_widx = 0; + last_key = -1; + + memset(keystate, 0, sizeof keystate); + num_pressed = 0; + + /* set our interrupt handler */ + _disable(); +#ifdef __WATCOMC__ + prev_handler = _dos_getvect(KB_INTR); + _dos_setvect(KB_INTR, kbintr); +#endif +#ifdef __DJGPP__ + _go32_dpmi_get_protected_mode_interrupt_vector(KB_INTR, &prev_intr); + intr.pm_offset = (intptr_t)kbintr; + intr.pm_selector = _go32_my_cs(); + _go32_dpmi_allocate_iret_wrapper(&intr); + _go32_dpmi_set_protected_mode_interrupt_vector(KB_INTR, &intr); +#endif + _enable(); + + return 0; +} + +void kb_shutdown(void) +{ + if(!DONE_INIT) { + return; + } + + /* restore the original interrupt handler */ + _disable(); +#ifdef __WATCOMC__ + _dos_setvect(KB_INTR, prev_handler); +#endif +#ifdef __DJGPP__ + _go32_dpmi_set_protected_mode_interrupt_vector(KB_INTR, &prev_intr); + _go32_dpmi_free_iret_wrapper(&intr); +#endif + _enable(); + + free(buffer); +} + +int kb_isdown(int key) +{ + switch(key) { + case KB_ANY: + return num_pressed; + + case KB_ALT: + return keystate[KB_LALT] + keystate[KB_RALT]; + + case KB_CTRL: + return keystate[KB_LCTRL] + keystate[KB_RCTRL]; + } + + if(isalpha(key)) { + key = tolower(key); + } + return keystate[key]; +} + +#ifdef __WATCOMC__ +void halt(void); +#pragma aux halt = \ + "sti" \ + "hlt"; +#endif + +#ifdef __DJGPP__ +#define halt() asm volatile("sti\n\thlt\n\t") +#endif + +void kb_wait(void) +{ + int key; + while((key = kb_getkey()) == -1) { +#ifdef USE_HLT + /* put the processor to sleep while waiting for keypresses, but first + * make sure interrupts are enabled, or we'll sleep forever + */ + halt(); +#endif + } + kb_putback(key); +} + +int kb_getkey(void) +{ + int res; + + if(buffer) { + if(buf_ridx == buf_widx) { + return -1; + } + res = buffer[buf_ridx]; + ADVANCE(buf_ridx); + } else { + res = last_key; + last_key = -1; + } + return res; +} + +void kb_putback(int key) +{ + if(buffer) { + /* go back a place */ + if(--buf_ridx < 0) { + buf_ridx += buffer_size; + } + + /* if the write end hasn't caught up with us, go back one place + * and put it there, otherwise just overwrite the oldest key which + * is right where we were. + */ + if(buf_ridx == buf_widx) { + ADVANCE(buf_ridx); + } + + buffer[buf_ridx] = key; + } else { + last_key = key; + } +} + +static void INTERRUPT kbintr() +{ + unsigned char code; + int key, c, press; + + code = inp(KB_PORT); + + if(code >= 128) { + press = 0; + code -= 128; + + if(num_pressed > 0) { + num_pressed--; + } + } else { + press = 1; + + num_pressed++; + } + + key = scantbl[code]; + c = (keystate[KB_LSHIFT] | keystate[KB_RSHIFT]) ? scantbl_shift[code] : key; + + if(press) { + /* append to buffer */ + last_key = c; + if(buffer_size > 0) { + buffer[buf_widx] = c; + ADVANCE(buf_widx); + /* if the write end overtook the read end, advance the read end + * too, to discard the oldest keypress from the buffer + */ + if(buf_widx == buf_ridx) { + ADVANCE(buf_ridx); + } + } + } + + /* and update keystate table */ + keystate[key] = press; + + outp(PIC1_CMD_PORT, OCW2_EOI); /* send end-of-interrupt */ +} diff --git a/src/dos/keyb.h b/src/dos/keyb.h new file mode 100644 index 0000000..9b21749 --- /dev/null +++ b/src/dos/keyb.h @@ -0,0 +1,55 @@ +/* +DOS interrupt-based keyboard driver. +Copyright (C) 2013 John Tsiombikas + +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 the program. If not, see +*/ +#ifndef KEYB_H_ +#define KEYB_H_ + +#include "game.h" + +#define KB_ANY (-1) +#define KB_ALT (-2) +#define KB_CTRL (-3) +#define KB_SHIFT (-4) + +#ifdef __cplusplus +extern "C" { +#endif + +int kb_init(int bufsz); /* bufsz can be 0 for no buffered keys */ +void kb_shutdown(void); /* don't forget to call this at the end! */ + +/* Boolean predicate for testing the current state of a particular key. + * You may also pass KB_ANY to test if any key is held down. + */ +int kb_isdown(int key); + +/* waits for any keypress */ +void kb_wait(void); + +/* removes and returns a single key from the input buffer. + * If buffering is disabled (initialized with kb_init(0)), then it always + * returns the last key pressed. + */ +int kb_getkey(void); + +void kb_putback(int key); + +#ifdef __cplusplus +} +#endif + +#endif /* KEYB_H_ */ diff --git a/src/dos/logger.c b/src/dos/logger.c new file mode 100644 index 0000000..70489ad --- /dev/null +++ b/src/dos/logger.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include +#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 index 0000000..9ef9a85 --- /dev/null +++ b/src/dos/logger.h @@ -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_ */ diff --git a/src/dos/main.c b/src/dos/main.c new file mode 100644 index 0000000..a41453b --- /dev/null +++ b/src/dos/main.c @@ -0,0 +1,77 @@ +#include +#include +#include "game.h" +#include "keyb.h" +#include "timer.h" +#include "gfx.h" +#include "logger.h" +#include "cdpmi.h" + +static void draw(void); + +static struct video_mode *vmode; +static void *vmem; + +int main(int argc, char **argv) +{ + struct video_mode *vmodes; + int vmidx; + + init_logger("game.log"); + + init_timer(100); + kb_init(32); + + if(init_video() == -1) { + return 1; + } + vmodes = video_modes(); + + if((vmidx = match_video_mode(640, 480, 16)) == -1) { + return 1; + } + if(!(vmem = set_video_mode(vmidx, 1))) { + return 1; + } + vmode = vmodes + vmidx; + + fb_width = vmode->xsz; + fb_height = vmode->ysz; + fb_size = (vmode->xsz * vmode->bpp / 8) * vmode->ysz; + + reset_timer(); + + for(;;) { + int key; + while((key = kb_getkey()) != -1) { + if(key == 27) goto break_evloop; + } + + time_msec = get_msec(); + draw(); + } + +break_evloop: + set_text_mode(); + kb_shutdown(); + return 0; +} + +static void draw(void) +{ + int i, j; + uint16_t *pptr = vmem; + + for(i=0; i> 2) == (j >> 2); + *pptr++ = chess ? 0xff00 : 0x00ff; + } + } +} + +void swap_buffers(void *pixels) +{ + wait_vsync(); + memcpy(vmem, pixels, fb_size); +} diff --git a/src/dos/mouse.asm b/src/dos/mouse.asm new file mode 100644 index 0000000..c82e7fd --- /dev/null +++ b/src/dos/mouse.asm @@ -0,0 +1,193 @@ +; vi:set filetype=nasm: +; foo_ are watcom functions, _foo are djgpp functions + +QUERY equ 0 +SHOW equ 1 +HIDE equ 2 +READ equ 3 +WRITE equ 4 +PIXRATE equ 15 +XLIM equ 7 +YLIM equ 8 + +PUSHA_EAX_OFFS equ 28 +PUSHA_ECX_OFFS equ 20 +PUSHA_EDX_OFFS equ 16 + + section .text + bits 32 + +; int have_mouse(void) + global have_mouse_ + global _have_mouse +have_mouse_: +_have_mouse: + pusha + mov ax, QUERY + int 0x33 + and eax, 0xffff + mov [esp + PUSHA_EAX_OFFS], eax + popa + ret + +; void show_mouse(int show) + global show_mouse_ +show_mouse_: + pusha + test ax, ax + mov ax, HIDE + jz .skip + mov ax, SHOW +.skip: int 0x33 + popa + ret + + global _show_mouse +_show_mouse: + push ebp + mov ebp, esp + push ebx + push esi + push edi + mov ax, [ebp + 8] + test ax, ax + mov ax, HIDE + jz .skip + mov ax, SHOW +.skip: int 0x33 + pop edi + pop esi + pop ebx + pop ebp + ret + +; int read_mouse(int *xp, int *yp) + global read_mouse_ +read_mouse_: + pusha + mov esi, eax ; xp + mov edi, edx ; yp + mov ax, READ + int 0x33 + xor eax, eax + and ecx, 0xffff + and edx, 0xffff + mov ax, bx + mov [esp + PUSHA_EAX_OFFS], eax + mov [esi], ecx + mov [edi], edx + popa + ret + + global _read_mouse +_read_mouse: + push ebp + mov ebp, esp + push ebx + push esi + push edi + mov ax, READ + int 0x33 + xor eax, eax + mov ax, bx + and ecx, 0xffff + mov ebx, [ebp + 8] + mov [ebx], ecx + and edx, 0xffff + mov ebx, [ebp + 12] + mov [ebx], edx + pop edi + pop esi + pop ebx + pop ebp + ret + +; void set_mouse(int x, int y) + global set_mouse_ +set_mouse_: + pusha + mov cx, ax + mov ax, WRITE + int 0x33 + popa + ret + + global _set_mouse +_set_mouse: + push ebp + mov ebp, esp + push ebx + push esi + push edi + mov ax, WRITE + mov cx, [ebp + 8] + mov dx, [ebp + 12] + int 0x33 + pop edi + pop esi + pop ebx + pop ebp + ret + +; void set_mouse_limits(int xmin, int ymin, int xmax, int ymax) + global set_mouse_limits_ +set_mouse_limits_: + pusha + mov cx, ax + mov dx, bx + mov ax, XLIM + int 0x33 + mov ax, YLIM + mov cx, [esp + PUSHA_EDX_OFFS] + mov dx, [esp + PUSHA_ECX_OFFS] + int 0x33 + popa + ret + + global _set_mouse_limits +_set_mouse_limits: + push ebp + mov ebp, esp + push ebx + push esi + push edi + mov ax, XLIM + mov cx, [ebp + 8] + mov dx, [ebp + 16] + int 0x33 + mov ax, YLIM + mov cx, [ebp + 12] + mov dx, [ebp + 20] + int 0x33 + pop edi + pop esi + pop ebx + pop ebp + ret + +; void set_mouse_rate(int xrate, int yrate) + global set_mouse_rate_ +set_mouse_rate_: + pusha + mov cx, ax + mov ax, PIXRATE + int 0x33 + popa + ret + + global _set_mouse_rate +_set_mouse_rate: + push ebp + mov ebp, esp + push ebx + push esi + push edi + mov ax, PIXRATE + mov cx, [esp + 4] + mov dx, [esp + 8] + int 0x33 + pop edi + pop esi + pop ebx + pop ebp + ret diff --git a/src/dos/mouse.h b/src/dos/mouse.h new file mode 100644 index 0000000..b54f6a9 --- /dev/null +++ b/src/dos/mouse.h @@ -0,0 +1,25 @@ +#ifndef MOUSE_H_ +#define MOUSE_H_ + +enum { + MOUSE_LEFT = 1, + MOUSE_RIGHT = 2, + MOUSE_MIDDLE = 4 +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int have_mouse(void); +void show_mouse(int show); +int read_mouse(int *xp, int *yp); +void set_mouse(int x, int y); +void set_mouse_limits(int xmin, int ymin, int xmax, int ymax); +void set_mouse_rate(int xrate, int yrate); + +#ifdef __cplusplus +} +#endif + +#endif /* MOUSE_H_ */ diff --git a/src/dos/pit8254.h b/src/dos/pit8254.h new file mode 100644 index 0000000..4389883 --- /dev/null +++ b/src/dos/pit8254.h @@ -0,0 +1,51 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016 John Tsiombikas + +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 . +*/ +#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/scancode.h b/src/dos/scancode.h new file mode 100644 index 0000000..48f8a0b --- /dev/null +++ b/src/dos/scancode.h @@ -0,0 +1,47 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016 John Tsiombikas + +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 . +*/ +#ifndef KEYB_C_ +#error "do not include scancode.h anywhere..." +#endif + +/* table with rough translations from set 1 scancodes to ASCII-ish */ +static int scantbl[] = { + 0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */ + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */ + KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */ + KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT, /* 2a - 36 */ + KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */ + KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */ + KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */ + 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */ +}; + +static int scantbl_shift[] = { + 0, KB_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', /* 0 - e */ + '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', /* f - 1c */ + KB_LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', /* 1d - 29 */ + KB_LSHIFT, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', KB_RSHIFT, /* 2a - 36 */ + KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */ + KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */ + KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */ + 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */ +}; diff --git a/src/dos/timer.c b/src/dos/timer.c new file mode 100644 index 0000000..d6c3b52 --- /dev/null +++ b/src/dos/timer.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include + +#ifdef __WATCOMC__ +#include +#endif + +#ifdef __DJGPP__ +#include +#include +#include +#endif + +#include "pit8254.h" +#include "inttypes.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/vbe.c b/src/dos/vbe.c new file mode 100644 index 0000000..3ada182 --- /dev/null +++ b/src/dos/vbe.c @@ -0,0 +1,508 @@ +#include +#include +#include +#include +#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) +{ + int i, num; + void *lowbuf; + uint16_t seg, sel; + uint16_t *modeptr; + 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, ®s); + + 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); + FIXPTR(info->accel_modes); + + 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) +{ + int i, num; + 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, ®s); + + 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); + } + + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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, ®s); + 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, ®s); + 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, ®s); + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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, ®s); + + 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 index 0000000..319f7d3 --- /dev/null +++ b/src/dos/vbe.h @@ -0,0 +1,242 @@ +#ifndef VBE_H_ +#define VBE_H_ + +#include +#include "inttypes.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 index 0000000..c829684 --- /dev/null +++ b/src/dos/vga.c @@ -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, ®s); + return 0; +} diff --git a/src/dos/vga.h b/src/dos/vga.h new file mode 100644 index 0000000..c6d7070 --- /dev/null +++ b/src/dos/vga.h @@ -0,0 +1,30 @@ +#ifndef VGA_H_ +#define VGA_H_ + +#include "inttypes.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 index 0000000..04d7b94 --- /dev/null +++ b/src/dos/watdpmi.c @@ -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__ */ diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..91ddb23 --- /dev/null +++ b/src/game.c @@ -0,0 +1,24 @@ +#include "game.h" + +int fb_width, fb_height; +long fb_size; +void *fb_pixels, *vmem; + +long time_msec; + +int game_init(int argc, char **argv) +{ + return 0; +} + +void game_cleanup(void) +{ +} + +void game_draw(void) +{ +} + +void game_keyboard(int key, int press) +{ +} diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..12e051b --- /dev/null +++ b/src/game.h @@ -0,0 +1,40 @@ +#ifndef GAME_H_ +#define GAME_H_ + +#define FB_BPP 8 + +extern int fb_width; +extern int fb_height; +extern long fb_size; +extern void *fb_pixels, *vmem; + +extern long time_msec; + +/* special keys */ +enum { + KB_ESC = 27, + KB_BACKSP = 127, + + KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4, + KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9, + KB_NUM_DOT, KB_NUM_DIV, KB_NUM_MUL, KB_NUM_MINUS, KB_NUM_PLUS, KB_NUM_ENTER, KB_NUM_EQUALS, + KB_UP, KB_DOWN, KB_RIGHT, KB_LEFT, + KB_INSERT, KB_HOME, KB_END, KB_PGUP, KB_PGDN, + KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, + KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12, + KB_F13, KB_F14, KB_F15, + KB_NUMLK, KB_CAPSLK, KB_SCRLK, + KB_RSHIFT, KB_LSHIFT, KB_RCTRL, KB_LCTRL, KB_RALT, KB_LALT, + KB_RMETA, KB_LMETA, KB_LSUPER, KB_RSUPER, KB_MODE, KB_COMPOSE, + KB_HELP, KB_PRINT, KB_SYSRQ, KB_BREAK +}; + +int game_init(int argc, char **argv); +void game_cleanup(void); + +void game_draw(void); + +void game_keyboard(int key, int press); + + +#endif /* GAME_H_ */ diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..1fa83c3 --- /dev/null +++ b/src/timer.h @@ -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/util.c b/src/util.c new file mode 100644 index 0000000..a91bb95 --- /dev/null +++ b/src/util.c @@ -0,0 +1,3 @@ +#include "util.h" + +uint32_t perf_start_count, perf_interval_count; diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..6d869eb --- /dev/null +++ b/src/util.h @@ -0,0 +1,105 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include "inttypes.h" + +#ifdef __GNUC__ +#define INLINE __inline +#define PACKED __attribute__((packed)) + +#elif defined(__WATCOMC__) +#define INLINE __inline +#define PACKED + +#else +#define INLINE +#define PACKED +#endif + +/* 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; +} + +extern uint32_t perf_start_count, perf_interval_count; + +#ifdef __WATCOMC__ +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__ +#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 +#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_ */ -- 1.7.10.4