/****************************************************************************
 * This file defines microcode commands.
 * Author: Dennis Kuschel, K.-G.-Kiesinger-Allee 14, 28279 Bremen, Germany
 ****************************************************************************/


/* ===== DEFINITIONS ===== */


#define COUNTER_LINES         5      /* number of microcode counter lines */
#define COUNT_OF_FLAGS        3      /* number of processor flags */

#define MCODES_PER_OP         (1 << COUNTER_LINES)
#define OPCODE_SIZE           (1 << (COUNTER_LINES+COUNT_OF_FLAGS))


/* Masks for lo active signals */
#define INVERT_MC1            0      /* NONE yet */
#define INVERT_MC2            (MC2_ALU_LOAD_FLAG_C | MC2_ALU_LOAD_FLAG_VZ)
#define INVERT_MC3            (MC3_LOAD_PC | MC3_ADR_SELECT | MC3_ALUCLOCK | MC3_INC_PC | MC3_MCODE_END | MC3_INTERN)


/* microcode EPROMs address inputs */
#define EPROM_OPCODE          0      /* starts at A0, ends at A7 */
#define EPROM_FLAGS           8      /* flags start at A8 (A8=C, A9=Z, A10=V) */
#define EPROM_IRQLINE         11     /* interrupt input is A11 */
#define EPROM_COUNTER         12     /* counter takes 5 inputs: A12 - A16 */


/* FLAGS */
#define FLAG_C                0x01L  /* D0  (EPROM-input:  A8)  */
#define FLAG_Z                0x02L  /* D1  (EPROM-input:  A9)  */
#define FLAG_V                0x04L  /* D2  (EPROM-input:  A10) */

/* Microcode-outputs, 3 Eproms */
#define MC1_CLOCKS            0x0FL  /* bit mask */
#define MC1_OUTENABLES        0xF0L  /* bit mask */
#define MC2_ALU_MODE_SELECT   0x0FL  /* ALU: mode select */
#define MC2_ALU_FL_REG_SELECT 0x10L  /* ALU: ALU-Flags(0) / Register-Flags(1) -Select */
#define MC2_ALU_CARRY_SELECT  0x20L  /* ALU: Carry select, 0 = visible, 1 = hidden */
#define MC2_ALU_LOAD_FLAG_C   0x40L  /* ALU: load carry flag */
#define MC2_ALU_LOAD_FLAG_VZ  0x80L  /* ALU: load sign and zero flag */
#define MC2_ALU_MODE_BITS     (MC2_ALU_MODE_SELECT | MC2_ALU_FL_REG_SELECT | MC2_ALU_CARRY_SELECT)
#define MC3_CONST_01          0x01L  /* CONSTANT: 00 / 01 */
#define MC3_CONST_02          0x02L  /* CONSTANT: 00 / 02 */
#define MC3_INTERN            0x04L  /* 1 = only internal process, no external bus access in next clock cycle */
#define MC3_LOAD_PC           0x08L  /* Load Program Counter (lo active) */
#define MC3_ADR_SELECT        0x10L  /* 1 = temp.Addr, 0 = PC-Addr */
#define MC3_ALUCLOCK          0x20L
#define MC3_INC_PC            0x40L
#define MC3_MCODE_END         0x80L  /* Microcode end */


/* Control Codes, bits 4..7 */
#define CTRL_ALUOP            0x10L  /* ALU operation */

/* decoded MC1_OES */
#define OE_CODE               0x10L
#define OE_DATA               0x20L
#define OE_PC_LO              0x30L
#define OE_PC_HI              0x40L
#define OE_CONST              0x50L
#define OE_CLI_CLK            0x60L
#define OE_SEI_CLK            0x70L
#define OE_REG_SP             0x80L
#define OE_REG_ACCU           0x90L
#define OE_REG_X              0xA0L
#define OE_REG_Y              0xB0L
#define OE_REG_P              0xC0L
#define OE_ALU                0xD0L
#define OE_FLAGS              0xE0L
#define OE_BPALU              0xF0L

/* decoded MC1_CLOCKS */
#define CLK_FETCH_OPCODE      0x01L
#define CLK_TEMP_ADDR_LO      0x02L
#define CLK_TEMP_ADDR_HI      0x03L
#define CLK_PC_LO             0x04L
#define CLK_PC_HI             0x05L
#define CLK_CLI               0x06L
#define CLK_WRITE_RAM         0x07L
#define CLK_REG_SP            0x08L
#define CLK_REG_ACCU          0x09L
#define CLK_REG_X             0x0AL
#define CLK_REG_Y             0x0BL
#define CLK_REG_P             0x0CL
#define CLK_SEI               0x0DL
#define CLK_AREG1             0x0EL
#define CLK_AREG2             0x0FL

/* simpler names for register access */
#define REG_SP                OE_REG_SP
#define REG_ACCU              OE_REG_ACCU
#define REG_X                 OE_REG_X
#define REG_Y                 OE_REG_Y
#define REG_P                 OE_REG_P

/* ALU 2Byte - Modes */  /* A17,A18,A19 + /OE,  CARRY = A16 */
#define MODE_AND              0x00L
#define MODE_OR               0x01L
#define MODE_XOR              0x02L
#define MODE_ADC              0x03L
#define MODE_SBC              0x04L
#define MODE_MULL             0x05L
#define MODE_MULH             0x06L
#define MODE_DIV              0x07L

/* ALU 1Byte - Modes */  /* A8,A9,A10 + /OE,  CARRY = A11 */
#define MODE_CINC             0x08L
#define MODE_CDEC             0x09L
#define MODE_SHL              0x0AL
#define MODE_SHR              0x0BL
#define MODE_ROL              0x0CL
#define MODE_ROR              0x0DL
#define MODE_INC              0x0EL
#define MODE_DEC              0x0FL
#define MODE_INCC             MODE_INC
#define MODE_DECC             MODE_DEC


/* ALU Carry Flag Selection */
#define CFLAG_ALU             MC2_ALU_CARRY_SELECT
#define CFLAG_REGISTER        (MC2_ALU_CARRY_SELECT | MC2_ALU_FL_REG_SELECT)
#define CFLAG_VISIBLE         0   /* "visible" means visible to processor */


/* higher level: data transfer codes */
#define MOVE_ACCU_TO_X        (OE_REG_ACCU | CLK_REG_X)
#define MOVE_ACCU_TO_Y        (OE_REG_ACCU | CLK_REG_Y)
#define MOVE_ACCU_TO_P        (OE_REG_ACCU | CLK_REG_P)
#define MOVE_ACCU_TO_SP       (OE_REG_ACCU | CLK_REG_SP)
#define MOVE_ACCU_TO_AREG1    (OE_REG_ACCU | CLK_AREG1)
#define MOVE_ACCU_TO_AREG2    (OE_REG_ACCU | CLK_AREG2)
#define MOVE_ACCU_TO_RAM      (OE_REG_ACCU | CLK_WRITE_RAM | (MC3_ADR_SELECT<<16))

#define MOVE_X_TO_ACCU        (OE_REG_X | CLK_REG_ACCU)
#define MOVE_X_TO_Y           (OE_REG_X | CLK_REG_Y)
#define MOVE_X_TO_P           (OE_REG_X | CLK_REG_P)
#define MOVE_X_TO_SP          (OE_REG_X | CLK_REG_SP)
#define MOVE_X_TO_AREG1       (OE_REG_X | CLK_AREG1)
#define MOVE_X_TO_AREG2       (OE_REG_X | CLK_AREG2)
#define MOVE_X_TO_RAM         (OE_REG_X | CLK_WRITE_RAM    | (MC3_ADR_SELECT<<16))
#define MOVE_X_TO_ADDR_LO     (OE_REG_X | CLK_TEMP_ADDR_LO)
#define MOVE_X_TO_ADDR_HI     (OE_REG_X | CLK_TEMP_ADDR_HI)

#define MOVE_Y_TO_ACCU        (OE_REG_Y | CLK_REG_ACCU)
#define MOVE_Y_TO_X           (OE_REG_Y | CLK_REG_X)
#define MOVE_Y_TO_P           (OE_REG_Y | CLK_REG_P)
#define MOVE_Y_TO_SP          (OE_REG_Y | CLK_REG_SP)
#define MOVE_Y_TO_AREG1       (OE_REG_Y | CLK_AREG1)
#define MOVE_Y_TO_AREG2       (OE_REG_Y | CLK_AREG2)
#define MOVE_Y_TO_RAM         (OE_REG_Y | CLK_WRITE_RAM    | (MC3_ADR_SELECT<<16))
#define MOVE_Y_TO_ADDR_LO     (OE_REG_Y | CLK_TEMP_ADDR_LO)
#define MOVE_Y_TO_ADDR_HI     (OE_REG_Y | CLK_TEMP_ADDR_HI)

#define MOVE_P_TO_ACCU        (OE_REG_P | CLK_REG_ACCU)
#define MOVE_P_TO_X           (OE_REG_P | CLK_REG_X)
#define MOVE_P_TO_Y           (OE_REG_P | CLK_REG_Y)
#define MOVE_P_TO_SP          (OE_REG_P | CLK_REG_SP)
#define MOVE_P_TO_AREG1       (OE_REG_P | CLK_AREG1)
#define MOVE_P_TO_AREG2       (OE_REG_P | CLK_AREG2)
#define MOVE_P_TO_RAM         (OE_REG_P | CLK_WRITE_RAM    | (MC3_ADR_SELECT<<16))
#define MOVE_P_TO_ADDR_LO     (OE_REG_P | CLK_TEMP_ADDR_LO)
#define MOVE_P_TO_ADDR_HI     (OE_REG_P | CLK_TEMP_ADDR_HI)
#define MOVE_P_TO_PC_LO       (OE_REG_P | CLK_PC_LO)
#define MOVE_P_TO_PC_HI       (OE_REG_P | CLK_PC_HI)

#define MOVE_SP_TO_ACCU       (OE_REG_SP | CLK_REG_ACCU)
#define MOVE_SP_TO_X          (OE_REG_SP | CLK_REG_X)
#define MOVE_SP_TO_Y          (OE_REG_SP | CLK_REG_Y)
#define MOVE_SP_TO_P          (OE_REG_SP | CLK_REG_P)
#define MOVE_SP_TO_AREG1      (OE_REG_SP | CLK_AREG1)
#define MOVE_SP_TO_AREG2      (OE_REG_SP | CLK_AREG2)
#define MOVE_SP_TO_RAM        (OE_REG_SP | CLK_WRITE_RAM   | (MC3_ADR_SELECT<<16))
#define MOVE_SP_TO_ADDR_LO    (OE_REG_SP | CLK_TEMP_ADDR_LO)

#define MOVE_CONST_TO_ACCU    (OE_CONST | CLK_REG_ACCU)
#define MOVE_CONST_TO_X       (OE_CONST | CLK_REG_X)
#define MOVE_CONST_TO_Y       (OE_CONST | CLK_REG_Y)
#define MOVE_CONST_TO_P       (OE_CONST | CLK_REG_P)
#define MOVE_CONST_TO_SP      (OE_CONST | CLK_REG_SP)
#define MOVE_CONST_TO_AREG1   (OE_CONST | CLK_AREG1)
#define MOVE_CONST_TO_AREG2   (OE_CONST | CLK_AREG2)
#define MOVE_CONST_TO_RAM     (OE_CONST | CLK_WRITE_RAM    | (MC3_ADR_SELECT<<16))
#define MOVE_CONST_TO_ADDR_LO (OE_CONST | CLK_TEMP_ADDR_LO)
#define MOVE_CONST_TO_ADDR_HI (OE_CONST | CLK_TEMP_ADDR_HI)
#define MOVE_CONST_TO_PC_LO   (OE_CONST | CLK_PC_LO)
#define MOVE_CONST_TO_PC_HI   (OE_CONST | CLK_PC_HI)
#define MOVE_CONST_TO_FLAGSR  (OE_CONST | (MC3_ALUCLOCK << 16))

#define MOVE_FLAGS_TO_ACCU    (OE_FLAGS | CLK_REG_ACCU)
#define MOVE_FLAGS_TO_X       (OE_FLAGS | CLK_REG_X)
#define MOVE_FLAGS_TO_Y       (OE_FLAGS | CLK_REG_Y)
#define MOVE_FLAGS_TO_P       (OE_FLAGS | CLK_REG_P)
#define MOVE_FLAGS_TO_AREG1   (OE_FLAGS | CLK_AREG1)
#define MOVE_FLAGS_TO_AREG2   (OE_FLAGS | CLK_AREG2)
#define MOVE_FLAGS_TO_RAM     (OE_FLAGS | CLK_WRITE_RAM    | (MC3_ADR_SELECT<<16))

#define MOVE_ALU_TO_ACCU      (OE_ALU | CLK_REG_ACCU)
#define MOVE_ALU_TO_X         (OE_ALU | CLK_REG_X)
#define MOVE_ALU_TO_Y         (OE_ALU | CLK_REG_Y)
#define MOVE_ALU_TO_P         (OE_ALU | CLK_REG_P)
#define MOVE_ALU_TO_SP        (OE_ALU | CLK_REG_SP)
#define MOVE_ALU_TO_AREG1     (OE_ALU | CLK_AREG1)
#define MOVE_ALU_TO_AREG2     (OE_ALU | CLK_AREG2)
#define MOVE_ALU_TO_RAM       (OE_ALU | CLK_WRITE_RAM      | (MC3_ADR_SELECT<<16))
#define MOVE_ALU_TO_ADDR_LO   (OE_ALU | CLK_TEMP_ADDR_LO)
#define MOVE_ALU_TO_ADDR_HI   (OE_ALU | CLK_TEMP_ADDR_HI)
#define MOVE_ALU_TO_PC_LO     (OE_ALU | CLK_PC_LO)
#define MOVE_ALU_TO_PC_HI     (OE_ALU | CLK_PC_HI)
#define MOVE_ALU_TO_FLAGSR    (OE_ALU | (MC3_ALUCLOCK << 16))

#define MOVE_RAM_TO_ACCU      (OE_DATA | CLK_REG_ACCU      | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_X         (OE_DATA | CLK_REG_X         | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_Y         (OE_DATA | CLK_REG_Y         | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_P         (OE_DATA | CLK_REG_P         | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_SP        (OE_DATA | CLK_REG_SP        | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_AREG1     (OE_DATA | CLK_AREG1         | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_AREG2     (OE_DATA | CLK_AREG2         | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_ADDR_LO   (OE_DATA | CLK_TEMP_ADDR_LO  | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_ADDR_HI   (OE_DATA | CLK_TEMP_ADDR_HI  | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_PC_LO     (OE_DATA | CLK_PC_LO         | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_PC_HI     (OE_DATA | CLK_PC_HI         | (MC3_ADR_SELECT<<16))
#define MOVE_RAM_TO_FLAGSR    (OE_DATA | ((MC3_ALUCLOCK | MC3_ADR_SELECT)<<16))

#define MOVE_ROMAD_TO_ACCU    (OE_CODE | CLK_REG_ACCU      | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_X       (OE_CODE | CLK_REG_X         | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_Y       (OE_CODE | CLK_REG_Y         | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_P       (OE_CODE | CLK_REG_P         | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_SP      (OE_CODE | CLK_REG_SP        | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_AREG1   (OE_CODE | CLK_AREG1         | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_AREG2   (OE_CODE | CLK_AREG2         | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_ADDR_LO (OE_CODE | CLK_TEMP_ADDR_LO  | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_ADDR_HI (OE_CODE | CLK_TEMP_ADDR_HI  | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_PC_LO   (OE_CODE | CLK_PC_LO         | (MC3_ADR_SELECT<<16))
#define MOVE_ROMAD_TO_PC_HI   (OE_CODE | CLK_PC_HI         | (MC3_ADR_SELECT<<16))

#define MOVE_ROMPC_TO_ACCU    (OE_CODE | CLK_REG_ACCU)
#define MOVE_ROMPC_TO_X       (OE_CODE | CLK_REG_X)
#define MOVE_ROMPC_TO_Y       (OE_CODE | CLK_REG_Y)
#define MOVE_ROMPC_TO_P       (OE_CODE | CLK_REG_P)
#define MOVE_ROMPC_TO_SP      (OE_CODE | CLK_REG_SP)
#define MOVE_ROMPC_TO_AREG1   (OE_CODE | CLK_AREG1)
#define MOVE_ROMPC_TO_AREG2   (OE_CODE | CLK_AREG2)
#define MOVE_ROMPC_TO_ADDR_LO (OE_CODE | CLK_TEMP_ADDR_LO)
#define MOVE_ROMPC_TO_ADDR_HI (OE_CODE | CLK_TEMP_ADDR_HI)
#define MOVE_ROMPC_TO_PC_LO   (OE_CODE | CLK_PC_LO)
#define MOVE_ROMPC_TO_PC_HI   (OE_CODE | CLK_PC_HI)

#define MOVE_PC_LO_TO_P       (OE_PC_LO | CLK_REG_P)
#define MOVE_PC_LO_TO_AREG1   (OE_PC_LO | CLK_AREG1)
#define MOVE_PC_LO_TO_AREG2   (OE_PC_LO | CLK_AREG2)
#define MOVE_PC_LO_TO_RAM     (OE_PC_LO | CLK_WRITE_RAM    | (MC3_ADR_SELECT<<16))

#define MOVE_PC_HI_TO_P       (OE_PC_HI | CLK_REG_P)
#define MOVE_PC_HI_TO_AREG1   (OE_PC_HI | CLK_AREG1)
#define MOVE_PC_HI_TO_AREG2   (OE_PC_HI | CLK_AREG2)
#define MOVE_PC_HI_TO_RAM     (OE_PC_HI | CLK_WRITE_RAM    | (MC3_ADR_SELECT<<16))

#define INC_PC                (MC3_INC_PC<<16)
#define CLI                   (OE_CLI_CLK | CLK_CLI)
#define SEI                   (OE_SEI_CLK | CLK_SEI)
#define FETCH                 (OE_CODE | CLK_FETCH_OPCODE | INC_PC)



/* ======= MACROS ======== */

#define ALU_TO(mode,cflag,dstreg)  ((CTRL_ALUOP<<24)|((mode)<<8)|((cflag)<<8)|(MC3_ALUCLOCK<<16)|OE_BPALU|dstreg)
#define ALU(mode,cflag)            ((CTRL_ALUOP<<24)|((mode)<<8)|((cflag)<<8)|(MC3_ALUCLOCK<<16))

#define ALU_TO_ACCU      CLK_REG_ACCU
#define ALU_TO_X         CLK_REG_X
#define ALU_TO_Y         CLK_REG_Y
#define ALU_TO_P         CLK_REG_P
#define ALU_TO_SP        CLK_REG_SP
#define ALU_TO_AREG1     CLK_AREG1
#define ALU_TO_AREG2     CLK_AREG2
#define ALU_TO_ADDR_LO   CLK_TEMP_ADDR_LO
#define ALU_TO_ADDR_HI   CLK_TEMP_ADDR_HI
#define ALU_TO_PC_LO     CLK_PC_LO
#define ALU_TO_PC_HI     CLK_PC_HI
#define ALU_TO_FLAGSR    0


#define DO_ON_CFLAG(x,y)      (x) | (FLAG_C << 24), (y)
#define DO_ON_ZFLAG(x,y)      (x) | (FLAG_Z << 24), (y)
#define DO_ON_VFLAG(x,y)      (x) | (FLAG_V << 24), (y)

#define CONST_00              0
#define CONST_01              (MC3_CONST_01 << 16)
#define CONST_02              (MC3_CONST_02 << 16)
#define CONST_03              ((MC3_CONST_01 | MC3_CONST_02) << 16)

#define NEXTOP                (MC3_MCODE_END << 16)
#define LOAD_PC               ((MC3_LOAD_PC << 16) | INC_PC)
#define NONE                  0x80000000L
#define NOP                   0

#define LOAD_CFLAG_ALU        (MC2_ALU_LOAD_FLAG_C << 8)
#define LOAD_CFLAG_REG        ((MC2_ALU_LOAD_FLAG_C | MC2_ALU_FL_REG_SELECT) << 8)
#define LOAD_ZVFLAGS_ALU      (MC2_ALU_LOAD_FLAG_VZ << 8)
#define LOAD_ZVFLAGS_REG      ((MC2_ALU_LOAD_FLAG_VZ | MC2_ALU_FL_REG_SELECT) << 8)
#define LOAD_FLAGS_ALU        ((MC2_ALU_LOAD_FLAG_VZ | MC2_ALU_LOAD_FLAG_C) << 8)
#define LOAD_FLAGS_REG        ((MC2_ALU_LOAD_FLAG_VZ | MC2_ALU_LOAD_FLAG_C | MC2_ALU_FL_REG_SELECT) << 8)

#define UPDATE_FLAGS(reg)     (reg | CLK_AREG1), ALU(MODE_MULH,CFLAG_VISIBLE), LOAD_ZVFLAGS_ALU

#define ALU_TO_UPDFLAGS(reg,uops)  DO_ON_CFLAG(ALU_TO(MODE_CDEC,CFLAG_VISIBLE,reg)|uops, \
                                               ALU_TO(MODE_CINC,CFLAG_VISIBLE,reg)|uops)

#define DO_NOTHING            FETCH, INC_PC | NEXTOP

#define INC_SP                MOVE_SP_TO_AREG1, ALU_TO(MODE_INC, CFLAG_VISIBLE, ALU_TO_SP)
#define DEC_SP                MOVE_SP_TO_AREG1, ALU_TO(MODE_DEC, CFLAG_VISIBLE, ALU_TO_SP)
