Issuing SmartPort Commands
Once the SmartPort dispatcher is found, commands can be issued. The FujiNet can respond to STATUS, CONTROL, READ, and WRITE commands.
Status commands are used to ask for information from the FujiNet.
int8_t sp_status(uint8_t dest, uint8_t statcode)
{
sp_error = 0;
// build the command list
sp_cmdlist[0] = SP_STATUS_PARAM_COUNT;
sp_cmdlist[1] = dest; // set before calling sp_status();
sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF);
sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF;
sp_cmdlist[4] = statcode;
sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF);
sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF;
// store cmd list
__asm__ volatile ("lda #%b", SP_CMD_STATUS);
__asm__ volatile ("sta %g", spCmd); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_low);
__asm__ volatile ("sta %g", spCmdListLow); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_high);
__asm__ volatile ("sta %g", spCmdListHigh); // store status command #
__asm__ volatile ("jsr $C50D"); // to do - find SP entry point using algorithm from firmware reference
spCmd:
__asm__ volatile ("nop");
spCmdListLow:
__asm__ volatile ("nop");
spCmdListHigh:
__asm__ volatile ("nop");
__asm__ volatile ("stx %v", sp_rtn_low);
__asm__ volatile ("sty %v", sp_rtn_high);
__asm__ volatile ("sta %v", sp_err);
sp_count = ((uint16_t)sp_rtn_high << 8) | (uint16_t)sp_rtn_low;
sp_error = sp_err;
return sp_err;
}
STATUSCODE = $00
jsr DISPATCH
DFB #STATUSCODE
DW CMLIST
BCS ERROR
; Everything ok
CLC
RTS
ERROR ; not okay
RTS
CMLIST: DFB #$03 ; status has 3 params
DFB #DEST ; Destination device #
DW BUFFER ; lo and hi bytes for buffer address
DFB STATUSCODE ; The status code
CONTROL commands are typically imperative in nature, asking the FujiNet to perform an operation.
CTRLCODE = $04
jsr DISPATCH
DFB #CTRLCODE
DW CMLIST
BCS ERROR
; Everything ok
CLC
RTS
ERROR ; not okay
RTS
CMLIST: DFB #$03 ; status has length of 3
DFB #DEST ; Destination device #
DW BUFFER ; lo and hi bytes for buffer address
DFB CTRLCODE ; The status code
int8_t sp_control(uint8_t dest, uint8_t ctrlcode)
{
sp_error = 0;
// sp_dest = 5; // need to search
// build the command list
sp_cmdlist[0] = SP_CONTROL_PARAM_COUNT;
sp_cmdlist[1] = dest; // set before calling sp_status();
sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF);
sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF;
sp_cmdlist[4] = ctrlcode;
sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF);
sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF;
// store cmd list
__asm__ volatile ("lda #%b", SP_CMD_CONTROL);
__asm__ volatile ("sta %g", spCmd); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_low);
__asm__ volatile ("sta %g", spCmdListLow); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_high);
__asm__ volatile ("sta %g", spCmdListHigh); // store status command #
__asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address
spCmd:
__asm__ volatile ("nop");
spCmdListLow:
__asm__ volatile ("nop");
spCmdListHigh:
__asm__ volatile ("nop");
__asm__ volatile ("sta %v", sp_err);
sp_error = sp_err;
return sp_err;
}
The READ command can be used, for example, with the Network Device, to read a number of bytes that are waiting in the receive buffer, and send them to the computer.
The main difference between this command (as well as the proceeding WRITE command) and the STATUS/CONTROL commands is that a buffer length is passed as part of the command list.
READCODE = $08
jsr DISPATCH
DFB #READCODE
DW CMLIST
BCS ERROR
; Everything ok
CLC
RTS
ERROR ; not okay
RTS
CMLIST: DFB #$04 ; status has 4 params
DFB #DEST ; Destination device #
DW BUFFER ; lo and hi bytes for buffer address
DW LEN ; # of bytes to read into buffer
int8_t sp_read(uint8_t dest, uint16_t len)
{
sp_error = 0;
// sp_dest = 5; // need to search
// build the command list
sp_cmdlist[0] = SP_READ_PARAM_COUNT;
sp_cmdlist[1] = dest; // set before calling sp_status();
sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF);
sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF;
sp_cmdlist[4] = len & 0xFF;
sp_cmdlist[5] = len >> 8;
sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF);
sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF;
// store cmd list
__asm__ volatile ("lda #%b", SP_CMD_READ);
__asm__ volatile ("sta %g", spCmd); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_low);
__asm__ volatile ("sta %g", spCmdListLow); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_high);
__asm__ volatile ("sta %g", spCmdListHigh); // store status command #
__asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address
spCmd:
__asm__ volatile ("nop");
spCmdListLow:
__asm__ volatile ("nop");
spCmdListHigh:
__asm__ volatile ("nop");
__asm__ volatile ("sta %v", sp_err);
sp_error = sp_err;
return sp_err;
}
The WRITE command can be used, for example, with the Network Device, to write a number of bytes to a remote network host (by placing them in the transmit buffer)
The main difference between this command (as well as the preceeding READ command) and the STATUS/CONTROL commands is that a buffer length is passed as part of the command list.
WRITECODE = $09
jsr DISPATCH
DFB #WRITECODE
DW CMLIST
BCS ERROR
; Everything ok
CLC
RTS
ERROR ; not okay
RTS
CMLIST: DFB #$04 ; write has 4 params
DFB #DEST ; Destination device #
DW BUFFER ; lo and hi bytes for buffer address
DW LEN ; # of bytes to read into buffer
int8_t sp_write(uint8_t dest, uint16_t len)
{
sp_error = 0;
// sp_dest = 5; // need to search
// build the command list
sp_cmdlist[0] = SP_WRITE_PARAM_COUNT;
sp_cmdlist[1] = dest; // set before calling sp_status();
sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF);
sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF;
sp_cmdlist[4] = len & 0xFF;
sp_cmdlist[5] = len >> 8;
sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF);
sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF;
// store cmd list
__asm__ volatile ("lda #%b", SP_CMD_WRITE);
__asm__ volatile ("sta %g", spCmd); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_low);
__asm__ volatile ("sta %g", spCmdListLow); // store status command #
__asm__ volatile ("lda %v", sp_cmdlist_high);
__asm__ volatile ("sta %g", spCmdListHigh); // store status command #
__asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address
spCmd:
__asm__ volatile ("nop");
spCmdListLow:
__asm__ volatile ("nop");
spCmdListHigh:
__asm__ volatile ("nop");
__asm__ volatile ("sta %v", sp_err);
sp_error = sp_err;
return sp_err;
}
You can see all of these functions as part of the 'netcat' program: https://github.com/FujiNetWIFI/fujinet-apps/tree/master/netcat/apple2
You can see all of these functions as part of the 'apple ampersand' program: https://github.com/FujiNetWIFI/fujinet-nhandler/tree/master/apple2
Now that you've seen how to issue various SmartPort commands, the following sections will show a table reference of commands:
Copyright 2023 Contributors to the FujiNetWIFI project.
Join us on Discord: https://discord.gg/7MfFTvD
- Home
- What is FujiNet?
- The Definition of Done
- Board bring up for FujiNet Platform.IO code
- The Complete Linux cli-only install and setup guide
- Development Env for Apps
- FujiNet-Development-Guidelines
- System Quickstarts
- FujiNet Flasher
- FujiNet Configuration File: fnconfig.ini
- AppKey Registry - SIO Command $DC Open App Key
- CP-M Support
- BBS
- Official Hardware Versions
- Prototype Board Revisions
- FujiNet Development Guidelines
- Atari Programming
- Apple Programming
- C64 Programming
- ADAM Programming
- Testing Plan
- Hacker List
- FujiNet VirtualMachine