spinsim/pasmsim.c

737 lines
19 KiB
C
Executable File

/*******************************************************************************
' Author: Dave Hein
' Version 0.21
' Copyright (c) 2010, 2011
' See end of file for terms of use.
'******************************************************************************/
#include <stdio.h>
#include "interp.h"
extern char *hubram;
extern int32_t memsize;
extern char lockstate[16];
extern char lockalloc[16];
extern PasmVarsT PasmVars[8];
extern int32_t pasmspin;
extern int32_t cycleaccurate;
extern int32_t loopcount;
extern int32_t pin_val_a;
extern FILE *tracefile;
void PrintResults(int32_t zcri, int32_t zflag, int32_t cflag, int32_t result)
{
if (zcri & 8) fprintf(tracefile, " Z=%d", zflag);
if (zcri & 4) fprintf(tracefile, " C=%d", cflag);
if (zcri & 2) fprintf(tracefile, " R=%8.8x", result);
}
static int32_t parity(int32_t val)
{
val ^= val >> 16;
val ^= val >> 8;
val ^= val >> 4;
val ^= val >> 2;
val ^= val >> 1;
return val & 1;
}
static int32_t abs(int32_t val)
{
return val < 0 ? -val : val;
}
int32_t CheckWaitFlag1(PasmVarsT *pasmvars, int mode)
{
int32_t hubmode = mode & 1;
int32_t debugmode = mode & 2;
int32_t waitflag = pasmvars->waitflag;
if (waitflag)
{
waitflag--;
}
else if (hubmode)
{
waitflag = ((pasmvars->cogid >> 1) - loopcount) & 3;
waitflag++;
}
else
{
waitflag = 1;
}
if (!debugmode)
{
//if (waitflag)
//pasmvars->pc = (pasmvars->pc - 1) & 511;
pasmvars->waitflag = waitflag;
}
return waitflag;
}
void AdjustPipeForJump(PasmVarsT *pasmvars, int32_t value, int32_t jump)
{
int32_t pc = value & 0x1ff;
pasmvars->instruct1 = pasmvars->mem[pc];
if (jump)
{
pasmvars->pc = (pc + 1) & 511;
pasmvars->pc1 = pc;
}
else
{
pasmvars->pc = pasmvars->pc1;
pasmvars->pc1 = pc | 512;
}
}
int32_t ExecutePasmInstruction(PasmVarsT *pasmvars)
{
int32_t cflag = pasmvars->cflag;
int32_t zflag = pasmvars->zflag;
int32_t instruct, cond, pc;
int32_t opcode, value2, value1, zcri;
int32_t srcaddr, dstaddr;
int32_t result = 0;
// Fetch a new instruction and update the pipeline
if (!pasmvars->waitflag)
{
//printf("Fetch new instruction\n");
pasmvars->instruct2 = pasmvars->instruct1;
pasmvars->instruct1 = pasmvars->mem[pasmvars->pc];
pasmvars->pc2 = pasmvars->pc1;
pasmvars->pc1 = pasmvars->pc;
pasmvars->pc = (pasmvars->pc + 1) & 511;
}
// Get the instruction and pc at the end of the pipeline
instruct = pasmvars->instruct2;
pc = pasmvars->pc2;
cond = (instruct >> 18) & 15;
// Return if not executed
if ((!((cond >> ((cflag << 1) | zflag)) & 1)) || (pc & 0xfffffe00))
{
return 0;
}
// Check for a hub wait
if (cycleaccurate && !(instruct & 0xe0000000))
{
if (CheckWaitFlag1(pasmvars, 1)) return 0;
}
// Extract parameters from the instruction
opcode = (instruct >> 26) & 63;
srcaddr = instruct & 511;
dstaddr = (instruct >> 9) & 511;
zcri = (instruct >> 22) & 15;
// Get the two operands
value1 = pasmvars->mem[dstaddr];
if (zcri & 1)
value2 = srcaddr;
else if (srcaddr == 0x1f1)
value2 = GetCnt();
else if (srcaddr == 0x1f2)
value2 = pin_val_a;
else
value2 = pasmvars->mem[srcaddr];
// Decode the three most significant bits of the opcode
switch(opcode >> 3)
{
// Hub access opcodes
case 0:
switch (opcode & 7)
{
case 0: // wrbyte, rdbyte
case 1: // wrword, rdword
case 2: // wrlong, rdlong
if (zcri & 2) // read
{
if ((opcode & 7) == 0)
result = BYTE(value2);
else if ((opcode & 7) == 1)
result = WORD(value2);
else
result = LONG(value2);
zflag = (result == 0);
}
else // write
{
if ((opcode & 7) == 0)
BYTE(value2) = pasmvars->mem[dstaddr];
else if ((opcode & 7) == 1)
WORD(value2) = pasmvars->mem[dstaddr];
else
LONG(value2) = pasmvars->mem[dstaddr];
}
break;
case 3: // misc hubops
switch (value2 & 7)
{
int32_t par, addr;
case 0: // clkset
result = value1 & 0xff;
if (result & 0x80)
{
RebootProp();
return 0;
}
break;
case 1: // cogid
result = pasmvars->cogid;
//pasmvars->mem[dstaddr] = result;
break;
case 2: // coginit
par = (value1 >> 16) & 0xfffc;
addr = (value1 >> 2) & 0xfffc;
if (value1 & 8) // Start new cog
{
// Look for next available cog
for (result = 0; result < 8; result++)
{
if (!PasmVars[result].state) break;
}
if (result == 8) // Check if none available
{
cflag = 1;
result = 7;
zflag = 0;
break;
}
}
else
{
result = value1 & 7;
}
cflag = 0;
zflag = (result == 0);
if (addr == 0xf004 && !pasmspin)
{
SpinVarsT *spinvars = (SpinVarsT *)&PasmVars[result].mem[0x1e0];
StartCog(spinvars, par, result);
}
else
{
StartPasmCog(&PasmVars[result], par, addr, result);
}
UpdatePins();
// Return without saving if we restarted this cog
if (result == pasmvars->cogid) return result;
break;
case 3: // cogstop
for (result = 0; result < 8; result++)
{
if (!PasmVars[result].state) break;
}
cflag = (result == 8);
result = value1 & 7;
zflag = (result == 0);
PasmVars[result].state = 0;
UpdatePins();
// Return without saving if we stopped this cog
if (result == pasmvars->cogid) return result;
break;
case 4: // locknew
for (result = 0; result < 8; result++)
{
if (!lockalloc[result]) break;
}
if (result == 8)
{
cflag = 1;
result = 7;
}
else
{
cflag = 0;
lockalloc[result] = 1;
}
zflag = (result == 0);
break;
case 5: // lockret
for (result = 0; result < 8; result++)
{
if (!lockalloc[result]) break;
}
cflag = (result == 8);
result = value1 & 7;
zflag = (result == 0);
lockalloc[result] = 0;
break;
case 6: // lockset
result = value1 & 7;
zflag = (result == 0);
cflag = lockstate[result] & 1;
lockstate[result] = -1;
break;
case 7: // lockclr
result = value1 & 7;
zflag = (result == 0);
cflag = lockstate[result] & 1;
lockstate[result] = 0;
break;
}
break;
default: // Not defined
//printf("Undefined op - %8.8x\n", instruct);
break;
}
break;
// Rotate and shift
case 1:
value2 &= 0x1f; // Get five LSB's
switch (opcode & 7)
{
case 0: // ror
result = (((uint32_t)value1) >> value2) | (value1 << (32 - value2));
cflag = value1 & 1;
break;
case 1: // rol
result = (((uint32_t)value1) >> (32 - value2)) | (value1 << value2);
cflag = (value1 >> 31) & 1;
break;
case 2: // shr
result = (((uint32_t)value1) >> value2);
cflag = value1 & 1;
break;
case 3: // shl
result = (value1 << value2);
cflag = (value1 >> 31) & 1;
break;
case 4: // rcr
if (value2)
{
result = (cflag << 31) | (((uint32_t)value1) >> 1);
result >>= (value2 - 1);
}
else
result = value1;
cflag = value1 & 1;
break;
case 5: // rcl
result = cflag ? (1 << value2) - 1 : 0;
result |= (value1 << value2);
cflag = (value1 >> 31) & 1;
break;
case 6: // sar
result = value1 >> value2;
cflag = value1 & 1;
break;
case 7: // rev
cflag = value1 & 1;
value2 = 32 - value2;
result = 0;
while (value2-- > 0)
{
result = (result << 1) | (value1 & 1);
value1 >>= 1;
}
break;
}
zflag = (result == 0);
break;
// Jump, call, return and misc.
case 2:
switch (opcode & 7)
{
case 0: // mins
cflag = (value1 < value2);
zflag = (value2 == 0);
result = cflag ? value2 : value1;
break;
case 1: // maxs
cflag = (value1 < value2);
zflag = (value2 == 0);
result = cflag ? value1 : value2;
break;
case 2: // min
cflag = (((uint32_t)value1) < ((uint32_t)value2));
zflag = (value2 == 0);
result = cflag ? value2 : value1;
break;
case 3: // max
cflag = (((uint32_t)value1) < ((uint32_t)value2));
zflag = (value2 == 0);
result = cflag ? value1 : value2;
break;
case 4: // movs
cflag = ((uint32_t)value1) < ((uint32_t)value2);
result = (value1 & 0xfffffe00) | (value2 &0x1ff);
zflag = (result == 0);
break;
case 5: // movd
cflag = ((uint32_t)value1) < ((uint32_t)value2);
result = (value1 & 0xfffc01ff) | ((value2 &0x1ff) << 9);
zflag = (result == 0);
break;
case 6: // movi
cflag = ((uint32_t)value1) < ((uint32_t)value2);
result = (value1 & 0x007fffff) | ((value2 &0x1ff) << 23);
zflag = (result == 0);
break;
case 7: // ret, jmp, call, jmpret
cflag = ((uint32_t)value1) < ((uint32_t)value2);
result = (value1 & 0xfffffe00) | ((pc + 1) & 0x1ff);
//pasmvars->pc = value2 & 0x1ff;
AdjustPipeForJump(pasmvars, value2, 1);
zflag = (result == 0);
break;
}
break;
// Logical operations
case 3:
switch (opcode & 7)
{
case 0: // test, and
result = value1 & value2;
break;
case 1: // testn, andn
result = value1 & (~value2);
break;
case 2: // or
result = value1 | value2;
break;
case 3: // xor
result = value1 ^ value2;
break;
case 4: // muxc
result = (value1 & (~value2)) | (value2 & (-cflag));
break;
case 5: // muxnc
result = (value1 & (~value2)) | (value2 & (~(-cflag)));
break;
case 6: // muxz
result = (value1 & (~value2)) | (value2 & (-zflag));
break;
case 7: // muxnz
result = (value1 & (~value2)) | (value2 & (~(-zflag)));
break;
}
zflag = (result == 0);
cflag = parity(result);
break;
// Add and subtract
case 4:
switch (opcode & 7)
{
case 0: // add
result = value1 + value2;
cflag = (((value1 & value2) | ((value1 | value2) & (~result))) >> 31) & 1;
break;
case 1: // cmp, sub
result = value1 - value2;
cflag = ((uint32_t)value1) < ((uint32_t)value2);
break;
case 2: // addabs
cflag = (value2 >> 31) & 1;
value2 = abs(value2);
result = value1 + value2;
cflag ^= (((value1 & value2) | ((value1 | value2) & (~result))) >> 31) & 1;
break;
case 3: // subabs
result = abs(value2);
cflag = ((value2 >> 31) & 1) ^
(((uint32_t)value1) < ((uint32_t)result));
result = value1 - result;
break;
case 4: // sumc
result = cflag ? value1 - value2 : value1 + value2;
cflag = (~cflag) << 31;
cflag = (((cflag ^ value1 ^ value2) & (value1 ^ result)) >> 31) & 1;
break;
case 5: // sumnc
result = cflag ? value1 + value2 : value1 - value2;
cflag = cflag << 31;
cflag = (((cflag ^ value1 ^ value2) & (value1 ^ result)) >> 31) & 1;
break;
case 6: // sumz
result = zflag ? value1 - value2 : value1 + value2;
cflag = (~zflag) << 31;
cflag = (((cflag ^ value1 ^ value2) & (value1 ^ result)) >> 31) & 1;
break;
case 7: // sumnz
result = zflag ? value1 + value2 : value1 - value2;
cflag = zflag << 31;
cflag = (((cflag ^ value1 ^ value2) & (value1 ^ result)) >> 31) & 1;
break;
}
zflag = (result == 0);
break;
// Move, absolute and negate
case 5:
switch (opcode & 7)
{
case 0: // mov
result = value2;
cflag = (value2 >> 31) & 1;
break;
case 1: // neg
cflag = value2 < 0;
result = -value2;
break;
case 2: // abs
cflag = (value2 >> 31) & 1;
result = abs(value2);
break;
case 3: // absneg
cflag = (value2 >> 31) & 1;
result = -abs(value2);
break;
case 4: // negc
result = cflag ? -value2 : value2;
cflag = (value2 >> 31) & 1;
break;
case 5: // negnc
result = cflag ? value2 : -value2;
cflag = (value2 >> 31) & 1;
break;
case 6: // negz
result = zflag ? -value2 : value2;
cflag = (value2 >> 31) & 1;
break;
case 7: // negnz
result = zflag ? value2 : -value2;
cflag = (value2 >> 31) & 1;
break;
}
zflag = (result == 0);
break;
// More add and subtract
case 6:
switch (opcode & 7)
{
case 0: // cmps
result = value1 - value2;
cflag = value1 < value2;
zflag = (result == 0);
break;
case 1: // cmpsx
result = value1 - value2 - cflag;
cflag = value1 < ((int64_t)value2 + cflag);
zflag = (result == 0) & zflag;
break;
case 2: // addx
result = value1 + value2 + cflag;
cflag = (((value1 & value2) | ((value1 | value2) & (~result))) >> 31) & 1;
zflag = (result == 0) & zflag;
break;
case 3: // cmpx, subx
result = value1 - value2 - cflag;
if (value2 != 0xffffffff || !cflag)
cflag = ((uint32_t)value1) < ((uint32_t)(value2 + cflag));
zflag = (result == 0) & zflag;
break;
case 4: // adds
result = value1 + value2;
cflag = (((~(value1 ^ value2)) & (value1 ^ result)) >> 31) & 1;
zflag = (result == 0);
break;
case 5: // subs
result = value1 - value2;
zflag = (result == 0);
cflag = (((value1 ^ value2) & (value1 ^ result)) >> 31) & 1;
break;
case 6: // addsx
result = value1 + value2 + cflag;
cflag = (((~(value1 ^ value2)) & (value1 ^ result)) >> 31) & 1;
zflag = (result == 0) & zflag;
break;
case 7: // subsx
result = value1 - value2 - cflag;
cflag = (((value1 ^ value2) & (value1 ^ result)) >> 31) & 1;
zflag = (result == 0) & zflag;
break;
}
break;
// Test and jump and wait ops
case 7:
switch (opcode & 7)
{
case 0: // cmpsub
cflag = (((uint32_t)value1) >= ((uint32_t)value2));
result = cflag ? value1 - value2 : value1;
//zflag = (result == 0) & cflag;
zflag = (value1 == value2);
break;
case 1: // djnz
result = value1 - 1;
zflag = (result == 0);
cflag = (result == -1);
AdjustPipeForJump(pasmvars, value2, !zflag);
break;
case 2: // tjnz
result = value1;
zflag = (result == 0);
cflag = 0;
AdjustPipeForJump(pasmvars, value2, !zflag);
break;
case 3: // tjz
result = value1;
zflag = (result == 0);
cflag = 0;
AdjustPipeForJump(pasmvars, value2, zflag);
break;
case 4: // waitpeq - result, zflag and cflag not validated
result = (pin_val_a & value2) ^ value1;
if (result)
{
//pasmvars->state = 6;
//pasmvars->pc = (pasmvars->pc - 1) & 511;
pasmvars->waitflag = 1;
return 0;
}
else
{
//pasmvars->state = 5;
zflag = (result == 0);
cflag = 0;
}
break;
case 5: // waitpne - result, zflag and cflag not validated
result = (pin_val_a & value2) ^ value1;
if (!result)
{
//pasmvars->state = 6;
//pasmvars->pc = (pasmvars->pc - 1) & 511;
pasmvars->waitflag = 1;
return 0;
}
else
{
//pasmvars->state = 5;
zflag = (result == 0);
cflag = zflag;
}
break;
case 6: // waitcnt
result = GetCnt() - value1;
if (result < 0 || result >= 4)
{
//pasmvars->state = 6;
//pasmvars->pc = (pasmvars->pc - 1) & 511;
pasmvars->waitflag = 1;
return 0;
}
else
{
//pasmvars->state = 5;
pasmvars->waitflag = 0;
result = value1 + value2;
zflag = (result == 0);
cflag = (((value1 & value2) | ((value1 | value2) & (~result))) >> 31) & 1;
}
break;
case 7: // waitvid
break;
}
break;
}
// Conditionally update flags and write result
if (zcri & 8) pasmvars->zflag = zflag;
if (zcri & 4) pasmvars->cflag = cflag;
if (zcri & 2)
{
//if (dstaddr == 0x1f4) printf("outa = %8.8x\n", result);
pasmvars->mem[dstaddr] = result;
// Check if we need to update the pins
if (dstaddr == 0x1f4 || dstaddr == 0x1f6) UpdatePins();
}
//CheckSerialOut(pasmvars);
if (pasmvars->waitflag)
{
fprintf(tracefile, "XXXXXXXXXX BAD XXXXXXXXXXXXXXX\n");
pasmvars->waitflag--;
}
if (pasmvars->printflag)
PrintResults(zcri, zflag, cflag, result);
return result;
}
/*
+------------------------------------------------------------------------------------------------------------------------------+
| TERMS OF USE: MIT License |
+------------------------------------------------------------------------------------------------------------------------------+
|Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation |
|files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, |
|modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software|
|is furnished to do so, subject to the following conditions: |
| |
|The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.|
| |
|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
|WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
|COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
|ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+------------------------------------------------------------------------------------------------------------------------------+
*/