Пример #1
0
 /**
  * Fetches 8x8 NES tile stored at the given offset. This is an artifact of the first renderer I
  * wrote which drew 8 scanlines at a time.
  *
  * @param offset
  * @return an 8x8 array with colors stored as RGB packed in int
  */
 private int[] debugGetTile(final int offset) {
   // read one whole tile from nametable and convert from bitplane to packed
   // only used for debugging
   int[] dat = new int[64];
   for (int i = 0; i < 8; ++i) {
     // per line of tile ( 1 byte)
     for (int j = 0; j < 8; ++j) {
       // per pixel(1 bit)
       dat[8 * i + j] =
           ((((mapper.ppuRead(i + offset) & (utils.BIT7 - j)) != 0)) ? 0x555555 : 0)
               + ((((mapper.ppuRead(i + offset + 8) & (utils.BIT7 - j)) != 0)) ? 0xaaaaaa : 0);
     }
   }
   return dat;
 }
Пример #2
0
 private void spriteFetch(
     final boolean spritesize, final int tilenum, int offset, final int oamextra) {
   int tilefetched;
   if (spritesize) {
     tilefetched = ((tilenum & 1) * 0x1000) + (tilenum & 0xfe) * 16;
   } else {
     tilefetched = tilenum * 16 + ((sprpattern) ? 0x1000 : 0);
   }
   tilefetched += offset;
   // now load up the shift registers for said sprite
   final boolean hflip = ((oamextra & (utils.BIT6)) != 0);
   if (!hflip) {
     spriteshiftregL[found] = reverseByte(mapper.ppuRead(tilefetched));
     spriteshiftregH[found] = reverseByte(mapper.ppuRead(tilefetched + 8));
   } else {
     spriteshiftregL[found] = mapper.ppuRead(tilefetched);
     spriteshiftregH[found] = mapper.ppuRead(tilefetched + 8);
   }
 }
Пример #3
0
 private void bgFetch() {
   // fetch tiles for background
   // on real PPU this logic is repurposed for sprite fetches as well
   // System.err.println(hex(loopyV));
   bgAttrShiftRegH |= ((nextattr >> 1) & 1);
   bgAttrShiftRegL |= (nextattr & 1);
   // background fetches
   switch ((cycles - 1) & 7) {
     case 1:
       fetchNTByte();
       break;
     case 3:
       // fetch attribute (FIX MATH)
       penultimateattr =
           getAttribute(((loopyV & 0xc00) + 0x23c0), (loopyV) & 0x1f, (((loopyV) & 0x3e0) >> 5));
       break;
     case 5:
       // fetch low bg byte
       linelowbits = mapper.ppuRead((tileAddr) + ((loopyV & 0x7000) >> 12));
       break;
     case 7:
       // fetch high bg byte
       linehighbits = mapper.ppuRead((tileAddr) + 8 + ((loopyV & 0x7000) >> 12));
       bgShiftRegL |= linelowbits;
       bgShiftRegH |= linehighbits;
       nextattr = penultimateattr;
       if (cycles != 256) {
         incLoopyVHoriz();
       } else {
         incLoopyVVert();
       }
       break;
     default:
       break;
   }
   if (cycles >= 321 && cycles <= 336) {
     bgShiftClock();
   }
 }
Пример #4
0
 /**
  * Read the appropriate color attribute byte for the current tile. this is fetched 2x as often as
  * it really needs to be, the MMC5 takes advantage of that for ExGrafix mode.
  *
  * @param ntstart //start of the current attribute table
  * @param tileX //x position of tile (0-31)
  * @param tileY //y position of tile (0-29)
  * @return attribute table value (0-3)
  */
 private int getAttribute(final int ntstart, final int tileX, final int tileY) {
   final int base = mapper.ppuRead(ntstart + (tileX >> 2) + 8 * (tileY >> 2));
   if (((tileY & (utils.BIT1)) != 0)) {
     if (((tileX & (utils.BIT1)) != 0)) {
       return (base >> 6) & 3;
     } else {
       return (base >> 4) & 3;
     }
   } else {
     if (((tileX & (utils.BIT1)) != 0)) {
       return (base >> 2) & 3;
     } else {
       return base & 3;
     }
   }
 }
Пример #5
0
  /** draw all 4 nametables/tileset/pallette to debug window. (for the nametable viewer) */
  private void debugDraw() {
    for (int i = 0; i < 32; ++i) {
      for (int j = 0; j < 30; ++j) {
        nametableView.setRGB(
            i * 8,
            j * 8,
            8,
            8,
            debugGetTile(mapper.ppuRead(0x2000 + i + 32 * j) * 16 + (bgpattern ? 0x1000 : 0)),
            0,
            8);
      }
    }
    for (int i = 0; i < 32; ++i) {
      for (int j = 0; j < 30; ++j) {
        nametableView.setRGB(
            i * 8 + 255,
            j * 8,
            8,
            8,
            debugGetTile(mapper.ppuRead(0x2400 + i + 32 * j) * 16 + (bgpattern ? 0x1000 : 0)),
            0,
            8);
      }
    }
    for (int i = 0; i < 32; ++i) {
      for (int j = 0; j < 30; ++j) {
        nametableView.setRGB(
            i * 8,
            j * 8 + 239,
            8,
            8,
            debugGetTile(mapper.ppuRead(0x2800 + i + 32 * j) * 16 + (bgpattern ? 0x1000 : 0)),
            0,
            8);
      }
    }
    for (int i = 0; i < 32; ++i) {
      for (int j = 0; j < 30; ++j) {
        nametableView.setRGB(
            i * 8 + 255,
            j * 8 + 239,
            8,
            8,
            debugGetTile(mapper.ppuRead(0x2C00 + i + 32 * j) * 16 + (bgpattern ? 0x1000 : 0)),
            0,
            8);
      }
    }

    // draw the tileset
    //        for (int i = 0; i < 16; ++i) {
    //            for (int j = 0; j < 32; ++j) {
    //                nametableView.setRGB(i * 8, j * 8, 8, 8,
    //                        debugGetTile((i + 16 * j) * 16), 0, 8);
    //            }
    //        }
    // draw the palettes on the bottom.
    //        for (int i = 0; i < 32; ++i) {
    //            for (int j = 0; j < 16; ++j) {
    //                for (int k = 0; k < 16; ++k) {
    //                    nametableView.setRGB(j + i * 16, k + 256, nescolor[0][pal[i]]);
    //                }
    //            }
    //        }
    debuggui.setFrame(nametableView);
    // debugbuff.clear();
  }
Пример #6
0
 private void fetchNTByte() {
   // fetch nt byte
   tileAddr =
       mapper.ppuRead(((loopyV & 0xc00) | 0x2000) + (loopyV & 0x3ff)) * 16
           + (bgpattern ? 0x1000 : 0);
 }
Пример #7
0
  /** runs the emulation for one PPU clock cycle. */
  public final void clock() {

    // cycle based ppu stuff will go here
    if (cycles == 1) {
      if (scanline == 0) {
        dotcrawl = renderingOn();
      }
      if (scanline < 240) {
        bgcolors[scanline] = pal[0];
      }
    }
    if (scanline < 240 || scanline == (numscanlines - 1)) {
      // on all rendering lines
      if (renderingOn() && ((cycles >= 1 && cycles <= 256) || (cycles >= 321 && cycles <= 336))) {
        // fetch background tiles, load shift registers
        bgFetch();
      } else if (cycles == 257 && renderingOn()) {
        // x scroll reset
        // horizontal bits of loopyV = loopyT
        loopyV &= ~0x41f;
        loopyV |= loopyT & 0x41f;

      } else if (cycles > 257 && cycles <= 341) {
        // clear the oam address from pxls 257-341 continuously
        oamaddr = 0;
      }
      if ((cycles == 340) && renderingOn()) {
        // read the same nametable byte twice
        // this signals the MMC5 to increment the scanline counter
        fetchNTByte();
        fetchNTByte();
      }
      if (cycles == 65 && renderingOn()) {
        oamstart = oamaddr;
      }
      if (cycles == 260 && renderingOn()) {
        // evaluate sprites for NEXT scanline (as long as either background or sprites are enabled)
        // this does in fact happen on scanine 261 but it doesn't do anything useful
        // it's cycle 260 because that's when the first important sprite byte is read
        // actually sprite overflow should be set by sprite eval somewhat before
        // so this needs to be split into 2 parts, the eval and the data fetches
        evalSprites();
      }
      if (scanline == (numscanlines - 1)) {
        if (cycles == 0) { // turn off vblank, sprite 0, sprite overflow flags
          vblankflag = false;
          sprite0hit = false;
          spriteoverflow = false;
        } else if (cycles >= 280 && cycles <= 304 && renderingOn()) {
          // loopyV = (all of)loopyT for each of these cycles
          loopyV = loopyT;
        }
      }
    } else if (scanline == vblankline && cycles == 1) {
      // handle vblank on / off
      vblankflag = true;
    }
    if (!renderingOn() || (scanline > 240 && scanline < (numscanlines - 1))) {
      // HACK ALERT
      // handle the case of MMC3 mapper watching A12 toggle
      // even when read or write aren't asserted on the bus
      // needed to pass Blargg's mmc3 tests
      mapper.checkA12(loopyV & 0x3fff);
    }
    if (scanline < 240) {
      if (cycles >= 1 && cycles <= 256) {
        int bufferoffset = (scanline << 8) + (cycles - 1);
        // bg drawing
        if (bgOn) { // if background is on, draw a line of that
          final boolean isBG = drawBGPixel(bufferoffset);
          // sprite drawing
          drawSprites(scanline << 8, cycles - 1, isBG);

        } else if (spritesOn) {
          // just the sprites then
          int bgcolor = ((loopyV > 0x3f00 && loopyV < 0x3fff) ? mapper.ppuRead(loopyV) : pal[0]);
          bitmap[bufferoffset] = bgcolor;
          drawSprites(scanline << 8, cycles - 1, true);
        } else {
          // rendering is off, so draw either the background color OR
          // if the PPU address points to the palette, draw that color instead.
          int bgcolor = ((loopyV > 0x3f00 && loopyV < 0x3fff) ? mapper.ppuRead(loopyV) : pal[0]);
          bitmap[bufferoffset] = bgcolor;
        }
        // deal with the grayscale flag
        if (grayscale) {
          bitmap[bufferoffset] &= 0x30;
        }
        // handle color emphasis
        bitmap[bufferoffset] = (bitmap[bufferoffset] & 0x3f) | emph;
      }
    }
    // handle nmi
    if (vblankflag && nmicontrol) {
      // pull NMI line on when conditions are right
      mapper.cpu.setNMI(true);
    } else {
      mapper.cpu.setNMI(false);
    }

    // clock CPU, once every 3 ppu cycles
    div = (div + 1) % cpudivider[cpudividerctr];
    if (div == 0) {
      mapper.cpu.runcycle(scanline, cycles);
      mapper.cpucycle(1);
      cpudividerctr = (cpudividerctr + 1) % cpudivider.length;
    }
    if (cycles == 257) {
      mapper.notifyscanline(scanline);
    } else if (cycles == 340) {
      scanline = (scanline + 1) % numscanlines;
      if (scanline == 0) {
        ++framecount;
      }
    }
  }
Пример #8
0
  /**
   * Performs a read from a PPU register, as well as causes any side effects of reading that
   * specific register.
   *
   * @param regnum
   * @return the data in the PPU register, or open bus (the last value written to a PPU register) if
   *     the register is read only
   */
  public final int read(final int regnum) {
    switch (regnum) {
      case 2:
        even = true;
        if (scanline == 241) {
          if (cycles == 1) { // suppress NMI flag if it was just turned on this same cycle
            vblankflag = false;
          }
          // OK, uncommenting this makes blargg's NMI suppression test
          // work but breaks Antarctic Adventure.
          // I'm going to need a cycle accurate CPU to fix that...
          //                    if (cycles < 4) {
          //                        //show vblank flag but cancel pending NMI before the CPU
          //                        //can actually do anything with it
          //                        //TODO: use proper interface for this
          //                        mapper.cpu.nmiNext = false;
          //                    }
        }
        openbus =
            (vblankflag ? 0x80 : 0)
                | (sprite0hit ? 0x40 : 0)
                | (spriteoverflow ? 0x20 : 0)
                | (openbus & 0x1f);
        vblankflag = false;
        break;
      case 4:
        // reading this is NOT reliable but some games do it anyways
        openbus = OAM[oamaddr];
        // System.err.println("codemasters?");
        if (renderingOn() && (scanline <= 240)) {
          if (cycles < 64) {
            return 0xFF;
          } else if (cycles <= 256) {
            return 0x00;
          } // Micro Machines relies on this:
          else if (cycles < 320) {
            return 0xFF;
          } // and this:
          else {
            return secOAM[0]; // is this the right value @ the time?
          }
        }
        break;
      case 7:
        // PPUDATA
        // correct behavior. read is delayed by one
        // -unless- is a read from sprite pallettes
        final int temp;
        if ((loopyV & 0x3fff) < 0x3f00) {
          temp = readbuffer;
          readbuffer = mapper.ppuRead(loopyV & 0x3fff);
        } else {
          readbuffer = mapper.ppuRead((loopyV & 0x3fff) - 0x1000);
          temp = mapper.ppuRead(loopyV);
        }
        if (!renderingOn() || (scanline > 240 && scanline < (numscanlines - 1))) {
          loopyV += vraminc;
        } else {
          // if 2007 is read during rendering PPU increments both horiz
          // and vert counters erroneously.
          incLoopyVHoriz();
          incLoopyVVert();
        }
        openbus = temp;
        break;

        // and don't increment on read
      default:
        return openbus; // last value written to ppu
    }
    return openbus;
  }