mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
708 lines
16 KiB
C
Executable File
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;
|
|
}
|
|
|