| ... | @@ -203,6 +203,21 @@ pub const Instruction = union(enum) { | ... | @@ -203,6 +203,21 @@ pub const Instruction = union(enum) { |
| 203 | opc: u2, | 203 | opc: u2, |
| 204 | sf: u1, | 204 | sf: u1, |
| 205 | }, | 205 | }, |
| | 206 | LoadRegister: packed struct { |
| | 207 | rt: u5, |
| | 208 | rn: u5, |
| | 209 | offset: u12, |
| | 210 | opc: u2 = 0b01, |
| | 211 | op1: u2, |
| | 212 | fixed: u4 = 0b111_0, |
| | 213 | size: u2, |
| | 214 | }, |
| | 215 | LoadLiteral: packed struct { |
| | 216 | rt: u5, |
| | 217 | imm19: u19, |
| | 218 | fixed: u6 = 0b011_0_00, |
| | 219 | opc: u2, |
| | 220 | }, |
| 206 | ExceptionGeneration: packed struct { | 221 | ExceptionGeneration: packed struct { |
| 207 | ll: u2, | 222 | ll: u2, |
| 208 | op2: u3, | 223 | op2: u3, |
| ... | @@ -227,12 +242,131 @@ pub const Instruction = union(enum) { | ... | @@ -227,12 +242,131 @@ pub const Instruction = union(enum) { |
| 227 | pub fn toU32(self: Instruction) u32 { | 242 | pub fn toU32(self: Instruction) u32 { |
| 228 | return switch (self) { | 243 | return switch (self) { |
| 229 | .MoveWideImmediate => |v| @bitCast(u32, v), | 244 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| | 245 | .LoadRegister => |v| @bitCast(u32, v), |
| | 246 | .LoadLiteral => |v| @bitCast(u32, v), |
| 230 | .ExceptionGeneration => |v| @bitCast(u32, v), | 247 | .ExceptionGeneration => |v| @bitCast(u32, v), |
| 231 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), | 248 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| 232 | .UnconditionalBranchImmediate => |v| @bitCast(u32, v), | 249 | .UnconditionalBranchImmediate => |v| @bitCast(u32, v), |
| 233 | }; | 250 | }; |
| 234 | } | 251 | } |
| 235 | | 252 | |
| | 253 | /// Represents the offset operand of a load or store instruction. |
| | 254 | /// Data can be loaded from memory with either an immediate offset |
| | 255 | /// or an offset that is stored in some register. |
| | 256 | pub const Offset = union(enum) { |
| | 257 | Immediate: union(enum) { |
| | 258 | PostIndex: i9, |
| | 259 | PreIndex: i9, |
| | 260 | Unsigned: u12, |
| | 261 | }, |
| | 262 | Register: struct { |
| | 263 | rm: u5, |
| | 264 | shift: union(enum) { |
| | 265 | Uxtw: u2, |
| | 266 | Lsl: u2, |
| | 267 | Sxtw: u2, |
| | 268 | Sxtx: u2, |
| | 269 | }, |
| | 270 | }, |
| | 271 | |
| | 272 | pub const none = Offset{ |
| | 273 | .Immediate = .{ .Unsigned = 0 }, |
| | 274 | }; |
| | 275 | |
| | 276 | pub fn toU12(self: Offset) u12 { |
| | 277 | return switch (self) { |
| | 278 | .Immediate => |imm_type| switch (imm_type) { |
| | 279 | .PostIndex => |v| @intCast(u12, @bitCast(u9, v)) << 2 + 1, |
| | 280 | .PreIndex => |v| @intCast(u12, @bitCast(u9, v)) << 2 + 3, |
| | 281 | .Unsigned => |v| v, |
| | 282 | }, |
| | 283 | .Register => |r| switch (r.shift) { |
| | 284 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, |
| | 285 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, |
| | 286 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, |
| | 287 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, |
| | 288 | }, |
| | 289 | }; |
| | 290 | } |
| | 291 | |
| | 292 | pub fn imm(offset: u12) Offset { |
| | 293 | return Offset{ |
| | 294 | .Immediate = .{ .Unsigned = offset }, |
| | 295 | }; |
| | 296 | } |
| | 297 | |
| | 298 | pub fn imm_post_index(offset: i9) Offset { |
| | 299 | return Offset{ |
| | 300 | .Immediate = .{ .PostIndex = offset }, |
| | 301 | }; |
| | 302 | } |
| | 303 | |
| | 304 | pub fn imm_pre_index(offset: i9) Offset { |
| | 305 | return Offset{ |
| | 306 | .Immediate = .{ .PreIndex = offset }, |
| | 307 | }; |
| | 308 | } |
| | 309 | |
| | 310 | pub fn reg(rm: Register) Offset { |
| | 311 | return Offset{ |
| | 312 | .Register = .{ |
| | 313 | .rm = rm.id(), |
| | 314 | .shift = .{ |
| | 315 | .Lsl = 0, |
| | 316 | }, |
| | 317 | }, |
| | 318 | }; |
| | 319 | } |
| | 320 | |
| | 321 | pub fn reg_uxtw(rm: Register, shift: u2) Offset { |
| | 322 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| | 323 | return Offset{ |
| | 324 | .Register = .{ |
| | 325 | .rm = rm.id(), |
| | 326 | .shift = .{ |
| | 327 | .Uxtw = shift, |
| | 328 | }, |
| | 329 | }, |
| | 330 | }; |
| | 331 | } |
| | 332 | |
| | 333 | pub fn reg_lsl(rm: Register, shift: u2) Offset { |
| | 334 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| | 335 | return Offset{ |
| | 336 | .Register = .{ |
| | 337 | .rm = rm.id(), |
| | 338 | .shift = .{ |
| | 339 | .Lsl = shift, |
| | 340 | }, |
| | 341 | }, |
| | 342 | }; |
| | 343 | } |
| | 344 | |
| | 345 | pub fn reg_sxtw(rm: Register, shift: u2) Offset { |
| | 346 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| | 347 | return Offset{ |
| | 348 | .Register = .{ |
| | 349 | .rm = rm.id(), |
| | 350 | .shift = .{ |
| | 351 | .Sxtw = shift, |
| | 352 | }, |
| | 353 | }, |
| | 354 | }; |
| | 355 | } |
| | 356 | |
| | 357 | pub fn reg_sxtx(rm: Register, shift: u2) Offset { |
| | 358 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| | 359 | return Offset{ |
| | 360 | .Register = .{ |
| | 361 | .rm = rm.id(), |
| | 362 | .shift = .{ |
| | 363 | .Sxtx = shift, |
| | 364 | }, |
| | 365 | }, |
| | 366 | }; |
| | 367 | } |
| | 368 | }; |
| | 369 | |
| 236 | // Helper functions for assembly syntax functions | 370 | // Helper functions for assembly syntax functions |
| 237 | | 371 | |
| 238 | fn moveWideImmediate( | 372 | fn moveWideImmediate( |
| ... | @@ -270,6 +404,69 @@ pub const Instruction = union(enum) { | ... | @@ -270,6 +404,69 @@ pub const Instruction = union(enum) { |
| 270 | } | 404 | } |
| 271 | } | 405 | } |
| 272 | | 406 | |
| | 407 | fn loadRegister(rt: Register, rn: Register, offset: Offset) Instruction { |
| | 408 | const off = offset.toU12(); |
| | 409 | const op1: u2 = blk: { |
| | 410 | switch (offset) { |
| | 411 | .Immediate => |imm| switch (imm) { |
| | 412 | .Unsigned => break :blk 0b01, |
| | 413 | else => {}, |
| | 414 | }, |
| | 415 | else => {}, |
| | 416 | } |
| | 417 | break :blk 0b00; |
| | 418 | }; |
| | 419 | switch (rt.size()) { |
| | 420 | 32 => { |
| | 421 | return Instruction{ |
| | 422 | .LoadRegister = .{ |
| | 423 | .rt = rt.id(), |
| | 424 | .rn = rn.id(), |
| | 425 | .offset = offset.toU12(), |
| | 426 | .op1 = op1, |
| | 427 | .size = 0b10, |
| | 428 | }, |
| | 429 | }; |
| | 430 | }, |
| | 431 | 64 => { |
| | 432 | return Instruction{ |
| | 433 | .LoadRegister = .{ |
| | 434 | .rt = rt.id(), |
| | 435 | .rn = rn.id(), |
| | 436 | .offset = offset.toU12(), |
| | 437 | .op1 = op1, |
| | 438 | .size = 0b11, |
| | 439 | }, |
| | 440 | }; |
| | 441 | }, |
| | 442 | else => unreachable, // unexpected register size |
| | 443 | } |
| | 444 | } |
| | 445 | |
| | 446 | fn loadLiteral(rt: Register, imm19: u19) Instruction { |
| | 447 | switch (rt.size()) { |
| | 448 | 32 => { |
| | 449 | return Instruction{ |
| | 450 | .LoadLiteral = .{ |
| | 451 | .rt = rt.id(), |
| | 452 | .imm19 = imm19, |
| | 453 | .opc = 0b00, |
| | 454 | }, |
| | 455 | }; |
| | 456 | }, |
| | 457 | 64 => { |
| | 458 | return Instruction{ |
| | 459 | .LoadLiteral = .{ |
| | 460 | .rt = rt.id(), |
| | 461 | .imm19 = imm19, |
| | 462 | .opc = 0b01, |
| | 463 | }, |
| | 464 | }; |
| | 465 | }, |
| | 466 | else => unreachable, // unexpected register size |
| | 467 | } |
| | 468 | } |
| | 469 | |
| 273 | fn exceptionGeneration( | 470 | fn exceptionGeneration( |
| 274 | opc: u3, | 471 | opc: u3, |
| 275 | op2: u3, | 472 | op2: u3, |
| ... | @@ -332,6 +529,21 @@ pub const Instruction = union(enum) { | ... | @@ -332,6 +529,21 @@ pub const Instruction = union(enum) { |
| 332 | return moveWideImmediate(0b11, rd, imm16, shift); | 529 | return moveWideImmediate(0b11, rd, imm16, shift); |
| 333 | } | 530 | } |
| 334 | | 531 | |
| | 532 | // Load register |
| | 533 | |
| | 534 | pub const LdrArgs = struct { |
| | 535 | rn: ?Register = null, |
| | 536 | offset: Offset = Offset.none, |
| | 537 | literal: ?u19 = null, |
| | 538 | }; |
| | 539 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { |
| | 540 | if (args.rn) |rn| { |
| | 541 | return loadRegister(rt, rn, args.offset); |
| | 542 | } else { |
| | 543 | return loadLiteral(rt, args.literal.?); |
| | 544 | } |
| | 545 | } |
| | 546 | |
| 335 | // Exception generation | 547 | // Exception generation |
| 336 | | 548 | |
| 337 | pub fn svc(imm16: u16) Instruction { | 549 | pub fn svc(imm16: u16) Instruction { |
| ... | @@ -379,6 +591,10 @@ pub const Instruction = union(enum) { | ... | @@ -379,6 +591,10 @@ pub const Instruction = union(enum) { |
| 379 | } | 591 | } |
| 380 | }; | 592 | }; |
| 381 | | 593 | |
| | 594 | test "" { |
| | 595 | testing.refAllDecls(@This()); |
| | 596 | } |
| | 597 | |
| 382 | test "serialize instructions" { | 598 | test "serialize instructions" { |
| 383 | const Testcase = struct { | 599 | const Testcase = struct { |
| 384 | inst: Instruction, | 600 | inst: Instruction, |
| ... | @@ -426,6 +642,18 @@ test "serialize instructions" { | ... | @@ -426,6 +642,18 @@ test "serialize instructions" { |
| 426 | .inst = Instruction.bl(0x10), | 642 | .inst = Instruction.bl(0x10), |
| 427 | .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, | 643 | .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, |
| 428 | }, | 644 | }, |
| | 645 | .{ // ldr x2, [x1] |
| | 646 | .inst = Instruction.ldr(.x2, .{ .rn = .x1 }), |
| | 647 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, |
| | 648 | }, |
| | 649 | .{ // ldr x2, [x1], (x3) |
| | 650 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.reg(.x3) }), |
| | 651 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, |
| | 652 | }, |
| | 653 | .{ // ldr x2, label |
| | 654 | .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), |
| | 655 | .expected = 0b01_011_0_00_0000000000000000001_00010, |
| | 656 | }, |
| 429 | }; | 657 | }; |
| 430 | | 658 | |
| 431 | for (testcases) |case| { | 659 | for (testcases) |case| { |