1
0
mirror of https://github.com/rzzzwilson/pymlac.git synced 2025-06-10 09:32:41 +00:00
2018-08-05 15:00:23 +07:00

708 lines
16 KiB
C
Executable File

/******************************************************************************\
* dcpu.c *
* ------- *
* *
* This file is used to decode and execute a display processor instruction. *
* *
\******************************************************************************/
#include "vimlac.h"
#include "dcpu.h"
#include "memory.h"
#include "display.h"
#include "log.h"
#include "trace.h"
/******
* Constants, etc.
******/
//#define MSBBITS 6
//#define LSBBITS 5
#define LSBBITS 4
#define MSBMASK 0x7e0
#define LSBMASK 0x1f
// full 11 bits of display addressing
#define DMASK 03777
// AC bits that set DX or DY
#define BITS10 01777
#define DMSB 03740
#define DLSB 037
// display CPU constants - modes
#define MODE_NORMAL 0
#define MODE_DEIM 1
// full 11 bits of display addressing
#define DMASK 03777
// AC bits that set DX or DY
#define BITS10 01777
#define DMSB 03740
#define DLSB 037
/******
* Emulated registers, state, memory, etc.
******/
static WORD DPC = 0; // the display PC
static WORD Prev_DPC; // the *previous* display PC
static WORD DRS[] = {0, 0, 0, 0, 0, 0, 0, 0}; // display CPU stack
static int DRSindex; //
static int DX; //
static int DY; //
int DIB = 0; // ????
static BYTE Mode = MODE_NORMAL; // DEIM mode
static bool Running = false; // true if display processor is running
// array for DTSTS scale inc values
static int DSTS_Inc[] = {1, 2, 4, 6}; // from p59 of Programming Guide
static int DSTS_Value = 1; // index into DSTS_Inc[] [0..3]
static int DSTS_Scale = 2; // actual inv value to use
static char DEIM_result[100]; // holds DEIM_decode() result
/******************************************************************************
Description : Functions to get/set various registers.
Parameters :
Returns :
Comments :
******************************************************************************/
WORD
dcpu_get_PC(void)
{
return DPC;
}
WORD
dcpu_get_prev_PC(void)
{
return Prev_DPC;
}
void
dcpu_set_PC(WORD value)
{
DPC = value;
}
void
dcpu_set_DRSindex(int value)
{
DRSindex = value;
}
/******************************************************************************
Description : Function to handle unrecognized instruction.
Parameters :
Returns :
Comments :
******************************************************************************/
static void
illegal(void)
{
WORD oldPC = Prev_DPC & MEMMASK;
error("INTERNAL ERROR: "
"unexpected display processor opcode %06.6o at address %06.6o",
mem_get(oldPC, false), oldPC);
}
/******************************************************************************
Description : Decode a DEIM byte into human text.
Parameters : byte - the DEIM byte to decode
Returns : A string that describes the DEIM byte
Comments : Uses the static buffer "DEIM_result".
******************************************************************************/
char *
DEIMdecode(BYTE byte)
{
// empty the result buffer
char *bptr = DEIM_result;
*bptr = '\0';
// now decode the DEIM byte
if (byte & 0x80)
{
// increment byte decoding
if (byte & 0x40) // beam on/off bit
{
strcat(bptr, "B");
*++bptr = '\0';
}
else
{
strcat(bptr, "D");
*++bptr = '\0';
}
if (byte & 0x20) // increment/decrement X
{
strcat(bptr, "-");
*++bptr = '\0';
}
sprintf(bptr, "%d", (byte >> 3) & 0x03); // magnitude X movement
bptr = DEIM_result + strlen(DEIM_result);
if (byte & 0x04) // increment/decrement Y
{
strcat(bptr, "-");
*++bptr = '\0';
}
sprintf(bptr, "%d", byte & 0x03);
bptr = DEIM_result + strlen(DEIM_result); // magnitude Y movement
}
else
{
// control byte decoding
if (byte == 0111)
{
strcat(bptr, "N");
*++bptr = '\0';
}
else if (byte == 0151)
{
strcat(bptr, "R");
*++bptr = '\0';
}
else if (byte == 0171)
{
strcat(bptr, "F");
*++bptr = '\0';
}
else if (byte == 0200)
{
strcat(bptr, "P");
*++bptr = '\0';
}
else
{
sprintf(bptr, "A%03o", byte);
bptr = DEIM_result + strlen(DEIM_result);
}
}
return DEIM_result;
}
/******************************************************************************
Description : Execute a DEIM instruction byte.
Parameters : byte - the DEIM byte to decode
: last - 'true' if the last byte in a word
Returns : A trace string that describes the DEIM byte
Comments :
******************************************************************************/
static
char *doDEIMByte(BYTE byte, bool last)
{
char *trace = DEIMdecode(byte);
vlog("doDEIMByte: before, DX=%04x, DY=%04x, DSTS_Scale=%d", DX, DY, DSTS_Scale);
if (byte & 0x80)
{
// increment mode
int dx = (byte & 0x18) >> 3; // extract X/Y deltas
int dy = (byte & 0x03);
int prevDX = DX; // save previous position
int prevDY = DY;
if (byte & 0x20) // get dx sign and move X
{
DX -= dx * DSTS_Scale;
}
else
{
DX += dx * DSTS_Scale;
}
if (byte & 0x04) // get dy sign and move Y
{
DY -= dy * DSTS_Scale;
}
else
{
DY += dy * DSTS_Scale;
}
// ensure dislay X/Y in limits
DX &= DMASK;
DY &= DMASK;
if (byte & 0x40) // if beam on
{
display_draw(prevDX, prevDY, DX, DY);
}
}
else
{
// control instructions
if (byte & 0x40) // escape DEIM mode
{
Mode = MODE_NORMAL;
}
if (byte & 0x20) // DRJM
{
if (DRSindex <= 0)
{
illegal();
}
DRSindex -= 1;
DPC = DRS[DRSindex];
}
if (byte & 0x10) // inc X MSB
{
vlog("before inc X MSB, X=%04x, (1 << LSBBITS) * DSTS_Scale=%04x", DX, (1 << LSBBITS) * DSTS_Scale);
DX += (1 << LSBBITS) * DSTS_Scale;
vlog("during inc X MSB, X=%04x", DX);
DX &= DMASK;
vlog(" after inc X MSB, X=%04x", DX);
}
if (byte & 0x08) // clear X LSB
{
vlog("before clear X LSB, X=%04x", DX);
DX &= MSBMASK;
vlog(" after clear X LSB, X=%04x", DX);
}
if (byte & 0x02) // inc Y MSB
{
vlog("before inc Y MSB, Y=%04x", DY);
DY += (1 << LSBBITS) * DSTS_Scale;
DY &= DMASK;
vlog(" after inc Y MSB, Y=%04x", DY);
}
if (byte & 0x01) // clear Y LSB
{
vlog("before clear Y LSB, Y=%04x", DY);
DY &= MSBMASK;
vlog(" after clear Y LSB, Y=%04x", DY);
}
}
vlog("doDEIMByte: after, DX=%04x, DY=%04x,", DX, DY);
return trace;
}
/******************************************************************************
Description : The emulated display CPU instructions.
Parameters :
Returns : The number of display CPU cycles executed.
Comments :
******************************************************************************/
static
int i_DDXM(void)
{
DX -= 02000;
DX &= DMASK;
trace_dcpu("DDXM");
return 1;
}
static
int i_DDYM(void)
{
DY -= 02000;
DY &= DMASK;
trace_dcpu("DDYM");
return 1;
}
static
int i_DEIM(WORD address)
{
Mode = MODE_DEIM;
trace_dcpu("DEIM %s", doDEIMByte(address & 0377, true));
return 1;
}
static
int i_DHLT(void)
{
Running = false;
trace_dcpu("DHLT");
return 1;
}
static
int i_DHVC(void)
{
trace_dcpu("DHVC");
// TODO: should DO SOMETHING here?
return 1;
}
static
int i_DIXM(void)
{
DX += 04000;
DX &= DMASK;
trace_dcpu("DIXM");
return 1;
}
static
int i_DIYM(void)
{
DY += 04000;
DY &= DMASK;
trace_dcpu("DIYM");
return 1;
}
static
int i_DJMP(WORD address)
{
DPC = MASK_MEM(address + (DIB << 12));
trace_dcpu("DJMP %04o", address);
return 1;
}
static
int i_DJMS(WORD address)
{
if (DRSindex >= 8)
{
illegal();
}
DRS[DRSindex] = DPC;
DRSindex += 1;
DPC = MASK_MEM(address + (DIB << 12));
trace_dcpu("DJMS %04o", address);
return 1;
}
static
int i_DLXA(WORD address)
{
//DX = (address & BITS10) << 1;
DX = address & BITS10;
trace_dcpu("DLXA %04o", address);
return 1;
}
static
int i_DLYA(WORD address)
{
//DY = (address & BITS10) << 1;
DY = address & BITS10;
trace_dcpu("DLYA %04o", address);
return 1;
}
static
int i_DLVH(WORD word1)
{
WORD word2 = mem_get(DPC, false);
DPC = MASK_MEM(DPC + 1);
WORD word3 = mem_get(DPC, false);
DPC = MASK_MEM(DPC + 1);
// WORD dotted = word2 & 040000;
// WORD beamon = word2 & 020000;
WORD negx = word3 & 040000;
WORD negy = word3 & 020000;
WORD ygtx = word3 & 010000;
WORD M = word2 & 007777;
WORD N = word3 & 007777;
int prevDX = DX;
int prevDY = DY;
if (ygtx) // M is y, N is x
{
if (negx)
DX -= N;
else
DX += N;
if (negy)
DY -= M;
else
DY += M;
}
else // M is x, N is y
{
if (negx)
DX -= M;
else
DX += M;
if (negy)
DY -= N;
else
DY += N;
//display_draw(prevDX, prevDY, DX, DY, dotted);
display_draw(prevDX, prevDY, DX, DY);
}
trace_dcpu("DLVH");
return 3;
}
static
int i_DRJM(void)
{
if (DRSindex <= 0)
{
illegal();
}
DRSindex -= 1;
DPC = DRS[DRSindex];
trace_dcpu("DRJM");
return 1; // FIXME check # cycles used
}
static
int i_DSTB(int block)
{
DIB = block;
trace_dcpu("DSTB %d", block);
return 1;
}
static
int i_DSTS(int scale)
{
if (0 <= scale && scale <= 3)
{
DSTS_Value = scale;
DSTS_Scale = DSTS_Inc[scale];
vlog("i_DSTS: DSTS_Value=%d, DSTS_Scale=%d", DSTS_Value, DSTS_Scale);
}
else
{
illegal();
}
trace_dcpu("DSTS %d", scale);
return 1; // FIXME check # cycles used
}
static
int page00(WORD instruction)
{
int cycles = 0;
if (instruction == 000000) // DHLT
cycles = i_DHLT();
else if (instruction == 004000) // DNOP
{
cycles = 1;
trace_dcpu("DNOP");
}
else if (instruction == 004004) // DSTS 0
cycles = i_DSTS(0);
else if (instruction == 004005) // DSTS 1
cycles = i_DSTS(1);
else if (instruction == 004006) // DSTS 2
cycles = i_DSTS(2);
else if (instruction == 004007) // DSTS 3
cycles = i_DSTS(3);
else if (instruction == 004010) // DSTB 0
cycles = i_DSTB(0);
else if (instruction == 004011) // DSTB 1
cycles = i_DSTB(1);
else if (instruction == 004040) // DRJM
cycles = i_DRJM();
else if (instruction == 004100) // DDYM
cycles = i_DDYM();
else if (instruction == 004200) // DDXM
cycles = i_DDXM();
else if (instruction == 004400) // DIYM
cycles = i_DIYM();
else if (instruction == 005000) // DIXM
cycles = i_DIXM();
else if (instruction == 006000) // DHVC
cycles = i_DHVC();
else
{
illegal();
}
return cycles;
}
/******************************************************************************
Description : Function to execute one display processor instruction.
Parameters :
Returns : The number of display CPU cycles executed.
Comments :
******************************************************************************/
int
dcpu_execute_one(void)
{
if (!Running)
{
return 0;
}
//WORD dot = DPC;
WORD instruction = mem_get(DPC, false);
DPC = MASK_MEM(DPC + 1);
if (Mode == MODE_DEIM)
{
static char tmp_buff[100];
strcpy(tmp_buff, doDEIMByte(instruction >> 8, false));
if (Mode == MODE_DEIM)
{
strcat(tmp_buff, ",");
strcat(tmp_buff, doDEIMByte(instruction & 0xff, true));
}
trace_dcpu("INC %s", tmp_buff);
return 1;
}
WORD opcode = instruction >> 12;
WORD address = instruction & 07777;
if (opcode == 000)
{
return page00(instruction);
}
else if (opcode == 001)
{
return i_DLXA(address);
}
else if (opcode == 002)
{
return i_DLYA(address);
}
else if (opcode == 003)
{
return i_DEIM(address);;
}
else if (opcode == 004)
{
return i_DLVH(address);
}
else if (opcode == 005)
{
return i_DJMS(address);
}
else if (opcode == 006)
{
return i_DJMP(address);
}
else if (opcode == 007)
{
illegal();
}
else
{
illegal();
}
return 0; // to turn off "control may reach end of non-void function"
}
/******************************************************************************
Description : Function to get CPU state.
Parameters :
Returns :
Comments :
******************************************************************************/
bool
dcpu_running(void)
{
return Running;
}
/******************************************************************************
Description : Function to start the CPU.
Parameters :
Returns :
Comments :
******************************************************************************/
void
dcpu_start(void)
{
Running = true;
}
/******************************************************************************
Description : Function to stop the CPU.
Parameters :
Returns :
Comments :
******************************************************************************/
void
dcpu_stop(void)
{
Running = false;
}
/******************************************************************************
Description : Function to set the DCPU DRS index.
Parameters :
Returns :
Comments :
******************************************************************************/
void
dcpu_set_drsindex(int index)
{
DRSindex = index;
}
/******************************************************************************
Description : Function to get the display X coord.
Parameters :
Returns :
Comments :
******************************************************************************/
int
dcpu_get_x(void)
{
return DX;
}
/******************************************************************************
Description : Function to get the display Y coord.
Parameters :
Returns :
Comments :
******************************************************************************/
int
dcpu_get_y(void)
{
return DY;
}