mirror of
https://github.com/mirror/tinycc.git
synced 2025-02-24 07:50:12 +08:00
arm-asm: Add and, eor, sub, rsb, add, adc, sbc, rsc, tst, teq, cmp, cmn, orr, mov, bic, mvn
This commit is contained in:
parent
c7682dd9aa
commit
3b1f06c3b2
165
arm-asm.c
165
arm-asm.c
@ -347,6 +347,137 @@ static void asm_block_data_transfer_opcode(TCCState *s1, int token)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void asm_data_processing_opcode(TCCState *s1, int token)
|
||||||
|
{
|
||||||
|
Operand ops[3];
|
||||||
|
int nb_ops;
|
||||||
|
|
||||||
|
/* 16 entries per instruction for the different condition codes */
|
||||||
|
uint32_t opcode_idx = (ARM_INSTRUCTION_GROUP(token) - TOK_ASM_andeq) >> 4;
|
||||||
|
|
||||||
|
for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
|
||||||
|
parse_operand(s1, &ops[nb_ops]);
|
||||||
|
if (tok != ',') {
|
||||||
|
++nb_ops;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next(); // skip ','
|
||||||
|
}
|
||||||
|
if (nb_ops < 2)
|
||||||
|
expect("at least two operands");
|
||||||
|
else if (nb_ops == 2) {
|
||||||
|
memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
|
||||||
|
memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
|
||||||
|
nb_ops = 3;
|
||||||
|
}
|
||||||
|
if (nb_ops != 3) {
|
||||||
|
expect("two or three operands");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
uint32_t opcode = 0;
|
||||||
|
uint32_t operands = 0;
|
||||||
|
|
||||||
|
// data processing (general case):
|
||||||
|
// operands:
|
||||||
|
// Rn: bits 19...16 (first operand)
|
||||||
|
// Rd: bits 15...12 (destination)
|
||||||
|
// Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
|
||||||
|
// operator:
|
||||||
|
// bits 24...21: "OpCode"--see below
|
||||||
|
|
||||||
|
/* operations in the token list are ordered by opcode */
|
||||||
|
opcode = (opcode_idx >> 1) << 21; // drop "s"
|
||||||
|
if (ops[0].type != OP_REG32)
|
||||||
|
expect("(destination operand) register");
|
||||||
|
else if (opcode == 0xa << 21 || opcode == 0xb << 21 || opcode == 0x8 << 21 || opcode == 0x9 << 21 ) // cmp, cmn, tst, teq
|
||||||
|
operands |= ENCODE_SET_CONDITION_CODES; // force S set, otherwise it's a completely different instruction.
|
||||||
|
else
|
||||||
|
operands |= ENCODE_RD(ops[0].reg);
|
||||||
|
if (ops[1].type != OP_REG32)
|
||||||
|
expect("(first source operand) register");
|
||||||
|
else if (!(opcode == 0xd << 21 || opcode == 0xf << 21)) // not: mov, mvn (those have only one source operand)
|
||||||
|
operands |= ENCODE_RN(ops[1].reg);
|
||||||
|
switch (ops[2].type) {
|
||||||
|
case OP_REG32:
|
||||||
|
// TODO: Parse and encode shift.
|
||||||
|
operands |= ops[2].reg;
|
||||||
|
break;
|
||||||
|
case OP_IM8:
|
||||||
|
// TODO: Parse and encode rotation.
|
||||||
|
operands |= ENCODE_IMMEDIATE_FLAG;
|
||||||
|
operands |= ops[2].e.v;
|
||||||
|
break;
|
||||||
|
case OP_IM8N: // immediate negative value
|
||||||
|
// TODO: Parse and encode rotation.
|
||||||
|
operands |= ENCODE_IMMEDIATE_FLAG;
|
||||||
|
/* Instruction swapping:
|
||||||
|
0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
|
||||||
|
0011 = RSB - Rd:= Op2 - Op1 -> difficult
|
||||||
|
0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
|
||||||
|
1000 = TST - CC on: Op1 AND Op2 -> difficult
|
||||||
|
1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
|
||||||
|
1100 = ORR - Rd:= Op1 OR Op2 -> difficult
|
||||||
|
*/
|
||||||
|
switch (opcode_idx >> 1) { // "OpCode" in ARM docs
|
||||||
|
#if 0
|
||||||
|
case 0x0: // AND - Rd:= Op1 AND Op2
|
||||||
|
opcode = 0xe << 21; // BIC
|
||||||
|
operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
|
||||||
|
break;
|
||||||
|
case 0x2: // SUB - Rd:= Op1 - Op2
|
||||||
|
opcode = 0x4 << 21; // ADD
|
||||||
|
operands |= (-ops[2].e.v) & 0xFF;
|
||||||
|
break;
|
||||||
|
case 0x4: // ADD - Rd:= Op1 + Op2
|
||||||
|
opcode = 0x2 << 21; // SUB
|
||||||
|
operands |= (-ops[2].e.v) & 0xFF;
|
||||||
|
break;
|
||||||
|
case 0x5: // ADC - Rd:= Op1 + Op2 + C
|
||||||
|
opcode = 0x6 << 21; // SBC
|
||||||
|
operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
|
||||||
|
break;
|
||||||
|
case 0x6: // SBC - Rd:= Op1 - Op2 + C
|
||||||
|
opcode = 0x5 << 21; // ADC
|
||||||
|
operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case 0xa: // CMP - CC on: Op1 - Op2
|
||||||
|
opcode = 0xb << 21; // CMN
|
||||||
|
operands |= (-ops[2].e.v) & 0xFF;
|
||||||
|
break;
|
||||||
|
case 0xb: // CMN - CC on: Op1 + Op2
|
||||||
|
opcode = 0xa << 21; // CMP
|
||||||
|
operands |= (-ops[2].e.v) & 0xFF;
|
||||||
|
break;
|
||||||
|
// moveq r1, r3: 0x01a01003; mov Rd, Op2
|
||||||
|
case 0xd: // MOV - Rd:= Op2
|
||||||
|
opcode = 0xf << 21; // MVN
|
||||||
|
operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
|
||||||
|
break;
|
||||||
|
#if 0
|
||||||
|
case 0xe: // BIC - Rd:= Op1 AND NOT Op2
|
||||||
|
opcode = 0x0 << 21; // AND
|
||||||
|
operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case 0xf: // MVN - Rd:= NOT Op2
|
||||||
|
opcode = 0xd << 21; // MOV
|
||||||
|
operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token, NULL));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
expect("(second source operand) register or immediate value");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* S=0 and S=1 entries alternate one after another, in that order */
|
||||||
|
opcode |= (opcode_idx & 1) ? ENCODE_SET_CONDITION_CODES : 0;
|
||||||
|
asm_emit_opcode(token, opcode | operands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void asm_multiplication_opcode(TCCState *s1, int token)
|
static void asm_multiplication_opcode(TCCState *s1, int token)
|
||||||
{
|
{
|
||||||
Operand ops[4];
|
Operand ops[4];
|
||||||
@ -669,6 +800,40 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
|
|||||||
case TOK_ASM_strbeq:
|
case TOK_ASM_strbeq:
|
||||||
return asm_single_data_transfer_opcode(s1, token);
|
return asm_single_data_transfer_opcode(s1, token);
|
||||||
|
|
||||||
|
case TOK_ASM_andeq:
|
||||||
|
case TOK_ASM_eoreq:
|
||||||
|
case TOK_ASM_subeq:
|
||||||
|
case TOK_ASM_rsbeq:
|
||||||
|
case TOK_ASM_addeq:
|
||||||
|
case TOK_ASM_adceq:
|
||||||
|
case TOK_ASM_sbceq:
|
||||||
|
case TOK_ASM_rsceq:
|
||||||
|
case TOK_ASM_tsteq:
|
||||||
|
case TOK_ASM_teqeq:
|
||||||
|
case TOK_ASM_cmpeq:
|
||||||
|
case TOK_ASM_cmneq:
|
||||||
|
case TOK_ASM_orreq:
|
||||||
|
case TOK_ASM_moveq:
|
||||||
|
case TOK_ASM_biceq:
|
||||||
|
case TOK_ASM_mvneq:
|
||||||
|
case TOK_ASM_andseq:
|
||||||
|
case TOK_ASM_eorseq:
|
||||||
|
case TOK_ASM_subseq:
|
||||||
|
case TOK_ASM_rsbseq:
|
||||||
|
case TOK_ASM_addseq:
|
||||||
|
case TOK_ASM_adcseq:
|
||||||
|
case TOK_ASM_sbcseq:
|
||||||
|
case TOK_ASM_rscseq:
|
||||||
|
// case TOK_ASM_tstseq:
|
||||||
|
// case TOK_ASM_teqseq:
|
||||||
|
// case TOK_ASM_cmpseq:
|
||||||
|
// case TOK_ASM_cmnseq:
|
||||||
|
case TOK_ASM_orrseq:
|
||||||
|
case TOK_ASM_movseq:
|
||||||
|
case TOK_ASM_bicseq:
|
||||||
|
case TOK_ASM_mvnseq:
|
||||||
|
return asm_data_processing_opcode(s1, token);
|
||||||
|
|
||||||
case TOK_ASM_muleq:
|
case TOK_ASM_muleq:
|
||||||
case TOK_ASM_mulseq:
|
case TOK_ASM_mulseq:
|
||||||
case TOK_ASM_mlaeq:
|
case TOK_ASM_mlaeq:
|
||||||
|
35
arm-tok.h
35
arm-tok.h
@ -103,3 +103,38 @@
|
|||||||
|
|
||||||
DEF_ASM_CONDED(push)
|
DEF_ASM_CONDED(push)
|
||||||
DEF_ASM_CONDED(pop)
|
DEF_ASM_CONDED(pop)
|
||||||
|
|
||||||
|
/* data processing instructions; order is important */
|
||||||
|
|
||||||
|
DEF_ASM_CONDED(and)
|
||||||
|
DEF_ASM_CONDED(ands)
|
||||||
|
DEF_ASM_CONDED(eor)
|
||||||
|
DEF_ASM_CONDED(eors)
|
||||||
|
DEF_ASM_CONDED(sub)
|
||||||
|
DEF_ASM_CONDED(subs)
|
||||||
|
DEF_ASM_CONDED(rsb)
|
||||||
|
DEF_ASM_CONDED(rsbs)
|
||||||
|
DEF_ASM_CONDED(add)
|
||||||
|
DEF_ASM_CONDED(adds)
|
||||||
|
DEF_ASM_CONDED(adc)
|
||||||
|
DEF_ASM_CONDED(adcs)
|
||||||
|
DEF_ASM_CONDED(sbc)
|
||||||
|
DEF_ASM_CONDED(sbcs)
|
||||||
|
DEF_ASM_CONDED(rsc)
|
||||||
|
DEF_ASM_CONDED(rscs)
|
||||||
|
DEF_ASM_CONDED(tst)
|
||||||
|
DEF_ASM_CONDED(tsts) // necessary here--but not useful to the user
|
||||||
|
DEF_ASM_CONDED(teq)
|
||||||
|
DEF_ASM_CONDED(teqs) // necessary here--but not useful to the user
|
||||||
|
DEF_ASM_CONDED(cmp)
|
||||||
|
DEF_ASM_CONDED(cmps) // necessary here--but not useful to the user
|
||||||
|
DEF_ASM_CONDED(cmn)
|
||||||
|
DEF_ASM_CONDED(cmns) // necessary here--but not useful to the user
|
||||||
|
DEF_ASM_CONDED(orr)
|
||||||
|
DEF_ASM_CONDED(orrs)
|
||||||
|
DEF_ASM_CONDED(mov)
|
||||||
|
DEF_ASM_CONDED(movs)
|
||||||
|
DEF_ASM_CONDED(bic)
|
||||||
|
DEF_ASM_CONDED(bics)
|
||||||
|
DEF_ASM_CONDED(mvn)
|
||||||
|
DEF_ASM_CONDED(mvns)
|
||||||
|
Loading…
Reference in New Issue
Block a user