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.