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 LazyCoProc
- 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(addr uint16) debugger.BreakGroup
HasBreak checks to see if disassembly entry has a breakpoint.
func (*LazyBreakpoints) SetUpdateList ¶ added in v0.8.0
func (lz *LazyBreakpoints) SetUpdateList(bank int, start uint16, end uint16)
SetUpdateList defines the range of addresses that will be checked by the HasBreak() function. The actual checking is performed during the normal lazyvalues.Refresh() process and defining a narrow a list as possible speeds up the process considerably.
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
Mapping string
Filename string
NumBanks int
CurrBank mapper.BankInfo
HasStaticBus bool
Static []mapper.CartStatic
HasRegistersBus bool
Registers mapper.CartRegisters
HasRAMbus bool
RAM []mapper.CartRAM
HasTapeBus bool
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 LazyCoProc ¶ added in v0.8.0
LazyCoProc lazily accesses coproceddor 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 {
LeftPlayer ports.Peripheral
RightPlayer 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
HasChanged bool
// 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 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 {
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
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
Frame int
Scanline int
Clock int
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 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 {
// 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
CoProc *LazyCoProc
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
// current time is put on the channel on every Refresh()
RefreshPulse chan time.Time
// 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) SetEmulation ¶ added in v0.14.0
func (val *LazyValues) SetEmulation(emulation emulation.Emulation)
Set the underlying emulator.
func (*LazyValues) SetEmulationState ¶ added in v0.14.0
func (val *LazyValues) SetEmulationState(state emulation.State)
SetEmulationState makes sure the lazy system can respond to an emulation in a particular state.