Issuing SmartPort Commands - FujiNetWIFI/fujinet-firmware GitHub Wiki
Once the SmartPort dispatcher is found, commands can be issued. The FujiNet can respond to STATUS, CONTROL, READ, and WRITE commands.
STATUS
Status commands are used to ask for information from the FujiNet.
C implementation (CC65)
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;
}
Asm implementation
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
CONTROL commands are typically imperative in nature, asking the FujiNet to perform an operation.
Asm Implementation
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
C (CC65) implementation
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;
}
READ
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 following WRITE command) and the STATUS/CONTROL commands is that a buffer length is passed as part of the command list.
Asm Implementation
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
C Implementation
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;
}
WRITE
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.
Asm Implementation
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
C Implementation
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;
}
SmartPort functions In a C context
You can see all of these functions as part of the 'netcat' program: https://github.com/FujiNetWIFI/fujinet-apps/tree/master/netcat/apple2
SmartPort functions In an Assembly context
You can see all of these functions as part of the 'apple ampersand' program: https://github.com/FujiNetWIFI/fujinet-nhandler/tree/master/apple2
What commands?
Now that you've seen how to issue various SmartPort commands, the following sections will show a table reference of commands: