https://github.com/virtualagc/virtualagc
Revision 66d8e606a8d996ded60bc81d5edf319142a5fad9 authored by Ron Burkey on 04 October 2021, 11:49:55 UTC, committed by Ron Burkey on 04 October 2021, 11:49:55 UTC
Tip revision: 66d8e606a8d996ded60bc81d5edf319142a5fad9 authored by Ron Burkey on 04 October 2021, 11:49:55 UTC
Merge branch 'master' of https://github.com/virtualagc/virtualagc
Merge branch 'master' of https://github.com/virtualagc/virtualagc
Tip revision: 66d8e60
agc_engine_init.c
/*
* Copyright 2003-2006,2009,2017 Ronald S. Burkey <info@sandroid.org>
*
* This file is part of yaAGC.
*
* yaAGC is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* yaAGC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with yaAGC; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, Ronald S. Burkey gives permission to
* link the code of this program with the Orbiter SDK library (or with
* modified versions of the Orbiter SDK library that use the same license as
* the Orbiter SDK library), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all respects for
* all of the code used other than the Orbiter SDK library. If you modify
* this file, you may extend this exception to your version of the file,
* but you are not obligated to do so. If you do not wish to do so, delete
* this exception statement from your version.
*
* Filename: agc_engine_init.c
* Purpose: This is the function which initializes the AGC simulation,
* from a file representing the binary image of core memory.
* Compiler: GNU gcc.
* Contact: Ron Burkey <info@sandroid.org>
* Reference: http://www.ibiblio.org/apollo/index.html
* Mods: 04/05/03 RSB. Began.
* 09/07/03 RSB. Fixed data ordering in the core-rope image
* file (to both endian CPU types to work).
* 11/26/03 RSB. Up to now, a pseudo-linear space was used to
* model internal AGC memory. This was simply too
* tricky to work with, because it was too hard to
* understand the address conversions that were
* taking place. I now use a banked model much
* closer to the true AGC memory map.
* 11/29/03 RSB. Added the core-dump save/load.
* 05/06/04 RSB Now use rfopen in looking for the binary.
* 07/12/04 RSB Q is now 16 bits.
* 07/15/04 RSB AGC data now aligned at bit 0 rathern then 1.
* 07/17/04 RSB I/O channels 030-033 now default to 077777
* instead of 00000, since the signals are
* supposed to be inverted.
* 02/27/05 RSB Added the license exception, as required by
* the GPL, for linking to Orbiter SDK libraries.
* 05/14/05 RSB Corrected website references.
* 07/05/05 RSB Added AllOrErasable.
* 07/07/05 RSB On a resume, now restores 010 on up (rather
* than 020 on up), on Hugh's advice.
* 02/26/06 RSB Various changes requested by Mark Grant
* to make it easier to integrate with Orbiter.
* The main change is the addition of an
* agc_load_binfile function. Shouldn't affect
* non-orbiter builds.
* 02/28/09 RSB Fixed some compiler warnings for 64-bit machines.
* 03/18/09 RSB Eliminated periodic messages about
* core-dump creation when the DebugMode
* flag is set.
* 03/27/09 RSB I've noticed that about half the time, using
* --resume causes the DSKY to become non-responsive.
* I wonder if somehow not all the state variables
* are being saved, and in particular not the
* state related to interrupt. (I haven't checked
* this!) Anyhow, there are extra state variables
* in the agc_t structure which aren't being
* saved or restored, so I'm adding all of these.
* 03/30/09 RSB Added the Downlink variable to the core dumps.
* 08/14/16 OH Issue #29 fix return value of agc_engine_init.
* 09/30/16 MAS Added initialization of NightWatchman.
* 01/04/17 MAS Added initialization of ParityFail.
* 01/30/17 MAS Added support for heuristic loading of ROM files
* produced with --hardware, by looking for any set
* parity bits. If such a file is detected, parity
* bit checking is enabled.
* 03/09/17 MAS Added initialization of SbyStillPressed.
* 03/26/17 MAS Added initialization of previously-static things
* from agc_engine.c that are now in agc_t.
* 03/27/17 MAS Fixed a parity-related program loading bug and
* added initialization of a new night watchman bit.
* 04/02/17 MAS Added initialization of a couple of flags used
* for simulation of the TC Trap hardware bug.
* 04/16/17 MAS Added initialization of warning filter variables.
* 05/16/17 MAS Enabled interrupts at startup.
* 05/31/17 RSB Added --initialize-sunburst-37.
* 07/13/17 MAS Added initialization of the three HANDRUPT traps.
* 05/13/21 MKF Disabled UnblockSocket for the WASI target
* (there are no sockets in wasi-libc)
*/
// For Orbiter.
#ifndef AGC_SOCKET_ENABLED
#include <stdio.h>
#include <string.h>
#include "yaAGC.h"
#include "agc_engine.h"
int initializeSunburst37 = 0;
FILE *
rfopen (const char *Filename, const char *mode);
//---------------------------------------------------------------------------
// Returns:
// 0 -- success.
// 1 -- ROM image file not found.
// 2 -- ROM image file larger than core memory.
// 3 -- ROM image file size is odd.
// 4 -- agc_t structure not allocated.
// 5 -- File-read error.
// 6 -- Core-dump file not found.
// Normally, on input the CoreDump filename is NULL, in which case all of the
// i/o channels, erasable memory, etc., are cleared to their reset values.
// When the CoreDump is loaded instead, it allows execution to continue precisely
// from the point at which the CoreDump was created, if AllOrErasable != 0.
// If AllOrErasable == 0, then only the erasable memory is initialized from the
// core-dump file.
int
agc_load_binfile (agc_t *State, const char *RomImage)
{
FILE *fp = NULL;
int Bank;
int m, n, i, j;
int RetVal = 0;
// The following sequence of steps loads the ROM image into the simulated
// core memory, in what I think is a pretty obvious way.
fp = rfopen (RomImage, "rb");
if (fp == NULL)
{
RetVal = 1;
goto Done;
}
fseek (fp, 0, SEEK_END);
n = ftell (fp);
if (0 != (n & 1))
{ // Must be an integral number of words.
RetVal = 3;
goto Done;
}
n /= 2; // Convert byte-count to word-count.
if (n > 36 * 02000)
{
RetVal = 2;
goto Done;
}
fseek (fp, 0, SEEK_SET);
if (State == NULL)
{
RetVal = 4;
goto Done;
}
State->CheckParity = 0;
memset (&State->Parities, 0, sizeof(State->Parities));
Bank = 2;
for (Bank = 2, j = 0, i = 0; i < n; i++)
{
unsigned char In[2];
uint8_t Parity;
uint16_t RawValue;
m = fread (In, 1, 2, fp);
if (m != 2)
{
RetVal = 5;
goto Done;
}
// Within the input file, the fixed-memory banks are arranged in the order
// 2, 3, 0, 1, 4, 5, 6, 7, ..., 35. Therefore, we have to take a little care
// reordering the banks.
if (Bank > 35)
{
RetVal = 2;
goto Done;
}
RawValue = (In[0] * 256 + In[1]);
Parity = RawValue & 1;
State->Fixed[Bank][j] = RawValue >> 1;
State->Parities[(Bank * 02000 + j) / 32] |= Parity << (j % 32);
j++;
// If any of the parity bits are actually set, this must be a ROM built with
// --hardware. Enable parity checking.
if (Parity)
State->CheckParity = 1;
if (j == 02000)
{
j = 0;
// Bank filled. Advance to next fixed-memory bank.
if (Bank == 2)
Bank = 3;
else if (Bank == 3)
Bank = 0;
else if (Bank == 0)
Bank = 1;
else if (Bank == 1)
Bank = 4;
else
Bank++;
}
}
Done: if (fp != NULL)
fclose (fp);
return (RetVal);
}
int
agc_engine_init (agc_t * State, const char *RomImage, const char *CoreDump,
int AllOrErasable)
{
#if defined (WIN32) || defined (__APPLE__)
uint64_t lli;
#else
unsigned long long lli;
#endif
int RetVal = 0, i, j, Bank;
FILE *cd = NULL;
#if !defined (WIN32) && !defined(WASI)
// The purpose of this is to make sure that getchar doesn't halt the program
// when there's no keystroke immediately available.
UnblockSocket (fileno (stdin));
#endif
// Fix for Issue #29 Return the values as the API documents
if (RomImage)
{
RetVal = agc_load_binfile (State, RomImage);
if (RetVal > 0)
goto Done;
}
// Clear i/o channels.
for (i = 0; i < NUM_CHANNELS; i++)
State->InputChannel[i] = 0;
State->InputChannel[030] = 037777;
State->InputChannel[031] = 077777;
State->InputChannel[032] = 077777;
State->InputChannel[033] = 077777;
// Clear erasable memory.
for (Bank = 0; Bank < 8; Bank++)
for (j = 0; j < 0400; j++)
State->Erasable[Bank][j] = 0;
State->Erasable[0][RegZ] = 04000; // Initial program counter.
// Set up the CPU state variables that aren't part of normal memory.
State->CycleCounter = 0;
State->ExtraCode = 0;
State->AllowInterrupt = 1; // The GOJAM sequence enables interrupts
State->InterruptRequests[8] = 1; // DOWNRUPT.
//State->RegA16 = 0;
State->PendFlag = 0;
State->PendDelay = 0;
State->ExtraDelay = 0;
//State->RegQ16 = 0;
State->OutputChannel7 = 0;
for (j = 0; j < 16; j++)
State->OutputChannel10[j] = 0;
State->IndexValue = 0;
for (j = 0; j < 1 + NUM_INTERRUPT_TYPES; j++)
State->InterruptRequests[j] = 0;
State->InIsr = 0;
State->SubstituteInstruction = 0;
State->DownruptTimeValid = 1;
State->DownruptTime = 0;
State->Downlink = 0;
State->NightWatchman = 0;
State->NightWatchmanTripped = 0;
State->RuptLock = 0;
State->NoRupt = 0;
State->TCTrap = 0;
State->NoTC = 0;
State->ParityFail = 0;
State->WarningFilter = 0;
State->GeneratedWarning = 0;
State->RestartLight = 0;
State->Standby = 0;
State->SbyPressed = 0;
State->SbyStillPressed = 0;
State->NextZ = 0;
State->ScalerCounter = 0;
State->ChannelRoutineCount = 0;
State->DskyTimer = 0;
State->DskyFlash = 0;
State->DskyChannel163 = 0;
State->TookBZF = 0;
State->TookBZMF = 0;
State->Trap31A = 0;
State->Trap31B = 0;
State->Trap32 = 0;
if (initializeSunburst37)
{
State->Erasable[0][0067] = 077777;
State->Erasable[0][0157] = 077777;
State->Erasable[0][0375] = 005605;
State->Erasable[0][0376] = 004003;
}
if (CoreDump != NULL)
{
cd = fopen (CoreDump, "r");
if (cd == NULL)
{
if (AllOrErasable)
RetVal = 6;
else
RetVal = 0;
}
else
{
RetVal = 5;
// Load up the i/o channels.
for (i = 0; i < NUM_CHANNELS; i++)
{
if (1 != fscanf (cd, "%o", &j))
goto Done;
if (AllOrErasable)
State->InputChannel[i] = j;
}
// Load up erasable memory.
for (Bank = 0; Bank < 8; Bank++)
for (j = 0; j < 0400; j++)
{
if (1 != fscanf (cd, "%o", &i))
goto Done;
if (AllOrErasable || Bank > 0 || j >= 010)
State->Erasable[Bank][j] = i;
}
if (AllOrErasable)
{
// Set up the CPU state variables that aren't part of normal memory.
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->CycleCounter = i;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->ExtraCode = i;
// I've seen no indication so far of a reset value for interrupt-enable.
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->AllowInterrupt = i;
//if (1 != fscanf (cd, "%o", &i))
// goto Done;
//State->RegA16 = i;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->PendFlag = i;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->PendDelay = i;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->ExtraDelay = i;
//if (1 != fscanf (cd, "%o", &i))
// goto Done;
//State->RegQ16 = i;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->OutputChannel7 = i;
for (j = 0; j < 16; j++)
{
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->OutputChannel10[j] = i;
}
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->IndexValue = i;
for (j = 0; j < 1 + NUM_INTERRUPT_TYPES; j++)
{
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->InterruptRequests[j] = i;
}
// Override the above and make DOWNRUPT always enabled at start.
State->InterruptRequests[8] = 1;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->InIsr = i;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->SubstituteInstruction = i;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->DownruptTimeValid = i;
if (1 != fscanf (cd, "%llo", &lli))
goto Done;
State->DownruptTime = lli;
if (1 != fscanf (cd, "%o", &i))
goto Done;
State->Downlink = i;
}
RetVal = 0;
}
}
Done: if (cd != NULL)
fclose (cd);
return (RetVal);
}
//-------------------------------------------------------------------------------
// A function for creating a core-dump which can be read by agc_engine_init.
void
MakeCoreDump (agc_t * State, const char *CoreDump)
{
#if defined (WIN32) || defined (__APPLE__)
uint64_t lli;
#else
unsigned long long lli;
#endif
FILE *cd = NULL;
int i, j, Bank;
extern int DebugMode;
cd = fopen (CoreDump, "w");
if (cd == NULL)
{
printf ("Could not create the core-dump file.\n");
return;
}
// Write out the i/o channels.
for (i = 0; i < NUM_CHANNELS; i++)
fprintf (cd, "%06o\n", State->InputChannel[i]);
// Write out the erasable memory.
for (Bank = 0; Bank < 8; Bank++)
for (j = 0; j < 0400; j++)
fprintf (cd, "%06o\n", State->Erasable[Bank][j]);
// Write out CPU state variables that aren't part of normal memory.
fprintf (cd, FORMAT_64O "\n", State->CycleCounter);
fprintf (cd, "%o\n", State->ExtraCode);
fprintf (cd, "%o\n", State->AllowInterrupt);
//fprintf (cd, "%o\n", State->RegA16);
fprintf (cd, "%o\n", State->PendFlag);
fprintf (cd, "%o\n", State->PendDelay);
fprintf (cd, "%o\n", State->ExtraDelay);
//fprintf (cd, "%o\n", State->RegQ16);
// 03/27/09 RSB. Extra agc_t fields that weren't being saved before
// now. I've made no analysis to determine that all of these are
// actually needed for anything.
fprintf (cd, "%o\n", State->OutputChannel7);
for (i = 0; i < 16; i++)
fprintf (cd, "%o\n", State->OutputChannel10[i]);
fprintf (cd, "%o\n", State->IndexValue);
for (i = 0; i < 1 + NUM_INTERRUPT_TYPES; i++)
fprintf (cd, "%o\n", State->InterruptRequests[i]);
fprintf (cd, "%o\n", State->InIsr);
fprintf (cd, "%o\n", State->SubstituteInstruction);
fprintf (cd, "%o\n", State->DownruptTimeValid);
fprintf (cd, "%llo\n", lli = State->DownruptTime);
fprintf (cd, "%o\n", State->Downlink);
if (!DebugMode)
printf ("Core-dump file \"%s\" created.\n", CoreDump);
fclose (cd);
return;
//Done:
// printf ("Write-error on core-dump file.\n");
// fclose (cd);
// return;
}
#endif // AGC_SOCKET_ENABLED
Computing file changes ...