Fujinet network device
This commit is contained in:
parent
5d857dda4b
commit
718f0b60b3
|
@ -30,7 +30,7 @@ Portable emulator of an Apple II+ or //e. Written in Go.
|
||||||
- Videx Videoterm 80 column card with the Videx Soft Video Switch (Apple ][+ only)
|
- Videx Videoterm 80 column card with the Videx Soft Video Switch (Apple ][+ only)
|
||||||
- SwyftCard (Apple //e only)
|
- SwyftCard (Apple //e only)
|
||||||
- Useful cards not emulating a real card
|
- Useful cards not emulating a real card
|
||||||
- Bootable Smartport / ProDOS card
|
- Bootable SmartPort / ProDOS card
|
||||||
- VidHd, limited to the ROM signature and SHR as used by Total Replay, only for //e models with 128Kb
|
- VidHd, limited to the ROM signature and SHR as used by Total Replay, only for //e models with 128Kb
|
||||||
- FASTChip, limited to what Total Replay needs to set and clear fast mode
|
- FASTChip, limited to what Total Replay needs to set and clear fast mode
|
||||||
- Mouse Card, emulates the entry points, not the softswitches.
|
- Mouse Card, emulates the entry points, not the softswitches.
|
||||||
|
@ -62,7 +62,7 @@ Portable emulator of an Apple II+ or //e. Written in Go.
|
||||||
- Softswitch reads and writes
|
- Softswitch reads and writes
|
||||||
- ProDOS MLI calls
|
- ProDOS MLI calls
|
||||||
- Apple Pascal BIOS calls
|
- Apple Pascal BIOS calls
|
||||||
- Smartport commands
|
- SmartPort commands
|
||||||
- BBC MOS calls when using [Applecorn](https://github.com/bobbimanners/)
|
- BBC MOS calls when using [Applecorn](https://github.com/bobbimanners/)
|
||||||
- Other features:
|
- Other features:
|
||||||
- Sound
|
- Sound
|
||||||
|
@ -245,7 +245,7 @@ Only valid on SDL mode
|
||||||
-traceCpu
|
-traceCpu
|
||||||
dump to the console the CPU execution. Use F11 to toggle.
|
dump to the console the CPU execution. Use F11 to toggle.
|
||||||
-traceHD
|
-traceHD
|
||||||
dump to the console the hd/smartport commands
|
dump to the console the hd/smartPort commands
|
||||||
-traceMLI
|
-traceMLI
|
||||||
dump to the console the calls to ProDOS machine language interface calls to $BF00
|
dump to the console the calls to ProDOS machine language interface calls to $BF00
|
||||||
-tracePascal
|
-tracePascal
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (a *Apple2) Start(paused bool) {
|
||||||
for i := 0; i < cpuSpinLoops; i++ {
|
for i := 0; i < cpuSpinLoops; i++ {
|
||||||
// Conditional tracing
|
// Conditional tracing
|
||||||
//pc, _ := a.cpu.GetPCAndSP()
|
//pc, _ := a.cpu.GetPCAndSP()
|
||||||
//a.cpu.SetTrace((pc >= 0xc300 && pc <= 0xc400) || (pc >= 0xc800 && pc <= 0xce00))
|
//a.cpu.SetTrace((pc >= 0xc500 && pc < 0xc600) || (pc >= 0xc700 && pc < 0xc800))
|
||||||
|
|
||||||
// Execution
|
// Execution
|
||||||
a.cpu.ExecuteInstruction()
|
a.cpu.ExecuteInstruction()
|
||||||
|
|
|
@ -138,13 +138,24 @@ func (a *Apple2) AddDisk2Sequencer(slot int, diskImage, diskBImage string, track
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSmartPortDisk adds a smart port card and image
|
// AddSmartPortDisk adds a smart port card and image
|
||||||
func (a *Apple2) AddSmartPortDisk(slot int, hdImage string, trace bool) error {
|
func (a *Apple2) AddSmartPortDisk(slot int, hdImage string, traceHD bool, traceSP bool) error {
|
||||||
c := NewCardSmartport()
|
c := NewCardSmartPort()
|
||||||
|
c.trace = traceSP
|
||||||
|
a.insertCard(c, slot)
|
||||||
|
|
||||||
|
err := c.LoadImage(hdImage, traceHD)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSmartPortDisk adds a smart port card and image
|
||||||
|
func (a *Apple2) AddFujinet(slot int, trace bool) {
|
||||||
|
c := NewCardSmartPort()
|
||||||
c.trace = trace
|
c.trace = trace
|
||||||
a.insertCard(c, slot)
|
a.insertCard(c, slot)
|
||||||
|
|
||||||
err := c.LoadImage(hdImage)
|
d := NewSmartPortFujinet(c)
|
||||||
return err
|
d.trace = trace
|
||||||
|
c.AddDevice(0, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddVidHD adds a card with the signature of VidHD
|
// AddVidHD adds a card with the signature of VidHD
|
||||||
|
|
|
@ -32,6 +32,10 @@ func MainApple() *Apple2 {
|
||||||
"hdSlot",
|
"hdSlot",
|
||||||
-1,
|
-1,
|
||||||
"slot for the hard drive if present. -1 for none.")
|
"slot for the hard drive if present. -1 for none.")
|
||||||
|
fujinetSlot := flag.Int(
|
||||||
|
"fujinet",
|
||||||
|
-1,
|
||||||
|
"slot for the smatport card hosting the Fujinet. -1 for none.")
|
||||||
smartPortImage := flag.String(
|
smartPortImage := flag.String(
|
||||||
"disk35",
|
"disk35",
|
||||||
"",
|
"",
|
||||||
|
@ -127,7 +131,11 @@ func MainApple() *Apple2 {
|
||||||
traceHD := flag.Bool(
|
traceHD := flag.Bool(
|
||||||
"traceHD",
|
"traceHD",
|
||||||
false,
|
false,
|
||||||
"dump to the console the hd/smarport commands")
|
"dump to the console the hd accesses")
|
||||||
|
traceSP := flag.Bool(
|
||||||
|
"traceSP",
|
||||||
|
false,
|
||||||
|
"dump to the console the smarport commands")
|
||||||
model := flag.String(
|
model := flag.String(
|
||||||
"model",
|
"model",
|
||||||
"2enh",
|
"2enh",
|
||||||
|
@ -260,12 +268,16 @@ func MainApple() *Apple2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *smartPortImage != "" {
|
if *smartPortImage != "" {
|
||||||
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD)
|
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD, *traceSP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *fujinetSlot >= 0 {
|
||||||
|
a.AddFujinet(*fujinetSlot, *traceSP)
|
||||||
|
}
|
||||||
|
|
||||||
if *fastChipCardSlot >= 0 {
|
if *fastChipCardSlot >= 0 {
|
||||||
a.AddFastChip(*fastChipCardSlot)
|
a.AddFastChip(*fastChipCardSlot)
|
||||||
}
|
}
|
||||||
|
@ -288,7 +300,7 @@ func MainApple() *Apple2 {
|
||||||
// If there is a hard disk image, but no slot assigned, use slot 7.
|
// If there is a hard disk image, but no slot assigned, use slot 7.
|
||||||
*hardDiskSlot = 7
|
*hardDiskSlot = 7
|
||||||
}
|
}
|
||||||
err := a.AddSmartPortDisk(*hardDiskSlot, hardDiskImageFinal, *traceHD)
|
err := a.AddSmartPortDisk(*hardDiskSlot, hardDiskImageFinal, *traceHD, *traceSP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func NewCardDisk2(trackTracer trackTracer) *CardDisk2 {
|
||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns smartport info
|
// GetInfo returns smartPort info
|
||||||
func (c *CardDisk2) GetInfo() map[string]string {
|
func (c *CardDisk2) GetInfo() map[string]string {
|
||||||
info := make(map[string]string)
|
info := make(map[string]string)
|
||||||
info["rom"] = "16 sector"
|
info["rom"] = "16 sector"
|
||||||
|
|
|
@ -5,8 +5,8 @@ import "fmt"
|
||||||
/*
|
/*
|
||||||
Apple II Memory Expansion Card
|
Apple II Memory Expansion Card
|
||||||
|
|
||||||
|
|
||||||
See:
|
See:
|
||||||
|
|
||||||
http://www.apple-iigs.info/doc/fichiers/a2me.pdf
|
http://www.apple-iigs.info/doc/fichiers/a2me.pdf
|
||||||
http://ae.applearchives.com/files/RamFactor_Manual_1.5.pdf
|
http://ae.applearchives.com/files/RamFactor_Manual_1.5.pdf
|
||||||
http://www.1000bit.it/support/manuali/apple/technotes/memx/tn.memx.1.html
|
http://www.1000bit.it/support/manuali/apple/technotes/memx/tn.memx.1.html
|
||||||
|
@ -14,22 +14,27 @@ See:
|
||||||
There is a self test in ROM, address Cs0A.
|
There is a self test in ROM, address Cs0A.
|
||||||
|
|
||||||
From the RamFactor docs:
|
From the RamFactor docs:
|
||||||
|
|
||||||
The RamFactor card has five addressable registers, which are addressed
|
The RamFactor card has five addressable registers, which are addressed
|
||||||
|
|
||||||
according to the slot number the card is in:
|
according to the slot number the card is in:
|
||||||
|
|
||||||
$C080+slot * 16low byte of RAM address
|
$C080+slot * 16low byte of RAM address
|
||||||
$C081+slot * 16middle byte of RAM address
|
$C081+slot * 16middle byte of RAM address
|
||||||
$C082+slot * 16high byte of RAM address
|
$C082+slot * 16high byte of RAM address
|
||||||
$C083+slot * 16data at addressed location
|
$C083+slot * 16data at addressed location
|
||||||
$C08F+slot * 16Firmware Bank Select
|
$C08F+slot * 16Firmware Bank Select
|
||||||
After power up or Control-Reset, the registers on the card are all in a
|
After power up or Control-Reset, the registers on the card are all in a
|
||||||
|
|
||||||
disabled state. They will be enabled by addressing any address in the firmware
|
disabled state. They will be enabled by addressing any address in the firmware
|
||||||
page $Cs00-CsFF.
|
page $Cs00-CsFF.
|
||||||
|
|
||||||
The three address bytes can be both written into and read from. If the card
|
The three address bytes can be both written into and read from. If the card
|
||||||
|
|
||||||
has one Megabyte or less, reading the high address byte will always return a
|
has one Megabyte or less, reading the high address byte will always return a
|
||||||
value in the range $F0-FF. The top nybble can be any value when you write it,
|
value in the range $F0-FF. The top nybble can be any value when you write it,
|
||||||
but it will always be “F” when you read it. If the card has more than one
|
but it will always be “F” when you read it. If the card has more than one
|
||||||
Megabyte of RAM, the top nybble will be a meaningful part of the address.
|
Megabyte of RAM, the top nybble will be a meaningful part of the address.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
memoryExpansionSize256 = 256 * 1024
|
memoryExpansionSize256 = 256 * 1024
|
||||||
|
@ -55,7 +60,7 @@ func NewCardMemoryExpansion() *CardMemoryExpansion {
|
||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns smartport info
|
// GetInfo returns smartPort info
|
||||||
func (c *CardMemoryExpansion) GetInfo() map[string]string {
|
func (c *CardMemoryExpansion) GetInfo() map[string]string {
|
||||||
info := make(map[string]string)
|
info := make(map[string]string)
|
||||||
info["size"] = fmt.Sprintf("%vKB", len(c.ram)/1024)
|
info["size"] = fmt.Sprintf("%vKB", len(c.ram)/1024)
|
||||||
|
|
181
cardSmartport.go
181
cardSmartport.go
|
@ -17,42 +17,48 @@ See:
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// CardSmartport represents a SmartPort card
|
// CardSmartPort represents a SmartPort card
|
||||||
type CardSmartport struct {
|
type CardSmartPort struct {
|
||||||
cardBase
|
cardBase
|
||||||
hardDiskDevice smartPortDevice
|
device smartPortDevice
|
||||||
hardDiskBlocks uint32
|
hardDiskBlocks uint32
|
||||||
|
|
||||||
mliParams uint16
|
mliParams uint16
|
||||||
trace bool
|
trace bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardSmartport creates a new SmartPort card
|
// NewCardSmartPort creates a new SmartPort card
|
||||||
func NewCardSmartport() *CardSmartport {
|
func NewCardSmartPort() *CardSmartPort {
|
||||||
var c CardSmartport
|
var c CardSmartPort
|
||||||
c.name = "Smartport Card"
|
c.name = "SmartPort Card"
|
||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns smartport info
|
// GetInfo returns smartPort info
|
||||||
func (c *CardSmartport) GetInfo() map[string]string {
|
func (c *CardSmartPort) GetInfo() map[string]string {
|
||||||
info := make(map[string]string)
|
info := make(map[string]string)
|
||||||
info["trace"] = strconv.FormatBool(c.trace)
|
info["trace"] = strconv.FormatBool(c.trace)
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadImage loads a disk image
|
// LoadImage loads a disk image
|
||||||
func (c *CardSmartport) LoadImage(filename string) error {
|
func (c *CardSmartPort) LoadImage(filename string, trace bool) error {
|
||||||
device, err := NewSmartPortHardDisk(c, filename)
|
device, err := NewSmartPortHardDisk(c, filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
device.trace = c.trace
|
device.trace = trace
|
||||||
c.hardDiskDevice = device
|
c.device = device
|
||||||
c.hardDiskBlocks = device.disk.GetSizeInBlocks() // Needed for the PRODOS status
|
c.hardDiskBlocks = device.disk.GetSizeInBlocks() // Needed for the PRODOS status
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardSmartport) assign(a *Apple2, slot int) {
|
// LoadImage loads a disk image
|
||||||
|
func (c *CardSmartPort) AddDevice(unt uint8, device smartPortDevice) {
|
||||||
|
c.device = device
|
||||||
|
c.hardDiskBlocks = 0 // Needed for the PRODOS status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CardSmartPort) assign(a *Apple2, slot int) {
|
||||||
c.loadRom(buildHardDiskRom(slot))
|
c.loadRom(buildHardDiskRom(slot))
|
||||||
|
|
||||||
c.addCardSoftSwitchR(0, func() uint8 {
|
c.addCardSoftSwitchR(0, func() uint8 {
|
||||||
|
@ -61,25 +67,26 @@ func (c *CardSmartport) assign(a *Apple2, slot int) {
|
||||||
unit := a.mmu.Peek(0x43) & 0x0f
|
unit := a.mmu.Peek(0x43) & 0x0f
|
||||||
|
|
||||||
// Generate Smarport compatible params
|
// Generate Smarport compatible params
|
||||||
var params []uint8
|
var call *smartPortCall
|
||||||
if command == proDosDeviceCommandStatus {
|
if command == proDosDeviceCommandStatus {
|
||||||
params = []uint8{
|
call = newSmartPortCallSynthetic(c, command, []uint8{
|
||||||
5, unit,
|
3, // 3 args
|
||||||
|
unit,
|
||||||
a.mmu.Peek(0x44), a.mmu.Peek(0x45), // data address
|
a.mmu.Peek(0x44), a.mmu.Peek(0x45), // data address
|
||||||
0,
|
0,
|
||||||
}
|
})
|
||||||
} else {
|
} else if command == proDosDeviceCommandRead || command == proDosDeviceCommandWrite {
|
||||||
params = []uint8{
|
call = newSmartPortCallSynthetic(c, command, []uint8{
|
||||||
7, unit,
|
3, // 3args
|
||||||
|
unit,
|
||||||
a.mmu.Peek(0x44), a.mmu.Peek(0x45), // data address
|
a.mmu.Peek(0x44), a.mmu.Peek(0x45), // data address
|
||||||
a.mmu.Peek(0x46), a.mmu.Peek(0x47), 0, // block number
|
a.mmu.Peek(0x46), a.mmu.Peek(0x47), 0, // block number
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return proDosDeviceBadCommand
|
||||||
}
|
}
|
||||||
}
|
|
||||||
result := c.hardDiskDevice.exec(command, params)
|
return c.exec(call)
|
||||||
if c.trace {
|
|
||||||
fmt.Printf("[CardSmartport] PRODOS command $%x on slot %v, unit $%x, result $%02x.\n", command, slot, unit, result)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}, "SMARTPORTPRODOSCOMMAND")
|
}, "SMARTPORTPRODOSCOMMAND")
|
||||||
|
|
||||||
c.addCardSoftSwitchR(1, func() uint8 {
|
c.addCardSoftSwitchR(1, func() uint8 {
|
||||||
|
@ -96,36 +103,61 @@ func (c *CardSmartport) assign(a *Apple2, slot int) {
|
||||||
command := c.a.mmu.Peek(c.mliParams + 1)
|
command := c.a.mmu.Peek(c.mliParams + 1)
|
||||||
paramsAddress := uint16(c.a.mmu.Peek(c.mliParams+2)) + uint16(c.a.mmu.Peek(c.mliParams+3))<<8
|
paramsAddress := uint16(c.a.mmu.Peek(c.mliParams+2)) + uint16(c.a.mmu.Peek(c.mliParams+3))<<8
|
||||||
|
|
||||||
paramsSize := int(a.mmu.Peek(paramsAddress + 0))
|
call := newSmartPortCall(c, command, paramsAddress)
|
||||||
params := make([]uint8, paramsSize)
|
return c.exec(call)
|
||||||
for i := 0; i < paramsSize; i++ {
|
|
||||||
params[i] = a.mmu.Peek(paramsAddress + uint16(i))
|
|
||||||
}
|
|
||||||
unit := params[1]
|
|
||||||
|
|
||||||
result := c.hardDiskDevice.exec(command, params)
|
|
||||||
if c.trace {
|
|
||||||
fmt.Printf("[CardSmartport] Smart port command $%x on slot %v, unit $%x, result $%02x.\n", command, slot, unit, result)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}, "SMARTPORTEXEC")
|
}, "SMARTPORTEXEC")
|
||||||
|
|
||||||
c.addCardSoftSwitchW(4, func(value uint8) {
|
c.addCardSoftSwitchW(4, func(value uint8) {
|
||||||
c.mliParams = (c.mliParams & 0xff00) + uint16(value)
|
c.mliParams = (c.mliParams & 0xff00) + uint16(value)
|
||||||
if c.trace {
|
|
||||||
fmt.Printf("[CardSmartport] Smart port LO: 0x%x.\n", c.mliParams)
|
|
||||||
}
|
|
||||||
}, "HDSMARTPORTLO")
|
}, "HDSMARTPORTLO")
|
||||||
c.addCardSoftSwitchW(5, func(value uint8) {
|
c.addCardSoftSwitchW(5, func(value uint8) {
|
||||||
c.mliParams = (c.mliParams & 0x00ff) + (uint16(value) << 8)
|
c.mliParams = (c.mliParams & 0x00ff) + (uint16(value) << 8)
|
||||||
if c.trace {
|
|
||||||
fmt.Printf("[CardSmartport] Smart port HI: 0x%x.\n", c.mliParams)
|
|
||||||
}
|
|
||||||
}, "HDSMARTPORTHI")
|
}, "HDSMARTPORTHI")
|
||||||
|
|
||||||
c.cardBase.assign(a, slot)
|
c.cardBase.assign(a, slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CardSmartPort) exec(call *smartPortCall) uint8 {
|
||||||
|
var result uint8
|
||||||
|
|
||||||
|
if call.command == proDosDeviceCommandStatus &&
|
||||||
|
// Call to the host
|
||||||
|
call.statusCode() == prodosDeviceStatusCodeDevice {
|
||||||
|
|
||||||
|
result = c.hostStatus(call)
|
||||||
|
} else if call.unit() > 1 {
|
||||||
|
result = proDosDeviceErrorNoDevice
|
||||||
|
} else {
|
||||||
|
// TODO: select the device, 0(host) is sent to 1
|
||||||
|
result = c.device.exec(call)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.trace {
|
||||||
|
fmt.Printf("[CardSmartPort] Command %v on slot %v => result %s.\n",
|
||||||
|
call, c.slot, smartPortErrorMessage(result))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CardSmartPort) hostStatus(call *smartPortCall) uint8 {
|
||||||
|
dest := call.param16(2)
|
||||||
|
if c.trace {
|
||||||
|
fmt.Printf("[CardSmartPort] Host status into $%x.\n", dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See http://www.1000bit.it/support/manuali/apple/technotes/smpt/tn.smpt.2.html
|
||||||
|
c.a.mmu.Poke(dest+0, 0x01) // One device
|
||||||
|
c.a.mmu.Poke(dest+1, 0xff) // No interrupt
|
||||||
|
c.a.mmu.Poke(dest+2, 0x00)
|
||||||
|
c.a.mmu.Poke(dest+3, 0x00) // Unknown manufacturer
|
||||||
|
c.a.mmu.Poke(dest+4, 0x01)
|
||||||
|
c.a.mmu.Poke(dest+5, 0x00) // Version 1.0 final
|
||||||
|
c.a.mmu.Poke(dest+6, 0x00)
|
||||||
|
c.a.mmu.Poke(dest+7, 0x00) // Reserved
|
||||||
|
|
||||||
|
return proDosDeviceNoError
|
||||||
|
}
|
||||||
|
|
||||||
func buildHardDiskRom(slot int) []uint8 {
|
func buildHardDiskRom(slot int) []uint8 {
|
||||||
data := make([]uint8, 256)
|
data := make([]uint8, 256)
|
||||||
ssBase := 0x80 + uint8(slot<<4)
|
ssBase := 0x80 + uint8(slot<<4)
|
||||||
|
@ -136,7 +168,38 @@ func buildHardDiskRom(slot int) []uint8 {
|
||||||
0xa9, 0x00, // LDA #$00
|
0xa9, 0x00, // LDA #$00
|
||||||
0xa9, 0x03, // LDA #$03
|
0xa9, 0x03, // LDA #$03
|
||||||
0xa9, 0x00, // LDA #$00
|
0xa9, 0x00, // LDA #$00
|
||||||
|
0xd0, 0x36, // BNE bootcode, there is no space for a jmp
|
||||||
|
})
|
||||||
|
|
||||||
|
if slot == 7 {
|
||||||
|
// It should be 0 for SmartPort, but with 0 it's not bootable with the II+ ROM
|
||||||
|
// See http://www.1000bit.it/support/manuali/apple/technotes/udsk/tn.udsk.2.html
|
||||||
|
data[0x07] = 0x3c
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(data[0x0a:], []uint8{
|
||||||
|
// Entrypoints and SmartPort body it has to be in $Cx0a
|
||||||
|
0x4c, 0x80, 0xc0 + uint8(slot), // JMP $cs80 ; Prodos Entrypoint
|
||||||
|
|
||||||
|
// 3 bytes later, smartPort entrypoint. Uses the ProDos MLI calling convention
|
||||||
|
0x68, // PLA
|
||||||
|
0x8d, ssBase + 4, 0xc0, // STA $c0n4 ; Softswitch 4, store LO(cmdBlock)
|
||||||
|
0xa8, // TAY ; We will need it later
|
||||||
|
0x68, // PLA
|
||||||
|
0x8d, ssBase + 5, 0xc0, // STA $c0n5 ; Softswitch 5, store HI(cmdBlock)
|
||||||
|
0x48, // PHA
|
||||||
|
0x98, // TYA
|
||||||
|
0x18, // CLC
|
||||||
|
0x69, 0x03, // ADC #$03 ; Fix return address past the cmdblock
|
||||||
|
0x48, // PHA
|
||||||
|
0xad, ssBase + 3, 0xc0, // LDA $C0n3 ; Softswitch 3, execute command. Error code in reg A.
|
||||||
|
0x18, // CLC ; Clear carry for no errors.
|
||||||
|
0xF0, 0x01, // BEQ $01 ; Skips the SEC if reg A is zero
|
||||||
|
0x38, // SEC ; Set carry on errors
|
||||||
|
0x60, // RTS
|
||||||
|
})
|
||||||
|
|
||||||
|
copy(data[0x40:], []uint8{
|
||||||
// Boot code: SS will load block 0 in address $0800. The jump there.
|
// Boot code: SS will load block 0 in address $0800. The jump there.
|
||||||
// Note: after execution the first block expects $42 to $47 to have
|
// Note: after execution the first block expects $42 to $47 to have
|
||||||
// valid values to read block 0. At least Total Replay expects that.
|
// valid values to read block 0. At least Total Replay expects that.
|
||||||
|
@ -155,34 +218,6 @@ func buildHardDiskRom(slot int) []uint8 {
|
||||||
0x4c, 0x01, 0x08, // JMP $801 ; Jump to loaded boot sector
|
0x4c, 0x01, 0x08, // JMP $801 ; Jump to loaded boot sector
|
||||||
})
|
})
|
||||||
|
|
||||||
if slot == 7 {
|
|
||||||
// It should be 0 for SmartPort, but with 0 it's not bootable with the II+ ROM
|
|
||||||
// See http://www.1000bit.it/support/manuali/apple/technotes/udsk/tn.udsk.2.html
|
|
||||||
data[0x07] = 0x3c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entrypoints and Smartport body
|
|
||||||
copy(data[0x40:], []uint8{
|
|
||||||
0x4c, 0x80, 0xc0 + uint8(slot), // JMP $cs80 ; Prodos Entrypoint
|
|
||||||
|
|
||||||
// 3 bytes later, smartport entrypoint. Uses the ProDos MLI calling convention
|
|
||||||
0x68, // PLA
|
|
||||||
0x8d, ssBase + 4, 0xc0, // STA $c0n4 ; Softswitch 4, store LO(cmdBlock)
|
|
||||||
0xa8, // TAY ; We will need it later
|
|
||||||
0x68, // PLA
|
|
||||||
0x8d, ssBase + 5, 0xc0, // STA $c0n5 ; Softswitch 5, store HI(cmdBlock)
|
|
||||||
0x48, // PHA
|
|
||||||
0x98, // TYA
|
|
||||||
0x18, // CLC
|
|
||||||
0x69, 0x03, // ADC #$03 ; Fix return address past the cmdblock
|
|
||||||
0x48, // PHA
|
|
||||||
0xad, ssBase + 3, 0xc0, // LDA $C0n3 ; Softswitch 3, execute command. Error code in reg A.
|
|
||||||
0x18, // CLC ; Clear carry for no errors.
|
|
||||||
0xF0, 0x01, // BEQ $01 ; Skips the SEC if reg A is zero
|
|
||||||
0x38, // SEC ; Set carry on errors
|
|
||||||
0x60, // RTS
|
|
||||||
})
|
|
||||||
|
|
||||||
// Prodos entrypoint body
|
// Prodos entrypoint body
|
||||||
copy(data[0x80:], []uint8{
|
copy(data[0x80:], []uint8{
|
||||||
0xad, ssBase + 0, 0xc0, // LDA $C0n0 ; Softswitch 0, execute command. Error code in reg A.
|
0xad, ssBase + 0, 0xc0, // LDA $C0n0 ; Softswitch 0, execute command. Error code in reg A.
|
||||||
|
@ -199,7 +234,7 @@ func buildHardDiskRom(slot int) []uint8 {
|
||||||
data[0xfc] = 0
|
data[0xfc] = 0
|
||||||
data[0xfd] = 0
|
data[0xfd] = 0
|
||||||
data[0xfe] = 3 // Status and Read. No write, no format. Single volume
|
data[0xfe] = 3 // Status and Read. No write, no format. Single volume
|
||||||
data[0xff] = 0x40 // Driver entry point
|
data[0xff] = 0x0a // Driver entry point // Must be $0a
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
/*
|
||||||
|
A smartPort device
|
||||||
|
*/
|
||||||
|
|
||||||
|
type smartPortDevice interface {
|
||||||
|
exec(call *smartPortCall) uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
proDosDeviceCommandStatus = 0
|
||||||
|
proDosDeviceCommandRead = 1
|
||||||
|
proDosDeviceCommandWrite = 2
|
||||||
|
proDosDeviceCommandFormat = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prodosDeviceStatusCodeDevice = 0
|
||||||
|
prodosDeviceStatusCodeDeviceControlBlock = 1
|
||||||
|
prodosDeviceStatusCodeNewline = 2
|
||||||
|
prodosDeviceStatusCodeDeviceInfo = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prodosDeviceStatusCodeTypeBlock = uint8(1) << 7
|
||||||
|
prodosDeviceStatusCodeTypeWrite = uint8(1) << 6
|
||||||
|
prodosDeviceStatusCodeTypeRead = uint8(1) << 5
|
||||||
|
prodosDeviceStatusCodeTypeOnline = uint8(1) << 4
|
||||||
|
prodosDeviceStatusCodeTypeFormat = uint8(1) << 3
|
||||||
|
prodosDeviceStatusCodeTypeProtected = uint8(1) << 2
|
||||||
|
prodosDeviceStatusCodeTypeInterruping = uint8(1) << 1
|
||||||
|
prodosDeviceStatusCodeTypeOpen = uint8(1) << 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
proDosDeviceNoError = uint8(0)
|
||||||
|
proDosDeviceBadCommand = uint8(1)
|
||||||
|
proDosDeviceErrorIO = uint8(0x27)
|
||||||
|
proDosDeviceErrorNoDevice = uint8(0x28)
|
||||||
|
proDosDeviceErrorWriteProtected = uint8(0x2b)
|
||||||
|
)
|
||||||
|
|
||||||
|
type smartPortCall struct {
|
||||||
|
host *CardSmartPort
|
||||||
|
command uint8
|
||||||
|
|
||||||
|
address uint16 // When the params are on the Apple memory
|
||||||
|
params []uint8 // When the params are built externally as on a ProDOS to SP translation
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSmartPortCall(host *CardSmartPort, command uint8, address uint16) *smartPortCall {
|
||||||
|
var spc smartPortCall
|
||||||
|
spc.host = host
|
||||||
|
spc.command = command
|
||||||
|
spc.address = address
|
||||||
|
spc.params = nil
|
||||||
|
return &spc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSmartPortCallSynthetic(host *CardSmartPort, command uint8, params []uint8) *smartPortCall {
|
||||||
|
var spc smartPortCall
|
||||||
|
spc.host = host
|
||||||
|
spc.command = command
|
||||||
|
spc.address = 0xffff
|
||||||
|
spc.params = params
|
||||||
|
return &spc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spc *smartPortCall) unit() uint8 {
|
||||||
|
return spc.param8(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spc *smartPortCall) statusCode() uint8 {
|
||||||
|
if spc.command != proDosDeviceCommandStatus {
|
||||||
|
panic("Status code paremeter requeted for a non status smartport call")
|
||||||
|
}
|
||||||
|
return spc.param8(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spc *smartPortCall) param8(offset uint8) uint8 {
|
||||||
|
if spc.params == nil {
|
||||||
|
return spc.host.a.mmu.Peek(spc.address + uint16(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(offset) >= len(spc.params) {
|
||||||
|
panic("Synthetised smartpot call out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
return spc.params[offset]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spc *smartPortCall) param16(offset uint8) uint16 {
|
||||||
|
return uint16(spc.param8(offset)) +
|
||||||
|
uint16(spc.param8(offset+1))<<8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spc *smartPortCall) param24(offset uint8) uint32 {
|
||||||
|
return uint32(spc.param8(offset)) +
|
||||||
|
uint32(spc.param8(offset+1))<<8 +
|
||||||
|
uint32(spc.param8(offset+2))<<16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spc *smartPortCall) String() string {
|
||||||
|
switch spc.command {
|
||||||
|
case proDosDeviceCommandStatus:
|
||||||
|
return fmt.Sprintf("STATUS(%v, unit=%v, code=%v)",
|
||||||
|
spc.command, spc.unit(),
|
||||||
|
spc.statusCode())
|
||||||
|
case proDosDeviceCommandRead:
|
||||||
|
return fmt.Sprintf("READ(%v, unit=%v, block=%v)",
|
||||||
|
spc.command, spc.unit(),
|
||||||
|
spc.param24(4))
|
||||||
|
case proDosDeviceCommandWrite:
|
||||||
|
return fmt.Sprintf("WRITE(%v, unit=%v, block=%v)",
|
||||||
|
spc.command, spc.unit(),
|
||||||
|
spc.param24(4))
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("UNKNOWN(%v, unit=%v)",
|
||||||
|
spc.command, spc.unit())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func smartPortErrorMessage(code uint8) string {
|
||||||
|
switch code {
|
||||||
|
case proDosDeviceNoError:
|
||||||
|
return "SUCCESS"
|
||||||
|
case proDosDeviceBadCommand:
|
||||||
|
return "BAD_COMMAND"
|
||||||
|
case proDosDeviceErrorIO:
|
||||||
|
return "ERROR_IO"
|
||||||
|
case proDosDeviceErrorNoDevice:
|
||||||
|
return "NO_DEVICE"
|
||||||
|
case proDosDeviceErrorWriteProtected:
|
||||||
|
return "WRITE_PROTECT_ERROR"
|
||||||
|
default:
|
||||||
|
return string(code)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
package izapple2
|
|
||||||
|
|
||||||
/*
|
|
||||||
A smartport device
|
|
||||||
*/
|
|
||||||
|
|
||||||
type smartPortDevice interface {
|
|
||||||
exec(command uint8, params []uint8) uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
proDosDeviceCommandStatus = 0
|
|
||||||
proDosDeviceCommandRead = 1
|
|
||||||
proDosDeviceCommandWrite = 2
|
|
||||||
proDosDeviceCommandFormat = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
proDosDeviceNoError = uint8(0)
|
|
||||||
proDosDeviceErrorIO = uint8(0x27)
|
|
||||||
proDosDeviceErrorNoDevice = uint8(0x28)
|
|
||||||
proDosDeviceErrorWriteProtected = uint8(0x2b)
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
func smartPortParam8(params []uint8, offset uint8) uint8 {
|
|
||||||
if int(offset) >= len(params) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return params[offset]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func smartPortParam16(params []uint8, offset uint8) uint16 {
|
|
||||||
if int(offset+1) >= len(params) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return uint16(params[offset]) + uint16(params[offset+1])<<8
|
|
||||||
}
|
|
||||||
|
|
||||||
func smartPortParam24(params []uint8, offset uint8) uint32 {
|
|
||||||
if int(offset+2) >= len(params) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return uint32(params[offset]) + uint32(params[offset+1])<<8 + uint32(params[offset+2])<<16
|
|
||||||
}
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
The network device as implemented by Fujinet:
|
||||||
|
|
||||||
|
See:
|
||||||
|
https://github.com/FujiNetWIFI/fujinet-platformio/blob/master/lib/device/iwm/network.cpp
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// SmartPortFujinet represents a Fujinet device
|
||||||
|
type SmartPortFujinet struct {
|
||||||
|
host *CardSmartPort // For DMA
|
||||||
|
trace bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSmartPortFujinet creates a new fujinet device
|
||||||
|
func NewSmartPortFujinet(host *CardSmartPort) *SmartPortFujinet {
|
||||||
|
var d SmartPortFujinet
|
||||||
|
d.host = host
|
||||||
|
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SmartPortFujinet) exec(call *smartPortCall) uint8 {
|
||||||
|
var result uint8
|
||||||
|
|
||||||
|
switch call.command {
|
||||||
|
case proDosDeviceCommandStatus:
|
||||||
|
address := call.param16(2)
|
||||||
|
result = d.status(call.statusCode(), address)
|
||||||
|
|
||||||
|
case proDosDeviceCommandRead:
|
||||||
|
address := call.param16(2)
|
||||||
|
block := call.param24(4)
|
||||||
|
result = d.readBlock(block, address)
|
||||||
|
|
||||||
|
case proDosDeviceCommandWrite:
|
||||||
|
address := call.param16(2)
|
||||||
|
block := call.param24(4)
|
||||||
|
result = d.writeBlock(block, address)
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Prodos device command not supported
|
||||||
|
result = proDosDeviceErrorIO
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.trace {
|
||||||
|
fmt.Printf("[SmartPortFujinet] Command %v, return %s \n",
|
||||||
|
call, smartPortErrorMessage(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SmartPortFujinet) readBlock(block uint32, dest uint16) uint8 {
|
||||||
|
if d.trace {
|
||||||
|
fmt.Printf("[SmartPortFujinet] Read block %v into $%x.\n", block, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return proDosDeviceNoError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SmartPortFujinet) writeBlock(block uint32, source uint16) uint8 {
|
||||||
|
if d.trace {
|
||||||
|
fmt.Printf("[SmartPortFujinet] Write block %v from $%x.\n", block, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return proDosDeviceNoError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SmartPortFujinet) status(code uint8, dest uint16) uint8 {
|
||||||
|
|
||||||
|
switch code {
|
||||||
|
case prodosDeviceStatusCodeDevice:
|
||||||
|
// See iwmNetwork::encode_status_reply_packet()
|
||||||
|
d.host.a.mmu.Poke(dest, prodosDeviceStatusCodeTypeRead&prodosDeviceStatusCodeTypeOnline)
|
||||||
|
d.host.a.mmu.Poke(dest+1, 0x00)
|
||||||
|
d.host.a.mmu.Poke(dest+2, 0x00)
|
||||||
|
d.host.a.mmu.Poke(dest+3, 0x00) // Block size is 0
|
||||||
|
|
||||||
|
case prodosDeviceStatusCodeDeviceInfo:
|
||||||
|
// See iwmNetwork::encode_status_reply_packet()
|
||||||
|
d.host.a.mmu.Poke(dest, prodosDeviceStatusCodeTypeRead&prodosDeviceStatusCodeTypeOnline)
|
||||||
|
d.host.a.mmu.Poke(dest+1, 0x00)
|
||||||
|
d.host.a.mmu.Poke(dest+2, 0x00)
|
||||||
|
d.host.a.mmu.Poke(dest+3, 0x00) // Block size is 0
|
||||||
|
d.host.a.mmu.Poke(dest+4, 0x07) // Name length
|
||||||
|
d.host.a.mmu.Poke(dest+5, 'N')
|
||||||
|
d.host.a.mmu.Poke(dest+6, 'E')
|
||||||
|
d.host.a.mmu.Poke(dest+7, 'T')
|
||||||
|
d.host.a.mmu.Poke(dest+8, 'W')
|
||||||
|
d.host.a.mmu.Poke(dest+9, 'O')
|
||||||
|
d.host.a.mmu.Poke(dest+10, 'R')
|
||||||
|
d.host.a.mmu.Poke(dest+11, 'K')
|
||||||
|
d.host.a.mmu.Poke(dest+12, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+13, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+14, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+15, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+16, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+17, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+18, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+19, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+20, ' ')
|
||||||
|
d.host.a.mmu.Poke(dest+20, 0x02) // Type hard disk
|
||||||
|
d.host.a.mmu.Poke(dest+20, 0x00) // Subtype network (comment in network.cpp has 0x0a)
|
||||||
|
d.host.a.mmu.Poke(dest+23, 0x00)
|
||||||
|
d.host.a.mmu.Poke(dest+24, 0x01) // Firmware
|
||||||
|
}
|
||||||
|
|
||||||
|
return proDosDeviceNoError // The return code is always success
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
To implement a smartport hard drive we have to support the smartport commands.
|
To implement a smartPort hard drive we have to support the smartPort commands.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
Beneath Prodos, section 6-6, 7-13 and 5-8. (http://www.apple-iigs.info/doc/fichiers/beneathprodos.pdf)
|
Beneath Prodos, section 6-6, 7-13 and 5-8. (http://www.apple-iigs.info/doc/fichiers/beneathprodos.pdf)
|
||||||
|
@ -15,16 +15,16 @@ See:
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// SmartPortHardDisk represents a SmartPort card
|
// SmartPortHardDisk represents a hard disk
|
||||||
type SmartPortHardDisk struct {
|
type SmartPortHardDisk struct {
|
||||||
host *CardSmartport // For DMA
|
host *CardSmartPort // For DMA
|
||||||
filename string
|
filename string
|
||||||
trace bool
|
trace bool
|
||||||
disk *storage.BlockDisk
|
disk *storage.BlockDisk
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSmartPortHardDisk creates a new hard disk with the smartport interface
|
// NewSmartPortHardDisk creates a new hard disk with the smartPort interface
|
||||||
func NewSmartPortHardDisk(host *CardSmartport, filename string) (*SmartPortHardDisk, error) {
|
func NewSmartPortHardDisk(host *CardSmartPort, filename string) (*SmartPortHardDisk, error) {
|
||||||
var d SmartPortHardDisk
|
var d SmartPortHardDisk
|
||||||
d.host = host
|
d.host = host
|
||||||
d.filename = filename
|
d.filename = filename
|
||||||
|
@ -38,22 +38,22 @@ func NewSmartPortHardDisk(host *CardSmartport, filename string) (*SmartPortHardD
|
||||||
return &d, nil
|
return &d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SmartPortHardDisk) exec(command uint8, params []uint8) uint8 {
|
func (d *SmartPortHardDisk) exec(call *smartPortCall) uint8 {
|
||||||
var result uint8
|
var result uint8
|
||||||
|
|
||||||
switch command {
|
switch call.command {
|
||||||
case proDosDeviceCommandStatus:
|
case proDosDeviceCommandStatus:
|
||||||
address := smartPortParam16(params, 2)
|
address := call.param16(2)
|
||||||
result = d.status(address)
|
result = d.status(address)
|
||||||
|
|
||||||
case proDosDeviceCommandRead:
|
case proDosDeviceCommandRead:
|
||||||
address := smartPortParam16(params, 2)
|
address := call.param16(2)
|
||||||
block := smartPortParam24(params, 4)
|
block := call.param24(4)
|
||||||
result = d.readBlock(block, address)
|
result = d.readBlock(block, address)
|
||||||
|
|
||||||
case proDosDeviceCommandWrite:
|
case proDosDeviceCommandWrite:
|
||||||
address := smartPortParam16(params, 2)
|
address := call.param16(2)
|
||||||
block := smartPortParam24(params, 4)
|
block := call.param24(4)
|
||||||
result = d.writeBlock(block, address)
|
result = d.writeBlock(block, address)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -62,7 +62,8 @@ func (d *SmartPortHardDisk) exec(command uint8, params []uint8) uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.trace {
|
if d.trace {
|
||||||
fmt.Printf("[SmartPortHardDisk] Command $%x, return $%02x \n", command, result)
|
fmt.Printf("[SmartPortFujinet] Command %v, return %s \n",
|
||||||
|
call, smartPortErrorMessage(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -77,6 +78,7 @@ func (d *SmartPortHardDisk) readBlock(block uint32, dest uint16) uint8 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return proDosDeviceErrorIO
|
return proDosDeviceErrorIO
|
||||||
}
|
}
|
||||||
|
|
||||||
// Byte by byte transfer to memory using the full Poke code path
|
// Byte by byte transfer to memory using the full Poke code path
|
||||||
for i := uint16(0); i < uint16(len(data)); i++ {
|
for i := uint16(0); i < uint16(len(data)); i++ {
|
||||||
d.host.a.mmu.Poke(dest+i, data[i])
|
d.host.a.mmu.Poke(dest+i, data[i])
|
||||||
|
@ -114,7 +116,7 @@ func (d *SmartPortHardDisk) status(dest uint16) uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See http://www.1000bit.it/support/manuali/apple/technotes/smpt/tn.smpt.2.html
|
// See http://www.1000bit.it/support/manuali/apple/technotes/smpt/tn.smpt.2.html
|
||||||
d.host.a.mmu.Poke(dest+0, 0x02) // One device
|
d.host.a.mmu.Poke(dest+0, 0x01) // One device
|
||||||
d.host.a.mmu.Poke(dest+1, 0xff) // No interrupt
|
d.host.a.mmu.Poke(dest+1, 0xff) // No interrupt
|
||||||
d.host.a.mmu.Poke(dest+2, 0x00)
|
d.host.a.mmu.Poke(dest+2, 0x00)
|
||||||
d.host.a.mmu.Poke(dest+3, 0x00) // Unknown manufacturer
|
d.host.a.mmu.Poke(dest+3, 0x00) // Unknown manufacturer
|
||||||
|
|
Loading…
Reference in New Issue