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.PushRawEvent() 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.PushRawEvent(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.PushRawEvent() -----> | 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 ¶
- type LazyBall
- type LazyBreakpoints
- type LazyCPU
- type LazyCart
- type LazyChipRegisters
- type LazyCollisions
- type LazyControllers
- type LazyDebugger
- type LazyLog
- type LazyMissile
- type LazyPlayer
- type LazyPlayfield
- type LazyPrefs
- type LazyRAM
- type LazyRewind
- type LazySaveKey
- type LazyTV
- type LazyTimer
- type LazyValues
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 PushRawEvent() // call 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 LazyBreakpoints ¶ added in v0.7.1
type LazyBreakpoints struct {
// contains filtered or unexported fields
}
func (*LazyBreakpoints) HasBreak ¶ added in v0.7.1
func (lz *LazyBreakpoints) HasBreak(e *disassembly.Entry) debugger.BreakGroup
HasBreak checks to see if disassembly entry has a breakpoint.
type LazyCPU ¶
type LazyCPU struct { HasReset bool RdyFlg bool PC registers.ProgramCounter A registers.Register X registers.Register Y registers.Register SP registers.Register StatusReg registers.StatusRegister // contains filtered or unexported fields }
LazyCPU lazily accesses CPU information from the emulator.
type LazyCart ¶
type LazyCart struct { ID string Summary string Filename string NumBanks int CurrBank mapper.BankInfo HasStaticBus bool StaticBus mapper.CartStaticBus Static []mapper.CartStatic HasRegistersBus bool RegistersBus mapper.CartRegistersBus Registers mapper.CartRegisters HasRAMbus bool RAMbus mapper.CartRAMbus RAM []mapper.CartRAM HasTapeBus bool TapeBus mapper.CartTapeBus TapeState mapper.CartTapeState IsPlusROM bool PlusROMAddrInfo plusrom.AddrInfo PlusROMNick string PlusROMID string PlusROMRecvBuff []uint8 PlusROMSendBuff []uint8 // contains filtered or unexported fields }
LazyCart lazily accesses cartridge information from the emulator.
type LazyChipRegisters ¶ added in v0.7.1
type LazyChipRegisters struct { SWCHA uint8 SWACNT uint8 SWCHB uint8 INPT0 uint8 INPT1 uint8 INPT2 uint8 INPT3 uint8 INPT4 uint8 INPT5 uint8 // contains filtered or unexported fields }
LazyChipRegisters lazily accesses chip registere 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 LazyControllers ¶
type LazyControllers struct { Player0 ports.Peripheral Player1 ports.Peripheral // contains filtered or unexported fields }
LazyControllers lazily accesses controller information from the emulator.
type LazyDebugger ¶
type LazyDebugger struct { Quantum debugger.QuantumMode LastResult disassembly.Entry // contains filtered or unexported fields }
LazyDebugger lazily accesses Debugger information.
type LazyLog ¶ added in v0.7.1
LazyLog lazily accesses chip registere information from the emulator.
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 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 PushRawEvent() // call 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 PushRawEvent() // call 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 LazyPrefs ¶ added in v0.2.1
type LazyPrefs struct { RandomState bool RandomPins bool FxxxMirror bool Symbols bool RewindMaxEntries int RewindFreq int // contains filtered or unexported fields }
LazyPrefs lazily accesses the debugger/emulator's preference states.
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 { Summary rewind.Summary 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.MessageState Dir savekey.DataDirection Ack bool Bits uint8 BitsCt int Address uint16 EEPROMdata []uint8 Dirty bool // contains filtered or unexported fields }
LazyChipRegisters lazily accesses chip registere information from the emulator.
type LazyTV ¶
type LazyTV struct { Spec specification.Spec TVstr string LastSignal signal.SignalAttributes Frame int Scanline int HP int IsStable bool AcutalFPS float32 ReqFPS float32 // contains filtered or unexported fields }
LazyTV lazily accesses tv information from the emulator.
type LazyTimer ¶
type LazyTimer struct { Divider string INTIMvalue uint8 TicksRemaining int // contains filtered or unexported fields }
LazyTimer lazily accesses RIOT timer information from the emulator.
type LazyValues ¶ added in v0.7.1
type LazyValues struct { // the debugger is racy. it should not be accessed directly except through // the lazy system or directly with Debugger.PushRawEvent() Dbg *debugger.Debugger // pointers to these instances. non-pointer instances trigger the race // detector for some reason. Debugger *LazyDebugger CPU *LazyCPU RAM *LazyRAM Timer *LazyTimer Playfield *LazyPlayfield Player0 *LazyPlayer Player1 *LazyPlayer Missile0 *LazyMissile Missile1 *LazyMissile Ball *LazyBall TV *LazyTV Cart *LazyCart Controllers *LazyControllers Prefs *LazyPrefs Collisions *LazyCollisions ChipRegisters *LazyChipRegisters Log *LazyLog SaveKey *LazySaveKey Rewind *LazyRewind // note that LazyBreakpoints works slightly different to the the other Lazy* types. Breakpoints *LazyBreakpoints // 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() *LazyValues
NewLazyValues is the preferred method of initialisation for the Values type.
func (*LazyValues) Reset ¶ added in v0.7.1
func (val *LazyValues) Reset(changingCart bool)
Reset lazy values instance.