ibmpc.cpp

00001 #include "openem.h"
00002 
00003 #include "arch/ibmpc/ibmpc.h"
00004 
00005 #include "arch/ibmpc/dma.h"             // driver for "Direct Memory Access" chip (DMA)
00006 #include "arch/ibmpc/sys_ppi_8255A.h"   // driver for "Programmable Peripheral Interface" (PPI)
00007 
00008 #include <stdio.h>
00009 
00010 namespace IBMPC
00011 {
00012     /* == class Memory: */
00013     Memory::Memory()
00014     :m_pages(NULL), m_pages_count_pow(0), m_page_size_pow(0),
00015      m_addr_page_shift(0), m_addr_page_mask(0), m_addr_clamp_mask(0),
00016      m_state(0)
00017     {
00018     }
00019 
00020     Memory::~Memory()
00021     {
00022         if (m_pages) {
00023             unsigned int total_pages = 1 << m_pages_count_pow;
00024             for (unsigned int page_index=0; page_index < total_pages; ++page_index)
00025                 page_free(page_index);
00026             delete[] m_pages;
00027         }
00028     }
00029 
00030     bool Memory::alloc_pagetable(unsigned int pages_count_pow, unsigned int page_size_pow)
00031     {
00032         /* In debug build check that machine we're running on can support given number of tables
00033            of given size. */
00034         OE_ASSERT(pages_count_pow <= sizeof(unsigned int)*8);
00035         OE_ASSERT(page_size_pow <= sizeof(unsigned int)*8);
00036 
00037         unsigned int page_size      = 1 << page_size_pow;
00038         unsigned int pages_count    = 1 << pages_count_pow;
00039 
00040         // allocate pages table
00041         page_t *new_page_table = new page_t[pages_count];
00042         if (new_page_table == NULL)
00043             return false; /* out of REAL memory =) */
00044 
00045         m_pages_count_pow           = pages_count_pow;
00046         m_page_size_pow             = page_size_pow;
00047 
00048         m_addr_page_shift   = page_size_pow;
00049         m_addr_page_mask    = page_size-1;
00050         m_addr_clamp_mask   = (pages_count * page_size) - 1;
00051 
00052         // free all allocated pages and deallocate old pagetable
00053         if (m_pages) {
00054             for (unsigned int page_index=0; page_index < pages_count; ++page_index)
00055                 page_free(page_index);
00056             delete[] m_pages;
00057         }
00058 
00059         // use new allocated page table
00060         m_pages = new_page_table;
00061 
00062         // initialize pages table
00063         for (unsigned int page_index=0; page_index < pages_count; ++page_index) {
00064             page_t &page = m_pages[page_index];
00065             page.flags = 0;
00066             page.mem   = NULL;
00067         }
00068 
00069         return true; // success
00070     }
00071 
00072     void    Memory::page_set_perm(unsigned int page_index, uint32 perm)
00073     {
00074         OE_ASSERT(page_index < (unsigned int)(1 << m_pages_count_pow)); /* assert page index is < total pages count */
00075         OE_ASSERT(perm & PAGE_ALLOCATED); /* user must NEVER specify this bit */
00076         OE_ASSERT(m_pages);
00077         perm &= ~PAGE_ALLOCATED; /* strip the bit fro user's perm (if he specified it and it was not caught by the OE_ASSERT above in RELEASE build) */
00078         page_t &page = m_pages[page_index];
00079         page.flags = (page.flags & PAGE_ALLOCATED) | perm; /* note: preserve the PAGE_ALLOCATED bit in page.flags */
00080     }
00081 
00082     uint32  Memory::page_get_perm(unsigned int page_index)
00083     {
00084         OE_ASSERT(page_index < (unsigned int)(1 << m_pages_count_pow)); /* assert page index is < total pages count */
00085         OE_ASSERT(m_pages);
00086         page_t &page = m_pages[page_index];
00087         return page.flags;
00088     }
00089 
00090     bool Memory::page_alloc(unsigned int page_index)
00091     {
00092         OE_ASSERT(page_index < (unsigned int)(1 << m_pages_count_pow)); /* assert page index is < total pages count */
00093         OE_ASSERT(m_pages);
00094         uint8   *new_page_mem = new uint8[1 << m_page_size_pow]; // real memory buffer of "page_size" bytes
00095         if (new_page_mem == NULL) {
00096             return false; // failure (out of REAL memory =)
00097         }
00098         page_free(page_index);
00099         page_t &page = m_pages[page_index];
00100         page.flags = PERM_READABLE | PERM_WRITEABLE | PAGE_ALLOCATED;
00101         page.mem = new_page_mem;
00102         return true; // success
00103     }
00104 
00105     void Memory::page_attach(unsigned int page_index, Device *driver)
00106     {
00107         OE_ASSERT(page_index < (unsigned int)(1 << m_pages_count_pow)); /* assert page index is < total pages count */
00108         OE_ASSERT(driver); /* Assert pointer to driver is not NULL */
00109         OE_ASSERT(m_pages);
00110         page_free(page_index); /* try to free/deallocate page which ensures page is not allocated */
00111         page_t &page = m_pages[page_index];
00112         page.flags = PERM_READABLE | PERM_WRITEABLE;
00113         page.driver = driver;
00114     }
00115 
00116     void Memory::page_free(unsigned int page_index)
00117     {
00118         OE_ASSERT(page_index < (unsigned int)(1 << m_pages_count_pow)); /* assert page index is < total pages count */
00119         OE_ASSERT(m_pages);
00120         page_t &page = m_pages[page_index];
00121         if (page.flags & PAGE_ALLOCATED)
00122             delete[] page.mem;
00123         page.flags = 0;
00124         page.mem = NULL;
00125     }
00126 
00127     // Methods below are called by the CPU at high rates, and so the should be REALLY FAST.
00128     uint8  Memory::mem_read8(unsigned int addr)
00129     {
00130         OE_ASSERT(m_pages);
00131         addr &= m_addr_clamp_mask; /* make address "wrap around" the whole memory space */
00132         unsigned int page_index = (addr >> m_addr_page_shift);  /* extract page index */
00133         unsigned int offset     = (addr & m_addr_page_mask);    /* extract offset in that page */
00134         page_t &page = m_pages[page_index]; /* get reference to page descriptor structure */
00135         if (page.flags & PERM_READABLE) {
00136             m_state = 0; /* indicate succesful reading memory */
00137             return (page.flags & PAGE_ALLOCATED) ? page.mem[offset] : page.driver->mem_read8(addr);
00138         }
00139         else {
00140             /* fault */
00141             m_state = STATE_FAULT; /* indicate fault reading memory */
00142             return 0;
00143         }
00144     }
00145 
00146     void   Memory::mem_write8(unsigned int addr, uint8 data)
00147     {
00148         OE_ASSERT(m_pages);
00149         addr &= m_addr_clamp_mask; /* make address "wrap around" the whole memory space */
00150         unsigned int page_index = (addr >> m_addr_page_shift);  /* extract page index */
00151         unsigned int offset     = (addr & m_addr_page_mask);    /* extract offset in that page */
00152         page_t &page = m_pages[page_index]; /* get reference to page descriptor structure */
00153         if (page.flags & PERM_WRITEABLE) {
00154             m_state = 0; /* indicate succesful reading memory */
00155             if (page.flags & PAGE_ALLOCATED)
00156                 page.mem[offset] = data;
00157             else
00158                 page.driver->mem_write8(addr, data);
00159         }
00160         else {
00161             /* fault */
00162             m_state = STATE_FAULT | STATE_DIR; /* indicate fault writing memory */
00163         }
00164     }
00165 
00166     /* == class IO */
00167     IO::IO()
00168     :m_state(0)
00169     {
00170     }
00171 
00172     IO::~IO()
00173     {
00174     }
00175 
00176     void    IO::port_set_perm(unsigned int port, uint32 perm)
00177     {
00178         // look for port in portmap
00179         std::map<unsigned int, port_spec_t>::iterator found = m_port_map.find(port);
00180         OE_ASSERT(found != m_port_map.end()); // ASSERT in DEBUG build if port does not exist.
00181         found->second.flags = perm;
00182     }
00183 
00184     uint32  IO::port_get_perm(unsigned int port)
00185     {
00186         // look for port in portmap
00187         std::map<unsigned int, port_spec_t>::const_iterator found = m_port_map.find(port);
00188         if (found != m_port_map.end())
00189             return found->second.flags;
00190         else
00191             return 0; // return 0 for ports not attached to any driver.
00192     }
00193 
00194     bool    IO::port_attach(unsigned int port_index, Device *driver)
00195     {
00196         OE_ASSERT(driver != NULL);
00197         // look for port in portmap
00198         std::map<unsigned int, port_spec_t>::const_iterator found = m_port_map.find(port_index);
00199         if (found == m_port_map.end() || found->second.driver == driver) {
00200             port_spec_t port_spec;
00201             port_spec.flags = PERM_READABLE|PERM_WRITEABLE;
00202             port_spec.driver = driver;
00203             m_port_map[port_index] = port_spec;
00204         }
00205         else
00206             return false; // fail: this port is already attached to other driver
00207         return true; // success
00208     }
00209 
00210     bool    IO::port_free(unsigned int port_index)
00211     {
00212         // look for port in portmap
00213         std::map<unsigned int, port_spec_t>::const_iterator found = m_port_map.find(port_index);
00214         if (found == m_port_map.end())
00215             return false; // failed: port is not attached to any driver
00216         else
00217             m_port_map.erase(port_index);
00218         return true; // success
00219     }
00220 
00221     // Methods below can be called by the CPU at high rates, and so they should be FAST.
00222     uint8  IO::port_read8(unsigned int port_index)
00223     {
00224         // look for port in portmap
00225         std::map<unsigned int, port_spec_t>::const_iterator found = m_port_map.find(port_index);
00226         if (found == m_port_map.end()) {
00227             // port is not allocated!
00228             fprintf (stderr, "[*] Attempt to read BYTE from unreserved port 0x%04X.\n", port_index);
00229             m_state = STATE_FAULT; // indicate fault reading
00230             return 0;
00231         }
00232         else
00233             return found->second.driver->port_read8(port_index);
00234         m_state = 0; // indicate success
00235     }
00236     void   IO::port_write8(unsigned int port_index, uint8 value)
00237     {
00238         // look for port in portmap
00239         std::map<unsigned int, port_spec_t>::const_iterator found = m_port_map.find(port_index);
00240         if (found == m_port_map.end()) {
00241             // port is not allocated!
00242             fprintf (stderr, "[*] Attempt to write BYTE 0x%02X to unreserved port 0x%04X.\n", value, port_index);
00243             m_state = STATE_FAULT | STATE_DIR; // indicate fault writing
00244         }
00245         else
00246             found->second.driver->port_write8(port_index, value);
00247         m_state = 0; // indicate success
00248     }
00249 
00250     /* == struct Core */
00251     Core::Core()
00252     {
00253         cpu.attach(this);
00254     }
00255 
00256     Core::~Core()
00257     {
00258     }
00259 
00260     /* == class Device */
00261     Device::Device()
00262     {
00263     }
00264 
00265     Device::~Device()
00266     {
00267     }
00268 
00269     /* Sets name of this virtual driver */
00270     void Device::set_name(std::string name)
00271     {
00272         m_name = name;
00273     }
00274 
00275     /* Returns name of this virtual driver */
00276     std::string Device::get_name()
00277     {
00278         return m_name;
00279     }
00280 
00281     /* == class VirtualMachine */
00282 
00283     /* helper function to copy raw data buffer into emulated paged memory */
00284     void copy_to_mem(uint8 *buf, int buf_size, Memory &mem, unsigned int addr)
00285     {
00286         for (int i=0; i<buf_size; i++) {
00287             mem.mem_write8(addr, buf[i]);
00288             addr++;
00289         }
00290     }
00291 
00292     /* helper function which loads 8086 ROM into simulated memory */
00293     void load_ibmpc_rom(char *rom_filename, Memory &mem)
00294     {
00295         // Load ROM into memory at FE00:0000
00296         FILE *stream = fopen(rom_filename, "rb");
00297         if (stream == NULL)
00298             throw "Could not open ROM file";
00299 
00300         uint32 flat_addr = 0xFE000; // BIOS ROM image starts at FE00:0000 in memory
00301         int value = fgetc(stream);
00302         do {
00303             mem.mem_write8(flat_addr, value);
00304             flat_addr++;
00305             value = fgetc(stream);
00306         } while (!feof(stream));
00307 
00308         fclose(stream);
00309     }
00310 
00311     VirtualMachine::VirtualMachine()
00312     :m_mem_total_pages(256), m_was_error(false)
00313     {
00314         // == Prepare some virtual memory pages
00315         m_core.mem.alloc_pagetable(8, 12); // use 2^12 = 4kb pages
00316 
00317         // allocate real memory for all the pages
00318         // give permissions to read and write to all the pages
00319         for (unsigned int page_index=0; page_index < m_mem_total_pages; ++page_index) {
00320             m_core.mem.page_alloc(page_index);
00321         }
00322 
00323         // Attach essential devices such as DMA and PIT controllers to the core.
00324         // note: this is similar to on-board devices.
00325 
00326         // DMA onboard device
00327         IBMPC::VDD_DMA  *vdd_dma = new VDD_DMA;
00328         m_core.attach(vdd_dma);     // attach to core
00329         register_driver(vdd_dma);   // register
00330 
00331         // Programmable interrupt timer onboard device
00332         IBMPC::VDD_SYS_PPI_8255A    *vdd_ppi = new IBMPC::VDD_SYS_PPI_8255A;
00333         m_core.attach(vdd_dma);     // attach to core
00334         register_driver(vdd_ppi);
00335 
00336         // load IBM_PC ROM binary image from file into simulated memory
00337         load_ibmpc_rom("..\\..\\bin\\BIOS.BIN", m_core.mem);
00338     }
00339 
00340     VirtualMachine::~VirtualMachine()
00341     {
00342         while (m_drivers.size())
00343         {
00344             Device *driver = *(m_drivers.end()-1);
00345             driver->done();
00346             m_drivers.pop_back();
00347         }
00348     }
00349 
00350     void VirtualMachine::register_driver(Device *driver)
00351     {
00352         for (unsigned int i=0; i<m_drivers.size(); ++i) {
00353             if (m_drivers[i] == driver) {
00354                 fprintf (stderr, "[*] Driver '%s' attempted to register with virtual system twice.\n", driver->get_name().c_str());
00355             }
00356         }
00357         m_drivers.push_back(driver);
00358         driver->set_core(&m_core);
00359         driver->init();
00360     }
00361 
00362     void VirtualMachine::unregister_driver(Device *driver)
00363     {
00364         for (std::vector<Device*>::iterator i=m_drivers.begin(); i!=m_drivers.end(); ++i) {
00365             if (*i == driver) {
00366                 (*i)->done();
00367                 driver->done();
00368                 driver->set_core(NULL);
00369                 m_drivers.erase(i);
00370                 return;
00371             }
00372         }
00373         // if we reached here we din't find the driver in the drivers list!
00374         fprintf(stderr, "[*] Attempt to detach driver '%s', which is not registered with this virtual system.\n", driver->get_name().c_str());
00375     }
00376 
00377     int VirtualMachine::drivers_count()
00378     {
00379         return m_drivers.size();
00380     }
00381 
00382     Device* VirtualMachine::get_driver(int index)
00383     {
00384         return m_drivers.at(index);
00385     }
00386 
00387     Device* VirtualMachine::find_driver(std::string name)
00388     {
00389         for (unsigned int i=0; i<m_drivers.size(); ++i) {
00390             if (m_drivers[i]->get_name() == name)
00391                 return m_drivers[i];
00392         }
00393         return NULL;
00394     }
00395 
00396     void VirtualMachine::reset()
00397     {
00398         fprintf(stderr, "[*] Machine reset.\n");
00399         m_core.cpu.reset(); // parameter tells whether to enable debug tracing/disassembly
00400     }
00401 
00402     void VirtualMachine::run(unsigned int cycles)
00403     {
00404         // set error if returned "number of cycles remaining to execute" is greater than zero.
00405         m_was_error = m_core.cpu.cycle(cycles) > 0 ? true : false;
00406     }
00407 
00408     bool VirtualMachine::was_error() const
00409     {
00410         return m_was_error;
00411     }
00412 
00413     void VirtualMachine::set_debug(bool debug_on, bool debug_pause)
00414     {
00415         m_core.cpu.set_dbg(debug_on, debug_pause);
00416     }
00417 
00418 
00419 } // end namespace IBMPC

Generated on Sat Sep 9 03:50:43 2006 for Openem APIs by  doxygen 1.4.7