use crate::systemz::systemz_instr_info::SystemzOpcode;
pub struct SystemzMCEncoder;
impl SystemzMCEncoder {
pub fn new() -> Self {
SystemzMCEncoder
}
pub fn encode(opcode: SystemzOpcode, r1: u8, r2: u8, b2: u8, disp: i64) -> Vec<u8> {
match opcode {
SystemzOpcode::BR => {
let word: u16 = 0x07F0 | (r1 as u16 & 0x0F);
vec![(word >> 8) as u8, word as u8]
}
SystemzOpcode::BCR => {
let word: u16 = 0x0700 | (r1 as u16 & 0x0F) << 4 | (r2 as u16 & 0x0F);
vec![(word >> 8) as u8, word as u8]
}
SystemzOpcode::BALR => {
let word: u16 = 0x0500 | (r1 as u16 & 0x0F) << 4 | (r2 as u16 & 0x0F);
vec![(word >> 8) as u8, word as u8]
}
SystemzOpcode::BASR => {
let word: u16 = 0x0D00 | (r1 as u16 & 0x0F) << 4 | (r2 as u16 & 0x0F);
vec![(word >> 8) as u8, word as u8]
}
SystemzOpcode::SPM => {
let word: u16 = 0x0400 | (r1 as u16 & 0x0F);
vec![(word >> 8) as u8, word as u8]
}
SystemzOpcode::NOP => {
vec![0x07, 0x00]
}
SystemzOpcode::BRK => {
vec![0x07, 0x00]
}
SystemzOpcode::LGR => {
let word: u32 = 0xB9040000 | ((r1 & 0x0F) as u32) << 4 | (r2 & 0x0F) as u32;
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::LTGR => {
let word: u32 = 0xB9020000 | ((r1 & 0x0F) as u32) << 4 | (r2 & 0x0F) as u32;
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::EPSW => {
let word: u32 = 0xB98D0000 | ((r1 & 0x0F) as u32) << 4 | (r2 & 0x0F) as u32;
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::IPM => {
let word: u32 = 0xB2220000 | ((r1 & 0x0F) as u32);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::L => {
let word: u32 = encode_rx(0x58, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::ST => {
let word: u32 = encode_rx(0x50, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::LA => {
let word: u32 = encode_rx(0x41, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::A => {
let word: u32 = encode_rx(0x5A, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::S => {
let word: u32 = encode_rx(0x5B, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::M => {
let word: u32 = encode_rx(0x5C, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::D => {
let word: u32 = encode_rx(0x5D, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::N => {
let word: u32 = encode_rx(0x54, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::O => {
let word: u32 = encode_rx(0x56, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::X => {
let word: u32 = encode_rx(0x57, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::C => {
let word: u32 = encode_rx(0x59, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::J => {
let disp16 = (disp as i16) as u16;
let word: u32 = 0xA7F40000u32 | (disp16 as u32 & 0xFFFF);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::BAL => {
let word: u32 = encode_rx(0x45, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::LD => {
let word: u32 = encode_rx(0x68, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::STD => {
let word: u32 = encode_rx(0x60, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::AD => {
let word: u32 = encode_rx(0x6A, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::SD => {
let word: u32 = encode_rx(0x6B, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::BRC => {
let disp16 = (disp as i16) as u16;
let word: u32 = 0xA7000000u32
| ((r1 & 0x0F) as u32) << 20
| (4u32 << 16)
| (disp16 as u32 & 0xFFFF);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::LG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x04);
bytes
}
SystemzOpcode::STG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x24);
bytes
}
SystemzOpcode::AG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x08);
bytes
}
SystemzOpcode::SG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x09);
bytes
}
SystemzOpcode::NG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x80);
bytes
}
SystemzOpcode::OG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x81);
bytes
}
SystemzOpcode::XG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x82);
bytes
}
SystemzOpcode::CG => {
let bytes = encode_rxy(0xE3, r1, r2, b2, disp, 0x20);
bytes
}
SystemzOpcode::CLI => {
let i2 = (disp & 0xFF) as u8;
let d1 = (r1 as u16 & 0xFFF) as u16; let word: u32 = 0x95000000u32
| (i2 as u32) << 16
| (b2 as u32 & 0x0F) << 12
| (d1 as u32 & 0xFFF);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::TM => {
let i2 = (disp & 0xFF) as u8;
let d1 = (r1 as u16 & 0xFFF) as u16;
let word: u32 = 0x91000000u32
| (i2 as u32) << 16
| (b2 as u32 & 0x0F) << 12
| (d1 as u32 & 0xFFF);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::SLL => {
let word: u32 = encode_rs(0x89, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::SRL => {
let word: u32 = encode_rs(0x88, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::SLA => {
let word: u32 = encode_rs(0x8B, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::SRA => {
let word: u32 = encode_rs(0x8A, r1, r2, b2, disp);
vec![
(word >> 24) as u8,
(word >> 16) as u8,
(word >> 8) as u8,
word as u8,
]
}
SystemzOpcode::SLLG => {
let bytes = encode_rxy(0xEB, r1, r2, b2, disp, 0x0D);
bytes
}
SystemzOpcode::SRLG => {
let bytes = encode_rxy(0xEB, r1, r2, b2, disp, 0x0C);
bytes
}
SystemzOpcode::SLAG => {
let bytes = encode_rxy(0xEB, r1, r2, b2, disp, 0x0B);
bytes
}
SystemzOpcode::SRAG => {
let bytes = encode_rxy(0xEB, r1, r2, b2, disp, 0x0A);
bytes
}
_ => vec![0x07, 0x00], }
}
pub fn instr_size(opcode: SystemzOpcode) -> u8 {
match opcode {
SystemzOpcode::BR
| SystemzOpcode::BCR
| SystemzOpcode::BALR
| SystemzOpcode::BASR
| SystemzOpcode::SPM
| SystemzOpcode::NOP
| SystemzOpcode::BRK => 2,
SystemzOpcode::LGR
| SystemzOpcode::LTGR
| SystemzOpcode::EPSW
| SystemzOpcode::IPM
| SystemzOpcode::L
| SystemzOpcode::ST
| SystemzOpcode::LA
| SystemzOpcode::A
| SystemzOpcode::S
| SystemzOpcode::M
| SystemzOpcode::D
| SystemzOpcode::N
| SystemzOpcode::O
| SystemzOpcode::X
| SystemzOpcode::C
| SystemzOpcode::J
| SystemzOpcode::BAL
| SystemzOpcode::LD
| SystemzOpcode::STD
| SystemzOpcode::AD
| SystemzOpcode::SD
| SystemzOpcode::MD
| SystemzOpcode::DD
| SystemzOpcode::CD
| SystemzOpcode::BRC
| SystemzOpcode::CLI
| SystemzOpcode::TM
| SystemzOpcode::SLL
| SystemzOpcode::SRL
| SystemzOpcode::SLA
| SystemzOpcode::SRA
| SystemzOpcode::RLL => 4,
SystemzOpcode::LG
| SystemzOpcode::STG
| SystemzOpcode::AG
| SystemzOpcode::SG
| SystemzOpcode::NG
| SystemzOpcode::OG
| SystemzOpcode::XG
| SystemzOpcode::CG
| SystemzOpcode::SLLG
| SystemzOpcode::SRLG
| SystemzOpcode::SLAG
| SystemzOpcode::SRAG => 6,
}
}
pub fn encode_sequence(instructions: &[(SystemzOpcode, u8, u8, u8, i64)]) -> Vec<u8> {
let mut bytes = Vec::new();
for &(op, r1, r2, b2, disp) in instructions {
bytes.extend(Self::encode(op, r1, r2, b2, disp));
}
bytes
}
}
fn encode_rx(op: u8, r1: u8, x2: u8, b2: u8, d2: i64) -> u32 {
let d2_12 = (d2 & 0xFFF) as u32 & 0xFFF;
(op as u32) << 24
| ((r1 & 0x0F) as u32) << 20
| ((x2 & 0x0F) as u32) << 16
| ((b2 & 0x0F) as u32) << 12
| d2_12
}
fn encode_rxy(op: u8, r1: u8, x2: u8, b2: u8, d2: i64, op2: u8) -> Vec<u8> {
let dl = (d2 & 0x0F) as u8; let dh = ((d2 >> 4) & 0xFF) as u8; let sign = if d2 < 0 { 0x08 } else { 0x00 };
vec![
op,
((r1 & 0x0F) << 4) | (x2 & 0x0F),
((b2 & 0x0F) << 4) | (dl & 0x0F),
dh,
op2,
sign,
]
}
fn encode_rs(op: u8, r1: u8, r3: u8, b2: u8, d2: i64) -> u32 {
let d2_12 = (d2 & 0xFFF) as u32 & 0xFFF;
(op as u32) << 24
| ((r1 & 0x0F) as u32) << 20
| ((r3 & 0x0F) as u32) << 16
| ((b2 & 0x0F) as u32) << 12
| d2_12
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_nop() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::NOP, 0, 0, 0, 0);
assert_eq!(bytes, vec![0x07, 0x00]);
}
#[test]
fn test_encode_br() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::BR, 14, 0, 0, 0);
assert_eq!(bytes, vec![0x07, 0xFE]);
}
#[test]
fn test_encode_brc() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::BRC, 15, 0, 0, 0);
assert_eq!(bytes, vec![0xA7, 0xF4, 0x00, 0x00]);
}
#[test]
fn test_encode_balr() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::BALR, 14, 15, 0, 0);
assert_eq!(bytes, vec![0x05, 0xEF]);
}
#[test]
fn test_encode_basr() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::BASR, 14, 15, 0, 0);
assert_eq!(bytes, vec![0x0D, 0xEF]);
}
#[test]
fn test_encode_lgr() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::LGR, 2, 3, 0, 0);
assert_eq!(bytes, vec![0xB9, 0x04, 0x00, 0x23]);
}
#[test]
fn test_encode_ltgr() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::LTGR, 2, 2, 0, 0);
assert_eq!(bytes, vec![0xB9, 0x02, 0x00, 0x22]);
}
#[test]
fn test_encode_l() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::L, 1, 2, 3, 0);
assert_eq!(bytes, vec![0x58, 0x12, 0x30, 0x00]);
}
#[test]
fn test_encode_st() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::ST, 5, 3, 4, 0x100);
assert_eq!(bytes, vec![0x50, 0x53, 0x41, 0x00]);
}
#[test]
fn test_encode_la() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::LA, 1, 0, 15, 0);
assert_eq!(bytes, vec![0x41, 0x10, 0xF0, 0x00]);
}
#[test]
fn test_encode_lg() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::LG, 1, 2, 3, 0);
assert_eq!(bytes, vec![0xE3, 0x12, 0x30, 0x00, 0x04, 0x00]);
}
#[test]
fn test_encode_stg() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::STG, 5, 3, 4, 0x100);
assert_eq!(bytes, vec![0xE3, 0x53, 0x40, 0x10, 0x24, 0x00]);
}
#[test]
fn test_instr_size_rr() {
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::BR), 2);
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::NOP), 2);
}
#[test]
fn test_instr_size_rre() {
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::LGR), 4);
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::LTGR), 4);
}
#[test]
fn test_instr_size_rx() {
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::L), 4);
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::ST), 4);
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::A), 4);
}
#[test]
fn test_instr_size_rxy() {
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::LG), 6);
assert_eq!(SystemzMCEncoder::instr_size(SystemzOpcode::STG), 6);
}
#[test]
fn test_encode_sequence() {
let seq = vec![
(SystemzOpcode::NOP, 0, 0, 0, 0),
(SystemzOpcode::BR, 14, 0, 0, 0),
];
let bytes = SystemzMCEncoder::encode_sequence(&seq);
assert_eq!(bytes, vec![0x07, 0x00, 0x07, 0xFE]);
}
#[test]
fn test_encode_rx_negative_displacement() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::L, 1, 2, 3, -1);
assert_eq!(bytes, vec![0x58, 0x12, 0x3F, 0xFF]);
}
#[test]
fn test_encode_rxy_negative_displacement() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::LG, 1, 2, 3, -1);
assert_eq!(bytes, vec![0xE3, 0x12, 0x3F, 0xFF, 0x04, 0x08]);
}
#[test]
fn test_encode_add() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::A, 1, 2, 3, 0);
assert_eq!(bytes, vec![0x5A, 0x12, 0x30, 0x00]);
}
#[test]
fn test_encode_sl() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::C, 2, 3, 4, 0);
assert_eq!(bytes, vec![0x59, 0x23, 0x40, 0x00]);
}
#[test]
fn test_encode_brc_with_offset() {
let bytes = SystemzMCEncoder::encode(SystemzOpcode::BRC, 15, 0, 0, 42);
assert_eq!(bytes, vec![0xA7, 0xF4, 0x00, 0x2A]);
}
}