From 7900a6bb614057a2d2d014f3c5e9936db9e388c9 Mon Sep 17 00:00:00 2001 From: Danny Milosavljevic Date: Wed, 13 Jan 2021 12:25:12 +0100 Subject: [PATCH] arm-asm: Add ldc, ldcl, stc, stcl --- arm-asm.c | 178 ++++++++++++++++++++++++++++++++++++- arm-tok.h | 5 ++ tests/arm-asm-testsuite.sh | 6 ++ 3 files changed, 188 insertions(+), 1 deletion(-) diff --git a/arm-asm.c b/arm-asm.c index dd802a94..67e5a517 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -165,6 +165,10 @@ static void asm_emit_opcode(int token, uint32_t opcode) { gen_le32((condition_code_of_token(token) << 28) | opcode); } +static void asm_emit_unconditional_opcode(uint32_t opcode) { + gen_le32(opcode); +} + static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t cp_number, uint8_t cp_opcode, uint8_t cp_destination_register, uint8_t cp_n_operand_register, uint8_t cp_m_operand_register, uint8_t cp_opcode2, int inter_processor_transfer) { uint32_t opcode = 0xe000000; @@ -182,7 +186,7 @@ static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t cp_number, opcode |= cp_opcode2 << 5; //assert(cp_m_operand_register < 16); opcode |= cp_m_operand_register; - gen_le32((high_nibble << 28) | opcode); + asm_emit_unconditional_opcode((high_nibble << 28) | opcode); } static void asm_nullary_opcode(int token) @@ -1260,6 +1264,171 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) } } +static void asm_emit_coprocessor_data_transfer(uint32_t high_nibble, uint8_t cp_number, uint8_t CRd, const Operand* Rn, const Operand* offset, int offset_minus, int preincrement, int writeback, int long_transfer, int load) { + uint32_t opcode = 0x0; + opcode |= 1 << 26; // Load/Store + opcode |= 1 << 27; // coprocessor + + if (long_transfer) + opcode |= 1 << 22; // long transfer + + if (load) + opcode |= 1 << 20; // L + + opcode |= cp_number << 8; + + opcode |= ENCODE_RD(CRd); + + if (Rn->type != OP_REG32) { + expect("register"); + return; + } + opcode |= ENCODE_RN(Rn->reg); + if (preincrement) + opcode |= 1 << 24; // add offset before transfer + + if (writeback) + opcode |= 1 << 21; // write offset back into register + + if (offset->type == OP_IM8 || offset->type == OP_IM8N || offset->type == OP_IM32) { + int v = offset->e.v; + if (offset_minus) + tcc_error("minus before '#' not supported for immediate values"); + if (offset->type == OP_IM8N || v < 0) + v = -v; + else + opcode |= 1 << 23; // up + if (v & 3) { + tcc_error("immediate offset must be a multiple of 4"); + return; + } + v >>= 2; + if (v > 255) { + tcc_error("immediate offset must be between -1020 and 1020"); + return; + } + opcode |= v; + } else if (offset->type == OP_REG32) { + if (!offset_minus) + opcode |= 1 << 23; // up + opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */ + opcode |= offset->reg; + tcc_error("Using register offset to register address is not possible here"); + return; + } else + expect("immediate or register"); + + asm_emit_unconditional_opcode((high_nibble << 28) | opcode); +} + +// Almost exactly the same as asm_single_data_transfer_opcode. +// Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again. +static void asm_coprocessor_data_transfer_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + uint8_t coprocessor; + uint8_t coprocessor_destination_register; + int preincrement = 0; + int exclam = 0; + int closed_bracket = 0; + int op2_minus = 0; + int long_transfer = 0; + // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged + // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4 + // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4 + + if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) { + coprocessor = tok - TOK_ASM_p0; + next(); + } else { + expect("'c'"); + return; + } + + if (tok == ',') + next(); + else + expect("','"); + + if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) { + coprocessor_destination_register = tok - TOK_ASM_c0; + next(); + } else { + expect("'c'"); + return; + } + + if (tok == ',') + next(); + else + expect("','"); + + if (tok != '[') + expect("'['"); + else + next(); // skip '[' + + parse_operand(s1, &ops[1]); + if (ops[1].type != OP_REG32) { + expect("(first source operand) register"); + return; + } + if (tok == ']') { + next(); + closed_bracket = 1; + // exclam = 1; // implicit in hardware; don't do it in software + } + if (tok == ',') { + next(); // skip ',' + if (tok == '-') { + op2_minus = 1; + next(); + } + parse_operand(s1, &ops[2]); + if (ops[2].type == OP_REG32) { + if (ops[2].reg == 15) { + tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL)); + return; + } + } + } else { + // end of input expression in brackets--assume 0 offset + ops[2].type = OP_IM8; + ops[2].e.v = 0; + preincrement = 1; // add offset before transfer + } + if (!closed_bracket) { + if (tok != ']') + expect("']'"); + else + next(); // skip ']' + preincrement = 1; // add offset before transfer + if (tok == '!') { + exclam = 1; + next(); // skip '!' + } + } + + // TODO: Support options. + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_stcleq: + long_transfer = 1; + /* fallthrough */ + case TOK_ASM_stceq: + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0); + break; + case TOK_ASM_ldcleq: + long_transfer = 1; + /* fallthrough */ + case TOK_ASM_ldceq: + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1); + break; + default: + expect("coprocessor data transfer instruction"); + } +} + static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token) { Operand ops[3]; @@ -1589,6 +1758,13 @@ ST_FUNC void asm_opcode(TCCState *s1, int token) case TOK_ASM_mcreq: case TOK_ASM_mrceq: return asm_coprocessor_opcode(s1, token); + + case TOK_ASM_ldceq: + case TOK_ASM_ldcleq: + case TOK_ASM_stceq: + case TOK_ASM_stcleq: + return asm_coprocessor_data_transfer_opcode(s1, token); + default: expect("known instruction"); } diff --git a/arm-tok.h b/arm-tok.h index 7f15b736..66f7178d 100644 --- a/arm-tok.h +++ b/arm-tok.h @@ -155,6 +155,11 @@ DEF_ASM_CONDED(stmib) DEF_ASM_CONDED(ldmib) + DEF_ASM_CONDED(ldc) + DEF_ASM_CONDED(ldcl) + DEF_ASM_CONDED(stc) + DEF_ASM_CONDED(stcl) + /* instruction macros */ DEF_ASM_CONDED(push) diff --git a/tests/arm-asm-testsuite.sh b/tests/arm-asm-testsuite.sh index 902be620..9edd072a 100755 --- a/tests/arm-asm-testsuite.sh +++ b/tests/arm-asm-testsuite.sh @@ -97,6 +97,12 @@ do "p10, #7, r2, c0, c1, #4" \ "#4" \ "#-4" \ + "p5, c2, [r3]" \ + "p5, c3, [r4]" \ + "p5, c2, [r3, #4]" \ + "p5, c2, [r3, #-4]" \ + "p5, c2, [r3, #0x45]" \ + "p5, c2, [r3, #-0x45]" \ "" do #echo ".syntax unified" > a.s