mirror of
https://github.com/irmen/ksim65.git
synced 2024-06-04 10:29:29 +00:00
attempt at adding more gfx modes to the c64 (bitmap mode)
This commit is contained in:
parent
cfeb71c4af
commit
31c50991c1
|
@ -16,13 +16,14 @@ This is a Kotlin/JVM library that simulates the 8-bit 6502 and 65C02 microproces
|
|||
Properties of this simulator:
|
||||
|
||||
- Written in Kotlin. It is low-level code, but hopefully still readable :-)
|
||||
- Designed to simulate hardware components (bus, cpu, memory, i/o controllers)
|
||||
- Designed to simulate various hardware components (bus, cpu, memory, i/o controllers)
|
||||
- IRQ and NMI simulation
|
||||
- Aims to be clock cycle-precise (not yet 100% correct right now)
|
||||
- Aims to simulate correct instruction cycle timing, but is not 100% cycle exact for simplicity
|
||||
- Aims to implements all 6502 and 65c02 instructions, including the 'illegal' 6502 instructions (not yet done)
|
||||
- correct BCD mode for adc/sbc instructions on both cpu types
|
||||
- passes several extensive unit test suites that verify instruction and cpu flags behavior
|
||||
- maximum simulated performance is a 6502 running at ~100 Mhz (on my machine)
|
||||
- provide a few virtual example machines, one of which is a Commodore-64 (character display mode only for now)
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
|
|
|
@ -2,11 +2,12 @@ package razorvine.c64emu
|
|||
|
||||
import razorvine.ksim65.Cpu6502
|
||||
import razorvine.ksim65.components.MemoryComponent
|
||||
import razorvine.ksim65.components.UByte
|
||||
import java.awt.*
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.KeyListener
|
||||
import java.awt.image.BufferedImage
|
||||
import java.awt.image.VolatileImage
|
||||
import java.awt.image.DataBufferInt
|
||||
import javax.swing.JFrame
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.Timer
|
||||
|
@ -24,29 +25,36 @@ object ScreenDefs {
|
|||
const val DISPLAY_PIXEL_SCALING: Double = 3.0
|
||||
const val BORDER_SIZE = 24
|
||||
|
||||
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||
Color(0x000000), // 0 = black
|
||||
Color(0xFFFFFF), // 1 = white
|
||||
Color(0x813338), // 2 = red
|
||||
Color(0x75cec8), // 3 = cyan
|
||||
Color(0x8e3c97), // 4 = purple
|
||||
Color(0x56ac4d), // 5 = green
|
||||
Color(0x2e2c9b), // 6 = blue
|
||||
Color(0xedf171), // 7 = yellow
|
||||
Color(0x8e5029), // 8 = orange
|
||||
Color(0x553800), // 9 = brown
|
||||
Color(0xc46c71), // 10 = light red
|
||||
Color(0x4a4a4a), // 11 = dark grey
|
||||
Color(0x7b7b7b), // 12 = medium grey
|
||||
Color(0xa9ff9f), // 13 = light green
|
||||
Color(0x706deb), // 14 = light blue
|
||||
Color(0xb2b2b2) // 15 = light grey
|
||||
)
|
||||
class Palette {
|
||||
// this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||
private val sixteenColors = listOf(
|
||||
Color(0x000000), // 0 = black
|
||||
Color(0xFFFFFF), // 1 = white
|
||||
Color(0x813338), // 2 = red
|
||||
Color(0x75cec8), // 3 = cyan
|
||||
Color(0x8e3c97), // 4 = purple
|
||||
Color(0x56ac4d), // 5 = green
|
||||
Color(0x2e2c9b), // 6 = blue
|
||||
Color(0xedf171), // 7 = yellow
|
||||
Color(0x8e5029), // 8 = orange
|
||||
Color(0x553800), // 9 = brown
|
||||
Color(0xc46c71), // 10 = light red
|
||||
Color(0x4a4a4a), // 11 = dark grey
|
||||
Color(0x7b7b7b), // 12 = medium grey
|
||||
Color(0xa9ff9f), // 13 = light green
|
||||
Color(0x706deb), // 14 = light blue
|
||||
Color(0xb2b2b2) // 15 = light grey
|
||||
)
|
||||
operator fun get(i: Int): Color = sixteenColors[i and 15]
|
||||
operator fun get(i: UByte): Color = sixteenColors[i.toInt() and 15]
|
||||
}
|
||||
|
||||
val colorPalette = Palette()
|
||||
}
|
||||
|
||||
private class BitmapScreenPanel(val chargenData: ByteArray, val ram: MemoryComponent) : JPanel() {
|
||||
|
||||
private val fullscreenImage: VolatileImage
|
||||
private val fullscreenImage: BufferedImage
|
||||
private val fullscreenG2d: Graphics2D
|
||||
private val normalCharacters = loadCharacters(false)
|
||||
private val shiftedCharacters = loadCharacters(true)
|
||||
|
@ -54,8 +62,9 @@ private class BitmapScreenPanel(val chargenData: ByteArray, val ram: MemoryCompo
|
|||
init {
|
||||
val ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||
val gd = ge.defaultScreenDevice.defaultConfiguration
|
||||
fullscreenImage = gd.createCompatibleVolatileImage(ScreenDefs.SCREEN_WIDTH + 2*ScreenDefs.BORDER_SIZE,
|
||||
fullscreenImage = gd.createCompatibleImage(ScreenDefs.SCREEN_WIDTH + 2*ScreenDefs.BORDER_SIZE,
|
||||
ScreenDefs.SCREEN_HEIGHT + 2*ScreenDefs.BORDER_SIZE, Transparency.OPAQUE)
|
||||
fullscreenImage.accelerationPriority = 1.0f
|
||||
fullscreenG2d = fullscreenImage.graphics as Graphics2D
|
||||
|
||||
val size = Dimension(
|
||||
|
@ -71,9 +80,8 @@ private class BitmapScreenPanel(val chargenData: ByteArray, val ram: MemoryCompo
|
|||
}
|
||||
|
||||
private fun loadCharacters(shifted: Boolean): Array<BufferedImage> {
|
||||
val chars = Array(256) { BufferedImage(8, 8, BufferedImage.TYPE_BYTE_BINARY) }
|
||||
val chars = Array(256) { BufferedImage(8, 8, BufferedImage.TYPE_BYTE_BINARY).also { it.accelerationPriority=1.0f } }
|
||||
val offset = if (shifted) 256 * 8 else 0
|
||||
// val color = ScreenDefs.colorPalette[14].rgb
|
||||
for (char in 0..255) {
|
||||
for (line in 0..7) {
|
||||
val charbyte = chargenData[offset + char * 8 + line].toInt()
|
||||
|
@ -87,39 +95,84 @@ private class BitmapScreenPanel(val chargenData: ByteArray, val ram: MemoryCompo
|
|||
}
|
||||
|
||||
override fun paint(graphics: Graphics) {
|
||||
// draw the background color
|
||||
fullscreenG2d.background = ScreenDefs.colorPalette[ram[0xd021].toInt() and 15]
|
||||
fullscreenG2d.clearRect(ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT)
|
||||
val windowG2d = graphics as Graphics2D
|
||||
val vicSCROLY = ram[0xd011].toInt()
|
||||
val vicVMCSB = ram[0xd018].toInt()
|
||||
if(vicSCROLY and 0b10000 == 0) {
|
||||
// screen blanked, only display border
|
||||
fullscreenG2d.background = ScreenDefs.colorPalette[ram[0xd020]]
|
||||
fullscreenG2d.clearRect(
|
||||
0, 0,
|
||||
ScreenDefs.SCREEN_WIDTH + 2 * ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_HEIGHT+ 2*ScreenDefs.BORDER_SIZE)
|
||||
} else {
|
||||
// draw the screen border
|
||||
fullscreenG2d.background = ScreenDefs.colorPalette[ram[0xd020]]
|
||||
fullscreenG2d.clearRect(0, 0, ScreenDefs.SCREEN_WIDTH + 2 * ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE)
|
||||
fullscreenG2d.clearRect(
|
||||
0, ScreenDefs.SCREEN_HEIGHT + ScreenDefs.BORDER_SIZE,
|
||||
ScreenDefs.SCREEN_WIDTH + 2 * ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE
|
||||
)
|
||||
fullscreenG2d.clearRect(0, ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_HEIGHT)
|
||||
fullscreenG2d.clearRect(
|
||||
ScreenDefs.SCREEN_WIDTH + ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE,
|
||||
ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_HEIGHT
|
||||
)
|
||||
|
||||
// draw the characters
|
||||
redrawCharacters()
|
||||
if(vicSCROLY and 0b100000 != 0) {
|
||||
// bitmap mode 320x200
|
||||
fullscreenG2d.background = ScreenDefs.colorPalette[ram[0xd021]]
|
||||
fullscreenG2d.clearRect(ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT)
|
||||
// TODO vic address offset in memory, so that medusa.prg will work
|
||||
val bitmap = ram.getPages(if(vicVMCSB and 0b00001000 != 0) 32 else 0, 32)
|
||||
val colorBytes = ram.getPages((vicVMCSB ushr 4) shl 2, 4)
|
||||
val pixels: IntArray = (fullscreenImage.raster.dataBuffer as DataBufferInt).data
|
||||
for(y in 0 until ScreenDefs.SCREEN_HEIGHT) {
|
||||
for(x in 0 until ScreenDefs.SCREEN_WIDTH step 8) {
|
||||
val colorbyte = ScreenDefs.SCREEN_WIDTH_CHARS*(y ushr 3) + (x ushr 3)
|
||||
val bgColor = ScreenDefs.colorPalette[colorBytes[colorbyte].toInt() and 15].rgb
|
||||
val fgColor = ScreenDefs.colorPalette[colorBytes[colorbyte].toInt() ushr 4].rgb
|
||||
draw8Pixels(pixels, x, y, bitmap, fgColor, bgColor)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal character mode
|
||||
fullscreenG2d.background = ScreenDefs.colorPalette[ram[0xd021]]
|
||||
fullscreenG2d.clearRect(ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT)
|
||||
redrawCharacters()
|
||||
}
|
||||
}
|
||||
|
||||
// draw the screen border
|
||||
fullscreenG2d.background = ScreenDefs.colorPalette[ram[0xd020].toInt() and 15]
|
||||
fullscreenG2d.clearRect(0, 0, ScreenDefs.SCREEN_WIDTH + 2*ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE)
|
||||
fullscreenG2d.clearRect(0, ScreenDefs.SCREEN_HEIGHT+ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_WIDTH + 2*ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE)
|
||||
fullscreenG2d.clearRect(0, ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_HEIGHT)
|
||||
fullscreenG2d.clearRect(ScreenDefs.SCREEN_WIDTH+ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, ScreenDefs.SCREEN_HEIGHT)
|
||||
|
||||
// scale and draw the image to the window
|
||||
val g2d = graphics as Graphics2D
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
|
||||
g2d.drawImage(
|
||||
// scale and draw the image to the window, and simulate a slight scanline effect
|
||||
windowG2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
|
||||
windowG2d.drawImage(
|
||||
fullscreenImage, 0, 0, (fullscreenImage.width * ScreenDefs.DISPLAY_PIXEL_SCALING).toInt(),
|
||||
(fullscreenImage.height * ScreenDefs.DISPLAY_PIXEL_SCALING).toInt(), null
|
||||
)
|
||||
|
||||
// simulate a slight scan line effect
|
||||
g2d.color = Color(0, 0, 0, 40)
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
|
||||
windowG2d.color = Color(0, 0, 0, 40)
|
||||
windowG2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
|
||||
val width = fullscreenImage.width * ScreenDefs.DISPLAY_PIXEL_SCALING.toInt()
|
||||
val height = fullscreenImage.height * ScreenDefs.DISPLAY_PIXEL_SCALING.toInt()
|
||||
for (y in 0 until height step ScreenDefs.DISPLAY_PIXEL_SCALING.toInt()) {
|
||||
g2d.drawLine(0, y, width, y)
|
||||
windowG2d.drawLine(0, y, width, y)
|
||||
}
|
||||
Toolkit.getDefaultToolkit().sync()
|
||||
}
|
||||
|
||||
private fun draw8Pixels(pixels: IntArray, xstart: Int, y: Int,
|
||||
bitmap: Array<UByte>, fgColorRgb: Int, bgColorRgb: Int) {
|
||||
val offset = ScreenDefs.BORDER_SIZE + ScreenDefs.BORDER_SIZE*fullscreenImage.width +
|
||||
xstart + y * fullscreenImage.width
|
||||
val byte = bitmap[ScreenDefs.SCREEN_WIDTH_CHARS*(y and 248) + (y and 7) + (xstart and 504)].toInt()
|
||||
pixels[offset + 0] = if(byte and 0b10000000 != 0) fgColorRgb else bgColorRgb
|
||||
pixels[offset + 1] = if(byte and 0b01000000 != 0) fgColorRgb else bgColorRgb
|
||||
pixels[offset + 2] = if(byte and 0b00100000 != 0) fgColorRgb else bgColorRgb
|
||||
pixels[offset + 3] = if(byte and 0b00010000 != 0) fgColorRgb else bgColorRgb
|
||||
pixels[offset + 4] = if(byte and 0b00001000 != 0) fgColorRgb else bgColorRgb
|
||||
pixels[offset + 5] = if(byte and 0b00000100 != 0) fgColorRgb else bgColorRgb
|
||||
pixels[offset + 6] = if(byte and 0b00000010 != 0) fgColorRgb else bgColorRgb
|
||||
pixels[offset + 7] = if(byte and 0b00000001 != 0) fgColorRgb else bgColorRgb
|
||||
}
|
||||
|
||||
private fun redrawCharacters() {
|
||||
val screen = 0x0400
|
||||
val colors = 0xd800
|
||||
|
|
|
@ -31,7 +31,7 @@ object ScreenDefs {
|
|||
|
||||
private fun loadCharacters(): Array<BufferedImage> {
|
||||
val img = ImageIO.read(javaClass.getResourceAsStream("/charset/unscii8x16.png"))
|
||||
val charactersImage = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
|
||||
val charactersImage = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB).also {it.accelerationPriority=1.0f}
|
||||
charactersImage.createGraphics().drawImage(img, 0, 0, null)
|
||||
|
||||
val black = Color(0, 0, 0).rgb
|
||||
|
|
|
@ -1 +1 @@
|
|||
version=1.3
|
||||
version=1.4
|
||||
|
|
Loading…
Reference in New Issue
Block a user