mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
1634 lines
40 KiB
C
Executable File
1634 lines
40 KiB
C
Executable File
/******************************************************************************\
|
|
* cpu.c *
|
|
* ------- *
|
|
* *
|
|
* This file is used to decode and execute a main processor instruction. *
|
|
* *
|
|
\******************************************************************************/
|
|
|
|
#include "vimlac.h"
|
|
#include "cpu.h"
|
|
#include "dcpu.h"
|
|
#include "memory.h"
|
|
#include "kb.h"
|
|
#include "ptrptp.h"
|
|
#include "ttyin.h"
|
|
#include "ttyout.h"
|
|
#include "trace.h"
|
|
#include "log.h"
|
|
|
|
|
|
/******
|
|
* Emulated registers, state, memory, etc.
|
|
******/
|
|
|
|
static WORD r_AC;
|
|
static WORD r_L;
|
|
static WORD r_PC;
|
|
static WORD Prev_r_PC;
|
|
static WORD r_DS; /* data switches */
|
|
|
|
/* 40Hz sync stuff */
|
|
static bool Sync40HzOn = false;
|
|
|
|
/******
|
|
* Environment stuff. PTR and TTY in and out files, etc
|
|
******/
|
|
|
|
static bool cpu_on; /* true if main processor is running */
|
|
static bool cpu_sync_on; /* true if 40HZ flag set */
|
|
|
|
/******
|
|
* Helper macros
|
|
******/
|
|
|
|
#define ISAUTOINC(a) ((((a) & 03777) >= 010) && (((a) & 03777) <= 017))
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Calculate the effective address.
|
|
Parameters : address - the memory address
|
|
: indirect - 'true' if the access is indirect
|
|
Returns : The effective address.
|
|
Comments : Also handle increments if AUTOINC memory locations.
|
|
******************************************************************************/
|
|
static WORD
|
|
cpu_eff_address(WORD address, bool indirect)
|
|
{
|
|
// the Imlac can get into infinite defer loops, and so can we!
|
|
while (indirect)
|
|
{
|
|
if (ISAUTOINC(address))
|
|
mem_put(address, false, mem_get(address, false) + 1);
|
|
address = mem_get(address, false);
|
|
indirect = (address & 0100000);
|
|
}
|
|
|
|
return address;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Function to start the main CPU.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
void
|
|
cpu_start(void)
|
|
{
|
|
cpu_on = true;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Function to stop the main CPU.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
void
|
|
cpu_stop(void)
|
|
{
|
|
cpu_on = false;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Functions to get various registers and states.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
WORD
|
|
cpu_get_AC(void)
|
|
{
|
|
return r_AC;
|
|
}
|
|
|
|
|
|
WORD
|
|
cpu_get_L(void)
|
|
{
|
|
return r_L;
|
|
}
|
|
|
|
|
|
WORD
|
|
cpu_get_PC(void)
|
|
{
|
|
return r_PC;
|
|
}
|
|
|
|
|
|
WORD
|
|
cpu_get_DS(void)
|
|
{
|
|
return r_DS;
|
|
}
|
|
|
|
|
|
WORD
|
|
cpu_get_prev_PC(void)
|
|
{
|
|
return Prev_r_PC;
|
|
}
|
|
|
|
bool
|
|
cpu_running(void)
|
|
{
|
|
return cpu_on;
|
|
}
|
|
|
|
|
|
void
|
|
cpu_set_AC(WORD new_ac)
|
|
{
|
|
r_AC = new_ac;
|
|
}
|
|
|
|
|
|
void
|
|
cpu_set_L(WORD new_l)
|
|
{
|
|
r_L = (new_l && 1);
|
|
}
|
|
|
|
|
|
void
|
|
cpu_set_PC(WORD new_pc)
|
|
{
|
|
r_PC = new_pc;
|
|
}
|
|
|
|
|
|
void
|
|
cpu_set_DS(WORD new_ds)
|
|
{
|
|
r_DS = new_ds;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Function to handle unrecognized instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static void
|
|
illegal(void)
|
|
{
|
|
WORD oldPC = Prev_r_PC & MEMMASK;
|
|
|
|
/* memdump(LogOut, oldPC - 8, 16); */
|
|
|
|
error("INTERNAL ERROR: "
|
|
"unexpected main processor opcode %06.6o at address %06.6o",
|
|
mem_get(oldPC, false), oldPC);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC LAW/LWC instructions.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Load AC with immediate value.
|
|
******************************************************************************/
|
|
static int
|
|
i_LAW_LWC(bool indirect, WORD address)
|
|
{
|
|
/* here 'indirect' selects between LWC and LAW */
|
|
if (indirect)
|
|
{
|
|
/* LWC */
|
|
r_AC = ~address & WORD_MASK;
|
|
trace("LWC\t %5.5o", address);
|
|
}
|
|
else
|
|
{
|
|
/* LAW */
|
|
r_AC = address;
|
|
trace("LAW\t %5.5o", address);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the JMP instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : PC set to new address.
|
|
******************************************************************************/
|
|
static int
|
|
i_JMP(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
r_PC = new_address & MEMMASK;
|
|
|
|
trace("JMP\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the DAC instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Deposit AC in MEM.
|
|
******************************************************************************/
|
|
static int
|
|
i_DAC(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
mem_put(new_address, false, r_AC);
|
|
|
|
trace("DAC\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC XAM instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Exchange AC with MEM.
|
|
******************************************************************************/
|
|
static int
|
|
i_XAM(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
WORD tmp = mem_get(new_address, false);
|
|
|
|
mem_put(new_address, false, r_AC);
|
|
r_AC = tmp;
|
|
|
|
trace("XAM\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the ISZ instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Increment MEM and skip if MEM == 0.
|
|
******************************************************************************/
|
|
static int
|
|
i_ISZ(bool indirect, WORD address)
|
|
{
|
|
WORD new_value;
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
|
|
new_value = (mem_get(new_address, false) + 1) & WORD_MASK;
|
|
mem_put(new_address, false, new_value);
|
|
if (new_value == 0)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("ISZ\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC JMS instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Store PC in MEM, jump to MEM + 1.
|
|
******************************************************************************/
|
|
static int
|
|
i_JMS(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
mem_put(new_address, false, r_PC);
|
|
r_PC = ++new_address & MEMMASK;
|
|
|
|
trace("JMS\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC AND instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : AND MEM with AC.
|
|
******************************************************************************/
|
|
static int
|
|
i_AND(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
r_AC &= mem_get(new_address, false);
|
|
|
|
trace("AND\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC IOR instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Inclusive OR MEM with AC.
|
|
******************************************************************************/
|
|
static int
|
|
i_IOR(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
r_AC |= mem_get(new_address, false);
|
|
|
|
trace("IOR\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC XOR instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : XOR AC and MEM. LINK unchanged.
|
|
******************************************************************************/
|
|
static int
|
|
i_XOR(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
r_AC ^= mem_get(new_address, false);
|
|
|
|
trace("XOR\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC LAC instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Load AC from MEM.
|
|
******************************************************************************/
|
|
static int
|
|
i_LAC(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
r_AC = mem_get(new_address, false);
|
|
|
|
trace("LAC\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the ADD instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Add value at MEM to AC.
|
|
******************************************************************************/
|
|
static int
|
|
i_ADD(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
r_AC += mem_get(new_address, false);
|
|
if (r_AC & OVERFLOWMASK)
|
|
{
|
|
r_L ^= 1;
|
|
r_AC &= WORD_MASK;
|
|
}
|
|
|
|
trace("ADD\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC SUB instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Subtract MEM from AC. LINK complemented if carry.
|
|
******************************************************************************/
|
|
static int
|
|
i_SUB(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
WORD value = (~mem_get(new_address, false) + 1) & WORD_MASK;
|
|
|
|
r_AC += value;
|
|
if (r_AC & OVERFLOWMASK)
|
|
{
|
|
r_L = !r_L;
|
|
r_AC &= WORD_MASK;
|
|
}
|
|
|
|
trace("SUB\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC SAM instruction.
|
|
Parameters : indirect - TRUE if address is indirect, FALSE if immediate
|
|
: address - the memory address
|
|
Returns :
|
|
Comments : Skip if AC same as MEM.
|
|
******************************************************************************/
|
|
static int
|
|
i_SAM(bool indirect, WORD address)
|
|
{
|
|
WORD new_address = cpu_eff_address(address, indirect);
|
|
|
|
if (r_AC == mem_get(new_address, false))
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("SAM\t%c%5.5o", (indirect) ? '*' : ' ', address);
|
|
|
|
return (indirect) ? 3 : 2;
|
|
}
|
|
|
|
/******************************************************************************
|
|
Description : Decode the 'microcode' instructions.
|
|
Parameters : instruction - the complete instruction word
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
microcode(WORD instruction)
|
|
{
|
|
char trace_msg[10]; /* little buffer for opcode */
|
|
|
|
/* T1 */
|
|
if (instruction & 001)
|
|
{
|
|
r_AC = 0;
|
|
}
|
|
if (instruction & 010)
|
|
{
|
|
r_L = 0;
|
|
}
|
|
|
|
/* T2 */
|
|
if (instruction & 002)
|
|
{
|
|
r_AC = (~r_AC) & WORD_MASK;
|
|
}
|
|
if (instruction & 020)
|
|
{
|
|
r_L = (~r_L) & 01;
|
|
}
|
|
|
|
/* T3 */
|
|
if (instruction & 004)
|
|
{
|
|
if (++r_AC & OVERFLOWMASK)
|
|
r_L = (~r_L) & 01;
|
|
r_AC &= WORD_MASK;
|
|
}
|
|
if (instruction & 040)
|
|
{
|
|
r_AC |= r_DS;
|
|
r_L = (~r_L) & 1;
|
|
}
|
|
|
|
/* do some sort of trace */
|
|
strcpy(trace_msg, "");
|
|
switch (instruction)
|
|
{
|
|
case 0100000: strcat(trace_msg, "NOP"); break;
|
|
case 0100001: strcat(trace_msg, "CLA"); break;
|
|
case 0100002: strcat(trace_msg, "CMA"); break;
|
|
case 0100003: strcat(trace_msg, "STA"); break;
|
|
case 0100004: strcat(trace_msg, "IAC"); break;
|
|
case 0100005: strcat(trace_msg, "COA"); break;
|
|
case 0100006: strcat(trace_msg, "CIA"); break;
|
|
case 0100010: strcat(trace_msg, "CLL"); break;
|
|
case 0100011: strcat(trace_msg, "CAL"); break;
|
|
case 0100020: strcat(trace_msg, "CML"); break;
|
|
case 0100030: strcat(trace_msg, "STL"); break;
|
|
case 0100040: strcat(trace_msg, "ODA"); break;
|
|
case 0100041: strcat(trace_msg, "LDA"); break;
|
|
}
|
|
|
|
if ((instruction & 0100000) == 0)
|
|
{
|
|
/* bit 0 is clear, it's HLT */
|
|
cpu_on = false;
|
|
if (trace_msg[0] != 0)
|
|
strcat(trace_msg, "+HLT");
|
|
else
|
|
strcat(trace_msg, "HLT");
|
|
}
|
|
|
|
strcat(trace_msg, "\t");
|
|
trace(trace_msg);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the DSF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if display is ON.
|
|
******************************************************************************/
|
|
static int
|
|
i_DSF(void)
|
|
{
|
|
if (dcpu_on())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("DSF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC HRB instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Read PTR value into AC.
|
|
: If PTR motor off return 0.
|
|
: If PTR motor on return byte from file.
|
|
******************************************************************************/
|
|
static int
|
|
i_HRB(void)
|
|
{
|
|
r_AC |= ptr_read();
|
|
|
|
trace("HRB\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the DSN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if display is OFF.
|
|
******************************************************************************/
|
|
static int
|
|
i_DSN(void)
|
|
{
|
|
if (!dcpu_on())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("DSN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC HSF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if PTR has data.
|
|
Comments : No data until cycle counter >= 'char ready' number.
|
|
******************************************************************************/
|
|
static int
|
|
i_HSF(void)
|
|
{
|
|
if (ptr_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("HSF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC HSN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if PTR has no data.
|
|
: There is no data until cycle counter >= 'char ready' number.
|
|
******************************************************************************/
|
|
static int
|
|
i_HSN(void)
|
|
{
|
|
if (!ptr_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("HSN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC KCF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Clear the keyboard flag.
|
|
******************************************************************************/
|
|
static int
|
|
i_KCF(void)
|
|
{
|
|
kb_clear_flag();
|
|
|
|
trace("KCF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC KRB instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Read a character from the keyboard into bits 5-15 of AC.
|
|
******************************************************************************/
|
|
static int
|
|
i_KRB(void)
|
|
{
|
|
r_AC |= kb_get_char();
|
|
|
|
trace("KRB\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC KRC instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Combine the KCF and KRB instruction: Read keyboard and clear flag.
|
|
******************************************************************************/
|
|
static int
|
|
i_KRC(void)
|
|
{
|
|
r_AC |= kb_get_char();
|
|
kb_clear_flag();
|
|
|
|
trace("KRC\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC KSF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if keyboard char available.
|
|
******************************************************************************/
|
|
static int
|
|
i_KSF(void)
|
|
{
|
|
if (kb_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("KSF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC KSN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if no keyboard char available.
|
|
******************************************************************************/
|
|
static int
|
|
i_KSN(void)
|
|
{
|
|
if (!kb_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("KSN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC PPC instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Punch AC low byte to tape.
|
|
******************************************************************************/
|
|
static int
|
|
i_PPC(void)
|
|
{
|
|
ptp_punch(r_AC & 0xff);
|
|
|
|
trace("PPC\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC PSF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if PTP ready.
|
|
******************************************************************************/
|
|
static int
|
|
i_PSF(void)
|
|
{
|
|
if (ptp_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("PSF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC RAL instruction.
|
|
Parameters : shift - the number of bits to shift by [1,3]
|
|
Returns :
|
|
Comments : Rotate AC+L left 'shift' bits.
|
|
******************************************************************************/
|
|
static int
|
|
i_RAL(int shift)
|
|
{
|
|
for (; shift > 0; --shift)
|
|
{
|
|
WORD oldlink = r_L;
|
|
|
|
r_L = (r_AC >> 15) & LOWBITMASK;
|
|
r_AC = ((r_AC << 1) + oldlink) & WORD_MASK;
|
|
}
|
|
|
|
trace("RAL\t %d", shift);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the RAR instruction.
|
|
Parameters : shift - number of bits to rotate [1,3]
|
|
Returns :
|
|
Comments : Rotate right AC+L 'shift' bits.
|
|
******************************************************************************/
|
|
static int
|
|
i_RAR(int shift)
|
|
{
|
|
for (; shift > 0; --shift)
|
|
{
|
|
WORD oldlink = r_L;
|
|
|
|
r_L = r_AC & LOWBITMASK;
|
|
r_AC = ((r_AC >> 1) | (oldlink << 15)) & WORD_MASK;
|
|
}
|
|
|
|
trace("RAR\t %d", shift);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC RCF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Clear the TTY buffer flag.
|
|
******************************************************************************/
|
|
static int
|
|
i_RCF(void)
|
|
{
|
|
ttyin_clear_flag();
|
|
|
|
trace("RCF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC RRB instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Read a character from the TTY into bits 5-15 of AC.
|
|
******************************************************************************/
|
|
static int
|
|
i_RRB(void)
|
|
{
|
|
r_AC |= ttyin_get_char();
|
|
|
|
trace("RRB\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC RRC instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Read a character from the TTY and clear buffer flag.
|
|
******************************************************************************/
|
|
static int
|
|
i_RRC(void)
|
|
{
|
|
r_AC |= ttyin_get_char();
|
|
ttyin_clear_flag();
|
|
|
|
trace("RRC\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC RSF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if TTY char available.
|
|
******************************************************************************/
|
|
static int
|
|
i_RSF(void)
|
|
{
|
|
if (ttyin_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("RSF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC RSN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if no TTY char available.
|
|
******************************************************************************/
|
|
static int
|
|
i_RSN(void)
|
|
{
|
|
if (!ttyin_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("RSN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC SAL instruction.
|
|
Parameters : shift - the number of bits to shift by
|
|
Returns :
|
|
Comments : Shift AC left n places. LINK unchanged.
|
|
******************************************************************************/
|
|
static int
|
|
i_SAL(int shift)
|
|
{
|
|
WORD oldbit0 = r_AC & HIGHBITMASK;
|
|
|
|
while (shift-- > 0)
|
|
r_AC = ((r_AC << 1) & WORD_MASK) | oldbit0;
|
|
|
|
trace("SAL\t %d", shift);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC SAR instruction.
|
|
Parameters : shift - the number of bits to shift by
|
|
Returns :
|
|
Comments : Shift AC right n places.
|
|
******************************************************************************/
|
|
static int
|
|
i_SAR(int shift)
|
|
{
|
|
while (shift-- > 0)
|
|
{
|
|
WORD oldbit0 = r_AC & HIGHBITMASK;
|
|
|
|
r_AC = (r_AC >> 1) | oldbit0;
|
|
}
|
|
|
|
trace("SAR\t %d", shift);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC SSF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if 40Hz sync flip-flop is set.
|
|
******************************************************************************/
|
|
static int
|
|
i_SSF(void)
|
|
{
|
|
if (cpu_sync_on)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("SSF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC SSN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if 40Hz sync flip-flop is NOT set.
|
|
******************************************************************************/
|
|
static int
|
|
i_SSN(void)
|
|
{
|
|
if (!cpu_sync_on)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("SSN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC TCF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Reset the TTY "output done" flag.
|
|
******************************************************************************/
|
|
static int
|
|
i_TCF(void)
|
|
{
|
|
ttyout_clear_flag();
|
|
|
|
trace("TCF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC TPC instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Transmit char in AC and clear TTY ready flag
|
|
******************************************************************************/
|
|
static int
|
|
i_TPC(void)
|
|
{
|
|
ttyout_send(r_AC & 0xff);
|
|
ttyout_clear_flag();
|
|
|
|
trace("TPC\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC TPR instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Send low byte in AC to TTY output.
|
|
******************************************************************************/
|
|
static int
|
|
i_TPR(void)
|
|
{
|
|
ttyout_send(r_AC & 0xff);
|
|
|
|
trace("TPR\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC TSF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if TTY done sending
|
|
******************************************************************************/
|
|
static int
|
|
i_TSF(void)
|
|
{
|
|
if (ttyout_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("TSF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC TSN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if TTY not done sending
|
|
******************************************************************************/
|
|
static int
|
|
i_TSN(void)
|
|
{
|
|
if (!ttyout_ready())
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("TSN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the ASZ instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if AC == 0.
|
|
******************************************************************************/
|
|
static int
|
|
i_ASZ(void)
|
|
{
|
|
if (r_AC == 0)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("ASZ\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the ASN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if AC != 0.
|
|
******************************************************************************/
|
|
static int
|
|
i_ASN(void)
|
|
{
|
|
if (r_AC != 0)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("ASN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC ASP instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if AC is positive.
|
|
******************************************************************************/
|
|
static int
|
|
i_ASP(void)
|
|
{
|
|
if ((r_AC & HIGHBITMASK) == 0)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("ASP\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the LSZ instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if LINK is zero.
|
|
******************************************************************************/
|
|
static int
|
|
i_LSZ(void)
|
|
{
|
|
if (r_L == 0)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("LSZ\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the LSN instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if LINK isn't zero.
|
|
******************************************************************************/
|
|
static int
|
|
i_LSN(void)
|
|
{
|
|
if (r_L != 0)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("LSN\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC ASM instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Skip if AC is negative.
|
|
******************************************************************************/
|
|
static int
|
|
i_ASM(void)
|
|
{
|
|
if (r_AC & HIGHBITMASK)
|
|
r_PC = (r_PC + 1) & WORD_MASK;
|
|
|
|
trace("ASM\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC DLA instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Load display CPU with a new PC.
|
|
******************************************************************************/
|
|
static int
|
|
i_DLA(void)
|
|
{
|
|
dcpu_set_PC(r_AC);
|
|
|
|
trace("DLA\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC CTB instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Load display CPU with a new PC.
|
|
******************************************************************************/
|
|
static int
|
|
i_CTB(void)
|
|
{
|
|
trace("CTB\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC DOF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Turn the display processor off.
|
|
******************************************************************************/
|
|
static int
|
|
i_DOF(void)
|
|
{
|
|
dcpu_stop();
|
|
|
|
trace("DOF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC DON instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Turn the display processor on.
|
|
******************************************************************************/
|
|
static int
|
|
i_DON(void)
|
|
{
|
|
dcpu_set_DRSindex(0);
|
|
dcpu_start();
|
|
|
|
trace("DON\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC HOF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Turn the PTR off.
|
|
******************************************************************************/
|
|
static int
|
|
i_HOF(void)
|
|
{
|
|
ptr_stop();
|
|
|
|
trace("HOF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC HON instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Turn the PTR on.
|
|
******************************************************************************/
|
|
static int
|
|
i_HON(void)
|
|
{
|
|
ptr_start();
|
|
|
|
trace("HON\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC STB instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_STB(void)
|
|
{
|
|
trace("STB\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC SCF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Clear the 40Hz "sync" flag.
|
|
******************************************************************************/
|
|
static int
|
|
i_SCF(void)
|
|
{
|
|
Sync40HzOn = false;
|
|
|
|
trace("SCF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC IOS instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOS(void)
|
|
{
|
|
trace("IOS\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC IOT101 instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOT101(void)
|
|
{
|
|
trace("IOT101\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC IOT111 instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOT111(void)
|
|
{
|
|
trace("IOT111\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC i_IOT131 instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOT131(void)
|
|
{
|
|
trace("i_IOT131\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC i_IOT132 instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOT132(void)
|
|
{
|
|
trace("i_IOT132\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC IOT134 instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOT134(void)
|
|
{
|
|
trace("IOT134\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC IOT141 instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOT141(void)
|
|
{
|
|
trace("IOT141\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC IOF instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_IOF(void)
|
|
{
|
|
trace("IOF\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Emulate the IMLAC ION instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
i_ION(void)
|
|
{
|
|
trace("ION\t");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Further decode the initial '02' opcode instruction.
|
|
Parameters : instruction - the complete instruction word
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
page02(WORD instruction)
|
|
{
|
|
switch (instruction)
|
|
{
|
|
case 0002001: return i_ASZ();
|
|
case 0102001: return i_ASN();
|
|
case 0002002: return i_ASP();
|
|
case 0102002: return i_ASM();
|
|
case 0002004: return i_LSZ();
|
|
case 0102004: return i_LSN();
|
|
case 0002010: return i_DSF();
|
|
case 0102010: return i_DSN();
|
|
case 0002020: return i_KSF();
|
|
case 0102020: return i_KSN();
|
|
case 0002040: return i_RSF();
|
|
case 0102040: return i_RSN();
|
|
case 0002100: return i_TSF();
|
|
case 0102100: return i_TSN();
|
|
case 0002200: return i_SSF();
|
|
case 0102200: return i_SSN();
|
|
case 0002400: return i_HSF();
|
|
case 0102400: return i_HSN();
|
|
default: illegal();
|
|
}
|
|
return 0; /* CAN'T REACH */
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Further decode the initial '00' opcode instruction.
|
|
Parameters : instruction - the complete instruction word
|
|
Returns :
|
|
Comments :
|
|
******************************************************************************/
|
|
static int
|
|
page00(WORD instruction)
|
|
{
|
|
/******
|
|
* Pick out microcode or page 2 instructions.
|
|
******/
|
|
|
|
if ((instruction & 0077700) == 000000)
|
|
return microcode(instruction);
|
|
|
|
if ((instruction & 0077000) == 002000)
|
|
return page02(instruction);
|
|
|
|
/******
|
|
* Decode a page 00 instruction
|
|
******/
|
|
|
|
switch (instruction)
|
|
{
|
|
case 001003: return i_DLA();
|
|
case 001011: return i_CTB();
|
|
case 001012: return i_DOF();
|
|
case 001021: return i_KRB();
|
|
case 001022: return i_KCF();
|
|
case 001023: return i_KRC();
|
|
case 001031: return i_RRB();
|
|
case 001032: return i_RCF();
|
|
case 001033: return i_RRC();
|
|
case 001041: return i_TPR();
|
|
case 001042: return i_TCF();
|
|
case 001043: return i_TPC();
|
|
case 001051: return i_HRB();
|
|
case 001052: return i_HOF();
|
|
case 001061: return i_HON();
|
|
case 001062: return i_STB();
|
|
case 001071: return i_SCF();
|
|
case 001072: return i_IOS();
|
|
case 001101: return i_IOT101();
|
|
case 001111: return i_IOT111();
|
|
case 001131: return i_IOT131();
|
|
case 001132: return i_IOT132();
|
|
case 001134: return i_IOT134();
|
|
case 001141: return i_IOT141();
|
|
case 001161: return i_IOF();
|
|
case 001162: return i_ION();
|
|
case 001271: return i_PPC();
|
|
case 001274: return i_PSF();
|
|
case 003001: return i_RAL(1);
|
|
case 003002: return i_RAL(2);
|
|
case 003003: return i_RAL(3);
|
|
case 003021: return i_RAR(1);
|
|
case 003022: return i_RAR(2);
|
|
case 003023: return i_RAR(3);
|
|
case 003041: return i_SAL(1);
|
|
case 003042: return i_SAL(2);
|
|
case 003043: return i_SAL(3);
|
|
case 003061: return i_SAR(1);
|
|
case 003062: return i_SAR(2);
|
|
case 003063: return i_SAR(3);
|
|
case 003100: return i_DON();
|
|
default: illegal();
|
|
}
|
|
|
|
return 0; /* CAN'T REACH */
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Description : Function to execute one main processor instruction.
|
|
Parameters :
|
|
Returns :
|
|
Comments : Perform initial decode of 5 bit opcode and either call
|
|
: appropriate emulating function or call further decode function.
|
|
******************************************************************************/
|
|
int
|
|
cpu_execute_one(void)
|
|
{
|
|
WORD instruction;
|
|
bool indirect;
|
|
WORD opcode;
|
|
WORD address;
|
|
|
|
/******
|
|
* If main processor not running, return immediately.
|
|
******/
|
|
|
|
if (!cpu_on)
|
|
return 0;
|
|
|
|
/******
|
|
* If interrupt pending, force JMS 0.
|
|
******/
|
|
|
|
#ifdef JUNK
|
|
if (InterruptsEnabled && (InterruptWait <= 0) && InterruptsPending)
|
|
{
|
|
InterruptsEnabled = false;
|
|
i_JMS(false, 0);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/******
|
|
* Fetch the instruction. Split into initial opcode and address.
|
|
******/
|
|
|
|
Prev_r_PC = r_PC;
|
|
instruction = mem_get(r_PC, false);
|
|
++r_PC;
|
|
r_PC = r_PC & MEMMASK;
|
|
|
|
indirect = (bool) (instruction & 0100000); /* high bit set? */
|
|
opcode = (instruction >> 11) & 017; /* high 5 bits */
|
|
address = instruction & 03777; /* low 11 bits */
|
|
|
|
/******
|
|
* Now decode it.
|
|
******/
|
|
|
|
switch (opcode)
|
|
{
|
|
case 000: return page00(instruction);
|
|
case 001: return i_LAW_LWC(indirect, address);
|
|
case 002: return i_JMP(indirect, address);
|
|
/* case 003: illegal(); */
|
|
case 004: return i_DAC(indirect, address);
|
|
case 005: return i_XAM(indirect, address);
|
|
case 006: return i_ISZ(indirect, address);
|
|
case 007: return i_JMS(indirect, address);
|
|
/* case 010: illegal(); */
|
|
case 011: return i_AND(indirect, address);
|
|
case 012: return i_IOR(indirect, address);
|
|
case 013: return i_XOR(indirect, address);
|
|
case 014: return i_LAC(indirect, address);
|
|
case 015: return i_ADD(indirect, address);
|
|
case 016: return i_SUB(indirect, address);
|
|
case 017: return i_SAM(indirect, address);
|
|
default: illegal();
|
|
}
|
|
|
|
return 0; /* CAN'T REACH */
|
|
}
|
|
|
|
|