mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-02 22:17:49 +00:00
Compare commits
1 Commits
dev
...
gbadisplay
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d1b0c39cc |
@@ -0,0 +1,85 @@
|
|||||||
|
// Package gbadisplay implements a simple driver for the GameBoy Advance
|
||||||
|
// display.
|
||||||
|
package gbadisplay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/gba"
|
||||||
|
"errors"
|
||||||
|
"image/color"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/pixel"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Image buffer type used by the GameBoy Advance.
|
||||||
|
type Image = pixel.Image[pixel.RGB555]
|
||||||
|
|
||||||
|
const (
|
||||||
|
displayWidth = 240
|
||||||
|
displayHeight = 160
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errOutOfBounds = errors.New("rectangle coordinates outside display area")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Device struct{}
|
||||||
|
|
||||||
|
// New returns a new GameBoy Advance display object.
|
||||||
|
func New() Device {
|
||||||
|
return Device{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayFrameBuffer = (*[160 * 240]volatile.Register16)(unsafe.Pointer(uintptr(gba.MEM_VRAM)))
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// TODO: add more display modes here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the display as a regular 15bpp framebuffer.
|
||||||
|
func (d Device) Configure(config Config) {
|
||||||
|
// Use video mode 3 (in BG2, a 16bpp bitmap in VRAM) and Enable BG2.
|
||||||
|
gba.DISP.DISPCNT.Set(gba.DISPCNT_BGMODE_3<<gba.DISPCNT_BGMODE_Pos |
|
||||||
|
gba.DISPCNT_SCREENDISPLAY_BG2_ENABLE<<gba.DISPCNT_SCREENDISPLAY_BG2_Pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the fixed size of this display.
|
||||||
|
func (d Device) Size() (x, y int16) {
|
||||||
|
return displayWidth, displayHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display is a no-op: the display framebuffer is modified directly.
|
||||||
|
func (d Device) Display() error {
|
||||||
|
// Nothing to do here.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPixel changes the pixel at (x, y) to the given color.
|
||||||
|
func (d Device) SetPixel(x, y int16, c color.RGBA) {
|
||||||
|
if x < 0 || y < 0 || x >= displayWidth || y > displayHeight {
|
||||||
|
// Out of bounds, so ignore.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val := pixel.NewColor[pixel.RGB555](c.R, c.G, c.B)
|
||||||
|
displayFrameBuffer[(int(y))*240+int(x)].Set(uint16(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawBitmap updates the rectangle at (x, y) to the image stored in buf.
|
||||||
|
func (d Device) DrawBitmap(x, y int16, buf Image) error {
|
||||||
|
width, height := buf.Size()
|
||||||
|
if x < 0 || y < 0 || int(x)+width > displayWidth || int(y)+height > displayHeight {
|
||||||
|
return errOutOfBounds
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: try to do a 4-byte memcpy if possible. That should significantly
|
||||||
|
// speed up the copying of this image.
|
||||||
|
for bufY := 0; bufY < int(height); bufY++ {
|
||||||
|
for bufX := 0; bufX < int(width); bufX++ {
|
||||||
|
val := buf.Get(bufX, bufY)
|
||||||
|
displayFrameBuffer[(int(y)+bufY)*240+int(x)+bufX].Set(uint16(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+32
-1
@@ -16,7 +16,7 @@ import (
|
|||||||
// particular display. Each pixel is at least 1 byte in size.
|
// particular display. Each pixel is at least 1 byte in size.
|
||||||
// The color format is sRGB (or close to it) in all cases.
|
// The color format is sRGB (or close to it) in all cases.
|
||||||
type Color interface {
|
type Color interface {
|
||||||
RGB888 | RGB565BE | RGB444BE
|
RGB888 | RGB565BE | RGB555 | RGB444BE
|
||||||
|
|
||||||
BaseColor
|
BaseColor
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,8 @@ func NewColor[T Color](r, g, b uint8) T {
|
|||||||
return any(NewRGB888(r, g, b)).(T)
|
return any(NewRGB888(r, g, b)).(T)
|
||||||
case RGB565BE:
|
case RGB565BE:
|
||||||
return any(NewRGB565BE(r, g, b)).(T)
|
return any(NewRGB565BE(r, g, b)).(T)
|
||||||
|
case RGB555:
|
||||||
|
return any(NewRGB555(r, g, b)).(T)
|
||||||
case RGB444BE:
|
case RGB444BE:
|
||||||
return any(NewRGB444BE(r, g, b)).(T)
|
return any(NewRGB444BE(r, g, b)).(T)
|
||||||
default:
|
default:
|
||||||
@@ -140,6 +142,35 @@ func (c RGB565BE) RGBA() color.RGBA {
|
|||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Color format used on the GameBoy Advance among others.
|
||||||
|
//
|
||||||
|
// Colors are stored as native endian values, with bits 0bbbbbgg_gggrrrrr (red
|
||||||
|
// is least significant, blue is most significant).
|
||||||
|
type RGB555 uint16
|
||||||
|
|
||||||
|
func NewRGB555(r, g, b uint8) RGB555 {
|
||||||
|
return RGB555(r)>>3 | (RGB555(g)>>3)<<5 | (RGB555(b)>>3)<<10
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c RGB555) BitsPerPixel() int {
|
||||||
|
// 15 bits per pixel, but there are 16 bits when stored
|
||||||
|
return 16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c RGB555) RGBA() color.RGBA {
|
||||||
|
color := color.RGBA{
|
||||||
|
R: uint8(c>>10) << 3,
|
||||||
|
G: uint8(c>>5) << 3,
|
||||||
|
B: uint8(c) << 3,
|
||||||
|
A: 255,
|
||||||
|
}
|
||||||
|
// Correct color rounding, so that 0xff roundtrips back to 0xff.
|
||||||
|
color.R |= color.R >> 5
|
||||||
|
color.G |= color.G >> 5
|
||||||
|
color.B |= color.B >> 5
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
// Color format that is supported by the ST7789 for example.
|
// Color format that is supported by the ST7789 for example.
|
||||||
// It may be a bit faster to use than RGB565BE on very slow SPI buses.
|
// It may be a bit faster to use than RGB565BE on very slow SPI buses.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user