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 LazyCPU
- type LazyCart
- type LazyCollisions
- type LazyDebugger
- type LazyMem
- type LazyMissile
- type LazyPeripherals
- type LazyPhaseClock
- type LazyPlayer
- type LazyPlayfield
- type LazyPorts
- type LazyRAM
- type LazyRewind
- type LazySaveKey
- type LazyTV
- type LazyTimer
- type LazyTracker
- 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 LazyCPU ¶
type LazyCPU struct {
HasReset bool
RdyFlg bool
Killed bool
PC registers.ProgramCounter
A registers.Register
X registers.Register
Y registers.Register
SP registers.Register
StatusReg registers.StatusRegister
LastResult execution.Result
RTSPrediction uint16
// 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
IsPlusROM bool
PlusROMAddrInfo plusrom.AddrInfo
PlusROMRecvBuff []uint8
PlusROMSendBuff []uint8
// 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.BreakpointsQuery
HasChanged bool
// the emulation.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
// emulation.State() value (which is safe to obtain outside of the lazy
// system) when synchronisation is important
State emulation.State
// contains filtered or unexported fields
}
LazyDebugger lazily accesses Debugger information.
type LazyMem ¶ added in v0.16.0
type LazyMem struct {
AddressBus uint16
DataBus uint8
LastAccessWrite bool
LastAccessMask uint8
// contains filtered or unexported fields
}
LazyMem lazily accesses Mem 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 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 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 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 LazyTracker ¶ added in v0.15.0
type LazyTracker struct {
Entries []tracker.Entry
LastEntry [2]tracker.Entry
// contains filtered or unexported fields
}
LazyTracker lazily accesses logging entries.
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
Mem *LazyMem
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
Tracker *LazyTracker
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(e emulation.Emulation) *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.