2022-10-24 21:09:06 +00:00
|
|
|
package izapple2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-10-31 21:47:53 +00:00
|
|
|
"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
|
2022-10-31 21:47:53 +00:00
|
|
|
|
|
|
|
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
|
2022-10-31 21:47:53 +00:00
|
|
|
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:47:53 +00:00
|
|
|
|
2022-10-31 21:57:18 +00:00
|
|
|
case smartPortCommandOpen:
|
|
|
|
result = smartPortNoError
|
2022-10-31 21:47:53 +00:00
|
|
|
|
2022-10-31 21:57:18 +00:00
|
|
|
case smartPortCommandClose:
|
|
|
|
result = smartPortNoError
|
2022-10-31 21:47:53 +00:00
|
|
|
|
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:
|
2022-10-31 21:47:53 +00:00
|
|
|
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)
|
2022-10-31 21:47:53 +00:00
|
|
|
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",
|
2022-10-31 21:47:53 +00:00
|
|
|
length, pos, dest)
|
2022-10-24 21:09:06 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 21:47:53 +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 {
|
2022-10-31 21:47:53 +00:00
|
|
|
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-10-31 21:47:53 +00:00
|
|
|
}
|
|
|
|
|
2022-11-01 15:57:45 +00:00
|
|
|
func (d *SmartPortFujinetNetwork) controlJsonParse() {
|
2022-10-31 21:47:53 +00:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2022-10-31 21:47:53 +00:00
|
|
|
data, errorCode := d.protocol.ReadAll()
|
|
|
|
if errorCode != fujinet.NoError {
|
|
|
|
d.errorCode = errorCode
|
|
|
|
return
|
|
|
|
}
|
2022-10-24 21:09:06 +00:00
|
|
|
|
2022-10-31 21:47:53 +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) {
|
2022-10-31 21:47:53 +00:00
|
|
|
if d.trace {
|
2022-11-01 15:57:45 +00:00
|
|
|
fmt.Printf("[SmartPortFujinetNetwork] control-query('%s')\n", query)
|
2022-10-31 21:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2022-10-31 21:47:53 +00:00
|
|
|
// 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) {
|
2022-10-31 21:47:53 +00:00
|
|
|
// 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)
|
2022-10-31 21:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2022-10-31 21:47:53 +00:00
|
|
|
d.host.a.mmu.pokeRange(dest, []uint8{
|
2022-10-31 21:57:18 +00:00
|
|
|
smartPortStatusCodeTypeRead & smartPortStatusCodeTypeOnline,
|
2022-10-31 21:47:53 +00:00
|
|
|
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()
|
2022-10-31 21:47:53 +00:00
|
|
|
d.host.a.mmu.pokeRange(dest, []uint8{
|
2022-10-31 21:57:18 +00:00
|
|
|
smartPortStatusCodeTypeRead & smartPortStatusCodeTypeOnline,
|
2022-10-31 21:47:53 +00:00
|
|
|
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
|
|
|
}
|