initial commit master
authorJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 17 Jan 2023 08:35:38 +0000 (10:35 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 17 Jan 2023 08:35:38 +0000 (10:35 +0200)
29 files changed:
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
src/client.c [new file with mode: 0644]
src/client.h [new file with mode: 0644]
src/dos/dpmi.c [new file with mode: 0644]
src/dos/dpmi.h [new file with mode: 0644]
src/dos/gfx.c [new file with mode: 0644]
src/dos/keyb.c [new file with mode: 0644]
src/dos/mouse.c [new file with mode: 0644]
src/dos/pit8254.h [new file with mode: 0644]
src/dos/scancode.h [new file with mode: 0644]
src/dos/timer.c [new file with mode: 0644]
src/dos/vbe.c [new file with mode: 0644]
src/dos/vbe.h [new file with mode: 0644]
src/gfx.h [new file with mode: 0644]
src/inttypes.h [new file with mode: 0644]
src/keyb.h [new file with mode: 0644]
src/logger.c [new file with mode: 0644]
src/logger.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/mouse.h [new file with mode: 0644]
src/proto.c [new file with mode: 0644]
src/proto.h [new file with mode: 0644]
src/server.c [new file with mode: 0644]
src/server.h [new file with mode: 0644]
src/timer.h [new file with mode: 0644]
src/util.c [new file with mode: 0644]
src/util.h [new file with mode: 0644]
src/xtypes.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3a1841b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,37 @@
+mainobj = main.obj logger.obj client.obj server.obj proto.obj util.obj
+sysobj = gfx.obj vbe.obj dpmi.obj timer.obj mouse.obj keyb.obj
+obj = $(mainobj) $(sysobj)
+bin = xdos.exe
+
+opt = -5 -fp5 -otexan
+dbg = -d2
+
+AS = nasm
+CC = wcc386
+CXX = wpp386
+ASFLAGS = -fobj
+CFLAGS = $(dbg) $(opt) -zq -bt=dos -mf -Isrc -Isrc\dos -I$(%WATT_ROOT)\inc
+CXXFLAGS = $(CFLAGS) -Isrc\stl
+LD = wlink
+LDFLAGS = library $(%WATT_ROOT)\lib\wattcpwf.lib
+
+$(bin): $(obj)
+       %write objects.lnk file { $(obj) }
+       $(LD) debug all name $@ @objects $(LDFLAGS)
+
+.c: src;src\dos
+.cc: src
+.asm: src
+
+.c.obj: .autodepend
+       $(CC) $(CFLAGS) $[*
+
+.cc.obj: .autodepend
+       $(CXX) $(CXXFLAGS) $[*
+
+.asm.obj:
+       $(AS) $(ASFLAGS) -o $@ $[*.asm
+
+clean: .symbolic
+       del *.obj
+       del $(bin)
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..0ff1c6a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+build instructions
+------------------
+- make sure you have WATT_TCP32 installed and you've set the WATT_ROOT env var
+  to point to its location (default: c:\net\watt).
diff --git a/src/client.c b/src/client.c
new file mode 100644 (file)
index 0000000..4f23def
--- /dev/null
@@ -0,0 +1,171 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include "proto.h"
+#include "client.h"
+#include "logger.h"
+
+static int handle_client_setup(struct client *c);
+static int send_server_info(struct client *c);
+
+static struct client *clist;
+
+int add_client(int s)
+{
+       struct client *c = malloc(sizeof *c);
+       if(!c) {
+               printlog("failed to allocate memory for new client\n");
+               return -1;
+       }
+       memset(c, 0, sizeof *c);
+       c->sock = s;
+       c->state = CLIENT_SETUP;
+
+       c->next = clist;
+       clist = c;
+       return 0;
+}
+
+int remove_client(int s)
+{
+       struct client dummy;
+       struct client *iter = &dummy;
+
+       dummy.next = clist;
+       while(iter->next) {
+               if(iter->next->sock == s) {
+                       struct client *tmp = iter->next;
+                       iter->next = tmp->next;
+                       free(tmp);
+                       return 0;
+               }
+               iter = iter->next;
+       }
+       return -1;
+}
+
+/* remove all clients with closed invalid sockets */
+int remove_closed(void)
+{
+       int removed = 0;
+       struct client dummy;
+       struct client *iter = &dummy;
+       dummy.next = clist;
+
+       while(iter->next) {
+               if(iter->next->sock == -1) {
+                       struct client *tmp = iter->next;
+                       iter->next = tmp->next;
+                       free(tmp);
+                       ++removed;
+               } else {
+                       iter = iter->next;
+               }
+       }
+       return removed;
+}
+
+struct client *find_client_sock(int s)
+{
+       struct client *c = clist;
+       while(c && c->sock != s) {
+               c = c->next;
+       }
+       return c;
+}
+
+struct client *get_clients(void)
+{
+       return clist;
+}
+
+void free_clients(void)
+{
+       while(clist) {
+               struct client *c = clist;
+               clist = clist->next;
+               free(c);
+       }
+       clist = 0;
+}
+
+/* --- client comms --- */
+
+/* handles all communications with clients */
+int handle_client(struct client *c)
+{
+       if(c->state == CLIENT_SETUP) {
+               if(handle_client_setup(c) == -1) {
+                       return -1;
+               }
+               if(c->bufsz <= 0) {     /* consumed all data, done */
+                       return 0;
+               }
+       }
+
+       /* TODO handle client requests ... */
+       c->bufsz = 0;
+       return 0;
+}
+
+/* handles data coming from a client in the CLIENT_SETUP state */
+static int handle_client_setup(struct client *c)
+{
+       int rd, auth_name_len, auth_data_len, fullsz, must_swap = 0;
+       struct xconn_setup_header *setup;
+       char *start = c->inbuf + c->bufsz;
+       int avail = CLIENT_BUF_SIZE - c->bufsz;
+
+       if((rd = recv(c->sock, start, avail, 0)) <= 0) {
+               return -1;
+       }
+
+       c->bufsz += rd;
+       if(c->bufsz < sizeof(struct xconn_setup_header)) {
+               return 0;       /* need more data */
+       }
+
+       setup = (struct xconn_setup_header*)c->inbuf;
+       if(setup->byteorder == 'B') {
+               must_swap = 1;
+               auth_name_len = ntohs(setup->auth_name_length);
+               auth_data_len = ntohs(setup->auth_data_length);
+       } else {
+               must_swap = 0;
+               auth_name_len = setup->auth_name_length;
+               auth_data_len = setup->auth_data_length;
+       }
+
+       /* take padding to the next 32bit-aligned address into account */
+       auth_name_len = (auth_name_len + 3) & 0xfffc;
+       auth_data_len = (auth_data_len + 3) & 0xfffc;
+       fullsz = sizeof(struct xconn_setup_header) + auth_name_len + auth_data_len;
+
+       if(c->bufsz < fullsz) {
+               return 0;       /* more to read... */
+       }
+
+       /* ok we've read it all, act on it... */
+       c->swap = must_swap;
+       if(must_swap) {
+               printlog("big endian client, protocol: %d.%d\n", ntohs(setup->proto_major), ntohs(setup->proto_minor));
+       } else {
+               printlog("little endian client, protocol: %d.%d\n", setup->proto_major, setup->proto_minor);
+       }
+       /* TODO send response */
+       c->state = CLIENT_ACTIVE;
+
+       if(c->bufsz > fullsz) {
+               int rem = c->bufsz - fullsz;
+               memmove(c->inbuf, c->inbuf + fullsz, rem);
+               c->bufsz = rem;
+       }
+       return 0;
+}
+
+static int send_server_info(struct client *c)
+{
+       struct xconn_accept_header *hdr;
+
+       return -1;      /* TODO */
+}
diff --git a/src/client.h b/src/client.h
new file mode 100644 (file)
index 0000000..03bce5b
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef CLIENT_H_
+#define CLIENT_H_
+
+enum client_state {
+       CLIENT_SETUP,   /* setup stage: waiting for the initial byteorder/version data */
+       CLIENT_ACTIVE,  /* main stage: accepting requests and sending events */
+       CLIENT_TEARDOWN /* TBD */
+};
+
+#define CLIENT_BUF_SIZE                256
+
+struct client {
+       int sock;
+       int swap;       /* true if we must byteswap all data to/from this client */
+
+       enum client_state state;
+
+       char inbuf[CLIENT_BUF_SIZE];
+       int bufsz;
+
+       struct client *next;
+};
+
+/* functions to manage the client list */
+int add_client(int s);
+int remove_client(int s);
+int remove_closed(void);
+
+struct client *find_client_sock(int s);        /* find by socket */
+struct client *get_clients(void);
+
+void free_clients(void);
+
+/* functions to handle client communictation */
+int handle_client(struct client *c);
+
+#endif /* CLIENT_H_ */
diff --git a/src/dos/dpmi.c b/src/dos/dpmi.c
new file mode 100644 (file)
index 0000000..7d2c2b5
--- /dev/null
@@ -0,0 +1,55 @@
+#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
+       }
+}
diff --git a/src/dos/dpmi.h b/src/dos/dpmi.h
new file mode 100644 (file)
index 0000000..352b9c1
--- /dev/null
@@ -0,0 +1,26 @@
+#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_ */
diff --git a/src/dos/gfx.c b/src/dos/gfx.c
new file mode 100644 (file)
index 0000000..b8c0517
--- /dev/null
@@ -0,0 +1,158 @@
+#ifndef GFX_H_
+#define GFX_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vbe.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: %dmb\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)
+{
+       /* TODO */
+}
+
+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_ */
diff --git a/src/dos/keyb.c b/src/dos/keyb.c
new file mode 100644 (file)
index 0000000..9ec20a9
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+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 */
+}
diff --git a/src/dos/mouse.c b/src/dos/mouse.c
new file mode 100644 (file)
index 0000000..18fa24a
--- /dev/null
@@ -0,0 +1,93 @@
+#include "mouse.h"
+#include "inttypes.h"
+
+#define INTR   0x33
+
+#define QUERY  0
+#define SHOW   1
+#define HIDE   2
+#define READ   3
+#define WRITE  4
+#define PIXRATE        0xf
+
+#define XLIM   7
+#define YLIM   8
+
+int have_mouse(void)
+{
+       uint16_t res = 0;
+       _asm {
+               mov eax, QUERY
+               int INTR
+               mov res, ax
+       }
+       return res;
+}
+
+void show_mouse(int show)
+{
+       uint16_t cmd = show ? SHOW : HIDE;
+       _asm {
+               mov ax, cmd
+               int INTR
+       }
+}
+
+int read_mouse(int *xp, int *yp)
+{
+       uint16_t x, y, state;
+       _asm {
+               mov eax, READ
+               int INTR
+               mov state, bx
+               mov x, cx
+               mov y, dx
+       }
+
+       if(xp) *xp = x;
+       if(yp) *yp = y;
+       return state;
+}
+
+void set_mouse(int x, int y)
+{
+       _asm {
+               mov eax, WRITE
+               mov ecx, x
+               mov edx, y
+               int INTR
+       }
+}
+
+void set_mouse_limits(int xmin, int ymin, int xmax, int ymax)
+{
+       _asm {
+               mov eax, XLIM
+               mov ecx, xmin
+               mov edx, xmax
+               int INTR
+               mov eax, YLIM
+               mov ecx, ymin
+               mov edx, ymax
+               int INTR
+       }
+}
+
+void set_mouse_rate(int xrate, int yrate)
+{
+       _asm {
+               mov ax, PIXRATE
+               mov ecx, xrate
+               mov edx, yrate
+               int INTR
+       }
+}
+
+void set_mouse_mode(enum mouse_mode mode)
+{
+       if(mode == MOUSE_GFX) {
+               set_mouse_rate(1, 1);
+       } else {
+               set_mouse_rate(8, 16);
+       }
+}
diff --git a/src/dos/pit8254.h b/src/dos/pit8254.h
new file mode 100644 (file)
index 0000000..5d45f55
--- /dev/null
@@ -0,0 +1,34 @@
+#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 (file)
index 0000000..d012f14
--- /dev/null
@@ -0,0 +1,17 @@
+#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 */
+};
diff --git a/src/dos/timer.c b/src/dos/timer.c
new file mode 100644 (file)
index 0000000..ab0027a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+pit8254 timer code for DOS programs.
+Copyright (C) 2011-2014  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"
+
+#ifdef BORLANDC
+#error borland unsupported
+#endif
+
+#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);
+}
diff --git a/src/dos/vbe.c b/src/dos/vbe.c
new file mode 100644 (file)
index 0000000..5182e0a
--- /dev/null
@@ -0,0 +1,153 @@
+#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(&regs, 0, sizeof regs);
+       regs.es = info_block_seg;
+       regs.eax = 0x4f00;
+
+       dpmi_real_int(0x10, &regs);
+
+       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(&regs, 0, sizeof regs);
+       regs.es = mode_info_seg;
+       regs.eax = 0x4f01;
+       regs.ecx = mode;
+       regs.es = mode_info_seg;
+
+       dpmi_real_int(0x10, &regs);
+       if(regs.eax & 0xff00) {
+               return 0;
+       }
+
+       return mi;
+}
+
+int vbe_set_mode(int mode)
+{
+       struct dpmi_real_regs regs;
+
+       memset(&regs, 0, sizeof regs);
+       regs.eax = 0x4f02;
+       regs.ebx = mode;
+       dpmi_real_int(0x10, &regs);
+
+       if(regs.eax == 0x100) {
+               return -1;
+       }
+       return 0;
+}
+
+int vbe_set_palette_bits(int bits)
+{
+       struct dpmi_real_regs regs;
+
+       memset(&regs, 0, sizeof regs);
+       regs.eax = 0x4f08;
+       regs.ebx = bits << 8;   /* bits in bh */
+       dpmi_real_int(0x10, &regs);
+
+       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);
+}
diff --git a/src/dos/vbe.h b/src/dos/vbe.h
new file mode 100644 (file)
index 0000000..d29052e
--- /dev/null
@@ -0,0 +1,70 @@
+#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(unsigned int mode);
+
+int vbe_set_mode(unsigned 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_ */
diff --git a/src/gfx.h b/src/gfx.h
new file mode 100644 (file)
index 0000000..b19c992
--- /dev/null
+++ b/src/gfx.h
@@ -0,0 +1,24 @@
+#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_ */
diff --git a/src/inttypes.h b/src/inttypes.h
new file mode 100644 (file)
index 0000000..1742166
--- /dev/null
@@ -0,0 +1,18 @@
+#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_ */
diff --git a/src/keyb.h b/src/keyb.h
new file mode 100644 (file)
index 0000000..3007ac5
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+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_ */
diff --git a/src/logger.c b/src/logger.c
new file mode 100644 (file)
index 0000000..f1873cb
--- /dev/null
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include "logger.h"
+
+#define LOGFNAME       "xdos.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);
+       fflush(logfile);
+}
diff --git a/src/logger.h b/src/logger.h
new file mode 100644 (file)
index 0000000..aec7d3b
--- /dev/null
@@ -0,0 +1,17 @@
+#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_ */
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..1c3f3ec
--- /dev/null
@@ -0,0 +1,141 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include "proto.h"
+#include "client.h"
+#include "logger.h"
+#include "keyb.h"
+
+static int init(void);
+static void cleanup(void);
+static void handle_key(int key);
+
+static int lis;        /* the listening socket */
+
+int main(int argc, char **argv)
+{
+       if(init() == -1) {
+               return 1;
+       }
+
+       main_loop();
+
+       cleanup();
+       return 0;
+}
+
+static int init(void)
+{
+       struct sockaddr_in sa;
+
+       /* initialize input */
+       if(kb_init(32) == -1) {
+               printlog("failed to initialize keyboard driver\n");
+               return -1;
+       }
+
+       /* start network server */
+       if((lis = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+               printlog("failed to create listening socket\n");
+               return -1;
+       }
+
+       memset(&sa, 0, sizeof sa);
+       sa.sin_family = AF_INET;
+       sa.sin_addr.s_addr = INADDR_ANY;
+       sa.sin_port = htons(6000);
+
+       if(bind(lis, (struct sockaddr*)&sa, sizeof sa) == -1) {
+               printlog("failed to bind socket to port 6000\n");
+               return -1;
+       }
+       listen(lis, 8);
+
+       return 0;
+}
+
+static void cleanup(void)
+{
+       struct client *iter = get_clients();
+
+       while(iter) {
+               close(iter->sock);
+               iter = iter->next;
+       }
+       free_clients();
+
+       kb_shutdown();
+}
+
+static int main_loop(void)
+{
+       for(;;) {
+               int key;
+               int s, maxfd;
+               struct client *c;
+               struct timeval tv = {0, 0};
+               fd_set rdset;
+
+               /* ctrl-alt-backspace quits */
+               if(kb_isdown(KB_ALT) && kb_isdown(KB_CTRL) && kb_isdown('\b')) {
+                       return 0;
+               }
+
+               /* get any key events and process them */
+               while((key = kb_getkey()) != -1) {
+                       handle_key(key);
+               }
+
+               /* construct the socket set of all open sockets */
+               FD_ZERO(&rdset);
+               FD_SET(lis, &rdset);
+               maxfd = lis;
+
+               c = get_clients();
+               while(c) {
+                       FD_SET(c->sock, &rdset);
+                       if(c->sock > maxfd)
+                               maxfd = c->sock;
+                       c = c->next;
+               }
+
+               if(select_s(maxfd + 1, &rdset, 0, 0, &tv) == -1) {
+                       continue;
+               }
+
+               /* check for any unread messages from clients and call handle_client */
+               c = get_clients();
+               while(c) {
+                       if(FD_ISSET(c->sock, &rdset)) {
+                               if(handle_client(c) == -1) {
+                                       close(c->sock);
+                                       c->sock = -1;   /* this effectively marks it for removal */
+                               }
+                       }
+                       c = c->next;
+               }
+
+               /* accept any new incoming connections on the listening socket */
+               if(FD_ISSET(lis, &rdset)) {
+                       struct sockaddr_in addr;
+                       int addrlen;
+
+                       if((s = accept(lis, (struct sockaddr*)&addr, &addrlen)) == -1) {
+                               printlog("failed to accept new connection\n");
+                       } else {
+                               printlog("new client from host: %s\n", inet_ntoa(addr.sin_addr));
+
+                               add_client(s);
+                       }
+               }
+
+               /* remove any clients with closed sockets */
+               remove_closed();
+       }
+}
+
+static void handle_key(int key)
+{
+}
diff --git a/src/mouse.h b/src/mouse.h
new file mode 100644 (file)
index 0000000..7dd17b8
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef MOUSE_H_
+#define MOUSE_H_
+
+enum {
+       MOUSE_LEFT              = 1,
+       MOUSE_RIGHT             = 2,
+       MOUSE_MIDDLE    = 4
+};
+
+enum mouse_mode {
+       MOUSE_GFX,
+       MOUSE_TEXT
+};
+
+#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);
+void set_mouse_mode(enum mouse_mode mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MOUSE_H_ */
diff --git a/src/proto.c b/src/proto.c
new file mode 100644 (file)
index 0000000..949cb94
--- /dev/null
@@ -0,0 +1,33 @@
+#include "proto.h"
+
+static uint16_t swap16(uint16_t x)
+{
+       return ((x & 0xff) << 8) | ((x & 0xff00) >> 8);
+}
+
+static uint32_t swap32(uint32_t x)
+{
+       return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) |
+               ((x & 0xff000000) >> 24);
+}
+
+#define SWAP16(x)      ((x) = swap16(x))
+#define SWAP32(x)      ((x) = swap32(x))
+
+void swap_xconn_accept(struct xconn_accept_header *hdr)
+{
+       SWAP16(hdr->proto_major);
+       SWAP16(hdr->proto_minor);
+       SWAP16(hdr->add_length);
+       SWAP32(hdr->release);
+       SWAP32(hdr->resid_base);
+       SWAP32(hdr->resid_mask);
+       SWAP32(hdr->motionbuf_size);
+       SWAP16(hdr->vendor_length);
+       SWAP16(hdr->max_req_length);
+       /* TODO swap pixel formats and screens */
+}
+
+void swap_xreq_header(struct xrequest_header *hdr)
+{
+}
diff --git a/src/proto.h b/src/proto.h
new file mode 100644 (file)
index 0000000..10a7aee
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef PROTO_H_
+#define PROTO_H_
+
+#include "xtypes.h"
+
+#define PROTO_MAJOR            11
+#define PROTO_MINOR            0
+
+struct xconn_setup_header {
+       char byteorder, unused1;
+       CARD16 proto_major, proto_minor;
+       CARD16 auth_name_length;
+       CARD16 auth_data_length;
+       CARD16 unused2;
+       /* then followed by:
+        * - n auth_name bytes
+        * - pad to next 32bit-aligned address
+        * - m auth_data bytes
+        * - pad to next 32bit-aligned address
+        */
+};
+
+struct xconn_accept_header {
+       CARD8 success, unused1;
+       CARD16 proto_major, proto_minor;
+       CARD16 add_length;      /* 8+2*numfmt+(vendor_len+pad+m?)/4 */
+       CARD32 release;
+       CARD32 resid_base, resid_mask;
+       CARD32 motionbuf_size;
+       CARD16 vendor_length;
+       CARD16 max_req_length;
+       CARD8 num_screens;
+       CARD8 num_pixfmt;
+       CARD8 img_byteorder;
+       CARD8 bm_fmt_bitorder;
+       CARD8 bm_fmt_scanline_unit;
+       CARD8 bm_fmt_scanline_pad;
+       KEYCODE min_keycode, max_keycode;
+       CARD32 unused2;
+       /* followed by:
+        * - vendor string of size vendor_length
+        * - pad to next 32bit-aligned address
+        * - 8*num_pixfmt pixel formats
+        * - list of screens (multiple of 4?)
+        */
+};
+
+struct xformat {
+       CARD8 depth;
+       CARD8 bpp;
+       CARD8 scanline_pad;
+       char unused[5];
+};
+
+struct xrequest_header {
+       CARD8 major;
+       CARD8 data;
+       CARD16 length;
+};
+
+struct xreply {
+       CARD32 length;
+       CARD16 req;
+       char data[28];
+};
+
+struct xerror {
+       CARD8 error;
+       CARD8 code;
+       CARD16 seqnum;
+       char data[28];
+};
+
+struct xevent {
+       CARD8 code;
+       CARD8 detail;
+       CARD16 seqnum;
+       char data[28];
+};
+
+/* in-place byteorder swapping functions for all protocol structures */
+void swap_xconn_accept(struct xconn_accept_header *hdr);
+
+void swap_xreq_header(struct xrequest_header *hdr);
+/* TODO: all possible requests */
+
+#endif /* PROTO_H_ */
diff --git a/src/server.c b/src/server.c
new file mode 100644 (file)
index 0000000..c9e421c
--- /dev/null
@@ -0,0 +1,71 @@
+#include <string.h>
+#include "server.h"
+#include "proto.h"
+#include "util.h"
+#include "client.h"
+
+#define NUM_SCR                1
+#define NUM_PIXFMT     1
+static const char *vendor = "John Tsiombikas";
+
+static int calc_add_size(void);
+static int calc_info_size(void);
+
+int server_info(void *buf, int sz)
+{
+       struct xconn_accept_header *info;
+       char *vendptr;
+       struct xformat *pixfmt;
+       int addsz = calc_add_size();
+       int infosz = addsz + sizeof *info;
+       int vendsz = strlen(vendor);
+
+       if(!buf) return infosz;
+       if(sz < infosz) return -1;
+
+       info = buf;
+       vendptr = (char*)buf + sizeof *info;
+       pixfmt = (struct xformat*)(vendptr + padded_size(vendsz));
+
+       info->success = 1;
+       info->proto_major = PROTO_MAJOR;
+       info->proto_minor = PROTO_MINOR;
+       info->add_length = addsz / 4;   /* in words */
+       info->release = 1;
+       info->resid_base = 0;
+       info->resid_mask = 0xffff;
+       info->motionbuf_size = 0;
+       info->vendor_length = vendsz;
+       info->max_req_length = CLIENT_BUF_SIZE;
+       info->num_screens = NUM_SCR;
+       info->num_pixfmt = NUM_PIXFMT;
+       info->img_byteorder = 0;        /* LSB first */
+       info->bm_fmt_bitorder = 0;      /* least significant */
+       info->bm_fmt_scanline_unit = 1; /* TODO */
+       info->bm_fmt_scanline_pad = 0;
+       info->min_keycode = 0;
+       info->max_keycode = 0xff;
+
+       memcpy(vendptr, vendor, vendsz);
+
+       pixfmt->depth = 24;
+       pixfmt->bpp = 24;
+       pixfmt->scanline_pad = 0;
+
+       return infosz;
+}
+
+static int calc_add_size(void)
+{
+       int ven_len = strlen(vendor);
+       int ven_pad = padding(ven_len);
+       int max_scr = padded_size(NUM_SCR);     /* must be multiple of 4 */
+
+       /* additional data size */
+       return 32 + 8 * NUM_PIXFMT + ven_len + ven_pad + max_scr;
+}
+
+static int calc_info_size(void)
+{
+       return sizeof(struct xconn_accept_header) + calc_add_size();
+}
diff --git a/src/server.h b/src/server.h
new file mode 100644 (file)
index 0000000..0d793ee
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef SERVER_H_
+#define SERVER_H_
+
+/* if buf is null, returns the size required for the server info data.
+ * if buf is not null and sz is less than the required size, returns -1.
+ * otherwise fills the buffer with the server info data and returns the size
+ */
+int server_info(void *buf, int sz);
+
+#endif /* SERVER_H_ */
diff --git a/src/timer.h b/src/timer.h
new file mode 100644 (file)
index 0000000..1164792
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+pit8254 timer code for DOS programs.
+Copyright (C) 2011-2014  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);
+unsigned long get_ticks(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TIMER_H_ */
diff --git a/src/util.c b/src/util.c
new file mode 100644 (file)
index 0000000..7b55924
--- /dev/null
@@ -0,0 +1,11 @@
+#include "util.h"
+
+int padded_size(int sz)
+{
+       return (sz + 3) & 0xfffffffc;
+}
+
+int padding(int sz)
+{
+       return padded_size(sz) - sz;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..2ee1fb0
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef UTIL_H_
+#define UTIL_H_
+
+/* calculates the padded size of an object of size "sz" */
+int padded_size(int sz);
+
+/* calculates the padding required for 32bit alignment after an object
+ * of size "sz" assuming itself is also aligned.
+ */
+int padding(int sz);
+
+#endif /* UTIL_H_ */
diff --git a/src/xtypes.h b/src/xtypes.h
new file mode 100644 (file)
index 0000000..9c63eae
--- /dev/null
@@ -0,0 +1,143 @@
+#ifndef XTYPES_H_
+#define XTYPES_H_
+
+#include "inttypes.h"
+
+typedef int8_t INT8;
+typedef int16_t INT16;
+typedef int32_t INT32;
+
+#ifndef BYTE
+typedef uint8_t BYTE;
+#endif
+
+typedef uint8_t CARD8;
+typedef uint16_t CARD16;
+typedef uint32_t CARD32;
+
+typedef CARD32 BITMASK;
+typedef CARD32 WINDOW;
+typedef CARD32 PIXMAP;
+typedef CARD32 CURSOR;
+typedef CARD32 FONT;
+typedef CARD32 GCONTEXT;
+typedef CARD32 COLORMAP;
+typedef CARD32 DRAWABLE;
+typedef CARD32 FONTABLE;
+typedef CARD32 ATOM;
+typedef CARD32 VISUALID;
+typedef CARD32 TIMESTAMP;
+typedef CARD32 KEYSYM;
+typedef CARD8  KEYCODE;
+typedef CARD8  BUTTON;
+
+typedef CARD8* STRING8;
+
+/* BITGRAVITY, WINGRAVITY */
+#define Forget         0
+#define Unmap          0
+#define NorthWest      1
+#define North          2
+#define NorthEast      3
+#define West           4
+#define Center         5
+#define East           6
+#define SouthWest      7
+#define South          8
+#define SouthEast      9
+#define Static         10
+
+#define False  0
+#define True   1
+
+/* SETofEVENT, SETofPOINTEREVENT, SETofDEVICEEVENT bitmasks */
+#define KeyPress                               0x00000001
+#define KeyRelease                             0x00000002
+#define ButtonPress                            0x00000004
+#define ButtonRelease                  0x00000008
+#define EnterWindow                            0x00000010
+#define LeaveWindow                            0x00000020
+#define PointerMotion                  0x00000040
+#define PointerMotionHint              0x00000080
+#define Button1Motion                  0x00000100
+#define Button2Motion                  0x00000200
+#define Button3Motion                  0x00000400
+#define Button4Motion                  0x00000800
+#define Button5Motion                  0x00001000
+#define ButtonMotion                   0x00002000
+#define KeymapState                            0x00004000
+#define Exposure                               0x00008000
+#define VisibilityChange               0x00010000
+#define StructureNotify                        0x00020000
+#define ResizeRedirect                 0x00040000
+#define SubstructureNotify             0x00080000
+#define SubstructureRedirect   0x00100000
+#define FocusChange                            0x00200000
+#define PropertyChange                 0x00400000
+#define ColormapChange                 0x00800000
+#define OwnerGrabButton                        0x01000000
+
+#define SetOfEventUnused               0xfe000000
+#define SetOfPointerEventUnused        0xffff8003
+#define SetOfDeviceEventUnused 0xffffc0b0
+
+/* SETofKEYBUTMASK, SETofKEYMASK bitmasks */
+#define Shift                  0x0001
+#define Lock                   0x0002
+#define Control                        0x0004
+#define Mod1                   0x0008
+#define Mod2                   0x0010
+#define Mod3                   0x0020
+#define Mod4                   0x0040
+#define Mod5                   0x0080
+#define Button1                        0x0100
+#define Button2                        0x0200
+#define Button3                        0x0400
+#define Button4                        0x0800
+#define Button5                        0x1000
+
+#define SetOfKeyButMaskUnused  0xe000
+#define SetOfKeyMaskUnused             0xff00
+
+typedef struct {
+       CARD8 byte1, byte2;
+} CHAR2B;
+
+typedef CHAR2B*        STRING16;
+
+typedef struct {
+       INT16 x, y;
+} POINT;
+
+typedef struct {
+       INT16 x, y;
+       CARD16 width, height;
+} RECTANGLE;
+
+typedef struct {
+       INT16 x, y;
+       CARD16 width, height;
+       INT16 angle1, angle2;
+} ARC;
+
+/* family field of the HOST structure */
+enum {
+       Internet                        = 0,
+       DECnet                          = 1,
+       Chaos                           = 2,
+       ServerInterpreted       = 5,
+       InternetV6                      = 6
+};
+
+typedef struct {
+       CARD8 family, unused1;
+       CARD16 addr_length;
+       BYTE addr[1];
+} HOST;
+
+typedef struct {
+       CARD8 length;
+       STRING8 name;
+} STR;
+
+#endif /* XTYPES_H_ */