/* codeimp16.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* */
/* AS */
/* */
/* Codegenerator National IMP-16/PACE */
/* */
/*****************************************************************************/
#include "stdinc.h"
#include "bpemu.h"
#include <ctype.h>
#include <string.h>
#include "strutil.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmallg.h"
#include "asmitree.h"
#include "codevars.h"
#include "codepseudo.h"
#include "intpseudo.h"
#include "headids.h"
#include "literals.h"
#include "codeimp16.h"
/*-------------------------------------------------------------------------*/
/* Types */
typedef struct
{
const char *p_name
;
Word code
;
} symbol_t
;
typedef enum
{
e_cpu_flag_none
= 0,
e_cpu_flag_core_pace
= 1 << 0,
e_cpu_flag_imp16_ext_instr
= 1 << 1,
e_cpu_flag_imp16_ien_status
= 1 << 2
} cpu_flags_t
;
/* NOTE: Only 4 bits are available for flags in the encoded machine code
for decode_mem(). Do NOT add more flags here! */
typedef enum
{
e_inst_flag_allow_indirect
= 1 << 0,
e_inst_flag_r01
= 1 << 1,
e_inst_flag_r0
= 1 << 2,
e_inst_flag_skip
= 1 << 3,
e_inst_flag_all
= 0x0f
} inst_flag_t
;
typedef struct
{
const char *p_name
;
cpu_flags_t flags
;
} cpu_props_t
;
#ifdef __cplusplus
# include "codeimp16.hpp"
#endif
/*-------------------------------------------------------------------------*/
/* Locals */
static symbol_t
*conditions
, *status_flags
;
static const cpu_props_t
*p_curr_cpu_props
;
static Boolean last_was_skip
, this_was_skip
;
static LongInt bps_val
;
/*-------------------------------------------------------------------------*/
/* Register Symbols */
/*!------------------------------------------------------------------------
* \fn decode_reg_core(const char *p_arg, Word *p_result, tSymbolSize *p_size)
* \brief check whether argument is a CPU register
* \param p_arg argument to check
* \param p_result numeric register value if yes
* \param p_size returns register size
* \return True if yes
* ------------------------------------------------------------------------ */
static Boolean decode_reg_core
(const char *p_arg
, Word
*p_result
, tSymbolSize
*p_size
)
{
switch (strlen(p_arg
))
{
case 3:
if ((as_toupper
(*p_arg
) == 'A')
&& (as_toupper
(p_arg
[1]) == 'C')
&& isdigit(p_arg
[2])
&& (p_arg
[2] < '4'))
{
*p_result
= p_arg
[2] - '0';
*p_size
= eSymbolSize16Bit
;
return True
;
}
break;
default:
break;
}
return False
;
}
/*!------------------------------------------------------------------------
* \fn dissect_reg_imp16(char *p_dest, size_t dest_size, tRegInt value, tSymbolSize inp_size)
* \brief dissect register symbols - IMP-16 variant
* \param p_dest destination buffer
* \param dest_size destination buffer size
* \param value numeric register value
* \param inp_size register size
* ------------------------------------------------------------------------ */
static void dissect_reg_imp16
(char *p_dest
, size_t dest_size
, tRegInt value
, tSymbolSize inp_size
)
{
switch (inp_size
)
{
case eSymbolSize16Bit
:
as_snprintf
(p_dest
, dest_size
, "AC%u", (unsigned)(value
& 3));
break;
default:
as_snprintf
(p_dest
, dest_size
, "%d-%u", (int)inp_size
, (unsigned)value
);
}
}
/*!------------------------------------------------------------------------
* \fn decode_reg(const tStrComp *p_arg, Word *p_result)
* \brief check whether argument is a CPU register or register alias
* \param p_arg argument to check
* \param p_result numeric register value if yes
* \param must_be_reg argument is expected to be a register
* ------------------------------------------------------------------------ */
static tRegEvalResult decode_reg
(const tStrComp
*p_arg
, Word
*p_result
)
{
tRegDescr reg_descr
;
tEvalResult eval_result
;
tRegEvalResult reg_eval_result
;
/* built-in register */
if (decode_reg_core
(p_arg
->str.
p_str, p_result
, &eval_result.
DataSize))
{
reg_descr.
Reg = *p_result
;
reg_eval_result
= eIsReg
;
eval_result.
DataSize = eSymbolSize16Bit
;
}
/* (register) symbol */
else
{
reg_eval_result
= EvalStrRegExpressionAsOperand
(p_arg
, ®_descr
, &eval_result
, eSymbolSizeUnknown
, False
);
/* always try numeric value for register */
if (eIsNoReg
== reg_eval_result
)
{
Boolean ok
;
reg_descr.
Reg = EvalStrIntExpression
(p_arg
, UInt2
, &ok
);
reg_eval_result
= ok
? eIsReg
: eRegAbort
;
if (ok
) eval_result.
DataSize = eSymbolSize16Bit
;
}
}
if (reg_eval_result
== eIsReg
)
{
if (eval_result.
DataSize != eSymbolSize16Bit
)
{
WrStrErrorPos
(ErrNum_InvOpSize
, p_arg
);
reg_eval_result
= eIsNoReg
;
}
}
*p_result
= reg_descr.
Reg & ~REGSYM_FLAG_ALIAS
;
return reg_eval_result
;
}
/*---------------------------------------------------------------------------*/
/* Address Parsing */
/*!------------------------------------------------------------------------
* \fn decode_mem_arg(const tStrComp *p_arg, Word *p_result, Boolean allow_indirect)
* \brief decode memory argument
* \param p_arg source argument
* \param p_result encoded addressing mode (one word mode)
* \param allow_indirect indirect mode (@) allowed?
* \return True if success
* ------------------------------------------------------------------------ */
static Boolean decode_mem_arg
(const tStrComp
*p_arg
, Word
*p_result
, Boolean allow_indirect
)
{
tStrComp arg
;
int split_pos
, arg_len
;
LongInt disp
;
LongWord addr
;
tEvalResult eval_result
;
Boolean force_pcrel
= False
, is_pcrel
;
Boolean base_ok
, disp_ok
;
*p_result
= 0x0000;
StrCompRefRight
(&arg
, p_arg
, 0);
if (*arg.
str.
p_str == '@')
{
if (!allow_indirect
)
{
WrStrErrorPos
(ErrNum_InvAddrMode
, p_arg
);
return False
;
}
StrCompIncRefLeft
(&arg
, 1);
*p_result
|= 0x1000;
}
/* The (emulated) immediate mode: We use the common literal
mechanism, and as soon as we have the literal's name, branch
to the common code handling absolute addresses: */
if (*arg.
str.
p_str == '#')
{
tEvalResult eval_result
;
Word value
;
Boolean critical
;
String l_str
;
value
= EvalStrIntExpressionOffsWithResult
(&arg
, 1, Int16
, &eval_result
);
if (!eval_result.
OK)
return False
;
critical
= mFirstPassUnknown
(eval_result.
Flags) || mUsesForwards
(eval_result.
Flags);
StrCompMkTemp
(&arg
, l_str
, sizeof(l_str
));
literal_make
(&arg
, NULL
, value
, eSymbolSize16Bit
, critical
);
force_pcrel
= True
;
goto parse_abs
;
}
split_pos
= FindDispBaseSplitWithQualifier
(arg.
str.
p_str, &arg_len
, NULL
, "()");
if (split_pos
>= 0)
{
tStrComp reg_arg
;
Word xreg
;
StrCompSplitRef
(&arg
, ®_arg
, &arg
, &arg.
str.
p_str[split_pos
]);
KillPostBlanksStrComp
(&arg
);
KillPrefBlanksStrCompRef
(®_arg
);
StrCompShorten
(®_arg
, 1);
KillPostBlanksStrComp
(®_arg
);
/* Allow addr(pc) to explicitly force PC-relative addressing */
if (!as_strcasecmp
(reg_arg.
str.
p_str, "PC"))
{
force_pcrel
= True
;
goto parse_abs
;
}
if (!decode_reg
(®_arg
, &xreg
)
|| (xreg
< 2))
return False
;
if (!*arg.
str.
p_str)
{
disp
= 0;
eval_result.
OK = True
;
}
else
disp
= EvalStrIntExpression
(&arg
, SInt8
, &eval_result.
OK);
if (!eval_result.
OK)
return False
;
*p_result
|= (xreg
<< 8) | (disp
& 0xff);
return True
;
}
parse_abs
:
addr
= EvalStrIntExpressionWithResult
(&arg
, UInt16
, &eval_result
);
if (!eval_result.
OK)
return False
;
disp
= addr
- (EProgCounter
() + 1);
disp_ok
= (disp
>= -128) && (disp
< 127);
base_ok
= bps_val
? ((addr
<= 127) || (addr
>= 0xff80u
))
: (addr
< 256);
/* For addresses in the CODE segment, preferrably use PC-relative
addressing. For all other addresses/values, preferrably use
absolute addressing: */
if (force_pcrel
)
is_pcrel
= True
;
else if (eval_result.
AddrSpaceMask & (1 << SegCode
))
is_pcrel
= disp_ok
|| !base_ok
;
else
is_pcrel
= !base_ok
;
if (is_pcrel
)
{
if (!mFirstPassUnknownOrQuestionable
(eval_result.
Flags)
&& !ChkRangePos
(disp
, -128, 127, &arg
))
return False
;
*p_result
|= (1 << 8) | (disp
& 0xff);
}
else
{
if (!mFirstPassUnknownOrQuestionable
(eval_result.
Flags)
&& !ChkRangePos
(addr
,
bps_val
? ((addr
& 0x8000ul
) ? 0xff80ul
: 0) : 0,
bps_val
? ((addr
& 0x8000ul
) ? 0xfffful
: 127) : 255,
&arg
))
return False
;
*p_result
|= (0 << 8) | (addr
& 0xff);
}
return True
;
}
/*!------------------------------------------------------------------------
* \fn decode_long_mem_arg(const tStrComp *p_arg, Word *p_result, Word *p_disp, IntType mem_type, Boolean allow_pcrel)
* \brief decode long (two word) memory argument
* \param p_arg source argument
* \param p_result encoded addressing mode (two word mode)
* \param p_disp displacement word
* \param mem_type address range for absolute addresses
* \param allow_pcrel allow PC-relative addressing?
* \return True if success
* ------------------------------------------------------------------------ */
static Boolean decode_long_mem_arg
(const tStrComp
*p_arg
, Word
*p_result
, Word
*p_disp
, IntType mem_type
, Boolean allow_pcrel
)
{
tStrComp arg
;
int split_pos
, arg_len
;
LongWord addr
;
LongInt disp
;
tEvalResult eval_result
;
Boolean force_pcrel
= False
, is_pcrel
;
*p_result
= *p_disp
= 0x0000;
StrCompRefRight
(&arg
, p_arg
, 0);
if (*arg.
str.
p_str == '@')
{
WrStrErrorPos
(ErrNum_InvAddrMode
, p_arg
);
return False
;
}
/* The (emulated) immediate mode: We use the common literal
mechanism, and as soon as we have the literal's name, branch
to the common code handling absolute addresses: */
if (*arg.
str.
p_str == '#')
{
tEvalResult eval_result
;
Word value
;
Boolean critical
;
String l_str
;
if (!allow_pcrel
)
{
WrStrErrorPos
(ErrNum_InvAddrMode
, &arg
);
return False
;
}
value
= EvalStrIntExpressionOffsWithResult
(&arg
, 1, Int16
, &eval_result
);
if (!eval_result.
OK)
return False
;
critical
= mFirstPassUnknown
(eval_result.
Flags) || mUsesForwards
(eval_result.
Flags);
StrCompMkTemp
(&arg
, l_str
, sizeof(l_str
));
literal_make
(&arg
, NULL
, value
, eSymbolSize16Bit
, critical
);
force_pcrel
= True
;
goto parse_abs
;
}
split_pos
= FindDispBaseSplitWithQualifier
(arg.
str.
p_str, &arg_len
, NULL
, "()");
if (split_pos
>= 0)
{
tStrComp reg_arg
;
Word xreg
;
StrCompSplitRef
(&arg
, ®_arg
, &arg
, &arg.
str.
p_str[split_pos
]);
KillPostBlanksStrComp
(&arg
);
KillPrefBlanksStrCompRef
(®_arg
);
StrCompShorten
(®_arg
, 1);
KillPostBlanksStrComp
(®_arg
);
if (!as_strcasecmp
(reg_arg.
str.
p_str, "PC"))
{
if (!allow_pcrel
)
{
WrStrErrorPos
(ErrNum_InvAddrMode
, p_arg
);
return False
;
}
force_pcrel
= True
;
goto parse_abs
;
}
if (!decode_reg
(®_arg
, &xreg
)
|| (xreg
< 2))
return False
;
if (!*arg.
str.
p_str)
{
*p_disp
= 0;
eval_result.
OK = True
;
}
else
*p_disp
= EvalStrIntExpression
(&arg
, (IntType
)(mem_type
+ 1), &eval_result.
OK);
if (!eval_result.
OK)
return False
;
*p_result
|= (xreg
<< 8);
return True
;
}
parse_abs
:
addr
= EvalStrIntExpressionWithResult
(&arg
, mem_type
, &eval_result
);
if (!eval_result.
OK)
return False
;
is_pcrel
= force_pcrel
|| (eval_result.
AddrSpaceMask & (1 << SegCode
));
if (is_pcrel
)
{
disp
= addr
- (EProgCounter
() + 1);
*p_result
|= (1 << 8);
*p_disp
= disp
& 0xffff;
return True
;
}
else
{
*p_result
|= (0 << 8);
*p_disp
= addr
& 0xffff;
return True
;
}
}
/*!------------------------------------------------------------------------
* \fn decode_symbol(const tStrComp *p_arg, Word *p_code, const symbol_t *p_symbols)
* \brief handle condition or status flag
* \param p_arg source argument
* \param p_code resulting code
* \param p_symbols array of available symbols
* \return True if success
* ------------------------------------------------------------------------ */
static Boolean decode_symbol
(const tStrComp
*p_arg
, Word
*p_code
, const symbol_t
*p_symbols
)
{
for (*p_code
= 0; p_symbols
[*p_code
].
p_name; (*p_code
)++)
{
if (!as_strcasecmp
(p_symbols
[*p_code
].
p_name, p_arg
->str.
p_str))
{
*p_code
= p_symbols
[*p_code
].
code;
return True
;
}
}
return False
;
}
#define decode_condition(p_arg, p_code) decode_symbol(p_arg, p_code, conditions)
#define decode_status_flag(p_arg, p_code) decode_symbol(p_arg, p_code, status_flags)
/*---------------------------------------------------------------------------*/
/* Coding Helpers */
/*!------------------------------------------------------------------------
* \fn put_code(Word code)
* \brief append one more word of machine code
* \param code machine code word to append
* ------------------------------------------------------------------------ */
static void put_code
(Word code
)
{
/* Skip instructions only skip the next word of code. If the
previous instruction was a skip, and we are about to create an
instruction consisting of more than one word, warn about this: */
if ((1 == CodeLen
) && last_was_skip
)
WrStrErrorPos
(ErrNum_TrySkipMultiwordInstruction
, &OpPart
);
WAsmCode
[CodeLen
++] = code
;
}
/*!------------------------------------------------------------------------
* \fn check_imp16_ext_instr(void)
* \brief check whether extended IMP-16 instructions are allowed and complain if not
* \return True if nothing to complain
* ------------------------------------------------------------------------ */
static Boolean check_imp16_ext_instr
(void)
{
if (!(p_curr_cpu_props
->flags
& e_cpu_flag_imp16_ext_instr
))
{
WrStrErrorPos
(ErrNum_InstructionNotSupported
, &OpPart
);
return False
;
}
return True
;
}
/*---------------------------------------------------------------------------*/
/* Instruction Decoders */
/*!------------------------------------------------------------------------
* \fn decode_ld_st(Word code)
* \brief handle load/store instructions on PACE
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_ld_st
(Word code
)
{
Word reg
, mem
, reg_max
;
if (!ChkArgCnt
(2, 2))
return;
if (!decode_mem_arg
(&ArgStr
[2], &mem
, True
))
return;
if (mem
& 0x1000)
{
reg_max
= 0;
code
-= 0x2000;
}
else
reg_max
= 3;
if ((decode_reg
(&ArgStr
[1], ®
) != eIsReg
)
|| (reg
> reg_max
))
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[1]);
return;
}
put_code
(code
| (reg
<< 10) | (mem
& 0x3ff));
}
/*!------------------------------------------------------------------------
* \fn decode_mem_reg(Word code)
* \brief handle instructions with register and memory argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_mem_reg
(Word code
)
{
Word reg
, mem
;
if (!ChkArgCnt
(2, 2))
return;
if ((decode_reg
(&ArgStr
[1], ®
) != eIsReg
)
|| ((code
& e_inst_flag_r01
) && (reg
>= 2))
|| ((code
& e_inst_flag_r0
) && (reg
!= 0)))
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[1]);
return;
}
if (decode_mem_arg
(&ArgStr
[2], &mem
, !!(code
& e_inst_flag_allow_indirect
)))
put_code
((code
& ~e_inst_flag_all
) | (reg
<< 10) | (mem
& 0x13ff));
this_was_skip
= !!(code
& e_inst_flag_skip
);
}
/*!------------------------------------------------------------------------
* \fn decode_mem(Word code)
* \brief handle instructions with one memory reference
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_mem
(Word code
)
{
Word mem
,
opcode
= (code
>> 10) & 0x3f,
i_opcode_xor
= (code
>> 4) & 0x3f;
if (!ChkArgCnt
(1, 1))
return;
if (decode_mem_arg
(&ArgStr
[1], &mem
, !!i_opcode_xor
))
put_code
(((opcode
^ ((mem
& 0x1000) ? i_opcode_xor
: 0x00)) << 10) | (mem
& 0x3ff));
this_was_skip
= !!(code
& e_inst_flag_skip
);
}
/*!------------------------------------------------------------------------
* \fn decode_long_mem(Word code)
* \brief handle instructions with one long memory reference
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_long_mem
(Word code
)
{
Word mem
, ext
;
if (!ChkArgCnt
(1, 1)
|| !check_imp16_ext_instr
())
return;
if (decode_long_mem_arg
(&ArgStr
[1], &mem
, &ext
, UInt16
, code
< 0x04c0))
{
put_code
(code
| mem
);
put_code
(ext
);
}
}
/*!------------------------------------------------------------------------
* \fn decode_long_byte_mem(Word code)
* \brief handle instructions with one long byte memory reference
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_long_byte_mem
(Word code
)
{
Word mem
, ext
;
if (!ChkArgCnt
(1, 1)
|| !check_imp16_ext_instr
())
return;
if (decode_long_mem_arg
(&ArgStr
[1], &mem
, &ext
, UInt15
, False
))
{
put_code
((code
& ~
1) | mem
);
put_code
((ext
<< 1) | (code
& 1));
}
}
/*!------------------------------------------------------------------------
* \fn decode_one_reg(Word code)
* \brief handle instructions having one register as argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_one_reg
(Word code
)
{
Word reg
;
if (!ChkArgCnt
(1, 1))
return;
if (decode_reg
(&ArgStr
[1], ®
) != eIsReg
)
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[1]);
return;
}
put_code
(code
| (reg
<< 8));
}
/*!------------------------------------------------------------------------
* \fn decode_reg_imm(Word code)
* \brief handle instructions having one register and one immediate argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_reg_imm
(Word code
)
{
Word reg
, imm_value
;
Boolean ok
;
if (!ChkArgCnt
(2, 2))
return;
if (decode_reg
(&ArgStr
[1], ®
) != eIsReg
)
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[1]);
return;
}
imm_value
= EvalStrIntExpression
(&ArgStr
[2], Int8
, &ok
);
if (ok
)
put_code
((code
& ~e_inst_flag_all
) | (reg
<< 8) | (imm_value
& 0xff));
this_was_skip
= !!(code
& e_inst_flag_skip
);
}
/*!------------------------------------------------------------------------
* \fn decode_reg_reg(Word code)
* \brief handle instructions having two register arguments
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_reg_reg
(Word code
)
{
Word reg1
, reg2
;
if (!ChkArgCnt
(2, 2))
return;
if (decode_reg
(&ArgStr
[1], ®1
) != eIsReg
)
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[1]);
return;
}
if (decode_reg
(&ArgStr
[2], ®2
) != eIsReg
)
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[2]);
return;
}
if (p_curr_cpu_props
->flags
& e_cpu_flag_core_pace
)
put_code
(code
| (reg1
<< 6) | (reg2
<< 8));
else
put_code
(code
| (reg1
<< 10) | (reg2
<< 8));
}
/*!------------------------------------------------------------------------
* \fn decode_none(Word code)
* \brief handle instructions having no argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_none
(Word code
)
{
if (ChkArgCnt
(0, 0))
put_code
(code
);
}
/*!------------------------------------------------------------------------
* \fn decode_none_ext(Word code)
* \brief handle extended instructions having no argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_none_ext
(Word code
)
{
if (ChkArgCnt
(0, 0)
|| check_imp16_ext_instr
())
put_code
(code
);
}
/*!------------------------------------------------------------------------
* \fn decode_imm7(Word code)
* \brief handle instructions having one 7 bit immediate argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_imm7
(Word code
)
{
Boolean ok
;
Word imm_value
;
if (!ChkArgCnt
(1, 1))
return;
imm_value
= EvalStrIntExpression
(&ArgStr
[1], UInt7
, &ok
);
if (ok
)
put_code
(code
| (imm_value
& 0x7f));
}
/*!------------------------------------------------------------------------
* \fn decode_imm8(Word code)
* \brief handle instructions having one 8 bit immediate argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_imm8
(Word code
)
{
Boolean ok
;
Word imm_value
;
if (!ChkArgCnt
(1, 1))
return;
imm_value
= EvalStrIntExpression
(&ArgStr
[1], UInt8
, &ok
);
if (ok
)
put_code
(code
| (imm_value
& 0xff));
}
/*!------------------------------------------------------------------------
* \fn decode_sflg_pflg_imp16(Word code)
* \brief handle PFLG/SFLG instructions - IMP-16 version
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_sflg_pflg_imp16
(Word code
)
{
tEvalResult eval_result
;
Word bit_num
, imm_value
;
if (!ChkArgCnt
(2, 2))
return;
bit_num
= EvalStrIntExpressionWithResult
(&ArgStr
[1], UInt4
, &eval_result
);
if (!eval_result.
OK)
return;
if (!mFirstPassUnknownOrQuestionable
(eval_result.
Flags)
&& !ChkRangePos
(bit_num
, 8, 15, &ArgStr
[1]))
return;
imm_value
= EvalStrIntExpressionWithResult
(&ArgStr
[2], UInt7
, &eval_result
);
if (eval_result.
OK)
put_code
(code
| ((bit_num
& 7) << 8) | (imm_value
& 0x7f));
}
/*!------------------------------------------------------------------------
* \fn decode_sflg_pflg_pace(Word code)
* \brief handle PFLG/SFLG instructions - PACE version
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_sflg_pflg_pace
(Word code
)
{
Word bit_num
;
if (ChkArgCnt
(1, 1)
&& decode_status_flag
(&ArgStr
[1], &bit_num
))
put_code
(code
| ((bit_num
& 15) << 8));
}
/*!------------------------------------------------------------------------
* \fn decode_imm7_io(Word code)
* \brief handle instructions having one 7 bit immediate argument as I/O address
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_imm7_io
(Word code
)
{
tEvalResult eval_result
;
Word imm_value
;
if (!ChkArgCnt
(1, 1))
return;
imm_value
= EvalStrIntExpressionWithResult
(&ArgStr
[1], UInt7
, &eval_result
);
if (eval_result.
OK)
{
ChkSpace
(SegIO
, eval_result.
AddrSpaceMask);
put_code
(code
| (imm_value
& 0x7f));
}
}
/*!------------------------------------------------------------------------
* \fn decode_jsri(Word code)
* \brief handle JSRI instruction
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_jsri
(Word code
)
{
tEvalResult eval_result
;
Word address
;
if (!ChkArgCnt
(1, 1))
return;
address
= EvalStrIntExpressionWithResult
(&ArgStr
[1], UInt16
, &eval_result
);
if (eval_result.
OK)
{
if (mFirstPassUnknownOrQuestionable
(eval_result.
Flags))
address
&= 0x7f;
if ((address
>= 0x80) && (address
< 0xff80))
WrStrErrorPos
(ErrNum_UnderRange
, &ArgStr
[1]);
else
put_code
(code
| (address
& 0x7f));
}
}
/*!------------------------------------------------------------------------
* \fn decode_jsrp(Word code)
* \brief handle JSRP instruction
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_jsrp
(Word code
)
{
tEvalResult eval_result
;
Word address
;
if (!ChkArgCnt
(1, 1)
|| !check_imp16_ext_instr
())
return;
address
= EvalStrIntExpressionWithResult
(&ArgStr
[1], UInt9
, &eval_result
);
if (eval_result.
OK)
{
if (mFirstPassUnknownOrQuestionable
(eval_result.
Flags))
address
&= ~
0x80;
if (address
& 0x80)
WrStrErrorPos
(ErrNum_OverRange
, &ArgStr
[1]);
else
put_code
(code
| (address
& 0x7f));
}
}
/*!------------------------------------------------------------------------
* \fn decode_shift_imp(Word code)
* \brief handle shift instructions - IMP-16 variant
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_shift_imp16
(Word code
)
{
Word reg
, count
;
Boolean ok
;
if (!ChkArgCnt
(2, 2))
return;
if (decode_reg
(&ArgStr
[1], ®
) != eIsReg
)
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[1]);
return;
}
count
= EvalStrIntExpression
(&ArgStr
[2], UInt7
, &ok
);
if (ok
)
{
if (code
& 0x0080)
count
= ~count
+ 1;
put_code
((code
& 0xfc00) | (reg
<< 8) | (count
& 0xff));
}
}
/*!------------------------------------------------------------------------
* \fn decode_shift_pace(Word code)
* \brief handle shift instructions - PACE variant
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_shift_pace
(Word code
)
{
Word reg
, count
, link
= 0;
Boolean ok
;
if (!ChkArgCnt
(2, 3))
return;
if (decode_reg
(&ArgStr
[1], ®
) != eIsReg
)
{
WrStrErrorPos
(ErrNum_InvReg
, &ArgStr
[1]);
return;
}
count
= EvalStrIntExpression
(&ArgStr
[2], UInt7
, &ok
);
if (!ok
)
return;
if (3 == ArgCnt
)
{
link
= EvalStrIntExpression
(&ArgStr
[3], UInt1
, &ok
);
if (!ok
)
return;
}
put_code
(code
| (reg
<< 8) | (count
<< 1) | link
);
}
/*!------------------------------------------------------------------------
* \fn decode_boc(Word code)
* \brief handle BOC instruction
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void decode_boc
(Word code
)
{
LongInt dist
;
tEvalResult eval_result
;
Word cond
;
if (!ChkArgCnt
(2, 2)
|| !decode_condition
(&ArgStr
[1], &cond
))
return;
dist
= EvalStrIntExpressionWithResult
(&ArgStr
[2], UInt16
, &eval_result
) - (EProgCounter
() + 1);
if (eval_result.
OK)
{
if (!mFirstPassUnknownOrQuestionable
(eval_result.
Flags) && !RangeCheck
(dist
, SInt8
))
WrStrErrorPos
(ErrNum_JmpDistTooBig
, &ArgStr
[2]);
else
put_code
(code
| (cond
<< 8) | (dist
& 0xff));
}
}
/*!------------------------------------------------------------------------
* \fn decode_reg_bit(Word code)
* \brief handle instructions with bit position as argument
* \param code machine code
* ------------------------------------------------------------------------ */
static void decode_reg_bit
(Word code
)
{
Word bit_pos
;
if (!ChkArgCnt
(1, 1)
|| !check_imp16_ext_instr
())
return;
if (!(code
& 0x0001)
|| !decode_status_flag
(&ArgStr
[1], &bit_pos
))
{
Boolean ok
;
bit_pos
= EvalStrIntExpression
(&ArgStr
[1], UInt4
, &ok
);
if (!ok
)
return;
}
put_code
((code
& 0xfffe) | (bit_pos
& 0x000f));
}
/*!------------------------------------------------------------------------
* \fn decode_jmpp_jint(Word code)
* \brief handle JMPP and JINT instructions
* \param code machine code
* ------------------------------------------------------------------------ */
static void decode_jmpp_jint
(Word code
)
{
Word address
;
tEvalResult eval_result
;
if (!ChkArgCnt
(1, 1)
|| !check_imp16_ext_instr
())
return;
address
= EvalStrIntExpressionWithResult
(&ArgStr
[1], UInt16
, &eval_result
);
if (!eval_result.
OK)
return;
if (mFirstPassUnknownOrQuestionable
(eval_result.
Flags))
address
&= 15;
if (address
< 16);
else if (!ChkRangePos
(address
, code
& 0x1f0, (code
& 0x1f0) + 15, &ArgStr
[1]))
return;
put_code
(code
| (address
& 15));
}
/*!------------------------------------------------------------------------
* \fn decode_port(Word code)
* \brief handle PORT instruction
* ------------------------------------------------------------------------ */
static void decode_port
(Word code
)
{
UNUSED
(code
);
CodeEquate
(SegIO
, 0, SegLimits
[SegIO
]);
}
/*!------------------------------------------------------------------------
* \fn decode_ltorg(Word code)
* \brief handle LTORG instruction
* ------------------------------------------------------------------------ */
static LargeInt ltorg_16
(const as_literal_t
*p_lit
, struct sStrComp
*p_name
)
{
LargeInt ret
;
SetMaxCodeLen
((CodeLen
+ 1) * 2);
ret
= EProgCounter
() + CodeLen
;
EnterIntSymbol
(p_name
, ret
, ActPC
, False
);
put_code
(p_lit
->value
& 0xffff);
return ret
;
}
static void decode_ltorg
(Word code
)
{
UNUSED
(code
);
if (ChkArgCnt
(0, 0))
literals_dump
(ltorg_16
, eSymbolSize16Bit
, MomSectionHandle
, False
);
}
/*---------------------------------------------------------------------------*/
/* Code Table Handling */
/*!------------------------------------------------------------------------
* \fn init_fields(void)
* \brief construct instruction hash table
* ------------------------------------------------------------------------ */
static void add_condition
(const char *p_name
, Word code
)
{
order_array_rsv_end
(conditions
, symbol_t
);
conditions
[InstrZ
].
p_name = p_name
;
conditions
[InstrZ
++].
code = code
;
}
static void add_status_flag
(const char *p_name
, Word code
)
{
order_array_rsv_end
(status_flags
, symbol_t
);
status_flags
[InstrZ
].
p_name = p_name
;
status_flags
[InstrZ
++].
code = code
;
}
static void init_fields
(Boolean is_pace
)
{
InstTable
= CreateInstTable
(103);
AddInstTable
(InstTable
, "HALT" , 0x0000, decode_none
);
AddInstTable
(InstTable
, "PUSHF", is_pace
? 0x0c00 : 0x0080, decode_none
);
AddInstTable
(InstTable
, "PULLF", is_pace
? 0x1000 : 0x0280, decode_none
);
AddInstTable
(InstTable
, "NOP" , NOPCode
, decode_none
);
AddInstTable
(InstTable
, "BOC" , is_pace
? 0x4000 : 0x1000, decode_boc
);
AddInstTable
(InstTable
, "JMP" , is_pace
? 0x1a00 : 0x2010, decode_mem
);
AddInstTable
(InstTable
, "JSR" , is_pace
? 0x1600 : 0x2810, decode_mem
);
AddInstTable
(InstTable
, "RADD" , is_pace
? 0x6800 : 0x3000, decode_reg_reg
);
AddInstTable
(InstTable
, "RAND" , is_pace
? 0x5400 : 0x3083, decode_reg_reg
);
AddInstTable
(InstTable
, "RXOR" , is_pace
? 0x5800 : 0x3082, decode_reg_reg
);
AddInstTable
(InstTable
, "RXCH" , is_pace
? 0x6c00 : 0x3080, decode_reg_reg
);
AddInstTable
(InstTable
, "RCPY" , is_pace
? 0x5c00 : 0x3081, decode_reg_reg
);
AddInstTable
(InstTable
, "PUSH" , is_pace
? 0x6000 : 0x4000, decode_one_reg
);
AddInstTable
(InstTable
, "PULL" , is_pace
? 0x6400 : 0x4400, decode_one_reg
);
AddInstTable
(InstTable
, "AISZ" , (is_pace
? 0x7800 : 0x4800) | e_inst_flag_skip
, decode_reg_imm
);
AddInstTable
(InstTable
, "LI" , is_pace
? 0x5000 : 0x4c00, decode_reg_imm
);
AddInstTable
(InstTable
, "CAI" , is_pace
? 0x7000 : 0x5000, decode_reg_imm
);
AddInstTable
(InstTable
, "XCHRS", is_pace
? 0x1c00 : 0x5400, decode_one_reg
);
AddInstTable
(InstTable
, "ISZ" , (is_pace
? 0x8c00 : 0x7800) | e_inst_flag_skip
, decode_mem
);
AddInstTable
(InstTable
, "DSZ" , (is_pace
? 0xac00 : 0x7c00) | e_inst_flag_skip
, decode_mem
);
AddInstTable
(InstTable
, "ADD" , is_pace
? 0xe000 : 0xc000, decode_mem_reg
);
AddInstTable
(InstTable
, "AND" , is_pace
? (0xa800 | e_inst_flag_r0
) : (0x6000 | e_inst_flag_r01
), decode_mem_reg
);
AddInstTable
(InstTable
, "OR" , is_pace
? (0xa400 | e_inst_flag_r0
) : (0x6800 | e_inst_flag_r01
), decode_mem_reg
);
AddInstTable
(InstTable
, "SKG" , (is_pace
? (0x9c00 | e_inst_flag_r0
) : 0xe000) | e_inst_flag_skip
, decode_mem_reg
);
AddInstTable
(InstTable
, "SKNE" , 0xf000 | e_inst_flag_skip
, decode_mem_reg
);
AddInstTable
(InstTable
, "SKAZ" , (is_pace
? (0xb800 | e_inst_flag_r0
) : (0x7000 | e_inst_flag_r01
)) | e_inst_flag_skip
, decode_mem_reg
);
if (is_pace
)
{
AddInstTable
(InstTable
, "LD" , 0xc000, decode_ld_st
);
AddInstTable
(InstTable
, "ST" , 0xd000, decode_ld_st
);
AddInstTable
(InstTable
, "RTI" , 0x7c00, decode_imm8
);
AddInstTable
(InstTable
, "RTS" , 0x8000, decode_imm8
);
AddInstTable
(InstTable
, "RADC" , 0x7400, decode_reg_reg
);
AddInstTable
(InstTable
, "SUBB" , (0x9000 | e_inst_flag_r0
), decode_mem_reg
);
AddInstTable
(InstTable
, "DECA" , (0x8800 | e_inst_flag_r0
), decode_mem_reg
);
AddInstTable
(InstTable
, "LSEX" , (0xbc00 | e_inst_flag_r0
), decode_mem_reg
);
AddInstTable
(InstTable
, "CFR" , 0x0400, decode_one_reg
);
AddInstTable
(InstTable
, "CRF" , 0x0800, decode_one_reg
);
AddInstTable
(InstTable
, "ROL" , 0x2000, decode_shift_pace
);
AddInstTable
(InstTable
, "ROR" , 0x2400, decode_shift_pace
);
AddInstTable
(InstTable
, "SHL" , 0x2800, decode_shift_pace
);
AddInstTable
(InstTable
, "SHR" , 0x2c00, decode_shift_pace
);
AddInstTable
(InstTable
, "SFLG" , 0x3080, decode_sflg_pflg_pace
);
AddInstTable
(InstTable
, "PFLG" , 0x3000, decode_sflg_pflg_pace
);
}
else /* IMP-16 */
{
AddInstTable
(InstTable
, "LD" , (0x8000 | e_inst_flag_allow_indirect
), decode_mem_reg
);
AddInstTable
(InstTable
, "ST" , (0xa000 | e_inst_flag_allow_indirect
), decode_mem_reg
);
AddInstTable
(InstTable
, "RTI" , 0x0100, decode_imm7
);
AddInstTable
(InstTable
, "RTS" , 0x0200, decode_imm7
);
AddInstTable
(InstTable
, "JSRP" , 0x0300, decode_jsrp
);
AddInstTable
(InstTable
, "JSRI" , 0x0380, decode_jsri
);
AddInstTable
(InstTable
, "RIN" , 0x0400, decode_imm7_io
);
AddInstTable
(InstTable
, "ROUT" , 0x0600, decode_imm7_io
);
AddInstTable
(InstTable
, "MPY" , 0x0480, decode_long_mem
);
AddInstTable
(InstTable
, "DIV" , 0x0490, decode_long_mem
);
AddInstTable
(InstTable
, "DADD" , 0x04a0, decode_long_mem
);
AddInstTable
(InstTable
, "DSUB" , 0x04b0, decode_long_mem
);
AddInstTable
(InstTable
, "LDB" , 0x04c0, decode_long_mem
);
AddInstTable
(InstTable
, "STB" , 0x04d0, decode_long_mem
);
AddInstTable
(InstTable
, "LLB" , 0x04c0, decode_long_byte_mem
);
AddInstTable
(InstTable
, "SLB" , 0x04d0, decode_long_byte_mem
);
AddInstTable
(InstTable
, "LRB" , 0x04c1, decode_long_byte_mem
);
AddInstTable
(InstTable
, "SRB" , 0x04d1, decode_long_byte_mem
);
AddInstTable
(InstTable
, "JMPP" , 0x0500, decode_jmpp_jint
);
AddInstTable
(InstTable
, "ISCAN", 0x0510, decode_none_ext
);
AddInstTable
(InstTable
, "JINT" , 0x0520, decode_jmpp_jint
);
AddInstTable
(InstTable
, "SETST", 0x0701, decode_reg_bit
);
AddInstTable
(InstTable
, "CLRST", 0x0711, decode_reg_bit
);
AddInstTable
(InstTable
, "SETBIT",0x0720, decode_reg_bit
);
AddInstTable
(InstTable
, "CLRBIT",0x0730, decode_reg_bit
);
AddInstTable
(InstTable
, "SKSTF", 0x0741, decode_reg_bit
);
AddInstTable
(InstTable
, "SKBIT", 0x0750, decode_reg_bit
);
AddInstTable
(InstTable
, "CMPBIT",0x0760, decode_reg_bit
);
AddInstTable
(InstTable
, "SUB" , 0xd000, decode_mem_reg
);
AddInstTable
(InstTable
, "ROL" , 0x5800, decode_shift_imp16
);
AddInstTable
(InstTable
, "ROR" , 0x5880, decode_shift_imp16
);
AddInstTable
(InstTable
, "SHL" , 0x5c00, decode_shift_imp16
);
AddInstTable
(InstTable
, "SHR" , 0x5c80, decode_shift_imp16
);
AddInstTable
(InstTable
, "SFLG" , 0x0800, decode_sflg_pflg_imp16
);
AddInstTable
(InstTable
, "PFLG" , 0x0880, decode_sflg_pflg_imp16
);
}
if (ValidSegs
& (1 << SegCode
))
AddInstTable
(InstTable
, "PORT" , 0, decode_port
);
AddInstTable
(InstTable
, "ASCII" , eIntPseudoFlag_AllowInt
| eIntPseudoFlag_AllowString
| eIntPseudoFlag_BigEndian
, DecodeIntelDB
);
AddInstTable
(InstTable
, "WORD" , eIntPseudoFlag_AllowInt
| eIntPseudoFlag_AllowString
, DecodeIntelDW
);
AddInstTable
(InstTable
, "LTORG", 0, decode_ltorg
);
InstrZ
= 0;
add_condition
("REQ0", 1);
add_condition
("PSIGN", 2);
add_condition
("BIT0", 3);
add_condition
("BIT1", 4);
add_condition
("NREQ0", 5);
add_condition
("NSIGN", 11);
if (is_pace
)
{
add_condition
("STFL", 0);
add_condition
("BIT2", 6);
add_condition
("CONTIN", 7);
add_condition
("LINK", 8);
add_condition
("IEN", 9);
add_condition
("CARRY", 10);
add_condition
("OVF", 12);
add_condition
("JC13", 13);
add_condition
("JC14", 14);
add_condition
("JC15", 15);
}
else
{
add_condition
("INT", 0);
add_condition
("CPINT", 6);
add_condition
("START", 7);
add_condition
("STFL", 8);
add_condition
("INEN", 9);
add_condition
("CY/OV", 10);
if (p_curr_cpu_props
->flags
& e_cpu_flag_imp16_ien_status
)
{
add_condition
("POA", 12);
add_condition
("SEL", 13);
}
}
add_condition
(NULL
, 0);
InstrZ
= 0;
if (is_pace
)
{
add_status_flag
("IE1", 1);
add_status_flag
("IE2", 2);
add_status_flag
("IE3", 3);
add_status_flag
("IE4", 4);
add_status_flag
("IE5", 5);
add_status_flag
("OV", 6);
add_status_flag
("CY", 7);
add_status_flag
("LINK", 8);
add_status_flag
("IEN", 9);
add_status_flag
("BYTE", 10);
add_status_flag
("F11", 11);
add_status_flag
("F12", 12);
add_status_flag
("F13", 13);
add_status_flag
("F14", 14);
}
else
{
add_status_flag
("L", 15);
add_status_flag
("OV", 14);
add_status_flag
("CY", 13);
if (p_curr_cpu_props
->flags
& e_cpu_flag_imp16_ien_status
)
{
add_status_flag
("IEN3", 12);
add_status_flag
("IEN2", 8);
add_status_flag
("IEN1", 4);
add_status_flag
("IEN0", 0);
}
}
add_status_flag
(NULL
, 0);
}
/*!------------------------------------------------------------------------
* \fn deinit_fields(void)
* \brief clean up hash table
* ------------------------------------------------------------------------ */
static void deinit_fields
(void)
{
order_array_free
(conditions
);
order_array_free
(status_flags
);
DestroyInstTable
(InstTable
);
}
/*---------------------------------------------------------------------------*/
/* Semiglobal Functions */
/*!------------------------------------------------------------------------
* \fn intern_symbol_imp16(char *pArg, TempResult *pResult)
* \brief handle built-in (register) symbols for IMP-16
* \param p_arg source argument
* \param p_result result buffer
* ------------------------------------------------------------------------ */
static void intern_symbol_imp16
(char *p_arg
, TempResult
*p_result
)
{
Word reg_num
;
if (decode_reg_core
(p_arg
, ®_num
, &p_result
->DataSize
))
{
p_result
->Typ
= TempReg
;
p_result
->Contents.
RegDescr.
Reg = reg_num
;
p_result
->Contents.
RegDescr.
Dissect = dissect_reg_imp16
;
p_result
->Contents.
RegDescr.
compare = NULL
;
}
}
/*!------------------------------------------------------------------------
* \fn make_code_imp16(void)
* \brief handle machine instuctions
* ------------------------------------------------------------------------ */
static void make_code_imp16
(void)
{
CodeLen
= 0; DontPrint
= False
;
this_was_skip
= False
;
/* to be ignored */
if (Memo
(""))
goto func_exit
;
/* pseudo instructions */
if (DecodeIntelPseudo
(False
))
goto func_exit
;
if (!LookupInstTable
(InstTable
, OpPart.
str.
p_str))
WrStrErrorPos
(ErrNum_UnknownInstruction
, &OpPart
);
func_exit
:
last_was_skip
= this_was_skip
;
}
/*!------------------------------------------------------------------------
* \fn is_def_imp16(void)
* \brief does instruction consume label?
* ------------------------------------------------------------------------ */
static Boolean is_def_imp16
(void)
{
return (ValidSegs
& (1 << SegCode
)) ? Memo
("PORT") : False
;
}
/*!------------------------------------------------------------------------
* \fn switch_to_imp16(void *p_user)
* \brief switch to target
* \param properties descriptor
* ------------------------------------------------------------------------ */
static void switch_to_imp16
(void *p_user
)
{
const TFamilyDescr
*p_descr
;
Boolean is_pace
;
p_curr_cpu_props
= (cpu_props_t
*)p_user
;
is_pace
= !!(p_curr_cpu_props
->flags
& e_cpu_flag_core_pace
);
p_descr
= FindFamilyByName
(is_pace
? "IPC-16" : "IMP-16");
TurnWords
= False
;
SetIntConstMode
(eIntConstModeIBM
);
IntConstModeIBMNoTerm
= True
;
QualifyQuote
= QualifyQuote_SingleQuoteConstant
;
PCSymbol
= ".";
HeaderID
= p_descr
->Id
;
NOPCode
= is_pace
? 0x5c00 : 0x3081; /* = RCPY AC0, AC0 */
DivideChars
= ",";
HasAttrs
= False
;
ValidSegs
= 1 << SegCode
;
Grans
[SegCode
] = 2; ListGrans
[SegCode
] = 2; SegInits
[SegCode
] = 0;
SegLimits
[SegCode
] = 0xffff;
if (is_pace
)
{
static ASSUMERec assume_pace
= { "BPS", &bps_val
, 0, 1, 0, NULL
};
pASSUMERecs
= &assume_pace
;
ASSUMERecCnt
= 1;
}
else
{
ValidSegs
|= 1 << SegIO
;
Grans
[SegIO
] = 2; ListGrans
[SegIO
] = 2; SegInits
[SegCode
] = 0;
SegLimits
[SegIO
] = 0x7f;
}
MakeCode
= make_code_imp16
;
IsDef
= is_def_imp16
;
InternSymbol
= intern_symbol_imp16
;
SwitchFrom
= deinit_fields
;
init_fields
(is_pace
);
}
/*!------------------------------------------------------------------------
* \fn init_pass_pace(void)
* \brief set internal variables to default upon start of pass
* ------------------------------------------------------------------------ */
static void init_pass_pace
(void)
{
bps_val
= 0;
}
/*!------------------------------------------------------------------------
* \fn codeimp16_init(void)
* \brief attach target
* ------------------------------------------------------------------------ */
static const cpu_props_t cpu_props
[] =
{
{ "IMP-16C/200", e_cpu_flag_none
},
{ "IMP-16C/300", e_cpu_flag_imp16_ext_instr
},
{ "IMP-16P/200", e_cpu_flag_none
},
{ "IMP-16P/300", e_cpu_flag_imp16_ext_instr
},
{ "IMP-16L" , e_cpu_flag_imp16_ext_instr
| e_cpu_flag_imp16_ien_status
},
{ "IPC-16" , e_cpu_flag_core_pace
},
{ "INS8900" , e_cpu_flag_core_pace
}
};
void codeimp16_init
(void)
{
const cpu_props_t
*p_prop
;
for (p_prop
= cpu_props
; p_prop
< cpu_props
+ as_array_size
(cpu_props
); p_prop
++)
(void)AddCPUUser
(p_prop
->p_name
, switch_to_imp16
, (void*)p_prop
, NULL
);
AddInitPassProc
(init_pass_pace
);
}