737 lines
19 KiB
C
Executable File
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. |
|
|
+------------------------------------------------------------------------------------------------------------------------------+
|
|
*/
|