Skip to content

Commit

Permalink
Proconfigured model for A2Desktop
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanizag committed Nov 4, 2024
1 parent 71ea4bd commit 42efdcd
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 58 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions apple2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions cardMouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions configs/desktop.cfg
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions doc/usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion frontend/a2sdl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
18 changes: 10 additions & 8 deletions frontend/a2sdl/sdlJoysticks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}

Expand Down
35 changes: 33 additions & 2 deletions resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand Down Expand Up @@ -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 added resources/A2DeskTop-1.4-en_800k.2mg
Binary file not shown.
4 changes: 2 additions & 2 deletions smartPortHardDisk.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down
99 changes: 70 additions & 29 deletions storage/blockDisk.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package storage

import (
"bytes"
"errors"
"fmt"
"io"
"os"
)

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

fileInfo, err := bd.file.Stat()
if err != nil {
return nil, err
}
bd.file = file

err2mg := parse2mg(&bd)
if err2mg == nil {
// It's a 2mg file, ready to use
return &bd, nil
size := uint32(fileInfo.Size())
bd.blocks, bd.dataOffset, err = getBlockAndOffset(bd.file, size)
if err != nil {
return nil, err
}
return &bd, nil
}

// Let's try to load as raw ProDOS Blocks
fileInfo, err := bd.file.Stat()
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
}

if fileInfo.Size() > int64(ProDosBlockSize*proDosMaxBlocks) {
return nil, fmt.Errorf("file is too big OR %s", err2mg.Error())
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())
}

size := uint32(fileInfo.Size())
if size%ProDosBlockSize != 0 {
return nil, fmt.Errorf("file size os invalid OR %s", err2mg.Error())
return 0, 0, fmt.Errorf("file size os invalid OR %s", err.Error())
}

// It's a valid raw file
bd.blocks = size / ProDosBlockSize
bd.dataOffset = 0
return &bd, nil
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")
}
Expand All @@ -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")
}
Expand All @@ -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")
}
24 changes: 8 additions & 16 deletions storage/file2mg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 42efdcd

Please sign in to comment.