| @@ -165,6 +165,10 @@ static void asm_emit_opcode(int token, uint32_t opcode) { · 164 unmodified lines |
| 165 | 165 | gen_le32((condition_code_of_token(token) << 28) | opcode); |
| 166 | 166 | } |
| 167 | 167 | |
| 168 | +static void asm_emit_unconditional_opcode(uint32_t opcode) { |
| 169 | + gen_le32(opcode); |
| 170 | +} |
| 171 | + |
| 168 | 172 | 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) |
| 169 | 173 | { |
| 170 | 174 | uint32_t opcode = 0xe000000; |
| @@ -182,7 +186,7 @@ static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t cp_number, · 11 unmodified lines |
| 182 | 186 | opcode |= cp_opcode2 << 5; |
| 183 | 187 | //assert(cp_m_operand_register < 16); |
| 184 | 188 | opcode |= cp_m_operand_register; |
| 185 | | − gen_le32((high_nibble << 28) | opcode); |
| 189 | + asm_emit_unconditional_opcode((high_nibble << 28) | opcode); |
| 186 | 190 | } |
| 187 | 191 | |
| 188 | 192 | static void asm_nullary_opcode(int token) |
| @@ -1260,6 +1264,171 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) · 1071 unmodified lines |
| 1260 | 1264 | } |
| 1261 | 1265 | } |
| 1262 | 1266 | |
| 1267 | +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) { |
| 1268 | + uint32_t opcode = 0x0; |
| 1269 | + opcode |= 1 << 26; // Load/Store |
| 1270 | + opcode |= 1 << 27; // coprocessor |
| 1271 | + |
| 1272 | + if (long_transfer) |
| 1273 | + opcode |= 1 << 22; // long transfer |
| 1274 | + |
| 1275 | + if (load) |
| 1276 | + opcode |= 1 << 20; // L |
| 1277 | + |
| 1278 | + opcode |= cp_number << 8; |
| 1279 | + |
| 1280 | + opcode |= ENCODE_RD(CRd); |
| 1281 | + |
| 1282 | + if (Rn->type != OP_REG32) { |
| 1283 | + expect("register"); |
| 1284 | + return; |
| 1285 | + } |
| 1286 | + opcode |= ENCODE_RN(Rn->reg); |
| 1287 | + if (preincrement) |
| 1288 | + opcode |= 1 << 24; // add offset before transfer |
| 1289 | + |
| 1290 | + if (writeback) |
| 1291 | + opcode |= 1 << 21; // write offset back into register |
| 1292 | + |
| 1293 | + if (offset->type == OP_IM8 || offset->type == OP_IM8N || offset->type == OP_IM32) { |
| 1294 | + int v = offset->e.v; |
| 1295 | + if (offset_minus) |
| 1296 | + tcc_error("minus before '#' not supported for immediate values"); |
| 1297 | + if (offset->type == OP_IM8N || v < 0) |
| 1298 | + v = -v; |
| 1299 | + else |
| 1300 | + opcode |= 1 << 23; // up |
| 1301 | + if (v & 3) { |
| 1302 | + tcc_error("immediate offset must be a multiple of 4"); |
| 1303 | + return; |
| 1304 | + } |
| 1305 | + v >>= 2; |
| 1306 | + if (v > 255) { |
| 1307 | + tcc_error("immediate offset must be between -1020 and 1020"); |
| 1308 | + return; |
| 1309 | + } |
| 1310 | + opcode |= v; |
| 1311 | + } else if (offset->type == OP_REG32) { |
| 1312 | + if (!offset_minus) |
| 1313 | + opcode |= 1 << 23; // up |
| 1314 | + opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */ |
| 1315 | + opcode |= offset->reg; |
| 1316 | + tcc_error("Using register offset to register address is not possible here"); |
| 1317 | + return; |
| 1318 | + } else |
| 1319 | + expect("immediate or register"); |
| 1320 | + |
| 1321 | + asm_emit_unconditional_opcode((high_nibble << 28) | opcode); |
| 1322 | +} |
| 1323 | + |
| 1324 | +// Almost exactly the same as asm_single_data_transfer_opcode. |
| 1325 | +// Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again. |
| 1326 | +static void asm_coprocessor_data_transfer_opcode(TCCState *s1, int token) |
| 1327 | +{ |
| 1328 | + Operand ops[3]; |
| 1329 | + uint8_t coprocessor; |
| 1330 | + uint8_t coprocessor_destination_register; |
| 1331 | + int preincrement = 0; |
| 1332 | + int exclam = 0; |
| 1333 | + int closed_bracket = 0; |
| 1334 | + int op2_minus = 0; |
| 1335 | + int long_transfer = 0; |
| 1336 | + // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged |
| 1337 | + // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4 |
| 1338 | + // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4 |
| 1339 | + |
| 1340 | + if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) { |
| 1341 | + coprocessor = tok - TOK_ASM_p0; |
| 1342 | + next(); |
| 1343 | + } else { |
| 1344 | + expect("'c<number>'"); |
| 1345 | + return; |
| 1346 | + } |
| 1347 | + |
| 1348 | + if (tok == ',') |
| 1349 | + next(); |
| 1350 | + else |
| 1351 | + expect("','"); |
| 1352 | + |
| 1353 | + if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) { |
| 1354 | + coprocessor_destination_register = tok - TOK_ASM_c0; |
| 1355 | + next(); |
| 1356 | + } else { |
| 1357 | + expect("'c<number>'"); |
| 1358 | + return; |
| 1359 | + } |
| 1360 | + |
| 1361 | + if (tok == ',') |
| 1362 | + next(); |
| 1363 | + else |
| 1364 | + expect("','"); |
| 1365 | + |
| 1366 | + if (tok != '[') |
| 1367 | + expect("'['"); |
| 1368 | + else |
| 1369 | + next(); // skip '[' |
| 1370 | + |
| 1371 | + parse_operand(s1, &ops[1]); |
| 1372 | + if (ops[1].type != OP_REG32) { |
| 1373 | + expect("(first source operand) register"); |
| 1374 | + return; |
| 1375 | + } |
| 1376 | + if (tok == ']') { |
| 1377 | + next(); |
| 1378 | + closed_bracket = 1; |
| 1379 | + // exclam = 1; // implicit in hardware; don't do it in software |
| 1380 | + } |
| 1381 | + if (tok == ',') { |
| 1382 | + next(); // skip ',' |
| 1383 | + if (tok == '-') { |
| 1384 | + op2_minus = 1; |
| 1385 | + next(); |
| 1386 | + } |
| 1387 | + parse_operand(s1, &ops[2]); |
| 1388 | + if (ops[2].type == OP_REG32) { |
| 1389 | + if (ops[2].reg == 15) { |
| 1390 | + tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL)); |
| 1391 | + return; |
| 1392 | + } |
| 1393 | + } |
| 1394 | + } else { |
| 1395 | + // end of input expression in brackets--assume 0 offset |
| 1396 | + ops[2].type = OP_IM8; |
| 1397 | + ops[2].e.v = 0; |
| 1398 | + preincrement = 1; // add offset before transfer |
| 1399 | + } |
| 1400 | + if (!closed_bracket) { |
| 1401 | + if (tok != ']') |
| 1402 | + expect("']'"); |
| 1403 | + else |
| 1404 | + next(); // skip ']' |
| 1405 | + preincrement = 1; // add offset before transfer |
| 1406 | + if (tok == '!') { |
| 1407 | + exclam = 1; |
| 1408 | + next(); // skip '!' |
| 1409 | + } |
| 1410 | + } |
| 1411 | + |
| 1412 | + // TODO: Support options. |
| 1413 | + |
| 1414 | + switch (ARM_INSTRUCTION_GROUP(token)) { |
| 1415 | + case TOK_ASM_stcleq: |
| 1416 | + long_transfer = 1; |
| 1417 | + /* fallthrough */ |
| 1418 | + case TOK_ASM_stceq: |
| 1419 | + 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); |
| 1420 | + break; |
| 1421 | + case TOK_ASM_ldcleq: |
| 1422 | + long_transfer = 1; |
| 1423 | + /* fallthrough */ |
| 1424 | + case TOK_ASM_ldceq: |
| 1425 | + 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); |
| 1426 | + break; |
| 1427 | + default: |
| 1428 | + expect("coprocessor data transfer instruction"); |
| 1429 | + } |
| 1430 | +} |
| 1431 | + |
| 1263 | 1432 | static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token) |
| 1264 | 1433 | { |
| 1265 | 1434 | Operand ops[3]; |
| @@ -1589,6 +1758,13 @@ ST_FUNC void asm_opcode(TCCState *s1, int token) · 323 unmodified lines |
| 1589 | 1758 | case TOK_ASM_mcreq: |
| 1590 | 1759 | case TOK_ASM_mrceq: |
| 1591 | 1760 | return asm_coprocessor_opcode(s1, token); |
| 1761 | + |
| 1762 | + case TOK_ASM_ldceq: |
| 1763 | + case TOK_ASM_ldcleq: |
| 1764 | + case TOK_ASM_stceq: |
| 1765 | + case TOK_ASM_stcleq: |
| 1766 | + return asm_coprocessor_data_transfer_opcode(s1, token); |
| 1767 | + |
| 1592 | 1768 | default: |
| 1593 | 1769 | expect("known instruction"); |
| 1594 | 1770 | } |