<commit_before>#![allow(unneeded_field_pattern)]
#![allow(private_in_public)]

use self::addressing_modes::NoTickMode;
use cpu::CPU;
use cpu::CYCLE_TABLE;
use cpu::IRQ_VECTOR;
use cpu::JitInterrupt;
use cpu::Registers;
use cpu::nes_analyst::Analyst;
use cpu::nes_analyst::BlockAnalysis;
use cpu::nes_analyst::InstructionAnalysis;

use dynasmrt::{AssemblyOffset, DynasmApi, DynasmLabelApi, ExecutableBuffer, DynamicLabel};
use fnv::FnvHashMap;
use memory::MemSegment;
use std::mem;

const CARRY: u8 = 0b0000_0001;
const ZERO: u8 = 0b0000_0010;
const SUPPRESS_IRQ: u8 = 0b0000_0100;
const DECIMAL: u8 = 0b0000_1000;
const BREAK: u8 = 0b0001_0000;
const OVERFLOW: u8 = 0b0100_0000;
const SIGN: u8 = 0b1000_0000;

const HIGH_BIT: u8 = 0b1000_0000;
const LOW_BIT: u8 = 0b0000_0001;

macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        &(*(0 as *const $ty)).$field as *const _ as usize
    }
}

macro_rules! offset_of_2 {
    ($ty:ty, $field:ident, $field2:ident) => {
        &(*(0 as *const $ty)).$field.$field2 as *const _ as usize
    }
}

pub struct ExecutableBlock {
    offset: AssemblyOffset,
    buffer: ExecutableBuffer,
}

impl ExecutableBlock {
    pub fn call(&self, cpu: &mut CPU) {
        let cpu: *mut CPU = cpu as _;
        let offset = self.offset;
        let f: fn(*mut CPU, *mut [u8; 0x800]) -> () =
            unsafe { mem::transmute(self.buffer.ptr(offset)) };
        let ram = unsafe { &mut (*cpu).ram };
        trampoline_to_nes(f, cpu, ram);
    }
}

pub fn compile(addr: u16, cpu: &mut CPU) -> ExecutableBlock {
    let analysis = Analyst::new(cpu).analyze(addr);
    Compiler::new(cpu, analysis).compile_block()
}

// rcx and sub-sections thereof are the general-purpose scratch register.
// Sometimes r8 and rax are used as scratch registers as well
dynasm!(this
    ; .alias cpu, rbx
    ; .alias ram, rdx
    ; .alias arg, r8b
    ; .alias arg_w, r8w
    ; .alias n_a, r9b
    ; .alias n_x, r10b
    ; .alias n_y, r11b
    ; .alias n_p, r12b
    ; .alias n_sp, r13b
    ; .alias n_pc, r14w
    ; .alias cyc, r15
);

macro_rules! load_registers {
    ($this:ident) => {{
        dynasm!($this.asm
            ; lea rcx, cpu => CPU.regs
            ; xor r8, r8
            ; movzx r9, BYTE rcx => Registers.a
            ; movzx r10, BYTE rcx => Registers.x
            ; movzx r11, BYTE rcx => Registers.y
            ; movzx r12, BYTE rcx => Registers.p
            ; movzx r13, BYTE rcx => Registers.sp
            ; movzx r14, WORD rcx => Registers.pc
            ; mov cyc, QWORD cpu => CPU.cycle
        );
    }};
}

macro_rules! store_registers {
    ($this:ident) => {{
        dynasm!($this.asm
            ; lea rcx, cpu => CPU.regs
            ; mov BYTE rcx => Registers.a, n_a
            ; mov BYTE rcx => Registers.x, n_x
            ; mov BYTE rcx => Registers.y, n_y
            ; mov BYTE rcx => Registers.p, n_p
            ; mov BYTE rcx => Registers.sp, n_sp
            ; mov WORD rcx => Registers.pc, n_pc
            ; mov QWORD cpu => CPU.cycle, cyc
        );
    }};
}

#[cfg(feature = "debug_features")]
macro_rules! call_trace {
    ($this:ident) => {dynasm!($this.asm
        ; mov n_pc, WORD $this.pc as _
        ;; store_registers!($this)
        ; push rax
        ; push rcx
        ; push rdx
        ; push r9
        ; push r10
        ; push r11
        ; mov rax, QWORD ::cpu::x86_64_compiler::trace as _
        ; mov rcx, rbx //Pointer to CPU is first arg
        ; sub rsp, 0x20
        ; call rax
        ; add rsp, 0x20
        ; pop r11
        ; pop r10
        ; pop r9
        ; pop rdx
        ; pop rcx
        ; pop rax
    );};
}

#[cfg(not(feature = "debug_features"))]
macro_rules! call_trace {
    ($this:ident) => {};
}

#[cfg(feature = "debug_features")]
pub extern "win64" fn trace(cpu: *mut CPU) {
    unsafe { (*cpu).trace() }
}

macro_rules! call_naked {
    ($this:ident, $addr:expr) => {dynasm!($this.asm
        ; mov rax, QWORD $addr as _
        ; call rax
    );};
}

fn trampoline_to_nes(
    f: fn(*mut CPU, *mut [u8; 0x800]) -> (),
    cpu: *mut CPU,<commit_msg>Fix segfault when running with JIT and CPU trace enabled.<commit_after>#![allow(unneeded_field_pattern)]
#![allow(private_in_public)]

use self::addressing_modes::NoTickMode;
use cpu::CPU;
use cpu::CYCLE_TABLE;
use cpu::IRQ_VECTOR;
use cpu::JitInterrupt;
use cpu::Registers;
use cpu::nes_analyst::Analyst;
use cpu::nes_analyst::BlockAnalysis;
use cpu::nes_analyst::InstructionAnalysis;

use dynasmrt::{AssemblyOffset, DynasmApi, DynasmLabelApi, ExecutableBuffer, DynamicLabel};
use fnv::FnvHashMap;
use memory::MemSegment;
use std::mem;

const CARRY: u8 = 0b0000_0001;
const ZERO: u8 = 0b0000_0010;
const SUPPRESS_IRQ: u8 = 0b0000_0100;
const DECIMAL: u8 = 0b0000_1000;
const BREAK: u8 = 0b0001_0000;
const OVERFLOW: u8 = 0b0100_0000;
const SIGN: u8 = 0b1000_0000;

const HIGH_BIT: u8 = 0b1000_0000;
const LOW_BIT: u8 = 0b0000_0001;

macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        &(*(0 as *const $ty)).$field as *const _ as usize
    }
}

macro_rules! offset_of_2 {
    ($ty:ty, $field:ident, $field2:ident) => {
        &(*(0 as *const $ty)).$field.$field2 as *const _ as usize
    }
}

pub struct ExecutableBlock {
    offset: AssemblyOffset,
    buffer: ExecutableBuffer,
}

impl ExecutableBlock {
    pub fn call(&self, cpu: &mut CPU) {
        let cpu: *mut CPU = cpu as _;
        let offset = self.offset;
        let f: fn(*mut CPU, *mut [u8; 0x800]) -> () =
            unsafe { mem::transmute(self.buffer.ptr(offset)) };
        let ram = unsafe { &mut (*cpu).ram };
        trampoline_to_nes(f, cpu, ram);
    }
}

pub fn compile(addr: u16, cpu: &mut CPU) -> ExecutableBlock {
    let analysis = Analyst::new(cpu).analyze(addr);
    Compiler::new(cpu, analysis).compile_block()
}

// rcx and sub-sections thereof are the general-purpose scratch register.
// Sometimes r8 and rax are used as scratch registers as well
dynasm!(this
    ; .alias cpu, rbx
    ; .alias ram, rdx
    ; .alias arg, r8b
    ; .alias arg_w, r8w
    ; .alias n_a, r9b
    ; .alias n_x, r10b
    ; .alias n_y, r11b
    ; .alias n_p, r12b
    ; .alias n_sp, r13b
    ; .alias n_pc, r14w
    ; .alias cyc, r15
);

macro_rules! load_registers {
    ($this:ident) => {{
        dynasm!($this.asm
            ; lea rcx, cpu => CPU.regs
            ; xor r8, r8
            ; movzx r9, BYTE rcx => Registers.a
            ; movzx r10, BYTE rcx => Registers.x
            ; movzx r11, BYTE rcx => Registers.y
            ; movzx r12, BYTE rcx => Registers.p
            ; movzx r13, BYTE rcx => Registers.sp
            ; movzx r14, WORD rcx => Registers.pc
            ; mov cyc, QWORD cpu => CPU.cycle
        );
    }};
}

macro_rules! store_registers {
    ($this:ident) => {{
        dynasm!($this.asm
            ; lea rcx, cpu => CPU.regs
            ; mov BYTE rcx => Registers.a, n_a
            ; mov BYTE rcx => Registers.x, n_x
            ; mov BYTE rcx => Registers.y, n_y
            ; mov BYTE rcx => Registers.p, n_p
            ; mov BYTE rcx => Registers.sp, n_sp
            ; mov WORD rcx => Registers.pc, n_pc
            ; mov QWORD cpu => CPU.cycle, cyc
        );
    }};
}

#[cfg(feature = "debug_features")]
macro_rules! call_trace {
    ($this:ident) => {dynasm!($this.asm
        ; mov n_pc, WORD $this.pc as _
        ;; store_registers!($this)
        ; push rax
        ; push rcx
        ; push rdx
        ; push r9
        ; push r10
        ; push r11
        ; mov rax, QWORD ::cpu::x86_64_compiler::trace as _
        ; mov rcx, rbx //Pointer to CPU is first arg
        ; sub rsp, 0x28
        ; call rax
        ; add rsp, 0x28
        ; pop r11
        ; pop r10
        ; pop r9
        ; pop rdx
        ; pop rcx
        ; pop rax
    );};
}

#[cfg(not(feature = "debug_features"))]
macro_rules! call_trace {
    ($this:ident) => {};
}

#[cfg(feature = "debug_features")]
pub extern "win64" fn trace(cpu: *mut CPU) {
    unsafe { (*cpu).trace() }
}

macro_rules! call_naked {
    ($this:ident, $addr:expr) => {dynasm!($this.asm
        ; mov rax, QWORD $addr as _
        ; call rax
    );};
}

fn trampoline_to_nes(
    f: fn(*mut CPU, *mut [u8; 0x800]) -> (),
    cpu: *mut CPU,
{'fraction_non_alphanumeric': 0.13653001464128844, 'fraction_numerical': 0.038555392874572963, 'mean_word_length': 2.3802061855670105, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5754230', 'n_tokens_mistral': 3253, 'n_tokens_neox': 3039, 'n_words': 782}