diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt index 45954d41d..7a7f3ab70 100644 --- a/compiler/src/prog8/astvm/AstVm.kt +++ b/compiler/src/prog8/astvm/AstVm.kt @@ -542,10 +542,10 @@ class AstVm(val program: Program) { dialog.canvas.clearScreen(6) } "c64scr.clear_screen" -> { - dialog.canvas.clearScreen(args[0].integerValue()) + dialog.canvas.clearScreen(args[0].integerValue().toShort()) } "c64scr.setcc" -> { - dialog.canvas.setChar(args[0].integerValue(), args[1].integerValue(), args[2].integerValue().toShort()) + dialog.canvas.setChar(args[0].integerValue(), args[1].integerValue(), args[2].integerValue().toShort(), args[3].integerValue().toShort()) } "c64scr.plot" -> { dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue()) diff --git a/compiler/src/prog8/astvm/ScreenDialog.kt b/compiler/src/prog8/astvm/ScreenDialog.kt index 8f6ffcedb..e63265e5b 100644 --- a/compiler/src/prog8/astvm/ScreenDialog.kt +++ b/compiler/src/prog8/astvm/ScreenDialog.kt @@ -1,6 +1,7 @@ package prog8.astvm import prog8.compiler.target.c64.Charset +import prog8.compiler.target.c64.Colors import prog8.compiler.target.c64.Petscii import java.awt.* import java.awt.event.KeyEvent @@ -47,20 +48,20 @@ class BitmapScreenPanel : KeyListener, JPanel() { g2d.drawImage(image, 0, 0, image.width * 3, image.height * 3, null) } - fun clearScreen(color: Int) { - g2d.background = palette[color and 15] - g2d.clearRect(0, 0, BitmapScreenPanel.SCREENWIDTH, BitmapScreenPanel.SCREENHEIGHT) + fun clearScreen(color: Short) { + g2d.background = Colors.palette[color % Colors.palette.size] + g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT) cursorX = 0 cursorY = 0 } - fun setPixel(x: Int, y: Int, color: Int) { - image.setRGB(x, y, palette[color and 15].rgb) + fun setPixel(x: Int, y: Int, color: Short) { + image.setRGB(x, y, Colors.palette[color % Colors.palette.size].rgb) } - fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int) { - g2d.color = palette[color and 15] + fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) { + g2d.color = Colors.palette[color % Colors.palette.size] g2d.drawLine(x1, y1, x2, y2) } - fun printText(text: String, color: Int, lowercase: Boolean) { + fun printText(text: String, color: Short, lowercase: Boolean) { val t2 = text.substringBefore(0.toChar()) val lines = t2.split('\n') for(line in lines.withIndex()) { @@ -71,15 +72,12 @@ class BitmapScreenPanel : KeyListener, JPanel() { } } } - private fun printTextSingleLine(text: String, color: Int, lowercase: Boolean) { - if(color!=1) { - TODO("text can only be white for now") - } + private fun printTextSingleLine(text: String, color: Short, lowercase: Boolean) { for(clearx in cursorX until cursorX+text.length) { g2d.clearRect(8*clearx, 8*y, 8, 8) } for(sc in Petscii.encodeScreencode(text, lowercase)) { - setChar(cursorX, cursorY, sc) + setChar(cursorX, cursorY, sc, color) cursorX++ if(cursorX>=(SCREENWIDTH/8)) { cursorY++ @@ -93,7 +91,7 @@ class BitmapScreenPanel : KeyListener, JPanel() { cursorX=0 cursorY++ } else { - setChar(cursorX, cursorY, char) + setChar(cursorX, cursorY, char, 1) cursorX++ if (cursorX >= (SCREENWIDTH / 8)) { cursorY++ @@ -102,9 +100,11 @@ class BitmapScreenPanel : KeyListener, JPanel() { } } - fun setChar(x: Int, y: Int, screenCode: Short) { + fun setChar(x: Int, y: Int, screenCode: Short, color: Short) { g2d.clearRect(8*x, 8*y, 8, 8) - g2d.drawImage(Charset.shiftedChars[screenCode.toInt()], 8*x, 8*y , null) + val colorIdx = (color % Colors.palette.size).toShort() + val coloredImage = Charset.getColoredChar(screenCode, colorIdx) + g2d.drawImage(coloredImage, 8*x, 8*y , null) } fun setCursorPos(x: Int, y: Int) { @@ -116,18 +116,16 @@ class BitmapScreenPanel : KeyListener, JPanel() { return Pair(cursorX, cursorY) } - fun writeText(x: Int, y: Int, text: String, color: Int, lowercase: Boolean) { + fun writeText(x: Int, y: Int, text: String, color: Short, lowercase: Boolean) { + val colorIdx = (color % Colors.palette.size).toShort() var xx=x - if(color!=1) { - TODO("text can only be white for now") - } for(clearx in xx until xx+text.length) { g2d.clearRect(8*clearx, 8*y, 8, 8) } for(sc in Petscii.encodeScreencode(text, lowercase)) { if(sc==0.toShort()) break - setChar(xx++, y, sc) + setChar(xx++, y, sc, colorIdx) } } @@ -136,24 +134,6 @@ class BitmapScreenPanel : KeyListener, JPanel() { const val SCREENWIDTH = 320 const val SCREENHEIGHT = 200 const val SCALING = 3 - val palette = 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 - ) } } @@ -171,19 +151,19 @@ class ScreenDialog : JFrame() { // the borders (top, left, right, bottom) val borderTop = JPanel().apply { preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } val borderBottom = JPanel().apply { preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } val borderLeft = JPanel().apply { preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } val borderRight = JPanel().apply { preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } var c = GridBagConstraints() c.gridx=0; c.gridy=1; c.gridwidth=3 diff --git a/compiler/src/prog8/compiler/target/c64/Commodore64.kt b/compiler/src/prog8/compiler/target/c64/Commodore64.kt index e00721374..c16f605fc 100644 --- a/compiler/src/prog8/compiler/target/c64/Commodore64.kt +++ b/compiler/src/prog8/compiler/target/c64/Commodore64.kt @@ -183,4 +183,60 @@ object Charset { val normalChars = scanChars(normalImg) val shiftedChars = scanChars(shiftedImg) + + private val coloredNormalChars = mutableMapOf>() + + fun getColoredChar(screenCode: Short, color: Short): BufferedImage { + val colorIdx = (color % Colors.palette.size).toShort() + val chars = coloredNormalChars[colorIdx] + if(chars!=null) + return chars[screenCode.toInt()] + + val coloredChars = mutableListOf() + val transparent = Color(0,0,0,0).rgb + val rgb = Colors.palette[colorIdx.toInt()].rgb + for(c in normalChars) { + val colored = c.copy() + for(y in 0 until colored.height) + for(x in 0 until colored.width) { + if(colored.getRGB(x, y)!=transparent) { + colored.setRGB(x, y, rgb) + } + } + coloredChars.add(colored) + } + coloredNormalChars[colorIdx] = coloredChars.toTypedArray() + return coloredNormalChars.getValue(colorIdx)[screenCode.toInt()] + } + +} + +private fun BufferedImage.copy(): BufferedImage { + val bcopy = BufferedImage(this.width, this.height, this.type) + val g = bcopy.graphics + g.drawImage(this, 0, 0, null) + g.dispose() + return bcopy +} + + +object Colors { + val palette = 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 + ) } diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index 6527cc873..1a1c83012 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -6,6 +6,9 @@ import kotlin.math.log2 /* todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it) + + Also see https://egorbo.com/peephole-optimizations.html + */ internal class SimplifyExpressions(private val program: Program) : IAstProcessor { diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index 4511c508e..b053fc0ed 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -8,7 +8,7 @@ import kotlin.math.floor /* todo: subroutines with 1 or 2 byte args or 1 word arg can be converted to asm sub calling convention (args in registers) - todo: implement usage counters for labels, variables (locals and heap), blocks. Remove if count is zero. + todo: implement usage counters for variables (locals and heap), blocks. Remove if count is zero. todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this */ diff --git a/compiler/src/prog8/stackvm/ScreenDialog.kt b/compiler/src/prog8/stackvm/ScreenDialog.kt index a43644cf8..bdf34d2b6 100644 --- a/compiler/src/prog8/stackvm/ScreenDialog.kt +++ b/compiler/src/prog8/stackvm/ScreenDialog.kt @@ -1,6 +1,7 @@ package prog8.stackvm import prog8.compiler.target.c64.Charset +import prog8.compiler.target.c64.Colors import prog8.compiler.target.c64.Petscii import java.awt.* import java.awt.event.KeyEvent @@ -47,20 +48,20 @@ class BitmapScreenPanel : KeyListener, JPanel() { g2d.drawImage(image, 0, 0, image.width * 3, image.height * 3, null) } - fun clearScreen(color: Int) { - g2d.background = palette[color and 15] - g2d.clearRect(0, 0, BitmapScreenPanel.SCREENWIDTH, BitmapScreenPanel.SCREENHEIGHT) + fun clearScreen(color: Short) { + g2d.background = Colors.palette[color % Colors.palette.size] + g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT) cursorX = 0 cursorY = 0 } - fun setPixel(x: Int, y: Int, color: Int) { - image.setRGB(x, y, palette[color and 15].rgb) + fun setPixel(x: Int, y: Int, color: Short) { + image.setRGB(x, y, Colors.palette[color % Colors.palette.size].rgb) } - fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int) { - g2d.color = palette[color and 15] + fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) { + g2d.color = Colors.palette[color % Colors.palette.size] g2d.drawLine(x1, y1, x2, y2) } - fun printText(text: String, color: Int, lowercase: Boolean) { + fun printText(text: String, color: Short, lowercase: Boolean) { val lines = text.split('\n') for(line in lines.withIndex()) { printTextSingleLine(line.value, color, lowercase) @@ -70,15 +71,12 @@ class BitmapScreenPanel : KeyListener, JPanel() { } } } - private fun printTextSingleLine(text: String, color: Int, lowercase: Boolean) { - if(color!=1) { - TODO("text can only be white for now") - } + private fun printTextSingleLine(text: String, color: Short, lowercase: Boolean) { for(clearx in cursorX until cursorX+text.length) { g2d.clearRect(8*clearx, 8*y, 8, 8) } for(sc in Petscii.encodeScreencode(text, lowercase)) { - setChar(cursorX, cursorY, sc) + setChar(cursorX, cursorY, sc, color) cursorX++ if(cursorX>=(SCREENWIDTH/8)) { cursorY++ @@ -92,7 +90,7 @@ class BitmapScreenPanel : KeyListener, JPanel() { cursorX=0 cursorY++ } else { - setChar(cursorX, cursorY, char) + setChar(cursorX, cursorY, char, 1) cursorX++ if (cursorX >= (SCREENWIDTH / 8)) { cursorY++ @@ -101,9 +99,11 @@ class BitmapScreenPanel : KeyListener, JPanel() { } } - fun setChar(x: Int, y: Int, screenCode: Short) { + fun setChar(x: Int, y: Int, screenCode: Short, color: Short) { g2d.clearRect(8*x, 8*y, 8, 8) - g2d.drawImage(Charset.shiftedChars[screenCode.toInt()], 8*x, 8*y , null) + val colorIdx = (color % Colors.palette.size).toShort() + val coloredImage = Charset.getColoredChar(screenCode, colorIdx) + g2d.drawImage(coloredImage, 8*x, 8*y , null) } fun setCursorPos(x: Int, y: Int) { @@ -115,16 +115,13 @@ class BitmapScreenPanel : KeyListener, JPanel() { return Pair(cursorX, cursorY) } - fun writeText(x: Int, y: Int, text: String, color: Int, lowercase: Boolean) { + fun writeText(x: Int, y: Int, text: String, color: Short, lowercase: Boolean) { var xx=x - if(color!=1) { - TODO("text can only be white for now") - } for(clearx in xx until xx+text.length) { g2d.clearRect(8*clearx, 8*y, 8, 8) } for(sc in Petscii.encodeScreencode(text, lowercase)) { - setChar(xx++, y, sc) + setChar(xx++, y, sc, color) } } @@ -133,24 +130,6 @@ class BitmapScreenPanel : KeyListener, JPanel() { const val SCREENWIDTH = 320 const val SCREENHEIGHT = 200 const val SCALING = 3 - val palette = 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 - ) } } @@ -168,19 +147,19 @@ class ScreenDialog : JFrame() { // the borders (top, left, right, bottom) val borderTop = JPanel().apply { preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } val borderBottom = JPanel().apply { preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } val borderLeft = JPanel().apply { preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } val borderRight = JPanel().apply { preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = BitmapScreenPanel.palette[14] + background = Colors.palette[14] } var c = GridBagConstraints() c.gridx=0; c.gridy=1; c.gridwidth=3 diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 130d26133..5d04d7a61 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -1886,7 +1886,7 @@ class StackVm(private var traceOutputFile: String?) { else { when(ins.callLabel) { "c64.CLEARSCR" -> { - canvas?.clearScreen(mem.getUByte(0xd021).toInt()) + canvas?.clearScreen(mem.getUByte(0xd021)) callstack.pop() } "c64.CHROUT" -> { @@ -1974,25 +1974,25 @@ class StackVm(private var traceOutputFile: String?) { // plot pixel at (x, y, color) from stack val color = evalstack.pop() val (y, x) = evalstack.pop2() - canvas?.setPixel(x.integerValue(), y.integerValue(), color.integerValue()) + canvas?.setPixel(x.integerValue(), y.integerValue(), color.integerValue().toShort()) } Syscall.VM_GFX_LINE -> { // draw line at (x1, y1, x2, y2, color) from stack val color = evalstack.pop() val (y2, x2) = evalstack.pop2() val (y1, x1) = evalstack.pop2() - canvas?.drawLine(x1.integerValue(), y1.integerValue(), x2.integerValue(), y2.integerValue(), color.integerValue()) + canvas?.drawLine(x1.integerValue(), y1.integerValue(), x2.integerValue(), y2.integerValue(), color.integerValue().toShort()) } Syscall.VM_GFX_CLEARSCR -> { val color = evalstack.pop() - canvas?.clearScreen(color.integerValue()) + canvas?.clearScreen(color.integerValue().toShort()) } Syscall.VM_GFX_TEXT -> { val textPtr = evalstack.pop().integerValue() val color = evalstack.pop() val (cy, cx) = evalstack.pop2() val text = heap.get(textPtr) - canvas?.writeText(cx.integerValue(), cy.integerValue(), text.str!!, color.integerValue(), true) + canvas?.writeText(cx.integerValue(), cy.integerValue(), text.str!!, color.integerValue().toShort(), true) } Syscall.FUNC_RND -> evalstack.push(RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255)) Syscall.FUNC_RNDW -> evalstack.push(RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535)) @@ -2317,8 +2317,8 @@ class StackVm(private var traceOutputFile: String?) { val x = variables.getValue("c64scr.setcc.column").integerValue() val y = variables.getValue("c64scr.setcc.row").integerValue() val char = variables.getValue("c64scr.setcc.char").integerValue() - // val color = variables.getValue("c64scr.setcc.color").integerValue() // text color other than 1 (white) can't be used right now - canvas?.setChar(x, y, char.toShort()) + val color = variables.getValue("c64scr.setcc.color").integerValue() + canvas?.setChar(x, y, char.toShort(), color.toShort()) } else -> throw VmExecutionException("unimplemented syscall $syscall") } diff --git a/examples/test.p8 b/examples/test.p8 index c84bd66e1..86473fd40 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,17 +1,80 @@ -;%import c64utils -;%zeropage basicsafe -%import c64flt +%import c64utils +%zeropage basicsafe ~ main { sub start() { + c64scr.setcc(0,0,160,0) + c64scr.setcc(0,1,160,1) + c64scr.setcc(0,2,160,2) + c64scr.setcc(0,3,160,3) + c64scr.setcc(0,4,160,4) + c64scr.setcc(0,5,160,5) + c64scr.setcc(0,6,160,6) + c64scr.setcc(0,7,160,7) + c64scr.setcc(0,8,160,8) + c64scr.setcc(0,9,160,9) + c64scr.setcc(0,10,160,10) + c64scr.setcc(0,11,160,11) + c64scr.setcc(0,12,160,12) + c64scr.setcc(0,13,160,13) + c64scr.setcc(0,14,160,14) + c64scr.setcc(0,15,160,15) - ;float[] xcoor = [ -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0 ] + c64scr.setcc(1,0,160,0) + c64scr.setcc(1,1,160,1) + c64scr.setcc(1,2,160,2) + c64scr.setcc(1,3,160,3) + c64scr.setcc(1,4,160,4) + c64scr.setcc(1,5,160,5) + c64scr.setcc(1,6,160,6) + c64scr.setcc(1,7,160,7) + c64scr.setcc(1,8,160,8) + c64scr.setcc(1,9,160,9) + c64scr.setcc(1,10,160,10) + c64scr.setcc(1,11,160,11) + c64scr.setcc(1,12,160,12) + c64scr.setcc(1,13,160,13) + c64scr.setcc(1,14,160,14) + c64scr.setcc(1,15,160,15) + c64scr.setcc(2,0,160,0) + c64scr.setcc(2,1,160,1) + c64scr.setcc(2,2,160,2) + c64scr.setcc(2,3,160,3) + c64scr.setcc(2,4,160,4) + c64scr.setcc(2,5,160,5) + c64scr.setcc(2,6,160,6) + c64scr.setcc(2,7,160,7) + c64scr.setcc(2,8,160,8) + c64scr.setcc(2,9,160,9) + c64scr.setcc(2,10,160,10) + c64scr.setcc(2,11,160,11) + c64scr.setcc(2,12,160,12) + c64scr.setcc(2,13,160,13) + c64scr.setcc(2,14,160,14) + c64scr.setcc(2,15,160,15) + + _x: + goto _x + +; for ubyte y in 0 to 3 { +; for ubyte x in 0 to 10 { +; ubyte product = x*y +; c64scr.setcc(x, y, 160, product) +; } +; } +; c64.CHROUT('\n') +; c64.CHROUT('\n') +; +; for ubyte y in 12 to 15 { +; for ubyte x in 0 to 10 { +; ubyte sumv = x+y +; c64scr.setcc(x, y, 160, sumv) +; } +; } - float x - float z = sin(x) * 3 ;ubyte bb = len(xcoor) ; storage for rotated coordinates