fn emit_call<C: Callable>(&mut self, callee: C, args: &[u64]) {
        debug!("call {:p} ({})", callee.get_addr() as *const (), utils::hexdump(args));
        assert_eq!(C::param_count() as usize, args.len());

        // Write registers to the `ChipState`, so the callee can access consistent data.
        for reg in 0..16 {
            self.spill_chip8_reg(reg);
        }

        // Save caller-saved registers in use
        // FIXME This differs between the 2 calling conventions, but if we save more than we need
        // to, it doesn't matter for correctness.
        self.emit_rsi_save();

        // Align the stack to next 16-byte boundary below current `rsp`.
        // FIXME This depends on the ABI.
        // Note that if we were to pass args via stack, we'd have to take their size into account.
        // First, put at least the low 4 bits into some callee-save reg so we can restore them.
        self.emit_raw(&[0x40, 0x88, 0xE3]);         // `mov bl, spl`
        self.emit_raw(&[0x48, 0x83, 0xE4, 0xF0]);   // `and rsp, 0xfffffffffffffff0`

        // Emit all arguments into the correct registers by emitting 64-bit immediate loads for
        // them.
        let regs = calls::get_arg_reg_codes();
        if regs.len() < args.len() {
            panic!("not enough registers for call arguments (got {} args, but {} regs)",
                args.len(), regs.len());
        }

        for (&arg, &reg) in args.iter().zip(regs) {
            self.emit_load_imm64(reg, arg);
        }

        // Emit the actual call. `call` doesn't support 64-bit immediates, so we load the 64-bit
        // address into `rax` and call that.
        self.emit_raw(&[0x48, 0xb8]);   // `movabs rax, <ADDRESS>`
        self.code_buffer.write_u64::<LittleEndian>(callee.get_addr()).unwrap();
        self.emit_raw(&[0xff, 0xd0]);   // `call rax`

        // Restore the low bits of `rsp` so our stack frame is sane again (else, `pop` would break)
        self.emit_raw(&[0x40, 0x08, 0xDC]); // `or spl, bl`

        self.emit_rsi_restore();
    }
/// Emits code for a function call, passing a number of integer arguments.
///
/// Before a function call can be performed, we need write all allocated registers to the
/// `ChipState` and deallocate them to ensure consistency.
Emits code for a function call, passing a number of integer arguments.
Before a function call can be performed, we need write all allocated registers to the
`ChipState` and deallocate them to ensure consistency.
Emits code for a function call passing a number of integer arguments . Before a function call can be performed we need write all allocated registers to the ` ChipState ` and deallocate them to ensure consistency .
fn emit_call<C: Callable>(&mut self, callee: C, args: &[u64]) {
        debug!("call {:p} ({})", callee.get_addr() as *const (), utils::hexdump(args));
        assert_eq!(C::param_count() as usize, args.len());
        // Write registers to the `ChipState`, so the callee can access consistent data.
        for reg in 0..16 {
            self.spill_chip8_reg(reg);
        }
        // Save caller-saved registers in use
        // FIXME This differs between the 2 calling conventions, but if we save more than we need
        // to, it doesn't matter for correctness.
        self.emit_rsi_save();
        // Align the stack to next 16-byte boundary below current `rsp`.
        // FIXME This depends on the ABI.
        // Note that if we were to pass args via stack, we'd have to take their size into account.
        // First, put at least the low 4 bits into some callee-save reg so we can restore them.
        self.emit_raw(&[0x40, 0x88, 0xE3]);         // `mov bl, spl`
        self.emit_raw(&[0x48, 0x83, 0xE4, 0xF0]);   // `and rsp, 0xfffffffffffffff0`
        // Emit all arguments into the correct registers by emitting 64-bit immediate loads for
        // them.
        let regs = calls::get_arg_reg_codes();
        if regs.len() < args.len() {
            panic!("not enough registers for call arguments (got {} args, but {} regs)",
                args.len(), regs.len());
        }
        for (&arg, &reg) in args.iter().zip(regs) {
            self.emit_load_imm64(reg, arg);
        }
        // Emit the actual call. `call` doesn't support 64-bit immediates, so we load the 64-bit
        // address into `rax` and call that.
        self.emit_raw(&[0x48, 0xb8]);   // `movabs rax, <ADDRESS>`
        self.code_buffer.write_u64::<LittleEndian>(callee.get_addr()).unwrap();
        self.emit_raw(&[0xff, 0xd0]);   // `call rax`
        // Restore the low bits of `rsp` so our stack frame is sane again (else, `pop` would break)
        self.emit_raw(&[0x40, 0x08, 0xDC]); // `or spl, bl`
        self.emit_rsi_restore();
    }
fn emit_call < C : Callable > ( & mut self , callee : C , args : & [ u64 ] ) { debug ! ( " " callee get_addr ( ) as const ( ) utils hexdump ( args ) ) ; assert_eq ! ( C param_count ( ) as usize args len ( ) ) ; // Write registers to the `ChipState`, so the callee can access consistent data. for reg in 0 .. 16 { self . spill_chip8_reg ( reg ) ; } // Save caller-saved registers in use // FIXME This differs between the 2 calling conventions, but if we save more than we need // to, it doesn't matter for correctness. self . emit_rsi_save ( ) ; // Align the stack to next 16-byte boundary below current `rsp`. // FIXME This depends on the ABI. // Note that if we were to pass args via stack, we'd have to take their size into account. // First, put at least the low 4 bits into some callee-save reg so we can restore them. self . emit_raw ( & [ 0x40 , 0x88 , 0xE3 ] ) ; // `mov bl, spl` self . emit_raw ( & [ 0x48 , 0x83 , 0xE4 , 0xF0 ] ) ; // `and rsp, 0xfffffffffffffff0` // Emit all arguments into the correct registers by emitting 64-bit immediate loads for // them. let regs = calls :: get_arg_reg_codes ( ) ; if regs . len ( ) < args . len ( ) { panic ! ( " " args len ( ) regs len ( ) ) ; } for ( & arg , & reg ) in args . iter ( ) . zip ( regs ) { self . emit_load_imm64 ( reg , arg ) ; } // Emit the actual call. `call` doesn't support 64-bit immediates, so we load the 64-bit // address into `rax` and call that. self . emit_raw ( & [ 0x48 , 0xb8 ] ) ; // `movabs rax, <ADDRESS>` self . code_buffer . write_u64 :: < LittleEndian > ( callee . get_addr ( ) ) . unwrap ( ) ; self . emit_raw ( & [ 0xff , 0xd0 ] ) ; // `call rax` // Restore the low bits of `rsp` so our stack frame is sane again (else, `pop` would break) self . emit_raw ( & [ 0x40 , 0x08 , 0xDC ] ) ; // `or spl, bl` self . emit_rsi_restore ( ) ; }
Emits code for a function call, passing a number of integer arguments.
Emits code for a function call passing a number of integer arguments .
&mut self
callee
C
args
&[u64]
&mut self
callee
C
args
&[u64]