| @@ -165,6 +165,10 @@ static void asm_emit_opcode(int token, uint32_t opcode) { · 164 unmodified lines |
| 165 | gen_le32((condition_code_of_token(token) << 28) | opcode); | 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 | 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) | 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 | uint32_t opcode = 0xe000000; | 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 | opcode |= cp_opcode2 << 5; | 186 | opcode |= cp_opcode2 << 5; |
| 183 | //assert(cp_m_operand_register < 16); | 187 | //assert(cp_m_operand_register < 16); |
| 184 | opcode |= cp_m_operand_register; | 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 | static void asm_nullary_opcode(int token) | 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 | static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token) | 1432 | static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token) |
| 1264 | { | 1433 | { |
| 1265 | Operand ops[3]; | 1434 | Operand ops[3]; |
| @@ -1589,6 +1758,13 @@ ST_FUNC void asm_opcode(TCCState *s1, int token) · 323 unmodified lines |
| 1589 | case TOK_ASM_mcreq: | 1758 | case TOK_ASM_mcreq: |
| 1590 | case TOK_ASM_mrceq: | 1759 | case TOK_ASM_mrceq: |
| 1591 | return asm_coprocessor_opcode(s1, token); | 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 | default: | 1768 | default: |
| 1593 | expect("known instruction"); | 1769 | expect("known instruction"); |
| 1594 | } | 1770 | } |