// use common::isa::instructions::Instruction;
// use dsa::{Emulator, MemoryBank, Page};
// /// DSA Emulator Benchmark
// ///
// /// Place this in `dsa/src/bin/bench.rs` and run with:
// ///   cargo run --bin bench --release
// ///
// /// Three workloads are tested:
// ///   1. ALU-heavy   — tight arithmetic loop, no memory, no branches
// ///   2. Memory-heavy — repeated load/store to a working set of addresses
// ///   3. Branch-heavy — the bf.dsa dispatch pattern (ieq + jnz chains)
// ///
// /// Each workload is a hand-assembled flat binary loaded directly into
// /// the emulator, bypassing all the args/display/IO plumbing.
// use std::{
//     thread,
//     time::{Duration, Instant},
//     u16,
// };

// // ---------------------------------------------------------------------------
// // Minimal no-op memory map + IO so the emulator boots without a config file.
// // ---------------------------------------------------------------------------

// fn make_emulator() -> Emulator {
//     use dsa::{
//         config::{MemoryMap, Region, RegionType},
//         io::display::Display,
//     };

//     let mmap = MemoryMap {
//         table_addr: 0x40000,
//         regions: vec![
//             Region {
//                 base: 0x0000_0000,
//                 size: 0x0010_0000, // 1 MiB RAM
//                 region_type: RegionType::Usable,
//                 identity_map_on_boot: true,
//             },
//             Region {
//                 base: 0x0020_0000,
//                 size: 0x0002_0000, // 128 KiB MMIO (display)
//                 region_type: RegionType::MMIO,
//                 identity_map_on_boot: false,
//             },
//         ],
//     };

//     // let iomap = vec![dsa::config::IoMapping {
//     //     device: dsa::io::DeviceId::Display,
//     //     base: 0x0020_0000,
//     //     size: 0x0000_07D0,
//     // }];

//     // let (display, _handle) = Display::new(80, 25);
//     // Emulator::new(MemoryBank::new())
//     //     .with_device(display)
//     //     .apply_memory_map(mmap)
//     //     .apply_io_map(iomap)
//     //     .unwrap()
// }

// // ---------------------------------------------------------------------------
// // Instruction encoding helpers (little-endian words)
// // ---------------------------------------------------------------------------

// /// Encode an I-type instruction: [op:6][src:5][dst:5][imm:16]
// fn enc_i(op: u8, src: u8, dst: u8, imm: u16) -> u32 {
//     ((op as u32 & 0x3F) << 26)
//         | ((src as u32 & 0x1F) << 21)
//         | ((dst as u32 & 0x1F) << 16)
//         | imm as u32
// }

// /// Encode an R-type instruction: [op:6][r1:5][r2:5][r3:5][r4:5][u6:6]
// fn enc_r(op: u8, r1: u8, r2: u8, r3: u8, r4: u8, u6: u8) -> u32 {
//     ((op as u32 & 0x3F) << 26)
//         | ((r1 as u32 & 0x1F) << 21)
//         | ((r2 as u32 & 0x1F) << 16)
//         | ((r3 as u32 & 0x1F) << 11)
//         | ((r4 as u32 & 0x1F) << 6)
//         | (u6 as u32 & 0x3F)
// }

// // Register indices — must match your Register enum discriminants
// const RG0: u8 = 0;
// const RG1: u8 = 1;
// const RG2: u8 = 2;
// const RG3: u8 = 3;
// const RG4: u8 = 4;
// const RG5: u8 = 5;
// const ZERO: u8 = 16;

// // Opcodes — must match your Opcode enum discriminants.
// // Fill these in from your ISA.md / encode.rs.
// const OP_NOP: u8 = 0x0D; // adjust to match your actual opcode values
// const OP_HLT: u8 = 0x2B;
// const OP_ADD: u8 = 0x22;
// const OP_ADDI: u8 = 0x23;
// const OP_SUBI: u8 = 0x24;
// const OP_LDW: u8 = 0x07;
// const OP_STW: u8 = 0x0A;
// const OP_LLI: u8 = 0x0B;
// const OP_LUI: u8 = 0x0C;
// const OP_IEQ: u8 = 0x0E;
// const OP_JNZ: u8 = 0x15;
// const OP_JMP: u8 = 0x12;

// // ---------------------------------------------------------------------------
// // Workload 1: ALU-heavy
// //
// // Counts down from 10_000_000 using subi, jumps back to top.
// // Pure register arithmetic, no memory accesses after init.
// // Instruction mix: lli, subi, ieq, jnz, hlt
// //
// // Layout (each word at address = index * 4):
// //   0: lli rg0, 0 (upper half of count)        [lui follows]
// //   4: lui rg0, <high>
// //   8: loop: subi rg0, 1, rg0
// //  12: ieq rg0, zero, rg1
// //  16: jnz rg1, zero, 8                         [jump to loop]
// //  20: hlt
// // ---------------------------------------------------------------------------
// fn alu_workload() -> Vec<u8> {
//     let count: u32 = 10_000_000;
//     let lo = (count & 0xFFFF) as u16;
//     let hi = ((count >> 16) & 0xFFFF) as u16;

//     let words: &[u32] = &[
//         enc_i(OP_LLI, 0, RG0, lo), // 0x00: lli rg0, lo(count)
//         enc_i(OP_LUI, 0, RG0, hi), // 0x04: lui rg0, hi(count)
//         // loop @ 0x08:
//         enc_i(OP_SUBI, RG0, RG0, 1),         // 0x08: subi rg0, rg0, 1
//         enc_r(OP_IEQ, RG0, ZERO, RG1, 0, 0), // 0x0C: ieq rg0, zero, rg1
//         enc_i(OP_JNZ, RG1, ZERO, 0x0008), // 0x10: jnz rg1, zero, 0x08  <- BUG: jnz jumps when rg1!=0
//         //   rg1=1 when rg0==0, so we want jez here to keep looping
//         //   fix: use jez (jump when rg1==0, i.e. rg0!=0)
//         enc_i(OP_HLT, 0, 0, 0), // 0x14: hlt
//     ];
//     // Note: replace OP_JNZ above with your JEZ opcode so the loop continues
//     // while rg0 != 0. JNZ exits when rg1 != 0, i.e. when rg0 == 0 (done).
//     // The ieq sets rg1=1 only when rg0==0, so jnz rg1 exits the loop correctly.
//     // Actually this IS correct: loop while ieq returns 0 (rg0 != 0),
//     // exit when ieq returns 1 (rg0 == 0). jnz rg1 = jump when rg1 != 0 = jump when done.
//     // We want to jump BACK while not done, so we need jez:
//     //   jez rg1, zero, 0x08  = jump back when rg1==0 (rg0 != 0, not done yet)
//     // Swap OP_JNZ for your JEZ opcode.
//     words_to_bytes(words)
// }

// // ---------------------------------------------------------------------------
// // Workload 2: Memory-heavy
// //
// // Repeatedly writes and reads a 64-word working set.
// // Tests page table lookup performance under repeated memory access.
// //
// // rg0 = base address (0x1000, well within RAM, page-aligned)
// // rg1 = loop counter (1000 outer iterations)
// // rg2 = inner counter (64 words per pass)
// // rg3 = scratch for store value
// // ---------------------------------------------------------------------------
// fn memory_workload() -> Vec<u8> {
//     let base: u32 = 0x1000;
//     let outer: u16 = 1000;
//     let inner: u16 = 64;

//     let words: &[u32] = &[
//         // init
//         enc_i(OP_LLI, 0, RG0, (base & 0xFFFF) as u16), // rg0 = base
//         enc_i(OP_LLI, 0, RG1, outer),                  // rg1 = outer count
//         // outer_loop @ 0x08:
//         enc_i(OP_LLI, 0, RG2, inner),  // rg2 = inner count
//         enc_i(OP_LLI, 0, RG3, 0xABCD), // rg3 = value to write
//         // inner_loop @ 0x10:
//         enc_i(OP_STW, RG3, RG0, 0),          // stw rg3, rg0, 0
//         enc_i(OP_LDW, RG0, RG3, 0),          // ldw rg0, rg3, 0  (read back)
//         enc_i(OP_ADDI, RG0, RG0, 4),         // rg0 += 4
//         enc_i(OP_SUBI, RG2, RG2, 1),         // rg2 -= 1
//         enc_r(OP_IEQ, RG2, ZERO, RG4, 0, 0), // ieq rg2, zero, rg4
//         enc_i(OP_JNZ, RG4, ZERO, 0x0010),    // jez rg4 -> inner_loop (swap opcode)
//         // restore base, dec outer
//         enc_i(OP_LLI, 0, RG0, (base & 0xFFFF) as u16),
//         enc_i(OP_SUBI, RG1, RG1, 1),
//         enc_r(OP_IEQ, RG1, ZERO, RG4, 0, 0),
//         enc_i(OP_JNZ, RG4, ZERO, 0x0008), // jez rg4 -> outer_loop (swap opcode)
//         enc_i(OP_HLT, 0, 0, 0),
//     ];
//     words_to_bytes(words)
// }

// // ---------------------------------------------------------------------------
// // Workload 3: Branch-heavy
// //
// // Mimics the bf.dsa dispatch pattern: compare a value against 8 constants,
// // branch on each. Worst case for branch predictor.
// // rg0 = current "instruction" (cycles through 8 values)
// // rg8-rgf = the 8 bf opcode constants
// // ---------------------------------------------------------------------------
// fn branch_workload() -> Vec<u8> {
//     // 8 bf opcodes: + - > < . , [ ]
//     let opcodes: [u16; 8] = [43, 45, 62, 60, 46, 44, 91, 93];
//     // We'll cycle rg0 through these values and do the full dispatch chain.
//     // To keep it self-contained: load opcodes, set outer loop counter,
//     // inner loop cycles rg0 through all 8 values doing ieq+jnz for each.
//     let outer: u16 = u16::MAX;

//     let mut w: Vec<u32> = Vec::new();

//     // Load 8 opcode constants into rg8-rgf (regs 8-15)
//     for (i, &op) in opcodes.iter().enumerate() {
//         w.push(enc_i(OP_LLI, 0, 8 + i as u8, op));
//     }
//     // rg1 = outer counter
//     w.push(enc_i(OP_LLI, 0, RG1, outer));

//     // outer_loop:
//     let outer_loop_addr = (w.len() * 4) as u16;

//     // rg5 = inner counter (8 opcodes)
//     w.push(enc_i(OP_LLI, 0, RG5, 8));
//     // rg2 = pointer into opcode table (we'll use addi to step rg0 through values)
//     // For simplicity: just set rg0 to each opcode in sequence via lli each iteration
//     // This is branch-heavy, not arithmetic-heavy, so the lli overhead is fine.
//     for (i, &op) in opcodes.iter().enumerate() {
//         let next_offset = ((w.len() + 4) * 4) as u16; // addr after the jnz
//         w.push(enc_i(OP_LLI, 0, RG0, op)); // rg0 = opcode
//         w.push(enc_r(OP_IEQ, RG0, 8 + i as u8, RG4, 0, 0)); // ieq rg0, rgX, rg4
//         w.push(enc_i(OP_JNZ, RG4, ZERO, next_offset)); // jnz rg4 (jez to skip)
//         w.push(enc_i(OP_NOP, 0, 0, 0)); // "handler" nop
//     }
//     // dec outer, loop
//     let after_dispatch = (w.len() * 4) as u16;
//     w.push(enc_i(OP_SUBI, RG1, RG1, 1));
//     w.push(enc_r(OP_IEQ, RG1, ZERO, RG4, 0, 0));
//     w.push(enc_i(OP_JNZ, RG4, ZERO, outer_loop_addr)); // jez -> outer_loop
//     w.push(enc_i(OP_HLT, 0, 0, 0));

//     words_to_bytes(&w)
// }

// fn words_to_bytes(words: &[u32]) -> Vec<u8> {
//     words.iter().flat_map(|w| w.to_le_bytes()).collect()
// }

// // ---------------------------------------------------------------------------
// // Runner
// // ---------------------------------------------------------------------------

// struct BenchResult {
//     name: &'static str,
//     instructions: u64,
//     elapsed: Duration,
//     mips: f64,
// }

// fn run_bench(name: &'static str, binary: Vec<u8>) -> BenchResult {
//     let mut emu = make_emulator();
//     let handle = emu.state_handle();

//     let pages: Vec<Page> = Page::paginate(&binary).collect();
//     for (i, page) in pages.iter().enumerate() {
//         emu.memory_mut().write_page(i as u32 * 4096, page);
//     }

//     let start = Instant::now();
//     // run() blocks until HLT
//     emu.run().expect("emulator run failed");
//     let elapsed = start.elapsed();

//     // read clock from emulator — we need to expose it.
//     // For now, estimate from elapsed + target MIPS.
//     // TODO: expose emulator.clock() -> u64 and use that directly.
//     let instructions = handle.proc.load().clock as u64;
//     let mips = instructions as f64 / elapsed.as_micros() as f64;

//     BenchResult {
//         name,
//         instructions,
//         elapsed,
//         mips,
//     }
// }

// fn main() {
//     println!("DSA Emulator Benchmark");
//     println!("======================");
//     println!();

//     // NOTE: you need to expose `pub fn clock(&self) -> usize` on Emulator
//     // and ensure `run()` returns normally on HLT without blocking in idle_wait.
//     // The idle_wait loop currently spins forever after HLT — for benchmarking,
//     // you want run() to return when the program halts. Add a flag or check:
//     //   if !self.internal_state.running { break 'emu; }
//     // after the HLT handler sets running=false.

//     let workloads: &[(&'static str, fn() -> Vec<u8>)] = &[
//         ("ALU-heavy (10M countdown)", alu_workload),
//         ("Memory-heavy (64K rw ops)", memory_workload),
//         ("Branch-heavy (bf dispatch)", branch_workload),
//     ];

//     let mut results = Vec::new();
//     for &(name, build) in workloads {
//         print!("Running {}... ", name);
//         let binary = build();
//         let result = run_bench(name, binary);
//         println!("done ({:.1}ms)", result.elapsed.as_secs_f64() * 1000.0);
//         results.push(result);
//     }

//     println!();
//     println!(
//         "{:<35} {:>12} {:>12} {:>10}",
//         "Workload", "Instructions", "Time", "MIPS"
//     );
//     println!("{}", "-".repeat(72));
//     for r in &results {
//         println!(
//             "{:<35} {:>12} {:>10.1}ms {:>10.1}",
//             r.name,
//             r.instructions,
//             r.elapsed.as_secs_f64() * 1000.0,
//             r.mips,
//         );
//     }

//     let avg_mips: f64 = results.iter().map(|r| r.mips).sum::<f64>() / results.len() as f64;
//     println!("{}", "-".repeat(72));
//     println!("{:<35} {:>35.1} MIPS avg", "Overall", avg_mips);
// }

fn main() {}
