Fujinet network device

This commit is contained in:
Ivan Izaguirre 2022-10-24 23:09:06 +02:00
parent 5d857dda4b
commit 718f0b60b3
11 changed files with 431 additions and 148 deletions

View File

@ -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)
- SwyftCard (Apple //e only)
- 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
- FASTChip, limited to what Total Replay needs to set and clear fast mode
- 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
- ProDOS MLI calls
- Apple Pascal BIOS calls
- Smartport commands
- SmartPort commands
- BBC MOS calls when using [Applecorn](https://github.com/bobbimanners/)
- Other features:
- Sound
@ -245,7 +245,7 @@ Only valid on SDL mode
-traceCpu
dump to the console the CPU execution. Use F11 to toggle.
-traceHD
dump to the console the hd/smartport commands
dump to the console the hd/smartPort commands
-traceMLI
dump to the console the calls to ProDOS machine language interface calls to $BF00
-tracePascal

View File

@ -67,7 +67,7 @@ func (a *Apple2) Start(paused bool) {
for i := 0; i < cpuSpinLoops; i++ {
// Conditional tracing
//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
a.cpu.ExecuteInstruction()

View File

@ -138,13 +138,24 @@ func (a *Apple2) AddDisk2Sequencer(slot int, diskImage, diskBImage string, track
}
// AddSmartPortDisk adds a smart port card and image
func (a *Apple2) AddSmartPortDisk(slot int, hdImage string, trace bool) error {
c := NewCardSmartport()
func (a *Apple2) AddSmartPortDisk(slot int, hdImage string, traceHD bool, traceSP bool) error {
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
a.insertCard(c, slot)
err := c.LoadImage(hdImage)
return err
d := NewSmartPortFujinet(c)
d.trace = trace
c.AddDevice(0, d)
}
// AddVidHD adds a card with the signature of VidHD

View File

@ -32,6 +32,10 @@ func MainApple() *Apple2 {
"hdSlot",
-1,
"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(
"disk35",
"",
@ -127,7 +131,11 @@ func MainApple() *Apple2 {
traceHD := flag.Bool(
"traceHD",
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",
"2enh",
@ -260,12 +268,16 @@ func MainApple() *Apple2 {
}
if *smartPortImage != "" {
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD)
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD, *traceSP)
if err != nil {
panic(err)
}
}
if *fujinetSlot >= 0 {
a.AddFujinet(*fujinetSlot, *traceSP)
}
if *fastChipCardSlot >= 0 {
a.AddFastChip(*fastChipCardSlot)
}
@ -288,7 +300,7 @@ func MainApple() *Apple2 {
// If there is a hard disk image, but no slot assigned, use slot 7.
*hardDiskSlot = 7
}
err := a.AddSmartPortDisk(*hardDiskSlot, hardDiskImageFinal, *traceHD)
err := a.AddSmartPortDisk(*hardDiskSlot, hardDiskImageFinal, *traceHD, *traceSP)
if err != nil {
panic(err)
}

View File

@ -52,7 +52,7 @@ func NewCardDisk2(trackTracer trackTracer) *CardDisk2 {
return &c
}
// GetInfo returns smartport info
// GetInfo returns smartPort info
func (c *CardDisk2) GetInfo() map[string]string {
info := make(map[string]string)
info["rom"] = "16 sector"

View File

@ -5,8 +5,8 @@ import "fmt"
/*
Apple II Memory Expansion Card
See:
http://www.apple-iigs.info/doc/fichiers/a2me.pdf
http://ae.applearchives.com/files/RamFactor_Manual_1.5.pdf
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.
From the RamFactor docs:
The RamFactor card has five addressable registers, which are addressed
according to the slot number the card is in:
$C080+slot * 16low byte of RAM address
$C081+slot * 16middle byte of RAM address
$C082+slot * 16high byte of RAM address
$C083+slot * 16data at addressed location
$C08F+slot * 16Firmware Bank Select
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
page $Cs00-CsFF.
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
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
Megabyte of RAM, the top nybble will be a meaningful part of the address.
*/
const (
memoryExpansionSize256 = 256 * 1024
@ -55,7 +60,7 @@ func NewCardMemoryExpansion() *CardMemoryExpansion {
return &c
}
// GetInfo returns smartport info
// GetInfo returns smartPort info
func (c *CardMemoryExpansion) GetInfo() map[string]string {
info := make(map[string]string)
info["size"] = fmt.Sprintf("%vKB", len(c.ram)/1024)

View File

@ -17,42 +17,48 @@ See:
*/
// CardSmartport represents a SmartPort card
type CardSmartport struct {
// CardSmartPort represents a SmartPort card
type CardSmartPort struct {
cardBase
hardDiskDevice smartPortDevice
device smartPortDevice
hardDiskBlocks uint32
mliParams uint16
trace bool
}
// NewCardSmartport creates a new SmartPort card
func NewCardSmartport() *CardSmartport {
var c CardSmartport
c.name = "Smartport Card"
// NewCardSmartPort creates a new SmartPort card
func NewCardSmartPort() *CardSmartPort {
var c CardSmartPort
c.name = "SmartPort Card"
return &c
}
// GetInfo returns smartport info
func (c *CardSmartport) GetInfo() map[string]string {
// GetInfo returns smartPort info
func (c *CardSmartPort) GetInfo() map[string]string {
info := make(map[string]string)
info["trace"] = strconv.FormatBool(c.trace)
return info
}
// 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)
if err == nil {
device.trace = c.trace
c.hardDiskDevice = device
device.trace = trace
c.device = device
c.hardDiskBlocks = device.disk.GetSizeInBlocks() // Needed for the PRODOS status
}
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.addCardSoftSwitchR(0, func() uint8 {
@ -61,25 +67,26 @@ func (c *CardSmartport) assign(a *Apple2, slot int) {
unit := a.mmu.Peek(0x43) & 0x0f
// Generate Smarport compatible params
var params []uint8
var call *smartPortCall
if command == proDosDeviceCommandStatus {
params = []uint8{
5, unit,
call = newSmartPortCallSynthetic(c, command, []uint8{
3, // 3 args
unit,
a.mmu.Peek(0x44), a.mmu.Peek(0x45), // data address
0,
}
} else {
params = []uint8{
7, unit,
})
} else if command == proDosDeviceCommandRead || command == proDosDeviceCommandWrite {
call = newSmartPortCallSynthetic(c, command, []uint8{
3, // 3args
unit,
a.mmu.Peek(0x44), a.mmu.Peek(0x45), // data address
a.mmu.Peek(0x46), a.mmu.Peek(0x47), 0, // block number
}
})
} else {
return proDosDeviceBadCommand
}
result := c.hardDiskDevice.exec(command, params)
if c.trace {
fmt.Printf("[CardSmartport] PRODOS command $%x on slot %v, unit $%x, result $%02x.\n", command, slot, unit, result)
}
return result
return c.exec(call)
}, "SMARTPORTPRODOSCOMMAND")
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)
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))
params := make([]uint8, paramsSize)
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
call := newSmartPortCall(c, command, paramsAddress)
return c.exec(call)
}, "SMARTPORTEXEC")
c.addCardSoftSwitchW(4, func(value uint8) {
c.mliParams = (c.mliParams & 0xff00) + uint16(value)
if c.trace {
fmt.Printf("[CardSmartport] Smart port LO: 0x%x.\n", c.mliParams)
}
}, "HDSMARTPORTLO")
c.addCardSoftSwitchW(5, func(value uint8) {
c.mliParams = (c.mliParams & 0x00ff) + (uint16(value) << 8)
if c.trace {
fmt.Printf("[CardSmartport] Smart port HI: 0x%x.\n", c.mliParams)
}
}, "HDSMARTPORTHI")
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 {
data := make([]uint8, 256)
ssBase := 0x80 + uint8(slot<<4)
@ -136,7 +168,38 @@ func buildHardDiskRom(slot int) []uint8 {
0xa9, 0x00, // LDA #$00
0xa9, 0x03, // LDA #$03
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.
// Note: after execution the first block expects $42 to $47 to have
// 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
})
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
copy(data[0x80:], []uint8{
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[0xfd] = 0
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
}

143
smartPortCall.go Normal file
View File

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

View File

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

121
smartPortFujinet.go Normal file
View File

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

View File

@ -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:
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 {
host *CardSmartport // For DMA
host *CardSmartPort // For DMA
filename string
trace bool
disk *storage.BlockDisk
}
// NewSmartPortHardDisk creates a new hard disk with the smartport interface
func NewSmartPortHardDisk(host *CardSmartport, filename string) (*SmartPortHardDisk, error) {
// NewSmartPortHardDisk creates a new hard disk with the smartPort interface
func NewSmartPortHardDisk(host *CardSmartPort, filename string) (*SmartPortHardDisk, error) {
var d SmartPortHardDisk
d.host = host
d.filename = filename
@ -38,22 +38,22 @@ func NewSmartPortHardDisk(host *CardSmartport, filename string) (*SmartPortHardD
return &d, nil
}
func (d *SmartPortHardDisk) exec(command uint8, params []uint8) uint8 {
func (d *SmartPortHardDisk) exec(call *smartPortCall) uint8 {
var result uint8
switch command {
switch call.command {
case proDosDeviceCommandStatus:
address := smartPortParam16(params, 2)
address := call.param16(2)
result = d.status(address)
case proDosDeviceCommandRead:
address := smartPortParam16(params, 2)
block := smartPortParam24(params, 4)
address := call.param16(2)
block := call.param24(4)
result = d.readBlock(block, address)
case proDosDeviceCommandWrite:
address := smartPortParam16(params, 2)
block := smartPortParam24(params, 4)
address := call.param16(2)
block := call.param24(4)
result = d.writeBlock(block, address)
default:
@ -62,7 +62,8 @@ func (d *SmartPortHardDisk) exec(command uint8, params []uint8) uint8 {
}
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
@ -77,6 +78,7 @@ func (d *SmartPortHardDisk) readBlock(block uint32, dest uint16) uint8 {
if err != nil {
return proDosDeviceErrorIO
}
// Byte by byte transfer to memory using the full Poke code path
for i := uint16(0); i < uint16(len(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
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+2, 0x00)
d.host.a.mmu.Poke(dest+3, 0x00) // Unknown manufacturer