--- /dev/null
+-x c
+-Isrc
+-Isrc/dos
+-Isrc/dos/stl
--- /dev/null
+*.swp
+*.o
+*.obj
+*.OBJ
+*.exe
+*.EXE
+*.lnk
+*.LNK
+*.log
+*.LOG
--- /dev/null
+baseobj = main.obj
+demoobj = demo.obj
+sysobj = gfx.obj vbe.obj dpmi.obj timer.obj keyb.obj logger.obj
+obj = $(baseobj) $(demoobj) $(sysobj)
+bin = demo.exe
+
+opt = -5 -fp5 -otexan
+dbg = -d1
+
+AS = nasm
+CC = wcc386
+CXX = wpp386
+ASFLAGS = -fobj
+CFLAGS = $(dbg) $(opt) -zq -bt=dos -Isrc -Isrc\dos
+CXXFLAGS = $(CFLAGS)
+LD = wlink
+
+$(bin): $(obj)
+ %write objects.lnk system dos4g file { $(obj) }
+ $(LD) debug all name $@ @objects $(LDFLAGS)
+
+.c: src;src/dos
+.cc: src;src/dos
+.asm: src;src/dos
+
+.c.obj: .autodepend
+ $(CC) -fo=$@ $(CFLAGS) $[*
+
+.cc.obj: .autodepend
+ $(CXX) -fo=$@ $(CXXFLAGS) $[*
+
+.asm.obj:
+ $(AS) $(ASFLAGS) -o $@ $[*.asm
+
+clean: .symbolic
+ del *.obj
+ del $(bin)
--- /dev/null
+Cross-compile on GNU/Linux
+--------------------------
+
+source owdev file with contents (change WATCOM var as necessary):
+
+ export WATCOM=$HOME/devel/ow
+ export PATH=$WATCOM/binl:$PATH
+ export INCLUDE=$WATCOM/h:$INCLUDE
+
+Run wmake to build. Needs dos4gw.exe and wstub.exe in current dir or PATH
+
+Simply running ./demo.exe will invoke magic of the ancients to start wine,
+which will in turn start dosbox, which will execute the DOS binary!
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include "demo.h"
+
+int fbwidth = 320;
+int fbheight = 240;
+int fbbpp = 8;
+unsigned char *fbpixels;
+unsigned long time_msec;
+
+static unsigned long nframes;
+
+int demo_init(int argc, char **argv)
+{
+ return 0;
+}
+
+void demo_cleanup(void)
+{
+ if(time_msec) {
+ float fps = (float)nframes / ((float)time_msec / 1000.0f);
+ printf("average framerate: %.1f\n", fps);
+ }
+}
+
+void demo_draw(void)
+{
+ int i, j;
+ unsigned char *fbptr = fbpixels;
+
+ for(i=0; i<fbheight; i++) {
+ for(j=0; j<fbwidth; j++) {
+ int val = i^j;
+
+ *fbptr++ = val;
+ }
+ }
+
+ ++nframes;
+}
+
+void demo_keyboard(int key, int state)
+{
+ if(state) {
+ switch(key) {
+ case 27:
+ demo_quit();
+ break;
+
+ default:
+ break;
+ }
+ }
+}
--- /dev/null
+#ifndef DEMO_H_
+#define DEMO_H_
+
+extern int fbwidth, fbheight, fbbpp;
+extern unsigned char *fbpixels;
+extern unsigned long time_msec;
+
+int demo_init(int argc, char **argv);
+void demo_cleanup(void);
+
+void demo_draw(void);
+
+void demo_keyboard(int key, int state);
+
+/* defined in main_*.c */
+void demo_quit(void);
+unsigned long get_msec(void);
+void set_palette(int idx, int r, int g, int b);
+
+#endif /* DEMO_H_ */
--- /dev/null
+/*
+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/>.
+*/
+#include "dpmi.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
+ }
+}
--- /dev/null
+#ifndef DPMI_H_
+#define DPMI_H_
+
+#include "inttypes.h"
+
+struct dpmi_real_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;
+};
+
+unsigned short dpmi_alloc(unsigned int par);
+#pragma aux dpmi_alloc = \
+ "mov eax, 0x100" \
+ "int 0x31" \
+ value[ax] parm[ebx];
+
+void dpmi_real_int(int inum, struct dpmi_real_regs *regs);
+
+void *dpmi_mmap(uint32_t phys_addr, unsigned int size);
+void dpmi_munmap(void *addr);
+
+#endif /* DPMI_H_ */
--- /dev/null
+#ifndef GFX_H_
+#define GFX_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "vbe.h"
+#include "dpmi.h"
+#include "logger.h"
+
+#define REALPTR(s, o) (void*)(((uint32_t)(s) << 4) + (uint32_t)(o))
+#define VBEPTR(x) REALPTR(((x) & 0xffff0000) >> 16, (x) & 0xffff)
+#define VBEPTR_SEG(x) (((x) & 0xffff0000) >> 16)
+#define VBEPTR_OFF(x) ((x) & 0xffff)
+
+#define SAME_BPP(a, b) \
+ ((a) == (b) || (a) == 16 && (b) == 15 || (a) == 15 && (b) == 16 || (a) == 32 && (b) == 24 || (a) == 24 && (b) == 32)
+
+static unsigned int make_mask(int sz, int pos);
+
+static struct vbe_info *vbe_info;
+static struct vbe_mode_info *mode_info;
+static int pal_bits = 6;
+
+void *set_video_mode(int xsz, int ysz, int bpp)
+{
+ int i;
+ uint16_t *modes, best = 0;
+ unsigned int fbsize;
+
+ /* check for VBE2 support and output some info */
+ if(!vbe_info) {
+ if(!(vbe_info = vbe_get_info())) {
+ fprintf(stderr, "VESA BIOS Extensions not available\n");
+ return 0;
+ }
+
+ printlog("VBE Version: %x.%x\n", vbe_info->version >> 8, vbe_info->version & 0xff);
+ if(vbe_info->version < 0x200) {
+ fprintf(stderr, "This program requires VBE 2.0 or greater. Try running UniVBE\n");
+ return 0;
+ }
+
+ printlog("Graphics adapter: %s, %s (%s)\n", VBEPTR(vbe_info->oem_vendor_name_ptr),
+ VBEPTR(vbe_info->oem_product_name_ptr), VBEPTR(vbe_info->oem_product_rev_ptr));
+ printlog("Video memory: %dkb\n", vbe_info->total_mem << 6);
+
+ modes = VBEPTR(vbe_info->vid_mode_ptr);
+ }
+
+ for(i=0; i<1024; i++) { /* impose an upper limit to avoid inf-loops */
+ if(modes[i] == 0xffff) {
+ break; /* reached the end */
+ }
+
+ mode_info = vbe_get_mode_info(modes[i] | VBE_MODE_LFB);
+ if(!mode_info || mode_info->xres != xsz || mode_info->yres != ysz) {
+ continue;
+ }
+ if(SAME_BPP(mode_info->bpp, bpp)) {
+ best = modes[i];
+ }
+ }
+
+ if(best) {
+ mode_info = vbe_get_mode_info(best);
+ } else {
+ fprintf(stderr, "Requested video mode (%dx%d %dbpp) is unavailable\n", xsz, ysz, bpp);
+ return 0;
+ }
+
+ if(vbe_set_mode(best | VBE_MODE_LFB) == -1) {
+ fprintf(stderr, "Failed to set video mode %dx%d %dbpp\n", mode_info->xres, mode_info->yres, mode_info->bpp);
+ return 0;
+ }
+
+ /* attempt to set 8 bits of color per component in palettized modes */
+ /*if(bpp <= 8) {
+ pal_bits = vbe_set_palette_bits(8);
+ printlog("palette bits per color primary: %d\n", pal_bits);
+ }
+ */
+
+ fbsize = xsz * ysz * mode_info->num_img_pages * (bpp / CHAR_BIT);
+ return (void*)dpmi_mmap(mode_info->fb_addr, fbsize);
+}
+
+int set_text_mode(void)
+{
+ vbe_set_mode(0x3);
+ return 0;
+}
+
+int get_color_depth(void)
+{
+ if(!mode_info) {
+ return -1;
+ }
+ return mode_info->bpp;
+}
+
+int get_color_bits(int *rbits, int *gbits, int *bbits)
+{
+ if(!mode_info) {
+ return -1;
+ }
+ *rbits = mode_info->rmask_size;
+ *gbits = mode_info->gmask_size;
+ *bbits = mode_info->bmask_size;
+ return 0;
+}
+
+int get_color_mask(unsigned int *rmask, unsigned int *gmask, unsigned int *bmask)
+{
+ if(!mode_info) {
+ return -1;
+ }
+ *rmask = make_mask(mode_info->rmask_size, mode_info->rpos);
+ *gmask = make_mask(mode_info->gmask_size, mode_info->gpos);
+ *bmask = make_mask(mode_info->bmask_size, mode_info->bpos);
+ return 0;
+}
+
+int get_color_shift(int *rshift, int *gshift, int *bshift)
+{
+ if(!mode_info) {
+ return -1;
+ }
+ *rshift = mode_info->rpos;
+ *gshift = mode_info->gpos;
+ *bshift = mode_info->bpos;
+ return 0;
+}
+
+void set_palette(int idx, int r, int g, int b)
+{
+ int col[3];
+ col[0] = r;
+ col[1] = g;
+ col[2] = b;
+ vbe_set_palette(idx, col, 1, pal_bits);
+}
+
+void wait_vsync(void)
+{
+ __asm {
+ mov dx, 0x3da
+ l1:
+ in al, dx
+ and al, 0x8
+ jnz l1
+ l2:
+ in al, dx
+ and al, 0x8
+ jz l2
+ }
+}
+
+static unsigned int make_mask(int sz, int pos)
+{
+ unsigned int i, mask = 0;
+
+ for(i=0; i<sz; i++) {
+ mask |= 1 << i;
+ }
+ return mask << pos;
+}
+
+
+#endif /* GFX_H_ */
--- /dev/null
+/*
+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 GFX_H_
+#define GFX_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *set_video_mode(int xsz, int ysz, int bpp);
+int set_text_mode(void);
+
+int get_color_depth(void);
+int get_color_bits(int *rbits, int *gbits, int *bbits);
+int get_color_shift(int *rshift, int *gshift, int *bshift);
+int get_color_mask(unsigned int *rmask, unsigned int *gmask, unsigned int *bmask);
+
+void set_palette(int idx, int r, int g, int b);
+
+void wait_vsync(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_H_ */
--- /dev/null
+/*
+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 INT_TYPES_H_
+#define INT_TYPES_H_
+
+#if defined(__DOS__) || defined(WIN32)
+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 unsigned long intptr_t;
+#else
+#include <stdint.h>
+#endif
+
+#endif /* INT_TYPES_H_ */
--- /dev/null
+/*
+DOS interrupt-based keyboard driver.
+Copyright (C) 2013 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 the program. If not, see <http://www.gnu.org/licenses/>
+*/
+#define KEYB_C_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <conio.h>
+#include <dos.h>
+#include <i86.h>
+#include "keyb.h"
+#include "scancode.h"
+
+#define KB_INTR 0x9
+#define KB_PORT 0x60
+
+#define PIC1_CMD_PORT 0x20
+#define OCW2_EOI (1 << 5)
+
+#define DONE_INIT (prev_handler)
+
+static void __interrupt __far kbintr();
+
+static void (__interrupt __far *prev_handler)();
+
+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();
+ prev_handler = _dos_getvect(KB_INTR);
+ _dos_setvect(KB_INTR, kbintr);
+ _enable();
+
+ return 0;
+}
+
+void kb_shutdown(void)
+{
+ if(!DONE_INIT) {
+ return;
+ }
+
+ /* restore the original interrupt handler */
+ _disable();
+ _dos_setvect(KB_INTR, prev_handler);
+ _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];
+ }
+ return keystate[key];
+}
+
+void kb_wait(void)
+{
+ int key;
+ while((key = kb_getkey()) == -1) {
+ /* put the processor to sleep while waiting for keypresses, but first
+ * make sure interrupts are enabled, or we'll sleep forever
+ */
+ __asm {
+ sti
+ hlt
+ }
+ }
+ 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 __far kbintr()
+{
+ unsigned char code;
+ int key, 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];
+
+ if(press) {
+ /* append to buffer */
+ last_key = key;
+ if(buffer_size > 0) {
+ buffer[buf_widx] = key;
+ 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 */
+}
--- /dev/null
+/*
+DOS interrupt-based keyboard driver.
+Copyright (C) 2013 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 the program. If not, see <http://www.gnu.org/licenses/>
+*/
+#ifndef KEYB_H_
+#define KEYB_H_
+
+#define KB_ANY (-1)
+#define KB_ALT (-2)
+#define KB_CTRL (-3)
+#define KB_SHIFT (-4)
+
+/* special keys */
+enum {
+ KB_LALT, KB_RALT,
+ KB_LCTRL, KB_RCTRL,
+ KB_LSHIFT, KB_RSHIFT,
+ 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_CAPSLK, KB_NUMLK, KB_SCRLK, KB_SYSRQ,
+ KB_ESC = 27,
+ KB_INSERT, KB_DEL, KB_HOME, KB_END, KB_PGUP, KB_PGDN,
+ KB_LEFT, KB_RIGHT, KB_UP, KB_DOWN,
+ KB_NUM_DOT, KB_NUM_ENTER, KB_NUM_PLUS, KB_NUM_MINUS, KB_NUM_MUL, KB_NUM_DIV,
+ 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_BACKSP = 127
+};
+
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KEYB_H_ */
--- /dev/null
+/*
+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/>.
+*/
+#include <stdio.h>
+#include <stdarg.h>
+#include "logger.h"
+
+#define LOGFNAME "demo.log"
+
+static FILE *logfile;
+
+void logger_output(FILE *fp)
+{
+ if(logfile) fclose(logfile);
+ logfile = fp;
+}
+
+void printlog(const char *fmt, ...)
+{
+ va_list ap;
+
+ if(!logfile) {
+ if(!(logfile = fopen(LOGFNAME, "w"))) {
+ return;
+ }
+ setvbuf(logfile, 0, _IOLBF, 0);
+ }
+
+ va_start(ap, fmt);
+ vfprintf(logfile, fmt, ap);
+ va_end(ap);
+}
--- /dev/null
+/*
+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 LOGGER_H_
+#define LOGGER_H_
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void logger_output(FILE *fp);
+void printlog(const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LOGGER_H_ */
--- /dev/null
+#include <stdio.h>
+#include "demo.h"
+#include "keyb.h"
+#include "timer.h"
+#include "gfx.h"
+
+static int quit;
+
+int main(int argc, char **argv)
+{
+ init_timer(100);
+ kb_init(32);
+
+ if(!(fbpixels = set_video_mode(fbwidth, fbheight, fbbpp))) {
+ return 1;
+ }
+
+ if(demo_init(argc, argv) == -1) {
+ set_text_mode();
+ return 1;
+ }
+ reset_timer();
+
+ while(!quit) {
+ int key;
+ while((key = kb_getkey()) != -1) {
+ demo_keyboard(key, 1);
+ }
+ if(quit) goto break_evloop;
+
+ wait_vsync();
+ time_msec = get_msec();
+ demo_draw();
+ }
+
+break_evloop:
+ set_text_mode();
+ demo_cleanup();
+ kb_shutdown();
+ return 0;
+}
+
+void demo_quit(void)
+{
+ quit = 1;
+}
--- /dev/null
+/*
+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_ */
--- /dev/null
+/*
+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 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 */
+};
--- /dev/null
+/*
+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/>.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <conio.h>
+#include <dos.h>
+#include <i86.h>
+#include "pit8254.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);
+static void __interrupt __far timer_irq();
+static void __interrupt __far dos_timer_intr();
+
+static void (__interrupt __far *prev_timer_intr)();
+
+static 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;
+ prev_timer_intr = _dos_getvect(inum);
+ _dos_setvect(inum, timer_irq);
+ } else {
+ tick_interval = 55;
+
+ inum = DOS_TIMER_INTR;
+ prev_timer_intr = _dos_getvect(inum);
+ _dos_setvect(inum, dos_timer_intr);
+ }
+ _enable();
+
+ atexit(cleanup);
+}
+
+static void cleanup(void)
+{
+ if(!prev_timer_intr) {
+ 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 */
+ _dos_setvect(inum, prev_timer_intr);
+ _enable();
+}
+
+void reset_timer(void)
+{
+ ticks = 0;
+}
+
+unsigned long get_msec(void)
+{
+ return ticks * tick_interval;
+}
+
+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);
+}
+
+static void __interrupt __far dos_timer_intr()
+{
+ ticks++;
+ _chain_intr(prev_timer_intr); /* DOES NOT RETURN */
+}
+
+/* first PIC command port */
+#define PIC1_CMD 0x20
+/* end of interrupt control word */
+#define OCW2_EOI (1 << 5)
+
+static void __interrupt __far timer_irq()
+{
+ static unsigned long dos_ticks;
+
+ ticks++;
+
+ 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 */
+ }
+
+ /* send EOI to the PIC */
+ outp(PIC1_CMD, OCW2_EOI);
+}
--- /dev/null
+/*
+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 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_ */
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "vbe.h"
+#include "dpmi.h"
+
+/* VGA DAC registers used for palette setting in 8bpp modes */
+#define VGA_DAC_STATE 0x3c7
+#define VGA_DAC_ADDR_RD 0x3c7
+#define VGA_DAC_ADDR_WR 0x3c8
+#define VGA_DAC_DATA 0x3c9
+
+#define MODE_LFB (1 << 14)
+
+
+struct vbe_info *vbe_get_info(void)
+{
+ static unsigned short info_block_seg;
+ static struct vbe_info *info;
+ struct dpmi_real_regs regs;
+
+ if(!info) {
+ /* allocate 32 paragraphs (512 bytes) */
+ info_block_seg = dpmi_alloc(32);
+ info = (struct vbe_info*)(info_block_seg << 4);
+ }
+
+ memcpy(info->sig, "VBE2", 4);
+
+ memset(®s, 0, sizeof regs);
+ regs.es = info_block_seg;
+ regs.eax = 0x4f00;
+
+ dpmi_real_int(0x10, ®s);
+
+ return info;
+}
+
+struct vbe_mode_info *vbe_get_mode_info(int mode)
+{
+ static unsigned short mode_info_seg;
+ static struct vbe_mode_info *mi;
+ struct dpmi_real_regs regs;
+
+ if(!mi) {
+ /* allocate 16 paragraphs (256 bytes) */
+ mode_info_seg = dpmi_alloc(16);
+ mi = (struct vbe_mode_info*)(mode_info_seg << 4);
+ }
+
+ memset(®s, 0, sizeof regs);
+ regs.es = mode_info_seg;
+ regs.eax = 0x4f01;
+ regs.ecx = mode;
+ regs.es = mode_info_seg;
+
+ dpmi_real_int(0x10, ®s);
+ if(regs.eax & 0xff00) {
+ return 0;
+ }
+
+ return mi;
+}
+
+int vbe_set_mode(int mode)
+{
+ struct dpmi_real_regs regs;
+
+ memset(®s, 0, sizeof regs);
+ regs.eax = 0x4f02;
+ regs.ebx = mode;
+ dpmi_real_int(0x10, ®s);
+
+ if(regs.eax == 0x100) {
+ return -1;
+ }
+ return 0;
+}
+
+int vbe_set_palette_bits(int bits)
+{
+ struct dpmi_real_regs regs;
+
+ memset(®s, 0, sizeof regs);
+ regs.eax = 0x4f08;
+ regs.ebx = bits << 8; /* bits in bh */
+ dpmi_real_int(0x10, ®s);
+
+ if((regs.eax >> 8) & 0xff == 3) {
+ return -1;
+ }
+ return regs.ebx >> 8 & 0xff; /* new color bits in bh */
+}
+
+/* TODO: implement palette setting through the VBE2 interface for
+ * non-VGA displays (actually don't).
+ */
+void vbe_set_palette(int idx, int *col, int count, int bits)
+{
+ int i, shift = 8 - bits;
+
+ __asm {
+ mov dx, VGA_DAC_ADDR_WR
+ mov eax, idx
+ out dx, al
+ }
+
+ for(i=0; i<count; i++) {
+ unsigned char r = *col++;
+ unsigned char g = *col++;
+ unsigned char b = *col++;
+
+ if(shift) {
+ r >>= shift;
+ g >>= shift;
+ b >>= shift;
+ }
+
+ __asm {
+ mov dx, VGA_DAC_DATA
+ mov al, r
+ out dx, al
+ mov al, g
+ out dx, al
+ mov al, b
+ out dx, al
+ }
+ }
+}
+
+static unsigned int get_mask(int sz, int pos)
+{
+ unsigned int i, mask = 0;
+
+ for(i=0; i<sz; i++) {
+ mask |= 1 << i;
+ }
+ return mask << pos;
+}
+
+void print_mode_info(FILE *fp, struct vbe_mode_info *mi)
+{
+ fprintf(fp, "resolution: %dx%d\n", mi->xres, mi->yres);
+ fprintf(fp, "color depth: %d\n", mi->bpp);
+ fprintf(fp, "mode attributes: %x\n", mi->mode_attr);
+ fprintf(fp, "bytes per scanline: %d\n", mi->scanline_bytes);
+ fprintf(fp, "number of planes: %d\n", (int)mi->num_planes);
+ fprintf(fp, "number of banks: %d\n", (int)mi->num_banks);
+ fprintf(fp, "mem model: %d\n", (int)mi->mem_model);
+ fprintf(fp, "red bits: %d (mask: %x)\n", (int)mi->rmask_size, get_mask(mi->rmask_size, mi->rpos));
+ fprintf(fp, "green bits: %d (mask: %x)\n", (int)mi->gmask_size, get_mask(mi->gmask_size, mi->gpos));
+ fprintf(fp, "blue bits: %d (mask: %x)\n", (int)mi->bmask_size, get_mask(mi->bmask_size, mi->bpos));
+ fprintf(fp, "framebuffer address: %x\n", mi->fb_addr);
+}
--- /dev/null
+#ifndef VBE_H_
+#define VBE_H_
+
+#include "inttypes.h"
+
+#define VBE_ATTR_LFB (1 << 7)
+#define VBE_MODE_LFB (1 << 14)
+
+#pragma pack (push, 0)
+struct vbe_info {
+ uint8_t sig[4];
+ uint16_t version;
+ uint32_t oem_str_ptr;
+ uint8_t caps[4]; /* capabilities */
+ uint32_t vid_mode_ptr; /* vbefarptr to video mode list */
+ uint16_t total_mem; /* num of 64k mem blocks */
+ uint16_t oem_sw_rev; /* VBE implementation software revision */
+ uint32_t oem_vendor_name_ptr;
+ uint32_t oem_product_name_ptr;
+ uint32_t oem_product_rev_ptr;
+ uint8_t reserved[222];
+ uint8_t oem_data[256];
+};
+
+struct vbe_mode_info {
+ uint16_t mode_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 rmask_size, rpos;
+ uint8_t gmask_size, gpos;
+ uint8_t bmask_size, bpos;
+ uint8_t xmask_size, 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 reserved2;
+ uint16_t reserved3;
+
+ uint8_t reserved4[206];
+};
+#pragma pack (pop)
+
+struct vbe_info *vbe_get_info(void);
+struct vbe_mode_info *vbe_get_mode_info(int mode);
+
+int vbe_set_mode(int mode);
+
+int vbe_set_palette_bits(int bits);
+void vbe_set_palette(int idx, int *col, int count, int bits);
+
+void print_mode_info(FILE *fp, struct vbe_mode_info *modei);
+
+#endif /* VBE_H_ */