izapple2/smartPortFujinetNetwork.go

248 lines
5.4 KiB
Go
Raw Normal View History

2022-10-24 21:09:06 +00:00
package izapple2
import (
"fmt"
"net/url"
"strings"
"github.com/ivanizag/izapple2/fujinet"
2022-10-24 21:09:06 +00:00
)
/*
The network device as implemented by Fujinet:
See:
https://github.com/FujiNetWIFI/fujinet-platformio/blob/master/lib/device/iwm/network.cpp
*/
2022-11-01 15:57:45 +00:00
// SmartPortFujinetNetwork represents a Fujinet device
type SmartPortFujinetNetwork struct {
2022-10-24 21:09:06 +00:00
host *CardSmartPort // For DMA
trace bool
protocol fujinet.Protocol
jsonChannelMode bool
statusByte uint8
errorCode fujinet.ErrorCode
jsonData *fujinet.FnJson
data []uint8
//connected uint8
2022-10-24 21:09:06 +00:00
}
2022-11-01 15:57:45 +00:00
// NewSmartPortFujinetNetwork creates a new fujinet device
func NewSmartPortFujinetNetwork(host *CardSmartPort) *SmartPortFujinetNetwork {
var d SmartPortFujinetNetwork
2022-10-24 21:09:06 +00:00
d.host = host
d.errorCode = fujinet.NoError
2022-10-24 21:09:06 +00:00
return &d
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) exec(call *smartPortCall) uint8 {
2022-10-24 21:09:06 +00:00
var result uint8
switch call.command {
2022-10-31 21:57:18 +00:00
case smartPortCommandOpen:
result = smartPortNoError
2022-10-31 21:57:18 +00:00
case smartPortCommandClose:
result = smartPortNoError
2022-10-31 21:57:18 +00:00
case smartPortCommandStatus:
2022-10-24 21:09:06 +00:00
address := call.param16(2)
result = d.status(call.statusCode(), address)
2022-10-31 21:57:18 +00:00
case smartPortCommandControl:
data := call.paramData(2)
controlCode := call.param8(4)
result = d.control(data, controlCode)
2022-10-24 21:09:06 +00:00
2022-10-31 21:57:18 +00:00
case smartPortCommandRead:
2022-10-24 21:09:06 +00:00
address := call.param16(2)
len := call.param16(4)
pos := call.param24(6)
result = d.read(pos, len, address)
2022-10-24 21:09:06 +00:00
default:
// Prodos device command not supported
2022-10-31 21:57:18 +00:00
result = smartPortErrorIO
2022-10-24 21:09:06 +00:00
}
if d.trace {
2022-11-01 15:57:45 +00:00
fmt.Printf("[SmartPortFujinetNetwork] Command %v, return %s \n",
2022-10-24 21:09:06 +00:00
call, smartPortErrorMessage(result))
}
return result
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) read(pos uint32, length uint16, dest uint16) uint8 {
2022-10-24 21:09:06 +00:00
if d.trace {
2022-11-01 15:57:45 +00:00
fmt.Printf("[SmartPortFujinetNetwork] Read %v bytes from pos %v into $%x.\n",
length, pos, dest)
2022-10-24 21:09:06 +00:00
}
// Byte by byte transfer to memory using the full Poke code path
for i := uint16(0); i < uint16(len(d.data)) && i < length; i++ {
d.host.a.mmu.Poke(dest+i, d.data[i])
}
2022-10-24 21:09:06 +00:00
2022-10-31 21:57:18 +00:00
return smartPortNoError
2022-10-24 21:09:06 +00:00
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) control(data []uint8, code uint8) uint8 {
switch code {
case 'O':
// Open URL
method := data[0]
translation := data[1]
url := data[2:]
d.controlOpen(method, translation, string(url))
case 'P':
if d.jsonChannelMode {
d.controlJsonParse()
}
case 'Q':
if d.jsonChannelMode {
d.controlJsonQuery(data)
}
case 0xfc:
mode := data[0]
d.controlChannelMode(mode)
}
2022-10-31 21:57:18 +00:00
return smartPortNoError
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) controlJsonParse() {
// See FNJSON::parse()
2022-10-24 21:09:06 +00:00
if d.trace {
2022-11-01 15:57:45 +00:00
fmt.Printf("[SmartPortFujinetNetwork] control-parse()\n")
2022-10-24 21:09:06 +00:00
}
data, errorCode := d.protocol.ReadAll()
if errorCode != fujinet.NoError {
d.errorCode = errorCode
return
}
2022-10-24 21:09:06 +00:00
d.jsonData = fujinet.NewFnJson()
d.errorCode = d.jsonData.Parse(data)
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) controlJsonQuery(query []uint8) {
if d.trace {
2022-11-01 15:57:45 +00:00
fmt.Printf("[SmartPortFujinetNetwork] control-query('%s')\n", query)
}
if d.jsonData != nil {
d.jsonData.Query(query)
d.data = d.jsonData.Result
}
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) controlChannelMode(mode uint8) {
// See iwmNetwork::channel_mode()
if d.trace {
fmt.Printf("control-channel-mode(%v)\n", mode)
}
if mode == 0 {
d.jsonChannelMode = false
} else if mode == 1 {
d.jsonChannelMode = true
}
// The rest of the cases do not change the mode
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) controlOpen(method uint8, translation uint8, rawUrl string) {
// See iwmNetwork::open()
if d.trace {
2022-11-01 15:57:45 +00:00
fmt.Printf("[SmartPortFujinetNetwork] control-open(%v, %v, '%s'\n", method, translation, rawUrl)
}
if d.protocol != nil {
d.protocol.Close()
d.protocol = nil
}
d.statusByte = 0
// Remove "N:" prefix
rawUrl = strings.TrimPrefix(rawUrl, "N:")
urlParsed, err := url.Parse(rawUrl)
if err != nil {
d.errorCode = fujinet.NetworkErrorInvalidDeviceSpec
d.statusByte = 4 //client_error
}
d.protocol, d.errorCode = fujinet.InstantiateProtocol(urlParsed, method)
if d.protocol == nil {
d.statusByte = 4 //client_error
return
}
d.protocol.Open(urlParsed)
d.jsonChannelMode = false
2022-10-24 21:09:06 +00:00
}
2022-11-01 15:57:45 +00:00
func (d *SmartPortFujinetNetwork) status(code uint8, dest uint16) uint8 {
2022-10-24 21:09:06 +00:00
switch code {
2022-10-31 21:57:18 +00:00
case smartPortStatusCodeDevice:
2022-10-24 21:09:06 +00:00
// See iwmNetwork::encode_status_reply_packet()
d.host.a.mmu.pokeRange(dest, []uint8{
2022-10-31 21:57:18 +00:00
smartPortStatusCodeTypeRead & smartPortStatusCodeTypeOnline,
0, 0, 0, // Block size is 0
})
2022-10-24 21:09:06 +00:00
2022-10-31 21:57:18 +00:00
case smartPortStatusCodeDeviceInfo:
2022-10-24 21:09:06 +00:00
// See iwmNetwork::encode_status_reply_packet()
d.host.a.mmu.pokeRange(dest, []uint8{
2022-10-31 21:57:18 +00:00
smartPortStatusCodeTypeRead & smartPortStatusCodeTypeOnline,
0, 0, 0, // Block size is 0
7, 'N', 'E', 'T', 'W', 'O', 'R', 'K', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
0x02, // Type hard disk
0x00, // Subtype network (comment in network.cpp has 0x0a)
0x00, 0x01, // Firmware version
})
case 'R':
// Net read, do nothing
case 'S':
// Get connection status
len := len(d.data)
if d.jsonChannelMode {
// See FNJSON
errorCode := 0
if len == 0 {
errorCode = int(fujinet.NetworkErrorEndOfFile)
}
d.host.a.mmu.pokeRange(dest, []uint8{
uint8(len & 0xff),
uint8((len >> 8) & 0xff),
1, /*True*/
uint8(errorCode),
})
} else {
// TODO
d.host.a.mmu.pokeRange(dest, []uint8{
uint8(len & 0xff),
uint8((len >> 8) & 0xff),
1, // ?? d.connected,
uint8(d.errorCode),
})
}
2022-10-24 21:09:06 +00:00
}
2022-10-31 21:57:18 +00:00
return smartPortNoError // The return code is always success
2022-10-24 21:09:06 +00:00
}