mirror of
https://github.com/sethm/symon.git
synced 2025-04-08 13:38:37 +00:00
README updates, CRTC tests.
- Added more CRTC information to the README file. - Added unit tests for the CRTC. - Implemented register read for cursor position in the CRTC. - Bundling a new version of jterminal that has correct backspace behavior.
This commit is contained in:
parent
070e7380fc
commit
4a510b635e
38
README.md
38
README.md
@ -3,7 +3,7 @@ SYMON - A 6502 System Simulator
|
||||
|
||||
**NOTE: THIS SOFTWARE IS UNDER ACTIVE DEVELOPMENT. Feedback is welcome!**
|
||||
|
||||
**Version:** 0.9.0
|
||||
**Version:** 0.9.0.1
|
||||
|
||||
**Last Updated:** 29 December, 2013
|
||||
|
||||
@ -22,7 +22,7 @@ in Java. Its core goals are accuracy, ease of development, clear
|
||||
documentation, and extensive test suites for validating correctness.
|
||||
|
||||
Symon simulates a complete system with a 1 MHz NMOS 6502, 32KB of RAM,
|
||||
16KB of ROM, a 6551 ACIA, and a 6522 VIA.
|
||||
16KB of ROM, a 6551 ACIA, a 6522 VIA, and a 6545 CRTC.
|
||||
|
||||
Symon has extensive unit tests to verify correctness, and fully passes
|
||||
Klaus Dormann's 6502 Functional Test Suite as of version 0.8.2
|
||||
@ -42,8 +42,11 @@ for more information about this functional test suite).
|
||||
- `$0000`--`$7FFF`: 32KB RAM
|
||||
- `$8000`--`$800F`: 6522 VIA
|
||||
- `$8800`--`$8803`: 6551 ACIA (Serial Console)
|
||||
- `$9000`--`$9001`: 6545 CRTC
|
||||
- `$C000`--`$FFFF`: 16KB ROM
|
||||
|
||||
The CRT Controller uses memory address `$7000` as the start of Video memory.
|
||||
|
||||
### 3.2 Serial Console and CPU Status
|
||||
|
||||
![Serial Console] (https://github.com/sethm/symon/raw/master/screenshots/console.png)
|
||||
@ -95,9 +98,6 @@ By default, the 40 x 25 character display uses video memory located at base addr
|
||||
This means that the memory from address `$7000` (28672 decimal) to `$73E8` (29672 decimal)
|
||||
is directly mapped to video.
|
||||
|
||||
The CRTC emulation is very rough around the edges at the moment. Only the following registers
|
||||
are supported:
|
||||
|
||||
- Address Register (at address `$9000`)
|
||||
- R1: Horizontal Displayed Columns
|
||||
- R6: Vertical Displayed Rows
|
||||
@ -106,10 +106,21 @@ are supported:
|
||||
- R11: Cursor End Scan Line
|
||||
- R12: Display Start Address (High Byte)
|
||||
- R13: Display Start Address (Low Byte)
|
||||
- R14: Cursor Position (High Byte) [Read Only]
|
||||
- R15: Cursor Position (Low Byte) [Read Only]
|
||||
- R14: Cursor Position (High Byte)
|
||||
- R15: Cursor Position (Low Byte)
|
||||
|
||||
In particular, please note that the Status register is not implemented yet. Still, the feature is ready for some testing and playing with.
|
||||
Although the simulation is pretty good, there are a few key differences between
|
||||
the simulated 6545 and a real 6545:
|
||||
|
||||
- The simulated 6545 supports only the straight binary addressing mode of the real 6545,
|
||||
and not the Row/Column addressing mode.
|
||||
- The simulated 6545 has full 16 bit addressing, where the real 6545 has only
|
||||
a 14-bit address bus.
|
||||
- The simulation is done at a whole-frame level, meaning that lots of
|
||||
6545 programming tricks that were achieved by updating the frame address
|
||||
during vertical and horizontal sync times are not achievable. There is no way
|
||||
(for example) to change the Display Start Address (R12 and R13) while a
|
||||
frame is being drawn.
|
||||
|
||||
For more information on the 6545 CRTC and its programming model, please see the following resources
|
||||
|
||||
@ -118,6 +129,17 @@ For more information on the 6545 CRTC and its programming model, please see the
|
||||
- [MOS 6545 Datasheet (PDF)] (http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
|
||||
|
||||
|
||||
#### 3.6.1 Example BASIC Program to test Video
|
||||
|
||||
This program will fill the video screen with all printable characters.
|
||||
|
||||
10 J = 0
|
||||
20 FOR I = 28672 TO 29672
|
||||
30 POKE I,J
|
||||
40 IF J < 255 THEN J = J + 1 ELSE J = 0
|
||||
50 NEXT I
|
||||
60 END
|
||||
|
||||
## 4.0 Usage
|
||||
|
||||
### 4.1 Building
|
||||
|
62
pom.xml
62
pom.xml
@ -4,7 +4,7 @@
|
||||
<groupId>com.loomcom.symon</groupId>
|
||||
<artifactId>symon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.9.0</version>
|
||||
<version>0.9.0.1</version>
|
||||
<name>symon</name>
|
||||
<url>http://www.loomcom.com/symon</url>
|
||||
<properties>
|
||||
@ -44,7 +44,13 @@
|
||||
<dependency>
|
||||
<groupId>com.grahamedgecombe.jterminal</groupId>
|
||||
<artifactId>jterminal</artifactId>
|
||||
<version>1.0.2.2-loomcom</version>
|
||||
<version>1.0.2.3-loomcom</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@ -125,47 +131,6 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Cobertura is essential -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<check>
|
||||
<haltOnFailure>false</haltOnFailure>
|
||||
<regexes>
|
||||
<regex>
|
||||
<pattern>com.loomcom.symon.*</pattern>
|
||||
<branchRate>90</branchRate>
|
||||
<lineRate>90</lineRate>
|
||||
</regex>
|
||||
</regexes>
|
||||
</check>
|
||||
<instrumentation>
|
||||
<includes>
|
||||
<include>com/loomcom/symon/*.class</include>
|
||||
</includes>
|
||||
</instrumentation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>clean</id>
|
||||
<phase>pre-site</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>instrument</id>
|
||||
<phase>site</phase>
|
||||
<goals>
|
||||
<goal>instrument</goal>
|
||||
<goal>cobertura</goal>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
@ -183,15 +148,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
</project>
|
||||
|
@ -23,10 +23,12 @@
|
||||
|
||||
package com.loomcom.symon;
|
||||
|
||||
import java.util.*;
|
||||
import com.loomcom.symon.devices.Device;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
import com.loomcom.symon.devices.*;
|
||||
import com.loomcom.symon.exceptions.*;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* The Bus ties the whole thing together, man.
|
||||
|
@ -17,7 +17,7 @@ public class Crtc extends Device {
|
||||
|
||||
// Memory locations in the CRTC address space
|
||||
public static final int REGISTER_SELECT = 0;
|
||||
public static final int REGISTER_WRITE = 1;
|
||||
public static final int REGISTER_RW = 1;
|
||||
|
||||
// Registers
|
||||
public static final int HORIZONTAL_DISPLAYED = 1;
|
||||
@ -90,25 +90,26 @@ public class Crtc extends Device {
|
||||
case REGISTER_SELECT:
|
||||
setCurrentRegister(data);
|
||||
break;
|
||||
case REGISTER_WRITE:
|
||||
case REGISTER_RW:
|
||||
writeRegisterValue(data);
|
||||
break;
|
||||
default:
|
||||
throw new MemoryAccessException("No such address.");
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
switch (address) {
|
||||
case REGISTER_SELECT:
|
||||
return status();
|
||||
case REGISTER_WRITE:
|
||||
return 0;
|
||||
case REGISTER_RW:
|
||||
switch (currentRegister) {
|
||||
case CURSOR_POSITION_LOW:
|
||||
return cursorPosition & 0xff;
|
||||
case CURSOR_POSITION_HIGH:
|
||||
return cursorPosition >> 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
throw new MemoryAccessException("No such address.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,10 +122,6 @@ public class Crtc extends Device {
|
||||
return memory.getDmaAccess();
|
||||
}
|
||||
|
||||
private int status() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getHorizontalDisplayed() {
|
||||
return horizontalDisplayed;
|
||||
}
|
||||
@ -169,7 +166,10 @@ public class Crtc extends Device {
|
||||
this.currentRegister = registerNumber;
|
||||
}
|
||||
|
||||
private void writeRegisterValue(int data) {
|
||||
private void writeRegisterValue(int data) throws MemoryAccessException {
|
||||
int oldStartAddress = startAddress;
|
||||
int oldCursorPosition = cursorPosition;
|
||||
|
||||
switch (currentRegister) {
|
||||
case HORIZONTAL_DISPLAYED:
|
||||
horizontalDisplayed = data;
|
||||
@ -213,24 +213,31 @@ public class Crtc extends Device {
|
||||
break;
|
||||
case DISPLAY_START_HIGH:
|
||||
startAddress = ((data & 0xff) << 8) | (startAddress & 0x00ff);
|
||||
// TODO: bounds checking.
|
||||
break;
|
||||
case DISPLAY_START_LOW:
|
||||
startAddress = ((data & 0xff) | (startAddress & 0xff00));
|
||||
// TODO: bounds checking.
|
||||
break;
|
||||
case CURSOR_POSITION_HIGH:
|
||||
cursorPosition = ((data & 0xff) << 8) | (cursorPosition & 0x00ff);
|
||||
// TODO: bounds checking.
|
||||
break;
|
||||
case CURSOR_POSITION_LOW:
|
||||
// TODO: bounds checking.
|
||||
cursorPosition = (data & 0xff) | (cursorPosition & 0xff00);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (startAddress + pageSize > memory.endAddress()) {
|
||||
startAddress = oldStartAddress;
|
||||
throw new MemoryAccessException("Cannot draw screen starting at selected address.");
|
||||
}
|
||||
|
||||
if (cursorPosition > memory.endAddress()) {
|
||||
cursorPosition = oldCursorPosition;
|
||||
throw new MemoryAccessException("Cannot position cursor past end of memory.");
|
||||
}
|
||||
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
343
src/test/java/com/loomcom/symon/CrtcTest.java
Normal file
343
src/test/java/com/loomcom/symon/CrtcTest.java
Normal file
@ -0,0 +1,343 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Crtc;
|
||||
import com.loomcom.symon.devices.DeviceChangeListener;
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CrtcTest {
|
||||
|
||||
Crtc crtc;
|
||||
|
||||
@Mock
|
||||
DeviceChangeListener changeListener;
|
||||
@Mock
|
||||
Memory memory;
|
||||
|
||||
@Before
|
||||
public void createDevices() throws Exception {
|
||||
crtc = new Crtc(0x9000, memory);
|
||||
crtc.registerListener(changeListener);
|
||||
|
||||
when(memory.startAddress()).thenReturn(0);
|
||||
when(memory.endAddress()).thenReturn(0x7fff);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectingRegisterDoesNotTriggedrCallback() throws Exception {
|
||||
crtc.write(0, 1);
|
||||
|
||||
verify(changeListener, never()).deviceStateChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldChangeHorizontalDisplayed() throws Exception {
|
||||
crtc.write(0, 1);
|
||||
|
||||
crtc.write(1, 80);
|
||||
assertEquals(80, crtc.getHorizontalDisplayed());
|
||||
|
||||
crtc.write(1, 40);
|
||||
assertEquals(40, crtc.getHorizontalDisplayed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeHorizontalDisplayedShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 1);
|
||||
|
||||
crtc.write(1, 80);
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldChangeVerticalDisplayed() throws Exception {
|
||||
crtc.write(0, 6);
|
||||
|
||||
crtc.write(1, 23);
|
||||
assertEquals(23, crtc.getVerticalDisplayed());
|
||||
|
||||
crtc.write(1, 26);
|
||||
assertEquals(26, crtc.getVerticalDisplayed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeVerticalDisplayedShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 6);
|
||||
|
||||
crtc.write(1, 23);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldChangeScanLinesPerRow() throws Exception {
|
||||
crtc.write(0, 9); // Select register 9, Scan Line
|
||||
|
||||
crtc.write(1, 3);
|
||||
assertEquals(3, crtc.getScanLinesPerRow());
|
||||
|
||||
crtc.write(1, 5);
|
||||
assertEquals(5, crtc.getScanLinesPerRow());
|
||||
|
||||
crtc.write(1, 9);
|
||||
assertEquals(9, crtc.getScanLinesPerRow());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeScanLinesPerRowShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 9); // Select register 9, Scan Line
|
||||
|
||||
crtc.write(1, 3);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldChangeCursorStartLine() throws Exception {
|
||||
crtc.write(0, 10);
|
||||
|
||||
crtc.write(1, 0);
|
||||
assertEquals(0, crtc.getCursorStartLine());
|
||||
|
||||
crtc.write(1, 1);
|
||||
assertEquals(1, crtc.getCursorStartLine());
|
||||
|
||||
crtc.write(1, 4);
|
||||
assertEquals(4, crtc.getCursorStartLine());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeCursorStartLineShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 10);
|
||||
|
||||
crtc.write(1, 5);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cursorStartLineRegisterChangesCursorVisibility() throws Exception {
|
||||
crtc.write(0, 10);
|
||||
|
||||
crtc.write(1, 0x00); // Start line 0, no blinking.
|
||||
|
||||
assertEquals(0, crtc.getCursorStartLine());
|
||||
assertEquals(0, crtc.getCursorBlinkRate());
|
||||
assertTrue(crtc.isCursorEnabled());
|
||||
|
||||
crtc.write(1, 0x23); // Start line 3, no cursor.
|
||||
|
||||
assertEquals(3, crtc.getCursorStartLine());
|
||||
assertEquals(0, crtc.getCursorBlinkRate());
|
||||
assertFalse(crtc.isCursorEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cursorStartLineRegisterChangesCursorBlinkRate() throws Exception {
|
||||
crtc.write(0, 10);
|
||||
|
||||
crtc.write(1, 0x40); // Start line 0, 500ms blink delay
|
||||
|
||||
assertEquals(0, crtc.getCursorStartLine());
|
||||
assertEquals(500, crtc.getCursorBlinkRate());
|
||||
assertTrue(crtc.isCursorEnabled());
|
||||
|
||||
crtc.write(1, 0x62); // Start line 3, 1000ms blink delay
|
||||
|
||||
assertEquals(2, crtc.getCursorStartLine());
|
||||
assertEquals(1000, crtc.getCursorBlinkRate());
|
||||
assertTrue(crtc.isCursorEnabled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldChangeCursorStopLine() throws Exception {
|
||||
crtc.write(0, 11);
|
||||
|
||||
crtc.write(1, 0);
|
||||
assertEquals(0, crtc.getCursorStopLine());
|
||||
|
||||
crtc.write(1, 3);
|
||||
assertEquals(3, crtc.getCursorStopLine());
|
||||
|
||||
crtc.write(1, 6);
|
||||
assertEquals(6, crtc.getCursorStopLine());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeCursorStopLineShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 11);
|
||||
|
||||
crtc.write(1, 7);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldChangeScreenStartAddressHighByte() throws Exception {
|
||||
crtc.write(0, 12);
|
||||
|
||||
crtc.write(1, 0x00);
|
||||
assertEquals(0x00, crtc.getStartAddress() >> 8);
|
||||
|
||||
crtc.write(1, 0x30);
|
||||
assertEquals(0x30, crtc.getStartAddress() >> 8);
|
||||
|
||||
crtc.write(1, 0x6f);
|
||||
assertEquals(0x6f, crtc.getStartAddress() >> 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeScreenStartAddressHighByteShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 12);
|
||||
|
||||
crtc.write(1, 0x30);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldChangeScreenStartAddressLowByte() throws Exception {
|
||||
crtc.write(0, 13);
|
||||
|
||||
crtc.write(1, 0x00);
|
||||
assertEquals(0x00, crtc.getStartAddress() & 0xff);
|
||||
|
||||
crtc.write(1, 0x11);
|
||||
assertEquals(0x11, crtc.getStartAddress() & 0xff);
|
||||
|
||||
crtc.write(1, 0xff);
|
||||
assertEquals(0xff, crtc.getStartAddress() & 0xff);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeScreenStartAddressLowByteShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 13);
|
||||
|
||||
crtc.write(1, 0xff);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = MemoryAccessException.class)
|
||||
public void shouldThrowMemoryAccessExceptionIfPageOutOfRange() throws Exception {
|
||||
crtc.write(0, 12);
|
||||
|
||||
crtc.write(1, 0x7f); // Page of text will extend beyond 0x7fff
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readingStartAddressShouldDoNothing() throws Exception {
|
||||
crtc.write(0, 12); // High byte
|
||||
|
||||
crtc.write(1, 0x03);
|
||||
assertEquals(0, crtc.read(1));
|
||||
|
||||
crtc.write(1, 0x70);
|
||||
assertEquals(0, crtc.read(1));
|
||||
|
||||
|
||||
crtc.write(0, 13); // Low byte
|
||||
|
||||
crtc.write(1, 0xff);
|
||||
assertEquals(0, crtc.read(1));
|
||||
|
||||
crtc.write(1, 0x0e);
|
||||
assertEquals(0, crtc.read(1));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldChangeCursorPositionHighByte() throws Exception {
|
||||
crtc.write(0, 14); // Select register 14
|
||||
|
||||
crtc.write(1, 0x73); // Set high cursor byte to $73
|
||||
assertEquals(0x73, crtc.getCursorPosition() >> 8);
|
||||
|
||||
crtc.write(1, 0x3f);
|
||||
assertEquals(0x3f, crtc.getCursorPosition() >> 8);
|
||||
|
||||
crtc.write(1, 0x7f);
|
||||
assertEquals(0x7f, crtc.getCursorPosition() >> 8);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToReadCursorPositionHighByte() throws Exception {
|
||||
crtc.write(0, 14);
|
||||
|
||||
crtc.write(1, 0x3f);
|
||||
assertEquals(0x3f, crtc.read(1));
|
||||
|
||||
crtc.write(1, 0x70);
|
||||
assertEquals(0x70, crtc.read(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeCursorPositionHighByteShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 14);
|
||||
|
||||
crtc.write(1, 0x73);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldChangeCursorPositionLowByte() throws Exception {
|
||||
crtc.write(0, 15); // Select register 15
|
||||
|
||||
crtc.write(1, 0x00); // Set low cursor byte to $00
|
||||
assertEquals(0x00, crtc.getCursorPosition() & 0xff);
|
||||
|
||||
crtc.write(1, 0x1f); // Set low cursor byte to $1f
|
||||
assertEquals(0x1f, crtc.getCursorPosition() & 0xff);
|
||||
|
||||
crtc.write(1, 0xff); // Set low cursor byte to $ff
|
||||
assertEquals(0xff, crtc.getCursorPosition() & 0xff);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToReadCursorPositionLowByte() throws Exception {
|
||||
crtc.write(0, 15);
|
||||
|
||||
crtc.write(1, 0x00);
|
||||
assertEquals(0x00, crtc.read(1));
|
||||
|
||||
crtc.write(1, 0x1f);
|
||||
assertEquals(0x1f, crtc.read(1));
|
||||
|
||||
crtc.write(1, 0xff);
|
||||
assertEquals(0xff, crtc.read(1));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void changeCursorPositionLowByteShouldTriggerCallback() throws Exception {
|
||||
crtc.write(0, 15);
|
||||
|
||||
crtc.write(1, 0x01);
|
||||
|
||||
verify(changeListener, times(1)).deviceStateChanged();
|
||||
}
|
||||
|
||||
@Test(expected = MemoryAccessException.class)
|
||||
public void shouldThrowMemoryAccessExceptionIfCursorGoesOutOfRange() throws Exception {
|
||||
crtc.write(0, 14); // Select register 14
|
||||
crtc.write(1, 0x80); // Can't position cursor
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user