tridoraemu: fix crash on invalid vmem addresses examples: removed 5cubes due to unclear licensing add stuff to gitignore, licenses, README import Vivado project added missing assembly files, extended .gitignore stdlib: use DEL instead of BS
132 lines
2.7 KiB
Go
132 lines
2.7 KiB
Go
// Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
|
package main
|
|
|
|
import (
|
|
// "fmt"
|
|
"image/color"
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
)
|
|
|
|
const VmemWords = 32768
|
|
const PaletteSlots = 16
|
|
const FB_RA = 0
|
|
const FB_WA = 1
|
|
const FB_IO = 2
|
|
const FB_PS = 3
|
|
const FB_PD = 4
|
|
const FB_CTL= 5
|
|
|
|
const PixelMask = 0b11110000000000000000000000000000
|
|
const PixelPerWord = 8
|
|
const VmemWidth = 32
|
|
const BitsPerPixel = 4
|
|
const ScreenWidth = 640
|
|
const ScreenHeight = 400
|
|
const WordsPerLine = ScreenWidth / PixelPerWord
|
|
|
|
type Framebuffer struct {
|
|
framebuffer *ebiten.Image
|
|
palette [PaletteSlots] color.Color
|
|
readAddr word
|
|
writeAddr word
|
|
paletteSlot word
|
|
vmem [VmemWords]word
|
|
readCount int
|
|
}
|
|
|
|
func (f *Framebuffer) initialize() {
|
|
f.framebuffer = ebiten.NewImage(ScreenWidth, ScreenHeight)
|
|
for i := 0; i <PaletteSlots; i++ {
|
|
f.palette[i] = color.RGBA{0,0,0,0}
|
|
}
|
|
}
|
|
|
|
func (f *Framebuffer) read(byteaddr word) (word, error) {
|
|
result := word(0)
|
|
|
|
addr := byteaddr & 0x7F
|
|
switch addr {
|
|
case FB_RA: result = f.readAddr
|
|
case FB_WA: result = f.writeAddr
|
|
case FB_IO: result = f.readVmem()
|
|
case FB_PS: result = f.paletteSlot
|
|
case FB_PD: result = f.readPalette()
|
|
case FB_CTL: result = f.readCtl()
|
|
default:
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (f *Framebuffer) write(value word, byteaddr word) (error) {
|
|
addr := byteaddr & 0x7F
|
|
switch addr {
|
|
case FB_RA: f.readAddr = value
|
|
case FB_WA: f.writeAddr = value
|
|
case FB_IO: f.writeVmem(value)
|
|
case FB_PS: f.paletteSlot = value
|
|
case FB_PD: f.writePalette(value)
|
|
case FB_CTL: f.writeCtl(value)
|
|
default:
|
|
}
|
|
|
|
idle(false)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *Framebuffer) readVmem() word {
|
|
result := f.vmem[f.readAddr & (VmemWords - 1)]
|
|
f.readAddr += 1
|
|
return result
|
|
}
|
|
|
|
func (f *Framebuffer) writeVmem(value word) {
|
|
vaddr := f.writeAddr & (VmemWords - 1)
|
|
f.vmem[vaddr] = value
|
|
|
|
y := vaddr / WordsPerLine
|
|
x := vaddr % WordsPerLine * PixelPerWord
|
|
|
|
for i := 0; i < PixelPerWord; i++ {
|
|
pixel := (value & PixelMask) >> (VmemWidth - BitsPerPixel)
|
|
value = value << BitsPerPixel
|
|
|
|
col := f.palette[pixel]
|
|
//fmt.Printf("set pixel %v, %v\n", x,y)
|
|
f.framebuffer.Set(int(x), int(y), col)
|
|
x = x + 1
|
|
}
|
|
|
|
f.writeAddr += 1
|
|
}
|
|
|
|
func (f *Framebuffer) readPalette() word {
|
|
return word(0)
|
|
}
|
|
|
|
func (f *Framebuffer) writePalette(value word) {
|
|
// 4 bits per color channel
|
|
r := uint8((value & 0b111100000000) >> 8)
|
|
g := uint8((value & 0b000011110000) >> 4)
|
|
b := uint8((value & 0b000000001111) >> 0)
|
|
|
|
// scale to 0-255
|
|
r = r << 4
|
|
g = g << 4
|
|
b = b << 4
|
|
|
|
f.palette[f.paletteSlot] = color.RGBA{r,g,b,0}
|
|
}
|
|
|
|
func (f *Framebuffer) readCtl() word {
|
|
if f.readCount == 0 {
|
|
f.readCount = 1000
|
|
return word(0)
|
|
} else {
|
|
f.readCount -= 1
|
|
return word(1)
|
|
}
|
|
}
|
|
|
|
func (f *Framebuffer) writeCtl(value word) {
|
|
}
|