diff --git a/src/main/kotlin/razorvine/c64emu/GUI.kt b/src/main/kotlin/razorvine/c64emu/GUI.kt index 06106b6..afbe630 100644 --- a/src/main/kotlin/razorvine/c64emu/GUI.kt +++ b/src/main/kotlin/razorvine/c64emu/GUI.kt @@ -6,10 +6,7 @@ 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.DataBufferInt import javax.swing.JFrame -import javax.swing.JPanel import javax.swing.Timer @@ -52,167 +49,6 @@ object ScreenDefs { val colorPalette = Palette() } -private class BitmapScreenPanel(val chargenData: ByteArray, val ram: MemoryComponent) : JPanel() { - - private val fullscreenImage: BufferedImage - private val fullscreenG2d: Graphics2D - private val normalCharacters = loadCharacters(false) - private val shiftedCharacters = loadCharacters(true) - - init { - val ge = GraphicsEnvironment.getLocalGraphicsEnvironment() - val gd = ge.defaultScreenDevice.defaultConfiguration - 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( - fullscreenImage.width * ScreenDefs.DISPLAY_PIXEL_SCALING.toInt(), - fullscreenImage.height*ScreenDefs.DISPLAY_PIXEL_SCALING.toInt() - ) - minimumSize = size - maximumSize = size - preferredSize = size - isFocusable = true - isDoubleBuffered = false - requestFocusInWindow() - } - - private fun loadCharacters(shifted: Boolean): Array { - val chars = Array(256) { BufferedImage(8, 8, BufferedImage.TYPE_BYTE_BINARY).also { it.accelerationPriority=1.0f } } - val offset = if (shifted) 256 * 8 else 0 - for (char in 0..255) { - for (line in 0..7) { - val charbyte = chargenData[offset + char * 8 + line].toInt() - for (x in 0..7) { - if (charbyte and (0b10000000 ushr x) != 0) - chars[char].setRGB(x, line, 0xffffff) - } - } - } - return chars - } - - override fun paint(graphics: Graphics) { - 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 - ) - - 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() - } - } - - // 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 - ) - 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()) { - windowG2d.drawLine(0, y, width, y) - } - Toolkit.getDefaultToolkit().sync() - } - - private fun draw8Pixels(pixels: IntArray, xstart: Int, y: Int, - bitmap: Array, 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 - val shifted = (ram[0xd018].toInt() and 0b00000010) != 0 - for (y in 0 until ScreenDefs.SCREEN_HEIGHT_CHARS) { - for (x in 0 until ScreenDefs.SCREEN_WIDTH_CHARS) { - val char = ram[screen + x + y * ScreenDefs.SCREEN_WIDTH_CHARS].toInt() - val color = ram[colors + x + y * ScreenDefs.SCREEN_WIDTH_CHARS].toInt() - drawColoredChar(x, y, char, color and 15, shifted) - } - } - } - - private val coloredCharacters = mutableMapOf, BufferedImage>() - - private fun drawColoredChar(x: Int, y: Int, char: Int, color: Int, shifted: Boolean) { - var cached = coloredCharacters[Triple(char, color, shifted)] - if (cached == null) { - cached = if (shifted) shiftedCharacters[char] else normalCharacters[char] - val colored = fullscreenG2d.deviceConfiguration.createCompatibleImage(8, 8, BufferedImage.BITMASK) - val sourceRaster = cached.raster - val coloredRaster = colored.raster - val pixelArray = IntArray(4) - val javaColor = ScreenDefs.colorPalette[color] - val coloredPixel = listOf(javaColor.red, javaColor.green, javaColor.blue, javaColor.alpha).toIntArray() - for (pixelY in 0..7) { - for (pixelX in 0..7) { - val source = sourceRaster.getPixel(pixelX, pixelY, pixelArray) - if (source[0] != 0) { - coloredRaster.setPixel(pixelX, pixelY, coloredPixel) - } - } - } - coloredCharacters[Triple(char, color, shifted)] = colored - cached = colored - } - fullscreenG2d.drawImage(cached, x * 8 + ScreenDefs.BORDER_SIZE, y * 8 + ScreenDefs.BORDER_SIZE, null) - } -} - class MainC64Window( title: String, chargenData: ByteArray, @@ -220,14 +56,12 @@ class MainC64Window( val cpu: Cpu6502, val keypressCia: Cia ) : JFrame(title), KeyListener { - private val canvas = BitmapScreenPanel(chargenData, ram) - init { defaultCloseOperation = EXIT_ON_CLOSE isResizable = false isFocusable = true - add(canvas) + add(Screen(chargenData, ram)) addKeyListener(this) pack() setLocationRelativeTo(null) diff --git a/src/main/kotlin/razorvine/c64emu/Screen.kt b/src/main/kotlin/razorvine/c64emu/Screen.kt new file mode 100644 index 0000000..9a5c4aa --- /dev/null +++ b/src/main/kotlin/razorvine/c64emu/Screen.kt @@ -0,0 +1,252 @@ +package razorvine.c64emu + +import razorvine.ksim65.components.Address +import razorvine.ksim65.components.MemoryComponent +import razorvine.ksim65.components.UByte +import java.awt.* +import java.awt.image.BufferedImage +import java.awt.image.DataBufferInt +import javax.swing.JPanel + +/** + * The rendering logic of the screen of the C64. + * It supports: Character mode, + * High res bitmap mode (320*200), Multicolor bitmap mode (160*200). + * TODO: sprites. Multicolor character mode. Extended background color mode. + */ +internal class Screen(private val chargenData: ByteArray, val ram: MemoryComponent) : JPanel() { + + private val fullscreenImage: BufferedImage + private val fullscreenG2d: Graphics2D + private val normalCharacters = loadCharacters(false) + private val shiftedCharacters = loadCharacters(true) + + init { + val ge = GraphicsEnvironment.getLocalGraphicsEnvironment() + val gd = ge.defaultScreenDevice.defaultConfiguration + 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( + fullscreenImage.width * ScreenDefs.DISPLAY_PIXEL_SCALING.toInt(), + fullscreenImage.height * ScreenDefs.DISPLAY_PIXEL_SCALING.toInt() + ) + minimumSize = size + maximumSize = size + preferredSize = size + isFocusable = true + isDoubleBuffered = false + requestFocusInWindow() + } + + private fun loadCharacters(shifted: Boolean): Array { + val chars = Array(256) { + BufferedImage(8, 8, BufferedImage.TYPE_BYTE_BINARY) + .also { it.accelerationPriority = 1.0f } + } + val offset = if (shifted) 256 * 8 else 0 + for (char in 0..255) { + for (line in 0..7) { + val charbyte = chargenData[offset + char * 8 + line].toInt() + for (x in 0..7) { + if (charbyte and (0b10000000 ushr x) != 0) + chars[char].setRGB(x, line, 0xffffff) + } + } + } + return chars + } + + override fun paint(graphics: Graphics) { + val windowG2d = graphics as Graphics2D + val vicSCROLY = ram[0xd011].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 { + val vicSCROLX = ram[0xd016].toInt() + val vicVMCSB = ram[0xd018].toInt() + val multiColorMode = vicSCROLX and 0b00010000 != 0 + val vicBank = when (ram[0xdd00].toInt() and 0b00000011) { + 0b00 -> 0xc000 + 0b01 -> 0x8000 + 0b10 -> 0x4000 + else -> 0x0000 + } + + // 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 + ) + + if (vicSCROLY and 0b00100000 != 0) + renderBitmapMode(vicBank, vicVMCSB, multiColorMode) + else { + fullscreenG2d.background = ScreenDefs.colorPalette[ram[0xd021]] + fullscreenG2d.clearRect( + ScreenDefs.BORDER_SIZE, ScreenDefs.BORDER_SIZE, + ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT + ) + renderCharacterMode(vicBank, vicVMCSB, multiColorMode) + } + } + + // 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 + ) + 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()) { + windowG2d.drawLine(0, y, width, y) + } + Toolkit.getDefaultToolkit().sync() + } + + private fun renderCharacterMode(vicBank: Address, vicVMCSB: Int, multiColorMode: Boolean) { + if (multiColorMode) { + TODO("multicolor character mode") + } else { + // normal character mode + val screenAddress = vicBank + (vicVMCSB ushr 4) shl 10 + val charsetAddress = (vicVMCSB and 0b00001110) shl 10 + if (charsetAddress == 0x1000 || charsetAddress == 0x1800) { + // use built-in character ROM + for (y in 0 until ScreenDefs.SCREEN_HEIGHT_CHARS) { + for (x in 0 until ScreenDefs.SCREEN_WIDTH_CHARS) { + val char = ram[screenAddress + x + y * ScreenDefs.SCREEN_WIDTH_CHARS].toInt() + val color = ram[0xd800 + x + y * ScreenDefs.SCREEN_WIDTH_CHARS].toInt() // colors always at $d800 + drawColoredChar(x, y, char, color, charsetAddress == 0x1800) + } + } + } + /* else { + // TODO: custom charsets from RAM. Currently the charset ROM is just loaded externally. + } */ + } + } + + private fun renderBitmapMode(vicBank: Address, vicVMCSB: Int, multiColorMode: Boolean) { + val bitmap = ram.getPages((vicBank ushr 8) + if (vicVMCSB and 0b00001000 != 0) 32 else 0, 32) + val colorBytes = ram.getPages((vicBank ushr 8) + ((vicVMCSB ushr 4) shl 2), 4) + val pixels: IntArray = (fullscreenImage.raster.dataBuffer as DataBufferInt).data + val screenColor = ScreenDefs.colorPalette[ram[0xd021]].rgb + if (multiColorMode) { + // multicolor bitmap mode 160x200 + val fourColors = IntArray(4) + fourColors[0b00] = screenColor + for (y in 0 until ScreenDefs.SCREEN_HEIGHT) { + for (x in 0 until ScreenDefs.SCREEN_WIDTH/2 step 4) { + val colorIdx = ScreenDefs.SCREEN_WIDTH_CHARS * (y ushr 3) + (x ushr 2) + fourColors[0b01] = ScreenDefs.colorPalette[colorBytes[colorIdx].toInt() ushr 4].rgb + fourColors[0b10] = ScreenDefs.colorPalette[colorBytes[colorIdx].toInt()].rgb + fourColors[0b11] = ScreenDefs.colorPalette[ram[0xd800 + colorIdx].toInt()].rgb + draw4bitmapPixelsMc(pixels, x, y, bitmap, fourColors) + } + } + } else { + // bitmap mode 320x200 + for (y in 0 until ScreenDefs.SCREEN_HEIGHT) { + for (x in 0 until ScreenDefs.SCREEN_WIDTH step 8) { + val colorIdx = ScreenDefs.SCREEN_WIDTH_CHARS * (y ushr 3) + (x ushr 3) + val bgColor = ScreenDefs.colorPalette[colorBytes[colorIdx].toInt()].rgb + val fgColor = ScreenDefs.colorPalette[colorBytes[colorIdx].toInt() ushr 4].rgb + draw8bitmapPixels(pixels, x, y, bitmap, fgColor, bgColor) + } + } + } + } + + private fun draw8bitmapPixels( + pixels: IntArray, xstart: Int, y: Int, + bitmap: Array, 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] = 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 draw4bitmapPixelsMc(pixels: IntArray, xstart: Int, y: Int, + bitmap: Array, fourColors: IntArray) { + val realx = xstart * 2 + val offset = ScreenDefs.BORDER_SIZE + ScreenDefs.BORDER_SIZE * fullscreenImage.width + + realx + y * fullscreenImage.width + val byte = bitmap[ScreenDefs.SCREEN_WIDTH_CHARS * (y and 248) + (y and 7) + (realx and 504)].toInt() + val colors = listOf( + byte and 0b11000000 ushr 6, + byte and 0b00110000 ushr 4, + byte and 0b00001100 ushr 2, + byte and 0b00000011 + ) + pixels[offset] = fourColors[colors[0]] + pixels[offset + 1] = fourColors[colors[0]] + pixels[offset + 2] = fourColors[colors[1]] + pixels[offset + 3] = fourColors[colors[1]] + pixels[offset + 4] = fourColors[colors[2]] + pixels[offset + 5] = fourColors[colors[2]] + pixels[offset + 6] = fourColors[colors[3]] + pixels[offset + 7] = fourColors[colors[3]] + } + + private val coloredCharacters = mutableMapOf, BufferedImage>() + + private fun drawColoredChar(x: Int, y: Int, char: Int, color: Int, shifted: Boolean) { + var cached = coloredCharacters[Triple(char, color, shifted)] + if (cached == null) { + cached = if (shifted) shiftedCharacters[char] else normalCharacters[char] + val colored = fullscreenG2d.deviceConfiguration.createCompatibleImage(8, 8, BufferedImage.BITMASK) + val sourceRaster = cached.raster + val coloredRaster = colored.raster + val pixelArray = IntArray(4) + val javaColor = ScreenDefs.colorPalette[color] + val coloredPixel = listOf(javaColor.red, javaColor.green, javaColor.blue, javaColor.alpha).toIntArray() + for (pixelY in 0..7) { + for (pixelX in 0..7) { + val source = sourceRaster.getPixel(pixelX, pixelY, pixelArray) + if (source[0] != 0) { + coloredRaster.setPixel(pixelX, pixelY, coloredPixel) + } + } + } + coloredCharacters[Triple(char, color, shifted)] = colored + cached = colored + } + fullscreenG2d.drawImage(cached, x * 8 + ScreenDefs.BORDER_SIZE, y * 8 + ScreenDefs.BORDER_SIZE, null) + } +} diff --git a/src/main/kotlin/razorvine/c64emu/c64Main.kt b/src/main/kotlin/razorvine/c64emu/c64Main.kt index 9f3b59b..80cd9a8 100644 --- a/src/main/kotlin/razorvine/c64emu/c64Main.kt +++ b/src/main/kotlin/razorvine/c64emu/c64Main.kt @@ -65,7 +65,7 @@ class C64Machine(title: String) : IVirtualMachine { hostDisplay.start(30) } - fun breakpointKernelLoad(cpu: Cpu6502, pc: Address): Cpu6502.BreakpointResultAction { + private fun breakpointKernelLoad(cpu: Cpu6502, pc: Address): Cpu6502.BreakpointResultAction { if (cpu.regA == 0) { val fnlen = ram[0xb7] // file name length val fa = ram[0xba] // device number @@ -85,7 +85,7 @@ class C64Machine(title: String) : IVirtualMachine { } else return Cpu6502.BreakpointResultAction(changePC = 0xf707) // 'device not present' (VERIFY command not supported) } - fun breakpointKernelSave(cpu: Cpu6502, pc: Address): Cpu6502.BreakpointResultAction { + private fun breakpointKernelSave(cpu: Cpu6502, pc: Address): Cpu6502.BreakpointResultAction { val fnlen = ram[0xb7] // file name length // val fa = ram[0xba] // device number // val sa = ram[0xb9] // secondary address @@ -107,16 +107,12 @@ class C64Machine(title: String) : IVirtualMachine { } else Cpu6502.BreakpointResultAction(changePC = 0xf710) // 'missing file name' } - fun breakpointBRK(cpu: Cpu6502, pc: Address): Cpu6502.BreakpointResultAction { + private fun breakpointBRK(cpu: Cpu6502, pc: Address): Cpu6502.BreakpointResultAction { throw Cpu6502.InstructionError("BRK instruction hit at $${hexW(pc)}") } - private fun searchAndLoadFile( - filename: String, - device: UByte, - secondary: UByte, - basicLoadAddress: Address - ): Address? { + private fun searchAndLoadFile(filename: String, + device: UByte, secondary: UByte, basicLoadAddress: Address): Address? { when (filename) { "*" -> { // load the first file in the directory @@ -170,11 +166,9 @@ class C64Machine(title: String) : IVirtualMachine { } } - private fun makeDirListing( - dirname: String, - files: Map, Pair>, - basicLoadAddress: Address - ): Array { + private fun makeDirListing(dirname: String, + files: Map, Pair>, basicLoadAddress: Address): Array + { var address = basicLoadAddress val listing = mutableListOf() fun addLine(lineNumber: Int, line: String) { @@ -193,8 +187,8 @@ class C64Machine(title: String) : IVirtualMachine { totalBlocks += blocksize val filename = it.key.first.take(16) val padding1 = " ".substring(blocksize.toString().length) - val padding2 = " ".substring(filename.length) - addLine(blocksize, "$padding1 \"$filename\" $padding2 ${it.key.second.take(3).padEnd(3)}") + val padding2 = " ".substring(filename.length) + addLine(blocksize, "$padding1 \"$filename\" $padding2${it.key.second.take(3).padEnd(3)}") } addLine(kotlin.math.max(0, 664 - totalBlocks), "BLOCKS FREE.") listing.add(0) @@ -249,19 +243,31 @@ class C64Machine(title: String) : IVirtualMachine { }.start() val timer = java.util.Timer("cpu-cycle", true) - timer.scheduleAtFixedRate(0, 1000L/VicII.framerate) { - if(!paused) { + timer.scheduleAtFixedRate(0, 1000L / VicII.framerate) { + if (!paused) { // we synchronise cpu cycles to the vertical blank of the Vic chip // this should result in ~1 Mhz cpu speed try { while (vic.vsync) step() while (!vic.vsync) step() - } catch(rx: RuntimeException) { - JOptionPane.showMessageDialog(hostDisplay, "Run time error: $rx", "Error during execution", JOptionPane.ERROR_MESSAGE) + } catch (rx: RuntimeException) { + JOptionPane.showMessageDialog( + hostDisplay, + "Run time error: $rx", + "Error during execution", + JOptionPane.ERROR_MESSAGE + ) this.cancel() - } catch(ex: Error) { - JOptionPane.showMessageDialog(hostDisplay, "Run time error: $ex", "Error during execution", JOptionPane.ERROR_MESSAGE) + throw rx + } catch (ex: Error) { + JOptionPane.showMessageDialog( + hostDisplay, + "Run time error: $ex", + "Error during execution", + JOptionPane.ERROR_MESSAGE + ) this.cancel() + throw ex } } } diff --git a/src/main/kotlin/razorvine/ksim65/Cpu6502.kt b/src/main/kotlin/razorvine/ksim65/Cpu6502.kt index c06ae28..afbdb80 100644 --- a/src/main/kotlin/razorvine/ksim65/Cpu6502.kt +++ b/src/main/kotlin/razorvine/ksim65/Cpu6502.kt @@ -16,8 +16,6 @@ open class Cpu6502 : BusComponent() { var tracing: ((state:String) -> Unit)? = null var totalCycles = 0L protected set - private var speedMeasureCycles = 0L - private var speedMeasureStart = System.nanoTime() private var resetTime = System.nanoTime() var breakpointForBRK: BreakpointHandler? = null @@ -166,14 +164,6 @@ open class Cpu6502 : BusComponent() { totalCycles) } - fun startSpeedMeasureInterval() { - speedMeasureCycles = totalCycles - speedMeasureStart = System.nanoTime() - } - - fun measureAvgIntervalSpeedKhz() = - (totalCycles-speedMeasureCycles).toDouble() / (System.nanoTime() - speedMeasureStart) * 1_000_000 - // has an interrupt been requested? protected enum class Interrupt { IRQ,