mirror of
https://github.com/irmen/ksim65.git
synced 2025-08-06 23:25:15 +00:00
demo prog and bugfixes in screen and rtc
This commit is contained in:
@@ -175,7 +175,7 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener {
|
|||||||
buttonPanel.border = BorderFactory.createTitledBorder("Control")
|
buttonPanel.border = BorderFactory.createTitledBorder("Control")
|
||||||
|
|
||||||
val resetBt = JButton("Reset").also { it.actionCommand = "reset" }
|
val resetBt = JButton("Reset").also { it.actionCommand = "reset" }
|
||||||
val cycleBt = JButton("Cycle").also { it.actionCommand = "cycle" }
|
val cycleBt = JButton("Step").also { it.actionCommand = "step" }
|
||||||
listOf(resetBt, cycleBt, pauseBt).forEach {
|
listOf(resetBt, cycleBt, pauseBt).forEach {
|
||||||
it.addActionListener(this)
|
it.addActionListener(this)
|
||||||
buttonPanel.add(it)
|
buttonPanel.add(it)
|
||||||
@@ -192,8 +192,8 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener {
|
|||||||
vm.bus.reset()
|
vm.bus.reset()
|
||||||
updateCpu(vm.cpu)
|
updateCpu(vm.cpu)
|
||||||
}
|
}
|
||||||
e.actionCommand == "cycle" -> {
|
e.actionCommand == "step" -> {
|
||||||
vm.bus.clock()
|
vm.stepInstruction()
|
||||||
updateCpu(vm.cpu)
|
updateCpu(vm.cpu)
|
||||||
}
|
}
|
||||||
e.actionCommand == "pause" -> {
|
e.actionCommand == "pause" -> {
|
||||||
|
@@ -29,6 +29,7 @@ class VirtualMachine(title: String) {
|
|||||||
init {
|
init {
|
||||||
ram[Cpu6502.RESET_vector] = 0x00
|
ram[Cpu6502.RESET_vector] = 0x00
|
||||||
ram[Cpu6502.RESET_vector + 1] = 0x10
|
ram[Cpu6502.RESET_vector + 1] = 0x10
|
||||||
|
ram.loadPrg(javaClass.getResource("/vmdemo.prg").toURI())
|
||||||
|
|
||||||
bus += rtc
|
bus += rtc
|
||||||
bus += timer
|
bus += timer
|
||||||
@@ -47,38 +48,26 @@ class VirtualMachine(title: String) {
|
|||||||
|
|
||||||
var paused = false
|
var paused = false
|
||||||
|
|
||||||
fun clock() {
|
fun stepInstruction() {
|
||||||
if(!paused) {
|
while (cpu.instrCycles > 0) bus.clock()
|
||||||
bus.clock()
|
bus.clock()
|
||||||
debugWindow.updateCpu(cpu)
|
while (cpu.instrCycles > 0) bus.clock()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
val timer = java.util.Timer("clock", true)
|
||||||
|
timer.scheduleAtFixedRate(1, 1) {
|
||||||
|
if(!paused) {
|
||||||
|
repeat(10) {
|
||||||
|
stepInstruction()
|
||||||
|
}
|
||||||
|
debugWindow.updateCpu(cpu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val machine = VirtualMachine("KSim65 demo virtual machine - using ksim65 v${Version.version}")
|
val machine = VirtualMachine("KSim65 demo virtual machine - using ksim65 v${Version.version}")
|
||||||
val v = 0xd000
|
machine.start()
|
||||||
|
|
||||||
machine.bus[v + 0x08] = 20
|
|
||||||
machine.bus[v + 0x09] = 2
|
|
||||||
val text = ">> Hello this is an example text! 1234567890 <<\n" +
|
|
||||||
"next line 1\n" +
|
|
||||||
"next line 2\n" +
|
|
||||||
"next line 3\rnext line 4\rnext line 5\n" +
|
|
||||||
"a mistakk\be\n\n\n\n\n\n\n\n\n\n"
|
|
||||||
text.forEach {
|
|
||||||
machine.bus[v + 0x0a] = it.toShort()
|
|
||||||
}
|
|
||||||
|
|
||||||
repeat(20) {
|
|
||||||
Thread.sleep(100)
|
|
||||||
"time: ${LocalDateTime.now()}\n".forEach { c ->
|
|
||||||
machine.bus[v + 0x0a] = c.toShort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val timer = java.util.Timer("clock", true)
|
|
||||||
timer.scheduleAtFixedRate(1, 1) {
|
|
||||||
machine.clock()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -49,8 +49,13 @@ class Bus {
|
|||||||
*/
|
*/
|
||||||
fun read(address: Address): UByte {
|
fun read(address: Address): UByte {
|
||||||
memComponents.forEach {
|
memComponents.forEach {
|
||||||
if (address >= it.startAddress && address <= it.endAddress)
|
if (address >= it.startAddress && address <= it.endAddress) {
|
||||||
return it[address]
|
val data = it[address]
|
||||||
|
require(data in 0..255) {
|
||||||
|
"data must be a byte 0..255"
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0xff
|
return 0xff
|
||||||
}
|
}
|
||||||
@@ -60,7 +65,9 @@ class Bus {
|
|||||||
* Any memory mapped component that listens to the address, will receive the data.
|
* Any memory mapped component that listens to the address, will receive the data.
|
||||||
*/
|
*/
|
||||||
fun write(address: Address, data: UByte) {
|
fun write(address: Address, data: UByte) {
|
||||||
require(data in 0..255) { "data must be a byte 0..255" }
|
require(data in 0..255) {
|
||||||
|
"data must be a byte 0..255"
|
||||||
|
}
|
||||||
memComponents.forEach {
|
memComponents.forEach {
|
||||||
if (address >= it.startAddress && address <= it.endAddress)
|
if (address >= it.startAddress && address <= it.endAddress)
|
||||||
it[address] = data
|
it[address] = data
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package razorvine.ksim65.components
|
package razorvine.ksim65.components
|
||||||
|
|
||||||
|
import razorvine.examplemachine.ScreenDefs
|
||||||
import razorvine.ksim65.IHostInterface
|
import razorvine.ksim65.IHostInterface
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@@ -67,11 +68,17 @@ class Display(
|
|||||||
|
|
||||||
override operator fun get(address: Address): UByte {
|
override operator fun get(address: Address): UByte {
|
||||||
return when(address-startAddress) {
|
return when(address-startAddress) {
|
||||||
|
0x00 -> charposX.toShort()
|
||||||
|
0x01 -> charposY.toShort()
|
||||||
0x02 -> {
|
0x02 -> {
|
||||||
if(charposY in 0 until charHeight && charposX in 0 until charWidth) {
|
if(charposY in 0 until charHeight && charposX in 0 until charWidth) {
|
||||||
charMatrix[charposY][charposX]
|
charMatrix[charposY][charposX]
|
||||||
} else 0xff
|
} else 0xff
|
||||||
}
|
}
|
||||||
|
0x03 -> (pixelX and 0xff).toShort()
|
||||||
|
0x04 -> (pixelX ushr 8).toShort()
|
||||||
|
0x05 -> (pixelY and 0xff).toShort()
|
||||||
|
0x06 -> (pixelY ushr 8).toShort()
|
||||||
0x07 -> if(host.getPixel(pixelX, pixelY)) 1 else 0
|
0x07 -> if(host.getPixel(pixelX, pixelY)) 1 else 0
|
||||||
0x08 -> cursorX.toShort()
|
0x08 -> cursorX.toShort()
|
||||||
0x09 -> cursorY.toShort()
|
0x09 -> cursorY.toShort()
|
||||||
@@ -99,8 +106,10 @@ class Display(
|
|||||||
0x05 -> pixelY = (pixelY and 0xff00) or data.toInt()
|
0x05 -> pixelY = (pixelY and 0xff00) or data.toInt()
|
||||||
0x06 -> pixelY = (pixelY and 0x00ff) or (data.toInt() shl 8)
|
0x06 -> pixelY = (pixelY and 0x00ff) or (data.toInt() shl 8)
|
||||||
0x07 -> {
|
0x07 -> {
|
||||||
if(data==0.toShort()) host.clearPixel(pixelX, pixelY)
|
if(pixelX in 0 until ScreenDefs.SCREEN_WIDTH && pixelY in 0 until ScreenDefs.SCREEN_HEIGHT) {
|
||||||
else host.setPixel(pixelX, pixelY)
|
if (data == 0.toShort()) host.clearPixel(pixelX, pixelY)
|
||||||
|
else host.setPixel(pixelX, pixelY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
0x08 -> cursorX = min(data.toInt() and 65535, charWidth-1)
|
0x08 -> cursorX = min(data.toInt() and 65535, charWidth-1)
|
||||||
0x09 -> cursorY = min(data.toInt() and 65535, charHeight-1)
|
0x09 -> cursorY = min(data.toInt() and 65535, charHeight-1)
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
package razorvine.ksim65.components
|
package razorvine.ksim65.components
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.URI
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A RAM chip with read/write memory.
|
* A RAM chip with read/write memory.
|
||||||
@@ -31,7 +33,14 @@ class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAdd
|
|||||||
* Load a c64-style prg program. This file type has the load address as the first two bytes.
|
* Load a c64-style prg program. This file type has the load address as the first two bytes.
|
||||||
*/
|
*/
|
||||||
fun loadPrg(filename: String) {
|
fun loadPrg(filename: String) {
|
||||||
val bytes = File(filename).readBytes()
|
loadPrg(Paths.get(filename).toUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a c64-style prg program. This file type has the load address as the first two bytes.
|
||||||
|
*/
|
||||||
|
fun loadPrg(file: URI) {
|
||||||
|
val bytes = File(file).readBytes()
|
||||||
val loadAddress = (bytes[0].toInt() or (bytes[1].toInt() shl 8)) and 65535
|
val loadAddress = (bytes[0].toInt() or (bytes[1].toInt() shl 8)) and 65535
|
||||||
val baseAddress = loadAddress - startAddress
|
val baseAddress = loadAddress - startAddress
|
||||||
bytes.drop(2).forEachIndexed { index, byte ->
|
bytes.drop(2).forEachIndexed { index, byte ->
|
||||||
|
@@ -50,11 +50,11 @@ class RealTimeClock(startAddress: Address, endAddress: Address) : MemMappedCompo
|
|||||||
0x05 -> LocalTime.now().minute.toShort()
|
0x05 -> LocalTime.now().minute.toShort()
|
||||||
0x06 -> LocalTime.now().second.toShort()
|
0x06 -> LocalTime.now().second.toShort()
|
||||||
0x07 -> {
|
0x07 -> {
|
||||||
val ms = LocalTime.now().nano / 1000
|
val ms = LocalTime.now().nano / 1000000
|
||||||
(ms and 255).toShort()
|
(ms and 255).toShort()
|
||||||
}
|
}
|
||||||
0x08 -> {
|
0x08 -> {
|
||||||
val ms = LocalTime.now().nano / 1000
|
val ms = LocalTime.now().nano / 1000000
|
||||||
(ms ushr 8).toShort()
|
(ms ushr 8).toShort()
|
||||||
}
|
}
|
||||||
else -> 0xff
|
else -> 0xff
|
||||||
|
114
src/main/resources/vmdemo.asm
Normal file
114
src/main/resources/vmdemo.asm
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
|
||||||
|
.cpu "6502"
|
||||||
|
|
||||||
|
DISPLAY = $d000
|
||||||
|
RTC = $d100
|
||||||
|
TIMER = $d200
|
||||||
|
MOUSE = $d300
|
||||||
|
KEYBOARD = $d400
|
||||||
|
SCREEN_WIDTH=640
|
||||||
|
|
||||||
|
* = $1000
|
||||||
|
|
||||||
|
start
|
||||||
|
sei
|
||||||
|
ldx #$ff
|
||||||
|
txs
|
||||||
|
cli
|
||||||
|
|
||||||
|
; ------- fill the screen
|
||||||
|
ldx #0
|
||||||
|
ldy #0
|
||||||
|
fillscreen
|
||||||
|
stx DISPLAY+0
|
||||||
|
sty DISPLAY+1
|
||||||
|
lda character
|
||||||
|
sta DISPLAY+2 ; plot the char on the screen
|
||||||
|
inc character
|
||||||
|
inx
|
||||||
|
cpx #80
|
||||||
|
bne fillscreen
|
||||||
|
ldx #0
|
||||||
|
iny
|
||||||
|
cpy #30
|
||||||
|
bne fillscreen
|
||||||
|
|
||||||
|
; ------- clear the screen
|
||||||
|
lda #$0c ; form feed (clear screen)
|
||||||
|
sta DISPLAY+$0a
|
||||||
|
|
||||||
|
; ------- draw pixel line
|
||||||
|
pixelline
|
||||||
|
ldx pix_x
|
||||||
|
stx DISPLAY+3
|
||||||
|
ldx pix_x+1
|
||||||
|
stx DISPLAY+4
|
||||||
|
ldx pix_y
|
||||||
|
stx DISPLAY+5
|
||||||
|
ldx pix_y+1
|
||||||
|
stx DISPLAY+6
|
||||||
|
lda #1
|
||||||
|
sta DISPLAY+7 ; plot
|
||||||
|
lda pix_x
|
||||||
|
clc
|
||||||
|
adc #2
|
||||||
|
sta pix_x
|
||||||
|
bcc +
|
||||||
|
inc pix_x+1
|
||||||
|
+ inc pix_y
|
||||||
|
bne +
|
||||||
|
inc pix_y+1
|
||||||
|
+ lda pix_x+1
|
||||||
|
cmp #>SCREEN_WIDTH
|
||||||
|
bcc pixelline
|
||||||
|
bne stop1
|
||||||
|
lda pix_x
|
||||||
|
cmp #<SCREEN_WIDTH
|
||||||
|
bcc pixelline
|
||||||
|
bcs stop1
|
||||||
|
pix_x .word 0
|
||||||
|
pix_y .word 0
|
||||||
|
|
||||||
|
stop1
|
||||||
|
|
||||||
|
;--------- draw with mouse
|
||||||
|
mousedraw
|
||||||
|
ldx MOUSE+0
|
||||||
|
ldy MOUSE+2
|
||||||
|
stx DISPLAY+3
|
||||||
|
sty DISPLAY+5
|
||||||
|
ldx MOUSE+1
|
||||||
|
ldy MOUSE+3
|
||||||
|
stx DISPLAY+4
|
||||||
|
sty DISPLAY+6
|
||||||
|
lda #1
|
||||||
|
sta DISPLAY+7 ; plot pixel
|
||||||
|
jmp mousedraw
|
||||||
|
|
||||||
|
|
||||||
|
;--------- RTC display TODO
|
||||||
|
ldy #0
|
||||||
|
lda #0
|
||||||
|
sta DISPLAY+0
|
||||||
|
sta DISPLAY+1
|
||||||
|
rtcloop
|
||||||
|
ldx #0
|
||||||
|
readrtc
|
||||||
|
lda RTC,x
|
||||||
|
sta DISPLAY+2
|
||||||
|
inc DISPLAY+0
|
||||||
|
inx
|
||||||
|
cpx #9
|
||||||
|
bne readrtc
|
||||||
|
lda #0
|
||||||
|
sta DISPLAY+0
|
||||||
|
iny
|
||||||
|
sty DISPLAY+1
|
||||||
|
cpy #20
|
||||||
|
bne rtcloop
|
||||||
|
|
||||||
|
|
||||||
|
done jmp done
|
||||||
|
|
||||||
|
|
||||||
|
character .byte 0
|
BIN
src/main/resources/vmdemo.prg
Normal file
BIN
src/main/resources/vmdemo.prg
Normal file
Binary file not shown.
Reference in New Issue
Block a user