| @@ -1116,6 +1116,142 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) · 1115 unmodified lines |
| 1116 | 1116 | } |
| 1117 | 1117 | } |
| 1118 | 1118 | |
| 1119 | +static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token) |
| 1120 | +{ |
| 1121 | + Operand ops[3]; |
| 1122 | + int exclam = 0; |
| 1123 | + int closed_bracket = 0; |
| 1124 | + int op2_minus = 0; |
| 1125 | + uint32_t opcode = (1 << 7) | (1 << 4); |
| 1126 | + |
| 1127 | + /* Note: |
| 1128 | + The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form. |
| 1129 | + The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller. |
| 1130 | + Also, the combination (P=0, W=1) is unpredictable here. |
| 1131 | + The immediate flag has moved to bit index 22--and its meaning has flipped. |
| 1132 | + The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0 |
| 1133 | + bit 26 (Load/Store instruction) is unset here. |
| 1134 | + bits 7 and 4 are set here. */ |
| 1135 | + |
| 1136 | + // Here: 0 0 0 P U I W L << 20 |
| 1137 | + // [compare single data transfer: 0 1 I P U B W L << 20] |
| 1138 | + |
| 1139 | + parse_operand(s1, &ops[0]); |
| 1140 | + if (ops[0].type == OP_REG32) |
| 1141 | + opcode |= ENCODE_RD(ops[0].reg); |
| 1142 | + else { |
| 1143 | + expect("(destination operand) register"); |
| 1144 | + return; |
| 1145 | + } |
| 1146 | + if (tok != ',') |
| 1147 | + expect("at least two arguments"); |
| 1148 | + else |
| 1149 | + next(); // skip ',' |
| 1150 | + |
| 1151 | + if (tok != '[') |
| 1152 | + expect("'['"); |
| 1153 | + else |
| 1154 | + next(); // skip '[' |
| 1155 | + |
| 1156 | + parse_operand(s1, &ops[1]); |
| 1157 | + if (ops[1].type == OP_REG32) |
| 1158 | + opcode |= ENCODE_RN(ops[1].reg); |
| 1159 | + else { |
| 1160 | + expect("(first source operand) register"); |
| 1161 | + return; |
| 1162 | + } |
| 1163 | + if (tok == ']') { |
| 1164 | + next(); |
| 1165 | + closed_bracket = 1; |
| 1166 | + // exclam = 1; // implicit in hardware; don't do it in software |
| 1167 | + } |
| 1168 | + if (tok == ',') { |
| 1169 | + next(); // skip ',' |
| 1170 | + if (tok == '-') { |
| 1171 | + op2_minus = 1; |
| 1172 | + next(); |
| 1173 | + } |
| 1174 | + parse_operand(s1, &ops[2]); |
| 1175 | + } else { |
| 1176 | + // end of input expression in brackets--assume 0 offset |
| 1177 | + ops[2].type = OP_IM8; |
| 1178 | + ops[2].e.v = 0; |
| 1179 | + opcode |= 1 << 24; // add offset before transfer |
| 1180 | + } |
| 1181 | + if (!closed_bracket) { |
| 1182 | + if (tok != ']') |
| 1183 | + expect("']'"); |
| 1184 | + else |
| 1185 | + next(); // skip ']' |
| 1186 | + opcode |= 1 << 24; // add offset before transfer |
| 1187 | + if (tok == '!') { |
| 1188 | + exclam = 1; |
| 1189 | + next(); // skip '!' |
| 1190 | + } |
| 1191 | + } |
| 1192 | + |
| 1193 | + if (exclam) { |
| 1194 | + if ((opcode & (1 << 24)) == 0) { |
| 1195 | + tcc_error("result of '%s' would be unpredictable here", get_tok_str(token, NULL)); |
| 1196 | + return; |
| 1197 | + } |
| 1198 | + opcode |= 1 << 21; // write offset back into register |
| 1199 | + } |
| 1200 | + |
| 1201 | + if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) { |
| 1202 | + int v = ops[2].e.v; |
| 1203 | + if (op2_minus) |
| 1204 | + tcc_error("minus before '#' not supported for immediate values"); |
| 1205 | + if (v >= 0) { |
| 1206 | + opcode |= 1 << 23; // up |
| 1207 | + if (v >= 0x100) |
| 1208 | + tcc_error("offset out of range for '%s'", get_tok_str(token, NULL)); |
| 1209 | + else { |
| 1210 | + // bits 11...8: immediate hi nibble |
| 1211 | + // bits 3...0: immediate lo nibble |
| 1212 | + opcode |= (v & 0xF0) << 4; |
| 1213 | + opcode |= v & 0xF; |
| 1214 | + } |
| 1215 | + } else { // down |
| 1216 | + if (v <= -0x100) |
| 1217 | + tcc_error("offset out of range for '%s'", get_tok_str(token, NULL)); |
| 1218 | + else { |
| 1219 | + v = -v; |
| 1220 | + // bits 11...8: immediate hi nibble |
| 1221 | + // bits 3...0: immediate lo nibble |
| 1222 | + opcode |= (v & 0xF0) << 4; |
| 1223 | + opcode |= v & 0xF; |
| 1224 | + } |
| 1225 | + } |
| 1226 | + opcode |= 1 << 22; // not ENCODE_IMMEDIATE_FLAG; |
| 1227 | + } else if (ops[2].type == OP_REG32) { |
| 1228 | + if (!op2_minus) |
| 1229 | + opcode |= 1 << 23; // up |
| 1230 | + opcode |= ops[2].reg; |
| 1231 | + } else |
| 1232 | + expect("register"); |
| 1233 | + |
| 1234 | + switch (ARM_INSTRUCTION_GROUP(token)) { |
| 1235 | + case TOK_ASM_ldrsheq: |
| 1236 | + opcode |= 1 << 5; // halfword, not byte |
| 1237 | + /* fallthrough */ |
| 1238 | + case TOK_ASM_ldrsbeq: |
| 1239 | + opcode |= 1 << 6; // sign extend |
| 1240 | + opcode |= 1 << 20; // L |
| 1241 | + asm_emit_opcode(token, opcode); |
| 1242 | + break; |
| 1243 | + case TOK_ASM_ldrheq: |
| 1244 | + opcode |= 1 << 5; // halfword, not byte |
| 1245 | + opcode |= 1 << 20; // L |
| 1246 | + asm_emit_opcode(token, opcode); |
| 1247 | + break; |
| 1248 | + case TOK_ASM_strheq: |
| 1249 | + opcode |= 1 << 5; // halfword, not byte |
| 1250 | + asm_emit_opcode(token, opcode); |
| 1251 | + break; |
| 1252 | + } |
| 1253 | +} |
| 1254 | + |
| 1119 | 1255 | /* Note: almost dupe of encbranch in arm-gen.c */ |
| 1120 | 1256 | static uint32_t encbranchoffset(int pos, int addr, int fail) |
| 1121 | 1257 | { |
| @@ -1232,6 +1368,12 @@ ST_FUNC void asm_opcode(TCCState *s1, int token) · 110 unmodified lines |
| 1232 | 1368 | case TOK_ASM_strexbeq: |
| 1233 | 1369 | return asm_single_data_transfer_opcode(s1, token); |
| 1234 | 1370 | |
| 1371 | + case TOK_ASM_ldrheq: |
| 1372 | + case TOK_ASM_ldrsheq: |
| 1373 | + case TOK_ASM_ldrsbeq: |
| 1374 | + case TOK_ASM_strheq: |
| 1375 | + return asm_misc_single_data_transfer_opcode(s1, token); |
| 1376 | + |
| 1235 | 1377 | case TOK_ASM_andeq: |
| 1236 | 1378 | case TOK_ASM_eoreq: |
| 1237 | 1379 | case TOK_ASM_subeq: |