From e8e4b5dd2475943c7a4500b1af3d67f7b4eae414 Mon Sep 17 00:00:00 2001 From: slederer Date: Sun, 8 Feb 2026 01:58:50 +0100 Subject: [PATCH] tridoraemu,docs: emulate vgafb shifter/maskgen and new iomem layout --- doc/vga.md | 20 +++++------ tridoraemu/cpu.go | 1 + tridoraemu/framebuffer.go | 72 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/doc/vga.md b/doc/vga.md index 76520f2..3ef8f8d 100644 --- a/doc/vga.md +++ b/doc/vga.md @@ -9,11 +9,11 @@ Registers | _FB_PS_ | $90C | Palette Select | | _FB_PD_ | $910 | Palette Data | | _FB_CTL_ | $914 | Control Register | -| _FB_SHIFTER | $918 | Shift Assist Register | -| _FB_SHIFTCOUNT | $91C | Shift Count Register | -| _FB_SHIFTERM | $920 | Shifted Mask Register | -| _FB_SHIFTERSP | $924 | Shifter Spill Register | -| _FB_MASKGEN | $928 | Mask Generator Register | +| _FB_SHIFTER_ | $918 | Shift Assist Register | +| _FB_SHIFTCOUNT_ | $91C | Shift Count Register | +| _FB_SHIFTERM_ | $920 | Shifted Mask Register | +| _FB_SHIFTERSP_ | $924 | Shifter Spill Register | +| _FB_MASKGEN_ | $928 | Mask Generator Register | ## Pixel Data Pixel data is organized in 32-bit-words. With four bits per pixel, one word @@ -121,12 +121,12 @@ For each four bits of a pixel, the corresponding four mask bits are all set to one if the pixel value is not zero. This can be used to combine foreground and background pixel data -with a pixel value of zero for a transparent background color. +where a pixel value of zero is used to indicate a transparent foreground pixel. -Usually, the mask will be inverted with a *NOT* instruction -to clear all pixels in the background that are set in the foreground -with an *AND* instruction -before *ORing* foreground and background together. +Usually, the mask will be inverted with a *NOT* instruction. +The result can then be used to clear all pixels in the background +that are set in the foreground, using an *AND* instruction. +As the last step, foreground and masked background data can be combined with an *OR* instruction. Example in hexadecimal, each digit is a pixel: | Pixel Data | Mask | diff --git a/tridoraemu/cpu.go b/tridoraemu/cpu.go index 9d6b08d..f1ae4de 100644 --- a/tridoraemu/cpu.go +++ b/tridoraemu/cpu.go @@ -249,6 +249,7 @@ func (c *CPU) step() error { var name string if (insWord & 1) == 1 { name = "STORE.B" + operand &= ^1 ea = c.BP + word(operand) } else { name = "STORE" diff --git a/tridoraemu/framebuffer.go b/tridoraemu/framebuffer.go index 189100a..71776ca 100644 --- a/tridoraemu/framebuffer.go +++ b/tridoraemu/framebuffer.go @@ -10,11 +10,16 @@ import ( 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 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 @@ -33,6 +38,9 @@ type Framebuffer struct { vmem [VmemWords]word readCount int paletteChanged bool + shiftAssistData word + shiftAssistCount int + maskGenData word } func (f *Framebuffer) initialize() { @@ -53,6 +61,11 @@ func (f *Framebuffer) read(byteaddr word) (word, error) { case FB_PS: result = f.paletteSlot case FB_PD: result = f.readPalette() case FB_CTL: result = f.readCtl() + case FB_SHIFTER: result = f.readShiftAssist() + case FB_SHIFTCOUNT: result = 0xFFFFFFF + case FB_SHIFTERM: result = f.readShifterM() + case FB_SHIFTERSP: result = f.readShifterSp() + case FB_MASKGEN: result = f.readMaskGen() default: } return result, nil @@ -67,6 +80,11 @@ func (f *Framebuffer) write(value word, byteaddr word) (error) { case FB_PS: f.paletteSlot = value case FB_PD: f.writePalette(value) case FB_CTL: f.writeCtl(value) + case FB_SHIFTER: f.writeShiftAssist(value) + case FB_SHIFTCOUNT: f.writeShiftCount(value) + case FB_SHIFTERM: + case FB_SHIFTERSP: + case FB_MASKGEN: f.writeMaskGen(value) default: } @@ -152,3 +170,47 @@ func (f *Framebuffer) readCtl() word { 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) +}