Skip to content
Snippets Groups Projects
  1. Jan 19, 2022
    • Geo Ster's avatar
      All round improvements/bug fixes · 05efa5ed
      Geo Ster authored
      * This commit doesn't implement any specific component or behaviour,
      rather it fixes another set of bugs that I found during testing.
      05efa5ed
  2. Jan 16, 2022
    • Geo Ster's avatar
      Implement SIO2/PAD communication and CDVD commands · 5a39532b
      Geo Ster authored
      * Implementing IOP components is probably the hardest task in a PS2
      emulator, even compared to complex chips like the VUs. That is because
      almost every single component in the IOP is completetly undocumented
      so emulators have to make assumptions and reverse engineer them to
      figure out what they do. This commit adds support three new IOP components.
      
      * The DVD drive is quite simple in concept. It accepts two types of
      commands, S commands (synchronous) and N commands (asynchronous).
      Async commands are used mainly for seeks and reads, so the CPU
      doesn't have to wait for the drive to fetch the data. On the other
      hand S commands complete instantly and are used for more misc
      operations.
      
      * Both types of commands contain three registers; one stores
      the current command, the other acts as the status register and
      the third register either gives the current command output
      (read) or stores the parameters of the command (write).
      
      * SIO2 is very peculiar since very little is actually known about it.
      It is responsible for managing peripherals like the memory card or
      the DualShock controller. The CPU first sends a peripheral byte that
      informs about the target peripheral, then a command and waits for
      a reply. The SEND3 array contains the command size for each SIO2 command.
      
      * Since in this case the gamepad gets accessed, that needs to be implemented.
      To prevent this commit message from getting extermely long, because the
      gamepad is very complex in its operation, I will talk about it in another
      time. Check out the new txt file in the docs folder for more info
      5a39532b
  3. Jan 10, 2022
    • Geo Ster's avatar
      DMA: Add SPU2 interrupts · df2591e9
      Geo Ster authored
      * Next the IOP DMA controller initiates an SPU2
      transfer. However similarly to SIF0/SIF1 when a
      transfer finishes, an irq is generated in the SPU
      STAT register.
      
      * After searching on github for any details on this particular
      register, I found a hacky snippet DobieStation that seems to
      properly initialize the SPU2 (according to the logs) so I'll use
      it here.
      df2591e9
  4. Jan 09, 2022
    • Geo Ster's avatar
      Fix some additional bugs, OSDSYS now loading! · 7a4b8e93
      Geo Ster authored
      * Fixed bug when writing to DICR/DICR2 flag field
      * Fixed definition of the DMAC tag
      * SIF0 doesn't use the id field, apparently?
      * Allow writing to some specific regions of the BIOS
      
      With these changes OSDSYS has now started loading! The BIOS
      is initiallizing the SPU2, probably to play that boot up charm
      the PS2 does.
      7a4b8e93
  5. Jan 08, 2022
    • Geo Ster's avatar
      Optimize PLZCW instruction · 16464a36
      Geo Ster authored
      * The original code was using brute force to ensure correct
      results but was very expensive, doing 2 * 30 = 60 loops. So I
      rewrote it to use __builtin_clz to count the bits instead, leading
      to noticeable speedup.
      16464a36
    • Geo Ster's avatar
      Major bug fixes + EE <-> IOP communication support · 073cba80
      Geo Ster authored
      * This commit was intended to start the DMA implementation, but
      quickly turned to a tedious bug fixing journey. So in return for
      having my soul drained from all the debugging,
      the following bugs were fixed:
      
      * Fixed bug that caused incorrect IOP and EE timer writes
      * Fixed bug that caused interrupts to not trigger
      (NOTE: This is an error on ps2tek and more specifically with the
      use of the I_CTRL register)
      * Fixed handling of some virtual addresses (0x2*)
      (The program doesn't notify me when out of bounds writes
      happen for some reason?)
      * Fixed handling of DICR2 register in the IOP DMA and
      * Fixed INT1 interrupts running endlessly
      * Fixed incorrect int1_pending field position in COP0 status
      
      In addition to the above bugfixes some new additions needed to be
      made to make DMA work:
      
      * Added VBLANK ON/OFF interrupts
      * Added INT1 interrupt detection
      * Added some new EE instructions related to exception handling.
      * Added syscall support
      
      So after all of that, at least the program started running normally
      again and I could begin with the DMA implementation. SIF0/SIF1 are
      quite confusing since nowhere in ps2tek does it mention where that data
      comes from. Thankfully I came across this linux kernel commit [1] which
      specified that there exists a bidirection FIFO in the SIF that sends
      data from either side to the other.
      
      There are problems however. The EE DMAC works exclusively with qwords
      or 4 word packets, while the IOP DMA only sends words. Also the DMAC
      tags are 128bit while the IOP tags are 64bit. So even though I would
      have liked to use qwords in the fifo, I was forced to switch to 32bit
      values to have finer control on how much data gets used.
      
      Other things to note are that the EE/IOP can start DMA transfer
      without the FIFO having any data. In that case the transfer stalls
      and waits until the other side starts filling it. A typical sequence
      goes as follows:
      
      1. The EE starts a SIF0 transfer, waiting for the IOP to send data.
      2. The IOP starts a SIF0 transfer, pushing data from its RAM to the fifo.
      3. The EE notices and starts taking that data to form a DMA tag
      (when the fifo reaches a size bigger than 4 words of course)
      5. The transfer completes and INT1 is asserted.
      6. An exception is triggered and the exception handler checks the
      data that was written.
      
      If any of those steps above go wrong (IOP sends too much data, EE receives
      it too early, the data is not written to the correct address) the whole
      process will be stuck in an infinite loop. Now figure out what happened
      in between the 1000+ instructions that were executed...
      At least it seems to be working quite well now.
      
      Note that this is not the final implementation of the DMA. Normally only
      one channel can trasfer data in a single cycle while in our current
      implementation all the channels are checked. I have a request system
      in mind, that should fix this but right now I really want some graphics
      on screen as fast as possible.
      
      [1] https://patchwork.kernel.org/project/linux-mips/patch/fb79dab2db2bfa9a06e96c211d27423d0c51399c.1567326213.git.noring@nocrew.org/
      073cba80
  6. Jan 05, 2022
    • Geo Ster's avatar
      Reduce logging in some areas · 39407eea
      Geo Ster authored
      * The read function of the SIF causes massive logspam, especially when
      component poll a specific register non-stop. Removing the logging also
      makes the code much faster
      
      * Some syscalls, especially 0x7A get called really often and fill up
      the whole console really quickly. Don't log unknown syscalls and add
      ability to disable exception logging. Now the logs are much cleaner
      39407eea
    • Geo Ster's avatar
      Fix misc CPU bugs · e1de8e78
      Geo Ster authored
      * The ERET instruction doesn't have a branch delay slot
      so the direct_jump function must be used
      
      * When skipping the branch delay slot don't return as that
      skips all the cycles left to execute
      
      * The CPU has now entered an infinite loop waiting for data from
      the 0x8c440 address. This probably means it's time for DMA
      e1de8e78
    • Geo Ster's avatar
      Implement syscall support · 1ead43d3
      Geo Ster authored
      * Finally we can start executing some kernel functions now. Executing
      syscalls is very straightforward; just create an exception of type 8
      and the handler will handle the rest. This is the first time the exception
      handler is used on the EE so I'm bit anxious whether it will be bug free.
      
      * Log each syscall and print its name instead of just a simple number. Known
      syscalls along with their ids are on ps2devwiki [1]
      
      [1] https://playstationdev.wiki/ps2devwiki/index.php?title=Syscalls
      1ead43d3
    • Geo Ster's avatar
      Improve COP0 instruction decoding · f427f844
      Geo Ster authored
      * The COP0 code was hacky and didn't report unknown TLB instructions.
      This caused an infinite loop when the program was calling ERET to exit
      the current syscall without me knowing.
      
      * Fix up the code a little and add the DI and ERET instructions
      f427f844
    • Geo Ster's avatar
      Only log instructions from the kernel and beyond · 3a7a6b80
      Geo Ster authored
      Shrinks the log file size from 4GB to 2GB
      3a7a6b80
  7. Jan 04, 2022
    • Geo Ster's avatar
      Rework IOP DMA reads/writes · 6592212f
      Geo Ster authored
      * The old code was pretty complicated and bad. Reorganize the register
      structs and simplify the reading/writing process.
      6592212f
    • Geo Ster's avatar
      Don't write out of bounds · 668d5417
      Geo Ster authored
      * VU0 tries to access address 0x4210 which maps to the VU1 I/O registers.
      Currently we don't have VU1 support so abort when trying to write to these
      locations
      668d5417
    • Geo Ster's avatar
      Fix bugged EE timer interrupts · 0cde5eb1
      Geo Ster authored
      * The interrupt raised flags should be cleared when mode is written. In
      addition the raised flag should not get set unless the interrupt has actually
      been fired
      0cde5eb1
    • Geo Ster's avatar
      Switch to cycle addition for ticking components · f1bf0dfc
      Geo Ster authored
      * Instead of ticking each component, each cycle which is expensive
      most emulators tick each components for specific cycles until
      they complete a frame which is rendered in the end. This decreases the
      accuracy by a tiny margin but no PS2 game can be cycle dependant enough
      to notice.
      
      * The timing information was sourced from my PS2 Slim with the hardware
      test that was uploaded in a recent commit.
      f1bf0dfc
    • Geo Ster's avatar
      Add special write to SIF_CTRL · f0dbc8c8
      Geo Ster authored
      * This register is pretty undocumented even though it's crucial
      for EE <-> IOP synchronization. I asked a dev PCSX2 dev about this
      and I was linked the code PCSX2 uses, so I will use it here as well.
      f0dbc8c8
    • Geo Ster's avatar
      Add timing hardware test · 9a677135
      Geo Ster authored
      * From now on, sometimes I have to rely on hardware tests to accurately
      measure timings or other info the emulator needs. This test was originally
      written by refraction for use with DobieStation. I touched up the code a bit
      and wrote a small build script for anyone who wants to run it on their own
      console.
      9a677135
  8. Jan 03, 2022
  9. Jan 02, 2022
    • Geo Ster's avatar
      Implement many new shift instructions · c24d6d32
      Geo Ster authored
      * Add support for a variety of new bit and memory shifting
      instructions. These include LDL/LDR/SDL/SDR/DSRL32/DSRAV etc.
      
      * Fix a small bug in SRA where the value would only be written
      to the lower word instead of the dword.
      
      * Finally the BIOS has finished the initialization phase now!
      
          # Initialize Done.
      
      * Now the IOP has entered an inifite loop waiting for the EE to wake
      it up. This will be added in the upcoming SIF implementation.
      c24d6d32
    • Geo Ster's avatar
      Add COP1 support · e3d892bb
      Geo Ster authored
      * There's not much to explain on the COP1, it's just an FPU that
      has similar quirks to the VUs. Currently very little instructions
      are required from it
      
      * Also capture IOP console output, by handling calls to the putc function
      Code "borrowed" from DobieStation [1]
      
      [1] https://github.com/PSI-Rockin/DobieStation/blob/40c9b01f50bc04debad809b3bad1cf51e7e7e495/src/core/emulator.cpp#L1201
      e3d892bb
    • Geo Ster's avatar
      Started work on the IPU · e5095670
      Geo Ster authored
      * For now it's only a basic class that handles reads/writes
      to the IPU.
      e5095670
    • Geo Ster's avatar
      Introduce VIF0/VIF1 to the emulator · ab346ac0
      Geo Ster authored
      * Next up the BIOS starts initializing the Vector Interface (VIF), which
      is used to communicate with VU1 and VU0 (when it's in micro mode). It has
      its own register set which is easy to implement as shown and a fifo, similar
      to the GIF FIFO. The fifo receives 128bit qwords sequentially. Each VIF "packet",
      starts with a 32bit header that tells the VIF which command to execute.
      Some headers are standalone, meaning that another command header will follow them,
      while for others some 32bit data words will be sent after (for exampe STROW/STCOL).
      
      * In the future I will change this to be an actual fifo structure, because
      some fifos in the PS2 can be blocked, meaning that they queue up written data
      until the component that blocked them, lifts the block. For now though this will
      do.
      
      * Also allow writing to code and data memory for VU0 to reduce the logspam a little
      
      Sources:
      https://psi-rockin.github.io/ps2tek/#vif
      ab346ac0
  10. Jan 01, 2022
    • Geo Ster's avatar
      VU0: Add basic ADD/SUB/MUL instructions · 787df2b4
      Geo Ster authored
      * The bios uses them to initialize the VU0 registers. For now I don't
      check for overflow but I think it's going to become necessary in the
      near future.
      787df2b4
    • Geo Ster's avatar
      Report ready DVD status to CDVDMAN · 71aab146
      Geo Ster authored
      * Early on the IOP continiously reads from 0x1F402005 which
      is the N command status register [1], to know if it's
      ready or not. So it be safe, let's report that the drive is ready
      by returning 0x40 (bit 6 set)
      
      [1] https://psi-rockin.github.io/ps2tek/#cdvdioports
      71aab146
    • Geo Ster's avatar
      Add VU0 support · b591d341
      Geo Ster authored
      * The VUs are custom made SIMD processors used to accelerate
      math operations on vectors and matrices. This doesn't seem that
      bad but in reality they are probably the hardest piece of hardware
      on the PS2 to emulate correctly. That is for two reasons:
      
      1. Not much documentation
      2. Complex and confusing pipeline
      
      The first it pretty self explanatory. However the second reason,
      the pipeline is what makes them so hard. Normally, even in LLE
      emulators we don't care about the internal pipeline of the chips,
      as it doesn't affect the result of the instructions themselves,
      it just makes them run faster. The CPU doesn't expose its pipeline
      to the target program.
      
      Some architectures are different. MIPS for example has branch delay
      slots which in reality are a pipeline quirk. Generally the more
      pipeline quirks you expose to the program the more complex it is
      to emulate correctly. The VUs basically expose their full pipeline...
      
      For now we only support a portion of the macro mode instruction set.
      The pipeline is going to come when the VU starts executing micro programs
      and when I figure out how it works...
      b591d341
  11. Dec 29, 2021
    • Geo Ster's avatar
      Lay the foundation for the DMAC · 690d9651
      Geo Ster authored
      * The BIOS now continues by initializing the DMA Controller.
      This is one of the most important hardware components of the PS2,
      as it assists the EE with transfering data where it needs to be. I've
      even read that at times it can do more work than the EE itself.
      
      * Since the DMAC isn't used at this stage, we only really have to
      implement its registers and reads/writes to them, which is pretty easy.
      However one register D_CTRL is a bit quirky in a sense that writes to it
      clear/reverse its bits, not overwrite them.
      
      * To emulate this, an additional struct is added to the register unions
      and bitwise operators are used to write to the upper and lower parts of
      the register appropriately. You can look into the source code for more details.
      
      * This allows the EE to start initializing the VU1 which is quite exciting!
      690d9651
    • Geo Ster's avatar
      Fix small timer bug · 1f7dedbe
      Geo Ster authored
      1f7dedbe
    • Geo Ster's avatar
      Implement NOR/SRAV EE instructions · c250c5ed
      Geo Ster authored
      * Allows us to progress futher into the initialization phase
      c250c5ed
    • Geo Ster's avatar
      Add initial implementation of EE timers · fc244e8a
      Geo Ster authored
      * Yeah, timers again, my favourite topic... To be frank the EE timers
      are a bit simpler than the IOP timers as they have less complexity
      in their configuration. However, since the BIOS starts to use them
      at this point, we can't get away with a extermelly partial implementation
      like the IOP.
      
      * The Emotion Engine has four hardware timers, each of them having
      three registers (four on Timer 0 and 1). They are practically the same
      with the IOP in that regard, having a count a compare/target and a mode
      register. Timer 0 and 1 have an additional register Tn_HOLD which
      keeps track of the count value when a peripheral on the
      SBUS generates an interrupt.
      
      * All the timers increment based on the bus clock which is exactly
      half of the EE clock. The timers can also be configured to count
      based on external sources, namely hblank and vblank. These are less
      accurate but can be used to keep track when the screen refreshes.
      I had hoped that we could have ignored hblank for now, but the BIOS
      configures Timer 3 (used for BIOS alarms) to use it so implementing it
      is necessary. The timings were taken from the timer header [1]
      of the ps2sdk.
      
      * An interesting fact as well is the interrupts as edge triggered
      which means that an interrupt is sent to the EE when the raised flags in
      Tn_MODE switch from 0 to 1 [2]. This is easy to implement and so did I,
      to avoid any headaches in the future.
      
      * Since the EE ticks the timers directly, we can't increment the counters
      each time the function get called. To properly emulate the timer frequency,
      an internal counter is used, that when its value is equal to the ratio
      between the EE frequency and the timer clock, the real counter is incremented.
      
      * This can be expensive since the timer function gets called every EE cycle
      so we will probably change it to cycle adding in the future, especially when
      the JIT will be implemented.
      
      [1] https://github.com/ps2dev/ps2sdk/blob/master/ee/kernel/include/timer.h#L53
      [2] https://psi-rockin.github.io/ps2tek/#eetimers
      fc244e8a
  12. Dec 27, 2021
    • Geo Ster's avatar
      Handle the entire register map of the GS · c0e3bc03
      Geo Ster authored
      c0e3bc03
    • Geo Ster's avatar
      Implement GIF PATH3 packed transfer mode · 366d03f9
      Geo Ster authored
      * Firstly, I fixed a small bug in the Handler that caused data loss
      on 128bit operations.
      
      * The GIF is a marvellous and complicated little piece of hardware that
      handles transfers between the EE and the GS. It can be "fed" by three
      paths, PATH1 is from the VPU1 memory, PATH2 is from the VPU1 FIFO and PATH3
      is directly from the main bus. Since we don't have any VUs implemented
      we only care about PATH3 at this stage.
      
      * Each primitive sent has the form of a linked list. The EE first sends an
      128bit GIFTag that acts as the header and tells the GIF how much more
      data to expect and what to do with it. The loop ends when the EE sends a GIFTag
      with the EOP field set to 1. (EE User's Manual [150])
      
      * Each data packet after a GIFTag can be processed in three different
      ways depending on the FLG field of the tag; PACKED, REGLIST or IMAGE mode.
      For now we only care about PACKED.
      
      * When in PACKED mode, the EE will send NREG * NLOOP (specified in GIFtag) qwords
      after the tag. Each qword can be processed in different ways depending on the desc
      in REG field of the GIFTag. Page 152 of the EE User's Manual shows the different modes.
      The REG field though is in reality a bit array of 4-bit descriptors. To understand
      this better, here are the processing steps:
      
      1. The first qword after the GIFTag is processed based on the least significant bits (64:67) (the first descriptor)
      and is output
      
      2. The second qword is processed based on the next descriptor (68:71) (second descriptor) and is output
      
      3. Steps 1,2 are repeated NREG times.
      
      4. Steps 2,3 are repeated NLOOP times
      
      There are more variables we have to take into account with PATH3, because it can also be masked
      by other PATHs which have higher priority. But that is for later. Don't worry though if you
      didn't get it completetly. The GIF is nowhere near finished, so I will have more
      chances to explain how it works. For more info you can read the GIF chapter of the provided EE User's Manual.
      366d03f9
  13. Dec 26, 2021
    • Geo Ster's avatar
      Log accesses to INTC · 295daa15
      Geo Ster authored
      295daa15
    • Geo Ster's avatar
      Register handlers with addresses · e29049d4
      Geo Ster authored
      * Since the components will never give pages directly, let them
      use addresses instead and compute the page in the register function
      to save some work on the component side.
      e29049d4
    • Geo Ster's avatar
      Unify 128bit EE reads/writes · 3c4945af
      Geo Ster authored
      * Initially the LQ/SQ instructions were implemented to perform two
      sequential 64bit operations to emulate 128bit reads/writes. However
      this won't work well for us, especially when writing to the GIF FIFO.
      To mitigate this we can use the __int128 gcc extension (yay for switching
      to clang once again!), which provides us with an optimized way of storing
      128bit data.
      3c4945af
    • Geo Ster's avatar
      Respect type of handler · 70f31230
      Geo Ster authored
      * Until now the memory system didn't take into account the bit width
      of the data coming in and out of the handlers. Instead I assumed that
      64bits would be large enough for everything. But alas I was wrong.
      Some addresses (notably the GIF/IPU FIFOs) are read/written with 128bit values.
      I don't want to force every function to return __int128 types as that
      would cripple performance so some tweaks were needed.
      
      This isn't as hard as it might sound. The emulator read/write functions
      are templates so we know which type we want beforehand. So it's as
      simple as abstracting the Handler with a bit more inheritence magic and
      we can cast HandlerBase to the type we want.
      70f31230
    • Geo Ster's avatar
      Initial GIF implementation · a81117ea
      Geo Ster authored
      Currently there's nothing really of note, it's just an empty
      class that handles reads/writes to some of the registers. The
      functionality will be explained in subsequent commits. Along with
      this I've added a new document, from which, the GIF implementation will
      be based on.
      a81117ea
    • Geo Ster's avatar
      Stop leaking memory · 622034ef
      Geo Ster authored
      * The handler table is dynamically but the memory never gets
      deallocated. Plug the leak by clearing any memory in the destructor ;)
      622034ef
    • Geo Ster's avatar
      Prevent write to some addresses · 52b34632
      Geo Ster authored
      They cause too much logspam for now and we don't use them
      52b34632
    • Geo Ster's avatar
      Cleanup IOP timer processing · abc036d3
      Geo Ster authored
      * Remove cycle argument, we don't need it as we tick the timers each
      IOP cycle
      
      * Make the code a bit cleaner
      abc036d3
  14. Dec 25, 2021
    • Geo Ster's avatar
      Refactor and fix IOP interrupts · a6679fdc
      Geo Ster authored
      This commit fixes some issues preventing IOP interrupts from working
      correctly while also seperating them into a seperate class for convenience.
      
      * Previously the pending flag was written to the first bit of cause.IP, which
      while correct was flawed. To understandw why let's look at how interrupts
      get triggered. COP0 has 2 8 bit masks, IP (cause) and Im (status). On both
      of these registers the first 2 bits are ignored because they are used for
      software interrupts which are unsupported on the IOP. However while Im was
      including these unused bits, IP did not thus causing mistaken comparions.
      Below is a diagram that shows the issue. IP was bits 10-15 while Im was bits
      8-15. Comparing diffent ranges like this doesn't work.
      
      Cause: ... 00|111111| ...
      Status: ... |00111111| ...
      
      The fix was to make IP point to 8-15 range and adjust the writing
      mechanism in the INTR::interrupt_pending function.
      
      * In addition the usage of >= instead of == in the timers, caused
      a bug where the timer would continiously send interrupts after reaching
      target which is not the intended behaviour. Fix that as well.
      a6679fdc
Loading