Сравнение моего SprinterNet API со Spectranet API (по архиву
https://web.archive.org/web/20200618075948/http://spectrum.alioth.net/doc/index.php/Software) - как оказалось они очень похожи:
Shaos wrote:
#D003 - getconf (возврат IP-конфигурации путём копирования 32 байт с адреса HL и 6 байт мака с адреса DE) - возвращает конфигурацию в том виде, как она сохранена в EEPROM (имя пользователя, IP-адрес, маска, гейтвей, 3 экстра байта для DHCP и 1 байт таймзона) + MAC;
https://web.archive.org/web/20120414225619/http://spectrum.alioth.net/doc/index.php/Ifconfig_inet
Code: Select all
; get_ifconfig_inet (HLCALL 0x3E6F) - read the internet protocol address
; get_ifconfig_netmask (HLCALL 0x3E72) - read the netmask
; get_ifconfig_gw (HLCALL 0x3E75) - read the default gateway
ld de, inet_addr
ld hl, GET_IFCONFIG_INET
call HLCALL
ld de, inet_netmask
ld hl, GET_IFCONFIG_NETMASK
call HLCALL
ld de, inet_gw
ld hl, GET_IFCONFIG_GW
call HLCALL
; he get_ifconfig_inet(), get_ifconfig_netmask() and get_ifconfig_gw() functions fill a 4 byte buffer with
; the current IP address, netmask and gateway respectively, in big endian form.
Сишный интерфейс:
Code: Select all
#include <sys/types.h>
#include <spectranet.h>
void get_ifconfig_inet(in_addr_t *inet_addr);
void get_ifconfig_netmask(in_addr_t *inet_netmask);
void get_ifconfig_gw(in_addr_t *inet_gw);
У меня все настройки забираются в один присест плюс ещё будет возможность забрать настройки в человеческом формате через псевдопеременные.
Shaos wrote:
#D00C - setip (изменение IP-конфигурации с адреса HL) -> A - запись в EEPROM IP-адреса, маски и гейтвея (если IP-адрес нулевой, то в недалёком будущем оно будет в рамках локальной сети делать DHCP запрос) и возвращает код ошибки (0 если всё Ок)- изменение конфигурации повлечёт за собой ре-инициализацию визнета;
https://web.archive.org/web/20120414225619/http://spectrum.alioth.net/doc/index.php/Ifconfig_inet
Code: Select all
; get_ifconfig_inet (HLCALL 0x3E6F) - read the internet protocol address
; get_ifconfig_netmask (HLCALL 0x3E72) - read the netmask
; get_ifconfig_gw (HLCALL 0x3E75) - read the default gateway
; deconfig (HLCALL 0x3E57) - Deconfigure all internet address settings
; all the functions use 4 byte buffers for the
; big endian address (inet_addr_t)
ld hl, inet_addr
ld ix, IFCONFIG_INET
call IXCALL
ld hl, inet_netmask
ld ix, IFCONFIG_NETMASK
call IXCALL
ld hl, inet_gw
ld ix, IFCONFIG_GW
call IXCALL
ld hl, DECONFIG
call HLCALL
; The ifconfig_inet(), ifconfig_netmask() and ifconfig_gw() functions set the interface configuration for internet addresses.
; The functions take a pointer to a 4 byte big endian internet address, netmask or gateway, defined as in_addr_t.
; The functions make an immediate change to the relevant interface configuration, however, the change is not permanent
; and will be lost when the power is cycled or if the built in DHCP client runs on reset. Permanent changes must be made
; in the configuration stored in flash.
; The deconfig() function resets the IP address, default gateway and netmask with immediate effect. The change is not permament.
Как можно видеть у спектранета эти настройки временные - для постоянных надо перезаписывать флеш. У меня же изменение конфигурации делается одним вызовом и это сразу меняет EEPROM и переинициализирует WizNet.
Сишный интерфейс в Спектранете:
Code: Select all
#include <sys/types.h>
#include <spectranet.h>
void ifconfig_inet(in_addr_t *inet_addr);
void ifconfig_netmask(in_addr_t *inet_netmask);
void ifconfig_gw(in_addr_t *inet_gw);
void deconfig();
Плюс в Спектранете есть функции для чтения и записи MAC-адреса, что в случае SprinterNet ненужно (и даже вредно):
https://web.archive.org/web/20120212192405/http://spectrum.alioth.net/doc/index.php/Sethwaddr
Code: Select all
sethwaddr (IXCALL 0x3E51) - set the MAC address
gethwaddr (HLCALL 0x3E54) - get the MAC address
(в спектранете функция записи мак-адреса также является временной - т.е. лишь меняет мак-адрес в ОЗУ)
Shaos wrote:
#D00F - socket (D=domain, E=type) -> A - возвращает следующий свободный сокет (если тип сокета AF_INET или AF_PACKET, то сокетов может быть максимум 3, а если AF_LOCAL, то скажем 16);
https://web.archive.org/web/20200217222129/http://spectrum.alioth.net/doc/index.php/Socket
Code: Select all
ld c, SOCK_STREAM ; A reliable stream - TCP
ld hl, SOCKET ; Call table for socket() - 0x3E00
call HLCALL ; Trigger ROM page in
bit 7, a ; Check for a negative return code (MSB set)
jr nz, .handle_error ; failed to allocate a socket if the return code < 0
; at this point, A contains the file descriptor (aka socket handle)
Сишный интерфейс (на самом деле проверка на успешность должна выглядеть как if(sock >= 0) т.к. 0 это валидный номер сокета):
Code: Select all
int sock;
sock=socket(AF_INET, SOCK_STREAM, 0);
if(sock > 0)
{
/* socket opened successfully, do something */
}
else
{
/* handle the error */
}
// The socket function opens an end point for communication, and returns a file descriptor on success.
// Only AF_INET sockets are supported. In the C interface, the domain and protocol parameters are
// provided for compatibility with the BSD socket library and are ignored. The protocol parameter
// should be set to zero; it is very possible that the protocol parameter will be used in a later release of the library.
У меня ещё предполагалось возможность использования AF_LOCAL (UNIX-сокеты внутри машины) и AF_PACKET (RAW-сокеты):
Code: Select all
int sock_r;
sock_r=socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sock_r<0)
{
printf("error in socket\n");
return -1;
}
Shaos wrote:
#D012 - bind (A=sock, HL=my_addr, E=addrlen) -> A - устанавливает порт для сокета (вызывает функцию визнета OPEN для открытия серверного сокета) и вызывает код ошибки (0 если всё Ок);
https://web.archive.org/web/20120212191405/http://spectrum.alioth.net/doc/index.php/Bind
Code: Select all
ld a, (socket_fd) ; The socket handle, as returned by socket
ld de, 2000 ; Port number
ld hl, BIND ; Jump table entry point - 0x3E0C
call HLCALL
jr c, .handle_error ; On error, carry flag is set
; bind() gives the socket sockfd the local address my_addr. In this implementation, only the port address is used
; for the local address, and other parameters in the sockaddr structure are currently ignored. Fields are provided for compatibility.
; It is normally necessary to assign a local address before a SOCK_STREAM can accept incoming connections.
; The port number passed to bind is the port that will be listened on when the listen call is made.
; Return value
; On success, the assembly language interface returns with the carry flag cleared.
; On error, the accumulator contains the error number and the carry flag is set.
Сишный интерфейс:
Code: Select all
#include <sys/socket.h>
#include <sys/types.h>
int sockfd;
struct sockaddr_in addr;
sock=socket(AF_INET, SOCK_STREAM, 0);
if(sock > 0)
{
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY); /* provided for compatibility */
addr.sin_port = htons(2000); /* htons is a no-op, but a macro is provided for compatibility */
if(bind(sockfd, &addr, sizeof(addr)) < 0)
{
handle_error("bind");
}
}
// On success, the C interface returns zero. On error, -1 is returned, and errno is set to the error number.
// The port number is the only local address that is used, since the hardware only supports a single
// internet protocol address; in the C interface, all other fields in the sockaddr structure are ignored.
// C programmers should still avoid invalid values in case these values are used in the future.
// It is acceptable to zero out unused values.
Shaos wrote:
#D015 - listen (A=sock, E=backlog) -> A - слушает входящие сообщения серверного TCP сокета (backlog в данный момент может принимать значения от 0 до 2 - вызывает функцию визнета LISTEN если сокет один либо OPEN/LISTEN несколько раз, если требуемых сокетов больше чем 1) и возвращает код ошибки (0 если всё Ок);
Shaos wrote:
#D018 - accept (A=sock, HL=in_addr, E=addrlen) - в регистре A возвращает сокет, к которому подцепился TCP клиент (функция не имеет аналога в визнете и будет реализована программно - возвращаемый сокет может совпадать или не совпадать с входящим);
Shaos wrote:
#D01B - shutdown (A=sock, E=param) - закрывает соединение правильным образом с серверной стороны в случае TCP (вызывает функцию визнета DISCON);
Shaos wrote:
#D01E - connect (A=sock, HL=their_addr, E=addrlen) -> A - коннектится к серверу по TCP со стороны клиента, используя параметры адреса (вызывает функции визнета OPEN и CONNECT), возвращает код ошибки (0 если всё Ок);
Shaos wrote:
#D021 - send (A=sock, HL=buf, DE=size, NO flags) -> BC - послать данные по TCP и вернуть количество переданных байт (вызывает функцию визнета SEND);
Shaos wrote:
#D024 - recv (A=sock, HL=buf, DE=size, NO flags) -> BC - получить данные по TCP и вернуть количество принятых байт (вызывает функцию визнета RECV);
Shaos wrote:
#D027 - sendto (A=sock, HL=buf, DE=size, NO flags, IY=out_addr) -> BC - послать данные по UDP и вернуть количество принятых байт (устанавливает адрес и порт удалённого конца и вызывает функцию визнета SEND);
Shaos wrote:
#D02A - recvfrom (A=sock, HL=buf, DE=size, NO flags, IY=in_addr) -> BC - получить данные по UDP и вернуть количество принятых байт (вызывает функцию визнета RECV и копирует адрес и порт с которых пришло сообщение в in_addr);
Shaos wrote:
#D02D - close (A=sock) - закрывает сокет (вызывает функцию визнета CLOSE, однако в случае серверных сокетов логика будет более запутанная, т.к. закрываемый сокет надо будет снова переиспользовать в accept);
Shaos wrote:
#D030 - httpget (IY=url, HL=buf, DE=maxsize) -> BC - прочитать данные из интернета по урлу в буфер ограниченного размера (в BC возвращается количество скопированных байт либо код ответа HTTP если был установлен флаг C - причём 0 будет обозначать ошибку соединения);
В спектранете аналогичной функции (самодостаточный HTTP-клиент для забирания небольших порций данных с произвольного HTTP-сервера) попросту нет.
Shaos wrote:
#D033 - resolv (HL=host) -> BCDE - выдает IP-адрес в регистрах по имени сайта, расположенному по адресу HL и заканчивающемся нулевым байтом (в первой версии IP-адрес будет искаться в записях hosts и если там не найдено или просрочилось - будет производится запрос к шлюзу, а в будущем может быть реализован честный DNS-запрос);
https://web.archive.org/web/20120212111230/http://spectrum.alioth.net/doc/index.php/Gethostbyname
Code: Select all
; gethostbyname (IXCALL 0x3E27) - get network host entry
ld hl, STR_HOSTNAME
ld de, BUF_ADDRESS
ld ix, GETHOSTBYNAME
call IXCALL
; The gethostbyname() call converts the string passed in name to an address. The parameter name may be
; a null-terminated hostname (such as 'www.worldofspectrum.org' or a null-terminated dotted decimal string
; representation of an IP address. If a dotted decimal address is passed, no lookup is made, and the string
; is converted to a 4 byte big-endian representation of the address. If a hostname is passed, the hostname
; is looked up using the DNS protocol, and the resolved address is returned as a 4 byte big-endian value.
; With the current implementation, only one address is returned on a successful lookup.
; For the assembly language interface, the register pair HL points to a null-terminated string containing the
; hostname or dotted decimal IP address, and the DE register pair points to a location in memory with at least
; 4 bytes free to receive the 4 byte big-endian representation of the address.
; Return values
; The assembly language interface returns with the carry flag set on error, with the A register containing the return code.
Сишный интерфейс:
Code: Select all
#include <netdb.h>
struct hostent *gethostbyname(char *name);
/*
For the C function call, the pointer returned is to a statically allocated buffer.
If the value returned needs to be preserved when a subsequent call to
gethostbyname() is made, the value must be copied before the next call is made.
The C interface returns a null pointer if gethostbyname() fails to resolve the address.
*/
Я планирую написать совместимую по заголовочным файлам либу для Спринтера (для z88dk и для SolidC), чтобы одни и те же сишные исходники можно было бы собирать и для ZX+Specranet, и для Sprinter+SprinterNet. В репозитории спектранета есть несколько чисто сишных программ:
- сетевая игра ctfgame
- клиент irc
- tankgame
- twitter (с проксей на перле, которую думаю можно переписать на php и внедрить в наш шлюз)
ну и ещё пара простых примеров клиентов и серверов под спектранет также можно сделать пересобираемыми под SprinterNet.