diff --git a/lib/driver_socket_udp.spin b/lib/driver_socket_udp.spin new file mode 100644 index 0000000..d6ac35a --- /dev/null +++ b/lib/driver_socket_udp.spin @@ -0,0 +1,1221 @@ +{{ + Ethernet TCP/IP Socket Layer Driver (IPv4) + ------------------------------------------ + + Copyright (c) 2006-2009 Harrison Pham + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + The latest version of this software can be obtained from + http://hdpham.com/PropTCP and http://obex.parallax.com/ +}} + +'' NOTICE: All buffer sizes must be a power of 2! + +CON +' *************************************** +' ** Versioning Information ** +' *************************************** + version = 5 ' major version + release = 2 ' minor version + apiversion = 8 ' api compatibility version + +' *************************************** +' ** User Definable Settings ** +' *************************************** + sNumSockets = 2 ' max number of concurrent registered sockets (max of 255) + +' *** End of user definable settings, don't edit anything below this line!!! +' *** All IP/MAC settings are defined by calling the start(...) method + +CON +' *************************************** +' ** Return Codes / Errors ** +' *************************************** + + RETBUFFEREMPTY = -1 ' no data available + RETBUFFERFULL = -1 ' buffer full + + ERRGENERIC = -1 ' generic errors + + ERR = -100 ' error codes start at -100 + ERRBADHANDLE = ERR - 1 ' bad socket handle + ERROUTOFSOCKETS = ERR - 2 ' no free sockets available + ERRSOCKETCLOSED = ERR - 3 ' socket closed, could not perform operation + +OBJ + nic : "driver_enc28j60" + + 'ser : "SerialMirror" + 'stk : "Stack Length" + +CON +' *************************************** +' ** Socket Constants and Offsets ** +' *************************************** + +' Socket states (user should never touch these) + SCLOSED = 0 ' closed, handle not used + SLISTEN = 1 ' listening, in server mode + SSYNSENT = 2 ' SYN sent, server mode, waits for ACK + SSYNSENTCL = 3 ' SYN sent, client mode, waits for SYN+ACK + SESTABLISHED = 4 ' established connection (either SYN+ACK, or ACK+Data) + SCLOSING = 5 ' connection is being forced closed by code + SCLOSING2 = 6 ' closing, we are waiting for a fin now + SFORCECLOSE = 7 ' force connection close (just RSTs, no waiting for FIN or anything) + SCONNECTINGARP1 = 8 ' connecting, next step: send arp request + SCONNECTINGARP2 = 9 ' connecting, next step: arp request sent, waiting for response + SCONNECTINGARP2G = 10 ' connecting, next step: arp request sent, waiting for response [GATEWAY REQUEST] + SCONNECTING = 11 ' connecting, next step: got mac address, send SYN + +' *************************************** +' ** TCP State Management Constants ** +' *************************************** + TIMEOUTMS = 500 ' (milliseconds) timeout before a retransmit occurs + RSTTIMEOUTMS = 2000 ' (milliseconds) timeout before a RST is sent to close the connection + WINDOWUPDATEMS = 25 ' (milliseconds) window advertisement frequency + + MAXUNACKS = 6 ' max number of unacknowledged retransmits before the stack auto closes the socket + ' timeout = TIMEOUTMS * MAXUNACKS (default: 500ms * 5 = 3000ms) + + EPHPORTSTART = 49152 ' ephemeral port start + EPHPORTEND = 65535 ' end + + MAXPAYLOAD = 1200 ' maximum TCP payload (data) in bytes, this only applies when your txbuffer_length > payload size + +DAT +' *************************************** +' ** Global Variables ** +' *************************************** + cog long 0 ' cog index (for stopping / starting) + stack long 0[128] ' stack for new cog (currently ~74 longs, using 128 for expansion) + + mac_ptr long 0 ' mac address pointer + + pkt_id long 0 ' packet fragmentation id + pkt_isn long 0 ' packet initial sequence number + + ip_ephport word 0 ' packet ephemeral port number (49152 to 65535) + + pkt_count byte 0 ' packet count + + lock_id byte 0 ' socket handle lock + + packet byte 0[nic#MAXFRAME] ' the ethernet frame + +' *************************************** +' ** IP Address Defaults ** +' *************************************** + ' NOTE: All of the MAC/IP variables here contain default values that will + ' be used if override values are not provided as parameters in start(). + long ' long alignment for addresses + ip_addr byte 10, 10, 1, 4 ' device's ip address + ip_subnet byte 255, 255, 255, 0 ' network subnet + ip_gateway byte 10, 10, 1, 254 ' network gateway (router) + ip_dns byte 10, 10, 1, 254 ' network dns + +' *************************************** +' ** Socket Data Arrays ** +' *************************************** + + long + SocketArrayStart + lMySeqNum long 0[sNumSockets] + lMyAckNum long 0[sNumSockets] + lSrcIp long 0[sNumSockets] + lTime long 0[sNumSockets] + + word + wSrcPort word 0[sNumSockets] + wDstPort word 0[sNumSockets] + wLastWin word 0[sNumSockets] + wLastTxLen word 0[sNumSockets] + wNotAcked word 0[sNumSockets] + + byte + bSrcMac byte 0[sNumSockets * 6] + bConState byte 0[sNumSockets] + SocketArrayEnd + +' *************************************** +' ** Circular Buffer Arrays ** +' *************************************** + word + FifoDataStart + rx_head word 0[sNumSockets] ' rx head array + rx_tail word 0[sNumSockets] ' rx tail array + tx_head word 0[sNumSockets] ' tx head array + tx_tail word 0[sNumSockets] ' tx tail array + + tx_tailnew word 0[sNumSockets] ' the new tx_tail value (unacked data) + + rxbuffer_length word 0[sNumSockets] ' each socket's buffer sizes + txbuffer_length word 0[sNumSockets] + + rxbuffer_mask word 0[sNumSockets] ' each socket's buffer masks for capping buffer sizes + txbuffer_mask word 0[sNumSockets] + + long + tx_bufferptr long 0[sNumSockets] ' pointer addresses to each socket's buffer spaces + rx_bufferptr long 0[sNumSockets] + FifoDataEnd + +PUB start(cs, sck, si, so, xtalout, macptr, ipconfigptr) +'' Start the TCP/IP Stack (requires 2 cogs) +'' Only call this once, otherwise you will get conflicts +'' macptr = HUB memory pointer (address) to 6 contiguous mac address bytes +'' ipconfigptr = HUB memory pointer (address) to ip configuration block (16 bytes) +'' Must be in order: ip_addr, ip_subnet, ip_gateway, ip_dns + + stop + 'stk.Init(@stack, 128) + + ' zero socket data arrays (clean up any dead stuff from previous instance) + bytefill(@SocketArrayStart, 0, @SocketArrayEnd - @SocketArrayStart) + + ' reset buffer pointers, zeros a contigous set of bytes, starting at rx_head + bytefill(@FifoDataStart, 0, @FifoDataEnd - @FifoDataStart) + + ' start new cog with tcp stack + cog := cognew(engine(cs, sck, si, so, xtalout, macptr, ipconfigptr), @stack) + 1 + +PUB stop +'' Stop the driver + + if cog + cogstop(cog~ - 1) ' stop the tcp engine + nic.stop ' stop nic driver (kills spi engine) + lockclr(lock_id) ' clear lock before returning it to the pool + lockret(lock_id) ' return the lock to the lock pool + +PRI engine(cs, sck, si, so, xtalout, macptr, ipconfigptr) | i + + lock_id := locknew ' checkout a lock from the HUB + lockclr(lock_id) ' clear the lock, just in case it was in a bad state + + ' Start the ENC28J60 driver in a new cog + nic.start(cs, sck, si, so, xtalout, macptr) ' init the nic + + if ipconfigptr > -1 ' init ip configuration + bytemove(@ip_addr, ipconfigptr, 16) + + mac_ptr := nic.get_mac_pointer ' get the local mac address pointer + + ip_ephport := EPHPORTSTART ' set initial ephemeral port number (might want to random seed this later) + + i := 0 + nic.banksel(nic#EPKTCNT) ' select packet count bank + repeat + pkt_count := nic.rd_cntlreg(nic#EPKTCNT) + if pkt_count > 0 + service_packet ' handle packet + nic.banksel(nic#EPKTCNT) ' re-select the packet count bank + + ++i + if i > 10 ' perform send tick + repeat while lockset(lock_id) + tick_tcpsend ' occurs every 10 cycles, since incoming packets more important + lockclr(lock_id) + + i := 0 + nic.banksel(nic#EPKTCNT) ' re-select the packet count bank + +PRI service_packet + + ' lets process this frame + nic.get_frame(@packet) + + ' check for arp packet type (highest priority obviously) + if packet[enetpacketType0] == $08 AND packet[enetpacketType1] == $06 + if packet[constant(arp_hwtype + 1)] == $01 AND packet[arp_prtype] == $08 AND packet[constant(arp_prtype + 1)] == $00 AND packet[arp_hwlen] == $06 AND packet[arp_prlen] == $04 + if packet[arp_tipaddr] == ip_addr[0] AND packet[constant(arp_tipaddr + 1)] == ip_addr[1] AND packet[constant(arp_tipaddr + 2)] == ip_addr[2] AND packet[constant(arp_tipaddr + 3)] == ip_addr[3] + case packet[constant(arp_op + 1)] + $01 : handle_arp + $02 : repeat while lockset(lock_id) + handle_arpreply + lockclr(lock_id) + '++count_arp + else + if packet[enetpacketType0] == $08 AND packet[enetpacketType1] == $00 + if packet[ip_destaddr] == ip_addr[0] AND packet[constant(ip_destaddr + 1)] == ip_addr[1] AND packet[constant(ip_destaddr + 2)] == ip_addr[2] AND packet[constant(ip_destaddr + 3)] == ip_addr[3] + case packet[ip_proto] + 'PROT_ICMP : 'handle_ping + 'ser.str(stk.GetLength(0, 0)) + 'stk.GetLength(30, 19200) + '++count_ping + PROT_TCP : repeat while lockset(lock_id) + \handle_tcp ' handles abort out of tcp handlers (no socket found) + lockclr(lock_id) + '++count_tcp + 'PROT_UDP : ++count_udp + +' ******************************* +' ** Protocol Receive Handlers ** +' ******************************* +PRI handle_arp | i + nic.start_frame + + ' destination mac address + repeat i from 0 to 5 + nic.wr_frame(packet[enetpacketSrc0 + i]) + + ' source mac address + repeat i from 0 to 5 + nic.wr_frame(BYTE[mac_ptr][i]) + + nic.wr_frame($08) ' arp packet + nic.wr_frame($06) + + nic.wr_frame($00) ' 10mb ethernet + nic.wr_frame($01) + + nic.wr_frame($08) ' ip proto + nic.wr_frame($00) + + nic.wr_frame($06) ' mac addr len + nic.wr_frame($04) ' proto addr len + + nic.wr_frame($00) ' arp reply + nic.wr_frame($02) + + ' write ethernet module mac address + repeat i from 0 to 5 + nic.wr_frame(BYTE[mac_ptr][i]) + + ' write ethernet module ip address + repeat i from 0 to 3 + nic.wr_frame(ip_addr[i]) + + ' write remote mac address + repeat i from 0 to 5 + nic.wr_frame(packet[enetpacketSrc0 + i]) + + ' write remote ip address + repeat i from 0 to 3 + nic.wr_frame(packet[arp_sipaddr + i]) + + return nic.send_frame + +PRI handle_arpreply | handle, ip, found + ' Gets arp reply if it is a response to an ip we have + + ip := (packet[constant(arp_sipaddr + 3)] << 24) + (packet[constant(arp_sipaddr + 2)] << 16) + (packet[constant(arp_sipaddr + 1)] << 8) + (packet[arp_sipaddr]) + + found := false + if ip == LONG[@ip_gateway] + ' find a handle that wants gateway mac + repeat handle from 0 to constant(sNumSockets - 1) + if bConState[handle] == SCONNECTINGARP2G + found := true + quit + else + ' find the one that wants this arp + repeat handle from 0 to constant(sNumSockets - 1) + if bConState[handle] == SCONNECTINGARP2 + if lSrcIp[handle] == ip + found := true + quit + + if found + bytemove(@bSrcMac[handle * 6], @packet + arp_shaddr, 6) + bConState[handle] := SCONNECTING + +'PRI handle_ping + ' Not implemented yet (save on space!) + +PRI handle_tcp | i, ptr, handle, srcip, dstport, srcport, datain_len + ' Handles incoming TCP packets + + srcip := packet[ip_srcaddr] << 24 + packet[constant(ip_srcaddr + 1)] << 16 + packet[constant(ip_srcaddr + 2)] << 8 + packet[constant(ip_srcaddr + 3)] + dstport := packet[TCP_destport] << 8 + packet[constant(TCP_destport + 1)] + srcport := packet[TCP_srcport] << 8 + packet[constant(TCP_srcport + 1)] + + handle := find_socket(srcip, dstport, srcport) ' if no sockets avail, it will abort out of this function + + ' at this point we assume we have an active socket, or a socket available to be used + datain_len := ((packet[ip_pktlen] << 8) + packet[constant(ip_pktlen + 1)]) - ((packet[ip_vers_len] & $0F) * 4) - (((packet[TCP_hdrlen] & $F0) >> 4) * 4) + + if (bConState[handle] == SSYNSENT OR bConState[handle] == SESTABLISHED) AND (packet[TCP_hdrflags] & TCP_ACK) AND datain_len > 0 + ' ACK, without SYN, with data + + ' set socket state, established session + bConState[handle] := SESTABLISHED + + i := packet[constant(TCP_seqnum + 3)] << 24 + packet[constant(TCP_seqnum + 2)] << 16 + packet[constant(TCP_seqnum + 1)] << 8 + packet[TCP_seqnum] + if lMyAckNum[handle] == i + if datain_len =< (rxbuffer_mask[handle] - ((rx_head[handle] - rx_tail[handle]) & rxbuffer_mask[handle])) + ' we have buffer space + ptr := rx_bufferptr[handle] + if (datain_len + rx_head[handle]) > rxbuffer_length[handle] + bytemove(ptr + rx_head[handle], @packet[TCP_data], rxbuffer_length[handle] - rx_head[handle]) + bytemove(ptr, @packet[TCP_data] + (rxbuffer_length[handle] - rx_head[handle]), datain_len - (rxbuffer_length[handle] - rx_head[handle])) + else + bytemove(ptr + rx_head[handle], @packet[TCP_data], datain_len) + rx_head[handle] := (rx_head[handle] + datain_len) & rxbuffer_mask[handle] + else + datain_len := 0 + + else + ' we had a bad ack number, meaning lost or out of order packet + ' we have to wait for the remote host to retransmit in order + datain_len := 0 + + ' recalculate ack number + lMyAckNum[handle] := conv_endianlong(conv_endianlong(lMyAckNum[handle]) + datain_len) + + ' ACK response + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, TCP_ACK) + send_tcpfinal(handle, 0) + + elseif (bConState[handle] == SSYNSENTCL) AND (packet[TCP_hdrflags] & TCP_SYN) AND (packet[TCP_hdrflags] & TCP_ACK) + ' We got a server response, so we ACK it + + bytemove(@lMySeqNum[handle], @packet + TCP_acknum, 4) + bytemove(@lMyAckNum[handle], @packet + TCP_seqnum, 4) + + lMyAckNum[handle] := conv_endianlong(conv_endianlong(lMyAckNum[handle]) + 1) + + ' ACK response + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, TCP_ACK) + send_tcpfinal(handle, 0) + + ' set socket state, established session + bConState[handle] := SESTABLISHED + + elseif (bConState[handle] == SLISTEN) AND (packet[TCP_hdrflags] & TCP_SYN) + ' Reply to SYN with SYN + ACK + + ' copy mac address so we don't have to keep an ARP table + bytemove(@bSrcMac[handle * 6], @packet + enetpacketSrc0, 6) + + ' copy ip, port data + bytemove(@lSrcIp[handle], @packet + ip_srcaddr, 4) + bytemove(@wSrcPort[handle], @packet + TCP_srcport, 2) + bytemove(@wDstPort[handle], @packet + TCP_destport, 2) + + ' get updated ack numbers + bytemove(@lMyAckNum[handle], @packet + TCP_seqnum, 4) + + lMyAckNum[handle] := conv_endianlong(conv_endianlong(lMyAckNum[handle]) + 1) + lMySeqNum[handle] := conv_endianlong(++pkt_isn) ' Initial seq num (random) + + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, constant(TCP_SYN | TCP_ACK)) + send_tcpfinal(handle, 0) + + ' incremement the sequence number for the next packet (it will be for an established connection) + lMySeqNum[handle] := conv_endianlong(conv_endianlong(lMySeqNum[handle]) + 1) + + ' set socket state, waiting for establish + bConState[handle] := SSYNSENT + + elseif (bConState[handle] == SESTABLISHED OR bConState[handle] == SCLOSING2) AND (packet[TCP_hdrflags] & TCP_FIN) + ' Reply to FIN with RST + + ' get updated sequence and ack numbers (gaurantee we have correct ones to kill connection with) + bytemove(@lMySeqNum[handle], @packet + TCP_acknum, 4) + bytemove(@lMyAckNum[handle], @packet + TCP_seqnum, 4) + + 'LONG[handle_addr + sMyAckNum] := conv_endianlong(conv_endianlong(LONG[handle_addr + sMyAckNum]) + 1) + + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, TCP_RST) + send_tcpfinal(handle, 0) + + ' set socket state, now free + bConState[handle] := SCLOSED + return + + elseif (bConState[handle] == SSYNSENT) AND (packet[TCP_hdrflags] & TCP_ACK) + ' if just an ack, and we sent a syn before, then it's established + ' this just gives us the ability to send on connect + bConState[handle] := SESTABLISHED + + elseif (packet[TCP_hdrflags] & TCP_RST) + ' Reset, reset states + bConState[handle] := SCLOSED + return + + if (bConState[handle] == SESTABLISHED OR bConState[handle] == SCLOSING) AND (packet[TCP_hdrflags] & TCP_ACK) + wNotAcked[handle] := 0 ' reset retransmit counter + ' check to see if our last sent data has been ack'd + i := packet[TCP_acknum] << 24 + packet[constant(TCP_acknum + 1)] << 16 + packet[constant(TCP_acknum + 2)] << 8 + packet[constant(TCP_acknum + 3)] + if i == (conv_endianlong(lMySeqNum[handle]) + wLastTxLen[handle]) + ' we received an ack for our last sent packet, so we update our sequence number and buffer pointers + lMySeqNum[handle] := conv_endianlong(conv_endianlong(lMySeqNum[handle]) + wLastTxLen[handle]) + tx_tail[handle] := tx_tailnew[handle] + wLastTxLen[handle] := 0 + + tcpsend(handle) ' send data + +PRI build_ipheaderskeleton(handle) | hdrlen, hdr_chksum + + bytemove(@packet + ip_destaddr, @lSrcIp[handle], 4) ' Set destination address + + bytemove(@packet + ip_srcaddr, @ip_addr, 4) ' Set source address + + bytemove(@packet + enetpacketDest0, @bSrcMac[handle * 6], 6) ' Set destination mac address + + bytemove(@packet + enetpacketSrc0, mac_ptr, 6) ' Set source mac address + + packet[enetpacketType0] := $08 + packet[constant(enetpacketType0 + 1)] := $00 + + packet[ip_vers_len] := $45 + packet[ip_tos] := $00 + + ++pkt_id + + packet[ip_id] := pkt_id >> 8 ' Used for fragmentation + packet[constant(ip_id + 1)] := pkt_id + + packet[ip_frag_offset] := $40 ' Don't fragment + packet[constant(ip_frag_offset + 1)] := 0 + + packet[ip_ttl] := $80 ' TTL = 128 + + packet[ip_proto] := $06 ' TCP protocol + +PRI build_tcpskeleton(handle, flags) | size + + bytemove(@packet + TCP_srcport, @wDstPort[handle], 2) ' Source port + bytemove(@packet + TCP_destport, @wSrcPort[handle], 2) ' Destination port + + bytemove(@packet + TCP_seqnum, @lMySeqNum[handle], 4) ' Seq Num + bytemove(@packet + TCP_acknum, @lMyAckNum[handle], 4) ' Ack Num + + packet[TCP_hdrlen] := $50 ' Header length + + packet[TCP_hdrflags] := flags ' TCP state flags + + ' we have to recalculate the window size often otherwise our stack + ' might explode from too much data :( + size := (rxbuffer_mask[handle] - ((rx_head[handle] - rx_tail[handle]) & rxbuffer_mask[handle])) + wLastWin[handle] := size + + packet[TCP_window] := (size & $FF00) >> 8 + packet[constant(TCP_window + 1)] := size & $FF + +PRI send_tcpfinal(handle, datalen) | i, tcplen, hdrlen, hdr_chksum + + tcplen := 40 + datalen ' real length = data + headers + + packet[ip_pktlen] := tcplen >> 8 + packet[constant(ip_pktlen + 1)] := tcplen + + ' calc ip header checksum + packet[ip_hdr_cksum] := $00 + packet[constant(ip_hdr_cksum + 1)] := $00 + hdrlen := (packet[ip_vers_len] & $0F) * 4 + hdr_chksum := calc_chksum(@packet[ip_vers_len], hdrlen) + packet[ip_hdr_cksum] := hdr_chksum >> 8 + packet[constant(ip_hdr_cksum + 1)] := hdr_chksum + + ' calc checksum + packet[TCP_cksum] := $00 + packet[constant(TCP_cksum + 1)] := $00 + hdr_chksum := nic.chksum_add(@packet[ip_srcaddr], 8) + hdr_chksum += packet[ip_proto] + i := tcplen - ((packet[ip_vers_len] & $0F) * 4) + hdr_chksum += i + hdr_chksum += nic.chksum_add(@packet[TCP_srcport], i) + hdr_chksum := calc_chksumfinal(hdr_chksum) + packet[TCP_cksum] := hdr_chksum >> 8 + packet[constant(TCP_cksum + 1)] := hdr_chksum + + tcplen += 14 + if tcplen < 60 + tcplen := 60 + + ' protect from buffer overrun + if tcplen => nic#TX_BUFFER_SIZE + return + + ' send the packet + nic.start_frame + nic.wr_block(@packet, tcplen) + nic.send_frame + + lTime[handle] := cnt ' update last sent time (for timeout detection) + +PRI find_socket(srcip, dstport, srcport) | handle, free_handle, listen_handle + ' Search for socket, matches ip address, port states + ' Returns handle address (start memory location of socket) + ' If no matches, will abort with -1 + ' If supplied with srcip = 0 then will return free unused handle, aborts with -1 if none avail + + free_handle := -1 + listen_handle := -1 + repeat handle from 0 to constant(sNumSockets - 1) + if bConState[handle] <> SCLOSED + if (lSrcIp[handle] == 0) OR (lSrcIp[handle] == conv_endianlong(srcip)) + ' ip match, ip socket srcip = 0, then will try to match dst port (find listening socket) + if (wDstPort[handle] == conv_endianword(dstport)) {AND (WORD[handle_addr + sSrcPort] == 0 OR WORD[handle_addr + sSrcPort] == conv_endianword(srcport))} + if wSrcPort[handle] == conv_endianword(srcport) + ' found exact socket match (established socket) + return handle + elseif wSrcPort[handle] == 0 + ' found a partial match (listening socket with no peer) + listen_handle := handle + elseif srcip == 0 + ' found a closed (unallocated) socket, save this as a free handle if we are searching for a free handle + free_handle := handle ' we found a free handle, may need this later + + if srcip <> 0 + ' return the listening handle we found + if listen_handle <> -1 + return listen_handle + else + ' searched for a free handle + if free_handle <> -1 + return free_handle + + ' could not find a matching socket / free socket... + abort -1 + +' ****************************** +' ** Transmit Buffer Handlers ** +' ****************************** +PRI tcpsend(handle) | ptr, len + ' Check buffers for data to send (called in main loop) + + if tx_tail[handle] == tx_head[handle] + ' no data in buffer, so just quit + return + + ' we have data to send, so send it + ptr := tx_bufferptr[handle] + len := ((tx_head[handle] - tx_tail[handle]) & txbuffer_mask[handle]) <# MAXPAYLOAD + if (len + tx_tail[handle]) > txbuffer_length[handle] + bytemove(@packet[TCP_data], ptr + tx_tail[handle], txbuffer_length[handle] - tx_tail[handle]) + bytemove(@packet[TCP_data] + (txbuffer_length[handle] - tx_tail[handle]), ptr, len - (txbuffer_length[handle] - tx_tail[handle])) + else + bytemove(@packet[TCP_data], ptr + tx_tail[handle], len) + tx_tailnew[handle] := (tx_tail[handle] + len) & txbuffer_mask[handle] + + wLastTxLen[handle] := len + + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, TCP_ACK {constant(TCP_ACK | TCP_PSH)}) + send_tcpfinal(handle, len) ' send actual data + + send_tcpfinal(handle, 0) ' send an empty packet to force the other side to ACK (hack to get around delayed acks) + + wNotAcked[handle]++ ' increment unacked packet counter + +PRI tick_tcpsend | handle, state, len + + repeat handle from 0 to constant(sNumSockets - 1) + state := bConState[handle] + + if state == SESTABLISHED OR state == SCLOSING + len := (rxbuffer_mask[handle] - ((rx_head[handle] - rx_tail[handle]) & rxbuffer_mask[handle])) + if wLastWin[handle] <> len AND len => (rxbuffer_length[handle] / 2) AND ((cnt - lTime[handle]) / (clkfreq / 1000) > WINDOWUPDATEMS) + ' update window size + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, TCP_ACK) + send_tcpfinal(handle, 0) + + if ((cnt - lTime[handle]) / (clkfreq / 1000) > TIMEOUTMS) OR wLastTxLen[handle] == 0 + ' send new data OR retransmit our last packet since the other side seems to have lost it + ' the remote host will respond with another dup ack, and we will get back on track (hopefully) + tcpsend(handle) + + if (state == SCLOSING) + + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, constant(TCP_ACK | TCP_FIN)) + send_tcpfinal(handle, 0) + + ' we now wait for the other side to terminate + bConState[handle] := SCLOSING2 + + elseif state == SCONNECTINGARP1 + ' We need to send an arp request + + arp_request_checkgateway(handle) + + elseif state == SCONNECTING + ' Yea! We got an arp response previously, so now we can send the SYN + + lMySeqNum[handle] := conv_endianlong(++pkt_isn) + lMyAckNum[handle] := 0 + + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, TCP_SYN) + send_tcpfinal(handle, 0) + + bConState[handle] := SSYNSENTCL + + elseif (state == SFORCECLOSE) OR (state == SESTABLISHED AND wNotAcked[handle] => MAXUNACKS) OR (lookdown(state: SCLOSING2, SSYNSENT, SSYNSENTCL, SCONNECTINGARP2, SCONNECTINGARP2G) {(state == SCLOSING2 OR state == SSYNSENT)} AND ((cnt - lTime[handle]) / (clkfreq / 1000) > RSTTIMEOUTMS)) + ' Force close (send RST, and say the socket is closed!) + + ' This is triggered when any of the following happens: + ' 1 - we don't get a response to our SSYNSENT state + ' 2 - we exceeded MAXUNACKS tcp retransmits (remote host lost) + ' 3 - we get stuck in the SSCLOSING2 state + ' 4 - we don't get a response to our client SYNSENTCL state + ' 5 - we don't get an ARP response state SCONNECTINGARP2 or SCONNECTINGARP2G + + build_ipheaderskeleton(handle) + build_tcpskeleton(handle, TCP_RST) + send_tcpfinal(handle, 0) + + bConState[handle] := SCLOSED + +PRI arp_request_checkgateway(handle) | ip_ptr + + ip_ptr := @lSrcIp[handle] + + if (BYTE[ip_ptr] & ip_subnet[0]) == (ip_addr[0] & ip_subnet[0]) AND (BYTE[ip_ptr + 1] & ip_subnet[1]) == (ip_addr[1] & ip_subnet[1]) AND (BYTE[ip_ptr + 2] & ip_subnet[2]) == (ip_addr[2] & ip_subnet[2]) AND (BYTE[ip_ptr + 3] & ip_subnet[3]) == (ip_addr[3] & ip_subnet[3]) + arp_request(conv_endianlong(LONG[ip_ptr])) + bConState[handle] := SCONNECTINGARP2 + else + arp_request(conv_endianlong(LONG[@ip_gateway])) + bConState[handle] := SCONNECTINGARP2G + + lTime[handle] := cnt + +PRI arp_request(ip) | i + nic.start_frame + + ' destination mac address (broadcast mac) + repeat i from 0 to 5 + nic.wr_frame($FF) + + ' source mac address (this device) + repeat i from 0 to 5 + nic.wr_frame(BYTE[mac_ptr][i]) + + nic.wr_frame($08) ' arp packet + nic.wr_frame($06) + + nic.wr_frame($00) ' 10mb ethernet + nic.wr_frame($01) + + nic.wr_frame($08) ' ip proto + nic.wr_frame($00) + + nic.wr_frame($06) ' mac addr len + nic.wr_frame($04) ' proto addr len + + nic.wr_frame($00) ' arp request + nic.wr_frame($01) + + ' source mac address (this device) + repeat i from 0 to 5 + nic.wr_frame(BYTE[mac_ptr][i]) + + ' source ip address (this device) + repeat i from 0 to 3 + nic.wr_frame(ip_addr[i]) + + ' unknown mac address area + repeat i from 0 to 5 + nic.wr_frame($00) + + ' figure out if we need router arp request or host arp request + ' this means some subnet masking + + ' dest ip address + repeat i from 3 to 0 + nic.wr_frame(ip.byte[i]) + + ' send the request + return nic.send_frame + +' ******************************* +' ** IP Packet Helpers (Calcs) ** +' ******************************* +PRI calc_chksum(ptr, hdrlen) : chksum + ' Calculates IP checksums + ' packet = pointer to IP packet + ' returns: chksum + ' http://www.geocities.com/SiliconValley/2072/bit33.txt + 'chksum := calc_chksumhalf(packet, hdrlen) + chksum := nic.chksum_add(ptr, hdrlen) + chksum := calc_chksumfinal(chksum) + +PRI calc_chksumfinal(chksumin) : chksum + ' Performs the final part of checksums + chksum := (chksumin >> 16) + (chksumin & $FFFF) + chksum := (!chksum) & $FFFF + +{PRI calc_chksumhalf(packet, hdrlen) : chksum + ' Calculates checksum without doing the final stage of calculations + chksum := 0 + repeat while hdrlen > 1 + chksum += (BYTE[packet++] << 8) + BYTE[packet++] + chksum := (chksum >> 16) + (chksum & $FFFF) + hdrlen -= 2 + if hdrlen > 0 + chksum += BYTE[packet] << 8} + +' *************************** +' ** Memory Access Helpers ** +' *************************** +PRI conv_endianlong(in) + 'return (in << 24) + ((in & $FF00) << 8) + ((in & $FF0000) >> 8) + (in >> 24) ' we can sometimes get away with shifting without masking, since shifts kill extra bits anyways + return (in.byte[0] << 24) + (in.byte[1] << 16) + (in.byte[2] << 8) + (in.byte[3]) + +PRI conv_endianword(in) + 'return ((in & $FF) << 8) + ((in & $FF00) >> 8) + return (in.byte[0] << 8) + (in.byte[1]) + +PRI _handleConvert(userHandle, ptrHandle) | handle +' Checks to see if a handle index is valid +' Aborts if the handle is invalid + + handle := userHandle.byte[0] ' extract the handle index from the lower 8 bits + + if handle < 0 OR handle > constant(sNumSockets - 1) ' check the handle index to make sure we don't go out of bounds + abort ERRBADHANDLE + + ' check handle to make sure it's the one we want (rid ourselves of bad user handles) + ' the current check method is as follows: + ' - compare sDstPort + + if wDstPort[handle] <> ((userHandle.byte[2] << 8) + userHandle.byte[1]) + abort ERRBADHANDLE + + ' if we got here without aborting then we can assume the handle is good + LONG[ptrHandle] := handle + +' ************************************ +' ** Public Accessors (Thread Safe) ** +' ************************************ +PUB listen(port, _ptrrxbuff, _rxlen, _ptrtxbuff, _txlen) | handle +'' Sets up a socket for listening on a port +'' port = port number to listen on +'' ptrrxbuff = pointer to the rxbuffer array +'' rxlen = length of the rxbuffer array (must be power of 2) +'' ptrtxbuff = pointer to the txbuffer array +'' txlen = length of the txbuffer array (must be power of 2) +'' Returns handle if available, ERROUTOFSOCKETS if none available +'' Nonblocking + + repeat while lockset(lock_id) + + ' just find any avail closed socket + handle := \find_socket(0, 0, 0) + + if handle < 0 + lockclr(lock_id) + abort ERROUTOFSOCKETS + + rx_bufferptr[handle] := _ptrrxbuff + tx_bufferptr[handle] := _ptrtxbuff + rxbuffer_length[handle] := _rxlen + txbuffer_length[handle] := _txlen + rxbuffer_mask[handle] := _rxlen - 1 + txbuffer_mask[handle] := _txlen - 1 + + lMySeqNum[handle] := 0 + lMyAckNum[handle] := 0 + lSrcIp[handle] := 0 + lTime[handle] := 0 + wLastTxLen[handle] := 0 + wNotAcked[handle] := 0 + bytefill(@bSrcMac[handle * 6], 0, 6) + + wSrcPort[handle] := 0 ' no source port yet + wDstPort[handle] := conv_endianword(port) ' we do have a dest port though + + wLastWin[handle] := rxbuffer_length[handle] + + tx_head[handle] := 0 + tx_tail[handle] := 0 + tx_tailnew[handle] := 0 + rx_head[handle] := 0 + rx_tail[handle] := 0 + + ' it's now listening + bConState[handle] := SLISTEN + + lockclr(lock_id) + + return ((port.byte[0] << 16) + (port.byte[1] << 8)) + handle + +PUB connect(ipaddr, remoteport, _ptrrxbuff, _rxlen, _ptrtxbuff, _txlen) | handle, user_handle +'' Connect to remote host +'' ipaddr = ipv4 address packed into a long (ie: 1.2.3.4 => $01_02_03_04) +'' remoteport = port number to connect to +'' ptrrxbuff = pointer to the rxbuffer array +'' rxlen = length of the rxbuffer array (must be power of 2) +'' ptrtxbuff = pointer to the txbuffer array +'' txlen = length of the txbuffer array (must be power of 2) +'' Returns handle to new socket, ERROUTOFSOCKETS if no socket available +'' Nonblocking + + repeat while lockset(lock_id) + + ' just find any avail closed socket + handle := \find_socket(0, 0, 0) + + if handle < 0 + lockclr(lock_id) + abort ERROUTOFSOCKETS + + rx_bufferptr[handle] := _ptrrxbuff + tx_bufferptr[handle] := _ptrtxbuff + rxbuffer_length[handle] := _rxlen + txbuffer_length[handle] := _txlen + rxbuffer_mask[handle] := _rxlen - 1 + txbuffer_mask[handle] := _txlen - 1 + + lMySeqNum[handle] := 0 + lMyAckNum[handle] := 0 + lTime[handle] := 0 + wLastTxLen[handle] := 0 + wNotAcked[handle] := 0 + bytefill(@bSrcMac[handle * 6], 0, 6) + + if(ip_ephport => EPHPORTEND) ' constrain ephport to specified range + ip_ephport := EPHPORTSTART + + user_handle := ((ip_ephport.byte[0] << 16) + (ip_ephport.byte[1] << 8)) + handle + + ' copy in ip, port data (with respect to the remote host, since we use same code as server) + lSrcIp[handle] := conv_endianlong(ipaddr) + wSrcPort[handle] := conv_endianword(remoteport) + wDstPort[handle] := conv_endianword(ip_ephport++) + + wLastWin[handle] := rxbuffer_length[handle] + + tx_head[handle] := 0 + tx_tail[handle] := 0 + tx_tailnew[handle] := 0 + rx_head[handle] := 0 + rx_tail[handle] := 0 + + bConState[handle] := SCONNECTINGARP1 + + lockclr(lock_id) + + return user_handle + +PUB close(user_handle) | handle, state +'' Closes a connection + + _handleConvert(user_handle, @handle) + + repeat while lockset(lock_id) + + state := bConState[handle] + + if state == SESTABLISHED + ' try to gracefully close the connection + bConState[handle] := SCLOSING + elseif state <> SCLOSING AND state <> SCLOSING2 + ' we only do an ungraceful close if we are not in ESTABLISHED, CLOSING, or CLOSING2 + bConState[handle] := SCLOSED + + lockclr(lock_id) + + ' wait for the socket to close, this is very important to prevent the client app from reusing the buffers + repeat until (bConState[handle] == SCLOSING2) or (bConState[handle] == SCLOSED) + +PUB isConnected(user_handle) | handle +'' Returns true if the socket is connected, false otherwise + + if \_handleConvert(user_handle, @handle) <> 0 + return false + + return (bConState[handle] == SESTABLISHED) + +PUB isValidHandle(user_handle) | handle +'' Checks to see if the handle is valid, handles will become invalid once they are used +'' In other words, a closed listening socket is now invalid, etc + + {if handle < 0 OR handle > constant(sNumSockets - 1) + ' obviously the handle index is out of range, so it's not valid! + return false} + + if \_handleConvert(user_handle, @handle) < 0 + return false + + return (bConState[handle] <> SCLOSED) + +PUB readDataNonBlocking(user_handle, ptr, maxlen) | handle, len, rxptr +'' Reads bytes from the socket +'' Returns number of read bytes +'' Not blocking (returns RETBUFFEREMPTY if no data) + + _handleConvert(user_handle, @handle) + + if rx_tail[handle] == rx_head[handle] + return RETBUFFEREMPTY + + len := (rx_head[handle] - rx_tail[handle]) & rxbuffer_mask[handle] + if maxlen < len + len := maxlen + + rxptr := rx_bufferptr[handle] + + if (len + rx_tail[handle]) > rxbuffer_length[handle] + bytemove(ptr, rxptr + rx_tail[handle], rxbuffer_length[handle] - rx_tail[handle]) + bytemove(ptr + (rxbuffer_length[handle] - rx_tail[handle]), rxptr, len - (rxbuffer_length[handle] - rx_tail[handle])) + else + bytemove(ptr, rxptr + rx_tail[handle], len) + + rx_tail[handle] := (rx_tail[handle] + len) & rxbuffer_mask[handle] + + return len + +PUB readData(user_handle, ptr, maxlen) : len | handle +'' Reads bytes from the socket +'' Returns the number of read bytes +'' Will block until data is received + + _handleConvert(user_handle, @handle) + + repeat while (len := readDataNonBlocking(user_handle, ptr, maxlen)) < 0 + ifnot isConnected(user_handle) + abort ERRSOCKETCLOSED + +PUB readByteNonBlocking(user_handle) : rxbyte | handle, ptr +'' Read a byte from the specified socket +'' Will not block (returns RETBUFFEREMPTY if no byte avail) + + _handleConvert(user_handle, @handle) + + rxbyte := RETBUFFEREMPTY + if rx_tail[handle] <> rx_head[handle] + ptr := rx_bufferptr[handle] + rxbyte := BYTE[ptr][rx_tail[handle]] + rx_tail[handle] := (rx_tail[handle] + 1) & rxbuffer_mask[handle] + +PUB readByte(user_handle) : rxbyte | handle, ptr +'' Read a byte from the specified socket +'' Will block until a byte is received + + _handleConvert(user_handle, @handle) + + repeat while (rxbyte := readByteNonBlocking(user_handle)) < 0 + ifnot isConnected(user_handle) + abort ERRSOCKETCLOSED + +PUB writeDataNonBlocking(user_handle, ptr, len) | handle, txptr +'' Writes bytes to the socket +'' Will not write anything unless your data fits in the buffer +'' Non blocking (returns RETBUFFERFULL if can't fit data) + + _handleConvert(user_handle, @handle) + + if (txbuffer_mask[handle] - ((tx_head[handle] - tx_tail[handle]) & txbuffer_mask[handle])) < len + return RETBUFFERFULL + + txptr := tx_bufferptr[handle] + + if (len + tx_head[handle]) > txbuffer_length[handle] + bytemove(txptr + tx_head[handle], ptr, txbuffer_length[handle] - tx_head[handle]) + bytemove(txptr, ptr + (txbuffer_length[handle] - tx_head[handle]), len - (txbuffer_length[handle] - tx_head[handle])) + else + bytemove(txptr + tx_head[handle], ptr, len) + + tx_head[handle] := (tx_head[handle] + len) & txbuffer_mask[handle] + + return len + +PUB writeData(user_handle, ptr, len) | handle +'' Writes data to the specified socket +'' Will block until all data is queued to be sent + + _handleConvert(user_handle, @handle) + + repeat while len > txbuffer_mask[handle] + repeat while writeDataNonBlocking(user_handle, ptr, txbuffer_mask[handle]) < 0 + ifnot isConnected(user_handle) + abort ERRSOCKETCLOSED + len -= txbuffer_mask[handle] + ptr += txbuffer_mask[handle] + + repeat while writeDataNonBlocking(user_handle, ptr, len) < 0 + ifnot isConnected(user_handle) + abort ERRSOCKETCLOSED + +PUB writeByteNonBlocking(user_handle, txbyte) | handle, ptr +'' Writes a byte to the specified socket +'' Will not block (returns RETBUFFERFULL if no buffer space available) + + _handleConvert(user_handle, @handle) + + ifnot (tx_tail[handle] <> (tx_head[handle] + 1) & txbuffer_mask[handle]) + return RETBUFFERFULL + + ptr := tx_bufferptr[handle] + BYTE[ptr][tx_head[handle]] := txbyte + tx_head[handle] := (tx_head[handle] + 1) & txbuffer_mask[handle] + + return txbyte + +PUB writeByte(user_handle, txbyte) | handle +'' Write a byte to the specified socket +'' Will block until space is available for byte to be sent + + _handleConvert(user_handle, @handle) + + repeat while writeByteNonBlocking(user_handle, txbyte) < 0 + ifnot isConnected(user_handle) + abort ERRSOCKETCLOSED + +PUB resetBuffers(user_handle) | handle +'' Resets send/receive buffers for the specified socket + + _handleConvert(user_handle, @handle) + + rx_tail[handle] := rx_head[handle] + tx_head[handle] := tx_tail[handle] + +PUB flush(user_handle) | handle +'' Flushes the send buffer (waits till the buffer is empty) +'' Will block until all tx data is sent + + _handleConvert(user_handle, @handle) + + repeat while isConnected(user_handle) AND tx_tail[handle] <> tx_head[handle] + +PUB getSocketState(user_handle) | handle +'' Gets the socket state (internal state numbers) +'' You can include driver_socket in any object and use the S... state constants for comparison + + _handleConvert(user_handle, @handle) + + return bConState[handle] + +PUB getReceiveBufferCount(user_handle) | handle +'' Returns the number of bytes in the receive buffer + + _handleConvert(user_handle, @handle) + + return (rx_head[handle] - rx_tail[handle]) & rxbuffer_mask[handle] + +CON + '****************************************************************** + '* TCP Flags + '****************************************************************** + TCP_FIN = 1 + TCP_SYN = 2 + TCP_RST = 4 + TCP_PSH = 8 + TCP_ACK = 16 + TCP_URG = 32 + TCP_ECE = 64 + TCP_CWR = 128 + '****************************************************************** + '* Ethernet Header Layout + '****************************************************************** + enetpacketDest0 = $00 'destination mac address + enetpacketDest1 = $01 + enetpacketDest2 = $02 + enetpacketDest3 = $03 + enetpacketDest4 = $04 + enetpacketDest5 = $05 + enetpacketSrc0 = $06 'source mac address + enetpacketSrc1 = $07 + enetpacketSrc2 = $08 + enetpacketSrc3 = $09 + enetpacketSrc4 = $0A + enetpacketSrc5 = $0B + enetpacketType0 = $0C 'type/length field + enetpacketType1 = $0D + enetpacketData = $0E 'IP data area begins here + '****************************************************************** + '* ARP Layout + '****************************************************************** + arp_hwtype = $0E + arp_prtype = $10 + arp_hwlen = $12 + arp_prlen = $13 + arp_op = $14 + arp_shaddr = $16 'arp source mac address + arp_sipaddr = $1C 'arp source ip address + arp_thaddr = $20 'arp target mac address + arp_tipaddr = $26 'arp target ip address + '****************************************************************** + '* IP Header Layout + '****************************************************************** + ip_vers_len = $0E 'IP version and header length 1a19 + ip_tos = $0F 'IP type of service + ip_pktlen = $10 'packet length + ip_id = $12 'datagram id + ip_frag_offset = $14 'fragment offset + ip_ttl = $16 'time to live + ip_proto = $17 'protocol (ICMP=1, TCP=6, UDP=11) + ip_hdr_cksum = $18 'header checksum 1a23 + ip_srcaddr = $1A 'IP address of source + ip_destaddr = $1E 'IP addess of destination + ip_data = $22 'IP data area + '****************************************************************** + '* TCP Header Layout + '****************************************************************** + TCP_srcport = $22 'TCP source port + TCP_destport = $24 'TCP destination port + TCP_seqnum = $26 'sequence number + TCP_acknum = $2A 'acknowledgement number + TCP_hdrlen = $2E '4-bit header len (upper 4 bits) + TCP_hdrflags = $2F 'TCP flags + TCP_window = $30 'window size + TCP_cksum = $32 'TCP checksum + TCP_urgentptr = $34 'urgent pointer + TCP_data = $36 'option/data + '****************************************************************** + '* IP Protocol Types + '****************************************************************** + PROT_ICMP = $01 + PROT_TCP = $06 + PROT_UDP = $11 + '****************************************************************** + '* ICMP Header + '****************************************************************** + ICMP_type = ip_data + ICMP_code = ICMP_type+1 + ICMP_cksum = ICMP_code+1 + ICMP_id = ICMP_cksum+2 + ICMP_seqnum = ICMP_id+2 + ICMP_data = ICMP_seqnum+2 + '****************************************************************** + '* UDP Header + '****************************************************************** + UDP_srcport = ip_data + UDP_destport = UDP_srcport+2 + UDP_len = UDP_destport+2 + UDP_cksum = UDP_len+2 + UDP_data = UDP_cksum+2 + '****************************************************************** + '* DHCP Message + '****************************************************************** + DHCP_op = UDP_data + DHCP_htype = DHCP_op+1 + DHCP_hlen = DHCP_htype+1 + DHCP_hops = DHCP_hlen+1 + DHCP_xid = DHCP_hops+1 + DHCP_secs = DHCP_xid+4 + DHCP_flags = DHCP_secs+2 + DHCP_ciaddr = DHCP_flags+2 + DHCP_yiaddr = DHCP_ciaddr+4 + DHCP_siaddr = DHCP_yiaddr+4 + DHCP_giaddr = DHCP_siaddr+4 + DHCP_chaddr = DHCP_giaddr+4 + DHCP_sname = DHCP_chaddr+16 + DHCP_file = DHCP_sname+64 + DHCP_options = DHCP_file+128 + DHCP_message_end = DHCP_options+312 diff --git a/system/administra/admnet/admnet-udp.spin b/system/administra/admnet/admnet-udp.spin new file mode 100644 index 0000000..9f4faf9 --- /dev/null +++ b/system/administra/admnet/admnet-udp.spin @@ -0,0 +1,1771 @@ +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle & Jörg Deckert │ +│ Copyright (c) 2010 Ingo Kripahle, 2013 Jörg Deckert │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : TriOS +Name : Administra-Flash (LAN) +Chip : Administra +Typ : Flash +Version : 00 +Subversion : 01 + +Funktion : Diese Codeversion basiert auf admflash.spin und wird um einen LAN-Treiber erweitert. + Die Soundfunktionen wurden aus Platzgründen beschränkt (kleiner HSS-Puffer, kein WAV). + + Dieser Code wird von Administra nach einem Reset aus dem EEProm in den hRAM kopiert + und gestartet. Im Gegensatz zu Bellatrix und Regnatix, die einen Loader aus dem EEProm + laden und entsprechende Systemdateien vom SD-Cardlaufwerk booten, also im + wesentlichen vor dem Bootvorgang keine weiter Funktionalität als die Ladeprozedur + besitzen, muß das EEProm-Bios von Administra mindestens die Funktionalität des + SD-Cardlaufwerkes zur Verfügung stellen können. Es erscheint deshalb sinnvoll, dieses + BIOS gleich mit einem ausgewogenen Funktionsumfang auszustatten, welcher alle Funktionen + für das System bietet. Durch eine Bootoption kann dieses BIOS aber zur Laufzeit + ausgetauscht werden, um das Funktionssetup an konkrete Anforderungen anzupassen. + + Chip-Managment-Funktionen + - Bootfunktion für Administra + - Abfrage und Verwaltung des aktiven Soundsystems + - Abfrage Version und Spezifikation + + SD-Funktionen: + - FAT32 oder FAT16 + - Partitionen bis 1TB und Dateien bis 2GB + - Verzeichnisse + - Verwaltung aller Dateiattribute + - DIR-Marker System + - Verwaltung eines Systemordners + - Achtung: Keine Verwaltung von mehreren geöffneten Dateien! + + HSS-Funktionen: + - 4-Kanal Tracker Engine + - 2-Kanal Sound FX Synthesizer + - 1-Kanal 1Bit ADPCM Sample Engine + + RTC-Funktionen: + - Datum, Uhrzeit auslesen + - Datum, Uhrzeit schreiben + - NVRAM auslesen + - NVRAM schreiben + - Wartefunktionen + + LAN-Funktionen: + - Ethernet-Port mit Daten aus NVRAM oder SD-Dard initialisieren + - ein- und ausgehende Verbindungen öffnen + - Daten übertragen + +Komponenten : HSS 1.2 Andrew Arsenault Lizenz unklar + FATEngine 01/18/2009 Kwabena W. Agyeman MIT Lizenz + RTCEngine 11/22/2009 Kwabena W. Agyeman MIT Lizenz + PropTCP 12/08/2009 Harrison Pham MIT Lizenz + +COG's : MANAGMENT 1 COG + FAT/RTC 1 COG + HSS 2 COG's + NET 2 COG + ------------------- + 6 COGs + +Logbuch : + +14-11-2008-dr235 - erste version erstellt +13-03-2009-dr235 - sd_eof eingefügt +25-01-2009-dr235 - komfortableres interface für hss-player eingefügt +19-03-2009-dr235 - seek, ftime, fattrib und fsize eingefügt +22-08-2009-dr235 - getcogs eingefügt +09-01-2010-dr235 - fehler in sfx_fire korrigiert +10-01-2010-dr235 - fehler in sdw_stop - hss wurde nicht wieder gestartet +15-03-2010-dr235 - start trios +21-03-2010-dr235 - screeninterface entfernt +24-03-2010-dr235 - start einbindung fatengine + - per flashcli laufen schon die ersten kommandos +25-03-2010-dr235 - umstellung fatengine auf fehlernummern +27-03-2010-dr235 - ich hab geburtstag :) + - test mount ok (fat16 + 32, div. sd-cards, fehlermeldungen) + - sd_volname eingefügt + test + - sd_checkmounted/sd_checkused/sd_checkfree eingefügt + test + - sd_checkopen eingefügt (test später) +28-03-2010-dr235 - fehler in der anbindung sd_open --> openFile: der modus + wurde als 0-term-string übergeben! änderung in einen normalen + 1-zeichen-parameter +02-04-2010-dr235 - sd_putblk/sd_getblk eingefügt und getestet +03-04-2010-dr235 - sd_opendit, sd_nextfile, sd_fattrib umgearbeitet und getestet +04-04-2010-dr235 - sd_newdir, sd_del, sd_rename eingefügt und getestet + - test sd_seek ok + - sd_chattrib, sd_chdir eingefügt und getestet + - mgr_getver, mgr_getspec, mgr_getcogs eingefügt + getestet + - mgr_aload eingefügt und getestet + - administra hat jetzt einen bootloader! :) +08-04-2010-dr235 - erster test dir-marker-system +12-04-2010-dr235 - neues soundsystem für wav eingebunden +16-04-2010-dr235 - komplexfehler im wav-system aufgelöst +21-04-2010-dr235 - pausen-modus positionsabfrage im wav-player eingefügt & getestet +29-04-2010-dr235 - wav-player: verwendung der dateigröße statt dem headerwert, da einige + programme definitiv den headerwert falsch setzen! +09-06-2010-dr085 - frida hat den fehler gefunden, welcher eine korrekte funktion der fatengine + nach einem bootvorgang von administra verhinderte :) +13-06-2010-dr235 - fehler in sd_volname korrigiert + - free/used auf fast umgestellt +18-06-2010-dr085 - fehler bei der businitialisierung: beim systemstart wurde ein kurzer impuls + auf der hs-leitung erzeugt, wodurch ein buszyklus verloren ging (symptom: + flashte man admin, so lief das system erst nach einem reset an) + - fatengine: endgültige beseitigung der feherhaften volname-abfrage +27-06-2010-dr085 - automount nach boot +19-07-2010-dr235 - booten eines alternativen administra-codes: befindet sich auf der karte + in der root eine datei "adm.sys", so wird diese datei automatisch in + administra geladen +18-09-2010-dr235 - funktion zur abfrage von eof eingefügt +29-10-2010-dr235 - grundlegende routinen für den plexbus eingefügt +03-12-2010-stepha - RTC Datums- und Zeit Funktionen +04-12-2010-stepha - NVRAM Funktionen +17-04-2013-dr235 - konstanten für administra-funktionen komplett ausgelagert +22-12-2013-joergd - LAN Funktionen + +Kommandoliste : + +Notizen : + + +}} + + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + + +' +---------- +' | +------- system +' | | +---- version (änderungen) +' | | | +- subversion (hinzufügungen) +CHIP_VER = $00_01_01_02 + +CHIP_SPEC = gc#A_FAT|gc#A_LDR|gc#A_HSS|gc#A_COM|gc#A_LAN + +' +' hbeat --------+ +' clk -------+| +' /wr ------+|| +' /hs -----+||| +------------------------- /cs +' |||| |+------------------------ adm-p22 +' |||| ||+----------------------- adm-p21 (io) +' |||| |||+---------------------- adm-p20 (rx) +' |||| ||||+--------------------- adm-p19 (tx) +' |||| ||||| +------+ d0..d7 +' |||| ||||| | | +DB_IN = %00001001_00100000_00000000_00000000 'dira-wert für datenbuseingabe +DB_OUT = %00001001_00100000_00000000_11111111 'dira-wert für datenbusausgabe + +M1 = %00000010_00000000_00000000_00000000 'busclk=1? & /prop1=0? +M2 = %00000010_10000000_00000000_00000000 'maske: busclk & /cs (/prop1) + +M3 = %00000000_00000000_00000000_00000000 'busclk=0? +M4 = %00000010_00000000_00000000_00000000 'maske: busclk + +LED_OPEN = gc#HBEAT 'led-pin für anzeige "dateioperation" +SD_BASE = gc#A_SDD0 'baspin cardreader +CNT_HBEAT = 5_000_0000 'blinkgeschw. front-led + +MPLEN = 3000 'größe des hss-musikpuffers + +'Netzwerk-Puffergrößen (müssen Vielfaches von 2 sein!) +rxlen = 2048 +txlen = 128 + +'index für dmarker +#0, RMARKER 'root + SMARKER 'system + UMARKER 'programmverzeichnis + AMARKER + BMARKER + CMARKER + +CON 'Signaldefinitionen -------------------------------------------------------------------------- + +'signaldefinitionen administra (todo: nach glob-con.spin auslagern!!!) + +#14, A_NETCS,A_NETSCK,A_NETSI,A_NETSO 'Pins zum ENC28J60 + +CON 'NVRAM Konstanten -------------------------------------------------------------------------- + +' todo: nach glob-con.spin auslagern!!! + +#4, NVRAM_IPADDR +#8, NVRAM_IPMASK +#12, NVRAM_IPGW +#16, NVRAM_IPDNS +#20, NVRAM_IPBOOT +#24, NVRAM_HIVE ' 4 Bytes + +OBJ + sdfat : "adm-fat" 'fatengine + hss : "adm-hss" 'hydra-sound-system + rtc : "adm-rtc" 'RTC-Engine + com : "adm-com" 'serielle schnittstelle + sock : "driver_socket" 'LAN + gc : "glob-con" 'globale konstanten + num : "glob-numbers" 'Number Engine + +DAT + + strNVRAMFile byte "nvram.sav",0 'contains the 56 bytes of NVRAM, if RTC is not available + +VAR + + long dmarker[6] 'speicher für dir-marker + byte tbuf[20] 'stringpuffer + byte tbuf2[20] + byte sfxdat[16 * 32] 'sfx-slotpuffer + byte fl_syssnd '1 = systemtöne an + byte st_sound '0 = aus, 1 = hss + long com_baud + byte lan_started 'LAN gestartet? + long sockhandle[sock#sNumSockets] 'Handle für mit sock.connect/sock.listen erstellten Socket + byte bufidx[sock#sNumSockets] 'zum Handle-Index gehörender Puffer-abschnitt + '(zum Socket mit dem Handle 2 gehört der Pufferabschnitt aus bufidx[2]) + byte bufrx[rxlen*sock#sNumSockets] 'LAN Empfangspuffer + byte buftx[txlen*sock#sNumSockets] 'LAN Sendepuffer + +CON ''------------------------------------------------- ADMINISTRA + +PUB main | cmd,err 'chip: kommandointerpreter +''funktionsgruppe : chip +''funktion : kommandointerpreter +''eingabe : - +''ausgabe : - + + init_chip 'bus/vga/keyboard/maus initialisieren + repeat + cmd := bus_getchar 'kommandocode empfangen + err := 0 + case cmd + 0: !outa[LED_OPEN] 'led blinken + +' ---------------------------------------------- SD-FUNKTIONEN + gc#a_sdMount: sd_mount("M") 'sd-card mounten ' + gc#a_sdOpenDir: sd_opendir 'direktory öffnen + gc#a_sdNextFile: sd_nextfile 'verzeichniseintrag lesen + gc#a_sdOpen: sd_open 'datei öffnen + gc#a_sdClose: sd_close 'datei schließen + gc#a_sdGetC: sd_getc 'zeichen lesen + gc#a_sdPutC: sd_putc 'zeichen schreiben + gc#a_sdGetBlk: sd_getblk 'block lesen + gc#a_sdPutBlk: sd_putblk 'block schreiben + gc#a_sdSeek: sd_seek 'zeiger in datei positionieren + gc#a_sdFAttrib: sd_fattrib 'dateiattribute übergeben + gc#a_sdVolname: sd_volname 'volumelabel abfragen + gc#a_sdCheckMounted: sd_checkmounted 'test ob volume gemounted ist + gc#a_sdCheckOpen: sd_checkopen 'test ob eine datei geöffnet ist + gc#a_sdCheckUsed: sd_checkused 'test wie viele sektoren benutzt sind + gc#a_sdCheckFree: sd_checkfree 'test wie viele sektoren frei sind + gc#a_sdNewFile: sd_newfile 'neue datei erzeugen + gc#a_sdNewDir: sd_newdir 'neues verzeichnis wird erzeugt + gc#a_sdDel: sd_del 'verzeichnis oder datei löschen + gc#a_sdRename: sd_rename 'verzeichnis oder datei umbenennen + gc#a_sdChAttrib: sd_chattrib 'attribute ändern + gc#a_sdChDir: sd_chdir 'verzeichnis wechseln + gc#a_sdFormat: sd_format 'medium formatieren + gc#a_sdUnmount: sd_unmount 'medium abmelden + gc#a_sdDmAct: sd_dmact 'dir-marker aktivieren + gc#a_sdDmSet: sd_dmset 'dir-marker setzen + gc#a_sdDmGet: sd_dmget 'dir-marker status abfragen + gc#a_sdDmClr: sd_dmclr 'dir-marker löschen + gc#a_sdDmPut: sd_dmput 'dir-marker status setzen + gc#a_sdEOF: sd_eof 'eof abfragen + +' ---------------------------------------------- COM-FUNKTIONEN + gc#a_comInit: com_init + gc#a_comTx: com_tx + gc#a_comRx: com_rx + +' ---------------------------------------------- RTC-FUNKTIONEN + gc#a_rtcGetSeconds: rtc_getSeconds 'Returns the current second (0 - 59) from the real time clock. + gc#a_rtcGetMinutes: rtc_getMinutes 'Returns the current minute (0 - 59) from the real time clock. + gc#a_rtcGetHours: rtc_getHours 'Returns the current hour (0 - 23) from the real time clock. + gc#a_rtcGetDay: rtc_getDay 'Returns the current day (1 - 7) from the real time clock. + gc#a_rtcGetDate: rtc_getDate 'Returns the current date (1 - 31) from the real time clock. + gc#a_rtcGetMonth: rtc_getMonth 'Returns the current month (1 - 12) from the real time clock. + gc#a_rtcGetYear: rtc_getYear 'Returns the current year (2000 - 2099) from the real time clock. + gc#a_rtcSetSeconds: rtc_setSeconds 'Sets the current real time clock seconds. Seconds - Number to set the seconds to between 0 - 59. + gc#a_rtcSetMinutes: rtc_setMinutes 'Sets the current real time clock minutes. Minutes - Number to set the minutes to between 0 - 59. + gc#a_rtcSetHours: rtc_setHours 'Sets the current real time clock hours. Hours - Number to set the hours to between 0 - 23. + gc#a_rtcSetDay: rtc_setDay 'Sets the current real time clock day. Day - Number to set the day to between 1 - 7. + gc#a_rtcSetDate: rtc_setDate 'Sets the current real time clock date. Date - Number to set the date to between 1 - 31. + gc#a_rtcSetMonth: rtc_setMonth 'Sets the current real time clock month. Month - Number to set the month to between 1 - 12. + gc#a_rtcSetYear: rtc_setYear 'Sets the current real time clock year. Year - Number to set the year to between 2000 - 2099. + gc#a_rtcSetNVSRAM: rtc_setNVSRAM 'Sets the NVSRAM to the selected value (0 - 255) at the index (0 - 55). + gc#a_rtcGetNVSRAM: rtc_getNVSRAM 'Gets the selected NVSRAM value at the index (0 - 55). + gc#a_rtcPauseForSec: rtc_pauseForSeconds 'Pauses execution for a number of seconds. Returns a puesdo random value derived from the current clock frequency and the time when called. Number - Number of seconds to pause for between 0 and 2,147,483,647. + gc#a_rtcPauseForMSec: rtc_pauseForMilliseconds 'Pauses execution for a number of milliseconds. Returns a puesdo random value derived from the current clock frequency and the time when called. Number - Number of milliseconds to pause for between 0 and 2,147,483,647. + gc#a_rtcTest: rtc_test 'Test if RTC Chip is available + +' ---------------------------------------------- LAN-FUNKTIONEN + gc#a_lanStart: lan_start 'Start Network + gc#a_lanStop:lan_stop 'Stop Network + gc#a_lanConnect: lan_connect 'ausgehende TCP-Verbindung öffnen + gc#a_lanListen: lan_listen 'auf eingehende TCP-Verbindung lauschen + gc#a_lanWaitConnTimeout: lan_waitconntimeout 'bestimmte Zeit auf Verbindung warten + gc#a_lanClose: lan_close 'TCP-Verbindung schließen + gc#a_lanRXTime: lan_rxtime 'bestimmte Zeit warten auf Byte aus Empfangspuffer + gc#a_lanRXData: lan_rxdata 'Daten aus Empfangspuffer lesen + gc#a_lanTXData: lan_txdata 'Daten senden + gc#a_lanRXByte: lan_rxbyte 'wenn vorhanden, Byte aus Empfangspuffer lesen + gc#a_lanIsConnected: lan_isconnected 'TRUE, wenn Socket verbunden, sonst FALSE + +' ---------------------------------------------- CHIP-MANAGMENT + gc#a_mgrSetSound: mgr_setsound 'soundsubsysteme verwalten + gc#a_mgrGetSpec: mgr_getspec 'spezifikation abfragen + gc#a_mgrSetSysSound: mgr_setsyssound 'systemsound ein/ausschalten + gc#a_mgrGetSoundSys: mgr_getsoundsys 'abfrage welches soundsystem aktiv ist + gc#a_mgrALoad: mgr_aload 'neuen code booten + gc#a_mgrGetCogs: mgr_getcogs 'freie cogs abfragen + gc#a_mgrGetVer: mgr_getver 'codeversion abfragen + gc#a_mgrReboot: reboot 'neu starten + +' ---------------------------------------------- HSS-FUNKTIONEN + gc#a_hssLoad: hss_load 'hss-datei in puffer laden (in admnet not supported) + gc#a_hssPlay: 'play (in admnet not supported) + gc#a_hssStop: 'stop (in admnet not supported) + gc#a_hssPause: hss.hmus_pause 'pause + gc#a_hssPeek: hss_peek 'register lesen + gc#a_hssIntReg: hss_intreg 'interfaceregister auslesen + gc#a_hssVol: hss_vol 'lautstärke setzen + gc#a_sfxFire: sfx_fire 'sfx abspielen + gc#a_sfxSetSlot: sfx_setslot 'sfx-slot setzen + gc#a_sfxKeyOff: sfx_keyoff + gc#a_sfxStop: sfx_stop + +' ---------------------------------------------- DEBUG-FUNKTIONEN + 255: mgr_debug 'debugfunktion + +PRI init_chip | err,i,j 'chip: initialisierung des administra-chips +''funktionsgruppe : chip +''funktion : - initialisierung des businterface +''eingabe : - +''ausgabe : - + + 'businterface initialisieren + outa[gc#bus_hs] := 1 'handshake inaktiv ,frida + dira := db_in 'datenbus auf eingabe schalten ,frida + + 'grundzustand herstellen (hss aktiv + systemklänge an) + + 'hss starten + hss.start 'soundsystem starten + st_sound := 1 'hss aktiviert + fl_syssnd := 1 'systemsound an + + 'sd-card starten + clr_dmarker 'dir-marker löschen + sdfat.FATEngine + repeat + waitcnt(cnt + clkfreq/10) + until sd_mount("B") == 0 + 'err := sd_mount("B") + 'siglow(err) + + 'RTC initialisieren + rtc.setSQWOUTFrequency(3) 'RTC Uhrenquarzt Frequenz wählen + rtc.setSQWOUTState(0) 'RT Zähler ein + + 'adm-code booten? + ifnot \sdfat.openFile(string("adm.sys"), "R") 'test ob adm.sys vorhanden ist + \sdfat.bootPartition(string("adm.sys"), ".") 'neuen code booten + + 'serielle schnittstelle starten + com_baud := 115200 + com.start(gc#SER_RX,gc#SER_TX,0,com_baud) 'start the default serial interface + + 'LAN + lan_started := false 'LAN noch nicht gestartet + +PRI bus_putchar(zeichen) 'chip: ein byte über bus ausgeben +''funktionsgruppe : chip +''funktion : senderoutine für ein byte zu regnatix über den systembus +''eingabe : byte zeichen +''ausgabe : - + + waitpeq(M1,M2,0) 'busclk=1? & /prop1=0? + dira := db_out 'datenbus auf ausgabe stellen + outa[7..0] := zeichen 'daten ausgeben + outa[gc#bus_hs] := 0 'daten gültig + waitpeq(M3,M4,0) 'busclk=0? + outa[gc#bus_hs] := 1 'daten ungültig + dira := db_in 'bus freigeben + +PRI bus_getchar : zeichen 'chip: ein byte über bus empfangen +''funktionsgruppe : chip +''funktion : emfangsroutine für ein byte von regnatix über den systembus +''eingabe : - +''ausgabe : byte zeichen + + waitpeq(M1,M2,0) 'busclk=1? & /prop1=0? + zeichen := ina[7..0] 'daten einlesen + outa[gc#bus_hs] := 0 'daten quittieren + outa[gc#bus_hs] := 1 + waitpeq(M3,M4,0) 'busclk=0? + +PRI sighigh(err) 'chip: schneller hbeat | fehlersound +''funktionsgruppe : chip +''funktion : schneller hbeat | fehlersound +''eingabe : - +''ausgabe : - + + if fl_syssnd == 1 + if err == 0 + hss.sfx_play(1, @SoundFX3) 'Heartbeat High + else + hss.sfx_play(1, @SoundFX7) 'Error + +PRI siglow(err) 'chip: langsamer hbeat | fehlersound +''funktionsgruppe : chip +''funktion : langsamer hbeat | fehlersound +''eingabe : - +''ausgabe : - + + if fl_syssnd == 1 + if err == 0 + hss.sfx_play(1, @SoundFX4) 'Heartbeat High + else + hss.sfx_play(1, @SoundFX7) 'Error + + +PRI clr_dmarker| i 'chip: dmarker-tabelle löschen +''funktionsgruppe : chip +''funktion : dmarker-tabelle löschen +''eingabe : - +''ausgabe : - + + i := 0 + repeat 6 'alle dir-marker löschen + dmarker[i++] := TRUE + +CON ''------------------------------------------------- SUBPROTOKOLL-FUNKTIONEN + +PRI sub_getstr | i,len 'sub: string einlesen +''funktionsgruppe : sub +''funktion : subprotokoll um einen string von regnatix zu empfangen und im +'' : textpuffer (tbuf) zu speichern +''eingabe : - +''ausgabe : - +''busprotokoll : [get.len][get.byte(1)]..[get.byte(len)] +'' : len - länge des dateinamens + + repeat i from 0 to 19 'puffer löschen und kopieren + tbuf2[i] := tbuf[i] + tbuf[i] := 0 + len := bus_getchar 'längenbyte name empfangen + repeat i from 0 to len - 1 'dateiname einlesen + tbuf[i] := bus_getchar + +PRI sub_putstr(strptr)|len,i 'sub: string senden +''funktionsgruppe : sub +''funktion : subprotokoll um einen string an regnatix zu senden +''eingabe : strptr - zeiger auf einen string (0-term) +''ausgabe : - +''busprotokoll : [put.len][put.byte(1)]..[put.byte(len)] +'' : len - länge des dateinamens + + len := strsize(strptr) + bus_putchar(len) + repeat i from 0 to len - 1 'string übertragen + bus_putchar(byte[strptr][i]) + +PRI sub_putword(wert) 'sub: long senden +''funktionsgruppe : sub +''funktion : subprotokoll um einen 16bit-wert an regnatix zu senden +''eingabe : 16bit wert der gesendet werden soll +''ausgabe : - +''busprotokoll : [put.byte1][put.byte2] +'' : [ hsb ][ lsb ] + + bus_putchar(wert >> 8) + bus_putchar(wert) + +PRI sub_getword:wert 'sub: long empfangen +''funktionsgruppe : sub +''funktion : subprotokoll um einen 16bit-wert von regnatix zu empfangen +''eingabe : - +''ausgabe : 16bit-wert der empfangen wurde +''busprotokoll : [get.byte1][get.byte2] +'' : [ hsb ][ lsb ] + + wert := wert + bus_getchar << 8 + wert := wert + bus_getchar + +PRI sub_putlong(wert) 'sub: long senden +''funktionsgruppe : sub +''funktion : subprotokoll um einen long-wert an regnatix zu senden +''eingabe : 32bit wert der gesendet werden soll +''ausgabe : - +''busprotokoll : [put.byte1][put.byte2][put.byte3][put.byte4] +'' : [ hsb ][ ][ ][ lsb ] + + bus_putchar(wert >> 24) '32bit wert senden hsb/lsb + bus_putchar(wert >> 16) + bus_putchar(wert >> 8) + bus_putchar(wert) + +PRI sub_getlong:wert 'sub: long empfangen +''funktionsgruppe : sub +''funktion : subprotokoll um einen long-wert von regnatix zu empfangen +''eingabe : - +''ausgabe : 32bit-wert der empfangen wurde +''busprotokoll : [get.byte1][get.byte2][get.byte3][get.byte4] +'' : [ hsb ][ ][ ][ lsb ] + + wert := bus_getchar << 24 '32 bit empfangen hsb/lsb + wert := wert + bus_getchar << 16 + wert := wert + bus_getchar << 8 + wert := wert + bus_getchar + + +CON ''------------------------------------------------- CHIP-MANAGMENT-FUNKTIONEN + +PRI mgr_setsound|sndstat 'cmgr: soundsubsysteme verwalten +''funktionsgruppe : cmgr +''funktion : soundsubsysteme an- bzw. abschalten +''eingabe : - +''ausgabe : - +''busprotokoll : [150][get.funktion][put.sndstat] +'' : funktion - 0: hss-engine abschalten +'' : 1: hss-engine anschalten +'' : sndstat - status/cognr startvorgang + + sndstat := 0 + case bus_getchar + 0: if st_sound == 1 + hss.hmus_stop + hss.sfx_stop(0) + hss.sfx_stop(1) + hss.stop + st_sound := 0 + + 1: if st_sound == 0 + sndstat := hss.start + st_sound := 1 + + bus_putchar(sndstat) + +PRI mgr_setsyssound 'cmgr: systemsound ein/ausschalten +''funktionsgruppe : cmgr +''funktion : systemklänge steuern +''eingabe : +''ausgabe : +''busprotokoll : [094][get.fl_syssnd] +'' : fl_syssnd - flag zur steuerung der systemsounds +'' : 0 - systemtöne aus +'' : 1 - systemtöne an + + fl_syssnd := bus_getchar + +PRI mgr_getsoundsys 'cmgr: abfrage welches soundsystem aktiv ist +''funktionsgruppe : cmgr +''funktion : abfrage welches soundsystem aktiv ist +''eingabe : +''ausgabe : +''busprotokoll : [095][put.st_sound] +'' : st_sound - status des soundsystems +'' : 0 - sound aus +'' : 1 - hss + + bus_putchar(st_sound) + +PRI mgr_aload | err 'cmgr: neuen administra-code booten +''funktionsgruppe : cmgr +''funktion : administra mit neuem code booten +''eingabe : +''ausgabe : +''busprotokoll : [096][sub_getstr.fn] +'' : fn - dateiname des neuen administra-codes + sub_getstr + err := \sdfat.bootPartition(@tbuf, ".") + sighigh(err) 'fehleranzeige + +PRI mgr_getcogs: cogs |i,c,cog[8] 'cmgr: abfragen wie viele cogs in benutzung sind +''funktionsgruppe : cmgr +''funktion : abfrage wie viele cogs in benutzung sind +''eingabe : - +''ausgabe : cogs - anzahl der cogs +''busprotokoll : [097][put.cogs] +'' : cogs - anzahl der belegten cogs + + cogs := i := 0 + repeat 'loads as many cogs as possible and stores their cog numbers + c := cog[i] := cognew(@entry, 0) + if c=>0 + i++ + while c => 0 + cogs := i + repeat 'unloads the cogs and updates the string + i-- + if i=>0 + cogstop(cog[i]) + while i=>0 + bus_putchar(cogs) + +PRI mgr_getver 'cmgr: abfrage der version +''funktionsgruppe : cmgr +''funktion : abfrage der version und spezifikation des chips +''eingabe : - +''ausgabe : cogs - anzahl der cogs +''busprotokoll : [098][sub_putlong.ver] +'' : ver - version +'' +---------- +'' | +------- system +'' | | +---- version (änderungen) +'' | | | +- subversion (hinzufügungen) +''version : $00_00_00_00 +'' + + sub_putlong(CHIP_VER) + +PRI mgr_getspec 'cmgr: abfrage der spezifikation des chips +''funktionsgruppe : cmgr +''funktion : abfrage der version und spezifikation des chips +''eingabe : - +''ausgabe : cogs - anzahl der cogs +''busprotokoll : [089][sub_putlong.spec] +'' : spec - spezifikation +'' +'' +---------- com +'' | +-------- i2c +'' | |+------- rtc +'' | ||+------ lan +'' | |||+----- sid +'' | ||||+---- wav +'' | |||||+--- hss +'' | ||||||+-- bootfähig +'' | |||||||+- dateisystem +''spezifikation : %00000000_00000000_00000000_01001111 + + sub_putlong(CHIP_SPEC) + +PRI mgr_debug 'cmgr: debug +' adresse der ersten variable senden + + sub_putlong(@dmarker) 'adresse erste variable als marker + +CON ''------------------------------------------------- SD-LAUFWERKS-FUNKTIONEN + +PRI sd_mount(mode) | err 'sdcard: sd-card mounten frida +''funktionsgruppe : sdcard +''funktion : eingelegtes volume mounten +''eingabe : - +''ausgabe : - +''busprotokoll : [001][put.error] +'' : error - fehlernummer entspr. list + + ifnot sdfat.checkPartitionMounted 'frida + err := \sdfat.mountPartition(0,0) 'karte mounten + siglow(err) + 'bus_putchar(err) 'fehlerstatus senden + if mode == "M" 'frida + bus_putchar(err) 'fehlerstatus senden + + ifnot err + dmarker[RMARKER] := sdfat.getDirCluster 'root-marker setzen + + err := \sdfat.changeDirectory(string("system")) + ifnot err + dmarker[SMARKER] := sdfat.getDirCluster 'system-marker setzen + + sdfat.setDirCluster(dmarker[RMARKER]) 'root-marker wieder aktivieren + hss.sfx_play(1, @SoundFX8) 'on-sound + else 'frida + bus_putchar(0) 'frida + +PRI sd_opendir | err 'sdcard: verzeichnis öffnen +''funktionsgruppe : sdcard +''funktion : verzeichnis öffnen +''eingabe : - +''ausgabe : - +''busprotokoll : [002] + + err := \sdfat.listReset + siglow(err) + +PRI sd_nextfile | strpt 'sdcard: nächsten eintrag aus verzeichnis holen +''funktionsgruppe : sdcard +''funktion : nächsten eintrag aus verzeichnis holen +''eingabe : - +''ausgabe : - +''busprotokoll : [003][put.status=0] +'' : [003][put.status=1][sub_putstr.fn] +'' : status - 1 = gültiger eintrag +'' : 0 = es folgt kein eintrag mehr +'' : fn - verzeichniseintrag string + + strpt := \sdfat.listName 'nächsten eintrag holen + if strpt 'status senden + bus_putchar(1) 'kein eintrag mehr + sub_putstr(strpt) + else + bus_putchar(0) 'gültiger eintrag folgt + +PRI sd_open | err,modus 'sdcard: datei öffnen +''funktionsgruppe : sdcard +''funktion : eine bestehende datei öffnen +''eingabe : - +''ausgabe : - +''busprotokoll : [004][get.modus][sub_getstr.fn][put.error] +'' : modus - "A" Append, "W" Write, "R" Read +'' : fn - name der datei +'' : error - fehlernummer entspr. list + + modus := bus_getchar 'modus empfangen + sub_getstr + err := \sdfat.openFile(@tbuf, modus) + sighigh(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + outa[LED_OPEN] := 1 + +PRI sd_close | err 'sdcard: datei schließen +''funktionsgruppe : sdcard +''funktion : die aktuell geöffnete datei schließen +''eingabe : - +''ausgabe : - +''busprotokoll : [005][put.error] +'' : error - fehlernummer entspr. list + + err := \sdfat.closeFile + siglow(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + outa[LED_OPEN] := 0 + +PRI sd_getc | n 'sdcard: zeichen aus datei lesen +''funktionsgruppe : sdcard +''funktion : zeichen aus datei lesen +''eingabe : - +''ausgabe : - +''busprotokoll : [006][put.char] +'' : char - gelesenes zeichen + + n := \sdfat.readCharacter + bus_putchar(n) + +PRI sd_putc 'sdcard: zeichen in datei schreiben +''funktionsgruppe : sdcard +''funktion : zeichen in datei schreiben +''eingabe : - +''ausgabe : - +''busprotokoll : [007][get.char] +'' : char - zu schreibendes zeichen + + \sdfat.writeCharacter(bus_getchar) + + +PRI sd_eof 'sdcard: eof abfragen +''funktionsgruppe : sdcard +''funktion : eof abfragen +''eingabe : - +''ausgabe : - +''busprotokoll : [030][put.eof] +'' : eof - eof-flag + + bus_putchar(sdfat.getEOF) + +PRI sd_getblk 'sdcard: block aus datei lesen +''funktionsgruppe : sdcard +''funktion : block aus datei lesen +''eingabe : - +''ausgabe : - +''busprotokoll : [008][sub_getlong.count][put.char(1)]..[put.char(count)] +'' : count - anzahl der zu lesenden zeichen +'' : char - gelesenes zeichen + + repeat sub_getlong + bus_putchar(\sdfat.readCharacter) + + +PRI sd_putblk 'sdcard: block in datei schreiben +''funktionsgruppe : sdcard +''funktion : block in datei schreiben +''eingabe : - +''ausgabe : - +''busprotokoll : [009][sub_getlong.count][put.char(1)]..[put.char(count)] +'' : count - anzahl der zu schreibenden zeichen +'' : char - zu schreibende zeichen + + repeat sub_getlong + \sdfat.writeCharacter(bus_getchar) + +PRI sd_seek | wert 'sdcard: zeiger in datei positionieren +''funktionsgruppe : sdcard +''funktion : zeiger in datei positionieren +''eingabe : - +''ausgabe : - +''busprotokoll : [010][sub_getlong.pos] +'' : pos - neue zeichenposition in der datei + + wert := sub_getlong + \sdfat.setCharacterPosition(wert) + +PRI sd_fattrib | anr,wert 'sdcard: dateiattribute übergeben +''funktionsgruppe : sdcard +''funktion : dateiattribute abfragen +''eingabe : - +''ausgabe : - +''busprotokoll : [011][get.anr][sub_putlong.wert] +'' : anr - 0 = Dateigröße +'' : 1 = Erstellungsdatum - Tag +'' : 2 = Erstellungsdatum - Monat +'' : 3 = Erstellungsdatum - Jahr +'' : 4 = Erstellungsdatum - Sekunden +'' : 5 = Erstellungsdatum - Minuten +'' : 6 = Erstellungsdatum - Stunden +'' : 7 = Zugriffsdatum - Tag +'' : 8 = Zugriffsdatum - Monat +'' : 9 = Zugriffsdatum - Jahr +'' : 10 = Änderungsdatum - Tag +'' : 11 = Änderungsdatum - Monat +'' : 12 = Änderungsdatum - Jahr +'' : 13 = Änderungsdatum - Sekunden +'' : 14 = Änderungsdatum - Minuten +'' : 15 = Änderungsdatum - Stunden +'' : 16 = Read-Only-Bit +'' : 17 = Hidden-Bit +'' : 18 = System-Bit +'' : 19 = Direktory +'' : 20 = Archiv-Bit +'' : wert - wert des abgefragten attributes + + anr := bus_getchar + case anr + 0: wert := \sdfat.listSize + 1: wert := \sdfat.listCreationDay + 2: wert := \sdfat.listCreationMonth + 3: wert := \sdfat.listCreationYear + 4: wert := \sdfat.listCreationSeconds + 5: wert := \sdfat.listCreationMinutes + 6: wert := \sdfat.listCreationHours + 7: wert := \sdfat.listAccessDay + 8: wert := \sdfat.listAccessMonth + 9: wert := \sdfat.listAccessYear + 10: wert := \sdfat.listModificationDay + 11: wert := \sdfat.listModificationMonth + 12: wert := \sdfat.listModificationYear + 13: wert := \sdfat.listModificationSeconds + 14: wert := \sdfat.listModificationMinutes + 15: wert := \sdfat.listModificationHours + 16: wert := \sdfat.listIsReadOnly + 17: wert := \sdfat.listIsHidden + 18: wert := \sdfat.listIsSystem + 19: wert := \sdfat.listIsDirectory + 20: wert := \sdfat.listIsArchive + sub_putlong(wert) + +PRI sd_volname 'sdcard: volumenlabel abfragen +''funktionsgruppe : sdcard +''funktion : name des volumes überragen +''eingabe : - +''ausgabe : - +''busprotokoll : [012][sub_putstr.volname] +'' : volname - name des volumes +'' : len - länge des folgenden strings + + sub_putstr(\sdfat.listVolumeLabel) 'label holen und senden + +PRI sd_checkmounted 'sdcard: test ob volume gemounted ist +''funktionsgruppe : sdcard +''funktion : test ob volume gemounted ist +''eingabe : - +''ausgabe : - +''busprotokoll : [013][put.flag] +'' : flag - 0 = unmounted, 1 mounted + + bus_putchar(\sdfat.checkPartitionMounted) + +PRI sd_checkopen 'sdcard: test ob eine datei geöffnet ist +''funktionsgruppe : sdcard +''funktion : test ob eine datei geöffnet ist +''eingabe : - +''ausgabe : - +''busprotokoll : [014][put.flag] +'' : flag - 0 = not open, 1 open + + bus_putchar(\sdfat.checkFileOpen) + +PRI sd_checkused 'sdcard: anzahl der benutzten sektoren senden +''funktionsgruppe : sdcard +''funktion : anzahl der benutzten sektoren senden +''eingabe : - +''ausgabe : - +''busprotokoll : [015][sub_putlong.used] +'' : used - anzahl der benutzten sektoren + + sub_putlong(\sdfat.checkUsedSectorCount("F")) + +PRI sd_checkfree 'sdcard: anzahl der freien sektoren senden +''funktionsgruppe : sdcard +''funktion : anzahl der freien sektoren senden +''eingabe : - +''ausgabe : - +''busprotokoll : [016][sub_putlong.free] +'' : free - anzahl der freien sektoren + + sub_putlong(\sdfat.checkFreeSectorCount("F")) + +PRI sd_newfile | err 'sdcard: eine neue datei erzeugen +''funktionsgruppe : sdcard +''funktion : eine neue datei erzeugen +''eingabe : - +''ausgabe : - +''busprotokoll : [017][sub_getstr.fn][put.error] +'' : fn - name der datei +'' : error - fehlernummer entspr. liste + + sub_getstr + err := \sdfat.newFile(@tbuf) + sighigh(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + +PRI sd_newdir | err 'sdcard: ein neues verzeichnis erzeugen +''funktionsgruppe : sdcard +''funktion : ein neues verzeichnis erzeugen +''eingabe : - +''ausgabe : - +''busprotokoll : [018][sub_getstr.fn][put.error] +'' : fn - name des verzeichnisses +'' : error - fehlernummer entspr. liste + + sub_getstr + err := \sdfat.newDirectory(@tbuf) + sighigh(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + +PRI sd_del | err 'sdcard: eine datei oder ein verzeichnis löschen +''funktionsgruppe : sdcard +''funktion : eine datei oder ein verzeichnis löschen +''eingabe : - +''ausgabe : - +''busprotokoll : [019][sub_getstr.fn][put.error] +'' : fn - name des verzeichnisses oder der datei +'' : error - fehlernummer entspr. liste + + sub_getstr + err := \sdfat.deleteEntry(@tbuf) + sighigh(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + +PRI sd_rename | err 'sdcard: datei oder verzeichnis umbenennen +''funktionsgruppe : sdcard +''funktion : datei oder verzeichnis umbenennen +''eingabe : - +''ausgabe : - +''busprotokoll : [020][sub_getstr.fn1][sub_getstr.fn2][put.error] +'' : fn1 - alter name +'' : fn2 - neuer name +'' : error - fehlernummer entspr. liste + + sub_getstr 'fn1 + sub_getstr 'fn2 + err := \sdfat.renameEntry(@tbuf2,@tbuf) + sighigh(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + +PRI sd_chattrib | err 'sdcard: attribute ändern +''funktionsgruppe : sdcard +''funktion : attribute einer datei oder eines verzeichnisses ändern +''eingabe : - +''ausgabe : - +''busprotokoll : [021][sub_getstr.fn][sub_getstr.attrib][put.error] +'' : fn - dateiname +'' : attrib - string mit attributen +'' : error - fehlernummer entspr. liste + + sub_getstr + sub_getstr + err := \sdfat.changeAttributes(@tbuf2,@tbuf) + siglow(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + +PRI sd_chdir | err 'sdcard: verzeichnis wechseln +''funktionsgruppe : sdcard +''funktion : verzeichnis wechseln +''eingabe : - +''ausgabe : - +''busprotokoll : [022][sub_getstr.fn][put.error] +'' : fn - name des verzeichnisses +'' : error - fehlernummer entspr. list + sub_getstr + err := \sdfat.changeDirectory(@tbuf) + siglow(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + +PRI sd_format | err 'sdcard: medium formatieren +''funktionsgruppe : sdcard +''funktion : medium formatieren +''eingabe : - +''ausgabe : - +''busprotokoll : [023][sub_getstr.vlabel][put.error] +'' : vlabel - volumelabel +'' : error - fehlernummer entspr. list + + sub_getstr + err := \sdfat.formatPartition(0,@tbuf,0) + siglow(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + +PRI sd_unmount | err 'sdcard: medium abmelden +''funktionsgruppe : sdcard +''funktion : medium abmelden +''eingabe : - +''ausgabe : - +''busprotokoll : [024][put.error] +'' : error - fehlernummer entspr. list + + err := \sdfat.unmountPartition + siglow(err) 'fehleranzeige + bus_putchar(err) 'ergebnis der operation senden + ifnot err + clr_dmarker + hss.sfx_play(1, @SoundFX9) 'off-sound + +PRI sd_dmact|markernr 'sdcard: einen dir-marker aktivieren +''funktionsgruppe : sdcard +''funktion : ein ausgewählter dir-marker wird aktiviert +''eingabe : - +''ausgabe : - +''busprotokoll : [025][get.dmarker][put.error] +'' : dmarker - dir-marker +'' : error - fehlernummer entspr. list + markernr := bus_getchar + ifnot dmarker[markernr] == TRUE + sdfat.setDirCluster(dmarker[markernr]) + bus_putchar(sdfat#err_noError) + else + bus_putchar(sdfat#err_noError) + + +PRI sd_dmset|markernr 'sdcard: einen dir-marker setzen +''funktionsgruppe : sdcard +''funktion : ein ausgewählter dir-marker mit dem aktuellen verzeichnis setzen +''eingabe : - +''ausgabe : - +''busprotokoll : [026][get.dmarker] +'' : dmarker - dir-marker + + markernr := bus_getchar + dmarker[markernr] := sdfat.getDirCluster + +PRI sd_dmget|markernr 'sdcard: einen dir-marker abfragen +''funktionsgruppe : sdcard +''funktion : den status eines ausgewählter dir-marker abfragen +''eingabe : - +''ausgabe : - +''busprotokoll : [027][get.dmarker][sub_putlong.dmstatus] +'' : dmarker - dir-marker +'' : dmstatus - status des markers + + markernr := bus_getchar + sub_putlong(dmarker[markernr]) + +PRI sd_dmput|markernr 'sdcard: einen dir-marker übertragen +''funktionsgruppe : sdcard +''funktion : den status eines ausgewählter dir-marker übertragen +''eingabe : - +''ausgabe : - +''busprotokoll : [029][get.dmarker][sub_getlong.dmstatus] +'' : dmarker - dir-marker +'' : dmstatus - status des markers + + markernr := bus_getchar + dmarker[markernr] := sub_getlong + +PRI sd_dmclr|markernr 'sdcard: einen dir-marker löschen +''funktionsgruppe : sdcard +''funktion : ein ausgewählter dir-marker löschen +''eingabe : - +''ausgabe : - +''busprotokoll : [028][get.dmarker] +'' : dmarker - dir-marker + + markernr := bus_getchar + dmarker[markernr] := TRUE + +CON ''------------------------------------------------- COM-FUNKTIONEN + +PRI com_init 'com: serielle schnittstelle initialisieren +''funktionsgruppe : com +''funktion : serielle schnittstelle initialisieren +''eingabe : - +''ausgabe : - +''busprotokoll : [031][sub_getlong.baudrate] + + + com_baud := sub_getlong + com.start(gc#SER_RX,gc#SER_TX,0,com_baud) ' start the default serial interface + +PRI com_tx 'com: zeichen senden +''funktionsgruppe : com +''funktion : zeichen senden +''eingabe : - +''ausgabe : - +''busprotokoll : [032][get.char] + + com.tx(bus_getchar) + +PRI com_rx 'com: zeichen empfangen +''funktionsgruppe : com +''funktion : zeichen empfangen +''eingabe : - +''ausgabe : - +''busprotokoll : [033][put.char] + + bus_putchar(com.rx) + +CON ''------------------------------------------------- HSS-FUNKTIONEN + +PRI sfx_fire | slot, chan, slotadr 'sfx: effekt im puffer abspielen +''funktionsgruppe : sfx +''funktion : effekt aus einem effektpuffer abspielen +''eingabe : - +''ausgabe : - +''busprotokoll : [107][get.slot][get.chan] +'' : slot - $00..$0f nummer der freien effektpuffer +'' : slot - $f0..ff vordefinierte effektslots +'' : chan - 0/1 stereokanal auf dem der effekt abgespielt werden soll +''vordefinierte effekte : &f0 - warnton +'' : $f1 - signalton +'' : $f2 - herzschlag schnell +'' : $f3 - herzschlag langsam +'' : $f4 - telefon +'' : $f5 - phaser :) +'' : $f6 - pling +'' : $f7 - on +'' : $f8 - off + + slot := bus_getchar + chan := bus_getchar 'channelnummer lesen + case slot + $f0: hss.sfx_play(1, @SoundFX1) 'warnton + $f1: hss.sfx_play(1, @SoundFX2) 'signalton + $f2: hss.sfx_play(1, @SoundFX3) 'herzschlag schnell + $f3: hss.sfx_play(1, @SoundFX4) 'herzschlag schnell + $f4: hss.sfx_play(1, @SoundFX5) 'telefon + $f5: hss.sfx_play(1, @SoundFX6) 'phase + $f6: hss.sfx_play(1, @SoundFX7) 'pling + $f7: hss.sfx_play(1, @SoundFX8) 'on + $f8: hss.sfx_play(1, @SoundFX9) 'off + other: + if slot < $f0 + slotadr := @sfxdat + (slot * 32) 'slotnummer lesen und adresse berechnen + hss.sfx_play(chan, slotadr) + +PRI sfx_setslot | slotadr, i 'sfx: daten in sfx-slotpuffer schreiben +''funktionsgruppe : sfx +''funktion : die daten für ein sfx-slot werden werden von regnatix gesetzt +''eingabe : - +''ausgabe : - +''busprotokoll : [108][get.slot][32:get.daten] +'' : slot - $00..$0f nummer der freien effektpuffer +'' : daten - 32 byte effektdaten +'' +''struktur der effektdaten: +'' +''[wav ][len ][freq][vol ] grundschwingung +''[lfo ][lfw ][fma ][ama ] modulation +''[att ][dec ][sus ][rel ] hüllkurve +''[seq ] (optional) +'' +''[wav] wellenform +'' 0 sinus (0..500hz) +'' 1 schneller sinus (0..1khz) +'' 2 dreieck (0..500hz) +'' 3 rechteck (0..1khz) +'' 4 schnelles rechteck (0..4khz) +'' 5 impulse (0..1,333hz) +'' 6 rauschen +''[len] tonlänge $0..$fe, $ff endlos +''[freq] frequenz $00..$ff +''[vol] lautstärke $00..$0f +'' +''[lfo] low frequency oscillator $ff..$01 +''[lfw] low frequency waveform +'' $00 sinus (0..8hz) +'' $01 fast sine (0..16hz) +'' $02 ramp up (0..8hz) +'' $03 ramp down (0..8hz) +'' $04 square (0..32hz) +'' $05 random +'' $ff sequencer data (es folgt eine sequenzfolge [seq]) +''[fma] frequency modulation amount +'' $00 no modulation +'' $01..$ff +''[ama] amplitude modulation amount +'' $00 no modulation +'' $01..$ff +''[att] attack $00..$ff +''[dec] decay $00..$ff +''[sus] sustain $00..$ff +''[rel] release $00..$ff + + slotadr := @sfxdat + (bus_getchar * 32) 'slotnummer lesen und adresse berechnen + repeat i from 0 to 31 + byte[slotadr + i] := bus_getchar 'sfx-daten einlesen + +PRI sfx_keyoff | chan 'sfx: release-phase einleiten um den effekt zu beenden +''funktionsgruppe : sfx +''funktion : für den aktuell abgespielten effekt wird die release-phase der +'' : adsr-hüllkurve eingeleitet, um ihn zu beenden +''eingabe : - +''ausgabe : - +''busprotokoll : [109][get.chan] +'' : chan - 0/1 stereokanal auf dem der effekt abgespielt werden soll + + chan := bus_getchar 'channelnummer lesen + hss.sfx_keyoff(chan) + +PRI sfx_stop | chan 'sfx: effekt sofort beenden +''funktionsgruppe : sfx +''funktion : der aktuell abgespielte effekt wird sofort beendet +''eingabe : - +''ausgabe : - +''busprotokoll : [110][get.chan] +'' : chan - 0/1 stereokanal auf dem der effekt abgespielt werden soll + + chan := bus_getchar 'channelnummer lesen + hss.sfx_stop(chan) + +PRI hss_vol 'hss: volume 0..15 einstellen +''funktionsgruppe : hss +''funktion : lautstärke des hss-players wird eingestellt +''eingabe : - +''ausgabe : - +''busprotokoll : [106][get.vol] +'' : vol - 0..15 gesamtlautstärke des hss-players + hss.hmus_vol(bus_getchar) + +PRI hss_intreg | regnr,wert 'hss: auslesen der player-register +''funktionsgruppe : hss +''funktion : abfrage eines hss-playerregisters (16bit) durch regnatix +''eingabe : - +''ausgabe : - +''busprotokoll : [105][get.regnr][put.reghwt][put.regnwt] +'' : regnr - 0..24 (5 x 5 register) +'' : reghwt - höherwertiger teil des 16bit-registerwertes +'' : regnwt - niederwertiger teil des 16bit-registerwertes +'' +''0 iEndFlag iRowFlag iEngineC iBeatC iRepeat globale Playerwerte +''5 iNote iOktave iVolume iEffekt iInstrument Soundkanal 1 +''10 iNote iOktave iVolume iEffekt iInstrument Soundkanal 2 +''15 iNote iOktave iVolume iEffekt iInstrument Soundkanal 3 +''20 iNote iOktave iVolume iEffekt iInstrument Soundkanal 4 +'' +''iEndFlag Repeat oder Ende wurde erreicht +''iRowFlag Trackerzeile (Row) ist fertig +''iEngineC Patternzähler +''iBeatC Beatzähler (Anzahl der Rows) +''iRepeat Zähler für Loops + + regnr := bus_getchar 'registernummer einlesen + wert := hss.intread(regnr) + bus_putchar(wert >> 8) '16-bit-wert senden hsb/lsb + bus_putchar(wert) + +PRI hss_peek 'hss: zugriff auf alle internen playerregister +''funktionsgruppe : hss +''funktion : zugriff auf die internen playerregister; leider sind die register +'' : nicht dokumentiert; 48 long-register +''eingabe : - +''ausgabe : - +''busprotokoll : [104][get.regnr][sub_putlong.regwert] +'' : regnr - registernummer +'' : regwert - long + + sub_putlong(hss.peek(bus_getchar)) + +PRI hss_load | err 'hss: musikdatei in puffer laden (dummy, in admnet not supported) +''funktionsgruppe : hss +''funktion : hss-datei wird in den modulpuffer geladen (in admnet not supported) +''eingabe : - +''ausgabe : - +''busprotokoll : [100][sub_getstr.fn][put.err] +'' : fn - dateiname +'' : err - fehlernummer entspr. liste + + sub_getstr 'dateinamen einlesen + bus_putchar(0) 'ergebnis der operation senden + +CON ''------------------------------------------------- RTC-FUNKTIONEN + +PRI rtc_getSeconds 'rtc: Returns the current second (0 - 59) from the real time clock. +''funktionsgruppe : rtc +''busprotokoll : [041][sub_putlong.seconds] +'' : seconds - current second (0 - 59) + sub_putlong(rtc.getSeconds) + +PRI rtc_getMinutes 'rtc: Returns the current minute (0 - 59) from the real time clock. +''funktionsgruppe : rtc +''busprotokoll : [042][sub_putlong.minutes] +'' : minutes - current minute (0 - 59) + sub_putlong(rtc.getMinutes) + +PRI rtc_getHours 'rtc: Returns the current hour (0 - 23) from the real time clock. +''funktionsgruppe : rtc +''busprotokoll : [043][sub_putlong.hours] +'' : hours - current hour (0 - 23) + sub_putlong(rtc.getHours) + +PRI rtc_getDay 'rtc: Returns the current day of the week (1 - 7) from the real time clock. +''funktionsgruppe : rtc +''busprotokoll : [044][sub_putlong.day] +'' : day - current day (1 - 7) of the week + sub_putlong(rtc.getDay) + +PRI rtc_getDate 'rtc: Returns the current date (1 - 31) from the real time clock. +''funktionsgruppe : rtc +''busprotokoll : [045][sub_putlong.date] +'' : date - current date (1 - 31) of the month + sub_putlong(rtc.getDate) + +PRI rtc_getMonth 'rtc: Returns the current month (1 - 12) from the real time clock. +''funktionsgruppe : rtc +''busprotokoll : [046][sub_putlong.month] +'' : month - current month (1 - 12) + sub_putlong(rtc.getMonth) + +PRI rtc_getYear 'rtc: Returns the current year (2000 - 2099) from the real time clock. +''funktionsgruppe : rtc +''busprotokoll : [047][sub_putlong.year] +'' : year - current year (2000 - 2099) + sub_putlong(rtc.getYear) + +PRI rtc_setSeconds : seconds 'rtc: Sets the current real time clock seconds. +''funktionsgruppe : rtc +''busprotokoll : [048][sub_getlong.seconds] +'' : seconds - Number to set the seconds to between 0 - 59. + rtc.setSeconds(sub_getlong) + +PRI rtc_setMinutes : minutes 'rtc: Sets the current real time clock minutes. +''funktionsgruppe : rtc +''busprotokoll : [049][sub_getlong.minutes] +'' : minutes - Number to set the minutes to between 0 - 59. + rtc.setMinutes(sub_getlong) + +PRI rtc_setHours 'rtc: Sets the current real time clock hours. +''funktionsgruppe : rtc +''busprotokoll : [050][sub_getlong.hours] +'' : hours - Number to set the hours to between 0 - 23.51: + rtc.setHours(sub_getlong) + +PRI rtc_setDay 'rtc: Sets the current real time clock day. +''funktionsgruppe : rtc +''busprotokoll : [051][sub_getlong.day] +'' : day - Number to set the day to between 1 - 7. + rtc.setDay(sub_getlong) + +PRI rtc_setDate 'rtc: Sets the current real time clock date. +''funktionsgruppe : rtc +''busprotokoll : [052][sub_getlong.date] +'' : date - Number to set the date to between 1 - 31. + rtc.setDate(sub_getlong) + +PRI rtc_setMonth 'rtc: Sets the current real time clock month. +''funktionsgruppe : rtc +''busprotokoll : [053][sub_getlong.month] +'' : month - Number to set the month to between 1 - 12. + rtc.setMonth(sub_getlong) + +PRI rtc_setYear 'rtc: Sets the current real time clock year. +''funktionsgruppe : rtc +''busprotokoll : [054][sub_getlong.year] +'' : year - Number to set the year to between 2000 - 2099. + rtc.setYear(sub_getlong) + +PRI rtc_setNVSRAM | index, value 'rtc: Sets the NVSRAM to the selected value (0 - 255) at the index (0 - 55). +''funktionsgruppe : rtc +''busprotokoll : [055][sub_getlong.index][sub_getlong.value] +'' : index - The location in NVRAM to set (0 - 55). │ +'' : value - The value (0 - 255) to change the location to. │ + index := sub_getlong + value := sub_getlong + rtc.setNVSRAM(index, value) + +PRI rtc_getNVSRAM 'rtc: Gets the selected NVSRAM value at the index (0 - 55). +''funktionsgruppe : rtc +''busprotokoll : [056][sub_getlong.index][sub_getlong.value] +'' : index - The location in NVRAM to get (0 - 55). │ + sub_putlong(rtc.getNVSRAM(sub_getlong)) + +PRI rtc_pauseForSeconds 'rtc: Pauses execution for a number of seconds. +''funktionsgruppe : rtc +''busprotokoll : [057][sub_getlong.number][sub_putlong.number] +'' : Number - Number of seconds to pause for between 0 and 2,147,483,647. +'' : Returns a puesdo random value derived from the current clock frequency and the time when called. + + sub_putlong(rtc.pauseForSeconds(sub_getlong)) + +PRI rtc_pauseForMilliseconds 'rtc: Pauses execution for a number of milliseconds. +''funktionsgruppe : rtc +''busprotokoll : [058][sub_getlong.number][sub_putlong.number] +'' : Number - Number of milliseconds to pause for between 0 and 2,147,483,647. +'' : Returns a puesdo random value derived from the current clock frequency and the time when called. + sub_putlong(rtc.pauseForMilliseconds(sub_getlong)) + +PRI probeRTC | hiveid + + hiveid := rtc.getNVSRAM(NVRAM_HIVE) 'read first byte of hive id + + rtc.setNVSRAM(NVRAM_HIVE, hiveid ^ $F) 'write back to NVRAM with flipped all bits + if rtc.getNVSRAM(NVRAM_HIVE) == hiveid ^ $F 'flipped bits are stored? + rtc.setNVSRAM(NVRAM_HIVE, hiveid) 'restore first byte of hive id + return(TRUE) 'RTC found + else + rtc.setNVSRAM(NVRAM_HIVE, hiveid) 'still restore first byte of hive id + return(FALSE) 'no RTC found + +PRI rtc_test 'rtc: Test if RTC Chip is available +''funktionsgruppe : rtc +''busprotokoll : [059][put.avaliable] +'' : Returns TRUE if RTC is available, otherwise FALSE + bus_putchar(probeRTC) + +CON ''------------------------------------------------- LAN-FUNKTIONEN + +PRI lan_start | hiveid, hivestr, strpos, macpos, i, a +''funktionsgruppe : lan +''funktion : Netzwerk starten +''eingabe : - +''ausgabe : - +''busprotokoll : [071] + + if (not lan_started) + + 'Pufferindex zurücksetzen + i := 0 + repeat sock#sNumSockets + bufidx[i++] := $FF '0xFF: nicht zugewiesen + + 'IP-Parameter setzen + if probeRTC + repeat a from 0 to 15 + ip_addr[a] := rtc.getNVSRAM(NVRAM_IPADDR+a) ' fill addresses + hiveid := rtc.getNVSRAM(NVRAM_HIVE) + hiveid += rtc.getNVSRAM(NVRAM_HIVE+1) << 8 + hiveid += rtc.getNVSRAM(NVRAM_HIVE+2) << 16 + hiveid += rtc.getNVSRAM(NVRAM_HIVE+3) << 24 + else + dmarker[UMARKER] := sdfat.getDirCluster 'u-marker setzen + ifnot dmarker[SMARKER] == TRUE 's-marker aktivieren + sdfat.setDirCluster(dmarker[SMARKER]) + ifnot \sdfat.openFile(@strNVRAMFile, "R") + \sdfat.setCharacterPosition(NVRAM_IPADDR) + repeat a from 0 to 15 + ip_addr[a] := \sdfat.readCharacter ' fill addresses + \sdfat.setCharacterPosition(NVRAM_HIVE) + hiveid := \sdfat.readCharacter + hiveid += \sdfat.readCharacter << 8 + hiveid += \sdfat.readCharacter << 16 + hiveid += \sdfat.readCharacter << 24 + \sdfat.closeFile + ifnot dmarker[UMARKER] == TRUE 'U-marker aktivieren + sdfat.setDirCluster(dmarker[UMARKER]) + + hivestr := num.ToStr(hiveid, num#DEC) + strpos := strsize(hivestr) + macpos := 5 + repeat while (strpos AND macpos) + strpos-- + if(strpos) + strpos-- + mac_addr[macpos] := num.FromStr(hivestr+strpos, num#HEX) + byte[hivestr+strpos] := 0 + macpos-- + + sock.start(A_NETCS,A_NETSCK,A_NETSI,A_NETSO, -1, @mac_addr, @ip_addr) + lan_started := true + + +PRI lan_stop +''funktionsgruppe : lan +''funktion : Netzwerk anhalten +''eingabe : - +''ausgabe : - +''busprotokoll : [072] + + if lan_started + sock.stop + lan_started := false + +PRI lan_connect | ipaddr, remoteport, handle, handleidx, i +''funktionsgruppe : lan +''funktion : ausgehende TCP-Verbindung öffnen (mit Server verbinden) +''eingabe : - +''ausgabe : - +''busprotokoll : [073][sub_getlong.ipaddr][sub_getword.remoteport][put.handleidx] +'' : ipaddr - ipv4 address packed into a long (ie: 1.2.3.4 => $01_02_03_04) +'' : remoteport - port number to connect to +'' : handleidx - lfd. Nr. der Verbindung (index des kompletten handle) + + ipaddr := sub_getlong + remoteport := sub_getword + + 'freien Pufferabschnitt suchen + i := 0 + repeat sock#sNumSockets + if bufidx[i] == $FF '0xFF: nicht zugewiesen + quit + i++ + + ifnot (handle := sock.connect(ipaddr, remoteport, @bufrx[i*rxlen], rxlen, @buftx[i*txlen], txlen)) == -102 + sock.resetBuffers(handle) + handleidx := handle.byte[0] 'extract the handle index from the lower 8 bits + sockhandle[handleidx] := handle 'komplettes handle zu handle index speichern + bufidx[i] :=handleidx + bus_putchar(handleidx) 'handleidx senden + else + bus_putchar($FF) + +PRI lan_listen | port, handle, handleidx, i +''funktionsgruppe : lan +''funktion : Port für eingehende TCP-Verbindung öffnen +''eingabe : - +''ausgabe : - +''busprotokoll : [074][sub_getword.port][put.handleidx] +'' : port - zu öffnende Portnummer +'' : handleidx - lfd. Nr. der Verbindung (index des kompletten handle) + + port := sub_getword + + 'freien Pufferabschnitt suchen + i := 0 + repeat sock#sNumSockets + if bufidx[i] == $FF '0xFF: nicht zugewiesen + quit + i++ + + ifnot (handle := sock.listen(port, @bufrx[i*rxlen], rxlen, @buftx[i*txlen], txlen)) == -102 + handleidx := handle.byte[0] 'extract the handle index from the lower 8 bits + sockhandle[handleidx] := handle 'komplettes handle zu handle index speichern + bufidx[i] :=handleidx + bus_putchar(handleidx) 'handleidx senden + else + bus_putchar($FF) + +PRI lan_waitconntimeout | handleidx, timeout, t, connected +''funktionsgruppe : lan +''funktion : bestimmte Zeit auf Verbindung warten +''eingabe : - +''ausgabe : - +''busprotokoll : [075][get.handleidx][sub_getword.timeout][put.connected] +'' : handleidx - lfd. Nr. der zu testenden Verbindung +'' : timeout - Timeout in Millisekunden +'' : connected - True, if connected + + handleidx := bus_getchar + timeout := sub_getword + + t := cnt + repeat until (connected := sock.isConnected(sockhandle[handleidx])) or (((cnt - t) / (clkfreq / 1000)) > timeout) + + bus_putchar(connected) + +PRI lan_close | handleidx, i +''funktionsgruppe : lan +''funktion : TCP-Verbindung (ein- oder ausgehend) schließen +''eingabe : - +''ausgabe : - +''busprotokoll : [076][get.handleidx] +'' : handleidx - lfd. Nr. der zu schließenden Verbindung + + handleidx := bus_getchar + + sock.close(sockhandle[handleidx]) + + 'reservierten Pufferabschnitt freigeben + i := 0 + repeat sock#sNumSockets + if bufidx[i++] == handleidx '0xFF: nicht zugewiesen + bufidx[i++] := $FF + quit + + +PRI lan_rxtime | handleidx, timeout, t, rxbyte +''funktionsgruppe : lan +''funktion : angegebene Zeit auf ASCII-Zeichen warten +'' : nicht verwenden, wenn anderes als ASCII (0 - 127) empfangen wird +''eingabe : - +''ausgabe : - +''busprotokoll : [077][get.handleidx][sub_getword.timeout][put.rxbyte] +'' : handleidx - lfd. Nr. der Verbindung +'' : timeout - Timeout in Millisekunden +'' : rxbyte - empfangenes Zeichen (0 - 127) oder +'' : sock#RETBUFFEREMPTY (-1) wenn Timeout oder keine Verbindung mehr + + handleidx := bus_getchar + timeout := sub_getword + + t := cnt + repeat until (rxbyte := sock.readByteNonBlocking(sockhandle[handleidx])) => 0 or (not sock.isConnected(sockhandle[handleidx])) or (cnt - t) / (clkfreq / 1000) > timeout + + bus_putchar(rxbyte) + +PRI lan_rxdata | handleidx, len, rxbyte, error +''funktionsgruppe : lan +''funktion : bei bestehender Verbindung die angegebene Datenmenge empfangen +''eingabe : - +''ausgabe : - +''busprotokoll : [078][get.handleidx][sub_getlong.len][put.byte1][put.byte][put.error] +'' : handleidx - lfd. Nr. der Verbindung +'' : len - Anzahl zu empfangender Bytes +'' : error - ungleich Null bei Fehler + + error := FALSE + handleidx := bus_getchar + len := sub_getlong + + repeat len + ifnot error + repeat while (rxbyte := sock.readByteNonBlocking(sockhandle[handleidx])) < 0 + ifnot sock.isConnected(sockhandle[handleidx]) + error := sock#ERRSOCKETCLOSED + quit + bus_putchar(rxbyte) + + bus_putchar(error) + +PRI lan_txdata | handleidx, len, txbyte, error +''funktionsgruppe : lan +''funktion : bei bestehender Verbindung die angegebene Datenmenge senden +''eingabe : - +''ausgabe : - +''busprotokoll : [079][get.handleidx][sub_getlong.len][get.byte1][get.byte][put.error] +'' : handleidx - lfd. Nr. der Verbindung +'' : len - Anzahl zu sendender Bytes +'' : error - ungleich Null bei Fehler + + error := FALSE + handleidx := bus_getchar + len := sub_getlong + + repeat len + txbyte := bus_getchar + ifnot error + repeat while sock.writeByteNonBlocking(sockhandle[handleidx], txbyte) < 0 + ifnot sock.isConnected(sockhandle[handleidx]) + error := sock#ERRSOCKETCLOSED + quit + + bus_putchar(error) + +PRI lan_rxbyte +''funktionsgruppe : lan +''funktion : wenn vorhanden, ein empfangenes Byte lesen +'' : nicht verwenden, wenn auch $FF empfangen werden kann +''eingabe : - +''ausgabe : - +''busprotokoll : [080][get.handleidx][put.rxbyte] +'' : handleidx - lfd. Nr. der Verbindung +'' : rxbyte - empfangenes Zeichen oder +'' : sock#RETBUFFEREMPTY (-1) wenn kein Zeichen vorhanden + + bus_putchar(sock.readByteNonBlocking(sockhandle[bus_getchar])) + +PRI lan_isconnected +''funktionsgruppe : lan +''funktion : Returns true if the socket is connected, false otherwise +''eingabe : - +''ausgabe : - +''busprotokoll : [081][get.handleidx][put.connected] +'' : handleidx - lfd. Nr. der Verbindung +'' : connected - TRUE wenn verbunden, sonst FALSE + + bus_putchar(sock.isConnected(sockhandle[bus_getchar])) + +DAT + long ' long alignment for addresses + ip_addr byte 10, 1, 1, 1 'ip + ip_subnet byte 255, 255, 255, 0 'subnet-maske + ip_gateway byte 10, 1, 1, 254 'gateway + ip_dns byte 10, 1, 1, 254 'dns + ip_boot long 0 'boot-server (IP address in long) + mac_addr byte $c0, $de, $ba, $be, $00, $00 'mac-adresse + +DAT 'dummyroutine für getcogs + org +' +' Entry: dummy-assemblercode fuer cogtest +' +entry jmp entry 'just loops + + + +DAT 'feste sfx-slots + + 'Wav 'Len 'Fre 'Vol 'LFO 'LFW 'FMa 'AMa +SoundFX1 byte $01, $FF, $80, $0F, $0F, $00, $07, $90 + 'Att 'Dec 'Sus 'Rel + byte $FF, $10, $00, $FF + + 'Wav 'Len 'Fre 'Vol 'LFO 'LFW 'FMa 'AMa +SoundFX2 byte $05, $FF, $00, $0F, $04, $FF, $01, $05 + 'Att 'Dec 'Sus 'Rel + byte $F1, $24, $00, $FF + '16step Sequencer Table + byte $F1, $78, $3C, $00, $00, $00, $F1, $78, $3C, $00, $00, $00, $00, $00, $00, $00 + + 'Wav 'Len 'Fre 'Vol 'LFO 'LFW 'FMa 'AMa 'Heartbeat +SoundFX3 byte $00, $FF, $06, $0F, $09, $FF, $04, $05 + 'Att 'Dec 'Sus 'Rel + byte $F1, $F4, $F0, $0F + byte $F1, $78, $3C, $00, $00, $00, $F1, $78, $3C, $00, $00, $00, $00, $00, $00, $00 + + 'Wav 'Len 'Fre 'Vol 'LFO 'LFW 'FMa 'AMa 'Heartbeat low +SoundFX4 byte $00, $FE, $06, $0f, $15, $FF, $04, $05 + 'Att 'Dec 'Sus 'Rel + byte $F1, $F4, $F0, $0F + byte $F1, $78, $3C, $00, $00, $00, $F1, $78, $3C, $00, $00, $00, $00, $00, $00, $00 + + 'Wav 'Len 'Fre 'Vol 'LFO 'LFW 'FMa 'AMa 'Telefon +SoundFX5 byte $05, $15, $4F, $0F, $01, $04, $05, $00 + 'Att 'Dec 'Sus 'Rel + byte $FF, $00, $00, $FF + + 'Wav 'Len 'Fre 'Vol 'LFO 'LFW 'FMa 'AMa +SoundFX6 byte $06, $FF, $5F, $0F, $01, $03, $01, $00 'Teleport + 'Att 'Dec 'Sus 'Rel + byte $FF, $14, $00, $FF + +SoundFX7 'pling +' Wav Len Fre Vol LFO LFW FMa AMa Att Dec Sus Rel +byte $04,$01,$80,$0F,$00,$00,$00,$00,$FF,$00,$00,$80 +byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01 + +SoundFX8 'on +' Wav Len Fre Vol LFO LFW FMa AMa Att Dec Sus Rel +byte $00,$05,$10,$0F,$08,$02,$05,$00,$FF,$00,$50,$11 +byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01 + +SoundFX9 'off +' Wav Len Fre Vol LFO LFW FMa AMa Att Dec Sus Rel +byte $00,$05,$33,$0F,$05,$03,$10,$00,$FF,$00,$50,$11 +byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01 + + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, PRIlish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} + diff --git a/system/regnatix/irc.spin b/system/regnatix/irc.spin index 6672a68..4eca6b1 100644 --- a/system/regnatix/irc.spin +++ b/system/regnatix/irc.spin @@ -44,8 +44,6 @@ _XINFREQ = 5_000_000 CON -LANMASK = %00000000_00000000_00000000_00100000 - W0X_MENU = 8 W0Y_MENU = 0 @@ -91,6 +89,7 @@ DAT VAR long t1char 'Empfangs-Zeitpunkt des 1. Zeichen einer Zeile + long tsrvact 'Zeitpunkt letzte Server-Aktivität long ip_addr long hiveid long ledcog @@ -113,6 +112,7 @@ VAR byte send_str[LEN_IRCLINE] byte receive_str[LEN_IRCLINE] byte brightness + byte newMsg PUB main | key @@ -120,7 +120,9 @@ PUB main | key repeat if ios.keystat > 0 - ledStop + if newMsg 'neue Mitteilung wurde signalisiert + newMsg := FALSE + ledStop key := ios.key case key gc#KEY_TAB: f_focus @@ -140,6 +142,12 @@ PUB main | key f_input(key) ifnot handleidx == $FF + if ((cnt - tsrvact) / clkfreq) > 60 'wenn seit dem Empfang der letzten Zeile vom Server mehr wie 60s vergangen sind + handleStatusStr(string("Timeout, sende PING"), 2, FALSE) + sendStr(string("PING HIVE")) + sendStr(str.trimCharacters(num.ToStr(hiveid, num#DEC))) + sendStr(string(13,10)) + tsrvact := cnt 'Zeitpunkt der letzten Serveraktivität setzen ircGetLine PRI init @@ -158,18 +166,35 @@ PRI init ircsrv[0] := 0 focus := 3 joined := FALSE + newMsg := FALSE ios.start 'ios initialisieren - ifnot (ios.admgetspec & LANMASK) - ios.print(@strNoNetwork) - ios.stop - ios.print(@strInitWait) + ifnot (ios.belgetspec & (gc#b_key|gc#b_txt|gc#b_win)) 'Wir brauchen Bellatrix mit Keyboard-, Text- und Fensterfunktionen + ios.belreset 'Bellatrix neu starten (aus ROM laden) + ios.print(@strInitWait) + ifnot (ios.belgetspec & (gc#b_key|gc#b_txt|gc#b_win)) + ios.print(@strWrongBel) 'Bellatrix-Flash enthält nicht die nötige Version + ios.stop 'Ende + else + ios.print(@strInitWait) + ios.sdmount + ifnot (ios.admgetspec & gc#A_LAN) 'Administra stellt kein Netzwerk zur Verfügung + ios.sddmset(ios#DM_USER) 'u-marker setzen + ios.sddmact(ios#DM_SYSTEM) 's-marker aktivieren + ios.admload(string("admnet.adm")) 'versuche, admnet zu laden + ios.sddmact(ios#DM_USER) 'u-marker aktivieren + ifnot (ios.admgetspec & gc#A_LAN) 'wenn Laden fehlgeschlagen + ios.print(@strNoNetwork) + ios.stop 'Ende setscreen conf_load if ip_addr == 0 ifnot f_setconf handleStatusStr(@strRestartConf, 2, FALSE) + 'sfx-slots setzen + ios.sfx_setslot(@soundNewMgs, 0) + PRI f_focus if ++focus == 4 @@ -547,6 +572,7 @@ PRI ircGetLine | i, x, prefixstr, nickstr, chanstr, msgstr, commandstr if readLine(2000) 'vollständige Zeile empfangen + tsrvact := cnt 'Zeitpunkt der letzten Serveraktivität setzen if receive_str[0] == ":" 'Prefix folgt (sollte jede hereinkommende Message enthalten) prefixstr := @receive_str[1] ifnot (commandstr := str.replaceCharacter(prefixstr, " ", 0)) 'nächstes Leerzeichen ist Ende des Prefix, dann folgt das Kommando @@ -573,8 +599,10 @@ PRI ircGetLine | i, x, prefixstr, nickstr, chanstr, msgstr, commandstr sendStr(nickstr) sendStr(string(" :VERSION HiveIRC 1.0.0 [P8X32A/80MHz] ",13,10)) else - ledStart - ios.sfx_fire($f4, 1) 'play phone sound + ifnot newMsg 'neue Mitteilung noch nicht signalisiert + newMsg := TRUE + ledStart + ios.sfx_fire($0, 1) 'play phone sound if byte[chanstr] == "#" 'Message an Channel handleChatStr(chanstr, nickstr, msgstr, 0) else 'Message an mich @@ -1285,7 +1313,13 @@ PRI ledTwinkle(rate) repeat brightness from 100 to 0 led.LEDBrightness(brightness,gc#HBEAT) 'Adjust LED brightness waitcnt(rate + cnt) 'Wait a moment -DAT ' Locale +DAT 'Sound + +' Wav Len Fre Vol LFO LFW FMa AMa Att Dec Sus Rel +soundNewMgs byte $00,$03,$FF,$0F,$08,$04,$05,$00,$FF,$00,$50,$11 + byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01 + +DAT 'Locale #ifdef __LANG_EN 'locale: english @@ -1296,6 +1330,7 @@ DAT ' Locale strWin2 byte "State",0 strWin3 byte "Input",0 + strWrongBel byte 13,"Bellatrix flash doesn't have the expected TriOS code.",13,0 strNoNetwork byte 13,"Administra doesn't provide network functions!",13,"Please load admnet.",13,0 strInitWait byte 13,"Initialiasing, please wait...",13,0 strRestartConf byte "Please restart configuration (F2)",0 @@ -1361,6 +1396,7 @@ DAT ' Locale strWin2 byte "Status",0 strWin3 byte "Eingabe",0 + strWrongBel byte 13,"Bellatrix-Flash enthält nicht den erforderlichen TriOS-Code.",13,0 strNoNetwork byte 13,"Administra stellt keine Netzwerk-Funktionen zur Verfügung!",13,"Bitte admnet laden.",13,0 strInitWait byte 13,"Initialisiere, bitte warten...",13,0 strRestartConf byte "Bitte Konfiguration neu starten (F2)",0