add mksd command

This commit is contained in:
Zellyn Hunter 2016-12-19 23:51:20 -05:00
parent e376f8ee41
commit 0b7db8d43d
3 changed files with 155 additions and 8 deletions

View File

@ -5,14 +5,17 @@ diskii
functionality, but I'm still experimenting with the command syntax and functionality, but I'm still experimenting with the command syntax and
organization, so don't get too comfy with it. organization, so don't get too comfy with it.
diskii is a commandline tool for working with Apple II disk images. diskii-the-tool is a commandline tool for working with Apple II disk
images. Given that
[AppleCommander](http://applecommander.sourceforge.net/) already does
everything, it's not terribly necessary. It is, however, mine. Minor
benefits (right now) are binaries you can copy around (no Java
needed), and support for Super-Mon symbol tables on NakedOS disks.
It is also a library of code that can be used by other Go programs. diskii-the-library is probably more useful: a library of
disk-image-manipulation code that can be used by other Go programs.
Its major advantage is that it's written in Go, hence diskii's major disadvantage is that it mostly doesn't exist yet.
cross-platform.
Its major disadvantage is that it mostly doesn't exist yet.
[![Build Status](https://travis-ci.org/zellyn/diskii.svg?branch=master)](https://travis-ci.org/zellyn/diskii) [![Build Status](https://travis-ci.org/zellyn/diskii.svg?branch=master)](https://travis-ci.org/zellyn/diskii)
[![Report Card](https://goreportcard.com/badge/github.com/zellyn/diskii)](https://goreportcard.com/report/github.com/zellyn/diskii) [![Report Card](https://goreportcard.com/badge/github.com/zellyn/diskii)](https://goreportcard.com/report/github.com/zellyn/diskii)
@ -69,8 +72,8 @@ will be likely to get priority.
- [x] Build per-platform binaries for Linux, MacOS, Windows. - [x] Build per-platform binaries for Linux, MacOS, Windows.
- [ ] Implement `GetFile` for DOS 3.3 - [ ] Implement `GetFile` for DOS 3.3
- [ ] Add and implement the `-l` flag for `ls` - [ ] Add and implement the `-l` flag for `ls`
- [ ] Add `Delete` to the `disk.Operator` interface - [x] Add `Delete` to the `disk.Operator` interface
- [ ] Implement it for supermon - [x] Implement it for Super-Mon
- [ ] Implement it for DOS 3.3 - [ ] Implement it for DOS 3.3
- [ ] Make 13-sector DOS disks work - [ ] Make 13-sector DOS disks work
- [ ] Read/write nybble formats - [ ] Read/write nybble formats

133
cmd/sd.go Normal file
View File

@ -0,0 +1,133 @@
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/zellyn/diskii/lib/disk"
"github.com/zellyn/diskii/lib/helpers"
)
var sdAddress uint16 // flag for address to load at
var sdStart uint16 // flag for address to start execution at
// mksdCmd represents the mksd command
var mksdCmd = &cobra.Command{
Use: "mksd",
Short: "create a Standard-Delivery disk image",
Long: `diskii mksd creates a "Standard Delivery" disk image containing a binary.
See https://github.com/peterferrie/standard-delivery for details.
Examples:
mksd test.dsk foo.o # load and run foo.o at the default address, then jump to the start of the loaded code.
mksd test.dsk foo.o --address 0x2000 --start 0x2100 # load foo.o at address 0x2000, then jump to 0x2100.`,
Run: func(cmd *cobra.Command, args []string) {
if err := runMkSd(args); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(-1)
}
},
}
func init() {
RootCmd.AddCommand(mksdCmd)
mksdCmd.Flags().Uint16VarP(&sdAddress, "address", "a", 0x6000, "memory location to load code at")
mksdCmd.Flags().Uint16VarP(&sdStart, "start", "s", 0x6000, "memory location to jump to")
}
// ----- mksd command -------------------------------------------------------
// runMkSd performs the actual mksd logic.
func runMkSd(args []string) error {
if len(args) != 2 {
return fmt.Errorf("usage: diskii mksd <disk image> <file-to-load>")
}
contents, err := helpers.FileContentsOrStdIn(args[1])
if err != nil {
return err
}
if sdAddress%256 != 0 {
return fmt.Errorf("address %d (%04X) not on a page boundary", sdAddress, sdAddress)
}
if sdStart < sdAddress {
return fmt.Errorf("start address %d (%04X) < load address %d (%04X)", sdStart, sdStart, sdAddress, sdAddress)
}
if int(sdStart) >= int(sdAddress)+len(contents) {
end := int(sdAddress) + len(contents)
return fmt.Errorf("start address %d (%04X) is beyond load address %d (%04X) + file length = %d (%04X)",
sdStart, sdStart, sdAddress, sdAddress, end, end)
}
if int(sdStart)+len(contents) > 0xC000 {
end := int(sdStart) + len(contents)
return fmt.Errorf("start address %d (%04X) + file length %d (%04X) = %d (%04X), but we can't load past page 0xBF00",
sdStart, sdStart, len(contents), len(contents), end, end)
}
sectors := (len(contents) + 255) / 256
loader := []byte{
0x01, 0xa8, 0xee, 0x06, 0x08, 0xad, 0x4e, 0x08, 0xc9, 0xc0, 0xf0, 0x40, 0x85, 0x27, 0xc8,
0xc0, 0x10, 0x90, 0x09, 0xf0, 0x05, 0x20, 0x2f, 0x08, 0xa8, 0x2c, 0xa0, 0x01, 0x84, 0x3d,
0xc8, 0xa5, 0x27, 0xf0, 0xdf, 0x8a, 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x48, 0xa9, 0x5b,
0x48, 0x60, 0xe6, 0x41, 0x06, 0x40, 0x20, 0x37, 0x08, 0x18, 0x20, 0x3c, 0x08, 0xe6, 0x40,
0xa5, 0x40, 0x29, 0x03, 0x2a, 0x05, 0x2b, 0xa8, 0xb9, 0x80, 0xc0, 0xa9, 0x30, 0x4c, 0xa8,
0xfc, 0x4c, byte(sdStart), byte(sdStart >> 8),
}
if len(loader)+sectors+1 > 256 {
return fmt.Errorf("file %q is %d bytes long, max is %d", args[1], len(contents), (255-len(loader))*256)
}
for len(contents)%256 != 0 {
contents = append(contents, 0)
}
sd := disk.Empty()
var track, sector byte
for i := 0; i < len(contents); i += 256 {
sector += 2
if sector >= sd.Sectors() {
sector = (sd.Sectors() + 1) - sector
if sector == 0 {
track++
if track >= sd.Tracks() {
return fmt.Errorf("ran out of tracks")
}
}
}
address := int(sdAddress) + i
loader = append(loader, byte(address>>8))
if err := sd.WritePhysicalSector(track, sector, contents[i:i+256]); err != nil {
return err
}
}
loader = append(loader, 0xC0)
for len(loader) < 256 {
loader = append(loader, 0)
}
if err := sd.WritePhysicalSector(0, 0, loader); err != nil {
return err
}
f, err := os.Create(args[0])
if err != nil {
return err
}
_, err = sd.Write(f)
if err != nil {
return err
}
if err = f.Close(); err != nil {
return err
}
return nil
}

View File

@ -40,6 +40,17 @@ func LoadDSK(filename string) (DSK, error) {
}, nil }, nil
} }
// Empty creates a .dsk image that is all zeros.
func Empty() DSK {
return DSK{
data: make([]byte, DOS33DiskBytes),
sectors: 16,
physicalToStored: Dos33PhysicalToLogicalSectorMap,
bytesPerTrack: 16 * 256,
tracks: DOS33Tracks,
}
}
// ReadPhysicalSector reads a single physical sector from the disk. It // ReadPhysicalSector reads a single physical sector from the disk. It
// always returns 256 byes. // always returns 256 byes.
func (d DSK) ReadPhysicalSector(track byte, sector byte) ([]byte, error) { func (d DSK) ReadPhysicalSector(track byte, sector byte) ([]byte, error) {