// 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 = 4 const FB_IO = 8 const FB_PS = 12 const FB_PD = 16 const FB_CTL= 20 const FB_SHIFTER = 24 const FB_SHIFTCOUNT = 28 const FB_SHIFTERM = 32 const FB_SHIFTERSP = 36 const FB_MASKGEN = 40 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 paletteChanged bool shiftAssistData word shiftAssistCount int maskGenData word } func (f *Framebuffer) initialize() { f.framebuffer = ebiten.NewImage(ScreenWidth, ScreenHeight) for i := 0; i > (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) startFrame() { // when the palette changes, we // need to redraw every pixel // to get the new colors if f.paletteChanged { oldRAddr := f.readAddr oldWAddr := f.writeAddr f.readAddr = 0 f.writeAddr = 0 for i := 0; i < VmemWords; i++ { f.writeVmem(f.readVmem()) } f.readAddr = oldRAddr f.writeAddr = oldWAddr f.paletteChanged = false } } 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} f.paletteChanged = true } 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) { } func (f *Framebuffer) writeShiftAssist(value word) { f.shiftAssistData = value f.shiftAssistCount = 0 } func (f *Framebuffer) readShiftAssist() word { return f.shiftAssistData >> (f.shiftAssistCount * 4) } func (f *Framebuffer) writeShiftCount(value word) { f.shiftAssistCount = int(value & 0x7) } func (f *Framebuffer) readShifterM() word { return convertToMask(f.readShiftAssist()) } func pixelToMask(pixels word, mask word) word { if (pixels & mask) != 0 { return mask } else { return 0 } } func convertToMask(pixels word) word { return pixelToMask(pixels, 0xF0000000) | pixelToMask(pixels, 0x0F000000) | pixelToMask(pixels, 0x00F00000) | pixelToMask(pixels, 0x000F0000) | pixelToMask(pixels, 0x0000F000) | pixelToMask(pixels, 0x00000F00) | pixelToMask(pixels, 0x000000F0) | pixelToMask(pixels, 0x0000000F) } func (f *Framebuffer) readShifterSp() word { return word(f.shiftAssistData << ((8-f.shiftAssistCount)*4)) } func (f *Framebuffer) writeMaskGen(value word) { f.maskGenData = value } func (f *Framebuffer) readMaskGen() word { return convertToMask(f.maskGenData) }