From 33ea1f6e7aa7651c82353b22b6b123215203f17e Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Mon, 31 Oct 2022 22:47:53 +0100 Subject: [PATCH] Basic Fujinet network support, iis-tracker works --- cardSmartport.go | 2 +- fujinet/json.go | 116 ++++++++++++++++++++++ fujinet/json_test.go | 128 ++++++++++++++++++++++++ fujinet/protocol.go | 39 ++++++++ fujinet/protocolHttp.go | 48 +++++++++ memoryManager.go | 10 +- smartPortCall.go | 59 +++++++++-- smartPortFujinet.go | 214 +++++++++++++++++++++++++++++++--------- smartPortHardDisk.go | 4 +- 9 files changed, 561 insertions(+), 59 deletions(-) create mode 100644 fujinet/json.go create mode 100644 fujinet/json_test.go create mode 100644 fujinet/protocol.go create mode 100644 fujinet/protocolHttp.go diff --git a/cardSmartport.go b/cardSmartport.go index c9bc32c..cdebf88 100644 --- a/cardSmartport.go +++ b/cardSmartport.go @@ -75,7 +75,7 @@ func (c *CardSmartPort) assign(a *Apple2, slot int) { a.mmu.Peek(0x44), a.mmu.Peek(0x45), // data address 0, }) - } else if command == proDosDeviceCommandRead || command == proDosDeviceCommandWrite { + } else if command == proDosDeviceCommandReadBlock || command == proDosDeviceCommandWriteBlock { call = newSmartPortCallSynthetic(c, command, []uint8{ 3, // 3args unit, diff --git a/fujinet/json.go b/fujinet/json.go new file mode 100644 index 0000000..bdff08c --- /dev/null +++ b/fujinet/json.go @@ -0,0 +1,116 @@ +package fujinet + +import ( + "encoding/json" + "strconv" + "strings" +) + +/* +See: + https://github.com/FujiNetWIFI/fujinet-platformio/tree/master/lib/fnjson + https://github.com/FujiNetWIFI/fujinet-platformio/wiki/JSON-Query-Format +*/ + +type FnJson struct { + data any + Result []uint8 +} + +func NewFnJson() *FnJson { + var js FnJson + return &js +} + +func (js *FnJson) Parse(data []uint8) ErrorCode { + // See FNJSON::parse() + err := json.Unmarshal(data, &js.data) + if err != nil { + return NetworkErrorJsonParseError + } + return NoError +} + +func (js *FnJson) Query(query []uint8) { + // See FNJSON::setReadQuery + // See https://github.com/kbranigan/cJSON/blob/master/cJSON_Utils.c + if query[len(query)-1] == 0 { + query = query[0 : len(query)-1] + } + queryString := string(query) + queryString = strings.TrimSuffix(queryString, "/0") + queryString = strings.TrimPrefix(queryString, "/") + queryString = strings.TrimSuffix(queryString, "/") + path := strings.Split(queryString, "/") + + js.Result = nil + current := js.data + for i := 0; i < len(path); i++ { + switch v := current.(type) { + case map[string]any: + var found bool + current, found = v[path[i]] + if !found { + // Not found + return + } + case []any: + index, err := strconv.Atoi(path[i]) + if err != nil { + // Path for arrays should be an int + return + } + if index < 0 || index >= len(v) { + // Index out of bounds + return + } + current = v[index] + default: + // It's a leaf. We can't go down + return + } + } + + js.Result = getJsonValue(current) +} + +func getJsonValue(data any) []uint8 { + // See FNJson::getValue + if data == nil { + return []uint8("NULL") + } + + switch v := data.(type) { + case bool: + if v { + return []uint8("TRUE") + } else { + return []uint8("FALSE") + } + case float64: + //if math.Floor(v) == v { // As done in FNJSON__getValue() + // It's an integer + return []uint8(strconv.Itoa(int(v))) + //} else { + // return []uint8(fmt.Sprintf("%.10f", v)) + //} + case string: + return []uint8(v) + case []any: + s := make([]uint8, 0) + for i := 0; i < len(v); i++ { + s = append(s, getJsonValue(v[i])...) + } + return s + case map[string]any: + s := make([]uint8, 0) + for k, e := range v { + s = append(s, []uint8(k)...) + s = append(s, getJsonValue(e)...) + } + return s + default: + // Should not be possible for an object unmarshalled from a JSON + return []uint8("UNKNOWN") + } +} diff --git a/fujinet/json_test.go b/fujinet/json_test.go new file mode 100644 index 0000000..baa1cea --- /dev/null +++ b/fujinet/json_test.go @@ -0,0 +1,128 @@ +package fujinet + +import ( + "testing" +) + +func testQuerys(t *testing.T, message string, queries [][]string) { + js := NewFnJson() + errorCode := js.Parse([]uint8(message)) + + if errorCode != NoError { + t.Fatalf("Parse error %v. It should be %v", errorCode, NoError) + } + + for _, pair := range queries { + js.Query([]uint8(pair[0])) + result := string(js.Result) + if result != pair[1] { + t.Errorf("Query for %s, returned %s. It should be %s", pair[0], result, pair[1]) + } + } +} + +func TestQueryMap(t *testing.T) { + // See https://github.com/FujiNetWIFI/fujinet-apps/blob/master/iss-tracker/apple2/src/satellite.c + testMessage := `{"timestamp": 1667218311, "message": "success", "iss_position": {"latitude": "21.3276", "longitude": "-39.4989"}}` + + testCases := [][]string{ + {"/iss_position/longitude", "-39.4989"}, + {"/iss_position/latitude", "21.3276"}, + {"/timestamp", "1667218311"}, + } + testQuerys(t, testMessage, testCases) +} + +// See https://github.com/FujiNetWIFI/fujinet-apps/blob/master/json-test/atari/jsontest.c +const testArrayMessage = ` +[ + { + "id": "108361296757279278", + "created_at": "2022-05-25T07:00:06.000Z", + "in_reply_to_id": null, + "in_reply_to_account_id": null, + "sensitive": false, + "spoiler_text": "", + "visibility": "public", + "language": "en", + "uri": "https://botsin.space/users/osxthemes/statuses/108361286061805267", + "url": "https://botsin.space/@osxthemes/108361286061805267", + "replies_count": 0, + "reblogs_count": 0, + "favourites_count": 0, + "edited_at": null, + "local_only": null, + "content": "

Floppies! - Robert Davis

", + "reblog": null, + "account": { + "id": "23439", + "username": "osxthemes", + "acct": "osxthemes@botsin.space", + "display_name": "Macintosh Themes", + "locked": false, + "bot": true, + "discoverable": true, + "group": false, + "created_at": "2018-03-28T00:00:00.000Z", + "note": "

I tweet Mac OSX (pre-10.5) and Kaleidoscope (Classic) themes. Bot by @Eramdam, inspired by kaleidoscopemac@twitter.com. Also on Twitter at https://twitter.com/osxthemes

", + "url": "https://botsin.space/@osxthemes", + "avatar": "https://assets.oldbytes.space/assets.oldbytes.space/accounts/avatars/000/023/439/original/322ac0c621b55624.png", + "avatar_static": "https://assets.oldbytes.space/assets.oldbytes.space/accounts/avatars/000/023/439/original/322ac0c621b55624.png", + "header": "https://assets.oldbytes.space/assets.oldbytes.space/cache/accounts/headers/000/023/439/original/ea0e0cd513b5a9f7.png", + "header_static": "https://assets.oldbytes.space/assets.oldbytes.space/cache/accounts/headers/000/023/439/original/ea0e0cd513b5a9f7.png", + "followers_count": 157, + "following_count": 1, + "statuses_count": 17615, + "last_status_at": "2022-05-25", + "emojis": [], + "fields": [] + }, + "media_attachments": [ + { + "id": "108361296738754794", + "type": "image", + "url": "https://assets.oldbytes.space/assets.oldbytes.space/cache/media_attachments/files/108/361/296/738/754/794/original/5785ab0a51d0db1f.gif", + "preview_url": "https://assets.oldbytes.space/assets.oldbytes.space/cache/media_attachments/files/108/361/296/738/754/794/small/5785ab0a51d0db1f.png", + "remote_url": "https://files.botsin.space/media_attachments/files/108/361/285/793/211/606/original/7fe52f343cf0c99a.gif", + "preview_remote_url": null, + "text_url": null, + "meta": { + "original": { + "width": 213, + "height": 181, + "size": "213x181", + "aspect": 1.1767955801104972 + }, + "small": { + "width": 213, + "height": 181, + "size": "213x181", + "aspect": 1.1767955801104972 + } + }, + "description": "Floppies! - Robert Davis", + "blurhash": "UbLNcMO@QkAAx{jJX4V@8yX9xYX7D@kXoZkV" + } + ], + "mentions": [], + "tags": [], + "emojis": [], + "card": null, + "poll": null + } +]` + +func TestQueryArray(t *testing.T) { + + testCases := [][]string{ + {"/0/account/display_name", "Macintosh Themes"}, + {"/0/created_at", "2022-05-25T07:00:06.000Z"}, + {"/0/content", "

Floppies! - Robert Davis

"}, + {"/0/nonexistent", "NULL"}, + {"/1/account/display_name", "NULL"}, + {"/-1/account/display_name", "NULL"}, + {"/zz/account/display_name", "NULL"}, + {"/0/media_attachments/0/meta/original", "width213height181size213x181aspect1.1767955801"}, + } + testQuerys(t, testArrayMessage, testCases) +} diff --git a/fujinet/protocol.go b/fujinet/protocol.go new file mode 100644 index 0000000..a74ea14 --- /dev/null +++ b/fujinet/protocol.go @@ -0,0 +1,39 @@ +package fujinet + +import ( + "net/url" + "strings" +) + +type Protocol interface { + Open(urlParsed *url.URL) + Close() + ReadAll() ([]uint8, ErrorCode) + Write(data []uint8) error +} + +type ErrorCode uint8 + +const ( + // See fujinet-platformio/lib/network-protocol/status_error_codes.h + NoError = ErrorCode(0) + NetworkErrorEndOfFile = ErrorCode(136) + NetworkErrorGeneral = ErrorCode(144) + NetworkErrorNotImplemented = ErrorCode(146) + NetworkErrorInvalidDeviceSpec = ErrorCode(165) + + // New + NetworkErrorJsonParseError = ErrorCode(250) +) + +func InstantiateProtocol(urlParsed *url.URL, method uint8) (Protocol, ErrorCode) { + scheme := strings.ToUpper(urlParsed.Scheme) + switch scheme { + case "HTTP": + return newProtocolHttp(method), NoError + case "HTTPS": + return newProtocolHttp(method), NoError + default: + return nil, NetworkErrorGeneral + } +} diff --git a/fujinet/protocolHttp.go b/fujinet/protocolHttp.go new file mode 100644 index 0000000..6daaad0 --- /dev/null +++ b/fujinet/protocolHttp.go @@ -0,0 +1,48 @@ +package fujinet + +import ( + "io" + "net/http" + "net/url" +) + +type protocolHttp struct { + method uint8 + url *url.URL +} + +func newProtocolHttp(method uint8) *protocolHttp { + var p protocolHttp + p.method = method + return &p +} + +func (p *protocolHttp) Open(urlParsed *url.URL) { + p.url = urlParsed +} + +func (p *protocolHttp) Close() { + +} + +func (p *protocolHttp) ReadAll() ([]uint8, ErrorCode) { + if p.method == 12 /*GET*/ { + resp, err := http.Get(p.url.String()) + if err != nil { + return nil, NetworkErrorGeneral + } + defer resp.Body.Close() + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, NetworkErrorGeneral + } + return data, NoError + } + + return nil, NetworkErrorNotImplemented +} + +func (p *protocolHttp) Write(data []uint8) error { + return nil +} diff --git a/memoryManager.go b/memoryManager.go index 50142f6..f40a63f 100644 --- a/memoryManager.go +++ b/memoryManager.go @@ -227,10 +227,6 @@ func (mmu *memoryManager) accessWrite(address uint16) memoryHandler { func (mmu *memoryManager) peekWord(address uint16) uint16 { return uint16(mmu.Peek(address)) + uint16(mmu.Peek(address+1))<<8 - - //return uint16(mmu.Peek(address)) + - // 0x100*uint16(mmu.Peek(address+1)) - } // Peek returns the data on the given address @@ -274,6 +270,12 @@ func (mmu *memoryManager) PeekCode(address uint16) uint8 { return value } +func (mmu *memoryManager) pokeRange(address uint16, data []uint8) { + for i := 0; i < len(data); i++ { + mmu.Poke(address+uint16(i), data[i]) + } +} + // Poke sets the data at the given address func (mmu *memoryManager) Poke(address uint16, value uint8) { mh := mmu.accessWrite(address) diff --git a/smartPortCall.go b/smartPortCall.go index 0843821..83d003d 100644 --- a/smartPortCall.go +++ b/smartPortCall.go @@ -11,10 +11,16 @@ type smartPortDevice interface { } const ( - proDosDeviceCommandStatus = 0 - proDosDeviceCommandRead = 1 - proDosDeviceCommandWrite = 2 - proDosDeviceCommandFormat = 3 + proDosDeviceCommandStatus = 0 + proDosDeviceCommandReadBlock = 1 + proDosDeviceCommandWriteBlock = 2 + proDosDeviceCommandFormat = 3 + proDosDeviceCommandControl = 4 + proDosDeviceCommandInit = 5 + proDosDeviceCommandOpen = 6 + proDosDeviceCommandClose = 7 + proDosDeviceCommandRead = 8 + proDosDeviceCommandWrite = 9 ) const ( @@ -103,20 +109,57 @@ func (spc *smartPortCall) param24(offset uint8) uint32 { uint32(spc.param8(offset+2))<<16 } +func (spc *smartPortCall) paramData(offset uint8) []uint8 { + address := uint16(spc.param8(offset)) + + uint16(spc.param8(offset+1))<<8 + + size := spc.host.a.mmu.peekWord(address) + + data := make([]uint8, size) + for i := 0; i < int(size); i++ { + data[i] = spc.host.a.mmu.Peek(address + 2 + uint16(i)) + } + + return data +} + 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 proDosDeviceCommandReadBlock: + return fmt.Sprintf("READBLOCK(%v, unit=%v, block=%v)", + spc.command, spc.unit(), + spc.param24(4)) + case proDosDeviceCommandWriteBlock: + return fmt.Sprintf("WRITEBLOCK(%v, unit=%v, block=%v)", + spc.command, spc.unit(), + spc.param24(4)) + case proDosDeviceCommandControl: + return fmt.Sprintf("CONTROL(%v, unit=%v, code=%v)", + spc.command, spc.unit(), + spc.param8(4)) + case proDosDeviceCommandInit: + return fmt.Sprintf("INIT(%v, unit=%v)", + spc.command, spc.unit()) + case proDosDeviceCommandOpen: + return fmt.Sprintf("OPEN(%v, unit=%v)", + spc.command, spc.unit()) + case proDosDeviceCommandClose: + return fmt.Sprintf("CLOSE(%v, unit=%v)", + spc.command, spc.unit()) case proDosDeviceCommandRead: - return fmt.Sprintf("READ(%v, unit=%v, block=%v)", + return fmt.Sprintf("READ(%v, unit=%v, pos=%v, len=%v)", spc.command, spc.unit(), - spc.param24(4)) + spc.param24(6), + spc.param16(4)) case proDosDeviceCommandWrite: - return fmt.Sprintf("WRITE(%v, unit=%v, block=%v)", + return fmt.Sprintf("WRITE(%v, unit=%v, pos=%v, len=%v)", spc.command, spc.unit(), - spc.param24(4)) + spc.param24(6), + spc.param16(4)) default: return fmt.Sprintf("UNKNOWN(%v, unit=%v)", diff --git a/smartPortFujinet.go b/smartPortFujinet.go index 59ed523..7db2365 100644 --- a/smartPortFujinet.go +++ b/smartPortFujinet.go @@ -2,6 +2,10 @@ package izapple2 import ( "fmt" + "net/url" + "strings" + + "github.com/ivanizag/izapple2/fujinet" ) /* @@ -17,12 +21,22 @@ See: type SmartPortFujinet struct { host *CardSmartPort // For DMA trace bool + + protocol fujinet.Protocol + jsonChannelMode bool + statusByte uint8 + errorCode fujinet.ErrorCode + + jsonData *fujinet.FnJson + data []uint8 + //connected uint8 } // NewSmartPortFujinet creates a new fujinet device func NewSmartPortFujinet(host *CardSmartPort) *SmartPortFujinet { var d SmartPortFujinet d.host = host + d.errorCode = fujinet.NoError return &d } @@ -31,19 +45,27 @@ func (d *SmartPortFujinet) exec(call *smartPortCall) uint8 { var result uint8 switch call.command { + + case proDosDeviceCommandOpen: + result = proDosDeviceNoError + + case proDosDeviceCommandClose: + result = proDosDeviceNoError + case proDosDeviceCommandStatus: address := call.param16(2) result = d.status(call.statusCode(), address) + case proDosDeviceCommandControl: + data := call.paramData(2) + controlCode := call.param8(4) + result = d.control(data, controlCode) + 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) + len := call.param16(4) + pos := call.param24(6) + result = d.read(pos, len, address) default: // Prodos device command not supported @@ -58,63 +80,167 @@ func (d *SmartPortFujinet) exec(call *smartPortCall) uint8 { return result } -func (d *SmartPortFujinet) readBlock(block uint32, dest uint16) uint8 { +func (d *SmartPortFujinet) read(pos uint32, length uint16, dest uint16) uint8 { if d.trace { - fmt.Printf("[SmartPortFujinet] Read block %v into $%x.\n", block, dest) + fmt.Printf("[SmartPortFujinet] Read %v bytes from pos %v into $%x.\n", + length, pos, dest) } - // TODO + // 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]) + } 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) +func (d *SmartPortFujinet) 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) } - // TODO - return proDosDeviceNoError } +func (d *SmartPortFujinet) controlJsonParse() { + // See FNJSON::parse() + if d.trace { + fmt.Printf("[SmartPortFujinet] control-parse()\n") + } + + data, errorCode := d.protocol.ReadAll() + if errorCode != fujinet.NoError { + d.errorCode = errorCode + return + } + + d.jsonData = fujinet.NewFnJson() + d.errorCode = d.jsonData.Parse(data) +} + +func (d *SmartPortFujinet) controlJsonQuery(query []uint8) { + if d.trace { + fmt.Printf("[SmartPortFujinet] control-query('%s')\n", query) + } + + if d.jsonData != nil { + d.jsonData.Query(query) + d.data = d.jsonData.Result + } +} + +func (d *SmartPortFujinet) 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 +} + +func (d *SmartPortFujinet) controlOpen(method uint8, translation uint8, rawUrl string) { + // See iwmNetwork::open() + if d.trace { + fmt.Printf("[SmartPortFujinet] 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 +} + 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 + d.host.a.mmu.pokeRange(dest, []uint8{ + prodosDeviceStatusCodeTypeRead & prodosDeviceStatusCodeTypeOnline, + 0, 0, 0, // 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 + d.host.a.mmu.pokeRange(dest, []uint8{ + prodosDeviceStatusCodeTypeRead & prodosDeviceStatusCodeTypeOnline, + 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), + }) + } } return proDosDeviceNoError // The return code is always success diff --git a/smartPortHardDisk.go b/smartPortHardDisk.go index 5759c36..97f9220 100644 --- a/smartPortHardDisk.go +++ b/smartPortHardDisk.go @@ -46,12 +46,12 @@ func (d *SmartPortHardDisk) exec(call *smartPortCall) uint8 { address := call.param16(2) result = d.status(address) - case proDosDeviceCommandRead: + case proDosDeviceCommandReadBlock: address := call.param16(2) block := call.param24(4) result = d.readBlock(block, address) - case proDosDeviceCommandWrite: + case proDosDeviceCommandWriteBlock: address := call.param16(2) block := call.param24(4) result = d.writeBlock(block, address)