mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-12-26 20:29:50 +00:00
Proconfigured model for A2Desktop
This commit is contained in:
parent
71ea4bd733
commit
42efdcd968
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,10 +4,12 @@ a2sdl.exe
|
||||
frontend/a2sdl/a2sdl
|
||||
frontend/*/*.woz
|
||||
frontend/*/*.dsk
|
||||
frontend/*/*.DSK
|
||||
frontend/*/*.po
|
||||
frontend/*/*.2mg
|
||||
frontend/*/*.hdv
|
||||
frontend/*/*.zip
|
||||
frontend/*/images
|
||||
frontend/a2fyne/a2fyne
|
||||
frontend/headless/headless
|
||||
frontend/*/snapshot.gif
|
||||
|
@ -234,6 +234,7 @@ The available pre-configured models are:
|
||||
cpm: Apple ][+ with CP/M
|
||||
cpm3: Apple //e with CP/M 3.0
|
||||
cpm65: Apple //e with CPM-65
|
||||
desktop: Apple II DeskTop
|
||||
dos32: Apple ][ with 13 sectors disk adapter and DOS 3.2x
|
||||
swyft: swyft
|
||||
|
||||
|
@ -23,6 +23,7 @@ type Apple2 struct {
|
||||
isApple2e bool
|
||||
hasLowerCase bool
|
||||
isFourColors bool // An Apple II without the 6 color mod
|
||||
usesMouse bool
|
||||
commandChannel chan command
|
||||
|
||||
dmaActive bool
|
||||
@ -67,6 +68,11 @@ func (a *Apple2) SetMouseProvider(m MouseProvider) {
|
||||
a.io.setMouseProvider(m)
|
||||
}
|
||||
|
||||
// UsesMouse returns true when the emulator uses the mouse
|
||||
func (a *Apple2) UsesMouse() bool {
|
||||
return a.usesMouse
|
||||
}
|
||||
|
||||
// IsPaused returns true when emulator is paused
|
||||
func (a *Apple2) IsPaused() bool {
|
||||
return a.paused
|
||||
|
@ -103,6 +103,7 @@ func (c *CardMouse) readMouse() (uint16, uint16, bool) {
|
||||
}
|
||||
|
||||
func (c *CardMouse) assign(a *Apple2, slot int) {
|
||||
a.usesMouse = true
|
||||
c.addCardSoftSwitchR(0, func() uint8 {
|
||||
c.checkFromFirmware()
|
||||
if c.iOut == 0 {
|
||||
|
15
configs/desktop.cfg
Normal file
15
configs/desktop.cfg
Normal file
@ -0,0 +1,15 @@
|
||||
name: Apple II DeskTop
|
||||
parent: _base
|
||||
board: 2e
|
||||
cpu: 65c02
|
||||
rom: <internal>/Apple2e_Enhanced.rom
|
||||
charrom: <internal>/Apple IIe Video Enhanced.bin
|
||||
ramworks: 8192
|
||||
nsc: main
|
||||
rgb: true
|
||||
s0: language
|
||||
s2: vidhd
|
||||
s3: fastchip
|
||||
s4: mouse
|
||||
s6: diskii
|
||||
s7: smartport,image1=<internal>/A2DeskTop-1.4-en_800k.2mg
|
@ -54,6 +54,7 @@ The available pre-configured models are:
|
||||
cpm: Apple ][+ with CP/M
|
||||
cpm3: Apple //e with CP/M 3.0
|
||||
cpm65: Apple //e with CPM-65
|
||||
desktop: Apple II DeskTop
|
||||
dos32: Apple ][ with 13 sectors disk adapter and DOS 3.2x
|
||||
swyft: swyft
|
||||
|
||||
|
@ -51,7 +51,7 @@ func sdlRun(a *izapple2.Apple2) {
|
||||
s.start()
|
||||
a.SetSpeakerProvider(s)
|
||||
|
||||
j := newSDLJoysticks(true)
|
||||
j := newSDLJoysticks(!a.UsesMouse())
|
||||
a.SetJoysticksProvider(j)
|
||||
|
||||
m := newSDLMouse()
|
||||
|
@ -104,14 +104,16 @@ func (j *sdlJoysticks) putMouseMotionEvent(e *sdl.MouseMotionEvent, width int32,
|
||||
}
|
||||
|
||||
func (j *sdlJoysticks) putMouseButtonEvent(e *sdl.MouseButtonEvent) {
|
||||
pressed := e.State == sdl.PRESSED
|
||||
switch e.Button {
|
||||
case 1: // BUTTON_LEFT
|
||||
j.mousebuttons[0] = pressed
|
||||
case 3: // BUTTON_RIGHT
|
||||
j.mousebuttons[1] = pressed
|
||||
case 2: // BUTTON_MIDDLE
|
||||
j.mousebuttons[2] = pressed
|
||||
if j.useMouse {
|
||||
pressed := e.State == sdl.PRESSED
|
||||
switch e.Button {
|
||||
case 1: // BUTTON_LEFT
|
||||
j.mousebuttons[0] = pressed
|
||||
case 3: // BUTTON_RIGHT
|
||||
j.mousebuttons[1] = pressed
|
||||
case 2: // BUTTON_MIDDLE
|
||||
j.mousebuttons[2] = pressed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
35
resources.go
35
resources.go
@ -32,8 +32,7 @@ func isHTTPResource(filename string) bool {
|
||||
strings.HasPrefix(filename, httpsPrefix)
|
||||
}
|
||||
|
||||
// LoadResource loads in memory a file from the filesystem, http or embedded
|
||||
func LoadResource(filename string) ([]uint8, bool, error) {
|
||||
func normalizeFilename(filename string) string {
|
||||
// Remove quotes if surrounded by them
|
||||
if strings.HasPrefix(filename, "\"") && strings.HasSuffix(filename, "\"") {
|
||||
filename = filename[1 : len(filename)-1]
|
||||
@ -45,7 +44,14 @@ func LoadResource(filename string) ([]uint8, bool, error) {
|
||||
if err == nil {
|
||||
filename = home + filename[1:]
|
||||
}
|
||||
|
||||
}
|
||||
return filename
|
||||
}
|
||||
|
||||
// LoadResource loads in memory a file from the filesystem, http or embedded
|
||||
func LoadResource(filename string) ([]uint8, bool, error) {
|
||||
filename = normalizeFilename(filename)
|
||||
|
||||
var writeable bool
|
||||
var file io.Reader
|
||||
@ -132,3 +138,28 @@ func LoadDiskette(filename string) (storage.Diskette, error) {
|
||||
|
||||
return storage.MakeDiskette(data, filename, writeable)
|
||||
}
|
||||
|
||||
// LoadBlockDisk returns a BlockDisk
|
||||
func LoadBlockDisk(filename string) (storage.BlockDisk, error) {
|
||||
filename = normalizeFilename(filename)
|
||||
|
||||
// Try to open as a file
|
||||
readOnly := false
|
||||
file, err := os.OpenFile(filename, os.O_RDWR, 0)
|
||||
if os.IsPermission(err) {
|
||||
// Retry in read-only mode
|
||||
readOnly = true
|
||||
file, _ = os.OpenFile(filename, os.O_RDONLY, 0)
|
||||
}
|
||||
if file != nil {
|
||||
return storage.NewBlockDiskFile(file, readOnly)
|
||||
}
|
||||
|
||||
// Load as a resource
|
||||
data, _, err := LoadResource(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storage.NewBlockDiskMemory(data)
|
||||
}
|
||||
|
BIN
resources/A2DeskTop-1.4-en_800k.2mg
Normal file
BIN
resources/A2DeskTop-1.4-en_800k.2mg
Normal file
Binary file not shown.
@ -20,7 +20,7 @@ type SmartPortHardDisk struct {
|
||||
host *CardSmartPort // For DMA
|
||||
filename string
|
||||
trace bool
|
||||
disk *storage.BlockDisk
|
||||
disk storage.BlockDisk
|
||||
}
|
||||
|
||||
// NewSmartPortHardDisk creates a new hard disk with the smartPort interface
|
||||
@ -29,7 +29,7 @@ func NewSmartPortHardDisk(host *CardSmartPort, filename string) (*SmartPortHardD
|
||||
d.host = host
|
||||
d.filename = filename
|
||||
|
||||
hd, err := storage.OpenBlockDisk(filename)
|
||||
hd, err := LoadBlockDisk(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
@ -17,67 +19,93 @@ const (
|
||||
)
|
||||
|
||||
// BlockDisk is any block device with 512 bytes blocks
|
||||
type BlockDisk struct {
|
||||
type BlockDisk interface {
|
||||
GetSizeInBlocks() uint32
|
||||
IsReadOnly() bool
|
||||
Read(block uint32) ([]uint8, error)
|
||||
Write(block uint32, data []uint8) error
|
||||
}
|
||||
|
||||
type blockDiskBase struct {
|
||||
file *os.File
|
||||
readOnly bool
|
||||
dataOffset uint32
|
||||
blocks uint32
|
||||
}
|
||||
|
||||
// OpenBlockDisk creates a new block device links to a file
|
||||
func OpenBlockDisk(filename string) (*BlockDisk, error) {
|
||||
var bd BlockDisk
|
||||
type blockDiskFile struct {
|
||||
blockDiskBase
|
||||
file *os.File
|
||||
}
|
||||
|
||||
bd.readOnly = false
|
||||
file, err := os.OpenFile(filename, os.O_RDWR, 0)
|
||||
if os.IsPermission(err) {
|
||||
// Retry in read-only mode
|
||||
bd.readOnly = true
|
||||
file, err = os.OpenFile(filename, os.O_RDONLY, 0)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type blockDiskMemory struct {
|
||||
blockDiskBase
|
||||
data []uint8
|
||||
}
|
||||
|
||||
// OpenBlockDisk creates a new block device linked to a file
|
||||
func NewBlockDiskFile(file *os.File, readOnly bool) (BlockDisk, error) {
|
||||
var bd blockDiskFile
|
||||
bd.file = file
|
||||
bd.readOnly = readOnly
|
||||
|
||||
err2mg := parse2mg(&bd)
|
||||
if err2mg == nil {
|
||||
// It's a 2mg file, ready to use
|
||||
return &bd, nil
|
||||
}
|
||||
|
||||
// Let's try to load as raw ProDOS Blocks
|
||||
fileInfo, err := bd.file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fileInfo.Size() > int64(ProDosBlockSize*proDosMaxBlocks) {
|
||||
return nil, fmt.Errorf("file is too big OR %s", err2mg.Error())
|
||||
}
|
||||
|
||||
size := uint32(fileInfo.Size())
|
||||
if size%ProDosBlockSize != 0 {
|
||||
return nil, fmt.Errorf("file size os invalid OR %s", err2mg.Error())
|
||||
bd.blocks, bd.dataOffset, err = getBlockAndOffset(bd.file, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// It's a valid raw file
|
||||
bd.blocks = size / ProDosBlockSize
|
||||
bd.dataOffset = 0
|
||||
return &bd, nil
|
||||
}
|
||||
|
||||
func NewBlockDiskMemory(data []uint8) (BlockDisk, error) {
|
||||
var bd blockDiskMemory
|
||||
bd.data = data
|
||||
bd.readOnly = true
|
||||
|
||||
var err error
|
||||
bd.blocks, bd.dataOffset, err = getBlockAndOffset(bytes.NewReader(data), uint32(len(data)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bd, nil
|
||||
}
|
||||
|
||||
func getBlockAndOffset(reader io.Reader, size uint32) (uint32, uint32, error) {
|
||||
header, err := parse2mg(reader, size)
|
||||
if err == nil {
|
||||
// It's a 2mg file
|
||||
return header.Blocks, header.OffsetData, nil
|
||||
}
|
||||
|
||||
// Let's try to load as raw ProDOS Blocks
|
||||
if size > ProDosBlockSize*proDosMaxBlocks {
|
||||
return 0, 0, fmt.Errorf("file is too big OR %s", err.Error())
|
||||
}
|
||||
|
||||
if size%ProDosBlockSize != 0 {
|
||||
return 0, 0, fmt.Errorf("file size os invalid OR %s", err.Error())
|
||||
}
|
||||
|
||||
// It's a valid raw file
|
||||
return size / ProDosBlockSize, 0, nil
|
||||
}
|
||||
|
||||
// GetSizeInBlocks returns the number of blocks of the device
|
||||
func (bd *BlockDisk) GetSizeInBlocks() uint32 {
|
||||
func (bd *blockDiskBase) GetSizeInBlocks() uint32 {
|
||||
return bd.blocks
|
||||
}
|
||||
|
||||
// IsReadOnly returns true if the device is read only
|
||||
func (bd *BlockDisk) IsReadOnly() bool {
|
||||
func (bd *blockDiskBase) IsReadOnly() bool {
|
||||
return bd.readOnly
|
||||
}
|
||||
|
||||
func (bd *BlockDisk) Read(block uint32) ([]uint8, error) {
|
||||
func (bd *blockDiskFile) Read(block uint32) ([]uint8, error) {
|
||||
if block >= bd.blocks {
|
||||
return nil, errors.New("disk block number is too big")
|
||||
}
|
||||
@ -93,7 +121,16 @@ func (bd *BlockDisk) Read(block uint32) ([]uint8, error) {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (bd *BlockDisk) Write(block uint32, data []uint8) error {
|
||||
func (bd *blockDiskMemory) Read(block uint32) ([]uint8, error) {
|
||||
if block >= bd.blocks {
|
||||
return nil, errors.New("disk block number is too big")
|
||||
}
|
||||
|
||||
offset := bd.dataOffset + block*ProDosBlockSize
|
||||
return bd.data[offset : offset+ProDosBlockSize], nil
|
||||
}
|
||||
|
||||
func (bd *blockDiskFile) Write(block uint32, data []uint8) error {
|
||||
if bd.readOnly {
|
||||
return errors.New("can't write in a readonly disk")
|
||||
}
|
||||
@ -109,3 +146,7 @@ func (bd *BlockDisk) Write(block uint32, data []uint8) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bd *blockDiskMemory) Write(block uint32, data []uint8) error {
|
||||
return errors.New("can't write in a readonly disk")
|
||||
}
|
||||
|
@ -35,31 +35,23 @@ type file2mgHeader struct {
|
||||
LengthCreator uint32
|
||||
}
|
||||
|
||||
func parse2mg(bd *BlockDisk) error {
|
||||
fileInfo, err := bd.file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func parse2mg(reader io.Reader, size uint32) (*file2mgHeader, error) {
|
||||
var header file2mgHeader
|
||||
minHeaderSize := binary.Size(&header)
|
||||
if fileInfo.Size() < int64(minHeaderSize) {
|
||||
return errors.New("invalid 2MG file")
|
||||
if size < uint32(minHeaderSize) {
|
||||
return nil, errors.New("invalid 2MG file")
|
||||
}
|
||||
|
||||
err = readHeader(bd.file, &header)
|
||||
err := readHeader(reader, &header)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bd.blocks = header.Blocks
|
||||
bd.dataOffset = header.OffsetData
|
||||
|
||||
if fileInfo.Size() < int64(bd.dataOffset+bd.blocks*ProDosBlockSize) {
|
||||
return errors.New("the 2MG file is too small")
|
||||
if size < header.OffsetData+header.Blocks*ProDosBlockSize {
|
||||
return nil, errors.New("the 2MG file is too small")
|
||||
}
|
||||
|
||||
return nil
|
||||
return &header, nil
|
||||
}
|
||||
|
||||
func readHeader(buf io.Reader, header *file2mgHeader) error {
|
||||
|
Loading…
Reference in New Issue
Block a user