1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Everything that is used by both `asm` and `exec`.

use std::io::{self, Read, Write};

pub mod serialization;
pub mod f80;
pub(crate) mod util;

use serialization::*;

/// The supported op codes for the execution engine.
/// 
/// Nearly all of these have sub-encodings and therefore actually encode many more instructions.
#[derive(Clone, Copy, Debug, FromPrimitive)]
#[repr(u8)]
pub enum OPCode
{
    // x86 instructions

    NOP,
    HLT,
    SYSCALL,

    LEA,
    MOV, CMOVcc, SETcc, XCHG,
    REGOP,

    AND, OR, XOR, TEST, BITWISE,
    ADD, SUB, CMP, CMPZ,
    MULDIV,

    JMP, Jcc, LOOPcc, CALL, RET,
    PUSH, POP,

    INC, DEC, NEG, NOT,

    STRING,

    // x87 instructions

    FINIT,
    FLD,
    
    FADD, FSUB, FSUBR,

    // misc instructions

    DEBUG,
    

    


    // STLDF,
    // FlagManip,

    // BSWAP, BEXTR, BLSI, BLSMSK, BLSR, ANDN, BTx,
    // Cxy, MOVxX,
    // ADXX, AAX,

    // BSx, TZCNT,

    // UD,

    // IO,

    // // x87 instructions

    // FWAIT,
    // FINIT, FCLEX,
    // FSTLDword,
    // FST, FXCH, FMOVcc,

    // FSUB, FSUBR,
    // FMUL, FDIV, FDIVR,

    // F2XM1, FABS, FCHS, FPREM, FPREM1, FRNDINT, FSQRT, FYL2X, FYL2XP1, FXTRACT, FSCALE,
    // FXAM, FTST, FCOM,
    // FSIN, FCOS, FSINCOS, FPTAN, FPATAN,
    // FINCDECSTP, FFREE,

    // // SIMD instructions

    // VPUMOV,

    // VPUFADD, VPUFSUB, VPUFMUL, VPUFDIV,
    // VPUAND, VPUOR, VPUXOR, VPUANDN,
    // VPUADD, VPUADDS, VPUADDUS,
    // VPUSUB, VPUSUBS, VPUSUBUS,
    // VPUMULL,

    // VPUFMIN, VPUFMAX,
    // VPUUMIN, VPUSMIN, VPUUMAX, VPUSMAX,

    // VPUFADDSUB,
    // VPUAVG,

    // VPUFCMP, VPUFCOMI,

    // VPUFSQRT, VPUFRSQRT,

    // VPUCVT,

    // // misc instructions

    // TRANS,

    // DEBUG,
}

/// The system calls recognized by the emulator.
/// 
/// System call codes should be loaded into the `RAX` register.
/// Because 32-bit writes zero the upper 32-bits and syscall codes are unsigned, it suffices to write to `EAX`.
/// Arguments to a system call procedure should be loaded into `RBX`, `RCX`, `RDX` (or partitions thereof, depending on procedure).
#[derive(Clone, Copy, FromPrimitive)]
pub enum Syscall {
    /// Instructs the emulator to end execution in a non-error state with the `i32` return value in `EBX`.
    /// This effectively means the program terminated rather than crashing.
    Exit,
    Read, Write, Seek,
    Break,
}

/// An executable file for use by the [`Emulator`].
/// 
/// Executables are produced by [`link`] by combining one or more [`ObjectFile`].
/// 
/// [`Emulator`]: ../exec/struct.Emulator.html
/// [`link`]: ../asm/fn.link.html
/// [`ObjectFile`]: ../asm/struct.ObjectFile.html
pub struct Executable {
    pub(crate) text_seglen: usize,
    pub(crate) rodata_seglen: usize,
    pub(crate) data_seglen: usize,
    pub(crate) bss_seglen: usize,
    pub(crate) content: Vec<u8>,
}
const EXE_PREFIX: &[u8] = "csx64-exe\0".as_bytes();
impl BinaryWrite for Executable {
    fn bin_write<F: Write>(&self, f: &mut F) -> io::Result<()> {
        EXE_PREFIX.bin_write(f)?;

        self.text_seglen.bin_write(f)?;
        self.rodata_seglen.bin_write(f)?;
        self.data_seglen.bin_write(f)?;
        self.bss_seglen.bin_write(f)?;
        self.content.bin_write(f)
    }
}
impl BinaryRead for Executable {
    fn bin_read<F: Read>(f: &mut F) -> io::Result<Executable> {
        if Vec::<u8>::bin_read(f)? != EXE_PREFIX {
            return Err(io::ErrorKind::InvalidData.into());
        }

        let text_seglen = BinaryRead::bin_read(f)?;
        let rodata_seglen = BinaryRead::bin_read(f)?;
        let data_seglen = BinaryRead::bin_read(f)?;
        let bss_seglen = BinaryRead::bin_read(f)?;
        let content = BinaryRead::bin_read(f)?;

        Ok(Executable { text_seglen, rodata_seglen, data_seglen, bss_seglen, content })
    }
}