/* Sprite2RedrawHandler.java (c) 2008-2013 Edward Swartz All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html */ package v9t9.video.v9938; import java.util.Arrays; import v9t9.common.memory.ByteMemoryAccess; import v9t9.common.video.VdpSprite; import v9t9.video.VdpRedrawInfo; import v9t9.video.common.VdpModeInfo; import v9t9.video.tms9918a.SpriteRedrawHandler; import static v9t9.common.hardware.VdpV9938Consts.*; /** * Sprite mode 2 * <p> * -- Separate color table -- 512 bytes before sprite attribute table * -- Color in sprite is ignored * -- 16 colors per sprite, one per line * -- EC | CC | IC | 0 | color * -- early clock, priority enable, collision detect * @author ejs * */ public class Sprite2RedrawHandler extends SpriteRedrawHandler { /** Cache the offset of each sprite's color pattern in memory * to avoid recreating the VDP memory accessors */ protected int[] sprcolOffsMap = new int[32]; { Arrays.fill(sprcolOffsMap, -1); } public Sprite2RedrawHandler(VdpRedrawInfo info, VdpModeInfo modeInfo) { super(info, modeInfo); } @Override protected void init() { info.touch.sprite = modify_sprite_default; info.touch.sprpat = modify_sprpat_default; spriteCanvas = new VdpSprite2Canvas(info.canvas, 8, info.vdp.getModeNumber() == MODE_GRAPHICS5); } @Override public boolean touch(int addr) { boolean visible = false; // sprite color table int sprcolbase = (modeInfo.sprite.base - 0x200) & 0x1ffff; if (sprcolbase <= addr && addr < modeInfo.sprite.base) { info.changes.sprite |= (1<< ((addr - sprcolbase) >> 4)); info.changes.changed = true; visible = true; } return super.touch(addr) || visible; } /** * This function does the mammoth tasks of: * * <p> * 0) Change sprites under whom chars changed * <p> * 1) See if sprite pattern change forces a sprite update. * <p> * 2) See if position change forces a sprite update. * <p> * 3) See if deleted-ptr changes, forcing sprite updates. * <p> * 4) Update vdp_changes.screen where sprites dance. * <p> * 5) Force updates to sprites under changed ones. * <p> * 6) Weed out blank sprites. * <p>T * 7) Set VDP status flags for coincidence and N sprites on a line * (hackish) * @param forceRedraw * * @return the updated VDP status bits */ public byte updateSpriteCoverage(byte vdpStatus, boolean forceRedraw) { // Update changes when sprite patterns change VdpSprite[] sprites = spriteCanvas.getSprites(); // figure sprite size/mag boolean isMag = false; int size = 8; int numchars = 1; switch (info.vdpregs[1] & (R1_SPRMAG + R1_SPR4)) { case 0: size = 8; numchars = 1; isMag = false; break; case R1_SPRMAG: size = 16; numchars = 1; isMag = true; break; case R1_SPR4: size = 16; numchars = 4; isMag = false; break; case R1_SPR4 + R1_SPRMAG: size = 32; numchars = 4; isMag = true; break; } int sprbase = modeInfo.sprite.base; int sprpatbase = modeInfo.sprpat.base; ByteMemoryAccess access = info.vdp.getByteReadMemoryAccess(sprbase); ByteMemoryAccess colorAccess = info.vdp.getByteReadMemoryAccess((sprbase - 0x200) & 0x1ffff); spriteCanvas.setNumSpriteChars(numchars); spriteCanvas.setMagnified(isMag); boolean deleted = false; for (int i = 0; i < 32; i++) { VdpSprite sprite = sprites[i]; int y = access.memory[access.offset++] & 0xff; int x = access.memory[access.offset++] & 0xff; int ch = access.memory[access.offset++] & 0xff; access.offset++; if (y == 0xd8) { // 216 deleted = true; } if (deleted) { sprite.setDeleted(true); } else { sprite.setDeleted(false); sprite.move(x, (y + 1) & 0xff); int patOffs = sprpatbase + (ch << 3); if (sprpatOffsMap[i] != patOffs) { sprite.setPattern(info.vdp.getByteReadMemoryAccess(patOffs)); sprpatOffsMap[i] = patOffs; } if (sprcolOffsMap[i] != colorAccess.offset) { ByteMemoryAccess colorStripe = new ByteMemoryAccess(colorAccess); sprite.setColorStripe(colorStripe); sprcolOffsMap[i] = colorAccess.offset; } int sizeX = size; int sizeY = size; int origClock = -1; // a sprite counts as double-wide if it has two // different early clock settings for (int offs = 0; offs < 16; offs++) { byte attr = colorAccess.memory[colorAccess.offset + offs]; int clock = VdpSprite2Canvas.EARLY && (attr & 0x80) != 0 ? -32 : 0; if (origClock == -1) origClock = clock; else if (clock != origClock) { if (clock < origClock) origClock = clock; sizeX += 32; break; } } sprite.setShift(origClock); sprite.setSize(sizeX, sizeY); // also check whether the pattern content changed if (info.changes.sprpat[ch] != 0) sprite.setBitmapDirty(true); } colorAccess.offset += 16; } // TODO: move the VDP status logic int nth_sprite = spriteCanvas.updateSpriteCoverage(info.canvas, info.changes.screen, forceRedraw); if (nth_sprite != -1) { vdpStatus = (byte) (vdpStatus & ~(VDP_FIVE_SPRITES | VDP_FIFTH_SPRITE) | (VDP_FIVE_SPRITES | nth_sprite)); } else { vdpStatus &= ~(VDP_FIVE_SPRITES | VDP_FIFTH_SPRITE); } return vdpStatus; } }