kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
732 wiersze
15 KiB
C
732 wiersze
15 KiB
C
/*****************************************************************************
|
|
|
|
This file is part of x2600, the Atari 2600 Emulator
|
|
===================================================
|
|
|
|
Copyright 1996 Alex Hornby. For contributions see the file CREDITS.
|
|
|
|
This software is distributed under the terms of the GNU General Public
|
|
License. This is free software with ABSOLUTELY NO WARRANTY.
|
|
|
|
See the file COPYING for details.
|
|
|
|
$Id: vmachine.c,v 2.22 1997/11/22 14:27:47 ahornby Exp $
|
|
******************************************************************************/
|
|
//This file was modified from its original version for use in PocketVCS
|
|
// by Stuart Russell
|
|
|
|
/*
|
|
The virtual machine. Contains the RIOT timer code, and hardware
|
|
initialisation.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "types.h"
|
|
#include "address.h"
|
|
#include "options.h"
|
|
#include "display.h"
|
|
#include "raster.h"
|
|
#include "cpu.h"
|
|
#include "collision.h"
|
|
#include "sound.h"
|
|
#include "vmachine.h"
|
|
#include "tiasound.h"
|
|
|
|
#include "emuapi.h"
|
|
|
|
#define snd 1
|
|
|
|
extern void BlitScreen(void);
|
|
extern int nOptions_SoundBufSize;
|
|
extern int nOptions_SoundOn;
|
|
int Touchpadx=0;
|
|
int Touchpady=0;
|
|
//extern int Touchpadx;
|
|
//extern int Touchpady;
|
|
extern int nOptions_Landscape;
|
|
extern int nOptions_SkipFrames;
|
|
|
|
|
|
|
|
/* The Rom define might need altering for paged carts */
|
|
/* Enough for 16k carts */
|
|
int rom_size;
|
|
|
|
/* Used as a simple file buffer to store the data */
|
|
// JMH
|
|
//BYTE theCart[16384];
|
|
//BYTE theCart[4096];
|
|
BYTE * theCart=0;
|
|
|
|
/* Scratch area for those banking types that require memcpy() */
|
|
//BYTE cartScratch[4096];
|
|
BYTE * cartScratch=0;
|
|
/* Area for those carts containing RAM */
|
|
//BYTE cartRam[1024];
|
|
BYTE * cartRam=0;
|
|
|
|
/* Pointer to start of ROM data */
|
|
BYTE *theRom;
|
|
|
|
BYTE theRam[128];
|
|
BYTE tiaRead[0x0e];
|
|
BYTE tiaWrite[0x2d];
|
|
BYTE keypad[2][4];
|
|
|
|
/* These don't strictly need so much space */
|
|
BYTE riotRead[0x298];
|
|
BYTE riotWrite[0x298];
|
|
|
|
/*
|
|
Hardware addresses not programmer accessible
|
|
*/
|
|
|
|
/* Set if whole emulator is reset */
|
|
int reset_flag = 0;
|
|
|
|
/* The timer resolution, can be 1,8,64,1024 */
|
|
int timer_res = 32;
|
|
int timer_count = 0;
|
|
int timer_clks = 0;
|
|
extern CLOCK clk;
|
|
extern int beamadj;
|
|
|
|
/* Electron beam position */
|
|
int ebeamx, ebeamy, sbeamx;
|
|
|
|
/* The state of the electron beam */
|
|
#define VSYNCSTATE 1
|
|
#define VBLANKSTATE 2
|
|
#define HSYNCSTATE 4
|
|
#define DRAWSTATE 8
|
|
#define OVERSTATE 16
|
|
int vbeam_state; /* 1 2 8 or 16 */
|
|
int hbeam_state; /* 4 8 or 16 */
|
|
|
|
/* The tv size, varies with PAL/NTSC */
|
|
int tv_width, tv_height, tv_vsync, tv_vblank, tv_overscan, tv_frame, tv_hertz,
|
|
tv_hsync;
|
|
|
|
|
|
PlayField pf[2];
|
|
|
|
Paddle paddle[4];
|
|
|
|
Player pl[2];
|
|
|
|
Missile ml[3];
|
|
|
|
|
|
|
|
#define MAXLIST 80
|
|
/* The various display lists */
|
|
struct RasterChange pl_change[2][MAXLIST], pf_change[1][MAXLIST], unified[MAXLIST];
|
|
|
|
/* The display list counters */
|
|
int pl_change_count[2], pf_change_count[1], unified_count;
|
|
|
|
|
|
/***************************************************************************
|
|
Let the functions begin!
|
|
****************************************************************************/
|
|
void
|
|
init_machine (void)
|
|
{
|
|
if (theCart == 0) theCart = (BYTE *)emu_Malloc(16384);
|
|
if (cartScratch == 0) cartScratch = (BYTE *)emu_Malloc(4096);
|
|
if (cartRam == 0) cartRam = (BYTE *)emu_Malloc(1024);
|
|
}
|
|
|
|
/* Device independent screen initialisations */
|
|
void
|
|
init_screen (void)
|
|
{
|
|
|
|
/* Set the electron beam to the top left */
|
|
ebeamx = -tv_hsync;
|
|
ebeamy = 0;
|
|
sbeamx = 0;
|
|
vbeam_state = VSYNCSTATE;
|
|
hbeam_state = OVERSTATE;
|
|
|
|
tv_vsync = 3;
|
|
tv_hsync = 68;
|
|
switch (base_opts.tvtype)
|
|
{
|
|
case NTSC:
|
|
tv_width = 160;
|
|
tv_height = 192;
|
|
tv_vblank = 40;
|
|
tv_overscan = 30;
|
|
tv_frame = 262;
|
|
tv_hertz = 60;
|
|
break;
|
|
case PAL:
|
|
case SECAM:
|
|
tv_width = 160;
|
|
tv_height = 228;
|
|
tv_vblank = 48;
|
|
tv_overscan = 36;
|
|
tv_frame = 312;
|
|
tv_hertz = 50;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/* Displays the tv screen */
|
|
void tv_display (void)
|
|
{
|
|
/* Only display if the frame is a valid one. */
|
|
//if ( (tv_counter % nOptions_SkipFrames) == 0)
|
|
//{
|
|
emu_DrawScreen(VBuf, tv_width, tv_height, tv_width);
|
|
emu_DrawVsync();
|
|
//}
|
|
//tv_counter++;
|
|
}
|
|
|
|
/* Initialise the RIOT (also known as PIA) */
|
|
void
|
|
init_riot (void)
|
|
{
|
|
int i;
|
|
|
|
/* Reset the arrays */
|
|
for(i=0; i< 0x298;i++)
|
|
{
|
|
riotRead[i]=0;
|
|
riotWrite[i]=0;
|
|
}
|
|
|
|
/* Wipe the RAM */
|
|
for (i = 0; i < 0x80; i++)
|
|
theRam[i] = 0;
|
|
|
|
/* Set the timer to zero */
|
|
riotRead[INTIM] = 0;
|
|
|
|
/* Set the joysticks and switches to input */
|
|
riotWrite[SWACNT] = 0;
|
|
riotWrite[SWBCNT] = 0;
|
|
|
|
/* Centre the joysticks */
|
|
riotRead[SWCHA] = 0xff;
|
|
riotRead[SWCHB] = 0x0b;
|
|
|
|
/* Set the counter resolution */
|
|
timer_res = 32;
|
|
timer_count = 0;
|
|
timer_clks = 0;
|
|
}
|
|
|
|
/* Initialise the television interface adaptor (TIA) */
|
|
void
|
|
init_tia (void)
|
|
{
|
|
int i;
|
|
for(i=0; i< 0x2d;i++)
|
|
{
|
|
tiaWrite[i]=0;
|
|
}
|
|
for(i=0; i< 0x0e;i++)
|
|
{
|
|
tiaRead[i]=0;
|
|
}
|
|
|
|
tiaWrite[CTRLPF] = 0x00;
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
pl[i].hmm = 0x0;
|
|
pl[i].x = 0x0;
|
|
pl[i].nusize = 0;
|
|
pl[i].grp = 0;
|
|
pl[i].vdel = 0;
|
|
pl[i].vdel_flag = 0;
|
|
pl_change_count[i] = 0;
|
|
}
|
|
|
|
pl[0].mask = PL0_MASK;
|
|
pl[1].mask = PL1_MASK;
|
|
ml[0].mask = ML0_MASK;
|
|
ml[1].mask = ML1_MASK;
|
|
reset_collisions ();
|
|
|
|
pf_change_count[0] = 0;
|
|
unified_count = 0;
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
ml[i].x = 0;
|
|
ml[i].hmm = 0;
|
|
ml[i].enabled = 0;
|
|
ml[i].locked = 0;
|
|
ml[i].width = 0;
|
|
ml[i].vdel = 0;
|
|
ml[i].vdel_flag = 0;
|
|
}
|
|
|
|
tiaWrite[VBLANK] = 0;
|
|
tiaRead[INPT4] = 0x80;
|
|
tiaRead[INPT5] = 0x80;
|
|
|
|
/* Set up the colour table */
|
|
colour_table[P0M0_COLOUR]= 0;
|
|
colour_table[P1M1_COLOUR]= 0;
|
|
colour_table[PFBL_COLOUR] = 0;
|
|
colour_table[BK_COLOUR] = 0;
|
|
}
|
|
|
|
void
|
|
init_memory(void)
|
|
{
|
|
int i;
|
|
for(i=0;i<1024; i++)
|
|
cartRam[i]=0;
|
|
for(i=0; i<128;i++)
|
|
theRam[i]=0;
|
|
}
|
|
|
|
void
|
|
init_banking (void)
|
|
{
|
|
/* Set to the first bank */
|
|
//dbg_message(DBG_NORMAL, "rom_size is set at %d bytes\n", rom_size);
|
|
if (rom_size == 2048)
|
|
theRom = &theCart[rom_size - 2048];
|
|
else
|
|
theRom = &theCart[rom_size - 4096];
|
|
//JMH
|
|
//switch(base_opts.bank)
|
|
// {
|
|
// case 3:
|
|
// /* Parker Brothers 8k E0 */
|
|
// memcpy(&cartScratch[0xc00],&theCart[0x1c00],1024);
|
|
// memcpy(&cartScratch[0],&theCart[0],3072);
|
|
// theRom=cartScratch;
|
|
// break;
|
|
// default:
|
|
// break;
|
|
// }
|
|
}
|
|
|
|
extern void init_cpu( ADDRESS addr);
|
|
|
|
/* Main hardware startup */
|
|
void
|
|
init_hardware (void)
|
|
{
|
|
// dbg_message(DBG_NORMAL,"Setting Up hardware\n");
|
|
init_screen ();
|
|
init_riot ();
|
|
init_tia ();
|
|
init_raster ();
|
|
init_memory();
|
|
init_banking();
|
|
init_cpu (0xfffc);
|
|
}
|
|
|
|
/* Do a raster change */
|
|
__inline void
|
|
do_raster_change (int i, int type, int val, struct RasterChange *rc)
|
|
{
|
|
rc->x = ebeamx + beamadj;
|
|
rc->type = type;
|
|
rc->val = val;
|
|
}
|
|
|
|
/* Do a raster change on the unified list */
|
|
/* type: type of change */
|
|
/* val: value of change */
|
|
__inline void
|
|
do_unified_change (int type, int val)
|
|
{
|
|
if (unified_count < MAXLIST)
|
|
{
|
|
unified[unified_count].x = ebeamx + beamadj;
|
|
unified[unified_count].type = type;
|
|
unified[unified_count].val = val;
|
|
unified_count++;
|
|
}
|
|
}
|
|
|
|
/* Do a player raster change */
|
|
/* i: player to change. 0 or 1 */
|
|
/* type: type of change */
|
|
/* val: value of change */
|
|
__inline void
|
|
do_plraster_change (int i, int type, int val)
|
|
{
|
|
int plc = pl_change_count[i];
|
|
/*printf("Raster change i=%d, x=%d, type=%d, val=%d\n", i, x, type, val); */
|
|
if (plc < MAXLIST)
|
|
{
|
|
do_raster_change (i, type, val, &pl_change[i][plc]);
|
|
if (type == 1)
|
|
pl_change[i][plc].x -= 3;
|
|
pl_change_count[i]++;
|
|
}
|
|
}
|
|
|
|
/* Do a playfield raster change */
|
|
/* i: playfield to change. Depreciated, as 0 is now only one used */
|
|
/* type: type of change */
|
|
/* val: value of change */
|
|
__inline void
|
|
do_pfraster_change (int i, int type, int val)
|
|
{
|
|
int pfc = pf_change_count[i];
|
|
/*
|
|
if(ebeamy>=100) {
|
|
printf("Raster change i=%d, x=%d, type=%d, val=%d\n",
|
|
i, ebeamx+beamadj, type, val);
|
|
//show();
|
|
}
|
|
*/
|
|
if (pfc < MAXLIST)
|
|
{
|
|
do_raster_change (i, type, val, &pf_change[i][pfc]);
|
|
pf_change_count[i]++;
|
|
}
|
|
}
|
|
|
|
|
|
/* Use a unified change */
|
|
/* rc: unified change structure to use */
|
|
__inline void
|
|
use_unified_change (struct RasterChange *rc)
|
|
{
|
|
switch (rc->type)
|
|
{
|
|
case 0:
|
|
/* P0MO colour */
|
|
colour_table[P0M0_COLOUR] = rc->val;
|
|
break;
|
|
case 1:
|
|
/* POM0 colour */
|
|
colour_table[P1M1_COLOUR] = rc->val;
|
|
break;
|
|
case 2:
|
|
/* PFBL colour */
|
|
colour_table[PFBL_COLOUR] = rc->val;
|
|
break;
|
|
case 3:
|
|
/* BK colour */
|
|
colour_table[BK_COLOUR] = rc->val;
|
|
break;
|
|
case 4:
|
|
/* Priority change Normal */
|
|
if(rc->val)
|
|
norm_val=1;
|
|
else
|
|
norm_val=0;
|
|
colour_lookup=colour_ptrs[norm_val][scores_val];
|
|
break;
|
|
case 5:
|
|
/* Priority change Scores */
|
|
if(rc->val)
|
|
{
|
|
if(rc->x < 80)
|
|
scores_val=1;
|
|
else
|
|
scores_val=2;
|
|
}
|
|
else
|
|
scores_val=0;
|
|
colour_lookup=colour_ptrs[norm_val][scores_val];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Use a playfield change */
|
|
/* pl: playfield to change */
|
|
/* rc: change to make */
|
|
__inline void
|
|
use_pfraster_change (PlayField *pl, struct RasterChange *rc)
|
|
{
|
|
switch (rc->type)
|
|
{
|
|
case 0:
|
|
/* PF0 */
|
|
pl->pf0 = rc->val;
|
|
break;
|
|
case 1:
|
|
/* PF1 */
|
|
pl->pf1 = rc->val;
|
|
break;
|
|
case 2:
|
|
/* PF2 */
|
|
pl->pf2 = rc->val;
|
|
break;
|
|
case 3:
|
|
/* Reflection */
|
|
pl->ref = rc->val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Use a player change */
|
|
/* pl: player to change */
|
|
/* rc: change to make */
|
|
__inline void
|
|
use_plraster_change ( Player *pl, struct RasterChange *rc)
|
|
{
|
|
switch (rc->type)
|
|
{
|
|
case 0:
|
|
/* GRP */
|
|
pl->grp = rc->val;
|
|
break;
|
|
/* Vertical delay */
|
|
case 1:
|
|
pl->vdel = pl->grp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
do_paddle (int padnum)
|
|
{
|
|
int res = 0x00;
|
|
int x=0;
|
|
if ((tiaWrite[VBLANK] & 0x80) == 0)
|
|
{
|
|
if (!nOptions_Landscape){
|
|
x=240-Touchpadx;
|
|
x=x*66;
|
|
if (paddle[padnum].val > x)
|
|
res=0x80;
|
|
|
|
}else{
|
|
x=320-Touchpady;
|
|
x=x*50;
|
|
if (paddle[padnum].val > x)
|
|
res=0x80;
|
|
}
|
|
|
|
if (x > clk)
|
|
res = 0x00;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* Calculate the keypad rows */
|
|
/* i.e. when reading from INPTx we don't know the row */
|
|
BYTE
|
|
do_keypad (int pad, int col)
|
|
{
|
|
BYTE res= 0x80;
|
|
|
|
// read_keypad(pad);
|
|
|
|
/* Bottom row */
|
|
if(pad==0) {
|
|
if( (riotWrite[SWCHA] & 0x80) && keypad[pad][col]==3)
|
|
res=0x00;
|
|
/* Third row */
|
|
if( (riotWrite[SWCHA] & 0x40) && keypad[pad][col]==2)
|
|
res=0x00;
|
|
if( (riotWrite[SWCHA] & 0x20) && keypad[pad][col]==1)
|
|
res=0x00;
|
|
if( (riotWrite[SWCHA] & 0x10) && keypad[pad][col]==0)
|
|
res=0x00;
|
|
}
|
|
else {
|
|
/* Bottom row */
|
|
if( (riotWrite[SWCHA] & 0x80) && keypad[pad][col]==3)
|
|
res=0x00;
|
|
/* Third row */
|
|
if( (riotWrite[SWCHA] & 0x40) && keypad[pad][col]==2)
|
|
res=0x00;
|
|
if( (riotWrite[SWCHA] & 0x20) && keypad[pad][col]==1)
|
|
res=0x00;
|
|
if( (riotWrite[SWCHA] & 0x10) && keypad[pad][col]==0)
|
|
res=0x00;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
Called when the timer is set .
|
|
Note that res is the bit shift, not absolute value.
|
|
Assumes that any timer interval set will last longer than the instruction
|
|
setting it.
|
|
*/
|
|
/* res: timer interval resolution as a bit shift value */
|
|
/* count: the number of intervals to set */
|
|
/* clkadj: the number of CPU cycles into the current instruction */
|
|
void
|
|
set_timer (int res, int count, int clkadj)
|
|
{
|
|
timer_count = count << res;
|
|
timer_clks = clk + clkadj;
|
|
timer_res = res;
|
|
}
|
|
|
|
/* New timer code, now only called on a read of INTIM */
|
|
/* clkadj: the number of CPU cycles into the current instruction */
|
|
/* returns: the current timer value */
|
|
BYTE
|
|
do_timer (int clkadj)
|
|
{
|
|
BYTE result;
|
|
int delta;
|
|
int value;
|
|
|
|
delta = clk - timer_clks;
|
|
value = delta >> timer_res;
|
|
if (delta <= timer_count)
|
|
{ /* Timer is still going down in res intervals */
|
|
result = value;
|
|
}
|
|
else
|
|
{
|
|
if (value == 0)
|
|
/* Timer is in holding period */
|
|
result = 0;
|
|
else
|
|
{
|
|
/* Timer is descending from 0xff in clock intervals */
|
|
set_timer (0, 0xff, clkadj);
|
|
result = 0;
|
|
}
|
|
}
|
|
|
|
/* printf("Timer result=%d\n", result); */
|
|
return result;
|
|
}
|
|
|
|
#ifdef snd
|
|
//extern unsigned char *sounddata;
|
|
//#define SoundBufSize 256
|
|
//unsigned char sounddata[SoundBufSize];
|
|
#endif
|
|
|
|
/* Do the screen related part of a write to VBLANK */
|
|
/* b: the byte written */
|
|
void
|
|
do_vblank (BYTE b)
|
|
{
|
|
|
|
if (b & 0x02)
|
|
{
|
|
/* Start vertical blank */
|
|
vbeam_state = VBLANKSTATE;
|
|
#ifdef snd
|
|
// Tia_process(sounddata, SoundBufSize);
|
|
// CESound_play_sample(sounddata, nOptions_SoundBufSize);
|
|
#endif
|
|
/* Also means we can update screen */
|
|
tv_display ();
|
|
}
|
|
else
|
|
{
|
|
/* End vblank, and start first hsync drawing */
|
|
int i;
|
|
|
|
vbeam_state = DRAWSTATE;
|
|
hbeam_state = HSYNCSTATE;
|
|
/* Set up the screen */
|
|
for (i = 0; i < unified_count; i++)
|
|
use_unified_change (&unified[i]);
|
|
/* Hope for a WSYNC, but just in case */
|
|
ebeamx = -tv_hsync;
|
|
ebeamy = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//int count = 1;
|
|
|
|
/* do a horizontal sync */
|
|
void
|
|
do_hsync (void)
|
|
{
|
|
/* Only perform heavy stuff if electron beam is in correct position */
|
|
if (vbeam_state == DRAWSTATE && (ebeamx > -tv_hsync))
|
|
{
|
|
// if (!(count--))
|
|
// {
|
|
// count = 2;
|
|
tv_raster (ebeamy);
|
|
|
|
// }
|
|
/* Fix the clock value */
|
|
clk += (ebeamx - tv_width) / 3;
|
|
ebeamy++;
|
|
}
|
|
hbeam_state = HSYNCSTATE;
|
|
ebeamx = -tv_hsync;
|
|
sbeamx = 0;
|
|
}
|
|
|
|
/* Main screen logic */
|
|
/* clks: CPU clock length of last instruction */
|
|
__inline void
|
|
do_screen (int clks)
|
|
{
|
|
switch (vbeam_state)
|
|
{
|
|
case VSYNCSTATE:
|
|
case VBLANKSTATE:
|
|
switch (hbeam_state)
|
|
{
|
|
case HSYNCSTATE:
|
|
ebeamx += clks * 3;
|
|
if (ebeamx >= 0)
|
|
{
|
|
hbeam_state = DRAWSTATE;
|
|
}
|
|
break;
|
|
case DRAWSTATE:
|
|
ebeamx += clks * 3;
|
|
if (ebeamx >= tv_width)
|
|
{
|
|
ebeamx -= (tv_hsync + tv_width);
|
|
/* Insert hsync stuff here */
|
|
sbeamx = ebeamx;
|
|
hbeam_state = HSYNCSTATE;
|
|
}
|
|
break;
|
|
case OVERSTATE:
|
|
break;
|
|
}
|
|
break;
|
|
case DRAWSTATE:
|
|
switch (hbeam_state)
|
|
{
|
|
case HSYNCSTATE:
|
|
ebeamx += clks * 3;
|
|
if (ebeamx >= 0)
|
|
{
|
|
hbeam_state = DRAWSTATE;
|
|
}
|
|
break;
|
|
case DRAWSTATE:
|
|
ebeamx += clks * 3;
|
|
if (ebeamx >= tv_width)
|
|
{
|
|
/* Insert hsync stuff here */
|
|
sbeamx = ebeamx;
|
|
ebeamx -= (tv_hsync + tv_width);
|
|
tv_raster (ebeamy);
|
|
ebeamy++;
|
|
hbeam_state = HSYNCSTATE;
|
|
}
|
|
if (ebeamy >= tv_height + tv_overscan)
|
|
{
|
|
vbeam_state = OVERSTATE;
|
|
ebeamy = 0;
|
|
}
|
|
break;
|
|
case OVERSTATE:
|
|
break;
|
|
}
|
|
break;
|
|
case OVERSTATE:
|
|
break;
|
|
}
|
|
}
|
|
|