Proconfigured model for A2Desktop

This commit is contained in:
Ivan Izaguirre 2024-11-04 21:49:33 +01:00
parent 71ea4bd733
commit 42efdcd968
13 changed files with 156 additions and 64 deletions

2
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View File

@ -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

View File

@ -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()

View File

@ -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
}
}
}

View File

@ -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)
}

Binary file not shown.

View File

@ -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
}

View File

@ -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")
}

View File

@ -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 {