Пока вызовы такие получаются (флаг C - ошибка):
#D000 -
netinit (HL - адрес буфера 64 символа) -> ABC (версия фирмваре) - вызов берёт из EEPROM настройки и записывает их в визнет, подготавливая его для работы, а также копирует содержимое идентификационной строки с версией прошивки в буфер;
#D003 -
getconf (возврат IP-конфигурации путём копирования 32 байт с адреса HL и 6 байт мака с адреса DE) - возвращает конфигурацию в том виде, как она сохранена в EEPROM (имя пользователя, IP-адрес, маска, гейтвей, 3 экстра байта для DHCP и 1 байт таймзона) + MAC;
#D006 -
getvar (HL=name, DE=buf, A=maxsize) -> C,A - прочитать переменную с именем, взятым по указателю HL из persistent storage (возвращает количество скопированных байт в C и сколько жизни осталось у переменной в минутах в регистре A, причём #FF будет означать >=255 минут);
#D009 -
setvar (HL=name, DE=value, A=minutes) -> C,A - записать (поменять) значение переменной с именем, взятым по указателю HL, новое значение берётся по указателю DE (обе строки заканчиваются нулём) - опционально можно указать время жизни переменной в persistent storage (0 - вечная переменная, 255 - живёт 24 часа, а остальные значения - это время жизни в минутах от 1 минуты до 4ч14м), в регистре C вернётся количество записанных байтов, в регистре A вернётся смещение, по которому переменная была записана в EEPROM - если оно #FF, то это значит, что место в EEPROM закончилось и переменная была записана в ОЗУ (хотя наверное для первой версии можно просто ошибку возвращать - памяти EEPROM должно хватить на размещение 95 коротких переменных (когда имя=значение умещается в 26 байт) либо чуть меньшее количество, если имя или значение длинные - можно длину имени переменной ограничить скажем в 25 байтов, а длину значения - в 100);
#D00C -
setip (изменение IP-конфигурации с адреса HL) -> A - запись в EEPROM IP-адреса, маски и гейтвея (если IP-адрес нулевой, то в недалёком будущем оно будет в рамках локальной сети делать DHCP запрос) и возвращает код ошибки (0 если всё Ок)- изменение конфигурации повлечёт за собой ре-инициализацию визнета;
#D00F -
socket (D=domain, E=type) -> A - возвращает следующий свободный сокет (если тип сокета AF_INET или AF_PACKET, то сокетов может быть максимум 3, а если AF_LOCAL, то скажем 16);
#D012 -
bind (A=sock, HL=my_addr, E=addrlen) -> A - устанавливает порт для сокета (вызывает функцию визнета OPEN для открытия серверного сокета) и возвращает код ошибки (0 если всё Ок);
#D015 -
listen (A=sock, E=backlog) -> A - слушает входящие сообщения серверного TCP сокета (backlog в данный момент может принимать значения от 0 до 2 - вызывает функцию визнета LISTEN если сокет один либо OPEN/LISTEN несколько раз, если требуемых сокетов больше чем 1) и возвращает код ошибки (0 если всё Ок);
#D018 -
accept (A=sock, HL=in_addr, E=addrlen) - в регистре A возвращает сокет, к которому подцепился TCP клиент (функция не имеет аналога в визнете и будет реализована программно - возвращаемый сокет может совпадать или не совпадать с входящим);
#D01B -
shutdown (A=sock, E=param) - закрывает соединение правильным образом с серверной стороны в случае TCP (вызывает функцию визнета DISCON);
#D01E -
connect (A=sock, HL=their_addr, E=addrlen) -> A - коннектится к серверу по TCP со стороны клиента, используя параметры адреса (вызывает функции визнета OPEN и CONNECT), возвращает код ошибки (0 если всё Ок);
#D021 -
send (A=sock, HL=buf, DE=size, NO flags) -> BC - послать данные по TCP и вернуть количество переданных байт (вызывает функцию визнета SEND);
#D024 -
recv (A=sock, HL=buf, DE=size, NO flags) -> BC - получить данные по TCP и вернуть количество принятых байт (вызывает функцию визнета RECV);
#D027 -
sendto (A=sock, HL=buf, DE=size, NO flags, IY=out_addr) -> BC - послать данные по UDP и вернуть количество принятых байт (устанавливает адрес и порт удалённого конца и вызывает функцию визнета SEND);
#D02A -
recvfrom (A=sock, HL=buf, DE=size, NO flags, IY=in_addr) -> BC - получить данные по UDP и вернуть количество принятых байт (вызывает функцию визнета RECV и копирует адрес и порт с которых пришло сообщение в in_addr);
#D02D -
close (A=sock) - закрывает сокет (вызывает функцию визнета CLOSE, однако в случае серверных сокетов логика будет более запутанная, т.к. закрываемый сокет надо будет снова переиспользовать в accept);
#D030 -
httpget (IY=url, HL=buf, DE=maxsize) -> BC - прочитать данные из интернета по урлу в буфер ограниченного размера (в BC возвращается количество скопированных байт либо код ответа HTTP если был установлен флаг C - причём 0 будет обозначать ошибку соединения);
#D033 -
resolv (HL=host) -> BCDE - выдает IP-адрес в регистрах по имени сайта, расположенному по адресу HL и заканчивающемся нулевым байтом (в первой версии IP-адрес будет искаться в записях hosts и если там не найдено или просрочилось - будет производится запрос к шлюзу, а в будущем может быть реализован честный DNS-запрос);
#D036 -
time (A=желаемая точность в секундах, HL=опционально указатель на буфер, куда будет записано GMT время в человеческом формате) -> BCDE (unixtime) - делает запрос к шлюзу, чтобы узнать текущее время, если сохранённое время с предыдущего раза было запрошено более чем A секунд назад, а в случае A=0 оно также будет подправлять системные часы в соответствии с выставленной таймзоной, если они показывают неточное время;
#D039 -
hash (A=mode, HL=dataptr, DE=datasz, IY=hashptr) -> запись 20-байтового хэша RIPEMD-160 по указателю IY (A=0 для одного куска данных в памяти, A=1 инициализация для последовательности вызовов, A=2 повторение 64-байтных блоков, A=3 последний блок размером меньше 64 и выдача результата) либо запись 12-байтовой подписи в соответствии с алгоритмом HMAC-RIPEMD-160-96 по указателю IY (в случае A=4);
#D03С -
code (A=mode, HL=dataptr, DE=datasz, IY=outdataptr) -> кодирование/декодирование произвольного блока данных - при кодировании A=0 для hex, A=1 для urlencode, A=2 для uuencode, A=3 для base32, A=4 для base64, A=5 для base64url (по завершению IY указывает на первый байт за последним закодированным) - для декодирования к A надо прибавить 80h;
#D03F -
auth (HL=passptr) -> A=0 если Ок иначе ошибка + появление непустой переменной sid - авторизация в сети SprinterNet.
Как можно легко догадаться, это просто цепочка джампов в начале ПЗУ (шаг между точками входа - 3 байта).
P.S. Небольшое усложнение серверных функций вызвано тем, что визнет сам не поддерживает accept - если надо сделать сервер, способный обработать несколько входящих запросов одновременно, то на визнете просто открывают несколько слушающих сокетов на одном и том же порту - визнет тупо передаст входящий коннект на этот порт первому свободному сокету, который этот порт указал - это несколько противоречит апи беркли-сокетов, поэтому я решил написать некую прослойку, которая будет это несоответствие прятать и показывать наружу API как по книжке (socket -> listen -> accept) - хотя в первой версии можно просто поддержать сервера с одним обработчиком - в этом случае accept будет отдавать управление, когда произошёл коннект, и будет возвращать тот же самый сокет.
P.P.S. Пока это всё про блокирующие сокеты, а в будущем можно сделать неблокирующую опцию:
#D0XX -
nonblock (A=sock) - установить non-blocking режим для сокета - на си это включается через fcntl(sock, F_SETFL, O_NONBLOCK) - чисто логическая функция, которая меняет поведение чтения и записи для сокета с точки зрения внешней программы.
P.P.P.S. Вообще если всё пространство до #D100 разбить под джампы, то это будет 85 джампов - запас на будущие расширения

Последний джамп при этом будет #D0FC и далее с адреса #D100 будет располагаться идентификационная строка, заканчивающаяся нулевым символом:
Code: Select all
SprinterNet v1.0.0 build 1 by shaos (01-MAR-2021 13:11:00)
P.P.P.P.S. На самом деле последним "джампом" можно сделать #D0FF, но по этому адресу будет не джамп, а RST 0
P.P.P.P.P.S. 22 марта 2021 года освободил регистровую пару BC, чтобы была возможность вызывать эти функции через DSS (RST 10h) - в этом случае подключение ISA-платы в третье окно процессора будет происходить автоматически и незаметно для пользователя.
P.P.P.P.P.P.S. 18 мая 2021 года убрал флаги из функций отправки и получения данных - когда возникнет необходимость заиметь флаги, то я просто добавлю второй набор функций
P.P.P.P.P.P.P.S. 15 июля 2021 года добавил вызов #D039 для подсчёта хэша RIPEMD-160 или для подписки блока данных по алгоритму HMAC-RIPEMD-160-96, вызов #D03C для кодирования и декодирования блока данных и вызов #D03F для авторизации в сети SprinterNet (к вопросу о том, что такое base32, base64 и base64url можно почитать тут: https://datatracker.ietf.org/doc/html/rfc4648)