Documentation ¶
Overview ¶
Package television implements the output device of the emulated VCS.
It is possible for instances of television to be embedded in other type structure, thereby extending the "features" of the television and allowing the extended type to be used wherever the Television interface is required. The digest package is a good example of this idea.
It is important to note that the television package does not render pixels or mix sound. Instead, the television interface exposes two functions, AddPixelRenderer() and AddAudioMixer(). These can be used to add as many renderers and mixers as required.
There is also the FrameTrigger and PauseTrigger interfaces for applications that have limited need for a full pixel renderer.
The main means of communication is the Signal() function. This function accepts an instance of SignalAttributes which gives details of how the television should be behaving.
Note that the television implementation no longer attempts to report the same frame/scanline/clock information as Stella. Early versions of the implementation did because it facilitated A/B testing but since we're now confident that the TIA emulation is correct the need to keep in "sync" with Stella is no longer required.
The reference implementation also handles framerate limiting according to the current incoming TV signal. For debugging purposes, the framerate can also be set to a specific value
Framesize adaptation is also handled by the television package.
Screen Rolling ¶
Screen rolling is not handled by the television package. However, the synced argument of the NewFrame() function in the PixelRenderer and FrameTrigger interfaces can be used to implement it if required. Something like this:
- If Synced is false, note scanline of last plot (unsynedScanline)
- For every SetPixel() add unsyncedScanline to the Scanline value in the SignalAttributes struct (adjustedScanline)
- Bring adjustedScanline into range by modulo ScanlinesTotal of the current TV specification.
Recovery from a screen roll should also be emulated. A good way of doing this is to reduce unsyncedScanline by a percentage (80% say) on synced frames (or every other synced frame) after an unsynced frame.
A good additionl policy would be to only roll if several, consecutive unsynced frames are indicated.
Index ¶
- Constants
- type AudioMixer
- type FrameInfo
- type FrameTrigger
- type PauseTrigger
- type PixelRenderer
- type State
- type Television
- func (tv *Television) AddAudioMixer(m AudioMixer)
- func (tv *Television) AddFrameTrigger(f FrameTrigger)
- func (tv *Television) AddPauseTrigger(p PauseTrigger)
- func (tv *Television) AddPixelRenderer(r PixelRenderer)
- func (tv *Television) AttachVCS(vcs VCSReturnChannel)
- func (tv *Television) End() error
- func (tv *Television) GetActualFPS() (float32, float32)
- func (tv *Television) GetFrameInfo() FrameInfo
- func (tv *Television) GetLastSignal() signal.SignalAttributes
- func (tv *Television) GetReqFPS() float32
- func (tv *Television) GetReqSpecID() string
- func (tv *Television) GetState(request signal.StateReq) int
- func (tv *Television) InstructionBoundary()
- func (tv *Television) Pause(pause bool) error
- func (tv *Television) PauseRendering(pause bool)
- func (tv *Television) PlumbState(vcs VCSReturnChannel, s *State)
- func (tv *Television) ReqAdjust(request signal.StateAdj, adjustment int, reset bool) (int, int, int, error)
- func (tv *Television) Reset(keepFrameNum bool) error
- func (tv *Television) SetFPS(fps float32)
- func (tv *Television) SetFPSCap(limit bool) bool
- func (tv *Television) SetSpec(spec string) error
- func (tv *Television) SetSpecConditional(spec string) error
- func (tv *Television) Signal(sig signal.SignalAttributes) error
- func (tv *Television) Snapshot() *State
- func (tv *Television) String() string
- type VCSReturnChannel
Constants ¶
const MaxSignalHistory = specification.ClksScanline * specification.AbsoluteMaxScanlines
MaxSignalHistory is the absolute maximum number of entries in a signal history for an entire frame.
const VisualUpdating float32 = 5.0
VisualUpdating is the value at which the screen drawing process should be shown to the user. ie. the FPS is low enough to require a visual indicator that something is happening.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AudioMixer ¶
type AudioMixer interface { // for efficiency reasons, SetAudio() implementations can be sent // SignalAttributes values that do not have valid AudioData (ie. // AudioUpdate bit is zero). implemenations should therefore take care when // processing the sig slice. // // the general algorithm for processing the sig slice is: // // for _, s := range sig { // if s&signal.AudioUpdate != signal.AudioUpdate { // continue // } // d := uint8((s & signal.AudioData) >> signal.AudioDataShift) // // ... // } SetAudio(sig []signal.SignalAttributes) error // some mixers may need to conclude and/or dispose of resources gently. // for simplicity, the AudioMixer should be considered unusable after // EndMixing() has been called EndMixing() error // Reset buffered audio and anything else that might need doing on, for // example, a cartridge change. Reset() }
AudioMixer implementations work with sound; most probably playing it. An example of an AudioMixer that does not play sound but otherwise works with it is the digest.Audio type.
type FrameInfo ¶ added in v0.14.0
type FrameInfo struct { Spec specification.Spec VisibleTop int VisibleBottom int TotalScanlines int // the refresh rate. calculated from the TotalScanlines value RefreshRate float32 // a VSynced frame is one which was generated from a valid VSYNC/VBLANK // sequence and which hasn't cause the update frequency of the television // to change. VSynced bool // Stable is true once the television frame has been consistent for N frames // after reset. This is useful for pixel renderers so that they don't show // the loose frames that often occur after VCS reset. // // once Stable is true then the Specification will not change (except // manually). This is important for ROMs that allow the VCS to run without // VSYNC - we don't want those ROMs to change the specifciation after he // startup period. A good example of such a ROM is Andrew Davie's 3e+ chess // demos. Stable bool }
FrameInfo records the current frame information, as opposed to the optimal values of the specification, a copy of which is provided as reference.
func NewFrameInfo ¶ added in v0.14.0
func NewFrameInfo(spec specification.Spec) FrameInfo
NewFrameInfo returns an initialised FrameInfo for the specification.
func (FrameInfo) IsAtariSafe ¶ added in v0.14.0
IsAtariSafe returns true if the current frame matches the AtariSafe values of the current specification.
type FrameTrigger ¶
type FrameTrigger interface { // See NewFrame() comment for PixelRenderer interface. NewFrame(FrameInfo) error }
FrameTrigger implementations listen for NewFrame events. FrameTrigger is a subset of PixelRenderer.
type PauseTrigger ¶ added in v0.10.1
FrameTrigger implementations listen for Pause events.
type PixelRenderer ¶
type PixelRenderer interface { // Resize is called when the television implementation detects that extra // scanlines are required in the display. // // Renderers must be prepared to resize to either a smaller or larger size. // // Resize can also be called speculatively so implementations should take // care not to perform any resizing unless absolutely necessary. // // The VisibleTop and VisibleBottom fields in the FrameInfor argument, // describe the top and bottom scanline that is *visible* on a normal // screen. pixels outside this range that are sent by SetPixel() can be // handled according to the renderers needs but would not normally be shown // for game-playing purposes. Resize(FrameInfo) error // NewFrame is called at the start of a new fame. // // PixelRenderer implementations should consider what to do when a // non-synced frame is submitted. Rolling the screen is a good response to // the non-synced frame, with the possiblity of a one or two frame // tolerance (ie. do not roll unless the non-sync frame are continuous) NewFrame(FrameInfo) error // NewScanline is called at the start of a new scanline NewScanline(scanline int) error // SetPixels sends a slice of SignalAttributes to the Renderer. // // The television is guaranteed to only send signal.NoSignal as trailing // filler. Processing of the slice should stop when the first NoSignal is // encountered. // // Signals are consecutive starting at the scanline/clock of the first // signal. The first signal does not necessary start with scanline and // clock values of zero. // // Due to the nature of the television, SetPixels may never receive signals // for some scanline/clock combinations. Implementations should consider // what to do with pixels that have been set in previous frames but are set // no longer. (These pixels will be either at the very start or very end of // the frame and can probably be ignore for renderers that don't show the // vblank and overscan areas. // // For renderers that are producing an accurate visual image, the pixel // should always be set to video black if VBLANK is on. Some renderers // however may find it useful to set the pixel to the RGB value regardless // of VBLANK. // // A very important note is that some ROMs use VBLANK to control pixel // color within the visible display area. For example: // // * Custer's Revenge // * Ladybug // * ET (turns VBLANK off late on scanline 40) // // In other words, the PixelRenderer should not simply assume VBLANK is // restricted to the "off-screen" areas as defined by the FrameInfo sent to // Resize() // // The current flag states that the signals should be considered to be // signals for the current frame. for most applications this will always be // true but in some circumstances, the television will send pixels from the // previous frame if they haven't been drawn yet for the current frame. An // implementation of PixelRenderer may choose to ignore non-current // signals. SetPixels(sig []signal.SignalAttributes, current bool) error // Reset all pixels. Called when TV is reset. // // Note that a Reset event does not imply a Resize() event. Implementations // should not call the Resize() function as a byproduct of a Reset(). The // television will send an explicit Resize() request if it is appropriate. Reset() // Some renderers may need to conclude and/or dispose of resources gently. EndRendering() error }
PixelRenderer implementations displays, or otherwise works with, visual information from a television. For example digest.Video.
PixelRenderer implementations often find it convenient to maintain a reference to the parent Television implementation and maybe even embed the Television interface. ie.
type ExampleTV struct { television.Television ... }
The most useful source of information though is the FrameInfo type supplied to the PixelRenderer through the Resize() and NewFrame() functions. A current copy of this information is also available from the television type GetFrameInfo() function.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State encapsulates the television values that can change from moment to moment. Used by the rewind system when recording the current television state.
type Television ¶
type Television struct {
// contains filtered or unexported fields
}
Television is a Television implementation of the Television interface. In all honesty, it's most likely the only implementation required.
func NewTelevision ¶
func NewTelevision(spec string) (*Television, error)
NewReference creates a new instance of the reference television type, satisfying the Television interface.
func (*Television) AddAudioMixer ¶
func (tv *Television) AddAudioMixer(m AudioMixer)
AddAudioMixer registers an implementation of AudioMixer. Multiple implemntations can be added.
func (*Television) AddFrameTrigger ¶
func (tv *Television) AddFrameTrigger(f FrameTrigger)
AddFrameTrigger registers an implementation of FrameTrigger. Multiple implemntations can be added.
func (*Television) AddPauseTrigger ¶ added in v0.10.1
func (tv *Television) AddPauseTrigger(p PauseTrigger)
AddPauseTrigger registers an implementation of PauseTrigger.
func (*Television) AddPixelRenderer ¶
func (tv *Television) AddPixelRenderer(r PixelRenderer)
AddPixelRenderer registers an implementation of PixelRenderer. Multiple implemntations can be added.
func (*Television) AttachVCS ¶ added in v0.8.0
func (tv *Television) AttachVCS(vcs VCSReturnChannel)
AttachVCS attaches an implementation of the VCSReturnChannel.
func (*Television) End ¶
func (tv *Television) End() error
some televisions may need to conclude and/or dispose of resources gently. implementations of End() should call EndRendering() and EndMixing() on each PixelRenderer and AudioMixer that has been added.
for simplicity, the Television should be considered unusable after EndRendering() has been called.
func (*Television) GetActualFPS ¶
func (tv *Television) GetActualFPS() (float32, float32)
GetActualFPS returns the current number of frames per second and the detected frequency of the TV signal.
Note that FPS measurement still works even when frame capping is disabled.
IS goroutine safe.
func (*Television) GetFrameInfo ¶ added in v0.14.0
func (tv *Television) GetFrameInfo() FrameInfo
GetFrameInfo returns the television's current frame information. FPS and RefreshRate is returned by GetReqFPS().
func (*Television) GetLastSignal ¶
func (tv *Television) GetLastSignal() signal.SignalAttributes
GetLastSignal Returns a copy of the most SignalAttributes sent to the TV (via the Signal() function).
func (*Television) GetReqFPS ¶
func (tv *Television) GetReqFPS() float32
GetReqFPS returns the requested number of frames per second. Compare with GetActualFPS() to check for accuracy.
IS goroutine safe.
func (*Television) GetReqSpecID ¶
func (tv *Television) GetReqSpecID() string
GetReqSpecID returns the specification that was requested on creation.
func (*Television) GetState ¶
func (tv *Television) GetState(request signal.StateReq) int
GetState returns state information for the TV.
func (*Television) InstructionBoundary ¶ added in v0.10.1
func (tv *Television) InstructionBoundary()
InstructionBoundary implements the cpu.BoundaryTrigger interface.
func (*Television) Pause ¶
func (tv *Television) Pause(pause bool) error
Pause indicates that emulation has been paused. All unpushed pixels will be pushed immeditately. Not the same as PauseRendering(). Pause() should be used when emulation is stopped. In this case, paused rendering is implied.
func (*Television) PauseRendering ¶ added in v0.8.0
func (tv *Television) PauseRendering(pause bool)
PauseRendering halts all forwarding to attached pixel renderers. Not the same as Pause(). PauseRendering() should be used when emulation is running but no rendering is to take place.
func (*Television) PlumbState ¶ added in v0.8.0
func (tv *Television) PlumbState(vcs VCSReturnChannel, s *State)
PlumbState attaches an existing television state.
func (*Television) ReqAdjust ¶ added in v0.10.1
func (tv *Television) ReqAdjust(request signal.StateAdj, adjustment int, reset bool) (int, int, int, error)
ReqAdjust requests the frame, scanline and clock values where the requested StateReq has been adjusted by the specified value. All values will be adjusted as required.
The reset argument instructs the function to return values that have been reset to zero as appropriate. So when request is ReqFramenum, the scanline and clock values will be zero; when request is ReqScanline, the clock value will be zero. It has no affect when request is ReqClock.
In the case of a StateAdj of AdjCPUCycle the only allowed adjustment value is -1. Any other value will return an error.
func (*Television) Reset ¶
func (tv *Television) Reset(keepFrameNum bool) error
Reset the television to an initial state.
func (*Television) SetFPS ¶
func (tv *Television) SetFPS(fps float32)
SetFPS requests the number frames per second. This overrides the frame rate of the specification. A negative value restores frame rate to the ideal value (the frequency of the incoming signal).
func (*Television) SetFPSCap ¶
func (tv *Television) SetFPSCap(limit bool) bool
SetFPSCap whether the emulation should wait for FPS limiter. Returns the setting as it was previously.
func (*Television) SetSpec ¶
func (tv *Television) SetSpec(spec string) error
SetSpec sets the television's specification. Will return an error if specification is not recognised.
Currently supported NTSC, PAL, PAL60 and AUTO. The empty string behaves like "AUTO".
func (*Television) SetSpecConditional ¶ added in v0.12.1
func (tv *Television) SetSpecConditional(spec string) error
SetSpecConditional sets the television's specification if the original specification (not the current spec, the original) is "AUTO".
This is used when attaching a cartridge to the VCS and also when processing setup entries (see setup package, particularly the TV type).
func (*Television) Signal ¶
func (tv *Television) Signal(sig signal.SignalAttributes) error
Signal updates the current state of the television.
func (*Television) Snapshot ¶
func (tv *Television) Snapshot() *State
Snapshot makes a copy of the television state.
func (*Television) String ¶
func (tv *Television) String() string
type VCSReturnChannel ¶ added in v0.8.0
VCSReturnChannel is used to send information from the TV back to the parent console. Named because I think of it as being similar to the Audio Return Channel (ARC) present in modern TVs.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package signal exposes the interface between the VCS and the television implementation.
|
Package signal exposes the interface between the VCS and the television implementation. |
Package specification contains the definitions, including colour, of the PAL and NTSC television protocols supported by the emulation.
|
Package specification contains the definitions, including colour, of the PAL and NTSC television protocols supported by the emulation. |