| ... | ... | @@ -536,59 +536,62 @@ pub const Instruction = struct { |
| 536 | 536 | } |
| 537 | 537 | try encoder.disp32(sib.disp); |
| 538 | 538 | }, |
| 539 | | .reg => |base| if (base.class() == .segment) { |
| 540 | | // TODO audit this wrt SIB |
| 541 | | try encoder.modRm_SIBDisp0(operand_enc); |
| 542 | | if (mem.scaleIndex()) |si| { |
| 543 | | const scale = math.log2_int(u4, si.scale); |
| 544 | | try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc()); |
| 545 | | } else { |
| 546 | | try encoder.sib_disp32(); |
| 547 | | } |
| 548 | | try encoder.disp32(sib.disp); |
| 549 | | } else { |
| 550 | | assert(base.class() == .general_purpose); |
| 551 | | const dst = base.lowEnc(); |
| 552 | | const src = operand_enc; |
| 553 | | if (dst == 4 or mem.scaleIndex() != null) { |
| 554 | | if (sib.disp == 0 and dst != 5) { |
| 555 | | try encoder.modRm_SIBDisp0(src); |
| 556 | | if (mem.scaleIndex()) |si| { |
| 557 | | const scale = math.log2_int(u4, si.scale); |
| 558 | | try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst); |
| 559 | | } else { |
| 560 | | try encoder.sib_base(dst); |
| 561 | | } |
| 562 | | } else if (math.cast(i8, sib.disp)) |_| { |
| 563 | | try encoder.modRm_SIBDisp8(src); |
| 564 | | if (mem.scaleIndex()) |si| { |
| 565 | | const scale = math.log2_int(u4, si.scale); |
| 566 | | try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst); |
| 539 | .reg => |base| switch (base.class()) { |
| 540 | .segment => { |
| 541 | // TODO audit this wrt SIB |
| 542 | try encoder.modRm_SIBDisp0(operand_enc); |
| 543 | if (mem.scaleIndex()) |si| { |
| 544 | const scale = math.log2_int(u4, si.scale); |
| 545 | try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc()); |
| 546 | } else { |
| 547 | try encoder.sib_disp32(); |
| 548 | } |
| 549 | try encoder.disp32(sib.disp); |
| 550 | }, |
| 551 | .general_purpose => { |
| 552 | const dst = base.lowEnc(); |
| 553 | const src = operand_enc; |
| 554 | if (dst == 4 or mem.scaleIndex() != null) { |
| 555 | if (sib.disp == 0 and dst != 5) { |
| 556 | try encoder.modRm_SIBDisp0(src); |
| 557 | if (mem.scaleIndex()) |si| { |
| 558 | const scale = math.log2_int(u4, si.scale); |
| 559 | try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst); |
| 560 | } else { |
| 561 | try encoder.sib_base(dst); |
| 562 | } |
| 563 | } else if (math.cast(i8, sib.disp)) |_| { |
| 564 | try encoder.modRm_SIBDisp8(src); |
| 565 | if (mem.scaleIndex()) |si| { |
| 566 | const scale = math.log2_int(u4, si.scale); |
| 567 | try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst); |
| 568 | } else { |
| 569 | try encoder.sib_baseDisp8(dst); |
| 570 | } |
| 571 | try encoder.disp8(@as(i8, @truncate(sib.disp))); |
| 567 | 572 | } else { |
| 568 | | try encoder.sib_baseDisp8(dst); |
| 573 | try encoder.modRm_SIBDisp32(src); |
| 574 | if (mem.scaleIndex()) |si| { |
| 575 | const scale = math.log2_int(u4, si.scale); |
| 576 | try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst); |
| 577 | } else { |
| 578 | try encoder.sib_baseDisp32(dst); |
| 579 | } |
| 580 | try encoder.disp32(sib.disp); |
| 569 | 581 | } |
| 570 | | try encoder.disp8(@as(i8, @truncate(sib.disp))); |
| 571 | 582 | } else { |
| 572 | | try encoder.modRm_SIBDisp32(src); |
| 573 | | if (mem.scaleIndex()) |si| { |
| 574 | | const scale = math.log2_int(u4, si.scale); |
| 575 | | try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst); |
| 583 | if (sib.disp == 0 and dst != 5) { |
| 584 | try encoder.modRm_indirectDisp0(src, dst); |
| 585 | } else if (math.cast(i8, sib.disp)) |_| { |
| 586 | try encoder.modRm_indirectDisp8(src, dst); |
| 587 | try encoder.disp8(@as(i8, @truncate(sib.disp))); |
| 576 | 588 | } else { |
| 577 | | try encoder.sib_baseDisp32(dst); |
| 589 | try encoder.modRm_indirectDisp32(src, dst); |
| 590 | try encoder.disp32(sib.disp); |
| 578 | 591 | } |
| 579 | | try encoder.disp32(sib.disp); |
| 580 | | } |
| 581 | | } else { |
| 582 | | if (sib.disp == 0 and dst != 5) { |
| 583 | | try encoder.modRm_indirectDisp0(src, dst); |
| 584 | | } else if (math.cast(i8, sib.disp)) |_| { |
| 585 | | try encoder.modRm_indirectDisp8(src, dst); |
| 586 | | try encoder.disp8(@as(i8, @truncate(sib.disp))); |
| 587 | | } else { |
| 588 | | try encoder.modRm_indirectDisp32(src, dst); |
| 589 | | try encoder.disp32(sib.disp); |
| 590 | 592 | } |
| 591 | | } |
| 593 | }, |
| 594 | else => unreachable, |
| 592 | 595 | }, |
| 593 | 596 | .frame => if (@TypeOf(encoder).options.allow_frame_locs) { |
| 594 | 597 | try encoder.modRm_indirectDisp32(operand_enc, undefined); |
| ... | ... | @@ -1101,7 +1104,7 @@ const TestEncode = struct { |
| 1101 | 1104 | var stream = std.io.fixedBufferStream(&enc.buffer); |
| 1102 | 1105 | var count_writer = std.io.countingWriter(stream.writer()); |
| 1103 | 1106 | const inst = try Instruction.new(.none, mnemonic, ops); |
| 1104 | | try inst.encode(count_writer.writer()); |
| 1107 | try inst.encode(count_writer.writer(), .{}); |
| 1105 | 1108 | enc.index = count_writer.bytes_written; |
| 1106 | 1109 | } |
| 1107 | 1110 | |
| ... | ... | @@ -1118,7 +1121,7 @@ test "encode" { |
| 1118 | 1121 | .{ .reg = .rbx }, |
| 1119 | 1122 | .{ .imm = Immediate.u(4) }, |
| 1120 | 1123 | }); |
| 1121 | | try inst.encode(buf.writer()); |
| 1124 | try inst.encode(buf.writer(), .{}); |
| 1122 | 1125 | try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items); |
| 1123 | 1126 | } |
| 1124 | 1127 | |
| ... | ... | @@ -1181,7 +1184,7 @@ test "lower MI encoding" { |
| 1181 | 1184 | try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); |
| 1182 | 1185 | |
| 1183 | 1186 | try enc.encode(.mov, &.{ |
| 1184 | | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) }, |
| 1187 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, |
| 1185 | 1188 | .{ .imm = Immediate.u(0x10) }, |
| 1186 | 1189 | }); |
| 1187 | 1190 | try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); |
| ... | ... | @@ -1205,7 +1208,7 @@ test "lower MI encoding" { |
| 1205 | 1208 | try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); |
| 1206 | 1209 | |
| 1207 | 1210 | try enc.encode(.mov, &.{ |
| 1208 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11 }) }, |
| 1211 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) }, |
| 1209 | 1212 | .{ .imm = Immediate.u(0x10) }, |
| 1210 | 1213 | }); |
| 1211 | 1214 | try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); |
| ... | ... | @@ -1221,26 +1224,26 @@ test "lower MI encoding" { |
| 1221 | 1224 | ); |
| 1222 | 1225 | |
| 1223 | 1226 | try enc.encode(.mov, &.{ |
| 1224 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -8 }) }, |
| 1227 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) }, |
| 1225 | 1228 | .{ .imm = Immediate.u(0x10) }, |
| 1226 | 1229 | }); |
| 1227 | 1230 | try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); |
| 1228 | 1231 | |
| 1229 | 1232 | try enc.encode(.mov, &.{ |
| 1230 | | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -2 }) }, |
| 1233 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) }, |
| 1231 | 1234 | .{ .imm = Immediate.s(-16) }, |
| 1232 | 1235 | }); |
| 1233 | 1236 | try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); |
| 1234 | 1237 | |
| 1235 | 1238 | try enc.encode(.mov, &.{ |
| 1236 | | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -1 }) }, |
| 1239 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) }, |
| 1237 | 1240 | .{ .imm = Immediate.u(0x10) }, |
| 1238 | 1241 | }); |
| 1239 | 1242 | try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); |
| 1240 | 1243 | |
| 1241 | 1244 | try enc.encode(.mov, &.{ |
| 1242 | 1245 | .{ .mem = Instruction.Memory.sib(.qword, .{ |
| 1243 | | .base = .ds, |
| 1246 | .base = .{ .reg = .ds }, |
| 1244 | 1247 | .disp = 0x10000000, |
| 1245 | 1248 | .scale_index = .{ .scale = 2, .index = .rcx }, |
| 1246 | 1249 | }) }, |
| ... | ... | @@ -1253,7 +1256,7 @@ test "lower MI encoding" { |
| 1253 | 1256 | ); |
| 1254 | 1257 | |
| 1255 | 1258 | try enc.encode(.adc, &.{ |
| 1256 | | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -0x10 }) }, |
| 1259 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, |
| 1257 | 1260 | .{ .imm = Immediate.u(0x10) }, |
| 1258 | 1261 | }); |
| 1259 | 1262 | try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); |
| ... | ... | @@ -1271,7 +1274,7 @@ test "lower MI encoding" { |
| 1271 | 1274 | try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); |
| 1272 | 1275 | |
| 1273 | 1276 | try enc.encode(.add, &.{ |
| 1274 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .rdx, .disp = -8 }) }, |
| 1277 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) }, |
| 1275 | 1278 | .{ .imm = Immediate.u(0x10) }, |
| 1276 | 1279 | }); |
| 1277 | 1280 | try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); |
| ... | ... | @@ -1283,13 +1286,13 @@ test "lower MI encoding" { |
| 1283 | 1286 | try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); |
| 1284 | 1287 | |
| 1285 | 1288 | try enc.encode(.add, &.{ |
| 1286 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -0x10 }) }, |
| 1289 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, |
| 1287 | 1290 | .{ .imm = Immediate.s(-0x10) }, |
| 1288 | 1291 | }); |
| 1289 | 1292 | try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); |
| 1290 | 1293 | |
| 1291 | 1294 | try enc.encode(.@"and", &.{ |
| 1292 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) }, |
| 1295 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, |
| 1293 | 1296 | .{ .imm = Immediate.u(0x10) }, |
| 1294 | 1297 | }); |
| 1295 | 1298 | try expectEqualHexStrings( |
| ... | ... | @@ -1299,7 +1302,7 @@ test "lower MI encoding" { |
| 1299 | 1302 | ); |
| 1300 | 1303 | |
| 1301 | 1304 | try enc.encode(.@"and", &.{ |
| 1302 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) }, |
| 1305 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) }, |
| 1303 | 1306 | .{ .imm = Immediate.u(0x10) }, |
| 1304 | 1307 | }); |
| 1305 | 1308 | try expectEqualHexStrings( |
| ... | ... | @@ -1309,7 +1312,7 @@ test "lower MI encoding" { |
| 1309 | 1312 | ); |
| 1310 | 1313 | |
| 1311 | 1314 | try enc.encode(.@"and", &.{ |
| 1312 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) }, |
| 1315 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, |
| 1313 | 1316 | .{ .imm = Immediate.u(0x10) }, |
| 1314 | 1317 | }); |
| 1315 | 1318 | try expectEqualHexStrings( |
| ... | ... | @@ -1319,7 +1322,7 @@ test "lower MI encoding" { |
| 1319 | 1322 | ); |
| 1320 | 1323 | |
| 1321 | 1324 | try enc.encode(.sub, &.{ |
| 1322 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) }, |
| 1325 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, |
| 1323 | 1326 | .{ .imm = Immediate.u(0x10) }, |
| 1324 | 1327 | }); |
| 1325 | 1328 | try expectEqualHexStrings( |
| ... | ... | @@ -1334,26 +1337,26 @@ test "lower RM encoding" { |
| 1334 | 1337 | |
| 1335 | 1338 | try enc.encode(.mov, &.{ |
| 1336 | 1339 | .{ .reg = .rax }, |
| 1337 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11 }) }, |
| 1340 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 } }) }, |
| 1338 | 1341 | }); |
| 1339 | 1342 | try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]"); |
| 1340 | 1343 | |
| 1341 | 1344 | try enc.encode(.mov, &.{ |
| 1342 | 1345 | .{ .reg = .rbx }, |
| 1343 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10 }) }, |
| 1346 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) }, |
| 1344 | 1347 | }); |
| 1345 | 1348 | try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10"); |
| 1346 | 1349 | |
| 1347 | 1350 | try enc.encode(.mov, &.{ |
| 1348 | 1351 | .{ .reg = .rax }, |
| 1349 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) }, |
| 1352 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) }, |
| 1350 | 1353 | }); |
| 1351 | 1354 | try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]"); |
| 1352 | 1355 | |
| 1353 | 1356 | try enc.encode(.mov, &.{ |
| 1354 | 1357 | .{ .reg = .rax }, |
| 1355 | 1358 | .{ .mem = Instruction.Memory.sib(.qword, .{ |
| 1356 | | .base = .rbp, |
| 1359 | .base = .{ .reg = .rbp }, |
| 1357 | 1360 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1358 | 1361 | .disp = -8, |
| 1359 | 1362 | }) }, |
| ... | ... | @@ -1363,7 +1366,7 @@ test "lower RM encoding" { |
| 1363 | 1366 | try enc.encode(.mov, &.{ |
| 1364 | 1367 | .{ .reg = .eax }, |
| 1365 | 1368 | .{ .mem = Instruction.Memory.sib(.dword, .{ |
| 1366 | | .base = .rbp, |
| 1369 | .base = .{ .reg = .rbp }, |
| 1367 | 1370 | .scale_index = .{ .scale = 4, .index = .rdx }, |
| 1368 | 1371 | .disp = -4, |
| 1369 | 1372 | }) }, |
| ... | ... | @@ -1373,7 +1376,7 @@ test "lower RM encoding" { |
| 1373 | 1376 | try enc.encode(.mov, &.{ |
| 1374 | 1377 | .{ .reg = .rax }, |
| 1375 | 1378 | .{ .mem = Instruction.Memory.sib(.qword, .{ |
| 1376 | | .base = .rbp, |
| 1379 | .base = .{ .reg = .rbp }, |
| 1377 | 1380 | .scale_index = .{ .scale = 8, .index = .rcx }, |
| 1378 | 1381 | .disp = -8, |
| 1379 | 1382 | }) }, |
| ... | ... | @@ -1383,7 +1386,7 @@ test "lower RM encoding" { |
| 1383 | 1386 | try enc.encode(.mov, &.{ |
| 1384 | 1387 | .{ .reg = .r8b }, |
| 1385 | 1388 | .{ .mem = Instruction.Memory.sib(.byte, .{ |
| 1386 | | .base = .rsi, |
| 1389 | .base = .{ .reg = .rsi }, |
| 1387 | 1390 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1388 | 1391 | .disp = -24, |
| 1389 | 1392 | }) }, |
| ... | ... | @@ -1398,10 +1401,10 @@ test "lower RM encoding" { |
| 1398 | 1401 | try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); |
| 1399 | 1402 | |
| 1400 | 1403 | try enc.encode(.mov, &.{ |
| 1401 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) }, |
| 1404 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, |
| 1402 | 1405 | .{ .reg = .fs }, |
| 1403 | 1406 | }); |
| 1404 | | try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs"); |
| 1407 | try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); |
| 1405 | 1408 | |
| 1406 | 1409 | try enc.encode(.mov, &.{ |
| 1407 | 1410 | .{ .reg = .r12w }, |
| ... | ... | @@ -1409,12 +1412,6 @@ test "lower RM encoding" { |
| 1409 | 1412 | }); |
| 1410 | 1413 | try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs"); |
| 1411 | 1414 | |
| 1412 | | try enc.encode(.mov, &.{ |
| 1413 | | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, |
| 1414 | | .{ .reg = .fs }, |
| 1415 | | }); |
| 1416 | | try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); |
| 1417 | | |
| 1418 | 1415 | try enc.encode(.movsx, &.{ |
| 1419 | 1416 | .{ .reg = .eax }, |
| 1420 | 1417 | .{ .reg = .bx }, |
| ... | ... | @@ -1435,7 +1432,7 @@ test "lower RM encoding" { |
| 1435 | 1432 | |
| 1436 | 1433 | try enc.encode(.movsx, &.{ |
| 1437 | 1434 | .{ .reg = .eax }, |
| 1438 | | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) }, |
| 1435 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) }, |
| 1439 | 1436 | }); |
| 1440 | 1437 | try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]"); |
| 1441 | 1438 | |
| ... | ... | @@ -1496,7 +1493,7 @@ test "lower RM encoding" { |
| 1496 | 1493 | try enc.encode(.lea, &.{ |
| 1497 | 1494 | .{ .reg = .rsi }, |
| 1498 | 1495 | .{ .mem = Instruction.Memory.sib(.qword, .{ |
| 1499 | | .base = .rbp, |
| 1496 | .base = .{ .reg = .rbp }, |
| 1500 | 1497 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1501 | 1498 | }) }, |
| 1502 | 1499 | }); |
| ... | ... | @@ -1504,31 +1501,31 @@ test "lower RM encoding" { |
| 1504 | 1501 | |
| 1505 | 1502 | try enc.encode(.add, &.{ |
| 1506 | 1503 | .{ .reg = .r11 }, |
| 1507 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10000000 }) }, |
| 1504 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, |
| 1508 | 1505 | }); |
| 1509 | 1506 | try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000"); |
| 1510 | 1507 | |
| 1511 | 1508 | try enc.encode(.add, &.{ |
| 1512 | 1509 | .{ .reg = .r12b }, |
| 1513 | | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) }, |
| 1510 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, |
| 1514 | 1511 | }); |
| 1515 | 1512 | try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000"); |
| 1516 | 1513 | |
| 1517 | 1514 | try enc.encode(.add, &.{ |
| 1518 | 1515 | .{ .reg = .r12b }, |
| 1519 | | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .fs, .disp = 0x10000000 }) }, |
| 1516 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) }, |
| 1520 | 1517 | }); |
| 1521 | 1518 | try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000"); |
| 1522 | 1519 | |
| 1523 | 1520 | try enc.encode(.sub, &.{ |
| 1524 | 1521 | .{ .reg = .r11 }, |
| 1525 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r13, .disp = 0x10000000 }) }, |
| 1522 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) }, |
| 1526 | 1523 | }); |
| 1527 | 1524 | try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]"); |
| 1528 | 1525 | |
| 1529 | 1526 | try enc.encode(.sub, &.{ |
| 1530 | 1527 | .{ .reg = .r11 }, |
| 1531 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12, .disp = 0x10000000 }) }, |
| 1528 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, |
| 1532 | 1529 | }); |
| 1533 | 1530 | try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]"); |
| 1534 | 1531 | |
| ... | ... | @@ -1562,7 +1559,7 @@ test "lower RMI encoding" { |
| 1562 | 1559 | |
| 1563 | 1560 | try enc.encode(.imul, &.{ |
| 1564 | 1561 | .{ .reg = .bx }, |
| 1565 | | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, |
| 1562 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, |
| 1566 | 1563 | .{ .imm = Immediate.s(-1024) }, |
| 1567 | 1564 | }); |
| 1568 | 1565 | try expectEqualHexStrings( |
| ... | ... | @@ -1573,7 +1570,7 @@ test "lower RMI encoding" { |
| 1573 | 1570 | |
| 1574 | 1571 | try enc.encode(.imul, &.{ |
| 1575 | 1572 | .{ .reg = .bx }, |
| 1576 | | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, |
| 1573 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, |
| 1577 | 1574 | .{ .imm = Immediate.u(1024) }, |
| 1578 | 1575 | }); |
| 1579 | 1576 | try expectEqualHexStrings( |
| ... | ... | @@ -1593,7 +1590,7 @@ test "lower MR encoding" { |
| 1593 | 1590 | try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); |
| 1594 | 1591 | |
| 1595 | 1592 | try enc.encode(.mov, &.{ |
| 1596 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) }, |
| 1593 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) }, |
| 1597 | 1594 | .{ .reg = .r11 }, |
| 1598 | 1595 | }); |
| 1599 | 1596 | try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); |
| ... | ... | @@ -1606,7 +1603,7 @@ test "lower MR encoding" { |
| 1606 | 1603 | |
| 1607 | 1604 | try enc.encode(.mov, &.{ |
| 1608 | 1605 | .{ .mem = Instruction.Memory.sib(.qword, .{ |
| 1609 | | .base = .r11, |
| 1606 | .base = .{ .reg = .r11 }, |
| 1610 | 1607 | .scale_index = .{ .scale = 2, .index = .r12 }, |
| 1611 | 1608 | .disp = 0x10, |
| 1612 | 1609 | }) }, |
| ... | ... | @@ -1622,7 +1619,7 @@ test "lower MR encoding" { |
| 1622 | 1619 | |
| 1623 | 1620 | try enc.encode(.mov, &.{ |
| 1624 | 1621 | .{ .mem = Instruction.Memory.sib(.byte, .{ |
| 1625 | | .base = .r11, |
| 1622 | .base = .{ .reg = .r11 }, |
| 1626 | 1623 | .scale_index = .{ .scale = 2, .index = .r12 }, |
| 1627 | 1624 | .disp = 0x10, |
| 1628 | 1625 | }) }, |
| ... | ... | @@ -1631,25 +1628,25 @@ test "lower MR encoding" { |
| 1631 | 1628 | try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b"); |
| 1632 | 1629 | |
| 1633 | 1630 | try enc.encode(.add, &.{ |
| 1634 | | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) }, |
| 1631 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, |
| 1635 | 1632 | .{ .reg = .r12b }, |
| 1636 | 1633 | }); |
| 1637 | 1634 | try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); |
| 1638 | 1635 | |
| 1639 | 1636 | try enc.encode(.add, &.{ |
| 1640 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) }, |
| 1637 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, |
| 1641 | 1638 | .{ .reg = .r12d }, |
| 1642 | 1639 | }); |
| 1643 | 1640 | try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); |
| 1644 | 1641 | |
| 1645 | 1642 | try enc.encode(.add, &.{ |
| 1646 | | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) }, |
| 1643 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) }, |
| 1647 | 1644 | .{ .reg = .r12d }, |
| 1648 | 1645 | }); |
| 1649 | 1646 | try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); |
| 1650 | 1647 | |
| 1651 | 1648 | try enc.encode(.sub, &.{ |
| 1652 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) }, |
| 1649 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, |
| 1653 | 1650 | .{ .reg = .r12 }, |
| 1654 | 1651 | }); |
| 1655 | 1652 | try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12"); |
| ... | ... | @@ -1664,13 +1661,13 @@ test "lower M encoding" { |
| 1664 | 1661 | try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12"); |
| 1665 | 1662 | |
| 1666 | 1663 | try enc.encode(.call, &.{ |
| 1667 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12 }) }, |
| 1664 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 } }) }, |
| 1668 | 1665 | }); |
| 1669 | 1666 | try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]"); |
| 1670 | 1667 | |
| 1671 | 1668 | try enc.encode(.call, &.{ |
| 1672 | 1669 | .{ .mem = Instruction.Memory.sib(.qword, .{ |
| 1673 | | .base = null, |
| 1670 | .base = .none, |
| 1674 | 1671 | .scale_index = .{ .index = .r11, .scale = 2 }, |
| 1675 | 1672 | }) }, |
| 1676 | 1673 | }); |
| ... | ... | @@ -1678,14 +1675,14 @@ test "lower M encoding" { |
| 1678 | 1675 | |
| 1679 | 1676 | try enc.encode(.call, &.{ |
| 1680 | 1677 | .{ .mem = Instruction.Memory.sib(.qword, .{ |
| 1681 | | .base = null, |
| 1678 | .base = .none, |
| 1682 | 1679 | .scale_index = .{ .index = .r12, .scale = 2 }, |
| 1683 | 1680 | }) }, |
| 1684 | 1681 | }); |
| 1685 | 1682 | try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]"); |
| 1686 | 1683 | |
| 1687 | 1684 | try enc.encode(.call, &.{ |
| 1688 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .gs }) }, |
| 1685 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .gs } }) }, |
| 1689 | 1686 | }); |
| 1690 | 1687 | try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); |
| 1691 | 1688 | |
| ... | ... | @@ -1695,12 +1692,12 @@ test "lower M encoding" { |
| 1695 | 1692 | try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); |
| 1696 | 1693 | |
| 1697 | 1694 | try enc.encode(.push, &.{ |
| 1698 | | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp }) }, |
| 1695 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp } }) }, |
| 1699 | 1696 | }); |
| 1700 | 1697 | try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); |
| 1701 | 1698 | |
| 1702 | 1699 | try enc.encode(.push, &.{ |
| 1703 | | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) }, |
| 1700 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) }, |
| 1704 | 1701 | }); |
| 1705 | 1702 | try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); |
| 1706 | 1703 | |
| ... | ... | @@ -1913,7 +1910,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand |
| 1913 | 1910 | |
| 1914 | 1911 | test "cannot encode" { |
| 1915 | 1912 | try cannotEncode(.@"test", &.{ |
| 1916 | | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) }, |
| 1913 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, |
| 1917 | 1914 | .{ .reg = .ah }, |
| 1918 | 1915 | }); |
| 1919 | 1916 | try cannotEncode(.@"test", &.{ |
| ... | ... | @@ -2093,7 +2090,7 @@ const Assembler = struct { |
| 2093 | 2090 | pub fn assemble(as: *Assembler, writer: anytype) !void { |
| 2094 | 2091 | while (try as.next()) |parsed_inst| { |
| 2095 | 2092 | const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops); |
| 2096 | | try inst.encode(writer); |
| 2093 | try inst.encode(writer, .{}); |
| 2097 | 2094 | } |
| 2098 | 2095 | } |
| 2099 | 2096 | |
| ... | ... | @@ -2262,26 +2259,26 @@ const Assembler = struct { |
| 2262 | 2259 | |
| 2263 | 2260 | // Supported rules and orderings. |
| 2264 | 2261 | const rules = .{ |
| 2265 | | .{ .open_br, .base, .close_br }, // [ base ] |
| 2266 | | .{ .open_br, .base, .plus, .disp, .close_br }, // [ base + disp ] |
| 2267 | | .{ .open_br, .base, .minus, .disp, .close_br }, // [ base - disp ] |
| 2268 | | .{ .open_br, .disp, .plus, .base, .close_br }, // [ disp + base ] |
| 2269 | | .{ .open_br, .base, .plus, .index, .close_br }, // [ base + index ] |
| 2270 | | .{ .open_br, .base, .plus, .index, .star, .scale, .close_br }, // [ base + index * scale ] |
| 2271 | | .{ .open_br, .index, .star, .scale, .plus, .base, .close_br }, // [ index * scale + base ] |
| 2272 | | .{ .open_br, .base, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ base + index * scale + disp ] |
| 2273 | | .{ .open_br, .base, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ base + index * scale - disp ] |
| 2274 | | .{ .open_br, .index, .star, .scale, .plus, .base, .plus, .disp, .close_br }, // [ index * scale + base + disp ] |
| 2275 | | .{ .open_br, .index, .star, .scale, .plus, .base, .minus, .disp, .close_br }, // [ index * scale + base - disp ] |
| 2276 | | .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .base, .close_br }, // [ disp + index * scale + base ] |
| 2277 | | .{ .open_br, .disp, .plus, .base, .plus, .index, .star, .scale, .close_br }, // [ disp + base + index * scale ] |
| 2278 | | .{ .open_br, .base, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base + disp + index * scale ] |
| 2279 | | .{ .open_br, .base, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base - disp + index * scale ] |
| 2280 | | .{ .open_br, .base, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base + disp + scale * index ] |
| 2281 | | .{ .open_br, .base, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base - disp + scale * index ] |
| 2262 | .{ .open_br, .general_purpose, .close_br }, // [ general_purpose ] |
| 2263 | .{ .open_br, .general_purpose, .plus, .disp, .close_br }, // [ general_purpose + disp ] |
| 2264 | .{ .open_br, .general_purpose, .minus, .disp, .close_br }, // [ general_purpose - disp ] |
| 2265 | .{ .open_br, .disp, .plus, .general_purpose, .close_br }, // [ disp + general_purpose ] |
| 2266 | .{ .open_br, .general_purpose, .plus, .index, .close_br }, // [ general_purpose + index ] |
| 2267 | .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + index * scale ] |
| 2268 | .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ index * scale + general_purpose ] |
| 2269 | .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ general_purpose + index * scale + disp ] |
| 2270 | .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ general_purpose + index * scale - disp ] |
| 2271 | .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .plus, .disp, .close_br }, // [ index * scale + general_purpose + disp ] |
| 2272 | .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .minus, .disp, .close_br }, // [ index * scale + general_purpose - disp ] |
| 2273 | .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ disp + index * scale + general_purpose ] |
| 2274 | .{ .open_br, .disp, .plus, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ disp + general_purpose + index * scale ] |
| 2275 | .{ .open_br, .general_purpose, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + disp + index * scale ] |
| 2276 | .{ .open_br, .general_purpose, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose - disp + index * scale ] |
| 2277 | .{ .open_br, .general_purpose, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose + disp + scale * index ] |
| 2278 | .{ .open_br, .general_purpose, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose - disp + scale * index ] |
| 2282 | 2279 | .{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ] |
| 2283 | 2280 | .{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ] |
| 2284 | | .{ .base, .colon, .disp }, // seg:disp |
| 2281 | .{ .segment, .colon, .disp }, // seg:disp |
| 2285 | 2282 | }; |
| 2286 | 2283 | |
| 2287 | 2284 | const pos = as.it.pos; |
| ... | ... | @@ -2301,7 +2298,7 @@ const Assembler = struct { |
| 2301 | 2298 | return Instruction.Memory.moffs(base, offset); |
| 2302 | 2299 | } |
| 2303 | 2300 | return Instruction.Memory.sib(ptr_size orelse .qword, .{ |
| 2304 | | .base = base, |
| 2301 | .base = .{ .reg = base }, |
| 2305 | 2302 | .scale_index = res.scale_index, |
| 2306 | 2303 | .disp = res.disp orelse 0, |
| 2307 | 2304 | }); |
| ... | ... | @@ -2323,7 +2320,7 @@ const Assembler = struct { |
| 2323 | 2320 | offset: ?u64 = null, |
| 2324 | 2321 | }; |
| 2325 | 2322 | |
| 2326 | | fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!Instruction.MemoryParseResult { |
| 2323 | fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult { |
| 2327 | 2324 | var res: MemoryParseResult = .{}; |
| 2328 | 2325 | inline for (rule, 0..) |cond, i| { |
| 2329 | 2326 | if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) { |
| ... | ... | @@ -2333,9 +2330,11 @@ const Assembler = struct { |
| 2333 | 2330 | .open_br, .close_br, .plus, .minus, .star, .colon => { |
| 2334 | 2331 | _ = try as.expect(cond); |
| 2335 | 2332 | }, |
| 2336 | | .base => { |
| 2333 | .general_purpose, .segment => { |
| 2337 | 2334 | const tok = try as.expect(.string); |
| 2338 | | res.base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand; |
| 2335 | const base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand; |
| 2336 | if (base.class() != cond) return error.InvalidMemoryOperand; |
| 2337 | res.base = base; |
| 2339 | 2338 | }, |
| 2340 | 2339 | .rip => { |
| 2341 | 2340 | const tok = try as.expect(.string); |
| ... | ... | @@ -2529,8 +2528,8 @@ test "assemble" { |
| 2529 | 2528 | 0xF3, 0x45, 0x0F, 0x10, 0xF9, |
| 2530 | 2529 | 0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0, |
| 2531 | 2530 | 0xF2, 0x0F, 0x11, 0x45, 0xF8, |
| 2532 | | 0xF3, 0x44, 0x0F, 0x7E, 0x45, 0xF0, |
| 2533 | | 0x66, 0x44, 0x0F, 0xD6, 0x45, 0xF0, |
| 2531 | 0x66, 0x4C, 0x0F, 0x6E, 0x45, 0xF0, |
| 2532 | 0x66, 0x4C, 0x0F, 0x7E, 0x45, 0xF0, |
| 2534 | 2533 | 0x66, 0x0F, 0x2E, 0x45, 0xF0, |
| 2535 | 2534 | 0xDD, 0x4D, 0xF0, |
| 2536 | 2535 | 0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00, |