I\'ve ported my library x86Lib to mbed. It fully emulates the 8086 processor, but a few things I\'m still working on. Notable missing things are interrupts. Previously I used exceptions for interrupts, but exceptions aren\'t supported with the mbed compiler. It is also quite slow

Dependents:   x86Lib_Tester

Revision:
0:217a7931b41f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/x86Lib.h	Sun Mar 04 08:15:47 2012 +0000
@@ -0,0 +1,452 @@
+/**
+Copyright (c) 2007 - 2010 Jordan "Earlz/hckr83" Earls  <http://www.Earlz.biz.tm>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+   
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+This file is part of the x86Lib project.
+**/
+
+#ifndef X86LIB
+#define X86LIB
+#include <iostream>
+#include <vector>
+#include <stdint.h>
+#include <string>
+#include "mbed.h"
+
+//! The main namespace of x86Lib
+namespace x86Lib{
+
+#ifdef X86LIB_BUILD
+#include <config.h>
+#endif
+
+//!    8086 CPU level
+static const uint32_t CPU086=1;
+//!    186 CPU level
+static const uint32_t CPU186=2|CPU086;
+//!    286 real mode only CPU level
+static const uint32_t CPU286_REAL=4|CPU186; //Only support real mode instructions
+//!    286 CPU level
+static const uint32_t CPU286=8|CPU286_REAL;
+//!    386 real mode only CPU level
+static const uint32_t CPU386_REAL=16|CPU286_REAL; //Only Support real mode instructions
+//!    386 CPU level
+static const uint32_t CPU386=32|CPU386_REAL|CPU286;
+//!    486 CPU level
+static const uint32_t CPU486=64|CPU386;
+//!    Pentium(586) CPU level
+static const uint32_t CPU_PENTIUM=128|CPU486;
+//!    Pentium Pro CPU level
+static const uint32_t CPU_PPRO=256|CPU_PENTIUM;
+//!    Default CPU level
+/*!    CPU_DEFAULT will use the CPU with the most complete emulation
+*/
+static const uint32_t CPU_DEFAULT=0; //this is actually changed internally..
+
+
+
+
+
+
+/**Exceptions...**/
+//!    Exception code for an infinite halt
+static const uint32_t CLIHLT_EXCP=1; //cli/hlt...nothing to do
+//!    Exception code for memory access exception
+static const uint32_t MEM_ACCESS_EXCP=3; //Memory Access Error...(can actually be page fault, or GPF, or stack fault...
+//!    Exception code for triple fault
+/*!    This code is always OR'd with another code
+    so that you can tell what caused the triple fault.
+*/
+static const uint32_t TRIPLE_FAULT_EXCP=0x10000; //Triple fault...This should be ORd with the last exception
+
+
+
+//!    A debug exception
+/*!    This exception should really only be used when debugging.
+    It should be used as throw(Default_excp(__FILE__,__FUNCTION__,__LINE__));
+*/
+class Default_excp{ //Internal only...these should never happen when released...
+
+    public:
+    /*!
+    \param file_ The file name in which the exception occured(use __FILE__)
+    \param func_ The function in which the exception occured(use __FUNCTION__)
+    \param line_ The line number which the excption occured on(use __LINE__)
+    */
+    Default_excp(std::string file_,std::string func_,int line_){
+        file=file_;
+        func=func_;
+        line=line_;
+    }
+    //! The file which the exception was thrown from
+    std::string file;
+    //! The function which the exception was thrown from
+    std::string func;
+    //! The line which the exception was thrown from
+    int line;
+};
+
+//! CPU Panic exception
+/*!    This exception is thrown out of x86CPU if a fatal CPU error occurs,
+    such as a triple fault.
+*/
+class CpuPanic_excp{ //used for fatal CPU errors, such as triple fault..
+
+    public:
+    /*!
+    \param desc_ A text description of the error
+    \param code_ An exception code
+    */
+    CpuPanic_excp(std::string desc_,uint32_t code_){
+        desc=desc_;
+        code=code_;
+    }
+    //!A text description of the error
+    std::string desc;
+    //!An exception code
+    uint32_t code;
+};
+
+//!    Memory error exception
+/*!    This should only be thrown out of the PhysMemory class
+    It is thrown out to tell x86CPU a memory exception has occured.
+    This does not always result in a triple fault.
+    /sa PhysMemory
+*/
+class Mem_excp{ //Exclusively for the Memory Classes, these are caught and then a more appropriate excp is thrown
+    public:
+    /*!
+    \param address_ The address at which had problems being read or written
+    */
+    Mem_excp(uint32_t address_){
+        address=address_;
+    }
+    uint32_t address;
+};
+
+class System_excp{
+    public:
+    System_excp(){}
+};
+
+class x86CPU;
+/**This will be used for memory mapped devices(including memory itself)**/
+class MemoryDevice{
+    public:
+    virtual void Read(uint32_t address,int count,void *buffer)=0;
+    virtual void Write(uint32_t address,int count,void *data)=0;
+    virtual int Readable(uint32_t address,int count){
+        return 1;
+        //This is optional. It is currently not used in the CPU code
+    }
+    virtual int Writeable(uint32_t address,int count){
+        return 1;
+        //This is optional. It is currently not used in the CPU code
+    }
+    virtual inline ~MemoryDevice()=0;
+};
+
+inline MemoryDevice::~MemoryDevice(){}
+void Onx86LibError();
+
+class PortDevice{
+    public:
+    virtual void Read(uint16_t address,int count,void *buffer)=0;
+    virtual void Write(uint16_t address,int count,void *data)=0;
+    virtual inline ~PortDevice()=0;
+};
+
+inline PortDevice::~PortDevice(){}
+
+typedef struct DeviceRange
+{
+    union
+    {
+        class MemoryDevice *memdev;
+        class PortDevice *portdev;
+    };
+    uint32_t high;
+    uint32_t low;
+}DeviceRange_t;
+/*Myk I hate you. This is going to be harder to implement than I thought. lol */
+class MemorySystem{
+    std::vector<DeviceRange_t> memorySystemVector;
+    protected:
+    //! Intended to be used to mark if the address space is locked.
+    volatile uint32_t locked; 
+    public:
+    MemorySystem();
+    void Add(uint32_t low,uint32_t high,MemoryDevice *memdev);
+    void Remove(uint32_t low,uint32_t high);
+    void Remove(MemoryDevice *memdev);
+    int RangeFree(uint32_t low,uint32_t high);
+    void Read(uint32_t address,int count,void *buffer);
+    void Write(uint32_t address,int count,void *data);
+    //! Tells if memory is locked
+    /*!
+    \return 1 if memory is locked, 0 if not locked.
+    */
+    bool IsLocked(){return locked;}
+    //!    Locks the address space
+    void Lock(){
+        while(locked==1){}
+        locked=1;
+    }
+    //! Unlocks the address space
+    void Unlock(){
+        locked=0;
+    }
+    void WaitLock(int haslock){
+        if(haslock==0){
+            while(locked>0){}
+        }
+    }
+};
+
+class PortSystem{
+    DeviceRange_t *list;
+    int count;
+    public:
+    PortSystem();
+
+    void Add(uint16_t low,uint16_t high,PortDevice *portdev);
+    void Remove(uint16_t low,uint16_t high);
+    void Remove(PortDevice *portdev);
+    int RangeFree(uint32_t low,uint32_t high);
+    void Read(uint16_t address,int count,void *buffer);
+    void Write(uint16_t address,int count,void *data);
+};
+
+
+
+//! The struct used to save the current state of x86CPU
+struct x86SaveData{
+    //! General registers
+    uint32_t reg32[8];
+    //! Segment registers
+    uint16_t seg[7];
+    //! Segment register routing(in case of segment overrides)
+    uint8_t seg_route[7];
+    //! Instruction pointer
+    uint32_t eip;
+    //! Which opcode map is currently in use
+    uint32_t opcode_mode;
+    //! Flags register
+    uint16_t freg;
+    //! CPU level
+    uint32_t cpu_level;
+};
+
+};
+
+#ifdef X86LIB_BUILD
+
+#include <x86Lib_internal.h>
+#endif
+
+
+
+namespace x86Lib{
+
+typedef void (x86Lib::x86CPU::*opcode)();
+typedef struct{
+     unsigned char rm:3;
+     unsigned char extra:3;
+     unsigned char mod:2;
+}
+__attribute__((packed))mod_rm16; //this struct is a described mod r/m byte..
+
+//Note, this will re-cache op_cache, so do not use op_cache afterward
+//Also, eip should be on the modrm byte!
+//On return, it is on the last byte of the modrm block, so no advancement needed unelss there is an immediate
+//Also, this will advance EIP upon exiting the opcode(deconstruction)
+class ModRM16{ //This is the best thing I have ever done...
+    //I love this class so much...am I cheating on her? lol
+    protected:
+    bool use_ss;
+    bool op_specific;
+    x86CPU *this_cpu;
+    private:
+    mod_rm16 modrm;
+    inline uint16_t GetRegD(); //This returns the register displacement value
+    inline uint16_t GetDisp();
+    public:
+    inline ModRM16(x86CPU* this_cpu_);
+    inline ~ModRM16();
+    //The r suffix means /r, which means for op_specific=1, use general registers
+    inline uint8_t ReadByter();
+    inline uint16_t ReadWordr();
+    inline uint32_t ReadDword();
+    inline void WriteByter(uint8_t byte);
+    inline void WriteWordr(uint16_t word);
+    inline void WriteDword(uint32_t dword);
+    inline uint8_t GetLength(); //This returns how many total bytes the modrm block consumes
+    inline uint8_t GetExtra(); //Get the extra fied from mod_rm
+    inline uint16_t ReadOffset(); //This is only used by LEA. It will obtain the offset and not dereference it...
+
+}; //I hope that SIB and ModR/M32 will be this good!
+//!    The main CPU control class
+/*!    This class is the complete CPU. That being said, it is quite big
+    and has many functions. It completely emulates the x86 line of CPUs
+*/
+class x86CPU{
+    friend class ModRM16;
+    volatile uint32_t reg32[8];
+    volatile uint16_t *regs16[8];
+    volatile uint8_t *regs8[8];
+    volatile uint16_t seg[7];
+    volatile uint32_t eip;
+    #ifdef X86LIB_BUILD
+    volatile FLAGS freg;
+    #else
+    volatile uint16_t freg;
+    #endif
+    volatile uint8_t op_cache[4];
+    volatile uint8_t ES;
+    volatile uint8_t CS;
+    volatile uint8_t SS;
+    volatile uint8_t DS;
+    volatile uint8_t FS;
+    volatile uint8_t GS;
+    volatile bool string_compares;
+    volatile uint8_t cli_count; //Whenever this is 1, an STI is done.
+    volatile bool int_pending;
+    volatile uint8_t int_number;
+    uint32_t cpu_level;
+    volatile bool busmaster;
+    void Init();
+    protected:
+    //! Do one CPU opcode
+    /*! This should be put in the main loop, as this is what makes the CPU work.
+    */
+    void Cycle();
+    opcode opcodes_16bit[256];
+    opcode *Opcodes;
+    
+    /*!
+    \return 0 if no interrupts are pending
+     */
+    int CheckInterrupts();
+    public:
+#ifdef ENABLE_OPCODE_CALLBACK
+    void (*EachOpcodeCallback)(x86CPU *thiscpu);
+#endif
+    MemorySystem *Memory;
+    PortSystem *Ports;
+    /*!
+    \param cpu_level The CPU level to use(default argument is default level)
+    \param flags special flags to control CPU (currently, there is none)
+    */
+    x86CPU(uint32_t cpu_level=0 ,uint32_t flags=0);
+    /*!
+    \param save The x86SaveData class to restore the cpu to
+    \param flags special flags to control CPU (currently, there is none)
+    */
+    x86CPU(x86SaveData &save,uint32_t flags=0);
+
+    //!Runs the CPU for the specified cyclecount.
+    void Exec(int cyclecount);
+    
+    //! Dump CPU state
+    /*! This will dump cpu state to output. This is mainly used for debugging, as it is not flexible.
+    \param output output stream which to use.
+    */
+    void DumpState(std::ostream &output);
+    //! Cause a CPU interrupt
+    /*! This will cause a CPU interrupt(unless interrupt flag is cleared)
+        Note! This does not resolve IRQs! This takes normal interrupt numbers(0-255)
+    \param num Interrupt number
+    */
+    void Int(uint8_t num);
+    //! Saves CPU state
+    /*! This will completely save the CPU state of the current x86CPU class
+    \param save_data_buffer This should be a free memory area the size of x86SaveData
+    */
+    void SaveState(x86SaveData *save_data_buffer);
+    //!Loads CPU state
+    /*! This will completely reset and reload the cpu state.
+    \param load_data where the x86SaveData is located
+    */
+    void LoadState(x86SaveData &load_data);
+
+    //!Completely resets the CPU
+    void Reset();
+    //~x86CPU();
+    //!Locks the PhysMemory in use, and declares this CPU as busmaster
+    void Lock();
+    //!Unlocks the PhysMemory in use
+    void Unlock();
+    //! Tells if PhysMemory in use is locked
+    /*!
+    \return 1 if PhysMemory in use is locked, otherwise returns 0
+    */
+    bool IsLocked();
+    /*Added after inital multi-branch switch over*/
+    //!Checks if an interrupt is on the stack waiting to be answered.
+    /*!
+    \return 1 if an interrupt is waiting to be answered by the CPU, else, 0.
+    */
+    bool IntPending();
+    
+    /*End public interface*/
+    #ifdef X86LIB_BUILD
+    private:
+    #include <opcode_def.h>
+    #endif
+
+};
+
+
+
+#ifdef X86LIB_BUILD
+#define X86_POST_CPU
+#include "x86Lib_internal.h"
+#undef X86_POST_CPU
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+
+#endif
+