Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ public class CentralProcessingUnit extends Thread
// Whether the CPU is waiting for a keypress
private boolean awaitingKeypress = false;

// The key seen going down while waiting on Fx0A, or -1 while none has
private int keypressPending = -1;

// Whether shift quirks are enabled
private boolean shiftQuirks = false;

Expand Down Expand Up @@ -838,9 +841,12 @@ protected void loadIndexWithValue() {
*/
protected void jumpToRegisterPlusValue() {
if (jumpQuirks) {
// The whole twelve bit address counts; its top nibble both names
// the register and is part of the address, so BE00 with vE at
// 0x9C jumps to 0xE9C.
int x = (operand & 0xF00) >> 8;
pc = v[x] + (operand & 0x00FF);
lastOpDesc = "JUMP V" + toHex(x, 1) + " + " + toHex(operand & 0x00FF, 4);
pc = v[x] + (operand & 0x0FFF);
lastOpDesc = "JUMP V" + toHex(x, 1) + " + " + toHex(operand & 0x0FFF, 3);
} else {
pc = v[0] + (operand & 0x0FFF);
lastOpDesc = "JUMP V0 + " + toHex(operand & 0x0FFF, 3);
Expand Down Expand Up @@ -910,6 +916,10 @@ protected void drawSprite() {
* @param activeIndex the effective index to use when loading sprite data
*/
private void drawExtendedSprite(int xPos, int yPos, int bitplane, int activeIndex) {
// The starting position always wraps around the screen. Clipping only
// decides what happens to the part of the sprite past the edge.
xPos = xPos % screen.getWidth();
yPos = yPos % screen.getHeight();
for (int yIndex = 0; yIndex < 16; yIndex++) {
for (int xByte = 0; xByte < 2; xByte++) {
short colorByte = memory.read(activeIndex + (yIndex * 2) + xByte);
Expand Down Expand Up @@ -948,6 +958,9 @@ private void drawExtendedSprite(int xPos, int yPos, int bitplane, int activeInde
* @param activeIndex the effective index to use when loading sprite data
*/
private void drawNormalSprite(int xPos, int yPos, int numBytes, int bitplane, int activeIndex) {
// As above: the start wraps, clipping applies to the overhang.
xPos = xPos % screen.getWidth();
yPos = yPos % screen.getHeight();
for (int yIndex = 0; yIndex < numBytes; yIndex++) {
short colorByte = memory.read(activeIndex + yIndex);
int yCoord = yPos + yIndex;
Expand Down Expand Up @@ -1065,6 +1078,7 @@ protected void moveDelayTimerIntoRegister() {
*/
protected void waitForKeypress() {
awaitingKeypress = true;
keypressPending = -1;
}

/**
Expand All @@ -1081,13 +1095,27 @@ protected boolean isAwaitingKeypress() {
* specified register. If no key is waiting, returns without doing anything.
*/
protected void decodeKeypressAndContinue() {
int currentKey = keyboard.getCurrentKey();
if (currentKey == -1) {
// The original hardware moves on when the key is released, not when
// it goes down. So a key going down is remembered, and execution
// continues once that key is up again. Reading the key state rather
// than the last key pressed also means a press from before the wait
// began cannot satisfy it.
if (keypressPending == -1) {
for (int key = 0; key < 16; key++) {
if (keyboard.isKeyPressed(key)) {
keypressPending = key;
return;
}
}
return;
}
if (keyboard.isKeyPressed(keypressPending)) {
return;
}

int x = (operand & 0x0F00) >> 8;
v[x] = (short) currentKey;
v[x] = (short) keypressPending;
keypressPending = -1;
lastOpDesc = "KEYD V" + toHex(x, 1);
awaitingKeypress = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public class CentralProcessingUnitTest
public void setUp() {
memory = new Memory();
screenMock = Mockito.mock(Screen.class);
Mockito.when(screenMock.getWidth()).thenReturn(64);
Mockito.when(screenMock.getHeight()).thenReturn(32);
keyboardMock = Mockito.mock(Keyboard.class);
Mockito.when(keyboardMock.getCurrentKey()).thenReturn(9);
cpu = new CentralProcessingUnit(memory, keyboardMock, screenMock);
Expand Down Expand Up @@ -775,12 +777,50 @@ public void testJumpToRegisterPlusValueJumpQuirks() {
cpu.operand = (short) value;
cpu.operand |= (register << 8);
cpu.jumpToRegisterPlusValue();
assertEquals(index + value, cpu.pc);
// The whole address counts, including the nibble that
// names the register, which the jump row of Timendus'
// quirks test checks.
assertEquals(index + ((register << 8) | value), cpu.pc);
}
}
}
}

@Test
public void testJumpToRegisterPlusValueJumpQuirksKeepsHighNibble() {
cpu.setJumpQuirks(true);
cpu.v[0xE] = 0x9C;
cpu.operand = 0xBE00;
cpu.jumpToRegisterPlusValue();
assertEquals(0xE9C, cpu.pc);
}

@Test
public void testDecodeKeypressContinuesOnReleaseNotPress() {
Keyboard keyboard = Mockito.mock(Keyboard.class);
cpu = new CentralProcessingUnit(memory, keyboard, screenMock);
cpu.operand = 1 << 8;
cpu.waitForKeypress();
Mockito.when(keyboard.isKeyPressed(5)).thenReturn(true);
cpu.decodeKeypressAndContinue();
assertTrue("a key going down is not enough", cpu.isAwaitingKeypress());
assertEquals(0, cpu.v[1]);
Mockito.when(keyboard.isKeyPressed(5)).thenReturn(false);
cpu.decodeKeypressAndContinue();
assertFalse("the key coming up lets the program move on", cpu.isAwaitingKeypress());
assertEquals(5, cpu.v[1]);
}

@Test
public void testDecodeKeypressIgnoresKeysNotHeld() {
Keyboard keyboard = Mockito.mock(Keyboard.class);
cpu = new CentralProcessingUnit(memory, keyboard, screenMock);
cpu.operand = 1 << 8;
cpu.waitForKeypress();
cpu.decodeKeypressAndContinue();
assertTrue("with nothing held the wait goes on", cpu.isAwaitingKeypress());
}

@Test
public void testAddRegisterToIndex() {
for (int register = 0; register < 0xF; register++) {
Expand Down Expand Up @@ -1894,6 +1934,23 @@ public void testDrawSpriteDrawsCorrectPattern() throws FontFormatException, IOEx
tearDownCanvas();
}

@Test
public void testDrawSpriteClipQuirksWrapsStartPosition() throws FontFormatException, IOException {
setUpCanvas();
cpu = new CentralProcessingUnit(memory, keyboardMock, screen);
cpu.setClipQuirks(true);
cpu.index = 0x200;
memory.write(0x80, 0x200);
cpu.v[0] = 64;
cpu.v[1] = 32;
cpu.operand = 0x11;
cpu.drawSprite();
// A start of (64, 32) is (0, 0) on the screen even when clipping,
// which is the clipping row of Timendus' quirks test.
assertTrue(screen.getPixel(0, 0, 1));
tearDownCanvas();
}

@Test
public void testDrawSpriteExtendedDrawsCorrectPattern() throws FontFormatException, IOException {
setUpCanvas();
Expand Down