Documentation ¶
Overview ¶
ZX Spectrum emulation core
Index ¶
- Constants
- Variables
- func AddCustomSearchPath(path string)
- func Assert(condition bool)
- func DownloadPath() string
- func Drain(ticker *time.Ticker)
- func FontPath(fileName string) (string, error)
- func InstallSignalHandler(handler SignalHandler)
- func ProgramPath(fileName string) (string, error)
- func ReadROM(path string) (*[0x4000]byte, error)
- func SameBorderEvents(l1, l2 []BorderEvent) bool
- func ScriptPath(fileName string) (string, error)
- func SetDownloadPath(path string)
- func SystemRomPath(fileName string) (string, error)
- func UninstallSignalHandler(handler SignalHandler)
- func WosGet(app *Application, stdout io.Writer, url string) (string, error)
- type Application
- func (app *Application) GetMessageOutput() MessageOutput
- func (app *Application) NewEventLoop() *EventLoop
- func (app *Application) PrintfMsg(format string, a ...interface{})
- func (app *Application) RequestExit()
- func (app *Application) SetMessageOutput(out MessageOutput) MessageOutput
- func (app *Application) Terminated() bool
- func (app *Application) TerminationInProgress() bool
- type Attr_4bit
- type AudioData
- type AudioReceiver
- type BeeperEvent
- type BorderEvent
- type Cmd_AddAudioReceiver
- type Cmd_AddDisplay
- type Cmd_CloseAllAudioReceivers
- type Cmd_CloseAllDisplays
- type Cmd_GetNumAudioReceivers
- type Cmd_GetNumDisplayReceivers
- type Cmd_KeyPress
- type Cmd_Load
- type Cmd_LoadSnapshot
- type Cmd_MakeSnapshot
- type Cmd_MakeVideoMemoryDump
- type Cmd_RenderFrame
- type Cmd_Reset
- type Cmd_SendLoad
- type Cmd_SetAcceleratedLoad
- type Cmd_SetFPS
- type Cmd_SetUlaEmulationAccuracy
- type DisplayData
- type DisplayInfo
- type DisplayReceiver
- type EventLoop
- type FrameStatusOfPorts
- type Joystick
- type Keyboard
- func (keyboard *Keyboard) GetKeyState(row uint) byte
- func (keyboard *Keyboard) KeyDown(logicalKeyCode uint)
- func (keyboard *Keyboard) KeyPress(logicalKeyCode uint) chan bool
- func (keyboard *Keyboard) KeyPressSequence(logicalKeyCodes ...uint) chan bool
- func (keyboard *Keyboard) KeyUp(logicalKeyCode uint)
- func (keyboard *Keyboard) SetKeyState(row uint, state byte)
- type Memory
- func (memory *Memory) ContendRead(address uint16, time int)
- func (memory *Memory) ContendReadNoMreq(address uint16, time int)
- func (memory *Memory) ContendReadNoMreq_loop(address uint16, time int, count uint)
- func (memory *Memory) ContendWriteNoMreq(address uint16, time int)
- func (memory *Memory) ContendWriteNoMreq_loop(address uint16, time int, count uint)
- func (memory *Memory) Data() []byte
- func (memory *Memory) Read(address uint16) byte
- func (memory *Memory) ReadByte(address uint16) byte
- func (memory *Memory) ReadByteInternal(address uint16) byte
- func (memory *Memory) Write(address uint16, value byte, protectROM bool)
- func (memory *Memory) WriteByte(address uint16, b byte)
- func (memory *Memory) WriteByteInternal(address uint16, b byte)
- type MessageOutput
- type Ports
- func (p *Ports) ContendPortPostio(address uint16)
- func (p *Ports) ContendPortPreio(address uint16)
- func (p *Ports) ReadPort(address uint16) byte
- func (p *Ports) ReadPortInternal(address uint16, contend bool) byte
- func (p *Ports) WritePort(address uint16, b byte)
- func (p *Ports) WritePortInternal(address uint16, b byte, contend bool)
- type RGBA
- type RomType
- type SignalHandler
- type Spectrum48k
- type Tape
- type TapeDrive
- type ULA
- type WosRecord
Constants ¶
const ( ScreenWidth = 256 ScreenHeight = 192 BytesPerLine = ScreenWidth / 8 // =32 BytesPerLine_log2 = 5 // =log2(BytesPerLine) ScreenWidth_Attr = ScreenWidth / 8 // =32 ScreenWidth_Attr_log2 = 5 // =log2(ScreenWidth_Attr) ScreenHeight_Attr = ScreenHeight / 8 // =24 ScreenBorderX = 32 ScreenBorderY = 32 // Screen dimensions, including the border TotalScreenWidth = ScreenWidth + ScreenBorderX*2 TotalScreenHeight = ScreenHeight + ScreenBorderY*2 SCREEN_BASE_ADDR = 0x4000 ATTR_BASE_ADDR = 0x5800 )
const ( PIXELS_PER_TSTATE = 2 // The number of screen pixels painted per T-state PIXELS_PER_TSTATE_LOG2 = 1 // = Log2(PIXELS_PER_TSTATE) // Horizontal LINE_SCREEN = ScreenWidth / PIXELS_PER_TSTATE // 128 T states of screen LINE_RIGHT_BORDER = 24 // 24 T states of right border LINE_RETRACE = 48 // 48 T states of horizontal retrace LINE_LEFT_BORDER = 24 // 24 T states of left border TSTATES_PER_LINE = (LINE_RIGHT_BORDER + LINE_SCREEN + LINE_LEFT_BORDER + LINE_RETRACE) // 224 T states FIRST_SCREEN_BYTE = 14336 // T-state when the first byte of the screen (16384) is displayed // Vertical LINES_TOP = 64 LINES_SCREEN = ScreenHeight LINES_BOTTOM = 56 BORDER_TOP = ScreenBorderY BORDER_BOTTOM = ScreenBorderY // The T-state which corresponds to pixel (0,0) on the host-machine display. // That pixel belongs to the border. DISPLAY_START = (FIRST_SCREEN_BYTE - TSTATES_PER_LINE*BORDER_TOP - ScreenBorderX/PIXELS_PER_TSTATE + BORDER_TSTATE_ADJUSTMENT) BORDER_TSTATE_ADJUSTMENT = 2 )
Spectrum 48k video timings
const ( KEMPSTON_FIRE = iota KEMPSTON_UP KEMPSTON_DOWN KEMPSTON_LEFT KEMPSTON_RIGHT )
const ( KEY_1 = iota KEY_2 KEY_3 KEY_4 KEY_5 KEY_6 KEY_7 KEY_8 KEY_9 KEY_0 KEY_Q KEY_W KEY_E KEY_R KEY_T KEY_Y KEY_U KEY_I KEY_O KEY_P KEY_A KEY_S KEY_D KEY_F KEY_G KEY_H KEY_J KEY_K KEY_L KEY_Enter KEY_CapsShift KEY_Z KEY_X KEY_C KEY_V KEY_B KEY_N KEY_M KEY_SymbolShift KEY_Space )
Logical key codes
const ( TAPE_DRIVE_START = iota TAPE_DRIVE_STOP TAPE_DRIVE_PAUSE TAPE_DRIVE_LEADER TAPE_DRIVE_SYNC TAPE_DRIVE_NEWBIT TAPE_DRIVE_HALF2 TAPE_DRIVE_PAUSE_STOP TAPE_DRIVE_NEWBYTE )
const ( TAPE_LEADER = 2168 TAPE_FIRST_SYNC = 667 TAPE_SECOND_SYNC = 735 TAPE_SET_BIT = 1710 TAPE_UNSET_BIT = 855 TAPE_HEADER_LEADER_PULSES = 8063 TAPE_DATA_LEADER_PULSES = 3223 TAPE_PAUSE = 3500000 )
const DefaultFPS = 50.08
const InterruptLength = 32 // How long does an interrupt last in T-states
const MAX_AUDIO_LEVEL = 3
const TAPE_ACCELERATION_IN_FPS = DefaultFPS * 20
const TStatesPerFrame = 69888 // Number of T-states per frame
Variables ¶
var Audio16_Table = [4]float32{ 0, 0x7fff * (Voltage_Issue2[1] - Voltage_Issue2[0]) / (Voltage_Issue2[3] - Voltage_Issue2[0]), 0x7fff * (Voltage_Issue2[2] - Voltage_Issue2[0]) / (Voltage_Issue2[3] - Voltage_Issue2[0]), 0x7fff, }
A table for converting the "audio level" to a 16-bit signed value. Note: Users of this table can assume that 'Audio16_Table[0]' equals to zero.
var DefaultUserDir = path.Join(os.Getenv("HOME"), ".config", "gospeccy")
var Palette [16]uint32 = [16]uint32{ RGBA{000, 000, 000, 255}.value32(), RGBA{000, 000, 192, 255}.value32(), RGBA{192, 000, 000, 255}.value32(), RGBA{192, 000, 192, 255}.value32(), RGBA{000, 192, 000, 255}.value32(), RGBA{000, 192, 192, 255}.value32(), RGBA{192, 192, 000, 255}.value32(), RGBA{192, 192, 192, 255}.value32(), RGBA{000, 000, 000, 255}.value32(), RGBA{000, 000, 255, 255}.value32(), RGBA{255, 000, 000, 255}.value32(), RGBA{255, 000, 255, 255}.value32(), RGBA{000, 255, 000, 255}.value32(), RGBA{000, 255, 255, 255}.value32(), RGBA{255, 255, 000, 255}.value32(), RGBA{255, 255, 255, 255}.value32(), }
var SDL_KeyMap = map[string][]uint{ "0": []uint{KEY_0}, "1": []uint{KEY_1}, "2": []uint{KEY_2}, "3": []uint{KEY_3}, "4": []uint{KEY_4}, "5": []uint{KEY_5}, "6": []uint{KEY_6}, "7": []uint{KEY_7}, "8": []uint{KEY_8}, "9": []uint{KEY_9}, "a": []uint{KEY_A}, "b": []uint{KEY_B}, "c": []uint{KEY_C}, "d": []uint{KEY_D}, "e": []uint{KEY_E}, "f": []uint{KEY_F}, "g": []uint{KEY_G}, "h": []uint{KEY_H}, "i": []uint{KEY_I}, "j": []uint{KEY_J}, "k": []uint{KEY_K}, "l": []uint{KEY_L}, "m": []uint{KEY_M}, "n": []uint{KEY_N}, "o": []uint{KEY_O}, "p": []uint{KEY_P}, "q": []uint{KEY_Q}, "r": []uint{KEY_R}, "s": []uint{KEY_S}, "t": []uint{KEY_T}, "u": []uint{KEY_U}, "v": []uint{KEY_V}, "w": []uint{KEY_W}, "x": []uint{KEY_X}, "y": []uint{KEY_Y}, "z": []uint{KEY_Z}, "return": []uint{KEY_Enter}, "space": []uint{KEY_Space}, "left shift": []uint{KEY_CapsShift}, "right shift": []uint{KEY_CapsShift}, "left ctrl": []uint{KEY_SymbolShift}, "right ctrl": []uint{KEY_SymbolShift}, "left": []uint{KEY_CapsShift, KEY_5}, "down": []uint{KEY_CapsShift, KEY_6}, "up": []uint{KEY_CapsShift, KEY_7}, "right": []uint{KEY_CapsShift, KEY_8}, "backspace": []uint{KEY_CapsShift, KEY_0}, "-": []uint{KEY_SymbolShift, KEY_J}, "=": []uint{KEY_SymbolShift, KEY_L}, "[": []uint{KEY_SymbolShift, KEY_8}, "]": []uint{KEY_SymbolShift, KEY_9}, ";": []uint{KEY_SymbolShift, KEY_O}, "'": []uint{KEY_SymbolShift, KEY_7}, ",": []uint{KEY_SymbolShift, KEY_N}, ".": []uint{KEY_SymbolShift, KEY_M}, "/": []uint{KEY_SymbolShift, KEY_V}, "[0]": []uint{KEY_0}, "[1]": []uint{KEY_1}, "[2]": []uint{KEY_2}, "[3]": []uint{KEY_3}, "[4]": []uint{KEY_4}, "[5]": []uint{KEY_5}, "[6]": []uint{KEY_6}, "[7]": []uint{KEY_7}, "[8]": []uint{KEY_8}, "[9]": []uint{KEY_9}, "[*]": []uint{KEY_SymbolShift, KEY_B}, "[-]": []uint{KEY_SymbolShift, KEY_J}, "[+]": []uint{KEY_SymbolShift, KEY_K}, "[/]": []uint{KEY_SymbolShift, KEY_V}, }
var Voltage_Issue2 = [4]float32{
0.39,
0.73,
3.66,
3.79,
}
Voltage levels on pin 28 of the ULA chip after an out to port 0xFE, with no input signal on the EAR socket. Issue 2 Spectrums. Source: http://www.worldofspectrum.org/faq/reference/48kreference.htm
The array is indexed by [bits 3&4 of the value sent to the port].
var Voltage_Issue3 = [4]float32{
0.34,
0.66,
3.56,
3.70,
}
Voltage levels on pin 28 of the ULA chip after an out to port 0xFE, with no input signal on the EAR socket. Issue 3 Spectrums. Source: http://www.worldofspectrum.org/faq/reference/48kreference.htm
The array is indexed by [bits 3&4 of the value sent to the port].
Functions ¶
func AddCustomSearchPath ¶
func AddCustomSearchPath(path string)
func DownloadPath ¶
func DownloadPath() string
func FontPath ¶
Return a valid path for the specified font file, or the original filename if the search did not find anything.
An error is returned if the search could not proceed.
The search is performed in this order: 1. ./fonts/ 2. $HOME/.config/gospeccy/fonts/ 3. $GOPATH/src/github.com/remogatto/gospeccy/fonts/ 4. Custom search paths
func InstallSignalHandler ¶
func InstallSignalHandler(handler SignalHandler)
Installs the specified handler. Trying to re-install an already installed handler is effectively a NOOP.
func ProgramPath ¶
Return a valid path for the file based on its extension, or the original filename if the search did not find anything.
An error is returned if the search could not proceed.
The search is performed in this order: 1. ./programs/ 2. $GOPATH/src/github.com/remogatto/gospeccy/programs/ 3. Custom search paths 4. Download path
func SameBorderEvents ¶
func SameBorderEvents(l1, l2 []BorderEvent) bool
func ScriptPath ¶
Return a valid path for the specified script, or the original filename if the search did not find anything.
An error is returned if the search could not proceed.
The search is performed in this order: 1. ./scripts/ 2. $HOME/.config/gospeccy/scripts/ 3. $GOPATH/src/github.com/remogatto/gospeccy/scripts/ 4. Custom search paths
func SetDownloadPath ¶
func SetDownloadPath(path string)
func SystemRomPath ¶
Returns a valid path for the 48k system ROM, or the original filename if the search did not find anything.
An error is returned if the search could not proceed.
The search is performed in this order: 1. ./roms/ 2. $HOME/.config/gospeccy/roms/ 3. $GOPATH/src/github.com/remogatto/gospeccy/roms/ 4. Custom search paths
func UninstallSignalHandler ¶
func UninstallSignalHandler(handler SignalHandler)
Uninstalls the specified handler. Trying to uninstall an non-existent handler is effectively a NOOP.
Types ¶
type Application ¶
type Application struct { HasTerminated chan byte // This channel is closed after the whole application has terminated Verbose bool VerboseShutdown bool CreationTime time.Time // The time when this Application object was created // contains filtered or unexported fields }
func NewApplication ¶
func NewApplication() *Application
func (*Application) GetMessageOutput ¶
func (app *Application) GetMessageOutput() MessageOutput
Get the current MessageOutput
func (*Application) NewEventLoop ¶
func (app *Application) NewEventLoop() *EventLoop
func (*Application) PrintfMsg ¶
func (app *Application) PrintfMsg(format string, a ...interface{})
func (*Application) RequestExit ¶
func (app *Application) RequestExit()
func (*Application) SetMessageOutput ¶
func (app *Application) SetMessageOutput(out MessageOutput) MessageOutput
Replaces the MessageOutput, and returns the previous MessageOutput
func (*Application) Terminated ¶
func (app *Application) Terminated() bool
func (*Application) TerminationInProgress ¶
func (app *Application) TerminationInProgress() bool
type Attr_4bit ¶
type Attr_4bit byte
The lower 4 bits define the paper, the higher 4 bits define the ink. Note that the paper is in the *lower* half. There is no flash bit.
type AudioData ¶
type AudioData struct { // The FPS (frames per second) value that applies to this AudioData object FPS float32 BeeperEvents []BeeperEvent }
This is the primary structure for sending audio data from the Z80 CPU emulation core to an audio device.
type AudioReceiver ¶
type AudioReceiver interface { GetAudioDataChannel() chan<- *AudioData // Closes the audio device associated with this AudioReceiver Close() }
Interface to an audio device awaiting audio data
type BeeperEvent ¶
type BeeperEvent struct { // The moment when the beeper-event occurred. // It is the number of T-states since the beginning of the frame. TState int // The beeper level (0 .. MAX_AUDIO_LEVEL) Level byte }
func (*BeeperEvent) GetTState ¶
func (e *BeeperEvent) GetTState() int
type BorderEvent ¶
type BorderEvent struct { // The moment when the border color was changed. // It is the number of T-states since the beginning of the frame. TState int // The new border color Color byte }
func (*BorderEvent) GetTState ¶
func (e *BorderEvent) GetTState() int
type Cmd_AddAudioReceiver ¶
type Cmd_AddAudioReceiver struct {
Receiver AudioReceiver
}
type Cmd_AddDisplay ¶
type Cmd_AddDisplay struct {
Display DisplayReceiver
}
type Cmd_CloseAllAudioReceivers ¶
type Cmd_CloseAllAudioReceivers struct {
Finished chan<- byte
}
type Cmd_CloseAllDisplays ¶
type Cmd_CloseAllDisplays struct {
Finished chan<- byte
}
type Cmd_GetNumAudioReceivers ¶
type Cmd_GetNumAudioReceivers struct {
N chan<- uint
}
type Cmd_GetNumDisplayReceivers ¶
type Cmd_GetNumDisplayReceivers struct {
N chan<- uint
}
type Cmd_KeyPress ¶
type Cmd_KeyPress struct {
// contains filtered or unexported fields
}
type Cmd_LoadSnapshot ¶
type Cmd_MakeSnapshot ¶
type Cmd_MakeSnapshot struct {
Chan chan<- *formats.FullSnapshot
}
type Cmd_MakeVideoMemoryDump ¶
type Cmd_MakeVideoMemoryDump struct {
Chan chan<- []byte
}
type Cmd_RenderFrame ¶
type Cmd_RenderFrame struct { // This channel (if not nil) will receive the real time when the rendering finished. // // If the list of host-machine displays is empty, the time only includes the emulation. // If the list of host-machine displays is non-empty, the time also includes host-machine // rendering. The sent time represents the moment of when the screen data reached // the host-machine display. On Linux this usually means: the moment right after // all pixels have been sent to the X server. // If there are multiple displays, the time includes the 1st display only. // // The time is obtained via a call to time.Nanoseconds(). CompletionTime_orNil chan<- time.Time }
type Cmd_Reset ¶
type Cmd_Reset struct { // This channel will receive [a channel X which will receive 'true' // when it is detected that the system ROM is loaded]. // The channel X will receive 'false' if the emulated machine is reset before // the detection process ends. SystemROMLoaded_orNil chan<- <-chan bool }
type Cmd_SendLoad ¶
type Cmd_SendLoad struct {
// contains filtered or unexported fields
}
type Cmd_SetAcceleratedLoad ¶
type Cmd_SetAcceleratedLoad struct { // Set accelerated tape load on/off Enable bool }
type Cmd_SetFPS ¶
type Cmd_SetUlaEmulationAccuracy ¶
type Cmd_SetUlaEmulationAccuracy struct {
AccurateEmulation bool
}
type DisplayData ¶
type DisplayData struct { Bitmap [BytesPerLine * ScreenHeight]byte // Linear y-coordinate Attr [BytesPerLine * ScreenHeight]Attr_4bit // Linear y-coordinate Dirty [ScreenWidth_Attr * ScreenHeight_Attr]bool // The 8x8 rectangular region was modified, either the bitmap or the attr BorderEvents []BorderEvent // From structure Cmd_RenderFrame CompletionTime_orNil chan<- time.Time }
This is the primary structure for sending display changes from the Z80 CPU emulation core to a rendering backend. The data is already preprocessed, to make the rendering-backend's code simpler and faster.
The content of 'bitmap' and 'attr' corresponding to non-dirty regions is unspecified.
type DisplayInfo ¶
type DisplayInfo struct {
// contains filtered or unexported fields
}
type DisplayReceiver ¶
type DisplayReceiver interface { GetDisplayDataChannel() chan<- *DisplayData // Closes the display associated with this DisplayReceiver Close() }
Interface to a rendering backend awaiting display changes
type EventLoop ¶
type EventLoop struct { // A symbolic name associated to the EventLoop (useful for // debugging). Name string // If [this channel receives a value] then [this event-loop // should pause]. As a response, after this event-loop // actually pauses, a value will appear on this channel. Pause chan byte // Constraint: A value can be sent to this channel only after // this event-loop has been paused. // // If [this channel receives a value] then [this event-loop // should terminate]. As a response, after this event-loop // actually terminates, a value will appear on this channel. Terminate chan byte // contains filtered or unexported fields }
func (*EventLoop) App ¶
func (e *EventLoop) App() *Application
type FrameStatusOfPorts ¶
type FrameStatusOfPorts struct {
// contains filtered or unexported fields
}
type Joystick ¶
type Joystick struct {
// contains filtered or unexported fields
}
func NewJoystick ¶
func NewJoystick() *Joystick
func (*Joystick) KempstonDown ¶
func (*Joystick) KempstonUp ¶
type Keyboard ¶
type Keyboard struct { CommandChannel chan interface{} // contains filtered or unexported fields }
func NewKeyboard ¶
func NewKeyboard() *Keyboard
func (*Keyboard) GetKeyState ¶
func (*Keyboard) KeyPressSequence ¶
func (*Keyboard) SetKeyState ¶
type Memory ¶
type Memory struct {
// contains filtered or unexported fields
}
func (*Memory) ContendRead ¶
func (*Memory) ContendReadNoMreq ¶
func (*Memory) ContendReadNoMreq_loop ¶
func (*Memory) ContendWriteNoMreq ¶
func (*Memory) ContendWriteNoMreq_loop ¶
func (*Memory) ReadByteInternal ¶
func (*Memory) WriteByteInternal ¶
type MessageOutput ¶
type MessageOutput interface { // Prints a single-line message. // If the format string does not end with the new-line character, // the new-line character is appended automatically. PrintfMsg(format string, a ...interface{}) }
type Ports ¶
type Ports struct {
// contains filtered or unexported fields
}
func (*Ports) ContendPortPostio ¶
func (*Ports) ContendPortPreio ¶
func (*Ports) ReadPortInternal ¶
type RomType ¶
type RomType int
const ( ROM_UNKNOWN RomType = iota ROM_OPENSE // OpenSE BASIC (http://www.worldofspectrum.org/infoseekid.cgi?id=0027510) )
type SignalHandler ¶
type Spectrum48k ¶
type Spectrum48k struct { Cpu *z80.Z80 Memory *Memory Keyboard *Keyboard Joystick *Joystick Ports *Ports CommandChannel chan<- interface{} // contains filtered or unexported fields }
func NewSpectrum48k ¶
func NewSpectrum48k(app *Application, rom [0x4000]byte) *Spectrum48k
Creates a new speccy object and starts its command-loop goroutine.
The returned object's CommandChannel can be used to configure the emulated machine before starting the emulation-loop and also to configure the machine while the emulation-loop is running.
To start the actual emulation-loop, create a separate goroutine for running the object's EmulatorLoop function.
func (*Spectrum48k) EmulatorLoop ¶
func (speccy *Spectrum48k) EmulatorLoop()
Sends 'Cmd_RenderFrame' commands to the 'speccy' object in regular intervals. The interval depends on the value of FPS (frames per second).
This function should run in a separate goroutine.
func (*Spectrum48k) GetCurrentFPS ¶
func (speccy *Spectrum48k) GetCurrentFPS() float32
Get current FPS
func (*Spectrum48k) GetEmulationEfficiency ¶
func (speccy *Spectrum48k) GetEmulationEfficiency() uint
Returns the average number of host-CPU instructions required to execute one Z80 instruction. Returns zero if this information is not available.
func (*Spectrum48k) MakeSnapshot ¶
func (speccy *Spectrum48k) MakeSnapshot() *formats.FullSnapshot
func (*Spectrum48k) TapeDrive ¶
func (speccy *Spectrum48k) TapeDrive() *TapeDrive
Return the TapeDrive instance
type TapeDrive ¶
type TapeDrive struct { AcceleratedLoad bool NotifyLoadComplete bool // contains filtered or unexported fields }
func NewTapeDrive ¶
func NewTapeDrive() *TapeDrive
func (*TapeDrive) LoadComplete ¶
type WosRecord ¶
type WosRecord struct { Title string MachineType string // "ZX Spectrum 48K", "ZX Spectrum 48K/128K", ... Publication string // "Freeware", "Commercial", "unknown", ... FtpFiles []string // formerly ftp, now http://www.worldofspectrum.org/... Score string // "7.59 (24 votes)", "No votes yet" }
Information about a game/demo/utility located at [www.worldofspectrum.org]