<feed xmlns='http://www.w3.org/2005/Atom'>
<title>gbos.git/c, branch master</title>
<subtitle>gbos - a tiny Unix-flavored OS for the Game Boy Color (kernel, shell, coreutils in C, RAM filesystem)</subtitle>
<id>https://git.sl0p.foo/gbos.git/atom/c?h=master</id>
<link rel='self' href='https://git.sl0p.foo/gbos.git/atom/c?h=master'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/'/>
<updated>2026-07-17T22:38:38Z</updated>
<entry>
<title>refactor: c/ -&gt; usr/, compiled program blobs out of the source tree</title>
<updated>2026-07-17T22:38:38Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T22:38:38Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=ba2d7ae1d7b319645b10aaa31a7f664f9f80c25c'/>
<id>urn:sha1:ba2d7ae1d7b319645b10aaa31a7f664f9f80c25c</id>
<content type='text'>
Userland sources live in usr/ (fits the Unix theme better than 'c').
SDCC output .bin blobs land in build/usr/ with the other build artifacts
instead of littering the source dir; programs.asm INCBINs them from there.
Byte-identical ROM.
</content>
</entry>
<entry>
<title>irc: keep the keyboard live during RX floods (poll input every pass)</title>
<updated>2026-07-17T21:20:55Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T21:20:55Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=15b4767b17f78a827fd221e941c7ac7aa987e02f'/>
<id>urn:sha1:15b4767b17f78a827fd221e941c7ac7aa987e02f</id>
<content type='text'>
'/join #sl0p right after connect' looked broken: the main loop only
polled input when net_recv_nb returned nothing, and 'continue'd on every
received segment. During a big MOTD (irc.sl0p.foo's is ~60 lines of
color art) that starved input for the whole ~20s flood - the OSK
wouldn't even open, and keystrokes were dropped. gbtype input fared a
bit better (buffered in the 64-byte console ring) but still wasn't
serviced until the flood ended.

Poll pollin()/pollcon() once every loop iteration regardless of RX, and
only idle-sleep when a pass both received nothing and read no key. The
protocol/join logic was already correct (verified: JOIN #sl0p is sent,
accepted, and the client switches to [#sl0p] with topic + names) - this
just makes typing responsive while messages are streaming.

Verified against the live server: injecting /join #sl0p *during* the
MOTD flood now joins immediately instead of waiting it out.

(Note: '#' is on the OSK - third row, second from the end:
10,8,":;,=+*_!?()[]&lt;&gt;@#~" - just not obvious.)
</content>
</entry>
<entry>
<title>irc: strip mIRC formatting, drop 004/005 noise, fix a RAM-bank-swap landmine</title>
<updated>2026-07-17T21:03:43Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T21:03:43Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=3b856b3d009b99d01cb84727b803735937a44d16'/>
<id>urn:sha1:3b856b3d009b99d01cb84727b803735937a44d16</id>
<content type='text'>
Against the real irc.sl0p.foo (via a TLS-stripping socat proxy) the
client's screen filled with garbage after connect. Two causes:

1. Color/format codes. The MOTD is a mIRC-color ASCII-art logo. We
   dropped the \x03 control byte (term ignores &lt;32) but left its numeric
   *arguments*, so the logo rendered as digit soup ('09&gt; stream the
   work', '030903 030903 ...'). strip_fmt() now removes \x03 color (+its
   fg[,bg] digits), \x04 hex color, and the \x02/\x0f/\x11/\x16/\x1d/
   \x1e/\x1f toggles, applied to the trailing text of every line (leaving
   \x01 for CTCP). Standalone digits like '3 apples' are untouched.

2. The real bug: handle() defaulted pfx/txt to a read-only "" literal,
   and both bang(pfx) and strip_fmt(txt) write a NUL terminator into it.
   That literal lives in ROM (000-FFF); on MBC5 a write there is a
   RAM-bank-select, silently swapping the  cart-RAM bank - where
   every static lives - out from under the program. One prefix-less or
   empty-trailing server line (real ircds send them: NOTICE AUTH, ERROR,
   registration PING) and all state turns to garbage. Fixed with a
   writable 1-byte 'empty' default (zeroed in main; gbos doesn't clear
   BSS).

Also suppress 004 (MYINFO) and 005 (ISUPPORT) - pure noise on 40 cols,
and InspIRCd splits ISUPPORT across several lines.

Verified end to end against the live server: full InspIRCd MOTD (logo,
LUSERS, links) renders clean and stable; host unit tests cover color/
bold/CTCP-action stripping and prefix-less NOTICE/ERROR/PING.
</content>
</entry>
<entry>
<title>irc: a bitchx/irssi-style IRC client</title>
<updated>2026-07-17T18:22:48Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T18:22:48Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=c9495d4b52b245951bf4768379b38aeb1843d60f'/>
<id>urn:sha1:c9495d4b52b245951bf4768379b38aeb1843d60f</id>
<content type='text'>
irc HOST [NICK] - connects over the kernel TCP stack (DNS-resolves the
host), registers, and runs a live client on the 40x18 LCD.

UI, within the terminal's means (no cursor addressing - just \r + \b):
messages scroll above a fixed irssi-style input line '[#chan] text_'
redrawn in place; long input scrolls horizontally. The elders' formats:
&lt;nick&gt; msg, &lt;nick:#c&gt; off-channel, *nick* private, -nick- notice,
* nick action, &gt;target&lt; outbound, -!- server/status. Keys come from both
the OSK (SELECT) and the console ring (pollcon), so a hub can drive it.

Commands: /join /part /msg /me /nick /quit /raw, plus bare text to the
current channel. Handles PING (PONG + a wink), CTCP ACTION/VERSION,
JOIN/PART/QUIT/KICK/NICK, 332/353 topic+names, and 433 nick-in-use
(auto-appends _). Registers on bank 32 as program id 30.

Note: gbos doesn't zero C statics, so main() inits its state explicitly;
the local TCP port is randomized (DIV) to dodge a stale server-side
half-open from an unclean prior exit. Tested end to end against a small
ircd through gbhub: full MOTD burst, join, channel + private messages,
actions, and bot replies all render correctly.
</content>
</entry>
<entry>
<title>net: SYS_POLLCON + larger console ring for link-injected input</title>
<updated>2026-07-17T18:22:34Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T18:22:34Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=bf647581fbad727ad22021d345d3955abf44c2f3'/>
<id>urn:sha1:bf647581fbad727ad22021d345d3955abf44c2f3</id>
<content type='text'>
Programs that own their main loop (the IRC client) need to poll for
typed input without blocking. pollin() only sees the on-screen keyboard;
bytes injected over the link (gbhub 'type', for scripted/hub-driven
sessions) land in the kernel console ring, previously only drained by the
blocking KGetc path. Add SYS_POLLCON: a non-blocking con_pop for
userland.

Also enlarge that console ring 16 -&gt; 64. One net_pump drains an entire
serial burst into the ring at once, so a whole injected command line has
to fit or bytes are dropped and lines merge (a 20-char command came out
truncated and glued to the next). 64 covers a full line; mask stays a
power of two.
</content>
</entry>
<entry>
<title>net: unbreak ping against hosts that intermittently drop ICMP</title>
<updated>2026-07-17T17:45:13Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T17:45:13Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=4f038ecad87777bc1d74f8f93f267da8cd396446'/>
<id>urn:sha1:4f038ecad87777bc1d74f8f93f267da8cd396446</id>
<content type='text'>
Pinging sl0p.foo through gbhub looked hung: DNS resolved, then nothing.
Packet-tracing showed the echo request leaving the hub's TUN and eth0
correctly NATed every time - but 80.78.19.56 blackholes ICMP for all
ids in windows of tens of seconds (provider rate limiting). One lost
reply wedged ping for minutes because net_recv's pump-counted timeout
is effectively unbounded at native emulation speed.

Two fixes:

- NET_RECVNB (op 8): non-blocking recv - one RX pump, $FE if nothing
  buffered, same delivery/EOF semantics as NET_RECV otherwise. ping now
  waits &lt;=~2s per seq (net_recv_nb + msleep loop), prints 'seq=N
  timeout' and moves on, like real ping.

- ICMP echo id was hardcoded $1234 for every GB, every boot, so all
  sessions produced byte-identical flows - hostile to NAT conntrack
  (keyed on icmp id). wNetEchoId is now our host octet + rDIV timing
  noise sampled at DHCP lease, distinct per GB and per boot.

Verified: 8 back-to-back native-speed gbhub runs, zero hangs; a run
that hit a blackhole window printed seq=1 timeout then recovered to
3/4 received. GB&lt;-&gt;GB ping and DNS/DHCP unaffected.
</content>
</entry>
<entry>
<title>net: ping resolves hostnames (ping sl0p.foo, not just ping 1.2.3.4)</title>
<updated>2026-07-17T13:23:03Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T13:23:03Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=7f01a9e22f8a19109daf56356a0bb8636af67a84'/>
<id>urn:sha1:7f01a9e22f8a19109daf56356a0bb8636af67a84</id>
<content type='text'>
ping used parse_ip only, so a hostname gave "bad address". It now uses resolve.h
(like wget/nslookup): dotted-quad is used as-is, a name is looked up via DNS
first. Copies the arg to a safe buffer before resolving (the 0xA000 arg/_DATA
overlap dance).

Verified through DHCP + NAT:
    /# ping sl0p.foo
    PING 80.78.19.56
      reply from 80.78.19.56 seq=1 ... -- 4/4 received
</content>
</entry>
<entry>
<title>cleanup: drop c/tcp.c (old userland-SLIP TCP client, superseded by kernel TCP/wget)</title>
<updated>2026-07-17T12:11:43Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T12:11:43Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=b16397c033de424231de9bb6e1001cbae19366df'/>
<id>urn:sha1:b16397c033de424231de9bb6e1001cbae19366df</id>
<content type='text'>
</content>
</entry>
<entry>
<title>net: DHCP client - lease the IP at boot instead of hardcoding it</title>
<updated>2026-07-17T12:11:22Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T12:11:22Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=a2cb756693229c7880d926d2cdde2f838c697415'/>
<id>urn:sha1:a2cb756693229c7880d926d2cdde2f838c697415</id>
<content type='text'>
The address is no longer baked in. net_init starts at 0.0.0.0; a DHCP client
runs as the first thing on boot (init/shell forks it and waits), and only once
it has a lease (or gives up) does the prompt appear.

Kernel:
- IP is configurable: 0.0.0.0 until leased; NET_SETIP op stores it.
- 16-bit frame length. DHCP/BOOTP packets are ~272 bytes, over the old 255-byte
  frame cap, so net_slip_send takes a 16-bit length, net_pump assembles into a
  320-byte buffer with a 16-bit wNetRxLen, and udp_send writes a 16-bit IP total.
  Payloads stay &lt;=255 (kept small on purpose) so the per-protocol datalen math
  is unchanged. wNetTx/wNetRxBuf 256-&gt;320, SK_RXBUF 208-&gt;288.

Userland:
- c/dhcp.c: DISCOVER-&gt;OFFER-&gt;REQUEST-&gt;ACK over a UDP socket, then net_setip();
  times out gracefully (shell still boots) if there's no server. sh.c runs it
  before the prompt.

Bridge (self-contained DHCP server, no dnsmasq):
- tunbridge.py + netboot intercept UDP-&gt;:67 and answer OFFER/ACK leasing
  10.0.0.2 (gateway 10.0.0.1); everything else is bridged/NATed as before.

Regression-tested ICMP/UDP/TCP after the 16-bit change. Verified end to end:
    dhcp: discovering
    dhcp: leased 10.0.0.2
    /# ping 10.0.0.1   -&gt; 4/4 (traffic from the leased address)
</content>
</entry>
<entry>
<title>kernel: sys_sleep - a cooperative delay off the DIV timer (+ ping pacing)</title>
<updated>2026-07-17T11:43:10Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-07-17T11:43:10Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/gbos.git/commit/?id=b52f37602968768901d2dd83ccf966d953a532f0'/>
<id>urn:sha1:b52f37602968768901d2dd83ccf966d953a532f0</id>
<content type='text'>
There was no time source at all (IRQ vectors just reti; scheduler is purely
cooperative). The DIV register (FF04) free-runs at 16384 Hz regardless of
interrupts, so sys_sleep accumulates DIV deltas across SchedYields (other procs
keep running) until the requested number of 1/64-second units elapse.

- SYS_SLEEP(34): B = 1/64s units; libc gsleep(units) / msleep(ms) wrappers.
- ping now msleep(800) between echoes, so it paces like real ping instead of
  blasting all four at once.

Verified real-time (capped emulator): replies land ~0.85s apart. In --uncapped
runs the delay is GB-time (fast wall-clock), as expected.
</content>
</entry>
</feed>
