mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
rewinding the main emulation breaks the strict synchronisation constraints with the comparison emulation. it should be possible to maintain two parallel rewind instances *and* to maintain the constraints but I've chosen not to tackle that just yet.
146 lines
3.7 KiB
Go
146 lines
3.7 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package comparison
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
|
|
"github.com/jetsetilly/gopher2600/hardware/television"
|
|
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
|
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
|
)
|
|
|
|
type driver struct {
|
|
tv *television.Television
|
|
|
|
frameInfo television.FrameInfo
|
|
|
|
img [2]*image.RGBA
|
|
cropImg [2]*image.RGBA
|
|
swapIdx bool
|
|
|
|
sync chan bool
|
|
ack chan bool
|
|
quit chan error
|
|
}
|
|
|
|
func newDriver(tv *television.Television) driver {
|
|
drv := driver{
|
|
tv: tv,
|
|
sync: make(chan bool),
|
|
ack: make(chan bool),
|
|
quit: make(chan error),
|
|
}
|
|
|
|
drv.img[0] = image.NewRGBA(image.Rect(0, 0, specification.ClksScanline, specification.AbsoluteMaxScanlines))
|
|
drv.img[1] = image.NewRGBA(image.Rect(0, 0, specification.ClksScanline, specification.AbsoluteMaxScanlines))
|
|
|
|
// start with a NTSC television as default
|
|
drv.Resize(television.NewFrameInfo(specification.SpecNTSC))
|
|
drv.Reset()
|
|
|
|
return drv
|
|
}
|
|
|
|
// Resize implements the television.PixelRenderer interface.
|
|
func (drv *driver) Resize(frameInfo television.FrameInfo) error {
|
|
drv.frameInfo = frameInfo
|
|
crop := image.Rect(
|
|
specification.ClksHBlank, frameInfo.VisibleTop,
|
|
specification.ClksHBlank+specification.ClksVisible, frameInfo.VisibleBottom,
|
|
)
|
|
|
|
drv.cropImg[0] = drv.img[0].SubImage(crop).(*image.RGBA)
|
|
drv.cropImg[1] = drv.img[1].SubImage(crop).(*image.RGBA)
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewFrame implements the television.PixelRenderer interface.
|
|
func (drv *driver) NewFrame(frameInfo television.FrameInfo) error {
|
|
drv.frameInfo = frameInfo
|
|
drv.swapIdx = !drv.swapIdx
|
|
|
|
select {
|
|
case drv.sync <- true:
|
|
case err := <-drv.quit:
|
|
return err
|
|
}
|
|
|
|
select {
|
|
case <-drv.ack:
|
|
case err := <-drv.quit:
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewScanline implements the television.PixelRenderer interface.
|
|
func (drv *driver) NewScanline(scanline int) error {
|
|
return nil
|
|
}
|
|
|
|
// SetPixels implements the television.PixelRenderer interface.
|
|
func (drv *driver) SetPixels(sig []signal.SignalAttributes, last int) error {
|
|
var col color.RGBA
|
|
var offset int
|
|
|
|
var pix []uint8
|
|
if drv.swapIdx {
|
|
pix = drv.img[0].Pix
|
|
} else {
|
|
pix = drv.img[1].Pix
|
|
}
|
|
|
|
for i := range sig {
|
|
// handle VBLANK by setting pixels to black
|
|
if sig[i]&signal.VBlank == signal.VBlank {
|
|
col = color.RGBA{R: 0, G: 0, B: 0}
|
|
} else {
|
|
px := signal.ColorSignal((sig[i] & signal.Color) >> signal.ColorShift)
|
|
col = drv.frameInfo.Spec.GetColor(px)
|
|
}
|
|
|
|
// small cap improves performance, see https://golang.org/issue/27857
|
|
s := pix[offset : offset+3 : offset+3]
|
|
s[0] = col.R
|
|
s[1] = col.G
|
|
s[2] = col.B
|
|
|
|
offset += 4
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Reset implements the television.PixelRenderer interface.
|
|
func (m *driver) Reset() {
|
|
// clear pixels. setting the alpha channel so we don't have to later (the
|
|
// alpha channel never changes)
|
|
for i := range m.img {
|
|
for y := 0; y < m.img[i].Bounds().Size().Y; y++ {
|
|
for x := 0; x < m.img[i].Bounds().Size().X; x++ {
|
|
m.img[i].SetRGBA(x, y, color.RGBA{0, 0, 0, 255})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// EndRendering implements the television.PixelRenderer interface.
|
|
func (drv *driver) EndRendering() error {
|
|
return nil
|
|
}
|