#include <stdio.h>
#include <X11/Xlib.h>
#include <stdlib.h>
#include <string.h>
#include "memtype.h"
#include "mem.h"
#include "6502.h"
#include "atari.h"


/* Atari hardware emulation
 *  by Frank Barrus
 */

extern char errmsg[];


static void timeout(int timer)
{
	SET_IRQ(timer+1);
}


static uint pokey_mem_read(uint addr)
{
/* static long seed = 6926492;	 my old Westford phone # */
register int reg;
	reg = addr & MASK_POKEY;
	switch(reg) {
	case R_POT0: case R_POT0+1: case R_POT0+2: case R_POT0+3:
	case R_POT0+4: case R_POT0+5: case R_POT0+6: case R_POT0+7:
		return sys.paddle[reg-R_POT0];
	case R_ALLPOT:
		break;
	case R_KBCODE:
		if(sys.keycode == 0xff) {
			sys.keycode = sys.keylast;
			sys.keylast = 0xff;
		}
		printf("Scanning for keys: key=%2x\n", sys.keycode);
		return sys.keycode;
	case R_RANDOM:
/*
		seed = seed*1103515245+12345;
		return seed&0xff;	
*/
		return random() & 0xff;
	case R_SERIN:
		return sys.serin;
	case R_IRQST:
		return sys.irq_status^0xff;
	case R_SKSTAT:
		return (sys.sk_status^0xff)|1;
	}
	if(sys.flags & MFLAG_DEBUG)
		printf("POKEY: invalid read %04x\n", addr);
	return 0;
}

static uint pia_mem_read(uint addr)
{
register int reg;
	reg = addr & MASK_PIA;
	switch(reg) {
	case RW_PORTA:
		return(((sys.stick[1]<<4) | sys.stick[0]) ^ 0xff);
	case RW_PORTB:
		if(sys.systype == SYSTYPE_800)
			return(((sys.stick[3]<<4) | sys.stick[2]) ^ 0xff);
		else
			return sys.portb;
	case RW_PACTL:
		return(sys.pactl);
	case RW_PBCTL:
		return(sys.pbctl);
	}
	if(sys.flags & MFLAG_DEBUG)
		printf("PIA: invalid read %04x\n", addr);
	return(0);
}

static uint gtia_mem_read(uint addr)
{
register int reg;
	reg = addr & MASK_GTIA;
	switch(reg) {
	case R_M0PF: case R_M0PF+1: case R_M0PF+2: case R_M0PF+3:
		return(sys.mpf[reg-R_M0PF]);
	case R_P0PF: case R_P0PF+1: case R_P0PF+2: case R_P0PF+3:
		return(sys.ppf[reg-R_P0PF]);
	case R_M0PL: case R_M0PL+1: case R_M0PL+2: case R_M0PL+3:
		return(sys.mpl[reg-R_M0PL]);
	case R_P0PL: case R_P0PL+1: case R_P0PL+2: case R_P0PL+3:
		return(sys.ppl[reg-R_P0PL]);
	case R_TRIG0: case R_TRIG0+1: case R_TRIG0+2: case R_TRIG0+3:
		return(sys.strig[reg-R_TRIG0]^1);
	case R_PAL:
		return(0x0d);
	case RW_HITCLR:
		sys.mpf[0] = sys.mpf[1] = sys.mpf[2] = sys.mpf[3] = 0;
		sys.ppf[0] = sys.ppf[1] = sys.ppf[2] = sys.mpf[3] = 0;
		sys.mpl[0] = sys.mpl[1] = sys.mpl[2] = sys.mpl[3] = 0;
		sys.ppl[0] = sys.ppl[1] = sys.ppl[2] = sys.ppl[3] = 0;
		return(0);
	case RW_CONSOL:
		return(sys.console & 7);
	}
	if(sys.flags & MFLAG_DEBUG)
		printf("GTIA: invalid read %04x\n", addr);
	return(0);
}

static uint antic_mem_read(uint addr)
{
register int reg;
	reg = addr & MASK_ANTIC;
	switch(reg) {
	case R_VCOUNT:
		return(sys.vcount/2);
	case R_PENH:
		return(sys.penh);
	case R_PENV:
		return(sys.penv);
	case R_NMIST:
		return(sys.nmi_status);
	}
	if(sys.flags & MFLAG_DEBUG)
		printf("ANTIC: invalid read %04x\n", addr);
	return(0);
}

static void pokey_mem_write(uint addr, uint val)
{
register int reg;
	reg = addr & MASK_POKEY;
	switch(reg) {
	case W_AUDF0: case W_AUDF0+2: case W_AUDF0+4: case W_AUDF0+6:
		sys.audf[(reg-W_AUDF0)/2]=val;
		interface_sound_update();
		break;
	case W_AUDC0: case W_AUDC0+2: case W_AUDC0+4: case W_AUDC0+6:
		sys.audc[(reg-W_AUDC0)/2]=val;
		interface_sound_update();
		break;
	case W_AUDCTL:
		sys.audctl = val;
		if(val&0x01)
			sys.clockbase = 64002;
		else
			sys.clockbase = 15059;
		sys.clock[0] = sys.clockbase;
		sys.clock[1] = sys.clockbase;
		sys.clock[2] = sys.clockbase;
		sys.clock[3] = sys.clockbase;
		if(val&0x40) {
			sys.clock[0] = CPUHZ;
			sys.clock[1] = CPUHZ;
		}
		if(val&0x20) {
			sys.clock[2] = CPUHZ;
			sys.clock[3] = CPUHZ;
		}
		interface_sound_update();
		break;
	case W_STIMER: 
		if(sys.audctl & 0x10) {
			sys.timer[0] = 0;
			sys.timer[1] = CPUHZ/(sys.clock[1]/
			   (WORD(sys.audf[1],sys.audf[0])+1));
		} else {
			sys.timer[0] = CPUHZ/(sys.clock[0]/
				(sys.audf[0]+1));
			sys.timer[1] = CPUHZ/(sys.clock[1]/
				(sys.audf[1]+1));
		}
		sys.timer[2] = 0;
		if(sys.audctl & 0x08)
			sys.timer[3] = CPUHZ/(sys.clock[3]/
					(WORD(sys.audf[3],sys.audf[2])+1));
		else 
			sys.timer[3] = CPUHZ/(sys.clock[3]/(sys.audf[3]+1));
		if(sys.flags & MFLAG_DEBUG) {
			printf("Timer start: %ud, %ud, %ud\n",
				sys.timer[0], sys.timer[1], sys.timer[2]);
			printf("audf: %d %d %d %d (%02x)\n",
				sys.audf[0], sys.audf[1], sys.audf[2], 
				sys.audf[3], sys.audctl);
		}
		cpu_schedule(SCHED_TIMER0,sys.timer[0],timeout,0); 
		cpu_schedule(SCHED_TIMER1,sys.timer[1],timeout,1); 
		cpu_schedule(SCHED_TIMER3,sys.timer[3],timeout,3); 
		break;
	case W_SKREST:
		sys.sk_status &= ~0xe0;	/* reset bits 5-7 */
		break;
	case W_POTGO:
		break;
	case W_SEROUT:
		sio_write(val);
		break;
	case W_IRQEN:
		sys.irq_enable = val;
		sys.irq_status &= val;
		if(sys.irq_status)
			cpu_irq();
		break;
	case W_SKCTL:
		if(val&0x80)
			printf("device--> break\n");
		break;
	default:
		if(sys.flags & MFLAG_DEBUG)
			printf("POKEY: invalid write %02x->%04x\n", val, addr);
	}
}

static void pia_mem_write(uint addr, uint val)
{
register int reg;
	reg = addr & MASK_PIA;
	switch(reg) {
	case RW_PORTA:
		sys.porta = val;
		break;
	case RW_PORTB:
		if(sys.systype == SYSTYPE_800) {
			sys.portb = val;
			break;
		}
		if(addr != 0xd301)
			break;
		if((val&0x80) && !(sys.portb&0x80)) 
			atari_mem_direct(MBASE_STROM, MSIZE_STROM, MTYPE_RAM);
		if(!(val&0x80) && (sys.portb&0x80)) 
			atari_mem_direct(MBASE_STROM, MSIZE_STROM, MTYPE_ROM);
		if((val&0x02) && !(sys.portb&0x02)) 
			atari_mem_direct(MBASE_CARTA, MSIZE_CARTA, MTYPE_RAM);
		if(!(val&0x02) && (sys.portb&0x02)) 
			atari_mem_direct(MBASE_CARTA, MSIZE_CARTA,MTYPE_CARTA);
		if((val&0x01) && !(sys.portb&0x01)) {
			atari_mem_direct(MBASE_OSROM1,MSIZE_OSROM1,MTYPE_ROM);
			atari_mem_direct(MBASE_OSROM2,MSIZE_OSROM2,MTYPE_ROM);
		}
		if(!(val&0x01) && (sys.portb&0x01)) {
			atari_mem_direct(MBASE_OSROM1,MSIZE_OSROM1,MTYPE_RAM);
			atari_mem_direct(MBASE_OSROM2,MSIZE_OSROM2,MTYPE_RAM);
		}
		if(sys.systype != SYSTYPE_XE || ((val&0x3c) == (sys.portb&0x3c))) {
			sys.portb = val;
			break;
		}
		if(val&0x10 || sys.extramsize < 0x10000) 
			atari_mem_direct(MBASE_BANK,MSIZE_BANK,MTYPE_RAM); 
		else 
			atari_extmem_direct(MBASE_BANK,MSIZE_BANK,
				0x10000+((val&0x0c)<<12), MTYPE_RAM);
		if(!(val&0x80)) 
			atari_mem_direct(MBASE_STROM, MSIZE_STROM, MTYPE_ROM);
		sys.portb = val;
		break;
	case RW_PACTL:
		sys.pactl = val;
		break;
	case RW_PBCTL:
		if((sys.pbctl^val)&8) 
			if(val&8)
				sio_cmd_end();
			else
				sio_cmd_start();
		sys.pbctl = val;
		break;
	default:
		if(sys.flags & MFLAG_DEBUG)
			printf("PIA: invalid write %02x->%02x\n", val, addr);
	}
}

static void gtia_mem_write(uint addr, uint val)
{
register int reg;
	reg = addr & MASK_GTIA;
	switch(reg) {
	case W_HPOSP0: case W_HPOSP0+1: case W_HPOSP0+2: case W_HPOSP0+3:
		sys.hposp[reg-W_HPOSP0] = val;
		break;
	case W_HPOSM0: case W_HPOSM0+1: case W_HPOSM0+2: case W_HPOSM0+3:
		sys.hposm[reg-W_HPOSM0] = val;
		break;
	case W_SIZEP0: case W_SIZEP0+1: case W_SIZEP0+2: case W_SIZEP0+3:
		sys.sizep[reg-W_SIZEP0] = val;
		break;
	case W_SIZEM:
		sys.sizem[0] = val&3;
		sys.sizem[1] = (val>>2)&3;
		sys.sizem[2] = (val>>4)&3;
		sys.sizem[3] = (val>>6)&3;
		break;
	case W_GRAFP0: case W_GRAFP0+1: case W_GRAFP0+2: case W_GRAFP0+3:
		sys.grafp[reg-W_GRAFP0] = val;
		break;
	case W_GRAFM:
		sys.grafm = val;
		break;
	case W_COLPM0: case W_COLPM0+1: case W_COLPM0+2: case W_COLPM0+3:
		sys.colpm[reg-W_COLPM0] = sys.pixel[val];
		break;
	case W_COLPF0: case W_COLPF0+1: case W_COLPF0+2: case W_COLPF0+3:
		sys.colpf[reg-W_COLPF0+1] = sys.pixel[val];
		break;
	case W_COLBK:
		sys.colpf[0] = sys.pixel[val];
		break;
	case W_PRIOR:
		sys.prior = val;
		break;
	case W_VDELAY:
		break;
	case W_GRACTL:
		sys.gractl = val;
		break;
	case RW_HITCLR:
		sys.mpf[0] = sys.mpf[1] = sys.mpf[2] = sys.mpf[3] = 0;
		sys.ppf[0] = sys.ppf[1] = sys.ppf[2] = sys.ppf[3] = 0;
		sys.mpl[0] = sys.mpl[1] = sys.mpl[2] = sys.mpl[3] = 0;
		sys.ppl[0] = sys.ppl[1] = sys.ppl[2] = sys.ppl[3] = 0;
		break;
	case RW_CONSOL:
		if((val&8) != (sys.speaker&8))
			interface_click(val&8);
		sys.speaker = val;
		break;
	default:
		if(sys.flags & MFLAG_DEBUG)
			printf("GTIA: invalid write %02x->%04x\n", val, addr);
	}
}

static void antic_mem_write(uint addr, uint val)
{
register int reg;
	reg = addr & MASK_ANTIC;
	switch(reg) {
	case W_DMACTL:
		if(val != sys.dmactl) {
			sys.dmactl = val;
			sys.dmactl_pfwidth = val&0x03;	
			sys.dmactl_missile = val&0x04;
			sys.dmactl_player = val&0x08;
			sys.dmactl_single = val&0x10;
			sys.dmactl_dma	= val&0x20;
			sys.refresh = True;	
		}
		break;
	case W_CHACTL:
		break;
	case W_DLISTL:
		if(val != LO(sys.dlist)) {
			sys.dlist = REPLACE_LO(sys.dlist, val);
			sys.refresh = True;
		}
		break;
	case W_DLISTH:
		if(val != HI(sys.dlist)) {
			sys.dlist = REPLACE_HI(sys.dlist, val);
			sys.refresh = True;
		}
		break;
	case W_HSCROL:
		val &= 15;
		if(sys.hscroll != val) {
			sys.hscroll = val;
			sys.refresh = True;
		}
		break;
	case W_VSCROL:
		val &= 15;
		if(sys.vscroll != val) {
			sys.vscroll = val;
			sys.refresh = True;
		}
		break;
	case W_PMBASE:
		sys.pmbase = WORD(0,val);
		break;
	case W_CHBASE:
		if(HI(sys.chbase) != val) {
			sys.chbase = WORD(0,val);
			sys.refresh = True;
		}
		break;
	case W_WSYNC:
		cpu.sync = True;
		break;
	case W_NMIEN:
		sys.nmi_enable = val;
		break;
	case W_NMIRES:
		sys.nmi_status = 0;
		break;
	default:
		if(sys.flags&MFLAG_DEBUG)
			printf("ANTIC: invalid write %02x->%04x\n", val, addr);
	}
}


static uint ram_mem_read(uint addr)
{
	return(sys.ram[addr]);
}

static void ram_mem_write(uint addr, uint val)
{
	sys.ram[addr] = val;
	sys.pixline[sys.wscan[addr]&0x1ff].update
			 = (sys.wscan[addr]&0xfe00)+1;
	sys.wscan[addr] = NSCAN;
}


static uint rom_mem_read(uint addr)
{
	return(sys.rom[addr&(MSIZE_OSROM-1)]);
}


static uint carta_mem_read(uint addr)
{
	return(sys.cartA[addr&(MSIZE_CARTA-1)]);
}

static uint cartb_mem_read(uint addr)
{
	return(sys.cartB[addr&(MSIZE_CARTB-1)]);
}


static uint illegal_mem_read(uint addr)
{
	if(sys.flags & MFLAG_DEBUG)
		printf("Illegal read %04x\n", addr);
	return(0xff);
}

static void rom_mem_write(uint addr, uint val)
{
	if(sys.flags & MFLAG_BURNROM)
		sys.rom[addr&(MSIZE_OSROM-1)] = val;
	else if(sys.flags & MFLAG_DEBUG)
		printf("Illegal ROM write %02x->%04x\n", val, addr);
}

static void carta_mem_write(uint addr, uint val)
{
	if(sys.flags & MFLAG_BURNCARTA)
		sys.cartA[addr&(MSIZE_CARTA-1)] = val;
	else if(sys.flags & MFLAG_DEBUG)
		printf("Illegal Cart-A ROM write %02x->%04x\n", val, addr);
}


static void cartb_mem_write(uint addr, uint val)
{
	if(sys.flags & MFLAG_BURNCARTB)
		sys.cartB[addr&(MSIZE_CARTB-1)] = val;
	else if(sys.flags & MFLAG_DEBUG)
		printf("Illegal Cart-B ROM write %02x->%04x\n", val, addr);
}


static void illegal_mem_write(uint addr, uint val)
{
	if(sys.flags & MFLAG_DEBUG)
		printf("Illegal write %02x->%04x\n", val, addr);
}


void atari_extmem_direct(uint base, uint len, uint adr, uint type)
{
uint	(*read)(uint addr);
void	(*write)(uint addr, uint val);
byte	*readptr;
byte	*writeptr;

	interface_map_update(base, len, adr, type);
	readptr = NULL;
	writeptr = NULL;
	if(base&0xff)
		printf("atari_mem_direct: warning: no page alignment\n");
	switch(type) {
	case MTYPE_RAM:
		readptr = sys.ram+base;
		read = ram_mem_read; 
		write = ram_mem_write; break;
	case MTYPE_ROM:
		readptr = sys.rom+(base&(MSIZE_OSROM-1));
		read = rom_mem_read;
		write = rom_mem_write; break;
	case MTYPE_GTIA:
		read = gtia_mem_read;
		write = gtia_mem_write; break;
	case MTYPE_POKEY:
		read = pokey_mem_read;
		write = pokey_mem_write; break;
	case MTYPE_PIA:
		read = pia_mem_read;
		write = pia_mem_write; break;
	case MTYPE_ANTIC:
		read = antic_mem_read;
		write = antic_mem_write; break;
	case MTYPE_CARTA:
		readptr = sys.cartA+(base&(MSIZE_CARTA-1));
		read = carta_mem_read; 
		write = carta_mem_write; break;
	case MTYPE_CARTB:
		readptr = sys.cartB+(base&(MSIZE_CARTB-1));
		read = cartb_mem_read; 
		write = cartb_mem_write; break;
	default:
		read = illegal_mem_read;
		write = illegal_mem_write; break;
	}
	base = HI(base);
	len = HI(len);
	while(len--) {
		mem.read[base] = read;
		mem.write[base] = write;
		mem.readptr[base] = readptr;
		mem.writeptr[base] = writeptr;
		if(readptr)
			readptr += 256;
		if(writeptr)
			writeptr += 256;
		base++;
	}
}

void atari_mem_direct(uint base, uint len, uint type)
{
	atari_extmem_direct(base, len, base, type);
}



static void draw_pm(ScanLine *scan, register word *sc)
{
register word *s;
register byte *p;
register uint i;
register uint x;
register byte *pf;
register word c;
byte m,cm,pfcm = 0;
word pmb;
byte data;
word mbase;
word *pbase;
static word spbase[4] = {1024,1280,1536,1792};
static word dpbase[4] = {512,640,768,896};
byte pixpl[SCWID/2+32];
byte pmask;
byte mmask;
	if(sys.dmactl_single) {
		pmb = sys.pmbase+sys.vcount+8;
		mbase = 768;
		pbase = spbase;
	} else {
		pmb = sys.pmbase+sys.vcount/2+4;
		mbase = 384;
		pbase = dpbase;
	}
	if(sys.dmactl_single || !(sys.vcount&1)) {
		if(sys.dmactl_missile)
			MEM_READ(pmb+mbase, data)
		else
			data = sys.grafm;
		for(i=0; i<4; i++) {
			if(sys.dmactl_player)
				MEM_READ(pmb+pbase[i], sys.pdata[i])
			else	
				sys.pdata[i] = sys.grafp[i];
			sys.mdata[i] = data&3;
			data >>= 2;
		}
	}
	if((sys.gractl & 2) && 
	(sys.pdata[0]||sys.pdata[1]||sys.pdata[2]||sys.pdata[3])) {
		pmask = 0x0f;
		memset(pixpl,0,SCWID/2+32);
	} else
		pmask = 0x00;
	if((sys.gractl & 1) && 
	(sys.mdata[0]||sys.mdata[1]||sys.mdata[2]||sys.mdata[3])) 
		mmask = 0x0f;
	else
		mmask = 0x00;
	if(pmask) {
		scan->redraw = True;
		scan->update = True;
		for(i=0; i<4; i++) {
			x = sys.hposp[i]-32;
			if(x>SCWID/2)
				continue;
			s = sc+x;
			p = &pixpl[x];
			pf = &scan->pix[x];
			data = sys.pdata[i];
			c = scan->pmcolor[i];
			cm = 0;
			pfcm = 0;
			m = 1<<i;
			x = 8;
			while(x--) {
			    if(data&0x80) {
			        *s++ = c; 
				cm |= *p;
				if(*pf)
					pfcm |= (1<<((*pf++)-1));
				*p++ |= m;
				if(sys.sizep[i]&1) {
			            *s++ = c; 
				    cm |= *p;
				    if(*pf)
					pfcm |= (1<<((*pf++)-1));
				    *p++ |= m; 
				    if(sys.sizep[i]&2) {
			                *s++ = c; *s++ = c;
				        cm |= *p; *p++ |= m; 
				        cm |= *p; *p++ |= m; 
				    	if(*pf)
						pfcm |= (1<<((*pf++)-1));
				    	if(*pf)
						pfcm |= (1<<((*pf++)-1));
				    }
				}
			    } else {
				s += (sys.sizep[i]+1);
				p += (sys.sizep[i]+1);
				pf += (sys.sizep[i]+1);
			    }
			    data <<= 1;
			}
			sys.ppl[i] |= cm;
			sys.ppf[i] |= pfcm;
			for(x=0; x<3; x++)
				if(x!=i && (cm & (1<<x)))
					sys.ppl[x] |= m;
		}
	}
	if(mmask) {
		scan->redraw = True;
		scan->update = True;
		for(i=0; i<4; i++) {
			x = sys.hposm[i]-32;
			if(x>SCWID/2)
				continue;
			s = sc+x;
			p = &pixpl[x];
			pf = &scan->pix[x];
			data = sys.mdata[i];
			if(sys.prior & 0x10)
				c = scan->pmcolor[4];
			else
				c = scan->pmcolor[i];
			cm = 0;
			x = 2;
			while(x--) {
			    if(data&2) {
				*s++ = c; 
				cm |= *p++;
				if(*pf)
					pfcm |= (1<<((*pf++)-1));
				if(sys.sizem[i]&1) {
				    *s++ = c; 
				    cm |= *p++;
			 	    if(*pf)
					pfcm |= (1<<((*pf++)-1));
				    if(sys.sizem[i]&2) {
				    	*s++ = c; *s++ = c;
				    	cm |= *p++;  cm |= *p++;
				    	if(*pf)
						pfcm |= (1<<((*pf++)-1));
				    	if(*pf)
						pfcm |= (1<<((*pf++)-1));
				    }
				}
			    } else {
				s += (sys.sizem[i]+1);
				p += (sys.sizem[i]+1);
				pf += (sys.sizem[i]+1);
			    }
			    data <<= 1;
			}
			sys.mpl[i] |= (cm&pmask);
			sys.mpf[i] |= pfcm;
		}
	}
}




void atari_video_refresh()
/* NOTE:  this used to actually be one simple, small function
          that handled all the modes using a couple of look up
	  tables for the different characteristics.
	  However, I purposely expanded it to lots of separate
	  cases, for the sake of speed, so that each part of
	  of the main switch() statement is hard-coded for
	  its own mode (I wouldn't write code that looks
	  so gross for any other reason :-)
*/
{
register word *p;
register byte *s;
register byte val;
register uint i;
register ScanLine *scan;
register PixLine *pixline;
uint n;
word *scstart;
byte mode,code;
word adr;
uint scborder[4] = {96,32,16,0};
uint scwid[4] = {0,8,10,12};
uint hgt[16] = {0,0,8,10,8,16,8,16,8,4,4,2,1,2,1,1};
uint border,wid;
byte *pix;
uint x;
uint c,m;
word chadr;
byte col[2];
byte c0,c1,c2,c3,c4,c5;
uint nscan;
uint vbi;
uint hscroll,vscroll;
uint refresh;
uint dlcount;
uint lcount;

/* DLIST should wrap on a 1K boundary, ADR on 4K */
		
	sys.vcount = 0;
	p = (word*)sys.screen;		/* address two pixels at a time */
	scan = sys.scan;
	pixline = sys.pixline;
	adr = sys.vid_adr;
	pix = sys.pix;
	if(!sys.dmactl_dma) {
		i = nscan = NSCAN;
		while(i--) {
			scan[i].pix = sys.blank;
			scan[i].dmacycles = 0;
			scan[i].update = False;
		}
	} else
		nscan = 0;
	vbi = False;
	refresh = False;
	dlcount = 0;
	refresh = sys.refresh;	
	sys.refresh = False;
	/* XXX - need to keep track of these per line: */
	border = scborder[sys.dmactl_pfwidth];
	wid = scwid[sys.dmactl_pfwidth];
	while(sys.vcount < NSCAN) {	
	    if(sys.refresh) 
		refresh = True;
	    if(!nscan) {
		if(pixline->update & 0x200)
			refresh = True;
		if(refresh) {
			pixline->adr = adr;
			pixline->dlist = sys.dlist;
			pixline->dmacycles = 1;
			sys.wscan[sys.dlist] = dlcount|0x200;
			MEM_READ(sys.dlist, code); 
			sys.dlist++;
			mode = code & 0xf;
			pixline->dli = code&0x80;
			if(mode) {
			    if(code&0x40) {
				pixline->dmacycles += 2;
				MEM_WREAD(sys.dlist, pixline->adr);
				sys.wscan[sys.dlist] = dlcount|0x200;
				sys.dlist++;
				sys.wscan[sys.dlist] = dlcount|0x200;
				sys.dlist++;
			    }
			    pixline->vscroll = code&0x20;
			    pixline->hscroll = code&0x10;
			} else {
			    pixline->vscroll = 0;
			    pixline->hscroll = 0;
			}
			pixline->mode = mode;
			n = nscan = hgt[mode];
			switch(mode) {
			case 0:
				n = nscan = ((code&0x70)>>4)+1;
				while(n--) {
					scan[n].pix = sys.blank;
					scan[n].dmacycles = 0;
					scan[n].update = True;
				}
				pixline->nbytes = 0;
				break;
			case 1:
				if(code&0x40)
					n = nscan = NSCAN-sys.vcount;
				else {
					n = 1;
					pixline->mode = 16;
				}
				while(n--) {
					scan[n].pix = sys.blank;
					scan[n].dmacycles = 0;
					scan[n].update = True;
				}
				pixline->nbytes = 0;
				break;
			case 2: case 3: case 4:
				while(n--) {
					scan[n].pix = pix;
					scan[n].dmacycles = wid*4;
					i = border;
					while(i--)
						*pix++ = 0;
					pix += wid*16;
					i = border;
					while(i--)
						*pix++ = 0;
				}
				scan[0].dmacycles = wid*8-8;
				if(wid<10)
					scan[0].dmacycles = wid*8-7;
				pixline->nbytes = wid*4;
				break;
			case 5:
				n = 8;
				while(n--) {
					scan[n*2].pix = pix;
					scan[n*2].dmacycles = wid*4;
					scan[n*2+1].pix = pix;
					scan[n*2+1].dmacycles = 0;
					i = border;
					while(i--)
						*pix++ = 0;
					pix += wid*16;
					i = border;
					while(i--)
						*pix++ = 0;
				}
				scan[0].dmacycles = wid*8-8;
				if(wid<10)
					scan[0].dmacycles = wid*8-7;
				pixline->nbytes = wid*4;
				break;
			case 6: 
				while(n--) {
					scan[n].pix = pix;
					scan[n].dmacycles = wid*2;
					i = border;
					while(i--)	
						*pix++ = 0;
					pix += wid*16;
					i = border;
					while(i--)
						*pix++ = 0;
				}
				scan[0].dmacycles = wid*4;
				pixline->nbytes = wid*2;
				break;
			case 7:
				n = 8;
				while(n--) {
					scan[n*2].pix = pix;
					scan[n*2].dmacycles = wid*2;
					scan[n*2+1].pix = pix;
					scan[n*2+1].dmacycles = 0;
					i = border;
					while(i--)	
						*pix++ = 0;
					pix += wid*16;
					i = border;
					while(i--)
						*pix++ = 0;
				}
				scan[0].dmacycles = wid*4;
				pixline->nbytes = wid*2;
				break;
			case 8: case 9:
				while(n--) {
					scan[n].pix = pix;
					scan[n].dmacycles = 0;
				}
				scan[0].dmacycles = wid;
				pixline->nbytes = wid;
				break;
			case 10: case 11: case 12:
				while(n--) {
					scan[n].pix = pix;
					scan[n].dmacycles = 0;
				}
				scan[0].dmacycles = wid*2;
				pixline->nbytes = wid*2;
				break;
			case 13: case 14: case 15:
				while(n--) {
					scan[n].pix = pix;
					scan[n].dmacycles = 0;
				}
				scan[0].dmacycles = wid*4;
				pixline->nbytes = wid*4;
				break;
			}
			scan[0].dmacycles += pixline->dmacycles;
			pixline->nscan = nscan;
		} else {
			sys.dlist += pixline->dmacycles;
		}
		if(pixline->dli) {
			sys.nmi_status = 0x80;
			if(sys.nmi_enable&0x80)
				cpu_nmi();
		}
		hscroll = 0;
		vscroll = 0;
		if(pixline->hscroll)
			hscroll = sys.hscroll;
		if(pixline->vscroll)
			vscroll = sys.vscroll;
		nscan = pixline->nscan;
		mode = pixline->mode;
		if(mode==1) {
			sys.dlist = pixline->adr;
			nscan = NSCAN-sys.vcount;
			sys.nmi_status = 0x40;
			if(sys.nmi_enable & 0x40) 
				cpu_nmi();
			vbi = True;
			sys.refresh = False;
			sys.vid_adr = adr;
		} else if(mode==16) {
			sys.dlist = pixline->adr;
			nscan = 1;
		} /* else
			sys.dlist + pixline->dmacycles;
*/
/*
		printf("dlcount=%d/%d(%04x) mode=%2d nscan=%d %04x %08x %04x\n",
			dlcount,pixline-sys.pixline, pixline->dlist,
			pixline->mode,
			pixline->nscan, pixline->adr, pixline->pix,
			pixline->update); 
*/
		if(refresh || pixline->update) {
	 	    adr = pixline->adr;
		    n = nscan;
		    while(n--) {
			scan[n].update = True;
		    }
		    n = nscan;
		    lcount = 1;
		    switch(mode) {
		    case 5:
			lcount = 2;
		    case 2: case 3: case 4:
			m = 6;
			if(mode>3)
				m = 0;
			i = wid*4;
			x = hscroll;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				if(val&0x80)
					c = 0xff;
				else
					c = 0;
				chadr = sys.chbase+(val&0x7f)*8;
				n = 8;
				while(n--) {
					sys.wscan[chadr] = dlcount;
					MEM_READ(chadr, val); chadr++;
					val ^= c;
					s = scan[(7-n)*lcount].pix+(border+x);
					*s++ = (val>>6&3)+m;
					*s++ = (val>>4&3)+m;
					*s++ = (val>>2&3)+m;
					*s++ = (val&3)+m;
				}
				if(nscan==10) {
					s = scan[8].pix+(border+x);
					*s++ = m;
					*s++ = m;
					*s++ = m;
					*s++ = m;
					s = scan[9].pix+(border+x);
					*s++ = m;
					*s++ = m;
					*s++ = m;
					*s++ = m;
				}
				x += 4;
			}
			break;
		    case 7:
			lcount = 2;
		    case 6: 
			i = wid*2;
			x = hscroll;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				col[0] = 0;
				col[1] = (val>>6)+1;
				chadr = sys.chbase+(val&0x3f)*8;	
				n = 8;
				while(n--) {
					sys.wscan[chadr] = dlcount;
					MEM_READ(chadr, val); chadr++;
					s = scan[(7-n)*lcount].pix+(border+x);
					*s++ = col[val>>7];
					*s++ = col[val>>6&1];
					*s++ = col[val>>5&1];
					*s++ = col[val>>4&1];
					*s++ = col[val>>3&1];
					*s++ = col[val>>2&1];
					*s++ = col[val>>1&1];
					*s++ = col[val&1];
				}
				x += 8;
			}
			break;
		    case 8:
			i = wid;
			s = scan->pix+border;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				c = *s++ = val>>6;   *s++=c; *s++=c; *s++=c;
				c = *s++ = val>>4&3; *s++=c; *s++=c; *s++=c;
				c = *s++ = val>>2&3; *s++=c; *s++=c; *s++=c;
				c = *s++ = val&3;    *s++=c; *s++=c; *s++=c;
			}
			break;
		    case 9:
			i = border;
			while(i--)	
				*pix++ = 0;
			pix += wid*16;
			i = border;
			while(i--)
				*pix++ = 0;
			i = wid;
			s = scan->pix+border;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				c = *s++ = val>>7;   *s++=c; 
				c = *s++ = val>>6&1; *s++=c; 
				c = *s++ = val>>5&1; *s++=c; 
				c = *s++ = val>>4&1; *s++=c; 
				c = *s++ = val>>3&1; *s++=c; 
				c = *s++ = val>>2&1; *s++=c; 
				c = *s++ = val>>1&1; *s++=c; 
				c = *s++ = val&1;    *s++=c; 
			}
			break;
		    case 10:
			i = border;
			while(i--)	
				*pix++ = 0;
			pix += wid*16;
			i = border;
			while(i--)
				*pix++ = 0;
			i = wid*2;
			s = scan->pix+border;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				c = *s++ = val>>6;   *s++=c; 
				c = *s++ = val>>4&3; *s++=c; 
				c = *s++ = val>>2&3; *s++=c; 
				c = *s++ = val&3;    *s++=c; 
			}
			break;
		    case 11: case 12:
			i = border;
			while(i--)	
				*pix++ = 0;
			pix += wid*16;
			i = border;
			while(i--)
				*pix++ = 0;
			i = wid*2;
			s = scan->pix+border;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				*s++ = val>>7;   
				*s++ = val>>6&1; 
				*s++ = val>>5&1;
				*s++ = val>>4&1; 
				*s++ = val>>3&1;
				*s++ = val>>2&1;
				*s++ = val>>1&1;
				*s++ = val&1;    
			}
			break;
		    case 13: case 14:
			i = border;
			while(i--)	
				*pix++ = 0;
			pix += wid*16;
			i = border;
			while(i--)
				*pix++ = 0;
			i = wid*4;
			s = scan->pix+border;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				*s++ = val>>6;   
				*s++ = val>>4&3;
				*s++ = val>>2&3;
				*s++ = val&3;   
			}
			break;
		    case 15:
			i = border;
			while(i--)	
				*pix++ = 0;
			pix += wid*16;
			i = border;
			while(i--)
				*pix++ = 0;
			i = wid*4;
			s = scan->pix+border;
			while(i--) {
				sys.wscan[adr] = dlcount;
				MEM_READ(adr, val); adr++;
				*s++ = (val>>6)+6;   
				*s++ = (val>>4&3)+6; 
				*s++ = (val>>2&3)+6;
				*s++ = (val&3)+6; 
			}
			break;
		    }
		}
		adr = pixline->adr + pixline->nbytes;
		pixline->update = 0;
		dlcount++;
		pixline++;
	    }
	    if(sys.colpf[0] != scan->colpf[0] ||
	       		sys.colpf[1] != scan->colpf[1] ||
	       		sys.colpf[2] != scan->colpf[2] ||
	       		sys.colpf[3] != scan->colpf[3] ||
	       		sys.colpf[4] != scan->colpf[4]) {
		c0 = scan->colpf[0] = sys.colpf[0];
		c1 = scan->colpf[1] = sys.colpf[1];
		c2 = scan->colpf[2] = sys.colpf[2];
		c3 = scan->colpf[3] = sys.colpf[3];
		c4 = scan->colpf[4] = sys.colpf[4];
		scan->color[0] = (c0<<8)|c0;
		scan->color[1] = (c1<<8)|c1;
		scan->color[2] = (c2<<8)|c2;
		scan->color[3] = (c3<<8)|c3;
		scan->color[4] = (c4<<8)|c4;
		c5 = (c3&0xf0)|(c2&0x0f);
		scan->color[6] = (c3<<8)|c3;
#if BYTE_ORDER == LITTLEENDIAN
		scan->color[7] = (c5<<8)|c3;
		scan->color[8] = (c3<<8)|c5;
#else
		scan->color[7] = (c3<<8)|c5;
		scan->color[8] = (c5<<8)|c3;
#endif
		scan->color[9] = (c5<<8)|c5;
		scan->pmcolor[4] = (c4<<8)|c4;
		scan->update = True;
	    }
	    if(sys.colpm[0] != scan->colpm[0] ||
			sys.colpm[1] != scan->colpm[1] ||
			sys.colpm[2] != scan->colpm[2] ||
			sys.colpm[3] != scan->colpm[3]) {
		c0 = scan->colpm[0] = sys.colpm[0];
		c1 = scan->colpm[1] = sys.colpm[1];
		c2 = scan->colpm[2] = sys.colpm[2];
		c3 = scan->colpm[3] = sys.colpm[3];
		scan->pmcolor[0] = (c0<<8)|c0;
		scan->pmcolor[1] = (c1<<8)|c1;
		scan->pmcolor[2] = (c2<<8)|c2;
		scan->pmcolor[3] = (c3<<8)|c3;
	    }
	    if(sys.vcount < SCHGT) {
	    	scstart = p;
	    	if(scan->update) {
	    		s = scan->pix;
	    		i = SCWID/2;
	    		while(i--) 
				*p++ = scan->color[*s++];
			scan->update = False;
			scan->redraw = True;
	    	} else 
			p += SCWID/2;
		if(sys.gractl & 3)
	    		draw_pm(scan,scstart);
	    }
	    cpu.sync = False;
	    cpu.tcycles += scan->dmacycles+9;
	    RUN_CPU(256);
	    sys.vcount++;
	    scan++;
	    nscan--;
	}
	if(!vbi) {
		sys.nmi_status = 0x40;
		if(sys.nmi_enable & 0x40) 
			cpu_nmi();
	}
	sys.fcount++;
}




void atari_logo(int count)
{
int i,x,y,w;
int scpos;
int v;
	interface_open();
	i = 0;
	sys.update_mask = 0;
	while(i<count) {	
		i += 2;
		scpos = 0;
		for(y=0; y<SCHGT; y ++) {
			v = sys.pixel[((i+(y+16)/4)&0xf0)+4];
			for(x=0; x<SCWID; x++)
				sys.screen[scpos+x] = v;
			if(y>30 && y<230) {
				if(y>120) {
					x = 16+(y-120)*(y-120)/140;
					w = 8+(y-120)*(y-120)/180;
					
				} else {
					x = 16;
					w = 8;
				} 
				if(x+w > 100)
					w = 100-x;
				v = sys.pixel[(i+y/2)&255];
				while(w--) {
					sys.screen[scpos+SCWID/2-x-w] = v;
					sys.screen[scpos+SCWID/2+x+w] = v;
				}
				w = 24;
				while(w--) 
					sys.screen[scpos+SCWID/2-12+w] = v;
			}
			sys.scan[y].redraw = True;
			scpos += SCWID;
		}
		sys.fcount++;
		interface_screen_update();
	}
	interface_close();
}


void atari_run(int frames)
{
	cpu.halt = 0;
	interface_open();
	while(frames-- && !cpu.halt) {
		atari_video_refresh();
		interface_screen_update();
		interface_control_update();
	}
	interface_close();
	cpu.halt = 0;
}


void atari_warmstart(void)
{
	printf("warmstart\n");
	sys.nmi_enable = 0;
	sys.nmi_status = 0x20;
	cpu_nmi();
}

void atari_coldstart(void)
{
	printf("coldstart\n");
	sys.nmi_enable = 0;
	sys.nmi_status = 0;
	pia_mem_write(0xd301, 1);	/* restore memory state */
	cpu_reset();
}



int atari_init(int argc, char **argv)
{
static int init = True;
int i;
int ramsize;
int narg = 0;
char **argp = argv;
char *sysname;

	if(!init)
		return argc;
	init = False;

	memset(&sys, 0, sizeof(sys));

	sys.systype = SYSTYPE_800;
	ramsize = 48*K;
	sysname = "800";

	while(argc--) {
		if(!strcmp(*argv,"-sys")) {
			sysname = *(++argv); argc--;
			if(!strcasecmp(sysname,"800")) {
				sys.systype = SYSTYPE_800; ramsize = 48*K;
			} else if(!strcasecmp(sysname, "400")) {
				sys.systype = SYSTYPE_800; ramsize = 16*K;
			} else if(!strcasecmp(sysname,"600XL")) {
				sys.systype = SYSTYPE_XL; ramsize = 16*K;
			} else if(!strcasecmp(sysname,"800XL")) {
				sys.systype = SYSTYPE_XL; ramsize = 64*K;
			} else if(!strcasecmp(sysname,"1200XL")) {
				sys.systype = SYSTYPE_XL; ramsize = 64*K;
			} else if(!strcasecmp(sysname,"65XE")) {
				sys.systype = SYSTYPE_XE; ramsize = 64*K;
			} else if(!strcasecmp(sysname,"130XE")) {
				sys.systype = SYSTYPE_XE; ramsize = 128*K;
			} else if(!strcasecmp(sysname,"320XE")) {
				sys.systype = SYSTYPE_XE; ramsize = 320*K;
			} else {
				fprintf(stderr, "invalid system: %s\n",
							sysname);
				exit(1);
			}
		} else if(!strcmp(*argv, "-ram")) {
			ramsize = atoi(*(++argv)) * K; argc--;
		} else if(!strcmp(*argv, "-rom")) {
			strncpy(sys.romfile, *(++argv), sizeof(sys.romfile));
			argc--;
		} else {
			*argp++ = *argv; narg++;
		}
		if(!strcmp(*argv++,"-h")) {
			printf("Atari options:\n");
			printf("\t-sys <sysname>\t\t400,800,600XL,800XL\n");
			printf("\t\t\t\t1200XL,65XE,130XE,320XE\n");
			printf("\t-ram <size>\t\tRAM size in K\n");
			printf("\t-rom <file>\t\tspecify alternate ROM\n");
			return narg;
		}
	}

	if(ramsize > 64*K) 
		sys.ramsize = 64*K;
	else
		sys.ramsize = ramsize;
	sys.extramsize = ramsize - sys.ramsize;

	printf("Initializing: Atari %s with %dK (%d/%d)\n",
		sysname, ramsize/K, sys.ramsize/K, sys.extramsize/K);

	sys.ram = (byte*)malloc(sys.ramsize);
	sys.extram = (byte*)malloc(sys.extramsize);
	sys.rom = (byte*)malloc(MSIZE_OSROM);
	sys.cartA = (byte*)malloc(MSIZE_CARTA);
	sys.cartB = (byte*)malloc(MSIZE_CARTB);
	memset(sys.rom, 0xff, MSIZE_OSROM);
	memset(sys.cartA, 0xff, MSIZE_CARTA);
	memset(sys.cartB, 0xff, MSIZE_CARTB);

	switch(sys.systype) {
	case SYSTYPE_800:
		if(!sys.romfile[0])
			strcpy(sys.romfile, "OSREVB.rom");
		rom_load(sys.rom+MSIZE_OSROM-MSIZE_OSROMB,
				MSIZE_OSROMB, sys.romfile);
		break;
	case SYSTYPE_XL: case SYSTYPE_XE:	
		if(!sys.romfile[0])
			strcpy(sys.romfile, "OSREVC.rom");
		rom_load(sys.rom, MSIZE_OSROMC, sys.romfile);
		break;
	}

	atari_mem_direct(0x0000, 0x10000, MTYPE_NULL);
	atari_mem_direct(0, sys.ramsize, MTYPE_RAM);
	atari_mem_direct(MBASE_OSROM1, MSIZE_OSROM1, MTYPE_ROM);
	atari_mem_direct(MBASE_OSROM2, MSIZE_OSROM2, MTYPE_ROM);
	atari_mem_direct(MBASE_GTIA, 0x800, MTYPE_NULL);
	atari_mem_direct(MBASE_GTIA, MSIZE_GTIA, MTYPE_GTIA);
	atari_mem_direct(MBASE_POKEY, MSIZE_POKEY, MTYPE_POKEY);
	atari_mem_direct(MBASE_PIA, MSIZE_PIA, MTYPE_PIA);
	atari_mem_direct(MBASE_ANTIC, MSIZE_ANTIC, MTYPE_ANTIC);

	sys.wscan = (word*)malloc(65536*sizeof(word));
	for(i=0; i<65536; i++)
		sys.wscan[i] = NSCAN;
	sys.flags = 0;
	sys.screen = (byte*)malloc(SCWID*SCHGT);
	sys.pix = (byte*)malloc(SCWID/2*(NSCAN+15));
	sys.refresh = 1;
	sys.console = 0xf;
	cpu.hcount = NCYCLES;
	cpu.htime = 0;
	return narg;
}



void mem_load(word base, word size, char *file)
{
FILE *f;
char buf[65536];
int i;
	printf("Loading: %04X-%04X: %s\n", base, base+size-1, file);
	if(!(f = fopen(file, "r"))) {
		perror(file);
		return;
	}	
	fread(buf,size,1,f);
	for(i=0; i<size; i++)
		MEM_WRITE(base+i,buf[i]);
	fclose(f);
}



void rom_load(byte *rom, word size, char *file)
{
FILE *f;
	printf("Loading ROM: %04X bytes: %s\n", size, file);
	if(!(f = fopen(file, "r"))) {
		perror(file);
		return;
	}
	fread(rom,size,1,f);
	fclose(f);
}


