Documentation ¶
Overview ¶
Package lazyvalues is the method used by sdlimgui to read emulator data from the GUI thread. Reading emulator values (which are handled by the emulator thread) will cause race errors in almost every circumstance so it is important that this lazyvalue mechanism be used whenever emulator information is required.
Note that this system is used in addition to the other systems which hand off information to the GUI. The PixelRenderer and AudioMixer interfaces from the television packages should be used in the normal way.
For writing data back to the emulation thread the terminal interface can be used for many things. Alternatively the debugger.PushFunction() function can be used. There is currently no way of pushing events onto the emulator unless the debugging loop is in use.
Example -------
Retrieving the foreground color of the playfield:
col := lazyval.Playfield.ForegroundColor
Writing the playfield values is done thought debugger's "raw event" system:
lazyval.Dbg.PushFunction(func() { lazyval.VCS.TIA.Video.Playfield.ForegroundColor = col })
Implementation --------------
The main goal of the lazyvalues system is to prevent the GUI loop from locking up while waiting for a response from the emulator thread. Given that we must use a thred-sage a communication channel between the GUI and emulator threads to avoid race conditions this is important - a unresponsive GUI can needlessly damage the user experience.
This section outlines the principles of the internals of the lazyvalues package. Users of the package need not understand these points.
The principle of the lazyvalues system is to use whatever values are available immediately and to update those values "lazily". In a GUI context this means that the values seen on screen may be several frames behind the emulation but at normal GUI refresh rates this isn't noticeable. Cartainly, when the emulation is paused, the values seen in the GUI will be accurate.
Lazy values are updated with the Refresh() function. In turn, this function will call the push() and update() functions of each component in the lazyvalues package.
The pseudocode below shows how the Refresh() updates the values in every type in the lazyvalues system, at the same time as requesting new values.
func Refresh() { .------------------. debugger.PushFunction() -----> | CPU.push() | | RAM.push() | CPU.update() | Playfield.push() | RAM.update() | . | . | . | . | . | . | Log.push() | Log.update() ------------------ }
The update() and push() functions (not visible from outside the lazyvalues package) of each type handle the retreiving and updating of emulation values. In most instances, this is achieved with the atomic.Value type, from the atomic package in the Go standard library.
In the instance of the LazyController type, we cannot use the atomic.Value. This is because of the limitation on atomic.Values only being able to store consistently typed values. In the case of the LazyController type we need to store the ports.Peripheral interface, which by definition may have differing underlying types.
For this reason, the LazyController type uses channels to communicate between the push() function (ie. the emulation thread) and the update() function, rather than atomic values. We could of course, use channels for all types and do away with atomic values but it is felt that in most cases the atomic solution is clearer.
As a final point about atomic values, note that arrays of atomic values require that the array itself be an atomic value, as well as the elements of the array. For example, the RAM package has code equivalent to this; an array of atomic.Value stored as an atomic value:
var ram atomic.Value ram.Store(make([]atomic.Value, size)
The exception to all the rules is the LazyBreakpoints type. Like LazyRAM it employs an array of atomic.Values storied as an atomic Value but unlike everythin else it is not refreshed with update() and push(). Instead, the unique function HasBreak() is used, which is called by the Disassembly window for every cartridge entry that is visible.
The reason for this function is so that we can pass an instance of disassembly.Entry and probe the debugger's breakpoints with that. There may be other ways of achieving the same effect, but whatever way we do it the additional context provided by the disassembly.Entry is required.
Ensuring Up-To-Date Information -------------------------------
Sometimes the GUI wants up-to-date information and nothing else will do. This is particularly important when the GUI is interacting directly with the emulation. For example, the rewind slider can stutter between the moment of selection/release and the the moment when the emulation has caught-up with the request.
In these instances, the lazy type can be instructed to "wait" for up-to-date values. Currently only the LazyRewind type supports this and can accessed by setting the Wait flag to true.
Use of "wait" should be kept to a minimum to ensure system responsiveness.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type LazyBall ¶
type LazyBall struct { // Bs is a pointer to the "live" data in the other thread. Do not access // the fields in this struct directly. It can be used in a PushFunction() Bs *video.BallSprite ResetPixel int HmovedPixel int Color uint8 VerticalDelay bool EnabledDelay bool Enabled bool Ctrlpf uint8 Size uint8 Hmove uint8 MoreHmove bool EncActive bool EncSecondHalf bool EncCpy int EncTicks int // contains filtered or unexported fields }
LazyBall lazily accesses ball information from the emulator.
type LazyBus ¶ added in v0.20.0
type LazyBus struct { AddressBus uint16 DataBus uint8 LastCPUWrite bool DataBusDriven uint8 // contains filtered or unexported fields }
LazyBus lazily accesses Mem information from the emulator.
type LazyCPU ¶
type LazyCPU struct { HasReset bool RdyFlg bool Killed bool PC registers.ProgramCounter A registers.Register X registers.Register Y registers.Register SP registers.StackPointer StatusReg registers.StatusRegister LastResult execution.Result RTSPrediction uint16 RTSPredictionValid bool // contains filtered or unexported fields }
LazyCPU lazily accesses CPU information from the emulator.
type LazyCart ¶
type LazyCart struct { ID string Mapping string Filename string Shortname string NumBanks int CurrBank mapper.BankInfo HasStaticBus bool StaticBus mapper.CartStaticBus Static mapper.CartStatic HasRegistersBus bool Registers mapper.CartRegisters HasRAMbus bool RAM []mapper.CartRAM HasTapeBus bool TapeState mapper.CartTapeState HasCoProcBus bool CoProcID string CoProcPC uint32 IsPlusROM bool PlusROMAddrInfo plusrom.AddrInfo PlusROMSendState plusrom.SendState // contains filtered or unexported fields }
LazyCart lazily accesses cartridge information from the emulator.
type LazyCollisions ¶ added in v0.2.1
type LazyCollisions struct { CXM0P uint8 CXM1P uint8 CXP0FB uint8 CXP1FB uint8 CXM0FB uint8 CXM1FB uint8 CXBLPF uint8 CXPPMM uint8 // contains filtered or unexported fields }
LazyTimer lazily accesses RIOT timer information from the emulator.
type LazyDebugger ¶
type LazyDebugger struct { Quantum debugger.Quantum LiveDisasmEntry disassembly.Entry Breakpoints debugger.LazyBreakpointsQuery HasChanged bool // the govern.State below is taken at the same time as the reset of the // lazy values. this value should be used in preference to the live // govern.State() value (which is safe to obtain outside of the lazy // system) when synchronisation is important State govern.State // contains filtered or unexported fields }
LazyDebugger lazily accesses Debugger information.
type LazyMissile ¶
type LazyMissile struct { Ms *video.MissileSprite ResetPixel int HmovedPixel int Color uint8 Enabled bool Nusiz uint8 Size uint8 Copies uint8 Hmove uint8 MoreHmove bool ResetToPlayer bool EncActive bool EncSecondHalf bool EncCpy int EncTicks int // contains filtered or unexported fields }
LazyMissile lazily accesses missile information from the emulator.
type LazyPeripherals ¶ added in v0.16.0
type LazyPeripherals struct { LeftPlayer ports.Peripheral RightPlayer ports.Peripheral // contains filtered or unexported fields }
LazyPeripherals lazily accesses controller information from the emulator.
type LazyPhaseClock ¶ added in v0.16.0
type LazyPhaseClock struct { LastPClk phaseclock.PhaseClock // contains filtered or unexported fields }
LazyPhaseClock lazily accesses PhaseClock information from the emulator.
type LazyPlayer ¶
type LazyPlayer struct { // P is a pointer to the "live" data in the other thread. Do not access // the fields in this struct directly. It can be used in a PushFunction() Ps *video.PlayerSprite ResetPixel int HmovedPixel int Color uint8 Nusiz uint8 SizeAndCopies uint8 Reflected bool VerticalDelay bool Hmove uint8 MoreHmove bool GfxDataNew uint8 GfxDataOld uint8 ScanIsActive bool ScanIsLatching bool ScanPixel int ScanCpy int ScanLatchedSizeAndCopies uint8 // contains filtered or unexported fields }
LazyPlayer lazily accesses player information from the emulator.
type LazyPlayfield ¶
type LazyPlayfield struct { // Pf is a pointer to the "live" data in the other thread. Do not access // the fields in this struct directly. It can be used in a PushFuncion() Pf *video.Playfield Ctrlpf uint8 ForegroundColor uint8 BackgroundColor uint8 Reflected bool Scoremode bool Priority bool Region video.ScreenRegion PF0 uint8 PF1 uint8 PF2 uint8 Idx int LeftData []bool RightData []bool // contains filtered or unexported fields }
LazyPlayfield lazily accesses playfield information from the emulator.
type LazyPorts ¶ added in v0.16.0
type LazyPorts struct { SWCHA uint8 SWACNT uint8 SWCHA_W uint8 SWCHA_Derived uint8 SWCHB uint8 SWBCNT uint8 SWCHB_W uint8 SWCHB_Derived uint8 INPT0 uint8 INPT1 uint8 INPT2 uint8 INPT3 uint8 INPT4 uint8 INPT5 uint8 // contains filtered or unexported fields }
LazyPorts lazily accesses RIOT Ports information from the emulator.
type LazyRAM ¶ added in v0.2.1
type LazyRAM struct { RAM []uint8 // contains filtered or unexported fields }
LazyRAM lazily accesses the RAM area of VCS memory.
type LazyRewind ¶ added in v0.7.1
type LazyRewind struct { Timeline rewind.Timeline Comparison *rewind.State // contains filtered or unexported fields }
LazyRewind lazily accesses VCS rewind information.
type LazySaveKey ¶ added in v0.7.1
type LazySaveKey struct { SaveKeyActive bool SDA []float32 SCL []float32 State savekey.SaveKeyState Dir savekey.DataDirection Ack bool Bits uint8 BitsCt int Address uint16 EEPROMdata []uint8 EEPROMdiskData []uint8 // contains filtered or unexported fields }
LazyChipRegisters lazily accesses chip registere information from the emulator.
type LazyTV ¶
type LazyTV struct { FrameInfo television.FrameInfo TVstr string LastSignal signal.SignalAttributes Coords coords.TelevisionCoords Hz float32 ActualFPS float32 ReqFPS float32 // contains filtered or unexported fields }
LazyTV lazily accesses tv information from the emulator.
type LazyTimer ¶
type LazyTimer struct { Divider timer.Divider INTIM uint8 TicksRemaining int TIMINT uint8 // contains filtered or unexported fields }
LazyTimer lazily accesses RIOT timer information from the emulator.
type LazyValues ¶ added in v0.7.1
type LazyValues struct { // pointers to these instances. non-pointer instances trigger the race // detector for some reason. Debugger *LazyDebugger CPU *LazyCPU Bus *LazyBus Phaseclock *LazyPhaseClock RAM *LazyRAM Timer *LazyTimer Playfield *LazyPlayfield Player0 *LazyPlayer Player1 *LazyPlayer Missile0 *LazyMissile Missile1 *LazyMissile Ball *LazyBall TV *LazyTV Cart *LazyCart Peripherals *LazyPeripherals Collisions *LazyCollisions Ports *LazyPorts SaveKey *LazySaveKey Rewind *LazyRewind // contains filtered or unexported fields }
LazyValues contains all values required by a debugger running in a different thread to the emulation. Use these values rather than directly accessing those exposed by the emulation.
func NewLazyValues ¶ added in v0.7.1
func NewLazyValues(dbg *debugger.Debugger) *LazyValues
NewLazyValues is the preferred method of initialisation for the Values type.
func (*LazyValues) FastRefresh ¶ added in v0.15.0
func (val *LazyValues) FastRefresh()
FastRefresh lazy values. Updates only the values that are needed in playmode.