//1// Copyright (c) 2004 K. Wilkins2//3// This software is provided 'as-is', without any express or implied warranty.4// In no event will the authors be held liable for any damages arising from5// the use of this software.6//7// Permission is granted to anyone to use this software for any purpose,8// including commercial applications, and to alter it and redistribute it9// freely, subject to the following restrictions:10//11// 1. The origin of this software must not be misrepresented; you must not12// claim that you wrote the original software. If you use this software13// in a product, an acknowledgment in the product documentation would be14// appreciated but is not required.15//16// 2. Altered source versions must be plainly marked as such, and must not17// be misrepresented as being the original software.18//19// 3. This notice may not be removed or altered from any source distribution.20//2122//////////////////////////////////////////////////////////////////////////////23// Handy - An Atari Lynx Emulator //24// Copyright (c) 1996,1997 //25// K. Wilkins //26//////////////////////////////////////////////////////////////////////////////27// System object class //28//////////////////////////////////////////////////////////////////////////////29// //30// This class provides the glue to bind of of the emulation objects //31// together via peek/poke handlers and pass thru interfaces to lower //32// objects, all control of the emulator is done via this class. Update() //33// does most of the work and each call emulates one CPU instruction and //34// updates all of the relevant hardware if required. It must be remembered //35// that if that instruction involves setting SPRGO then, it will cause a //36// sprite painting operation and then a corresponding update of all of the //37// hardware which will usually involve recursive calls to Update, see //38// Mikey SPRGO code for more details. //39// //40// K. Wilkins //41// August 1997 //42// //43//////////////////////////////////////////////////////////////////////////////44// Revision History: //45// ----------------- //46// //47// 01Aug1997 KW Document header added & class documented. //48// //49//////////////////////////////////////////////////////////////////////////////5051#define SYSTEM_CPP5253//#include <crtdbg.h>54//#define TRACE_SYSTEM5556#include "system.h"5758CSystem::CSystem(const uint8 *game, uint32 gamesize, const uint8 *bios, uint32 biossize, int pagesize0, int pagesize1, bool lowpass)59{60// load lynxboot.img61mRom = new CRom(bios, biossize);6263mCart = new CCart(game, gamesize, pagesize0, pagesize1);64mRam = new CRam();6566mMikie = new CMikie(*this);67mSusie = new CSusie(*this);6869// Instantiate the memory map handler70mMemMap = new CMemMap(*this);7172// Now the handlers are set we can instantiate the CPU as is will use handlers on reset73mCpu = new C65C02(*this);7475mMikie->mikbuf.set_sample_rate(44100, 60);76mMikie->mikbuf.clock_rate((long int)(16000000 / 4));77mMikie->mikbuf.bass_freq(60);78mMikie->miksynth.volume(0.50);79mMikie->miksynth.treble_eq(lowpass ? -35 : 0);8081// Now init is complete do a reset, this will cause many things to be reset twice82Reset();83}8485CSystem::~CSystem()86{87delete mCart;88delete mRom;89delete mRam;90delete mCpu;91delete mMikie;92delete mSusie;93delete mMemMap;94}9596void CSystem::Reset()97{98gSystemCycleCount=0;99gNextTimerEvent=0;100// gCPUBootAddress=0;101gSystemIRQ=FALSE;102gSystemNMI=FALSE;103gSystemCPUSleep=FALSE;104gSystemHalt=FALSE;105gSuzieDoneTime = 0;106107mMemMap->Reset();108mCart->Reset();109mRom->Reset();110mRam->Reset();111mMikie->Reset();112mSusie->Reset();113mCpu->Reset();114}115116/*117static int Load(MDFNFILE *fp)118{119try120{121lynxie = new CSystem(fp->data, fp->size);122123switch(lynxie->CartGetRotate())124{125case CART_ROTATE_LEFT:126MDFNGameInfo->rotated = MDFN_ROTATE270;127break;128129case CART_ROTATE_RIGHT:130MDFNGameInfo->rotated = MDFN_ROTATE90;131break;132}133134memcpy(MDFNGameInfo->MD5, lynxie->mCart->MD5, 16);135MDFNGameInfo->GameSetMD5Valid = FALSE;136137MDFN_printf(_("ROM: %dKiB\n"), (lynxie->mCart->InfoROMSize + 1023) / 1024);138MDFN_printf(_("ROM CRC32: 0x%08x\n"), lynxie->mCart->CRC32());139MDFN_printf(_("ROM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());140141MDFNGameInfo->fps = (uint32)(59.8 * 65536 * 256);142143if(MDFN_GetSettingB("lynx.lowpass"))144{145lynxie->mMikie->miksynth.treble_eq(-35);146}147else148{149lynxie->mMikie->miksynth.treble_eq(0);150}151152}153*/154155bool CSystem::Advance(int buttons, uint32 *vbuff, int16 *sbuff, int &sbuffsize)156{157// this check needs to occur at least once every 250 million cycles or better158mMikie->CheckWrap();159160SetButtonData(buttons);161mSusie->lagged = true;162163uint32 start = gSystemCycleCount;164165// nominal timer values are div16 for prescalar, 158 for line timer, and 104 for frame timer166// reloads are actually +1 due to the way the hardware works167// so this is a frame, theoretically168uint32 target = gSystemCycleCount + 16 * 105 * 159 - frameoverflow;169170// audio start frame171mMikie->startTS = start;172173videobuffer = vbuff;174175while (gSystemCycleCount < target)176//while (mMikie->mpDisplayCurrent && gSystemCycleCount - start < 800000)177{178Update(target);179}180181// total cycles executed is now gSystemCycleCount - start182frameoverflow = gSystemCycleCount - target;183184mMikie->mikbuf.end_frame((gSystemCycleCount - start) >> 2);185sbuffsize = mMikie->mikbuf.read_samples(sbuff, sbuffsize);186187return mSusie->lagged;188}189190void CSystem::Blit(const uint32 *src)191{192if (!videobuffer)193{194// a game shouldn't be able to get two frames in in the length of time we traverse in a single195// call to advance. what is going on here?196return;197}198199const int W = 160;200const int H = 102;201202switch (rotate)203{204case 0:205std::memcpy(videobuffer, src, sizeof(uint32) * W * H);206break;207case 1:208{209uint32 *dest = videobuffer + H * (W - 1);210for (int j = 0; j < H; j++)211{212for (int i = 0; i < W; i++)213{214*dest = *src++;215dest -= H;216}217dest += H * W + 1;218}219}220break;221case 2:222{223uint32 *dest = videobuffer + H * W - 1;224for (int i = 0; i < W * H; i++)225{226*dest-- = *src++;227}228}229break;230case 3:231{232uint32 *dest = videobuffer + H - 1;233for (int j = 0; j < H; j++)234{235for (int i = 0; i < W; i++)236{237*dest = *src++;238dest += H;239}240dest -= H * W + 1;241}242}243break;244}245246videobuffer = nullptr;247}248249void CSystem::SetButtonData(uint32 data)250{251// bit: 7654252// input DURL253// rot=1 RLUD254// rot=2 UDLR255// rot=3 LRDU256257uint32 newdata;258switch (rotate)259{260case 0:261newdata = data; break;262case 1:263newdata = data & 0xff0f | data >> 3 & 0x0010 | data >> 1 & 0x0020 | data << 2 & 0x00c0; break;264case 2:265newdata = data & 0xff0f | data >> 1 & 0x0050 | data << 1 & 0x00a0; break;266case 3:267newdata = data & 0xff0f | data >> 2 & 0x0030 | data << 1 & 0x0040 | data << 3 & 0x0080; break;268}269270mSusie->SetButtonData(newdata);271}272273SYNCFUNC(CSystem)274{275// mMemMap regenerates the mMemoryHandlers directly on load276277TSS(mCart);278TSS(mRom);279TSS(mMemMap);280TSS(mRam);281TSS(mCpu);282TSS(mMikie);283TSS(mSusie);284285NSS(gSuzieDoneTime);286NSS(gSystemCycleCount);287NSS(gNextTimerEvent);288NSS(gSystemIRQ);289NSS(gSystemNMI);290NSS(gSystemCPUSleep);291NSS(gSystemHalt);292NSS(frameoverflow);293}294295296/*297static MDFNSetting LynxSettings[] =298{299{ "lynx.rotateinput", MDFNSF_NOFLAGS, gettext_noop("Virtually rotate D-pad along with screen."), NULL, MDFNST_BOOL, "1" },300{ "lynx.lowpass", MDFNSF_CAT_SOUND, gettext_noop("Enable sound output lowpass filter."), NULL, MDFNST_BOOL, "1" },301{ NULL }302};303*/304305/*306static const InputDeviceInputInfoStruct IDII[] =307{308{ "a", "A (outer)", 8, IDIT_BUTTON_CAN_RAPID, NULL },309{ "b", "B (inner)", 7, IDIT_BUTTON_CAN_RAPID, NULL },310{ "option_2", "Option 2 (lower)", 5, IDIT_BUTTON_CAN_RAPID, NULL },311{ "option_1", "Option 1 (upper)", 4, IDIT_BUTTON_CAN_RAPID, NULL },312313{ "left", "LEFT ←", 2, IDIT_BUTTON, "right", { "up", "right", "down" } },314{ "right", "RIGHT →", 3, IDIT_BUTTON, "left", { "down", "left", "up" } },315{ "up", "UP ↑", 0, IDIT_BUTTON, "down", { "right", "down", "left" } },316{ "down", "DOWN ↓", 1, IDIT_BUTTON, "up", { "left", "up", "right" } },317{ "pause", "PAUSE", 6, IDIT_BUTTON, NULL },318};319*/320321/*322static const FileExtensionSpecStruct KnownExtensions[] =323{324{ ".lnx", gettext_noop("Atari Lynx ROM Image") },325{ NULL, NULL }326};327*/328329/*330MDFNGI EmulatedLynx =331{332"lynx",333"Atari Lynx",334KnownExtensions,335MODPRIO_INTERNAL_HIGH,336NULL,337&InputInfo,338Load,339TestMagic,340NULL,341NULL,342CloseGame,343SetLayerEnableMask,344NULL,345NULL,346NULL,347NULL,348NULL,349NULL,350NULL,351false,352StateAction,353Emulate,354SetInput,355DoSimpleCommand,356LynxSettings,357MDFN_MASTERCLOCK_FIXED(16000000),3580,359360false, // Multires possible?361362160, // lcm_width363102, // lcm_height364NULL, // Dummy365366367160, // Nominal width368102, // Nominal height369370160, // Framebuffer width371102, // Framebuffer height3723732, // Number of output sound channels374};375*/376377378