1
0
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:
Irmen de Jong 2019-10-11 01:44:20 +02:00
parent cfeb71c4af
commit 31c50991c1
4 changed files with 103 additions and 49 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1 +1 @@
version=1.3
version=1.4