Filewatcher File Search
FTP Search
  
Directory (beta)
  
Content Search (beta)
   
pkg://joyce-1.33-1.src.rpm:209516/joyce133.tar.gz  info  downloads

JoyceSdlScr.c100600    764    764       23525  6663351120  11204 0ustar  jcejce/** JOYCE PCW emulator for Linux/SDL *************************/
/**                                                         **/
/** JoyceSdlScr.c: contains the screen drawing code for SDL **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#define SCRMOD
#include "Joyce.h"
#include "Joycevga.h"

#include "mask.xbm"

void flip_xbm(int w, int h, byte * bits);


/* Palette reloading */

void reload_palette(void)
{
	SDL_SetColors(framebuf, colours, 0, 256);
	SDL_MapSurface(framebuf, screen->format);
}


static SDL_Surface *iconsurface;


char InitScr(int type)	/* Initialise the screen */
{
	SDL_Palette *palette;
	int nr, ng, nb;
        static char title[50];

        ScrEnabled = 1;

        if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) 
	{
                joyce_dprintf("Couldn't initialize SDL: %s\n", SDL_GetError());
		return 0;
        }

        /* Have a preference for 8-bit, but accept any depth */
	if (screen == NULL)
	{
	        screen = SDL_SetVideoMode(800, 600, 8, 
                                          SDL_SWSURFACE|SDL_ANYFORMAT);
	}
        if ( screen == NULL ) 
	{
                joyce_dprintf("Couldn't set 800x600x8 video mode: %s\n",
                                                        SDL_GetError());
		return 0;
        }
        joyce_dprintf("Set 800x600 at %d bits-per-pixel mode\n",
                                        screen->format->BitsPerPixel);

	if (framebuf == NULL)
	{
		mustlock = 0;
		if (screen->format->BitsPerPixel == 8)
		{
			framebuf = SDL_AllocSurface(SDL_HWSURFACE, 800, 600, 8, 
                           0, 0, 0, 0);
			if (framebuf == NULL) return 0;
			if (framebuf->flags & SDL_HWSURFACE) mustlock = 1;
		}
		else 
		{
			framebuf = SDL_AllocSurface(SDL_SWSURFACE, 800, 600, 8,
                           0, 0, 0, 0);
			if (framebuf == NULL) return 0;
		}
	}
	context = screen;

	linelen = framebuf->pitch;

/* Init colours */

       /* Get the current colormap */
        palette = framebuf->format->palette;
        if ( palette == NULL ) 
	{        /* The frame buffer is not palettized; which is pretty *
                  * weird, considering  we asked for 8-bit palettized. */
                return 0;
        }
        ncolours = palette->ncolors;
        colours = (SDL_Color *)malloc(ncolours*sizeof(SDL_Color));
        memcpy(colours, palette->colors, ncolours*sizeof(SDL_Color));

        for (nr = 0 ; nr < 256; nr += 85)
                for (ng = 0; ng < 256; ng += 85)
                        for (nb = 0; nb < 256; nb += 85)
                                colour_find(nr, ng, nb);

/* Got the original palette */

        colour_nextfree= 0;
        ScrType        = type;
        rram_base      = PCWRAM;
        scr_h          = 256;
        y_origin       = 0;
        colour_reg     = 0;
        dblk = blk     = colour_find(0, 0, 0);
        dwhi = whi     = colour_find(0xFF, 0xFF, 0xFF);
        colour_nextfree=128;
	reload_palette();

        Border();
        GrClearScreen(blk);
        shadow_invalid = 1;     /* (v1.30) */

/* Set up the colours of JOYCE screen elements, using the "guaranteed" 
  palette */

        border_beep = colour_find(255,   0,   0);
        text_fg     = colour_find(170, 255, 255);
        text_bg     = colour_find(  0,   0, 170);
        debug_fg    = colour_find(  0, 255,   0);
        debug_bg    = colour_find(  0,   0,   0);
        shadow_hi   = colour_find( 85,  85, 170);
        shadow_lo   = colour_find(  0,   0,  85);
	reload_palette();

	if (!iconsurface) 
	{
		flip_xbm(mask_width, mask_height, mask_bits);
		iconsurface = SDL_LoadBMP(LIBROOT "/pcw.bmp");
	}
	if (iconsurface) SDL_WM_SetIcon(iconsurface, mask_bits);
	sprintf(title, "JOYCE v%x.%x", BCDVERS >> 8, BCDVERS & 0xFF);
	SDL_WM_SetCaption(title, "Joyce");

        ScInited = 1;
        if(DebugMode) dcls();
        return 1;
}


void UnitScr(void)
{
        if (ScInited)
        {
		SDL_FreeSurface(framebuf);
		if (iconsurface) SDL_FreeSurface(iconsurface);
		SDL_Quit();
		free(colours);
        }
        ScInited=0;
}


void beep_on(void)
{
	beeperstat = 1;
	if (!jset.beep)		/* v1.20: Using jset.beep==0 not BeepType==1 */ 
	{
		Border();
		DrawScr();
	}
	else			/* v1.20 */
	{
/*		sound(2000);*/	/* Sort of approximately the right pitch, */
	}			/* but I was _not_ going to sit there all */
}				/* evening with two beepers going full */
				/* blast trying to get it right */


void beep_off(void)
{
	beeperstat = 0;
	if (!jset.beep) 	/* v1.20 */
	{
		Border();
		DrawScr();
	}
	else			/* v1.20 */
	{
/*		nosound();*/	/* v1.20 */
	}
}

void screen_on(void)
{
	ScrEnabled = 1;
	shadow_invalid = 1;	/* (v1.30) */
	DrawScr();
}

void screen_off(void)
{
	ScrEnabled = 0;
	DrawScr();
}

void screen_colour(int wr, int wg, int wb, int br, int bg, int bb)
{
	set_ink(&dblk, br, bg, bb);
	set_ink(&dwhi, wr, wg, wb);	
	shadow_invalid = 1;
}


void Border(void)	/* Draw the border in the frame buffer */
{
	int lblk;

	if (ScrType || !ScrEnabled) return;
	if (colour_reg & 0x80)	/* Colours inverted */
	{
		lblk = blk = dwhi;
		whi  = dblk;
	}
	else
	{
		lblk = blk = dblk;
		whi  = dwhi;
	}

	if (!jset.beep && beeperstat) /* v1.20 */
	{
		lblk = border_beep; 
	}

	context = framebuf;
	if (mustlock && SDL_LockSurface(context) < 0 )
	{
		context = screen; 
		return;
	}

	if (colour_reg & 0x40)	/* Border covers screen? */
	{
		if(DebugMode)
		{	
			GrFilledBoxNC(  0,   0, 799,  21, lblk);
			GrFilledBoxNC(  0,  22,  39, 277, lblk);
			GrFilledBoxNC(760,  22, 799, 277, lblk);
			GrFilledBoxNC(  0, 278, 799, 299, lblk);
		}
		else
		{
			GrFilledBoxNC(  0,   0, 799,  43, lblk);
			GrFilledBoxNC(  0,  43,  39, 556, lblk);
			GrFilledBoxNC(760,  43, 799, 556, lblk);
			GrFilledBoxNC(  0, 556, 799, 599, lblk);
		}
	}
	else
	{
		GrFilledBoxNC(0, 0, 800, (DebugMode ? 299 : 599),lblk);
	}
	if (mustlock) SDL_UnlockSurface(context);
	context = screen;
}
	



void BlitBuf(void)		/* Send the Framebuf to video RAM  */
{
	SDL_BlitSurface(framebuf, NULL, screen, NULL);
}


void SaveBuf(void)		/* Save the video RAM to Framebuf */
{
        SDL_MapSurface(screen, framebuf->format);
        SDL_BlitSurface(screen, NULL, framebuf, NULL);
        SDL_MapSurface(framebuf, screen->format);

}



static void DrawLine(unsigned char *bytes, short yoff) /* Draw a PCW line */
{
	int x;
	register int b;
	register byte v;
	long yp;
	byte *data = (byte *)framebuf->pixels;

	if (DebugMode)
	{
		yp = ((yoff+22)*linelen + 40);
		for (x=0;x<90;x++)
		{
			v=(*bytes);
			if (v != screen_shadow[x][yoff] || shadow_invalid) 
								/* (v1.30) */
			{
				screen_shadow[x][yoff] = v; /* (v1.30) */
				for (b=0;b<8;b++)
				{
					data[yp++]=(v & 0x80 ? whi:blk);
					v=(v<<1);
				}
			}
			else yp += 8;
			bytes+=8;
		}

	}
	else
	{
		yp = ((yoff*2+44)*linelen+40);
		for (x=0;x<90;x++)
		{
			v=(*bytes);
			if (v != screen_shadow[x][yoff] || shadow_invalid) /* (v1.30) */
			{
				screen_shadow[x][yoff] = v; /* (v1.30) */
				for (b=0;b<8;b++)
				{
					data[yp++]=(v & 0x80 ? whi:blk);
					v=(v<<1);
				}
			}
			else yp += 8;
			bytes+=8;
		}
		memcpy(data+yp+linelen-720, 
                       data+yp-720, linelen);
	}
}



void DrawScr(void)	/* Draw the PCW screen */
{
	short y;
	register int rrvalue;

	if (ScrType) return;
       
	if (mustlock && SDL_LockSurface(framebuf) < 0 ) return;

/* If you get problems with screen updates uncomment this line
 *
 *	shadow_invalid = 1;
 */
	if (!ScrEnabled)
	{
		memset(framebuf->pixels, dblk, 600*linelen);
		shadow_invalid = 1;	/* (v1.30) */
	}
	else if (colour_reg & 0x40)
	{
		context  = framebuf;
		for (y=0;y<scr_h;y++)
		{
			register short yy=((y+y_origin)&0xFF)<<1;

			rrvalue=rram_base[yy+1];
			rrvalue=rrvalue <<8;
			rrvalue+=rram_base[yy];
			DrawLine(PCWRAM+(rrvalue    << 1) - (rrvalue & 7),y);
		}
		context = screen;
		shadow_invalid = 0;
	}
	if (jstate.popup[0]) 
	{
		context = framebuf;
		DrawPopup();
		context = screen;
	}
	SDL_UnlockSurface(framebuf);
	BlitBuf();
	SDL_UpdateRect(screen, 0, 0, 0, 0);
}



void VidPort(unsigned char port, unsigned char value) /* I/O to a video port */
{
	static int prev_colour_reg = -1;	/* (v1.30) */

	switch(port)
	{
		case 0xF5:
		rram_base=&PCWRAM[(long)value*512L];
		break;

		case 0xF6:	/* Move Y origin */
		y_origin=value;
		break;

		case 0xF7:	/* Set colour register */
		colour_reg=value;
		if (prev_colour_reg != value)
		{
			shadow_invalid = 1;	/* (v1.30) */
			prev_colour_reg = value;
		}			
		Border();
		break;
	}
}

/************************************************************************/
/* Palette management */

int colour_find(int r, int g, int b)    /* Search for a colour - if not */
{                                       /* found, allocate it */
        int n;
        for (n = 0; n < 255; n++)
        {
                if (colours[n].r == r && colours[n].g == g && 
                    colours[n].b == b) return n;
        }
	colours[colour_nextfree].r = r;
	colours[colour_nextfree].g = g;
	colours[colour_nextfree].b = b;

/* The top 128 colours can be freed when we need to */

	n = colour_nextfree;
        ++colour_nextfree;
        if (colour_nextfree == 256) colour_nextfree = 128;
	return n;
}


void GrQueryColor(int n, int *r, int *g, int *b)
{
	*r = colours[n].r;
	*g = colours[n].g;
	*b = colours[n].b;
}


void set_ink(int * ink, int r, int g, int b)    /* Set foreground or background */
{
        int ocol = *ink;

        (*ink) = colour_find(r,g,b);

        if (whi == ocol) whi = (*ink);
        if (blk == ocol) blk = (*ink);

	reload_palette();
}


void flip_xbm(int w, int h, byte * bits)
{
	int n, m;
	byte b, b2;

	for (n = (((w + 7) / 8) * h) - 1; n >= 0; n--)
	{
		b = bits[n];
		b2 = 0;
		for (m = 0; m < 8; m++)
		{
                	b2 = b2 >> 1;
			if (b & 0x80) b2 |= 0x80;
			b  = b  << 1;
		}
		bits[n] = b2;
	}
}
JoyceSdlKbd.c100600    764    764       21746  6656157262  11175 0ustar  jcejce/** JOYCE PCW emulator for Linux/SDL *************************/
/**                                                         **/
/** JoyceSdlKbd.c: contains the SDL interface               **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/


#include "Joyce.h"

volatile char keyboard_map[17];
volatile char keyboard_flag;

/* NOTE: This won't compile out of the box on SDL 0.8.10. You need to add
  the keys SDLK_HASH, SDLK_LSUPER and SDLK_RSUPER */

byte sdl_keyboard_xlt[] = { 
	SDLK_BACKSPACE, 9, 128, 9, 128,
	SDLK_TAB,       8, 16,  8, 16,
//	SDLK_CLEAR,
	SDLK_RETURN,    2, 4,   2, 4, 
	SDLK_ESCAPE,    1, 1,   1, 1,
	SDLK_SPACE,     5, 128, 5, 128,
	SDLK_QUOTE,     3, 16,  3, 16,
	SDLK_COMMA,     4, 128, 4, 128,
	SDLK_MINUS,     3, 2,   3, 2,
	SDLK_PERIOD,    3, 128, 3, 128,
	SDLK_SLASH,     3, 64,  3, 64,
	SDLK_0,         4, 1,   4, 1,
	SDLK_1,         8, 1,   8, 1,
	SDLK_2,         8, 2,   8, 2,
 	SDLK_3,         7, 2,   7, 2,
	SDLK_4,         7, 1,   7, 1,
	SDLK_5,         6, 2,   6, 2,
	SDLK_6,         6, 1,   6, 1,
	SDLK_7,         5, 2,   5, 2,
	SDLK_8,         5, 1,   5, 1,
	SDLK_9,         4, 2,   4, 2,
	SDLK_BACKSLASH, 2, 64,  2, 64,
	SDLK_SEMICOLON, 3, 32,  3, 32,
	SDLK_EQUALS,    3, 1,   3, 1,
	SDLK_KP0,       0, 2,   0, 2,
	SDLK_KP1,       2, 16,  1, 128,
	SDLK_KP2,       10,64,  0, 128,
	SDLK_KP3,       0, 16,  0, 64,
	SDLK_KP4,       1,128,  1, 32,
	SDLK_KP5,       0,128,  1, 64,
	SDLK_KP6,       0, 64,  0, 32,
	SDLK_KP7,       1, 32,  2, 16,
	SDLK_KP8,       1, 64,  1, 16,
	SDLK_KP9,       0, 32,  0, 16,
	SDLK_KP_PERIOD, 10, 4, 10, 64,
	SDLK_KP_DIVIDE,  3,64,  3, 64,
	SDLK_KP_MULTIPLY,1, 2,  1,  2,
	SDLK_KP_MINUS,   1,128, 1, 32,
	SDLK_KP_PLUS,    2,128, 2,128,
	SDLK_KP_ENTER,   10,32,10, 32,
//	SDLK_KP_EQUALS = 86,
	SDLK_LEFTBRACKET,3,  4, 3,  4,
	SDLK_HASH,        2, 8, 2,  8,
	SDLK_RIGHTBRACKET,2, 2, 2,  2,
	SDLK_BACKQUOTE,   8, 4, 8,  4,
	SDLK_a,           8,32, 8, 32,
	SDLK_b,           6,64, 6, 64,
	SDLK_c,          7, 64, 7, 64,
	SDLK_d,          7, 32, 7, 32,
	SDLK_e,          7,  4, 7,  4,
	SDLK_f,          6, 32, 6, 32,
	SDLK_g,          6, 16, 6, 16,
	SDLK_h,          5, 16, 5, 16,
	SDLK_i,          4,  8, 4,  8,
	SDLK_j,          5, 32, 5, 32,
	SDLK_k,          4, 32, 4, 32,
	SDLK_l,          4, 16, 4, 16,
	SDLK_m,          4, 64, 4, 64,
	SDLK_n,          5, 64, 5, 64,
	SDLK_o,          4,  4, 4,  4,
	SDLK_p,          3,  8, 3,  8,
	SDLK_q,          8,  8, 8,  8,
	SDLK_r,          6,  4, 6,  4,
	SDLK_s,          7, 16, 7, 16,
	SDLK_t,          6,  8, 6,  8,
	SDLK_u,          5,  4, 5,  4,
	SDLK_v,          6,128, 6,128,
	SDLK_w,          7,  8, 7,  8,
	SDLK_x,          7,128, 7,128,
	SDLK_y,          5,  8, 5,  8,
	SDLK_z,          8,128, 8,128,
	SDLK_DELETE,     2,  1, 2,  1,
	SDLK_F1,         0,  4, 0,  4,
	SDLK_F2,         0,  4, 0,  4,
	SDLK_F3,         0,  1, 0,  1,
	SDLK_F4,         0,  1, 0,  1,
	SDLK_F5,        10,  1,10,  1,
	SDLK_F6,        10,  1,10,  1,
	SDLK_F7,        10, 16,10, 16,
	SDLK_F8,        10, 16,10, 16,
	SDLK_F9,        16,  1,16,  1,	// Joyce actions
	SDLK_F10,       16,  2,16,  2,	// Joyce exit
	SDLK_F11,        2,128, 2,128,
	SDLK_F12,       10,  8,10,  8,
	SDLK_F13,       10,  2,10,  2,
	SDLK_F14,       10,  2,10,  2,
	SDLK_F15,       16,  1,16,  1,
	SDLK_PAUSE,     16,  1,16,  1,
	SDLK_NUMLOCK,    0,  2, 0,  2,
	SDLK_UP,         1, 64, 1, 64,
	SDLK_DOWN,      10, 64,10, 64,
	SDLK_RIGHT,      0, 64, 0, 64,
	SDLK_LEFT,       1,128, 1,128,
	SDLK_INSERT,     1,  4, 1,  4,
	SDLK_HOME,       1,  8, 1,  8,
	SDLK_END,        9,  8, 9,  8,
	SDLK_PAGEUP,     0,  8, 0,  8,
	SDLK_PAGEDOWN,   1, 16, 1, 16,
	SDLK_CAPSLOCK,   10,32,10, 32,
	SDLK_SCROLLOCK,  8, 64, 8, 64,
	SDLK_RSHIFT,     2, 32, 2, 32,
	SDLK_LSHIFT,     2, 32, 2, 32,
	SDLK_RCTRL,      10, 2, 10, 2,
	SDLK_LCTRL,      10, 2, 10, 2,
	SDLK_RALT,       10,128,10,128,
	SDLK_LALT,       10,128,10,128,
	SDLK_RMETA,      10,128,10,128,
	SDLK_LMETA,      10,128,10,128,
//	SDLK_HELP = 164,
	SDLK_PRINT,      1, 2, 1, 2,  
	SDLK_SYSREQ,     1, 2, 1, 2,
	SDLK_MENU,       16,1,16, 1,
	SDLK_BREAK,      16,2,16, 2,
//	SDLK_EURO,     
	SDLK_POWER,      16,2,16,2, 
	SDLK_LSUPER,     10,2,10,2,
	SDLK_RSUPER,     10,2,10,2,
	0
};

static byte keyb_map[1024];

SDL_Event ev;

// Event loop...
void eventloop(void)
{
	static int aushift = 0, trushift = 0;
	int evid = SDL_PollEvent(&ev);
	byte *km;

	while (evid)
	{
		if (ev.type == SDL_KEYDOWN)
		{
			if (ev.key.keysym.sym == SDLK_F2 ||
                            ev.key.keysym.sym == SDLK_F4 ||
                            ev.key.keysym.sym == SDLK_F6 ||
                            ev.key.keysym.sym == SDLK_F8)  aushift = 1; 

			if (ev.key.keysym.sym == SDLK_LSHIFT ||
                            ev.key.keysym.sym == SDLK_RSHIFT) trushift = 1;
		}

		if (ev.type == SDL_KEYUP)
		{
                        if (ev.key.keysym.sym == SDLK_F2 ||
                            ev.key.keysym.sym == SDLK_F4 ||
                            ev.key.keysym.sym == SDLK_F6 ||
                            ev.key.keysym.sym == SDLK_F8)  aushift = 0;

			if (ev.key.keysym.sym == SDLK_LSHIFT ||
                            ev.key.keysym.sym == SDLK_RSHIFT) trushift = 0;
		}

		switch(ev.type)
		{

			case SDL_QUIT: 
				keyboard_map[0x10] |= 2; 
				break; /* Quit key */
			case SDL_KEYDOWN:
				km = keyb_map + 4 * ev.key.keysym.sym;
				keyboard_map[km[0]] |= km[1];
				keyboard_flag = 1;
				break;
			case SDL_KEYUP:
				km = keyb_map + 4 * ev.key.keysym.sym;
				keyboard_map[km[0]] &= ~km[1];
				keyboard_flag = 1;
				break;
			case SDL_MOUSEMOTION:
		                if (tracking) track_mouse(ev.motion.x, ev.motion.y);
				break;
		}
	switch(ev.type)
	{
		case SDL_KEYUP:
		case SDL_KEYDOWN:
		if (trushift || aushift) keyboard_map[2] |= 32;
		else		 	 keyboard_map[2] &= ~32;
		break;
	}
	evid = SDL_PollEvent(&ev);
	}	
}

static void rebuild_map(void)
{
	int n;
	byte *b;
	
	memset(keyb_map, 0, sizeof(keyb_map));
	
	for (b = sdl_keyboard_xlt; *b; b += 5)
	{
		n = b[0];
		keyb_map[n*4]   = b[1];		
		keyb_map[n*4+1] = b[2];		
		keyb_map[n*4+2] = b[3];		
		keyb_map[n*4+3] = b[4];
	}		
}

int InitKbd(void) 
{
	rebuild_map();
	KeySet();
	return 1;
}              

void UnitKbd() { }


void keyboard_chain(int toggle) 
{
//        SDL_EventState(SDL_KEYDOWN, SDL_IGNORE);
//        SDL_EventState(SDL_KEYUP, SDL_IGNORE);
}


void AssignKey(byte keynum, byte l, byte h)
{
	int n = 4 * keynum;
	keyb_map[n]   = l;
	keyb_map[n+1] = h;
	keyb_map[n+2] = l;
	keyb_map[n+3] = h;
}

void KeyboardED(Z80 *R)
{
	int n, l, h, k;

	switch(R->BC.B.l)
	{
		case 0:
		R->DE.W &= 0xFF;
		R->HL.B.l = keyb_map[R->DE.W * 4];
		R->HL.B.h = keyb_map[R->DE.W * 4 + 1];
		break;

		case 1:
		AssignKey(R->DE.W & 0xFF, R->HL.B.l, R->HL.B.h);
		break;
		
		case 2:
		for (n = 0; n < R->DE.W; n++)
		{
			k = RdZ80(R->IX.W + 4*n);
			l = RdZ80(R->IX.W + 4*n + 2);
			h = RdZ80(R->IX.W + 4*n + 3);
			
			AssignKey(k, l, h);	
		} 
		break;
	}
}


void PollKbd(char mutex, Z80 *R)
{
        unsigned char keyno;

	eventloop();

        if (!keyboard_flag) return;
        keyboard_flag=0;

        for (keyno=0;keyno<16;keyno++)
        {
                PCWRAM[0xFFF0+keyno]=keyboard_map[keyno];
                PCWRAM[0xFFFD] |= 0x80;
        }

        if ((!mutex) && (keyboard_map[0x10]&1))
        {
                JoyceMenu(R);
                for (keyno=0;keyno<16;keyno++)
                {
                        PCWRAM[0xFFF0+keyno]=0;
                }
                return;
        }
        if (keyboard_map[0x10]&2)
        {
                ExitConf();
                for (keyno=0;keyno<16;keyno++)
                {
                        PCWRAM[0xFFF0+keyno]=0;
                }
                return;
        }
}


static int ALTS[] = { SDLK_LALT,  SDLK_RALT, SDLK_LMETA, SDLK_RMETA, -1 };
static int CTLS[] = { SDLK_LCTRL, SDLK_RCTRL, -1 };
static int BKSP[] = { SDLK_BACKSPACE, -1 };
static int DELS[] = { SDLK_DELETE,    -1 };

static void setRange(int *R, byte a, byte b, byte c, byte d)
{
	int n;

        for (; R[0] >= 0; ++R)
        {
                n = *R; 
                keyb_map[n*4]   = a;
                keyb_map[n*4+1] = b;
                keyb_map[n*4+2] = c;
                keyb_map[n*4+3] = d;
        }

}

void KeySet(void)
{
	if (jset.swap_alt)
	{
		setRange(ALTS, 10,   2, 10,   2);
		setRange(CTLS, 10, 128, 10, 128);
	}
	else
	{
                setRange(ALTS, 10, 128, 10, 128);
                setRange(CTLS, 10,   2, 10,   2);
	}
	if (jset.swap_delete)
	{
		setRange(BKSP, 2,   1, 2,   1);
		setRange(DELS, 9, 128, 9, 128);
	}
	else
	{
		setRange(BKSP, 9, 128, 9, 128);
		setRange(DELS, 2,   1, 2,   1);
	}
}


unsigned char get_keytoken(void)
{
        int evid = SDL_WaitEvent(&ev);

        while (1)
        {
		evid = SDL_WaitEvent(&ev);
                if (ev.type == SDL_KEYDOWN) return ev.key.keysym.sym;
		if (ev.type == SDL_QUIT)    return SDLK_y;
	}
}

JoyceSdlMnu.c100600    764    764       54553  6663343557  11240 0ustar  jcejce/** JOYCE PCW emulator for Linux / SDL ***********************/
/**                                                         **/
/** JOYCEMNU.C - handles the menu system and the debugging  **/
/**             console                                     **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#include "Joyce.h"
#include "Joycevga.h"
#include "Joyceset.h"
#include "keyboard.h"
#include "inline.h"


/* XBM files from xpaint(1) define the image as an array of "unsigned char", 
  which compiles without warnings. XBM files from xv(1) define it as 
  "char", which causes lots of warnings. */

#include "pcwkey.xbm"	/* X-Window bitmap, a graphic format designed to
			 * be used in C source.
			 */

/* Routines for JOYCE to draw on the 800x600 screen. Similar to those in
   JOYCEVGA, but for the menu system. */

static short vid_y=0, vid_x=0;
static text_rv=0; 

/* Set reverse video */

void vid_rv(char r)
{
	if (r && text_rv) return;
	if (!r && !text_rv) return;

	text_rv=text_fg;
	text_fg=text_bg;
	text_bg=text_rv;
	text_rv=r;
}

/* Draw character */

void vid_putch(char c)
{
	byte *sb;
	int ox = vid_x, oy = vid_y, ow = 8, oh = 16;

        if ((mustlock || context == screen) && SDL_LockSurface(context) < 0 ) return;

	if (c==1) vid_rv(0);
	else if (c==2) vid_rv(1);
	else if (c=='\n')
	{
		GrFilledBoxNC(vid_x, vid_y, 799, vid_y + 15, text_bg); /* (v1.20) */

		ox = 0; ow = 800;
 		vid_x=0;
		vid_y+=16;
		if (vid_y>584) vid_y=0;
	}
	else 
	{
		sb=scrfont+16*c;
		draw_bitmap(vid_y, vid_x, 16, 8, sb, text_fg, text_bg);
		vid_x+=8;
		if (vid_x>792)
		{
			vid_x=0;
			vid_y+=16;
			if (vid_y>584) vid_y=0;
		}
	}
	if (context == screen) 
	{
		if (mustlock) SDL_UnlockSurface(context);
		SDL_UpdateRect(context, ox, oy, ow, oh);
	}

}

/* Move cursor */

void vid_at(char y,char x)
{
	vid_y=y*16;
	vid_x=x*8;
}

/* Put string */

void vid_puts(char *s)
{
	while(*s) vid_putch(*s++);	
}

/* Put string at y,x */

void vid_putsxy(char y, char x, char *s)
{
	vid_at(y,x);
	vid_puts(s);
}




void vid_packsxy(char y, char x, char *s)	/* Print text, clear the */
{						/* rest of the line */
	vid_putsxy(y,x,s);

	if ((mustlock || context == screen) && SDL_LockSurface(context) < 0 ) return;
	GrFilledBoxNC((x + strlen(s)) * 8, y * 16, 799, y * 16 + 15, text_bg);	/* v1.20 */
        if (context == screen)
        {
                if (mustlock) SDL_UnlockSurface(context);
                SDL_UpdateRect(context, 0, y * 16, 800, 16);
        }
}



int dgetstr(int y, int x, char *S, int maxc)	/* Input string */
{						
	char c[2];
	int L = 0;

	c[1] = 0;
	while(1)
	{
		c[0] = dgetch();
		if (c[0] == '\n' || c[0] == '\r')
		{
			S[L++] = 0;
			return 1;
		}
		if (c[0] == 0x1B)
		{
			S[L++] = 0;
			return 0;
		}
		if (c[0] == 8 || c[0] == 127)
		{
			if (L > 0)
			{
				--L;
				--x;
				vid_putsxy(y,x," ");
			}
		}
		else
		{
			if (L < maxc - 1)
			{
				S[L++] = c[0];
				vid_putsxy(y,x,c);
				++x;
			}
		}
	}
}		
	


/* Back up screen display before drawing menus etc. on it */

void SaveBuf1(void)
{
	if (ScrType) SaveBuf();
}


static void KeyDraw(void)	/* Draw the keyboard map */
{
	int y,x,b,z=0;
	byte ch;

        if ((mustlock || context == screen) && SDL_LockSurface(context) < 0 ) return;

	for (y = 0; y < pcwkey_height; y++)
		for (x = 0; x < pcwkey_width; x+=8)
		{
			ch = pcwkey_bits[z++];
			for (b = 0; b < 8; b++)
			{
				GrPlotNC(x+b, y+32, (ch & 1) ? text_bg : text_fg);
				ch = (ch >> 1);
			}
		}
        if (context == screen)
        {
                if (mustlock) SDL_UnlockSurface(context);
                SDL_UpdateRect(context, 0, 32, pcwkey_width, pcwkey_height);
        }
}




/* Ask the user if they want to quit. Returns only if they don't */

int Confirm(char *s)
{
	volatile char c;

	vid_rv(0);
	vid_packsxy(0,0,s);
	vid_packsxy(1,0," ");
	while(1)
	{
		c = dgetch();
		if (c == 'Y' || c == 'y')
		{
			return 1;
		}
		if (c == 'N' || c == 'n')
		{
			return 0;
		}
	}
}





void ExitConf(void)
{
	static char mutex=0;

	if (mutex) return;

	mutex=1;
	SaveBuf1();
	if (Confirm("Press Y to quit JOYCE, N to return")) goodbye(99);
	BlitBuf();
        UpdateArea(0,0,0,0);
}

/* For dumping the CPU state */

static void putFlags(char *s, byte F)
{
	strcat(s, F & S_FLAG ? "S" : "s");
	strcat(s, F & Z_FLAG ? "Z" : "z");
	strcat(s, F & H_FLAG ? "H" : "h");
	strcat(s, F & P_FLAG ? "P" : "p");
	strcat(s, F & V_FLAG ? "V" : "v");
	strcat(s, F & N_FLAG ? "N" : "n");
	strcat(s, F & C_FLAG ? "C" : "c");
}

/* Dump the CPU state */

void CPUstate(Z80 *R)
{
	char s[104];
	byte c;
	word t[3];

	t[0] = RdZ80(R->SP.W  ) + (RdZ80(R->SP.W+1) << 8);
	t[1] = RdZ80(R->SP.W+2) + (RdZ80(R->SP.W+3) << 8);
	t[2] = RdZ80(R->SP.W+4) + (RdZ80(R->SP.W+5) << 8);

	sprintf(s,"\1A=%2.2x BC=%4.4x DE=%4.4x HL=%4.4x IX=%4.4x "
                  "IY=%4.4x PC=%4.4x SP=%4.4x -> %4.4x %4.4x %4.4x   ",
		  R->AF.B.h, R->BC.W, R->DE.W, R->HL.W, R->IX.W, R->IY.W, 
                  R->PC.W, R->SP.W, t[1], t[2], t[3]);
	putFlags(s, R->AF.B.l);
	strcat(s,"\n");
	vid_putsxy(0,0,s);
	sprintf(s,"\1A'%2.2x BC'%4.4x DE'%4.4x HL'%4.4x I=%2.2x "
                  "IFF=%2.2x  %2.2x %2.2x %2.2x %2.2x | %2.2x %2.2x "
                  "%2.2x %2.2x %2.2x %2.2x %2.2x     ", R->AF1.B.h, R->BC1.W, 
                  R->DE1.W, R->HL1.W, R->I, R->IFF, RdZ80(R->PC.W-4), 
                  RdZ80(R->PC.W-3), RdZ80(R->PC.W-2), RdZ80(R->PC.W-1), 
                  RdZ80(R->PC.W), RdZ80(R->PC.W+1), RdZ80(R->PC.W+2),
		  RdZ80(R->PC.W+3), RdZ80(R->PC.W+4), RdZ80(R->PC.W+5), 
                  RdZ80(R->PC.W+6) /*" "*/);

	putFlags(s, R->AF1.B.l);
	strcat(s, "    ESC=Exit");
	vid_putsxy(1,0,s);

	c=0;
	while (c != 27) c = dgetch();
}



void CenSettings(void)	/* CEN settings */
{
	char cenport[101];
	int refresh = 0;
	char *str;

	while(1)
	{

		vid_rv(0);
		if (!jset.cen_port & 0x80)
		{
			jset.cen_port = 0x80;
			sprintf(jset.printer_file, "/dev/null");
		}
		sprintf(cenport,"CEN is mapped to file %-77.77s",
					jset.printer_file);
		vid_packsxy(0,0,cenport);
		vid_putsxy(1,0,"\1"
			       "F=Choose file                                     "
			       "                                          ESC=Exit");
		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				case 'F': case 'f':
				vid_packsxy(0,0,"Enter path for file");
				vid_packsxy(1,0,"Press ESC to cancel");
//				vid_rv(1);
//				vid_packsxy(0,22," ");
				str = Itemsel("*.*"); /* v1.20 */			
				if (str)
				{
					strcpy(jset.printer_file, str);
					jset.cen_port |= 0x80;
				}
				refresh = 1;
				break;
			
				case 0x1B:
				PtrSettings();
				return;
			}
		}
	}
}


void ComSettings(void)	/* (v1.31) Com settings */
{
	char comport[101];
	char *str;
	int refresh = 0;

	while(1)
	{

		vid_rv(0);      
		sprintf(comport,"Output to %-40.40s    Input %-40.40s", jset.com_out, jset.com_in);
		vid_packsxy(0,0,comport);
		vid_putsxy(1,0,"\1"
			       "O=Output file  I=Input file                       "
			       "                                          ESC=Exit");
		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				case 'I': case 'i':
                                vid_packsxy(0,0,"Enter path for file");
                                vid_packsxy(1,0,"Press ESC to cancel");
                                str = Itemsel("*"); /* v1.20 */
                                if (str)
                                {
                                        strcpy(jset.com_in, str);
                                }
                                refresh = 1;
                                break;

                                case 'O': case 'o':
                                vid_packsxy(0,0,"Enter path for file");
                                vid_packsxy(1,0,"Press ESC to cancel");
                                str = Itemsel("*"); /* v1.20 */
                                if (str)
                                {
                                        strcpy(jset.com_out, str);
                                }
                                refresh = 1;
                                break;

				case 0x1B:
				set_com();
				return;
			}
		}
	}
}




void KbdSettings(void)	/* Keyboard settings */
{
	int refresh = 0;

	while(1)
	{

		vid_rv(0);
		if (jset.swap_alt) vid_putsxy(0,0, "Host CTRL -> PCW ALT    Host ALT -> PCW EXTRA     ");
		else	           vid_putsxy(0,0, "Host CTRL -> PCW EXTRA  Host ALT -> PCW ALT       ");
                if (jset.swap_delete) 
			vid_packsxy(0, 50, "Backspace and Delete swapped.");
                else 	vid_packsxy(0, 50, "Backspace and Delete not swapped.");

		vid_putsxy(1,0,"\1"
			       "TAB=swap CTRL and ALT  D=swap Backspace and Delete"
			       "                                          ESC=Exit");
		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				case 9:
				jset.swap_alt = !(jset.swap_alt);
				refresh = 1;
				break;

				case 'D': case 'd':
				jset.swap_delete = !(jset.swap_delete);
				refresh = 1;
				break;
			
				case 0x1B:
				KeySet();	/* Write new settings to keyboard */
				return;
			}
		}
	}
}



void BeepSettings(void)	/* v1.20: Beeper settings */
{
	int refresh = 0;

	while(1)
	{

		vid_rv(0);
		if (jset.beep)     vid_packsxy(0,0,"No beeper");
		else	           vid_packsxy(0,0,"Beeper flashes the screen");

		vid_putsxy(1,0,"\1"
			       "TAB to change beeper operation                    "
			       "                                          ESC=Exit");
		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				case 9:
				jset.beep = 1 - jset.beep;
				refresh = 1;
				break;
			
				case 0x1B:
				return;
			}
		}
	}
}










void MouSettings(void)	/* Mouse settings */
{
	int refresh = 0;
	char cbuf[60];

	while(1)
	{

		vid_rv(0);				 
		if (jset.mousemode < 0)   vid_putsxy(0,0,"Mouse disabled");
		else if (!jset.mousemode) vid_putsxy(0,0,"Kempston mouse");
		else			  vid_putsxy(0,0,"AMX mouse     ");		

		if (MouseButtons < 0)       vid_packsxy(0,16,"No host mouse");
		else if (MouseButtons == 3) vid_packsxy(0,16,"Host mouse has 3 buttons - AMX mode recommended");
		else                        vid_packsxy(0,16,"Host mouse has 2 buttons - Kempston mode recommended");

		vid_putsxy(1,0,"\1"
			       "TAB to change mouse mode                          "
			       "                                          ESC=Exit");

		/* (v1.31) avoid divide-by-0 errors */
		if (jset.mxscale == 0) jset.mxscale = 8;
		if (jset.myscale == 0) jset.myscale = 8;

		/* (v1.23) extra mouse dynamics options for AMX */
		sprintf(cbuf,"X speed %d  Y speed %d  Warp speed %d", 
                        256 / jset.mxscale, 256 / jset.myscale, jset.mxspeed);
		if (jset.mousemode == 1) vid_putsxy(1,27,cbuf);

		/* End AMX extras */

		/* (v1.27) Mouse auto-patch */
		sprintf(cbuf, " A = Auto-patch (%s)", jset.patchmouse ? "on" : "off");
		vid_putsxy(0, 79 + jset.patchmouse, cbuf);
		/* End auto-patch */

		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				case 9:
				++jset.mousemode;
				if (jset.mousemode == 2) jset.mousemode = (-1);
				refresh = 1;
				break;
			
				case 'X': case 'x':
				if (jset.mousemode != 1) break;
				--jset.mxscale; if (!jset.mxscale) jset.mxscale = 15;
				refresh = 1;
				break;

				case 'Y': case 'y':
				if (jset.mousemode != 1) break;
				--jset.myscale; if (!jset.myscale) jset.myscale = 15;
				refresh = 1;
				break;

				case 'W': case 'w':
				if (jset.mousemode != 1) break;
				++jset.mxspeed; 
				if (jset.mxspeed > 15) jset.mxspeed = 1;
				jset.myspeed = jset.mxspeed;
				refresh = 1;
				break;

				case 'A': case 'a':
				jset.patchmouse = ((!jset.patchmouse) & 1);
				refresh = 1;
				break;

				case 0x1B:
				return;
			}
		}
	}
}




void DskSettings(void)	/* Disk settings */
{
	int refresh = 0;
	char txt[101];

	while(1)
	{

		vid_rv(0);
		sprintf(txt,"Start up with %s  Boot discs are %s",
			    (jset.one_drive ? "one drive " : "two drives"),
			    (jset.boot_ro   ? "read-only " : "read/write"));
		vid_packsxy(0,0,txt);
		vid_putsxy(1,0,"\1"
			       "1, 2 set number of drives at startup   W=Write Pro"
			       "tect                                      ESC=Exit");
		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				case '1': 
				jset.one_drive = 1;
				refresh = 1;
				break;

				case '2': 
				jset.one_drive = 0;
				refresh = 1;
				break;

				case 'W': case 'w': 
				jset.boot_ro = !(jset.boot_ro);
				refresh = 1;
				break;

				case 0x1B:
				return;
			}
		}
	}
}









void DskImage(void)	/* Insert/remove disc image file */
{
	int refresh;
	static byte cdrv = 0;
	static char ImgPath[4][79];
	char txt[101];

	while(1)
		{
		vid_rv(0);
		vid_putsxy(1,0,"A=Select drive A:   B=Select drive B:   E=Eject   "
			       "I=Insert  W=Write Protect  D=Disable      ESC=Exit");
		
		sprintf(txt,"Drive %c:  %s  %s", (cdrv + 'A'),
				(drive_readonly[cdrv] ? "Read Only " : "Read/Write"),	
				(drive_filename[cdrv] ? drive_filename[cdrv] :
					(drive_readonly[cdrv] ? "[Ejected]" : "[Disabled]")
				)
			);
		vid_packsxy(0,0,txt);

		/* 'Insert' disabled if disc in drive */
		if (drive_filename[cdrv]) vid_putsxy(1,50,"\1        ");	
		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				char *str;

				case 'A': case 'a':
				case 'B': case 'b':
				cdrv = ((c - 'A') & 1);
				refresh = 1;
				break;

				case 'W': case 'w':
				if (drive_filename[cdrv]) 
				{
					drive_readonly[cdrv] ^= 1;
					refresh = 1;
				}				
				break;

				case 'E': case 'e':
				if (drive_filename[cdrv])
				img_eject(cdrv);
				drive_readonly[cdrv] = 1;
				refresh = 1;
				break;

				case 'D': case 'd':
				img_eject(cdrv);
				drive_readonly[cdrv] = 0;
				refresh = 1;
				break;

				case 'I': case 'i':
				if (drive_filename[cdrv]) break;
				vid_packsxy(0,0,"Enter path for image");
				vid_packsxy(1,0,"Press ESC to cancel");
				//vid_rv(1);
				//vid_packsxy(0,22," ");
				str = Itemsel("*.DSK"); /* v1.20 */
				if (str)
				{
					FILE *dfp;	
					strcpy(ImgPath[cdrv], str);	
					drive_filename[cdrv] = ImgPath[cdrv];
					drive_readonly[cdrv] = 0;
		
					dfp = fopen(drive_filename[cdrv], "rb");
					if (!dfp)
					{	
						char c;
						vid_rv(0);
						vid_packsxy(1,0,"This file does not exist. Create it (Y/N)?");
						do {
							c = dgetch();
							if (c == 'N' || c == 'n')
							{
								img_eject(cdrv);
								drive_readonly[cdrv] = 1;
							}
							if (c == 'Y' || c == 'y') 
								if (!img_new(cdrv))
								{
									vid_packsxy(1, 0, "Failed to create file. Press SPACE");
									dgetch();
									img_eject(cdrv);
									drive_readonly[cdrv] = 1;
									c = 'N';
								}
						} while (c != 'Y' && c != 'y' && c != 'n' && c != 'N');
					}
					else fclose(dfp);
				}
				vid_rv(0);
				refresh = 1;
				break;

				case 0x1B:
				return;
			}
		}
	}


}




void Settings(void)	/* The settings menu */
{
	int refresh;
	while(1)
		{
		vid_putsxy(0,0,"\1"
			       "JOYCE settings                                    "
		       	       "                                                  ");
		vid_putsxy(1,0,"\1"
			       "C=CEN port  K=Keyboard  D=Disks   M=Mouse   B=Beep"
			       "er  O=cOm port                    S=Save  ESC=Exit");
		refresh = 0;
		while(!refresh)
		{
			char c = dgetch();
			switch(c)
			{
				case 'C': case 'c':	
				CenSettings();
				refresh = 1;
				break;
			
				case 'K': case 'k':
				KbdSettings();
				refresh = 1;
				break;

				case 'D': case 'd':
				DskSettings();
				refresh = 1;
				break;

				case 'O': case 'o': /* v1.21 */
				ComSettings();
				refresh = 1;
				break;

				case 'M': case 'm':
				MouSettings();
				refresh = 1;
				break;

				case 'B': case 'b':
				BeepSettings();
				refresh = 1;
				break;

				case 'S': case 's':
				if (write_settings())
					vid_putsxy(0,0,"\1Settings were saved    ");
				else	vid_putsxy(0,0,"\1Failed to save settings");
				break;

				case 0x1B:
				return;
			}
		}
	}
}


/* Main menu */

void MenuDraw(void)	/* Draw the menu */
{
	char menustr[100];

	sprintf(menustr,"JOYCE v%x.%02x   Copyright 1996-1999, John Elliott",
			BCDVERS >> 8,
			BCDVERS & 0xFF);
	vid_rv(0);
	vid_packsxy(0,0,menustr);
	vid_packsxy(1,0, "ESC=Return to JOYCE  I=dIsks  S=Settings  B=Boot  "
                         "C=CPU state  f10=Quit  K=Keyboard map");
#ifdef DEBUG				       
	vid_putsxy(1,89,"D=Debug");
#endif
}

void JoyceMenu(Z80 *R)	/* Handle the menu */
{
	static char mutex = 0;
	volatile byte c;

	if (mutex) return;	/* Prevent recursion */

	mutex=1;
	SaveBuf1();
	MenuDraw();
	c=0;
	while (!c)
	{
		c=dgetch();
		switch(c)
		{
			case 'D': case 'd':	/* D = Debug mode */
			DebugMode = !DebugMode;
			if(DebugMode) R->Trace = 1;
			shadow_invalid = 1;	/* Force screen repaint */
			Border();
			dcls();			/* FALL THROUGH */
			
			case 27:		/* ESC */
			BlitBuf();
			UpdateArea(0,0,0,0);
			mutex=0;
			return;
			break;

			case 'B': case 'b':	/* B = Boot */
			if (Confirm("Press Y to reboot the PCW, N to cancel"))
			{
				BlitBuf();
	                        UpdateArea(0,0,0,0);
				mutex=0;
				jstate.reset_flag=1;
				return;
			}
			MenuDraw();
			c=0;
			break;

			case 'I': case 'i':	/* I = disk Images menu */
			DskImage();
			MenuDraw();
			c=0;
			break;

			case 'K': case 'k': 	/* K = show keyboard */
			KeyDraw();
			c=0;
			break;

			case 'S': case 's':	/* S = Settings */
			Settings();
			MenuDraw();
			c=0;
			break;		

			case 'C': case 'c':	/* C = CPU state */
			CPUstate(R);
			MenuDraw();
			c=0;
			break;

			case 10:	/* f10 = Quit */
			if (Confirm("Press Y to quit JOYCE, N to return")) goodbye(99);
			MenuDraw();

			/* FALL THROUGH to */

			default:
			c=0;
			break;
		}
	}
}

char message[2048];		/* Used in joyce_dprintf*() */

static int dy = 19, dx = 0;	/* cursor coords for debugger console */

static void dputch(char c)	/* putch() for the debugger console */
{
	int a, b, x, y;
	int ox, oy, ow = 8, oh = 16;

	if (framebuf == NULL) return;

	oy = y = dy * 16;
	ox = x = dx * 8;
	context = framebuf;

        if ((mustlock || context == screen) && SDL_LockSurface(context) < 0 ) return;

	for (a = 0; a < 16; a++)
	{
		byte v = scrfont[c*16+a];
		for (b = 0; b < 8; b++)
		{
			GrPlotNC(x+b, y+a, (v & 0x80 ? debug_fg : debug_bg));
			v = (v << 1);
		}
	}
        if (context == screen)
        {
                if (mustlock) SDL_UnlockSurface(context);
                SDL_UpdateRect(context, ox, oy, ow, oh);
        }
	context = screen;
}


static void dputs(char *str)	/* puts() for the debugger console */
{
	int p = 0;
	while (str[p])
	{
		if (str[p] == '\n') { dx = 0; ++dy; }
		else if (str[p] == 8) { if (dx > 0) --dx; }
		else	
		{
			dputch(str[p]);		
			dx++;
			if (dx > 100) { dx = 0; ++dy; }
		}
		if (dy > 36)
		{
			SDL_Rect sr, dr;

			sr.x = dr.x = 0;
			dr.y = 300; 
                        sr.y = 316;
			sr.w = dr.w = 800;
			sr.h = dr.h = 284;

			if (framebuf == NULL) return;

		        SDL_MapSurface(framebuf, framebuf->format);
			SDL_BlitSurface(framebuf, &sr, framebuf, &dr);
			SDL_MapSurface(framebuf, screen->format);

                        if (mustlock && SDL_LockSurface(framebuf) < 0 ) return;
			context = framebuf;
			GrFilledBoxNC(0, 576, 799, 599, colour_find(0,0,0));
			context = screen;
		        if (mustlock) SDL_UnlockSurface(framebuf);
			dy = 36;
		}	
		++p;
	}	
}

void joyce_dprintf(char *s, ...)	/* printf() for the debugger console */
{
	va_list arg;

	va_start(arg, s);
	if (!DebugMode || !ScInited)	/* If debugger not active, or screen
					  not set up, write to log file */
	{
		if (fplog) 
		{
			vfprintf(fplog, s, arg);
			fflush(fplog);
		}
	}
	else
	{
		vsprintf(message, s, arg);
		dputs(message);
	}

	va_end(arg);
}

void dgets(char *S, int maxc)	/* Gets() for the debugger console */
{
	int L = 0;

	char c[2];

	c[1] = 0;
	while(1)
	{
		c[0] = dgetch();
		if (c[0] == '\n' || c[0] == '\r')
		{
			S[L++] = 0;
			joyce_dprintf("\n");
			DrawScr();
			return;
		}
		if (c[0] == 8 || c[0] == 127)
		{
			if (L > 0)
			{
				--L;
				joyce_dprintf("\010 \010");
				DrawScr();
			}
		}
		else
		{
			joyce_dprintf(c);
			DrawScr();
			if (L < maxc - 1) S[L++] = c[0];
		}
	}
}		


char dgetch(void)
{
	unsigned char c = get_keytoken();

	switch(c)	/* (v1.20) Map some keys to WordStar control codes */
	{
		case SDLK_F10:		/* f10   */ c = 10; break;
		case SDLK_UP:		/* up    */ c =  5; break; 
		case SDLK_DOWN:		/* down  */ c = 24; break;
		case SDLK_LEFT:		/* left  */ c = 19; break;
		case SDLK_RIGHT:	/* right */ c =  4; break;
		case SDLK_DELETE:	/* del   */ c =  7; break;
		case SDLK_INSERT:	/* ins   */ c = 22; break;
		case SDLK_PAGEUP:	/* pgup  */ c = 18; break;
		case SDLK_PAGEDOWN:	/* pgdn  */ c =  3; break;
	}

	memset((byte *)keyboard_map, 0, sizeof(keyboard_map));
	keyboard_chain(0);
	return c;
}


void dcls(void)
{
	if (!DebugMode) return;

        if (mustlock && SDL_LockSurface(framebuf) < 0 ) return;

	context = framebuf;
	GrFilledBoxNC(0, 300, 799, 599, colour_find(0,0,0));
	context = screen;

	if (mustlock) SDL_UnlockSurface(framebuf);
}


/****************** (v1.27) Support for popup messages ****************/

void DrawPopup(void)
{
	char pbuff[81];
	int l, x, w, y, ty;
	time_t t;

	l = strlen(jstate.popup);
	if (l > 80) l = 80;
	sprintf(pbuff, "%-*.*s", l, l, jstate.popup);

	x = (400 - 4*l) - 8;
	w = 8*l + 16;

	if (jstate.popup_at_bottom) 
	{
		y  = 556; 
		ty = 35;
	}
	else
	{
		y  = 300;
		ty = 19;
	}

        if ((mustlock || context == screen) && SDL_LockSurface(context) < 0 ) return;

	GrFilledBoxNC(x,     y,     x + w,     y + 24, text_bg);

	GrBoxNC      (x,     y,     x + w,     y + 24, text_fg);
        Border3d     (x - 1, y - 1, x + w + 1, y + 25, 1);
        Border3d     (x - 2, y - 2, x + w + 2, y + 26, 1);
        Border3d     (x - 3, y - 3, x + w + 3, y + 27, 1);

	vid_putsxy(ty, 50 - (l/2), pbuff);
        if (context == screen)
        {
                if (mustlock) SDL_UnlockSurface(context);
                SDL_UpdateRect(context, x, y, w, 24);
        }

	time(&t);
	if (t == jstate.popup_timeout) popup_off();
}


void popup_on(char *msg, int secs, int centred)
{
	time_t t;

	if (ScrType) 
	{
		joyce_dprintf(msg);
		return;
	}

	t = 0;
	if (secs) 
	{
		time(&t);
		t += secs;
	}
	strncpy(jstate.popup, msg, 80);
	jstate.popup_timeout = t;
	jstate.popup_at_bottom = !centred;
}

void popup_off(void)
{
	shadow_invalid = 1;
	if (jstate.popup_at_bottom) Border();
	jstate.popup_timeout = 0;
	jstate.popup_at_bottom = 0;
	jstate.popup[0] = 0;
}
JoyceSdlGfx.c100600    764    764       32015  6663355146  11207 0ustar  jcejce/** JOYCE PCW emulator for Linux/SDL *************************/
/**                                                         **/
/** JoyceSdlGfx.c: contains graphics primitives             **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

/* JOYCE for DOS was written atop the GRX graphics library. JOYCE
 * for X uses SDL (there is a GRX for X, but for various reasons
 * I'm using SDL instead). This file emulates those GRX functions
 * that JOYCE uses and that aren't in SDL.
 *
 */

#include "Joyce.h"
#include "Joycevga.h"

static int clipx1, clipy1, clipx2, clipy2;

/* Graphics primitives */

static void GrDot(Uint8 *bits, Uint32 pixel, int bpp)
{
        switch(bpp)
        {
                case 1:
                *((Uint8 *)(bits)) = (Uint8)pixel;
                break;

                case 2:
                *((Uint16 *)(bits)) = (Uint16)pixel;
                break;

                case 3:
                { /* Format/endian independent */
                    Uint8 r, g, b;

                    r = (pixel>>context->format->Rshift)&0xFF;
                    g = (pixel>>context->format->Gshift)&0xFF;
                    b = (pixel>>context->format->Bshift)&0xFF;
                    *((bits)+context->format->Rshift/8) = r;
                    *((bits)+context->format->Gshift/8) = g;
                    *((bits)+context->format->Bshift/8) = b;
                }
                break;

                case 4:
                *((Uint32 *)(bits)) = (Uint32)pixel;
                break;
        }
}

static void GrDotRop(Uint8 *bits, Uint32 pixel, int bpp, int rop)
{
        switch(bpp)
        {
                case 1: switch(rop)
                {
			default: *((Uint8 *)(bits)) = (Uint8)pixel; break;
			case 1: *((Uint8 *)(bits)) &= (Uint8)pixel; break;
			case 2: *((Uint8 *)(bits)) |= (Uint8)pixel; break;
			case 3: *((Uint8 *)(bits)) ^= (Uint8)pixel; break;
		}
                break;

                case 2: switch(rop)
                {
                	default: *((Uint16 *)(bits))  = (Uint16)pixel; break;
                	case 1: *((Uint16 *)(bits)) &= (Uint16)pixel; break;
                	case 2: *((Uint16 *)(bits)) |= (Uint16)pixel; break;
                	case 3: *((Uint16 *)(bits)) ^= (Uint16)pixel; break;
                }
                break;

                case 3:
                { /* Format/endian independent */
                    Uint8 r, g, b;

                    r = (pixel>>context->format->Rshift)&0xFF;
                    g = (pixel>>context->format->Gshift)&0xFF;
                    b = (pixel>>context->format->Bshift)&0xFF;
                    switch(rop)
                    {
                      default:
                      *((bits)+context->format->Rshift/8) = r;
                      *((bits)+context->format->Gshift/8) = g;
                      *((bits)+context->format->Bshift/8) = b;
                      break;

                      case 1:
                      *((bits)+context->format->Rshift/8) &= r;
                      *((bits)+context->format->Gshift/8) &= g;
                      *((bits)+context->format->Bshift/8) &= b;
                      break;
                      
                      case 2:
                      *((bits)+context->format->Rshift/8) |= r;
                      *((bits)+context->format->Gshift/8) |= g;
                      *((bits)+context->format->Bshift/8) |= b;
                      break;
                      
                      case 3:
                      *((bits)+context->format->Rshift/8) ^= r;
                      *((bits)+context->format->Gshift/8) ^= g;
                      *((bits)+context->format->Bshift/8) ^= b;
                      break;
                    }
                }
                break;

                case 4: switch(rop)
		{
	                default: *((Uint32 *)(bits))  = (Uint32)pixel;
	                case 1: *((Uint32 *)(bits)) &= (Uint32)pixel;
	                case 2: *((Uint32 *)(bits)) |= (Uint32)pixel;
	                case 3: *((Uint32 *)(bits)) ^= (Uint32)pixel;
	        }
                break;
        }
}


static void GrXorDot(Uint8 *bits, int bpp)
{
	int n;
	for (n = 0; n < bpp; n++) bits[n] ^= 0xFF;
}



void GrPlotNC(int x, int y, int colour)
{
        int bpp;
        Uint32 pixel;
        Uint8 *bits;

        bpp = context->format->BytesPerPixel;

        pixel = SDL_MapRGB(context->format, colours[colour].r,
                                            colours[colour].g,
                                            colours[colour].b);

	bits = ((Uint8 *)context->pixels)+y*context->pitch+x*bpp;

	GrDot(bits, pixel, bpp);
}

int GrPixel(int x, int y)
{
        int bpp;
        Uint8 *bits;

        bpp = context->format->BytesPerPixel;
        bits = ((Uint8 *)context->pixels)+y*context->pitch+x*bpp;
        switch(bpp)
        {
                case 1:
		return *((Uint8 *)(bits));
                break;

                case 2:
                case 3:
		case 4:
                { /* Format/endian independent */
                    Uint8 r, g, b;

                    r = *((bits)+context->format->Rshift/8);
                    g = *((bits)+context->format->Gshift/8);
                    b = *((bits)+context->format->Bshift/8);
                    return colour_find(r,g,b);
                }
	}
	return 0;
}



void GrPlotRopNC(int x, int y, int colour, int rop)
{
        int bpp;
        Uint32 pixel;
        Uint8 *bits;

        bpp = context->format->BytesPerPixel;

        pixel = SDL_MapRGB(context->format, colours[colour].r,
                                            colours[colour].g,
                                            colours[colour].b);

	bits = ((Uint8 *)context->pixels)+y*context->pitch+x*bpp;

	GrDotRop(bits, pixel, bpp, rop);
}





void GrFilledBoxNC(int x1, int y1, int x2, int y2, int colour)
{
	int x, y, bpp;
	Uint32 pixel;
	Uint8 *bits;

        bpp = context->format->BytesPerPixel;

	pixel = SDL_MapRGB(context->format, colours[colour].r,
                                            colours[colour].g,
                                            colours[colour].b);

	for (y = y1; y <= y2; y++)
	{
		bits = ((Uint8 *)context->pixels)+ (y * context->pitch) + (x1 * bpp);
		for (x = x1; x <= x2; x++) 
		{	
			GrDot(bits, pixel, bpp);
			bits += bpp;
		}
	}
}

void xor_block(int y1, int x1, int h, int w)
{
        int x, y, bpp;
	Uint8 *bits;

        bpp = context->format->BytesPerPixel;

        for (y = y1; y < y1 + h; y++)
        {
                bits = ((Uint8 *)context->pixels)+ (y * context->pitch) + (x1 * bpp);
                for (x = x1; x <= x1 + w; x++)
                {
                        GrXorDot(bits, bpp);
                        bits += bpp;
                }
        }
}




void GrClearScreen(int index)
{
	GrFilledBoxNC(0, 0, 799, 599, index);
}

void GrBoxNC(int x1, int y1, int x2, int y2, int colour)
{
	GrHLineNC(x1, x2, y1, colour);
	GrHLineNC(x1, x2, y2, colour);
	GrVLineNC(x1, y1, y2, colour);
	GrVLineNC(x2, y1, y2, colour);
}




void GrHLineNC(int x1,int x2,int y, int c)
{
	int x,bpp;
	Uint32 pixel;
	Uint8 *bits;

        bpp = context->format->BytesPerPixel;

        pixel = SDL_MapRGB(context->format, colours[c].r,
                                            colours[c].g,
                                            colours[c].b);

        bits = ((Uint8 *)context->pixels)+y*context->pitch+x1*bpp;
        for (x = x1; x <= x2; x++)
        {
                GrDot(bits, pixel, bpp);
                bits += bpp;
        }
}




	
void GrVLineNC(int x,int y1,int y2, int c)
{
        int y, bpp;
        Uint32 pixel;
        Uint8 *bits;

        bpp = context->format->BytesPerPixel;

        pixel = SDL_MapRGB(context->format, colours[c].r,
                                            colours[c].g,
                                            colours[c].b);

        bits = ((Uint8 *)context->pixels)+y1*context->pitch+x*bpp;
        for (y = y1; y <= y2; y++)
        {
                GrDot(bits, pixel, bpp);                
                bits += context->pitch;
        }
}

void GrHLine(int x, int y1, int y2, int c)
{
	if (x  < clipx1)  x = clipx1;
	if (y1 < clipy1) y1 = clipy1;
	if (y2 < clipy1) y2 = clipy1;
	if (x >  clipx2)  x = clipx2;
	if (y1 > clipy2) y1 = clipy2;
	if (y2 > clipy2) y2 = clipy2;
	if (y1 > y2) { int t; t = y1; y1 = y2; y2 = t; }
	GrHLineNC(x, y1, y2, c);
}

void GrVLine(int x1, int x2, int y, int c)
{
        if (y  < clipy1)  y = clipy1;
        if (x1 < clipx1) x1 = clipx1;
        if (x2 < clipx1) x2 = clipx1;
        if (y  > clipy2)  y = clipy2;
        if (x1 > clipy2) x1 = clipx2;
        if (x2 > clipy2) x2 = clipx2; 
        if (x1 > x2) { int t; t = x1; x1 = x2; x2 = t; }
        GrVLineNC(x1, x2, y, c);
}



void GrClearClipBox(int col)
{
        GrFilledBoxNC(clipx1, clipy1, clipx2, clipx2, col);
	UpdateArea(clipx1, clipy1, clipx2, clipy2);
}


void GrSetClipBox(int x1, int y1, int x2, int y2)
{
	clipx1 = x1; clipx2 = x2;
	clipy1 = y1; clipy2 = y2;

	if (clipx1 < 0)   clipx1 = 0;
	if (clipx2 < 0)   clipx2 = 0;
	if (clipx1 > 799) clipx1 = 799;
	if (clipx2 > 799) clipx2 = 799;

        if (clipy1 < 0)   clipy1 = 0;
        if (clipy2 < 0)   clipy2 = 0;
        if (clipy1 > 599) clipy1 = 599;
        if (clipy2 > 599) clipy2 = 599;

	if (clipx2 < clipx1) { int t; t = clipx1; clipx1 = clipx2; clipx2 = t; }
	if (clipy2 < clipy1) { int t; t = clipy1; clipy1 = clipy2; clipy2 = t; }
}


void GrFilledBox(int x1, int y1, int x2, int y2, int colour)
{
        if (x1 < clipx1) x1 = clipx1;
        if (x2 < clipx1) x2 = clipx1;
        if (x1 > clipx2) x1 = clipx2;
        if (x2 > clipx2) x2 = clipx2;

        if (y1 < clipy1) y1 = clipy1;
        if (y2 < clipy1) y2 = clipy1;
        if (y1 > clipy2) y1 = clipy2;
        if (y2 > clipy2) y2 = clipy2;

        if (x2 < x1) { int t; t = x1; x1 = x2; x2 = t; }
        if (y2 < y1) { int t; t = y1; y1 = y2; y2 = t; }

	GrFilledBoxNC(x1, y1, x2, y2, colour);
}





void GrPlotRop(int x1, int y1, int colour, int rop)
{
        if (x1 < clipx1) x1 = clipx1;
        if (x1 > clipx2) x1 = clipx2;

        if (y1 < clipy1) y1 = clipy1;
        if (y1 > clipy2) y1 = clipy2;

	GrPlotRopNC(x1, y1, colour, rop);
}



void GrPlot(int x1, int y1, int colour)
{
        if (x1 < clipx1) x1 = clipx1;
        if (x1 > clipx2) x1 = clipx2;

        if (y1 < clipy1) y1 = clipy1;
        if (y1 > clipy2) y1 = clipy2;

        GrPlotNC(x1, y1, colour);
}

/* Draw a 3d border around something */

void Border3d(int x1, int y1, int x2, int y2, int raised) /* v1.27: not static */
{
        GrHLine(x1,x2,y1, raised ? shadow_hi : shadow_lo);
        GrVLine(x1,y1,y2, raised ? shadow_hi : shadow_lo);
        GrVLine(x2,y1,y2, raised ? shadow_lo : shadow_hi);
        GrHLine(x1,x2,y2, raised ? shadow_lo : shadow_hi);
}



void UpdateArea(int x, int y, int w, int h)
{
	SDL_UpdateRect(screen, x,y,w,h);
}

void SdlScrBlit(int dx, int dy, int sx, int sy, int sx2, int sy2)
{
	Uint8 *sbits, *dbits;
	int bpp = screen->format->BytesPerPixel;
	int lpi = screen->pitch;
	int y,w = (sx2 - sx + 1) * bpp;

	if (SDL_LockSurface(screen) < 0 ) return;

	if (sy < dy && dy < sy2) for (y = (sy2-sy); y > 0; y--)
        {
                sbits = ((Uint8 *)screen->pixels) + ((y+sy) * lpi) + (sx * bpp);
                dbits = ((Uint8 *)screen->pixels) + ((y+dy) * lpi) + (dx * bpp);

                memmove(dbits, sbits, w);
        }

        else for (y = 0; y < (sy2-sy); y++)
        {
                sbits = ((Uint8 *)screen->pixels) + ((y+sy) * lpi) + (sx * bpp);
		dbits = ((Uint8 *)screen->pixels) + ((y+dy) * lpi) + (dx * bpp);

		memmove(dbits, sbits, w);
	}

	SDL_UnlockSurface(screen);
}

__inline__ int sgn(int x)
{
	/* Beautiful, isn't it? */
	return (!x ? 0 : (( x < 0 ) ? -1 : 1));
}


/* This line-drawing algorithm comes straight from the ZX81 manual, I kid
   you not. The comments are transcribed exactly, and the ZX81 has no
   lower-case letters; hence the shouting. */

void GrLineRop(int a, int b, int c, int d, int colour, int rop)
{
	int u   = c - a;	/* U SHOWS HOW MANY STEPS ALONG WE NEED TO GO */
	int v   = d - b;	/* V SHOWS HOW MANY STEPS UP */
	int d1x = sgn(u);	/* (D1X,D1Y) IS A SINGLE STEP IN A */
	int d1y = sgn(v);	/*           DIAGONAL DIRECTION    */ 
	int d2x = d1x;
	int d2y = 0;		/* (D2X,D2Y) IS A SINGLE STEP LEFT OR RIGHT */
	int m   = abs(u);
	int n   = abs(v);
	int s, i;

	if (m <= n)
	{
		d2x = 0;
		d2y = d1y;	/* NOW (D2X,D2Y) IS A SINGLE STEP UP OR DOWN */
	
		m = abs(v);
		n = abs(u);	
	}			/* M IS THE LARGER OF ABS U & ABS V, N IS */
				/* THE SMALLER */
	s = m / 2;		/* WE WANT TO MOVE FROM (A,B) TO (C,D) IN */
				/* M STEPS USING N UP-DOWN OR RIGHT-LEFT  */
				/* STEPS D2, & M-N DIAGONAL STEPS D1, */
				/* DISTRIBUTED AS EVENLY AS POSSIBLE */
	for (i = 0; i < m; i++)
	{
		GrPlotRop(a,b,colour,rop);
		s += n;
		if (s >= m)
		{
			s -= m;
			a += d1x;
			b += d1y;	/* A DIAGONAL STEP */
		}
		else
		{
			a += d2x;
			b += d2y;	/* AN UP-DOWN OR LEFT-RIGHT STEP */
		}
	}	
}



JoyceSdlMouse.c100600    764    764       24077  6676253565  11572 0ustar  jcejce/** JOYCE PCW emulator for DJGPP *****************************/
/**                                                         **/
/** JOYCEMOU.C: Emulates the PCW mouse via a Microsoft or   **/
/**            MouseSystems mouse                           **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#include "Joyce.h"
#include "Joycemou.h"
#include "inline.h"

/* New in v1.23: better mouse handling.

[Terminology: a 'mickey' is the basic unit of mouse movement]

  The pre-v1.23 handling in AMX mode was very simple:

      PCW mickeys = (PC mickeys & 0x0F)

  This meant that any move above 15 mickeys would become a move by a random
 number below 15. In turn, this meant that large moves were jerky and 
 inaccurate.

  New behaviour: 
   * The PC mickey count can be scaled (eg 4 PC mickeys : 1 PCW mickey). 
    This improves accuracy on small moves. The scale factors are jset.mxscale
    and jset.myscale.
   * The equation is now: PCW mickeys = min ((PC mickeys / scale), speed)
    where the speed is jset.mxspeed / jset.myspeed. This means that if the
    PC mickey count is over (say) 15, 15 is returned. Reducing the value
    of 'speed' reduces the number of PCW mickeys that can be returned by
    one call, making motion smoother if slower.
   * If these transformations mean that not all the PC mickeys have been
    "used up", then the unused ones are kept for next time. This improves
    long-distance navigation as the big PC move is translated into separate
    PCW moves rather than truncated.

  The Kempston mouse uses 8-bit mickey counts rather than 4-bit so is less
likely to be affected by the original problem.

 */

#define min(a,b) (a < b ? a : b)
#define max(a,b) (a > b ? a : b)

/* These figures define the mouse ballistics */

/* Values as per JOYCE v1.22
 *
 * jset.mxscale = 1; jset.myscale = 1;
 * jset.mxspeed  = 15, jset.myspeed  = 15; 
 *
 */

/* These make it smoother but slower in MD3 
 *
 * jset.mxscale = 4, jset.myscale = 2;
 * jset.mxspeed  = 7, jset.myspeed  = 7; 
 *
 */


void MouseRead(short *dy, short *dx)    /* Read the no. of mickeys moved */
{
	static Uint16 ox, oy, x, y;

	SDL_GetMouseState(&x, &y);

        *dx += (x-ox);
        *dy += (y-oy);

	ox = x;	
	oy = y;
}


void MouseBtn(word *btn)        /* Check for buttons */
{
	Uint16 x;

	*btn = SDL_GetMouseState(&x, &x);
}




/* (v1.23) mouse scaling options (PC mickeys : PCW mickeys)
 */

static signed short scale_x(signed short x)
{
	if (x < 0) x = (-x);
	return min((x / jset.mxscale), jset.mxspeed);
}


static signed short scale_y(signed short y)
{
	if (y < 0) y = (-y);
	return min((y / jset.myscale), jset.myspeed);
}



void InitMouse(void)	/* Initialise the mouse driver */
{
	MouseButtons = 3;	/* On X, we assume 3 buttons. */
}


/* (v1.27)  New mouse capture code

              The idea of this code is to bypass all that tedious mucking 
            about with I/O ports and mickeys, and to pass mouse coordinates
            directly into the program.
 
               The problem is: there is no standard mouse driver interface.
             What we have to do is trap a mouse read, and find out where the
             program keeps its mouse driver code. We then patch it.
*/ 

/* According to the PCW Plus Tips Collection:
 * AMX:
 *
 * IN A0 gives vertical movement -   low  nibble = no. of moves up
 *                                   high nibble = no. of moves down
 * IN A1 gives horizontal movement - low  nibble = no. of moves right
 *                                   high nibble = no. of moves left
 * IN A2 gives button state in bottom 3 bits
 */


void mouse_patch(Z80 *R)	/* Called when our patch is triggered */
{
	Uint8  btn;
	Uint16 col, row;

	btn = SDL_GetMouseState(&col, &row);

/*** This code (used in DOS) maps the whole screen area to the PCW mouse; so that the real mouse has to be
    at the edge of the physical screen when the PCW mouse is at the edge of its border. In an X window
    (especially when focus follows the mouse) this is too confusing.

	row = (256L * row) / 600;
	col = (720L * col) / 800;
*/

	row /= 2;
	if (col < 40) col = 0; else col -= 40;	/* Limit to within the border area */
	if (row < 22) row = 0; else row -= 22;

	if (col > 719) col = 719;
	if (row > 255) row = 255;

	if (jstate.md3_mx)	/* MD3 patch active? */
	{
		row = 255 - row;
		if (row == RdWord(jstate.md3_my) && (col == RdWord(jstate.md3_mx))) 
		{
			R->AF.B.h = 0;
			return;
		}
		WrWord(jstate.md3_mx,  col);
		WrWord(jstate.md3_my,  row);
		WrZ80(jstate.md3_mf1, 0xFF);
		WrZ80(jstate.md3_mf2, 0xFF);
		R->AF.B.h = 1;
	}
	if (jstate.sp_mx)
	{
		WrWord(jstate.sp_mx, col);
		WrWord(jstate.sp_my, row);
	}
        if (jstate.mp_mx)
        {
                WrWord(jstate.mp_mx, col);
                WrWord(jstate.mp_mx+2, row);
	}
}

static int zmemcmp(word addr, byte *compare, int count)
{
	int n;

	for (n = 0; n < count; n++) if (RdZ80(addr++) != compare[n]) return -1;
	return 0;
}

static void zmemput(word addr, byte *replace, int count)
{
	int n;
	for (n = 0; n < count; n++) WrZ80(addr++, replace[n]);
}


/* Called the first time a mouse port is read. Checks if the mouse driver
  is anything we recognise */

void check_for_patch(void)
{
	int    patch_add = jstate.Rg.PC.W - 2;	/* Address of calling code */

	static byte md3_match1[5] = {0xDB, 0xA2, 0xF6, 0xF8, 0xC9};
	static byte md3_match2[6] = {0xCD, 0x00, 0x00, 0x28, 0x0F, 0x2A};
	static byte md3_patch2[6] = {0x3E, 0xFD, 0xED, 0xFE, 0xA7, 0xC9};
	static byte sp_match[9]   = {0xED, 0x78, 0xFE, 0x10, 0x20, 
                                     0x0E, 0x01, 0x80, 0x81};
	static byte sp_patch[7]   = {0x3E, 0xFD, 0xED, 0xFE, 0xC3, 0x00, 0x00};
        static byte mp_match1[7]  = {0xDB, 0xA2, 0x3C, 0x28, 0x49, 0x3D, 0x21};
        static byte mp_match2[14] = {0xDB, 0xD0, 0x47, 0xDB, 0xD1, 0x4F, 0xDB,
                                     0xD4, 0x57, 0xA0, 0xA1, 0x3C, 0x28, 0x50};

	jstate.mouse_patched = -1;
        /* Check for MasterPaint code */
        if (jstate.mouse_patched == 0) jstate.mouse_patched = 0x80;

        /* Check for MasterPaint code. The first check triggers the patch
          mechanism, but we're not ready to do the patch yet. So we put
          it into patch mode 2 (watch permanently). The second check is
          called when we are ready to do the patch. */

        if (!zmemcmp(patch_add, mp_match1, 7))
        {
                jstate.mouse_patched = 0x81;
                popup_on("Detected MasterPaint...", 0, 1);
        }
        if (!zmemcmp(patch_add, mp_match2, 14))
        {
                jstate.mp_mx = RdWord(patch_add + 0x69);
                sp_patch[4] = 0xC9;
                zmemput(patch_add + 0x68, sp_patch, 5);
                sp_patch[4] = 0xC3;
                jstate.mouse_patched = 3;
                popup_on("Applied MasterPaint mouse patch", 4, 1);
        }

	/* Check for md3 code */
	if (!zmemcmp(patch_add, md3_match1, 5)) 
	{
		md3_match2[1] = (patch_add - 0x20) & 0xFF;
		md3_match2[2] = (patch_add - 0x20) >> 8;
		if (!zmemcmp(patch_add - 0x254, md3_match2, 6))
		{	
			zmemput(patch_add - 0x254, md3_patch2, 6);

			jstate.md3_my  = RdWord(patch_add - 0x24E);
			jstate.md3_mx  = RdWord(patch_add - 0x23B);
			jstate.md3_mf1 = RdWord(patch_add - 0x228);
			jstate.md3_mf2 = RdWord(patch_add - 0x225);

			popup_on("Applied MD3 mouse patch", 4, 1);
			jstate.mouse_patched = 1;
		}
	}
	/* Check if we can recognise the Stop Press mouse code; and if we can,
           hook it. */
	if (!zmemcmp(patch_add, sp_match, 9))
	{
		sp_patch[5] = (patch_add + 0x103) & 0xFF;
		sp_patch[6] = (patch_add + 0x103) >> 8;
		zmemput(patch_add + 0x88, sp_patch, 7);
	
		jstate.sp_mx = RdWord(patch_add + 0x25);
		jstate.sp_my = RdWord(patch_add + 0x2b);		
		popup_on("Applied Stop Press mouse patch", 3, 1);
		jstate.mouse_patched = 2;
	}

	if (jstate.mouse_patched > 0)	/* Remove X's own cursor */
	{
		static Uint16 x, y;

		SDL_GetMouseState(&x, &y);

		tracking = 1;
		track_mouse(x, y);
	}

}

/* End v1.27 code */

byte MouseIn(byte port)	/* Deal with input from mouse port */
{
	byte rv=0xFF;
	static signed short y=0,x=0;
	word btn;

	if (jset.mousemode < 0 || MouseButtons < 0) return 0xFF;

	/* (v1.27) MD3 patch code */
	if ((jstate.mouse_patched == 0 || jstate.mouse_patched == 0x81)
             && jset.patchmouse) check_for_patch();

	if (jset.mousemode == 1) /* AMX */ switch(port)
	{
		case 0xA0:	/* (v1.23) Read & scale vertical movement */
		MouseRead(&y,&x);
		if (y <= 0) 
		{	
			rv =  scale_y(y)       & 0x0F;
			y  += min(-y, jset.myspeed);
		}
		else
		{
			rv = (scale_y(y) << 4) & 0xF0;
			y -= min(y, jset.myspeed);
		}
		//y = 0;	/* reset Y count */
		return rv;

		case 0xA1:	/* Read horizontal movement */
		MouseRead(&y,&x);
		if (x >= 0) 
		{
			rv =  scale_x(x)       & 0x0F;
			x -=  min(x,jset.mxspeed);
		}
		else
		{
			rv = (scale_x(x) << 4) & 0xF0;
			x += min(-x, jset.mxspeed);
		}
		//x = 0;	/* reset X count */
		return rv;
		
		case 0xA2:
		MouseBtn(&btn);
		rv = 0xFF;
		if (btn & 1) rv &= 0xFE;	/* L */
		if (btn & 2) rv &= 0xFD;	/* M */
		if (btn & 4) rv &= 0xFB;	/* R */		
		return rv;
	
		case 0xA3:
		return 0;
	}

	else  /* Kempston */ switch(port)
	{
		case 0xD0:	/* Read horizontal movement */
		MouseRead(&y,&x);
		return (x >> 3);

		case 0xD1:	/* Read vertical movement */
		MouseRead(&y,&x);
		return (-(y >> 3));

		case 0xD2:	/* ? */
//		MouseRead(&y,&x);
		return 0;

		case 0xD3:	/* ? */
//		MouseRead(&y,&x);
		return 0;
		
		case 0xD4:
		MouseBtn(&btn);
		rv = 0xFF;
		if (btn & 1) rv &= 0xFD;	/* L */
		if (btn & 2) rv &= 0xFE;	/* R */
		if (btn & 4) rv &= 0xFE;	/* M */
		return rv;
	}
	return 0xFF;
}


void UnitMouse(void)
{

}


void mouse_reset(void)
{
	SDL_ShowCursor(1);
	tracking = 0;
}


void track_mouse(Uint16 x, Uint16 y)
{
	static int sc = -1;

	if (!tracking)
	{
        	SDL_ShowCursor(1);
		return;
	}

	if (x < 40 || x >= 760 || y < 44 || y >= 556)
	{
		if (sc != 1) SDL_ShowCursor(1);
		sc = 1;
	}
	else
	{
		if (sc != 0) SDL_ShowCursor(0);
		sc = 0;
	}
}

JoyceSdlLogo.c100600    764    764       35054  6663355703  11370 0ustar  jcejce/** JOYCE PCW emulator for SDL *******************************/
/**                                                         **/
/** (v1.22) Screen access for Digital Research Logo         **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#include "Joyce.h"
#include "Joycelgo.h"
#include "Joycevga.h"
#include "inline.h"

#define P1_WORD RdZ80(R->SP.W + 2) + 256*RdZ80(R->SP.W + 3)
#define P2_WORD RdZ80(R->SP.W + 4) + 256*RdZ80(R->SP.W + 5)
#define P3_WORD RdZ80(R->SP.W + 6) + 256*RdZ80(R->SP.W + 7)
#define P4_WORD RdZ80(R->SP.W + 8) + 256*RdZ80(R->SP.W + 9)

#define P1_BYTE RdZ80(R->SP.W + 2)
#define P3_BYTE RdZ80(R->SP.W + 6)	/* After 2 words */
#define P4_BYTE RdZ80(R->SP.W + 7)
#define P5_BYTE RdZ80(R->SP.W + 10)	/* After 4 words */
#define P6_BYTE RdZ80(R->SP.W + 11)

#define ESC "\0x1B"

static void vgastr(char *s)
{
	while (*s)
	{
		vga_putchar(*s);
		++s;
	}
}


/* LIOS interface:
 
  E=function number
 SP->stack frame:
	DW	return		or	DW	return
	DW	param1			DB	param1 
	DW	param2			DB	param2
	...				...

functions are:

E=	Parameters 	Description
--------------------------------------------------------------------
0:	none		Initialise. By this time, the info block has
			already been used to set up the screen size;
			so the Amstrad LIOS (and the JOYCE LIOS) 
			change it when the RSX is awakened (function 
			0FFh below) and then restart the COM file 
			with a JP 100h. This routine, which will be
			called the second time around, is therefore
			a no-op.
1:	word 0		Terminate
2:	none		Turn on text mode, no graphics
3:	byte line	Set split line (no. of text lines visible)
4:	none		CONIN - return in HL
5:	none		CONST - return in HL
6:	byte ch		CONOUT - print char if text screen available
7:	word T,		Clear text window from T to B
	word B
8:	word X,		Move text cursor to X,Y
	word Y
9:	word T,		Scroll text window from T to B
	word B
10:	word X,		Plot a point. pen is 2 for PX else PD
	word Y,		
	byte pen,
	byte colour
11:	word X1,	Draw a line from X1,Y1 to X2,Y2
	word Y1,
	word X2,
	word Y2,
	byte pen,
	byte colour
12:	none?		no-op?
13:	word colour,	Clear screen - height=pixels
	word pixels
14:	none		Beep
15:	none		Get top of memory
16:	word X,		Read pixel colour into L
	word Y
17:	none		LISTST
18:	byte ch		LIST
19-23:	none		no-ops? Not called by DR Logo
24:	word BUF,	Generate record <RECNO> of a picture savefile.
	word RECNO	Return 1 in HL if more records follow
25:	word BUF,	Load record <RECNO> of a picture savefile.
	word RECNO

26:	none?		These are not hooked in the Amstrad LIOS or JOYCE, but
27:	none?		are used for 'pal' and 'setpal' respectively.

0FFh: 	word PARAMS	Initialise. Enter with HL->message buffer, write 
			message here if there is a failure. Return HL=0
			if failure, else 1

PARAMS:	DW	0	;+0	Text screen height, chars
	DW	0	;+2	Text screen width, chars
	DW	0	;+4	Text screen height, chars
	DW	0	;+6	Text screen width, chars
	DW	0	;+8	Standard split line, chars
	DW	0	;+A	Default foreground colour
	DW	0	;+C	Default background colour
	DW	0	;+E
	DW	0	;+10	Number of foreground colours - 1
	DW	0	;+12
	DW	0	;+14
	DW	0	;+16
	DW	0	;+18	Graphics screen width, pixels
	DW	0	;+1A	Graphics screen height, pixels
	DW	0	;+1C
	DW	0	;+1E	Maximum scrunch ratio
	DW	0	;+20	1 if split screen disabled?
	DW	0	;+22	Number of background colours - 1
	DW	0	;+24	Text screen height, chars
	DW	0	;+26	Scrunch ratio, numerator
	DW	0	;+28	Scrunch ratio, denominator
	DW	0	;+2A	->Version-specific title string, ASCIIZ
	DW	0	;+2C	->"Edit", ASCIIZ
	DW	0	;+2E	->"_",    ASCIIZ

*/

static byte oldtype;
static int splitsz, gfx_y;


static void dosplit(void)
{
	if (!splitsz) gfx_y = 600;
	else gfx_y = 16 * splitsz; 
	GrSetClipBox(0, 0, 800, 600);
}

void __inline__ WrByte(word a, byte v)
{
	WrZ80(a,     v);
	WrZ80(a + 1, 0);
}




int makerop(byte pen, byte *colour)
{
	switch(pen)
	{
		default: return 0;		/* pd */
		case 2:  *colour = 0; return 2;	/* pu */
		case 3:	 return 3;		/* px */
		case 4:  *colour = 0; return 0;	/* pe */
	}
}

void lios0(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios4(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios5(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios12(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios17(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios18(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios19(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios20(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios21(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios22(void) {}	/* Possible no-ops, functions implemented in Z80 */
void lios23(void) {}	/* Possible no-ops, functions implemented in Z80 */

void zstrcpy(word dest, char *src)
{
	do
	{
		WrZ80(dest++, *src);
	} while (*src++);
}

word lios_init(word liospb, word msgbuf)
{
	char title[90];

	sprintf(title,"Amstrad LOGO v2.0 under JOYCE %x.%02x", BCDVERS >> 8, BCDVERS & 0xFF);
	oldtype = ScrType;
	vgastr(" ");			/* Force it into 800x600 VGA */
	vga_cursor(0);
	vga_bg = colour_find(0,0,0);		/* Black */
	vga_fg = colour_find(255,255,255);	/* White */
	GrClearScreen(vga_bg);
	vga_curvis = 0;
	WrByte(liospb,      36);
	WrByte(liospb + 2,  99);
	WrByte(liospb + 4,  36);
	WrByte(liospb + 6,  99);
	WrByte(liospb + 8,  27);
	WrWord(liospb +10, (word)vga_fg);
	WrWord(liospb +12, (word)vga_bg);
	WrWord(liospb +16, 255);		/* Allow 256 colours */
	WrWord(liospb +24, 800);
	WrWord(liospb +26, 600);
	WrWord(liospb +34, 255);		/* 256 background colours */
	WrByte(liospb +36,  36);
	WrWord(liospb +38,  32);		/* 1:1 scrunch ratio */
	WrWord(liospb +42,  msgbuf);
	zstrcpy(msgbuf,title); 
	splitsz = 27;
	dosplit();
        SDL_UpdateRect(screen, 0, 0, 0, 0);
	return 1;
}

void lios1(void)
{
	vga_fg = dwhi;
	vga_bg = dblk;
	vgastr(ESC "1" ESC "w");
	vga_cursor(1);
	ScrType = oldtype;
}

void lios2(void)
{
	splitsz = -1;
	vga_curon = 1;
}

void lios3(byte split)
{
	splitsz = split;
	if (!splitsz)	/* All graphics no text */
		vga_curon = 0;
	else	vga_curon = 1;
	dosplit();
}

void __inline__ lios6(byte ch)
{
	if (splitsz) vga_putchar(ch);	/* Don't do text in fullscreen */
}

void lios7(word T, word B)	/* Clear text lines T to B */
{
	long top    = (T-1) * 16;
	long bottom = (B-1) * 16 + 15;

	if (bottom > 599) bottom = 599;
	if (top > 599)    top    = 599;

	if (vga_curvis) 
	{
		vga_drawcur(); 
		vga_curvis = 0;
	}
	GrFilledBox(0, top, 799, bottom, colour_find(0,0,0));
        SDL_UpdateRect(screen, 0, top, 800, bottom-top);

}

void lios8(word X, word Y)  /* Move text cursor to X,Y */
{
	vga_putchar(0x1B);
	vga_putchar('Y');
	vga_putchar(Y+31);
	vga_putchar(X+31);
}


void lios9(word T, word B) /* Scroll text window */
{
	long top    = (T-1) * 16;
	long bottom = (B-1) * 16 + 15;

	if (bottom > 599) bottom = 599;
	if (top > 599)    top    = 599;

	if (vga_curvis) 
	{
		vga_drawcur(); 
		vga_curvis = 0;
	}
	SdlScrBlit(0, top, 0, top + 16, 799, bottom);
	GrFilledBox(0, bottom - 16, 799, bottom, colour_find(0,0,0));
        SDL_UpdateRect(screen, 0, top, 800, bottom-top);
}

word lios10(word X, word Y, byte colour, byte pen)
{
	int rop = makerop((pen == 2) ? 3 : 1, &colour);	

	GrPlotRop(X, Y, colour, rop);
        SDL_UpdateRect(screen, X, Y, 1, 1);
	return 1;
}


word lios11(word X1, word Y1, word X2, word Y2, byte colour, byte pen)
{
	int rop = makerop((pen == 2) ? 3 : 1, &colour);	

        GrLineRop(X1, Y1, X2, Y2, colour, rop);

	if (X1 > X2) { word t = X2; X2 = X1; X1 = t; }
	if (Y1 > Y2) { word t = Y2; Y2 = Y1; Y1 = t; }

        SDL_UpdateRect(screen, X1, Y1, X2-X1+1, Y2-Y1+1);
	return 1;
}

word __inline__ lios13(word colour, word pixels)
{
	if (vga_curvis) 
	{
		vga_drawcur(); 
		vga_curvis = 0;
	}
	GrFilledBox(0, 0, 799, pixels, colour);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
	return 1;
}

void __inline__ lios14(void)
{
	vga_putchar(7);
}


word lios15(void)
{
	return RdZ80(6)+256*RdZ80(7);
}

word __inline__ lios16(word X, word Y)
{
	return (word)(GrPixel(X, Y));
}


word lios24(word buf, word recno)
{
	return get_bmp_record(buf, recno);
}

word lios25(word buf, word recno)
{
	return put_bmp_record(buf, recno);
}

void dr_logo(Z80 *R)
{
/*	joyce_dprintf("LIOS call %d ", R->DE.B.l);
	switch(R->DE.B.l)
	{
		case 3: joyce_dprintf("(%d)", P1_BYTE); break;
		case 6: joyce_dprintf("(%c)", P1_BYTE); break;
		case 7:
		case 8:
		case 9:
		case 13:
		case 16:joyce_dprintf("(%d,%d)", P1_WORD, P2_WORD); break;
		case 10:joyce_dprintf("(%d,%d,%d,%d)", P1_WORD, P2_WORD, P3_BYTE, P4_BYTE); break;
		case 11:joyce_dprintf("(%d,%d,%d,%d,%d,%d)", P1_WORD, P2_WORD, P3_WORD, P4_WORD, P5_BYTE, P6_BYTE); break;
	}
	joyce_dprintf(" SP=%x\n", R->SP.W);
*/	switch(R->DE.B.l)
	{
		case 0:	lios0(); break;
		case 1: lios1(); break;
		case 2: lios2(); break;
		case 3:	lios3(P1_BYTE); break;
		case 4: lios4(); break; /* done in Z80 code */
		case 5: lios5(); break;	/* done in Z80 code */
		case 6: lios6(P1_BYTE); break;
		case 7: lios7(P1_WORD, P2_WORD); break;
		case 8: lios8(P1_WORD, P2_WORD); break;
		case 9: lios9(P1_WORD, P2_WORD); break;
		case 10:R->HL.W = lios10(P1_WORD, P2_WORD, P3_BYTE, P4_BYTE); break;
		case 11:R->HL.W = lios11(P1_WORD, P2_WORD, P3_WORD, P4_WORD,
			       P5_BYTE, P6_BYTE); break;
		case 12:lios12(); break;
		case 13:R->HL.W = lios13(P1_WORD, P2_WORD); break;
		case 14:lios14(); break;
		case 15:R->HL.W = lios15(); break;
		case 16:R->HL.W = lios16(P1_WORD, P2_WORD); break;
		case 17:lios17(); break; /* done in Z80 code */
		case 18:lios18(); break; /* done in Z80 code */
		case 19:lios19(); break;
		case 20:lios20(); break;
		case 21:lios21(); break;
		case 22:lios22(); break;
		case 23:lios23(); break;
		case 24:R->HL.W = lios24(P1_WORD, P2_WORD); break;
		case 25:R->HL.W = lios25(P1_WORD, P2_WORD); break;

		case 0xFF: R->HL.W = lios_init(P1_WORD, R->HL.W); break;

		default: joyce_dprintf("Unrecognised LIOS call %d\n", R->DE.B.l);
	}
}

/* BMP headers. HDR1 used for I/O; stdhdr holds an 800x600x256 bmp header */
	
byte hdr1[54];
byte stdhdr[54] = {'B', 'M', 	  /* (2) Magic number */
		   54, 87, 7, 0,  /* (4) file size */
		   0,   0, 0, 0,  /* (4) zero */
		   54,  4, 0, 0,  /* (4) offset to image data */
		   40,  0, 0, 0,  /* (4) length of info area*/
		   32, 3, 0, 0,	  /* (4) width  = 800 */
		   88, 2, 0, 0,   /* (4) height = 600 */
		   1,  0,         /* (2) planes */
		   8,  0,         /* (2) colour depth */
		   0,  0, 0, 0,   /* (4) no compression */
		   0, 83, 7, 0,   /* (4) picture size, 800*600 */
		   0,  0, 0, 0,   /* (4) horizontal resolution */
		   0,  0, 0, 0,   /* (4) vertical resolution */		   		   
		   0,  0, 0, 0,   /* (4) colours used */
		   0,  0, 0, 0,   /* (4) important colours */
};				  /* total: 54 bytes */

/* BMP palette */

byte palette[1024];

long hdrlen, hdrused; /* hdrlen: Length of info area */
long pallen, palused, palbegin, palend;
long piclen, pich, picw;

static int isbmp(void)
{
	if ((hdr1[0] != 'B') || (hdr1[1] != 'M')) return 0;
	hdrlen = hdr1[14] + 256 * hdr1[15] + 65536 * hdr1[16] + 16777216 * hdr1[17];
	hdrused = (hdrlen < 40 ? hdrlen : 40);
	return 1;
}


static int is8bit(void)
{
	if ((hdr1[26] != 1) || (hdr1[28] != 8) || (hdr1[30] != 0)) return 0;
	pallen = hdr1[10] + 256 * hdr1[11] + 65536 * hdr1[12] + 16777216 * hdr1[13];
	pallen -= (hdrlen + 14);
	palused = (pallen < 1024 ? pallen : 1024);
	piclen = hdr1[2] + 256 * hdr1[3] + 65536 * hdr1[4] + 16777216 * hdr1[5];
	picw   = hdr1[18] + 256 * hdr1[19] + 65536 * hdr1[20] + 16777216 * hdr1[21];
	pich   = hdr1[22] + 256 * hdr1[23] + 65536 * hdr1[24] + 16777216 * hdr1[25];
	while (picw & 3) ++picw;	

	palbegin = hdrlen   + 14;
	palend   = palbegin + pallen;
	return 1;

}



static void get_hdr1(void)
{
	memcpy(&hdr1, &stdhdr, 54);
}



static void get_palette(void)
{
	int n, r, g, b;

	memset(palette, 0, sizeof(palette));
	for (n = 0; n < 256; n++)
	{
		GrQueryColor(n, &r, &g, &b);
		palette[n*4    ] = b;
		palette[n*4 + 1] = g;
		palette[n*4 + 2] = r;
	}
}


static byte colmap[256];	// Mappings of BMP colours to JOYCE colours



static void put_palette(void)
{
	int n, m;
	int r, g, b;

	for (n = 0; n < 256; n++)
	{
		b = palette[n*4    ];
		g = palette[n*4 + 1];
		r = palette[n*4 + 2];
		m = colour_find(r,g,b);
		colmap[n] = m;
	}
}


/* Generate any 128-byte record from a .BMP file */

word get_bmp_record(word Z80addr, word recno)
{
	int n;
	long where = recno * 128;

	if (where > (800*600 + sizeof(palette) + sizeof(hdr1))) return 0;

	get_hdr1();
	get_palette();

	for (n = 0; n < 128; n++)
	{
		register int m = n + where;

		if (m < sizeof(hdr1)) 
			WrZ80(Z80addr++, hdr1[m]);
		else if (m < (1024 + sizeof(hdr1)))
			WrZ80(Z80addr++, palette[m - sizeof(hdr1)]);
		else	
		{
			int px, py, pz;

			pz = (m - sizeof(hdr1) - sizeof(palette));
			py = (pz / 800);
			px = (pz % 800);

			WrZ80(Z80addr++, GrPixel(px,599 - py));
		}
	}
	return 1;
}




word put_bmp_record(word Z80addr, word recno)
{
	int bmpptr, z80ptr = 0;
	int where = 128 * recno;
	static int loadpal = 0;

	if (!recno)
	{
/*
 * Record 0: Load the header, which gives the sizes of the other objects in
 *          the BMP. This is rather complicated.
 * 
 * 1. Read in the header (14 bytes) and the first dword of the info area
 *   (4 bytes)
 *
 */
		for (z80ptr = 0; z80ptr < 18; z80ptr++)
		{
			hdr1[z80ptr] = RdZ80(Z80addr + z80ptr);
		}
		if (!isbmp()) return 0;
/*
 * hdrlen  = number of bytes in info area 
 * hdrused = number of info bytes we can use ( <= hdrlen)
 *
 * 2. Read in the rest of the info area ((hdrused - 4) bytes)
 * 
 */
		for (z80ptr = 18; z80ptr < 14 + hdrused; z80ptr++)
		{
			hdr1[z80ptr] = RdZ80(Z80addr + z80ptr);
		}
		if (!is8bit()) return 0;
		z80ptr = hdrused + 14;	/* header + used info area */
	}
/*
 * 3. Read palette or picture data
 */
	while (z80ptr < 128)
	{
		bmpptr = where + z80ptr;
		if      (bmpptr < hdrlen + 14) continue; /* Skipping bits of header */
		else if (bmpptr < palend) /* Loading palette */
		{
			if (bmpptr - palbegin < 1024)
			{
				palette[bmpptr - palbegin] = RdZ80(Z80addr + z80ptr);
			}
			loadpal = 1;
		}
		else if (bmpptr < palend) /* Skipping bits of palette */
		{	
			++z80ptr;
			continue;
		}
		else	/* Part of the picture - plot it */
		{
			int px, py, pz;

			if (loadpal)
			{
				put_palette();
				loadpal = 0;			
			}
			pz = (bmpptr - palend);
			py = (pz / picw);
			px = (pz % picw);

			GrPlot(px, pich - py - 1, colmap[RdZ80(Z80addr + z80ptr)]);
		}
		++z80ptr;
	}
	if (where + 128 > piclen) 
	{
	        SDL_UpdateRect(screen, 0, 0, 0, 0);
		return 0;
	}
	return 1;
}
sdldummy.c100600    764    764        2675  6663350712  10647 0ustar  jcejce
#include "Joyce.h"
#include "Joycevga.h"
#include "Joycemou.h"
#include "keyboard.h"
#include <assert.h>

////////////////////////////////////////////////////////////////////////
// The dummy functions come in two categories...
//
// Stubs for calls that haven't yet been implemented
// Calls that don't do anything in the Unix version

// Stubs...

#define false 0

void gsx_cmd(word pb) { assert(false); }




////////////////////////////////////////////////////////////////////////
// Raw screen access
//

void cputs(char *s) { fputs(s, stderr); }
char getch(void) { return getchar(); }

////////////////////////////////////////////////////////////////////////
// Sound no-ops. If SDL is capable of emulating an AY-3-8910 
// portably, this is where it would get initialised & deinitialised.

byte InitAdlib(void)
{
	return 0;	/* No sound card */
}

void TrashAdlib(void)
{
			/* No sound card */
}

void AYPort(register byte P, register byte V)
{
	(void)P;
	(void)V;
}

//////////////////////////////////////////////////////////////////////
//
// Code to use when debugging

__inline__ byte RdZ80(word addr)
{
        return (PCWRD[addr>>14])[addr];
}


__inline__ void WrZ80(word addr,byte v)
{
        (PCWWR[addr>>14])[addr]=v;
}


__inline__ word RdWord(word addr)
{
        return (((word)RdZ80(addr + 1)) << 8) | RdZ80(addr);
}


__inline__ void WrWord(word addr, word value)
{
        WrZ80(addr++, (value & 0xff));
        WrZ80(addr--, (value >> 8));
}

JoycecomUnix.c100600    764    764        6572  6654436635  11436 0ustar  jcejce/** JOYCE PCW emulator for UNIX  *****************************/
/**                                                         **/
/** JoycecomUnix.c: Allow JOYCE access to a pair of files   **/
/** representing input & output channels of SIO             **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#include "Joyce.h"
#include "inline.h"
#include <termios.h>

static int fd_comin, fd_comout;

void com_ems(Z80 *R);	/* in JOYCEFID.C */

static int baud[] = { B50,  B75,   B110,  B134,  B150,  B300,
            	      B600, B1200, B1800, B2400, B2400, B4800,
                      B4800,B9600, B19200,B9600, B19200,B19200,
                      B19200,B57600,B115200};

static int spd = 14;	/* 9600 */

void setspeed(int speed)
{
	struct termios t;

	tcgetattr(fd_comin, &t);
	cfsetispeed(&t, baud[speed]);
	tcsetattr(fd_comin, TCSADRAIN, &t);

	tcgetattr(fd_comout, &t);
	cfsetospeed(&t, baud[speed]);
	tcsetattr(fd_comout, TCSADRAIN, &t);

	spd = speed;
}

static char input_stat(void)
{
        int i;
        fd_set rfds;
        struct timeval tv;

        FD_ZERO(&rfds);
        FD_SET(fd_comin, &rfds);

        tv.tv_sec = tv.tv_usec = 0;

        i = select(1, &rfds, NULL, NULL, &tv);

        if (i) return 1;        /* Data ready */
        else return 0;          /* Data not ready */
}

static char output_stat(void)
{
        int i;
        fd_set rfds;
        struct timeval tv;

        FD_ZERO(&rfds);
        FD_SET(fd_comout, &rfds);

        tv.tv_sec = tv.tv_usec = 0;

        i = select(1, NULL, &rfds, NULL, &tv);

        if (i) return 1;        /* Data ready */
        else return 0;          /* Data not ready */
}

static char com_input(void)
{
	char c;

	if (read(fd_comin, &c, 1) < 1) c = 0x1A;

	return c;	
}

static void com_output(char c)
{
	write(fd_comout, &c, 1);
}



void fid_com(Z80 *R)
{
	if (comstat)
	{
		switch(R->BC.B.l)
		{
			case 7: com_ems(R); break;
			case 8: break;		 /* COM port is a file*/
			case 9: R->AF.B.h = 0x80; break;
		}
		return;
	}

	switch(R->BC.B.l)
	{
		case 0: tcdrain(fd_comout); break;	/* Empty buffers */
		case 1: break;  /* Handshake */
		case 2: setspeed(R->HL.W); break;  /* Set baud */
		case 3: R->AF.B.h = input_stat();     break;
		case 4: R->AF.B.h = com_input();      break;
		case 5: R->AF.B.h = output_stat();    break;
		case 6: com_output(R->DE.B.l);	      break;
		case 7: com_ems(R);		      break;
		case 8: set_com();                    break;
		case 9: R->AF.B.h = 0x80;             break;

	}
}

void com_deinit()
{
	if (fd_comin  >= 0) close(fd_comin);
	if (fd_comout >= 0) close(fd_comout);
}

void com_init()
{
	struct termios t;

	if (jset.com_in[0]  == 0) strcpy(jset.com_in,   "/dev/zero");
	if (jset.com_out[0] == 0) strcpy(jset.com_out,  "/dev/null");

	fd_comin = open(jset.com_in,            O_RDONLY          );
	fd_comout= open(jset.com_out, O_CREAT | O_WRONLY | O_APPEND | O_NDELAY);

        tcgetattr(fd_comin, &t);
        cfmakeraw(&t);
        tcsetattr(fd_comin, TCSADRAIN, &t);

        tcgetattr(fd_comout, &t);
        cfmakeraw(&t);
        tcsetattr(fd_comout, TCSADRAIN, &t);

	
	setspeed(spd);
}


void set_com(void)
{
	com_deinit();
	com_init();
}

JoyceUnix.c100600    764    764        2177  6663343710  10723 0ustar  jcejce#include "Joyce.h"
#include "Joycevga.h"
#include "Joycemou.h"
#include "keyboard.h"

void txt2fcb(word addr, char *s);

word jdos_wildex(Z80 *R)
	{
	char pathname[4 * _POSIX_PATH_MAX];
	int m = 0, n = 0;
	char c;
	glob_t results;

        do {
                c = RdZ80(R->DE.W + m); /* Extract pathname */
                pathname[m] = c;
                ++m;
        } while (c);

	/* Case-insensitivity (from v1.31) removed, now that utilities have
          been rewritten for case-sensitivity */

	if (glob(pathname, GLOB_MARK, NULL, &results)) return 0;
	
        m = R->BC.B.h;

	for (n = 0; n < results.gl_pathc; n++)
        {	
		char *s = results.gl_pathv[n];

		/* Skip directories */
		if (s[0] && s[strlen(s) - 1] == '/') continue;
		if (m == 0) break;  
		--m;
        }
	/* Was a match found? */
	if (n >= results.gl_pathc) { globfree(&results); return 0; }

	/* Work out what we got. */

        txt2fcb(R->HL.W, results.gl_pathv[n]);
        m = (-1);
        do {
                ++m;
                WrZ80(R->HL.W + m + 11, results.gl_pathv[n][m]);
        } while (results.gl_pathv[n][m]);

	globfree(&results);

	return 1;
	}



JoyceGtk.c100600    764    764        5610  6664327770  10531 0ustar  jcejce/** JOYCE PCW emulator for UNIX ******************************/
/**                                                         **/
/** JoyceGtk.c: Interface to the GTK+ file selection widget **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#include "Joyce.h"
#include <gtk/gtk.h>

static int chosen = 0;
static char fname[160];
static int pipefd[2];

/* Get the selected filename and print it to the console */
static void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
{
    chosen = 1;
    strncpy(fname, gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)), 159);
    fname[159] = 0;
    write(pipefd[1], fname, 160);
    gtk_widget_destroy(GTK_WIDGET(fs));
}

static void destroy (GtkWidget *widget, gpointer data)
{
    if (!chosen)
    { 
             strcpy(fname, "*");
             write(pipefd[1], fname, 160);
             chosen = 1;
    }
    gtk_main_quit ();
}


void itemsel_main(char *pattern)
{
    int argc = 1;
    char *pargv;
    char **argv;
    GtkWidget *filew;

    pargv = "xjoyce";
    argv  = &pargv;
 
    gtk_init (&argc, &argv);

    strcpy(fname, "*");	/* None */
    
    /* Create a new file selection widget */
    filew = gtk_file_selection_new ("File selection");
    
    gtk_signal_connect (GTK_OBJECT (filew), "destroy",
			(GtkSignalFunc) destroy, &filew);
    /* Connect the ok_button to file_ok_sel function */
    gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
			"clicked", (GtkSignalFunc) file_ok_sel, filew );
    
    /* Connect the cancel_button to destroy the widget */
    gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
			       "clicked", (GtkSignalFunc) gtk_widget_destroy,
			       GTK_OBJECT (filew));
    
    /* Lets set the filename, as if this were a save dialog, and we are giving
     a default filename */
    gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew), 
				     pattern);
    
    gtk_widget_show(filew);
    chosen = 0;
    gtk_main ();

    exit(0);
}



char *Itemsel(char *pattern)
{
        static char fname[160];
	int status, pid;

        /* We don't start the GTK event loop until we need a file selector.
          And since the GTK event loop can only be started once, but we want
          multiple file selectors, we fork() off a separate selector and
          communicate with it using pipe(). Yes, it's a hack. */

        if (pipe(pipefd)) return NULL;

	pid = fork();
        if (!pid) itemsel_main(pattern);    /* Does not return */
        else
        {
                read(pipefd[0], fname, 160);
		waitpid(pid, &status, 0);
        }
        if (fname[0] == '*') return NULL;
        return fname;
}

Joyce.c100600    764    764       41003  6671032166  10065 0ustar  jcejce/** JOYCE PCW emulator for DOS/DJGPP *************************/
/**                                                         **/
/** JOYCE.C: contains the main PCW specific code            **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1999                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/


/*
 * Note: JOYCE may well contain assumptions that an int is 32 bits or more.
 * I wouldn't recommend porting it to a 16-bit system - the ability
 * to do a malloc(2megs) is quite important :-)
 *
 */

#define MAIN
#include "Joyce.h"
#include "Joycefid.h"
#include "Joyceptr.h"
#include "Joyceset.h"
#include "Joycedos.h"
#include "Joycemou.h"
#include "Joycelgo.h"
#include "Joycedsk.h"
#include "ay8910.h"
#include "inline.h"

void StartCPM(void);
void TrashCPM(void);
void LoadBoot(void);
void UnitJoyce(void);

/* Argument processing */

static int autoboot = -1;

void checkarg(char *s)
{
	if (!strcasecmp(s,"-DEBUG")) DebugMode = 1;
	if ((s[1] == 'B' || s[1] == 'b') && s[2] == '=')
	{
		drive_filename[1] = s + 3;
		printf("Set drive B: to %s\n", drive_filename[1]);
	}
	/* v1.22 Alternative bootfile (undocumented) */ 
	if ((s[1] == 'E' || s[1] == 'e') && s[2] == '=')
	{
		strcpy(bootfile, s + 3);
		printf("Set boot ROM to %s\n", bootfile);
	}
	/* v1.33 Automatic boot (still undocumented) */
	if ((s[1] == 'A' || s[1] == 'a') && s[2] == '=')
	{
		autoboot = atoi(s + 3);
		printf("Automatically booting from boot disc %d\n", autoboot); 
	}
}


/* This function is called once every cycle to check for a reboot 
  (forcing reload of the boot image and hardware reset) and for the
  termination of an FDC operation (jstate.fdcint == 1).

*/

void JoyceZ80(Z80 *R)
{
	if (jstate.reset_flag)
	{
		/* Zero the memory mapped keyboard */
		
		vga_setmode(0);
		memset(&PCWRAM[0xFFF0],0,16);

		/* set Program Counter to 0000H */
		jstate.reset_flag = 1;
		ResetZ80(R);

		/* Page in the bottom 64k of memory */

		mem_page(0xF0,0x80);
		mem_page(0xF1,0x81);
		mem_page(0xF2,0x82);
		mem_page(0xF3,0x83);
		mem_lock(0xF1);
		LoadBoot();	/* Reload the boot ROM */

		jstate.timeron = 1; 
		jstate.timerint = 1;
		jstate.reset_flag = 0;		
		jstate.mouse_patched = 0; /* (v1.27) reset mouse variables */
		jstate.md3_mx = 0;
		jstate.md3_my = 0;
		jstate.md3_mf1 = 0;
		jstate.md3_mf2 = 0;
		jstate.sp_mx = 0;
		jstate.sp_my = 0;	
		mouse_reset();
	}

	/* Check for end of FDC transfer */
	if (jstate.fdcint == 1)
	{
		jstate.inf8 |= 0x20;	/* FDC is interrupting */
		jstate.fdcint = 0;
		switch (jstate.fdc_int_mode)
		{
			case 2: IntZ80(R, 0x66);
				break;

			case 3: IntZ80(R, 0x38);
				break;

			default: jstate.fdcint = 0;
				 break;
		}
	}
	else if (jstate.fdcint > 1) --jstate.fdcint;
}


/* Reload the boot ROM */

void LoadBoot(void)
{	/* Open the boot ROM */

	FILE *fp;
	word address = 0;
	int c;

	if ((fp = fopen(bootfile,"rb")) == NULL) {
		UnitScr();
		fprintf(stderr,"Open of %s failed!  \r\n", bootfile);
		diewith("Failed to open boot file\r\n",98);
	}
	/* Load the boot ROM into Z80 memory */
	while ((c = getc(fp)) != EOF) PCWRAM[address++] = c;

	fclose(fp);
}



/******************* main() *******************/

int init_joyce(int argc, char **argv)
{
	int na;

	joycename=argv[0]+strlen(argv[0]);
	joycedir=".";

	while (--joycename != argv[0])
	{
		if (*joycename=='\\' || *joycename=='/' || *joycename == ':')
		{
			*joycename=0;
			joycename++;
			joycedir=argv[0];
			break;
		}
	}

        /* load default boot image (v1.22: moved to before checkarg() call) */
        strcpy(bootfile, find_boot_file(BOOT_SYS));

        for (na = 1; na < argc; na++)
        {
                if (argv[na][0] == '-') checkarg(argv[na]);
        }

        fplog = fopen(find_joyce_file(LOGFILE),"w");

        if (!read_settings())
        {
                if(jset.verbose) printf("Using default settings\n");
                default_settings();
        }
	return 0;
}





#if defined(__MSDOS__) || defined(USE_SDL)
	/* WX JOYCE uses the wxApp class as its main() */


void main(int argc,char *argv[])
{
        printf("JOYCE PCW Emulator v%x.%x\n", BCDVERS >> 8, BCDVERS & 0xFF);

	init_joyce(argc, argv);

	if (jset.verbose) printf("Joyce directory is %s\n\r",joycedir);

	/* trap signals so the terminal doesn't go to hell
	 * when we exit
	 */
	signal(SIGINT, goodbye);

	atexit(UnitJoyce);

	com_init();

	/* kick off CP/M */
	StartCPM();

	/* Return memory */
	TrashCPM();

	/* we should exit through the goodbye() function
	 * to restore terminal modes and close disk drives
	 */
	goodbye(99);

	/* The logic will never get here */
}


#endif /* def __MSDOS__ || def USE_SDL */

/* Initialise the emulator */

void StartCPM(void)
{
	word A;
	int *T;
	int memsiz;
	int c;

	(void)c;

	/*** STARTUP CODE starts here: ***/

	T=(int *)"\01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
#ifdef LSB_FIRST
	if (*T!=1) {
		fprintf(stderr, "********** This machine is high-endian. **********\r\n");
		fprintf(stderr, "Take #define LSB_FIRST out and compile JOYCE again.\r\n");
		fflush(stderr);
		goodbye(99);
	}
#else
	if (*T==1) {
		fprintf(stderr, "********* This machine is low-endian. **********\r\n");
		fprintf(stderr, "Insert #define LSB_FIRST and compile JOYCE again.\r\n");
		goodbye(99);
	}
#endif

/* Set initial values */
	jstate.inf8 = 0x30;
	jstate.fdcint = 0;
	jstate.timerint = 1;
	jstate.timeron = 1;	//0;
	jstate.reset_flag = 0;

#ifdef __MSDOS__
/* Allocate 800x600 memory for a screen buffer. */

	Framebuf=malloc(800*600);
	if (!Framebuf) memsiz = 0; 
	else memsiz = mem_alloc();
#else	/* __MSDOS__ */
	memsiz = mem_alloc();
#endif
	if (!memsiz)
	{
#ifdef __MSDOS__
		if(Framebuf) free (Framebuf);
#endif
		fprintf(stderr,"Sorry... Not enough memory to run JOYCE.\r\n");
		fflush(stderr);
		goodbye(99);
	}
	else if (jset.verbose)	/* Say how much memory we got */
		printf("This is a PCW 8%d\r\n",memsiz);

#ifdef __MSDOS__	/* Don't bother under UNIX */
	cputs("Press SPACE to continue...\r\n");
	c=getch();
#endif

	InitPtr();	  /* Initialise the printer */
	InitFDC(0);	  /* Initialise the emulated FDC */
	InitAdlib();
	if (!InitScr(0))  /* Initialise the emulated screen */
	{
		TrashAdlib();
		fprintf(stderr,"Sorry, could not select 800x600 graphics mode.\n");
		fprintf(stderr,"%s needs at least 800x600 8-bit graphics.\n",joycename);
		exit(2);
	}
	InitMouse();	  /* Initialise the mouse */

	/* All "outside functions" are regulated in JOYCE by a
	  simulated 900Hz clock. For a 4MHz CPU, this is every
	  4e6/9e2 = 4444 cycles, or about 500 instructions. 
          On a PCW with a Sprinter, double this; but if the interrupts get 
          too infrequent, they will never occur when EI is on. I'm 
          using 4441 rather than 4444 because it's prime, and so we're 
          less likely to get problems caused by interrupts synchronising 
          exactly with a tight loop. */

	jstate.Rg.IPeriod=4441;  /* Every 4441 cycles (~900Hz) check if
			  something needs doing */

	/* Initialise the emulated keyboard */
	if (!InitKbd()) diewith("DPMI error\r\n",98);

	LoadBoot();

	extern_stop = 0;	/* v1.31 */

	/* set Program Counter to 0000H */
	ResetZ80(&jstate.Rg);
	jstate.Rg.Trace = 0;
	jstate.Rg.Trap = 0xFFFF;

	/* Page in the bottom 64k of memory */

	mem_page(0xF0,0x80);
	mem_page(0xF1,0x81);
	mem_page(0xF2,0x82);
	mem_page(0xF3,0x83);

	if (autoboot > 0 && fdc_boot(autoboot))
	{
		jstate.Rg.PC.W = 0xF010;
		jstate.Rg.SP.W = 0xF000;
		jstate.Rg.IFF = 0;
	}


	/* Finally... set the Z80 going... */

	A=RunZ80(&jstate.Rg);

	/*if(jset.verbose)*/ cprintf("EXITED at PC = %Xh.\r\n",A);

}



/****************************************************************/
/*** Extended JOYCE instructions. This is called on the ED FD ***/
/*** instruction to bypass anything in the BIOS that needs    ***/
/*** bypassing. There are no register parameters; the 	      ***/
/*** function is calculated from the address of the call.     ***/
/****************************************************************/

void BIOSZ80(Z80 *R)
{
	/* (v1.26) Support for printer diversion */

	if (R->PC.W < 0xFC12 && R->PC.W >= 0xFC0F) /* LIST */
	{
		list(R);
	}
	if (R->PC.W < 0xFC30 && R->PC.W >= 0xFC2D) /* LISTST */
	{
		listst(R);
	}
	/* (v1.26) ends */

	if (R->PC.W < 0xFC51 && R->PC.W >= 0xFC4E) /* TIME */
	{
		word temp = R->DE.W;

		R->DE.W = 0xFBF4;
		jdos_time(R, 1);

		R->DE.W = temp;
	}
}




/****************************************************************/
/*** Extended JOYCE instructions. This is called on the ED FE ***/
/*** instruction to emulate anything that distinguishes JOYCE ***/
/*** from a real PCW					      ***/
/****************************************************************/

void PatchZ80(Z80 *R)
{
	switch(R->AF.B.h)	/* Register A */
	{
		case 0x00:	/* Get JOYCE version */
			R->AF.B.h = 0xFF;	/* Utility compatibility number. 
						 * 0  for a real Z80
						 * -1 for Joyce 1.00+ */
			R->HL.W = BCDVERS;	/* JOYCE version */
			R->DE.W = 'J';		/* Emulator ID */
			break;

		case 0x01:
/*
       A=1:  Boot. If B=0, read sector 1 from floppy drive & enter it;
		   If B>0, read sector 1 from image .\BOOT\BOOTn.DSK and
			   enter it.
*/
			if (fdc_boot(R->BC.B.h))
			{
				R->PC.W = 0xF010;
				R->SP.W = 0xF000;
				R->IFF = 0;
			}
			break;

		case 0x02: 
/*
	A=2: Set colour. BCD = 'white' RGB
			 EHL = 'black' RGB
*/
			screen_colour( R->BC.B.h, R->BC.B.l, R->DE.B.h,
				       R->DE.B.l, R->HL.B.h, R->HL.B.l);
			break;

/* 
	A=3 - A=7: Service a call from a disc driver FID

*/
		case 0x03:
			fid_d_login(R);
			break;
		case 0x04:
			fid_d_read(R);
			break;
		case 0x05:
			fid_d_write(R);
			break;
		case 0x06:
			fid_d_flush(R);
			break;
		case 0x07:
			fid_ems(R);
			break;
		case 0x08:		/* A=8: Save settings */
			write_settings(); 			
			break;
		case 0x09:		/* A=9: Switch which LPT we're using */
			R->HL.W = jset.cen_port;
			if (R->DE.W != 0xFFFF) jset.cen_port = R->DE.W;
			break;
		case 0x0A:		/* A=10: Access DOS filesystem */
			jdos(R);
			break;
		case 0x0B:
			fid_char(R);	/* A=11: Screen control FID */
			break;
		case 0x0C:
			KeyboardED(R);	/* A=12: low-level keyboard mapping */
			break;
		case 0x0D:
			fid_com(R);	/* (v1.21) A=13: COMPORT.FID */
			break;
		case 0x0E:
			fid_d_mess(R);	/* (v1.22) FID disc messages */
			break;
		case 0x0F:		/* (v1.22) LOGO */
			dr_logo(R);
			break;
		case 0x10:		/* (v1.22) parse BOOT/BOOT.CFG */
			boot_id(R);
			break;
		case 0xFC:		/* (v1.30) CPMREDIR interface */
			cpmredir_intercept(R);
			break;
		case 0xFD:		/* (v1.27) MD3 mouse patch */
			mouse_patch(R);
			break;
		case 0xFE:		/* Set JOYCE timing parameters */
			R->HL.W   = R->IPeriod;
			R->AF.B.h = jset.screen_refresh;
			if (R->BC.B.l) jset.screen_refresh = R->BC.B.l;
			if (R->DE.W) R->IPeriod = R->DE.W;
			break;
		case 0xFF:		/* Halt & catch fire */
			goodbye(R->HL.W);
			break;

		default:		/* Invalid ED xx */
			joyce_dprintf("Unknown operation: ED FE with A=%02x at PC=%04x",
				R->AF.B.h, R->PC.W);
			break;
	}
	return;
}


/****************************************************************/
/*** Write byte to given IO port.			      ***/
/****************************************************************/
void OutZ80(register word Port,register byte Value)
{
	switch(Port & 0xFF)
	{
		case 0: /* Floppy disc controller */
		case 1: FDCport(Port,Value); break;

		case 0x84: /* CEN: */
		case 0x85:
		case 0x86:
		case 0x87: cen_out(Port, Value); 

		case 0xA0: /* AY-8910 */
		case 0xA1:
		case 0xA2:
		case 0xA3: AYPort(Port, Value);

		case 0xE1: /* (v1.24) These are serial port accesses,   */
		case 0xE2: /*        but I'm using them as a convenient */ 
		case 0xE3: /*        way of starting the timer. Sadly   */
		case 0xE4: /*        this doesn't work for Loco 1       */
		case 0xE5: /*        because it doesn't use the SIO     */
		case 0xE7: /*  */
		jstate.timeron= 1;
		break;

		case 0xF0: /* Memory paging */
		case 0xF1:
		case 0xF2:
		case 0xF3: mem_page(Port,Value); break;

		case 0xF4: mem_lock(Value); break; 

		case 0xF5: /* Video controller */
		case 0xF6:
		case 0xF7: VidPort(Port,Value); break;

		case 0xF8: /* Odds and ends */
			   switch(Value)
		{
			case 1: /* Reboot! */
			jstate.reset_flag = 1;
			break;

			/* Zero the memory mapped keyboard */

			memset(&PCWRAM[0xFFF0],0,16);

			/* set Program Counter to 0000H */
			jstate.reset_flag = 1;
			jstate.Rg.PC.W = 0x0000;

			/* Page in the bottom 64k of memory */

			mem_page(0xF0,0x80);
			mem_page(0xF1,0x81);
			mem_page(0xF2,0x82);
			mem_page(0xF3,0x83);
			mem_lock(0xF1);
			LoadBoot();	/* Reload the boot ROM */

			break;

			case 2: /* Change the way FDC interrupts */
			case 3:
			case 4:
			jstate.fdc_int_mode = Value;
			break;

			case 5:	/* Set the FDC terminal count */
			case 6:	/* Setting it aborts data transfer */
			jstate.fdc_terminal_count = Value;
			fdc_set_tcount(Value);
			break;

			case 7:
			screen_on();
			break;

			case 8:
			screen_off();
			break;

			case 9: /* FDC motors on */
			fdc_mstart();
			break;

			case 10: /* FDC motors off */
			fdc_mstop();
			break;

			case 11: /* Beeper on */
			beep_on();
			break;

			case 12: /* Beeper off */
			beep_off();
			break;

			default:
			break;
		}
		break;

		case 0xFC:	/* Printer control, ignored */
		case 0xFD:	/* (v1.20) */
		break;

		default:
		joyce_dprintf("OUT 0x%02x, 0x%02x\n", Port, Value);
		break;
	}
}

/****************************************************************/
/*** Read byte from given IO port.			      ***/
/****************************************************************/
byte InZ80(register word Port)
{
	byte v;

	switch (Port & 0xFF)
	{
		case 0: /* Floppy disc controller */
		case 1:
		return(FDCin(Port));
		break;

		case 0x84: /* CEN: */
		case 0x85:
		case 0x86:
		case 0x87: return cen_in(Port & 0xFF); 

		case 0xF4:
		v = jstate.inf8;
		jstate.inf8 &= 0xF0;	/* Reset 300Hz counter */
		return v;

		case 0xF8:	/* Odds & ends */
		return(jstate.inf8);
		break;

		case 0xFC:	/* (v1.20) Printer */
		return 0xF8;	/* No printer */
		break;

		case 0xFD:	/* (v1.20) Printer */
		if (jstate.model_type == 1) return 0xFD; /* 9512 */
		return 0xC8;	/* No printer */
		break;

		case 0xA0:	/* AMX mouse */
		case 0xA1:
		case 0xA2:
		case 0xD0:	/* Kempston mouse */
		case 0xD1:
		case 0xD2:
		case 0xD3:
		case 0xD4:
		return MouseIn(Port & 0xFF);
		break;
	}
	return(-1);
}


/****************************************************************/
/*** This function is called at 900Hz. It has to implement    ***/
/*** all PCW features which rely on time.		      ***/
/****************************************************************/
word LoopZ80(Z80 *R)
{
	static int Tcount=0, Scount=0, Rcount=0;

	if (extern_stop) return INT_QUIT;

	{
		vga_timer();
		Tcount++;		/* Tickers used to drop the 900Hz */
		Scount++;		/* down to 50Hz & 300Hz */
		Rcount++;

		if (Scount >= jset.screen_refresh) /* Draw the screen at 50Hz */
		{
			DrawScr();	   /* Redraw the screen */
			Scount=0;
		}
		if (Rcount == 18) Rcount = 0;
		if (Rcount < 3) 	       /* In the vertical retrace */
			jstate.inf8 |= 0x40;
		else	jstate.inf8 &= 0xBF;

		PollKbd(0, R);	/* Poll the keyboard */

		if (Tcount >= 3) /* 300Hz signal */
		{
			Tcount = 0;
			if ((jstate.inf8 & 0x0F) < 0x0F) jstate.inf8++;	/* Increase the 300Hz counter */
			if (jstate.timeron || jstate.timerint)
			{
				jstate.timerint = 0;
				if (R->IFF & 0x01)     /* EI */ return 0x0038;
				R->IRequest = 0x0038;  /* Pending INT */
				return 0x0038;
			}
		}
	}
	return 0xFFFF;	/* No interrupt */
}

/* Free memory */

void TrashCPM(void)
{
	/* de allocate memory for Z80 CPU */
	if (PCWRAM) free(PCWRAM);
	PCWRAM=NULL;
#ifdef __MSDOS__
	if (Framebuf) free(Framebuf);
	Framebuf=NULL;
#endif
}


/* signal trap - reset tty modes and leave program
 * 99 is used if the program wants to exit gracefully,
 */
void goodbye(int sig)
{
	if (fplog)
	{
		if (sig != 99) fprintf(fplog, "Stopped by signal: %d\n", sig);
		fclose(fplog);
	}
	if(PCWRAM) free(PCWRAM);
#ifdef __MSDOS__
	if(Framebuf) free(Framebuf);
#endif
	fid_cleanup();
	UnitKbd();
	UnitPtr();	/* (v1.11) */
	UnitFDC();
	UnitMouse();
        UnitScr();
	TrashAdlib();
	exit(sig);
}


/* As goodbye(), but prints a farewell message */

void diewith(char *s, int sig)
{
	if (fplog)
	{
		fprintf(fplog, "Fatal error %d: %s", sig, s);
		fclose(fplog);
	}
	if(PCWRAM) free(PCWRAM);
#ifdef __MSDOS__
	if(Framebuf) free(Framebuf);
#endif
	fid_cleanup();
	UnitPtr();	/* (v1.11) */
	UnitKbd();
	UnitFDC();
	UnitScr();
	UnitMouse();
	TrashAdlib();
	cputs(s);
	cputs("Press SPACE to leave...\n\r");
	getch();
	exit(sig);
}



void UnitJoyce(void)	/* v1.21 The very last things done */
{
	fid_fd_unit();	/* v1.22 */
	com_deinit();
}
Joycemem.c100600    764    764        5255  6653706156  10564 0ustar  jcejce/** JOYCE PCW emulator for DJGPP *****************************/
/**                                                         **/
/** JOYCEMEM: Memory manager                                **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1998                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#include "Joyce.h"

/* The idea of the paging is that PCWRD[addr>>14][addr] will give the 
  address of the byte at addr. Separate read and write pointers are 
  maintained because of the CPC-like paging mode (which AFAIK is not 
  used by any PCW software). */

static unsigned char last_mpage[4] = { 0x80, 0x81, 0x82, 0x83 };

static int nbanks;

/* Allocate the memory */

int mem_alloc(void)
{
	long siz=2097152;        /* 2048k, ie 2 megs */

	if (PCWRAM) return (nbanks*16);

	while (siz>262144)  /* ie 256k */
	{
		PCWRAM=malloc(siz);
		if (PCWRAM)
		{
			nbanks=siz/16384;
			memset(PCWRAM, 0, siz);
			return(siz/1024);
		}
		siz-=262144;
	}
	return(0);
}


/* Process an OUT 0xF4. If one of the top 4 bits is set, memory reads and
 * writes will be to the same bank. This is only ever a concern if the 
 * CPC-like paging system is used; according to Richard Clayton CP/M and
 * LocoScript do not use this system.
 */

void mem_lock(unsigned char value)
{
	int np;

	bank_lock[0] = value & 0x40;
	bank_lock[1] = value & 0x10;
	bank_lock[2] = value & 0x20;
	bank_lock[3] = value & 0x80;

	/* If memory was paged the CPC way, then paging may have to be
          recalculated */

	for(np = 0; np < 4; np++)
	{
		if (!(last_mpage[np] & 0x80)) mem_page(np, last_mpage[np]);
	}
}

		
void mem_page(unsigned char port, unsigned char value)
{
	int bankoff;

	port &=3;               /* This handles the memory paging. */
	last_mpage[port] = value;
	if (value & 0x80)	/* `Extended' (PCW-like) paging */
	{
		value = value % nbanks;      /* The idea is that PCWRD[addr>>14] */
		bankoff=(value-port)*16384;  /* +addr gives the correct address */
		PCWWR[port] = PCWRD[port] = PCWRAM+bankoff;
	}
	else
	{
		int rv, wv;
		rv = (value >> 4) & 7;
		wv = value & 7;
		bankoff=(rv-port)*16384;  /* +addr gives the correct address */
		PCWRD[port] = PCWRAM+bankoff;
		bankoff=(wv-port)*16384;  /* +addr gives the correct address */
		PCWWR[port] = PCWRAM+bankoff;
		if (bank_lock[port]) PCWRD[port] = PCWWR[port];
	}
	
}


/* Read and write the PCW's memory - inlined
 *
 *
 *byte RdZ80(word addr)
 *{
 *	return (PCWRD[addr>>14])[addr];
 *}
 *
 *
 *void WrZ80(word addr,byte v)
 *{
 *	(PCWWR[addr>>14])[addr]=v;
 *}
 *
 */
Joycefdc.c100600    764    764       65053  6663343750  10562 0ustar  jcejce/** JOYCE PCW emulator for DJGPP *****************************/
/**                                                         **/
/** JOYCEFDC.C - uPD765A floppy disc controller             **/
/**                                                         **/
/** Copyright (c) John Elliott 1996-1998                    **/
/**     You are not allowed to sell this software for       **/
/**     profit.                                             **/   
/*************************************************************/

#include "Joyce.h"

byte fdctrace;

/* PC ports for the host FDC - currently unused */
#define FDC_SELECT 0x3F2
#define FDC_STATUS 0x3F4
#define FDC_DATA   0x3F5

/* Max no. of bytes that can be buffered by this simulated FDC */

#define MAX_SECTOR_LEN 8192

/* Cause of an FDC interrupt. 
 * 
 * 1 => Entering result phase of Read/Write/Format/Scan command
 * 2 => Ready for data transfer (in execution phase)
 * 4 => End of Seek or Recalibrate command
 */

static int int_cause;

/* The FDC status registers: st0, st1, st2, st3 */

static byte st0, st1, st2, st3;

/* The results from the SPECIFY command */

static byte spec1, spec2;

/* The last drive for which diskimage header info was requested */
/* Set this to -1 if any image file is closed */

static int last_info_request = -1;

/* The last sector for which a DD READ ID request was made. Reset by
  seeks, reads, writes etc. */

static int last_id_request = 0;

/* Buffers for data transfer in and out of the FDC */

static int cur_cmd = -1;	/* Command being read in, -1 for none */
static int cur_cnt = 0;		/* No. of bytes read so far in this command */
static int fdc_cmd[20];		/* Buffer for command bytes received */

static byte fdc_xfer[MAX_SECTOR_LEN];	/* Execution phase buffer */
static int fdc_xfercnt;			/* No. of bytes to transfer */
static int fdc_xfercur;			/* Bytes transferred so far */

static int fdc_rsl[20]; 		/* Result bytes to return */
static int fdc_rslcnt;			/* No. of bytes to transfer */
static int fdc_rslcur;			/* Bytes returned so far */

/* The lengths of the standard commands, 1 for unknown commands */

static int bytes_in_cmd[32] = { 1,  /* 0 = none */
				1,  /* 1 = none */  
				9,  /* 2 = READ TRACK, not implemented */
				3,  /* 3 = SPECIFY */
				2,  /* 4 = SENSE DRIVE STATUS */ 
				9,  /* 5 = WRITE DATA */ 
				9,  /* 6 = READ DATA */
				2,  /* 7 = RECALIBRATE */
				1,  /* 8 = SENSE INTERRUPT STATUS */ 
				9,  /* 9 = WRITE DELETED DATA, not implemented */
				2,  /* 10 = READ SECTOR ID */
				1,  /* 11 = none */
				9,  /* 12 = READ DELETED DATA, not implemented */
				6,  /* 13 = FORMAT A TRACK */
				1,  /* 14 = none */ 
				3,  /* 15 = SEEK */
				1,  /* 16 = none */
				9,  /* 17 = SCAN EQUAL */
				1,  /* 18 = none */
				1,  /* 19 = none */
				1,  /* 20 = none */
				1,  /* 21 = none */
				1,  /* 22 = none */
				1,  /* 23 = none */ 
				1,  /* 24 = none */
				9,  /* 25 = SCAN LOW OR EQUAL */
				1,  /* 26 = none */
				1,  /* 27 = none */
				1,  /* 28 = none */
				1,  /* 29 = none */
				9,  /* 30 = SCAN HIGH OR EQUAL */
				1   /* 31 = none */
			      };

static int more_bytes = 0;	/* No. of bytes remaining in current command */

static void fdc_write_end(void);	/* Called after data transferred in a Write command */
static void fdc_fmt_end(void);		/* Called after data transferred in a Format command */

static byte image_header[256];		/* The header of a CPCEMU-format disc image */
					/* Its magic number is "MV - CPC" at 0 */
					/* Some useful offsets in the header: */
#define OFFS_TRK  0x30	/* No. of tracks */
#define OFFS_HEAD 0x31	/* No. of heads */
#define OFFS_TLEN 0x32  /* Bytes / track, little-endian word */

#ifdef __MSDOS__
struct diskinfo_t bios_info;	/* parameters for a BIOS call */
#endif

/* Create a new, empty CPCEMU disc image */

int img_new(int unit)
{
	FILE *fp;
	int err;

	unit &= 1;
	fp = fopen(drive_filename[unit], "wb");
	if (!fp) 
	{
		joyce_dprintf("Cannot open %s\n", drive_filename[unit]);
		return 0;
	}

	memset(image_header, 0, 256);	
	sprintf((char *)image_header,"MV - CPCEMU Disk-File\r\nDisk-Info\r\n(JOYCE)");
	err = fwrite(image_header, 1 , 256, fp);
	fclose(fp);
	return (err == 256);
}

/* The following code i