✕

HOME

Changelog dettagliato

Tutte le modifiche, fix e feature per versione

CANARY v0.2.1.5.10 Fix generale — dispatch unificato gfxshell + help dinamico ⚠️ ROLLBACK in 5.11 Giu 2026
  • Dispatch generico in gfxshell_main.c: fallback su shell_commands[] per tutti i comandi non gestiti esplicitamente
  • 77+ comandi precedentemente "sconosciuti" in gfxshell ora funzionano senza modifiche aggiuntive
  • Help dinamico generato dalla tabella (100 comandi), con paginazione INVIO/ESC
  • Aggiungere un comando a commands.c ora basta per gfxshell — nessuna altra modifica necessaria
  • Nessuna nuova feature: versione di consolidamento e fix architetturale
  • ⚠️ Annullata in v0.2.1.5.11: molti comandi in tabella usano vga_* diretto, non visibile in framebuffer — serve prima la conversione term_* (Strada A)
help — 100 comandi dinamici
help v0.2.1.5.10
tasks — ora disponibile in gfxshell
tasks v0.2.1.5.10
ROLLBACK v0.2.1.5.11 Rollback a base 9b — annulla dispatch generico di 5.10 Giu 2026
  • Identica a v0.2.1.5.9b: kernel/gfxshell_main.c riportato al dispatch hardcoded originale
  • Motivo: il fallback generico di 5.10 esponeva comandi vga_* non visibili in framebuffer
  • tasks torna "Comando sconosciuto", help torna alla lista statica — verificato via screenshot
  • RestAPI, NATTour, NATBrowser A+B invariati e funzionanti
  • Prossimo passo corretto: Strada A (vga_* → term_*) prima di riconsiderare il dispatch generico
tasks — torna sconosciuto (rollback ok)
tasks rollback
help — torna statico
help rollback
ROLLBACK v0.2.1.5.11 Rollback a v0.2.1.5.9b — dispatch generico rimosso Giu 2026
  • Rollback puro: nessuna nuova feature, nessun fix
  • Il fallback generico v0.2.1.5.10 eseguiva comandi ancora scritti in vga_* — non funzionanti in framebuffer grafico, ma senza dare errore
  • gfxshell_main.c ripristinato identico byte-per-byte a v0.2.1.5.9b (verificato via md5)
  • Vero prossimo passo: v0.2.1.6 Strada A, conversione sistematica vga_* → term_*
help — hardcoded, come in 9b
help v0.2.1.5.11 rollback
CANARY v0.2.1.6 Strada A — primi 20 comandi vga_* to term_* Lug 2026
  • Prima tappa della conversione sistematica vga_* to term_* (20 comandi: help, clear, echo, ls, cat, cd, pwd, mkdir, touch, write, nano, edit, run, rm, tree, logo, tasks, demo, userdemo, syscalls)
  • FIX CRITICO scoperto testando pwd: shell_cwd non era mai inizializzato nel boot grafico (bug preesistente, mai emerso prima)
  • Fix verificato quantitativamente: output pwd passa da 24px ("/") a 229px ("/testdir") dopo mkdir+cd
  • Scoperto e convertito un helper nascosto (tree_print_rec) non toccato dalla prima passata di conversione
  • nano esclusa deliberatamente dalla whitelist: editor.c scrive diretto su memoria VGA 0xB8000, fuori scope
  • Whitelist esplicita (18 comandi) nel dispatch gfxshell - non un fallback generico come la v0.2.1.5.10 rollbackata
pwd dopo mkdir+cd — path dinamico reale
pwd fix v0.2.1.6
help — lista dinamica identica a text mode
help v0.2.1.6
CANARY v0.2.1.6.1 help-classic accanto al nuovo help dinamico Lug 2026
  • help resta il nuovo help dinamico (v0.2.1.6, generato da shell_commands[], paginato)
  • help-classic ripristina la vecchia lista statica hardcoded, identica a v0.2.1.5.11
  • Stesso principio di bootanim-classic: la versione precedente non si cancella, resta disponibile sotto un nome -classic
  • help-classic e' gfxshell-only (non in shell_commands[]): la vecchia lista non e' mai esistita in text mode
help — dinamico, paginato (~22 righe)
help v0.2.1.6.1
help-classic — lista statica fissa (~27 righe)
help-classic v0.2.1.6.1
CANARY v0.2.1.6.2 Marcatori [GFX] in help + audit versioni stale Lug 2026
  • help dinamico ora marca [GFX] i comandi davvero eseguibili in gfxshell (whitelist Strada A), non piu' tutti uguali
  • help-classic ora elencato nell'help dinamico (era invisibile, non essendo in shell_commands[])
  • Whitelist spostata a livello di file (gfx_termsafe.h condiviso), niente piu' duplicazione dispatch/help
  • Audit in 3 giri (grep specifico, poi per pattern "System ROM", poi regex generica) su tutte le versioni hardcoded stale
  • Corretti: boot ROM banner (grafico+testo), servercfg, gfxtxt/gfxhpe (versioni ancora piu' vecchie mai toccate: v0.2.1.1.5/v0.2.1.1.7), DynPower e SeaOfSens dashboard
  • Lasciati invariati i banner storici di comandi demo/deprecati (etichetta di quando sono stati scritti, non "versione corrente")
boot ” System ROM Version dinamico
boot v0.2.1.6.2
help ” marcatori [GFX] + help-classic in lista
help v0.2.1.6.2
CANARY v0.2.1.7 Strada A: batch system info + boot screen HPE-fidelity Lug 2026
  • 7 comandi convertiti a term_*: date, uptime, whoami, mem, id, time, diskinfo
  • Fix funzionale: la vecchia 'date' in gfxshell diceva "RTC non disponibile" - falso, la vera cmd_date() legge il CMOS RTC per data/ora reali
  • history esclusa deliberatamente: gfxshell ha un proprio buffer cronologia separato da text mode, instradarla sulla whitelist mostrerebbe dati sbagliati
  • hwinfo (111 vga_*, troppo grande), about, man, motd, elfinfo rimandati a batch dedicato
  • Boot screen confrontato con screenshot reale HPE ProLiant DL360 Gen10: aggiunta riga "Advanced Memory Protection Mode", griglia checkbox espansa 9→12 (grafico) e 7→9 (testo) con feature reali (NATBrowser, NATTour, TermSafe)
date ” ora legge il vero CMOS RTC
date v0.2.1.7
boot screen ” 12 checkbox, HPE-fidelity
boot v0.2.1.7
CANARY v0.2.1.8 Strada A: batch filesystem + FIX CRITICO heap text mode Ago 2026
  • FIX CRITICO: step_heap() era definita ma mai inserita in boot_steps[] di text mode - heap_init() non veniva mai chiamato, ogni kmalloc() falliva silenziosamente dopo un login reale
  • Scoperto testando con OCR (tesseract) dopo un login completo (root/root, ~55s boot) - mai fatto correttamente prima in questo progetto
  • 10 comandi convertiti a term_*: cp, mv, chmod, chown, df, fsinfo, find, mkfs, dd, dd-write
  • Trovato un altro helper nascosto (find_recursive, chiamata da cmd_find) con 4 vga_* mai convertite
  • Scoperto ma non risolto: ls vuoto in modalita' grafica alla root - confermato preesistente anche in v0.2.1.7 via A/B diretto, rimandato a versione dedicata
cp con login reale — "copiato 451 byte"
cp v0.2.1.8
CANARY v0.2.1.9 Strada A: multi-utente grafico reale Ago 2026
  • gfxlogin_run() (gia' completa e funzionante, prima solo demo isolata) ora collegata al vero boot grafico - sostituisce l'auto-login incondizionato come root
  • Login in loop: ESC ripresenta la schermata invece di bypassare l'autenticazione
  • Dopo login, stessa logica home-directory di text mode: /disk/home/<user> se c'e' un disco NATFS, altrimenti root del VFS
  • FIX: prompt e banner benvenuto erano hardcoded "root@natalizio:/home/root" indipendentemente da chi era davvero loggato - ora dinamici (current_user + vfs_get_path)
  • Verificato con due login distinti (root e guest): prompt e banner cambiano correttamente
  • Con questo si chiude Strada A: tutti i batch di comandi convertiti + login reale multi-utente
schermata di login reale (non piu' auto-login)
login v0.2.1.9
login come guest — prompt/banner dinamici
guest v0.2.1.9
HOTFIX v0.2.1.9.1 Fix compilazione su GCC/C23 (MSYS2 UCRT64) Ago 2026
  • kernel/graphic/natalizioos.h era una copia congelata (v0.2.1.5) del vero header, con lo stesso nome di include guard - per lo standard C, l'include tra virgolette cercava prima nella stessa cartella, trovando sempre la copia invece del vero header
  • La copia conteneva "typedef int bool;" - su GCC C23 (dove bool e' keyword riservata) e' un errore fatale, non solo un warning
  • Fix: l'header duplicato ora inoltra semplicemente al vero kernel/natalizioos.h; il typedef bool/true/false e' condizionale, definito solo se il compilatore non li fornisce gia' come keyword native
  • Verificato riproducendo l'errore esatto con gcc -std=c2x prima e dopo il fix
  • Nessuna modifica comportamentale, solo compatibilita' con compilatori piu' recenti
login + shell funzionanti dopo il fix
v0.2.1.9.1 hotfix
MAJOR v0.2.2.0 MAJOR: Strada A completata — 703 sostituzioni, 64 comandi in gfxshell Ago 2026
  • Batch finale Strada A: 703 sostituzioni vga_*→term_* su 34 funzioni, il piu' grande dell'intero progetto
  • 33 comandi convertiti: head, tail, wc, grep, version, passwd, useradd, users, sudo, visudo, sort, which, env, sleep, more, alias, set, motd, selftest, man, about, elfinfo, elfrun, elftest, hwinfo, pagecheck, selfdiag e altri
  • hwinfo da solo aveva 111 chiamate vga_* — era stato rimandato in v0.2.1.7 perche' troppo grande; ora funziona in modalita' grafica
  • Trovato un altro helper nascosto: read_password_masked() (5 vga_*), stesso pattern di tree_print_rec e find_recursive
  • Whitelist gfxshell da 30 a 64 comandi. I 36 restanti sono esclusioni deliberate e documentate: spegnimento (reboot/halt/shutdown), ring3 (ringtest), framebuffer-specifici (gfx*demo), editor con VGA diretto (nano/edit), login testuale (logout/su)
hwinfo — 111 sostituzioni, ora in grafica
hwinfo v0.2.2.0
man ls — pagina manuale in gfxshell
man v0.2.2.0
CANARY v0.2.2.1 Rimosse tutte le esclusioni — 83 comandi in gfxshell Ago 2026
  • Portati in whitelist tutti i comandi esclusi in v0.2.2.0: servite 420 sostituzioni aggiuntive, non bastava toglierli dalla lista
  • kernel/editor.c riscritto: put_cell() scriveva DIRETTO a 0xB8000 (invisibile in framebuffer). Ora usa term_set_cursor/set_color/putchar — nano ed edit funzionano finalmente in grafica
  • kernel/users.c: login_screen() (33 vga_*) e read_line_hidden() (5) convertiti — logout e su ora mostrano il prompt di login anche in grafica
  • Convertiti reboot (57), ringtest1/2/3 (163 totali), shutdown (35), halt, tutti i gfx* e bootanim-graphic
  • Whitelist da 64 a 83 comandi su 100. Attenzione: reboot/shutdown/halt ora fanno davvero quello che dicono
nano in grafica — editor.c riscritto
nano v0.2.2.1
logout — login ora visibile in grafica
logout v0.2.2.1
MAJOR v0.2.2.2 Integrazione internal_UI — Desktop2, selector ambiente, 1024x768 Ago 2026
  • Integrata la base internal_UI: ~17.000 righe nuove, 360 file, 48 sorgenti desktop2_core tutti compilati e linkati
  • Desktop2 (desktop2/gui2): window manager con drag&resize, dock, menu bar, VFS proprio + app reali — terminal, files, calc, clock, notes, paint, music, videos, writer, sheets, slides, store, spotlight, settings e minigiochi (chess, doom, tetris, pong, breakout, mines, 2048, ttt)
  • Desktop1 (desktop/gui) modernizzato: icone, taskbar con Start e orologio, ombre e gradienti
  • Menu selezione ambiente post-login a 5 card: Shell Grafica, Shell Testuale, Desktop 1, Desktop 2, Aggiornamento Software
  • Risoluzione portata a 1024x768 (gconsole 128x48), comandi resolution/setres per cambiarla a runtime
  • FIX: la build text-mode era rotta nella base originale (verificato compilando lo zip non modificato). Tre cause: gconsole_puts non protetta, system_version definita solo in graphic, gfx_watermark con fb_puts senza guardia
  • Riportate le migliorie Strada A mancanti: editor.c (put_cell scriveva ancora a 0xB8000) e users.c (login_screen), piu' 255 sostituzioni su 16 comandi. Whitelist da 72 a 90
  • Comandi shell: da 100 a 108. Build graphic 607KB, text 230KB — entrambe funzionanti
menu selezione ambiente — 5 opzioni
menu v0.2.2.2
Desktop 1 — icone + taskbar
desktop1
Desktop 2 — dock + finestre
desktop2
CANARY v0.2.2.2.1 Audit sicurezza + verifica funzionale di tutti i 108 comandi + fix gfxlogin Ago 2026
  • SICUREZZA — halt era l'unico dei tre comandi di spegnimento senza controllo privilegi: un utente guest poteva fermare la macchina (denial-of-service). Verificato con login guest
  • SICUREZZA — cat non applicava i permessi UNIX: chiunque poteva leggere file di altri utenti o di root, nonostante vfs_check_perm esistesse gia' ed fosse usato da rm/mkdir
  • SICUREZZA — write stesso problema ma distruttivo: si poteva sovrascrivere qualunque file, inclusi quelli di root
  • BUG CRITICO — in entry.asm l'istruzione di allineamento stack "and esp, 0xFFFFFFF0" distruggeva il parametro passato da Stage2. Il kernel riceveva 0, leggeva la memory map E820 dall'indirizzo sbagliato e marcava TUTTA la RAM come occupata: "mem" mostrava Frames free: 0 con 128 MB installati. Diagnosticato via UART. Risultato: da 0 a 7642 frame liberi (31368 KB)
  • BUG — il prompt grafico mostrava la home fissa invece della directory corrente: dopo "cd docs" diceva ancora /home/root mentre pwd diceva /docs
  • Watermark corretto da "NATALIZIOOS Internal" a NATALIZIOOS_SERVER_EDITION_CANARY
  • Verificati uno per uno tutti i 108 comandi in shell grafica con login reale e lettura OCR dell'output: nessuna reimplementazione necessaria, tutti rispondono correttamente
watermark corretto + alias funzionante
watermark v0.2.2.2.1
prompt segue cd, memoria corretta
prompt fix
guest: permessi negati
sicurezza
CANARY v0.2.2.2.2 Verifica comandi pericolosi + login completo Ago 2026
  • Verificati i 40 comandi esclusi dal test automatico di v0.2.2.2.1 perche' distruttivi o interattivi. Copertura da 68 a tutti i 108 comandi
  • Esito: nessun bug trovato. Le protezioni sui comandi pericolosi erano gia' implementate correttamente
  • LOGIN — credenziali sbagliate rifiutate, messaggio "Login failed: bad credentials" mostrato, retry funzionante, guest entra con privilegi ridotti (prompt $ invece di #)
  • rm su directory rifiuta con "usa -r" (protezione stile UNIX); rm -r rimuove ricorsivamente
  • mkfs chiede conferma esplicita scrivendo "CONFERMA" prima di cancellare il disco
  • reboot esegue soft reset completo e torna alla schermata di login; shutdown e halt corretti (halt nega a guest, fix v0.2.2.2.1 riconfermato)
  • ringtest/ringtest2/ringtest3 e elfrun/elftest funzionano senza crash, con avvisi onesti sui limiti noti
rm su directory: protezione "usa -r"
rm protezione
mkfs chiede CONFERMA
mkfs conferma
login: credenziali rifiutate
login errore
CANARY v0.2.3.0 / v0.2.3.1 Build unificata TEXT+GUI e recovery toolkit nativo Ago 2026
  • v0.2.3.0 — BUILD UNIFICATA: un solo binario invece di due. Il boot selector sceglie TEXT o GUI a runtime (timeout 5s → GUI)
  • Era gia' possibile senza saperlo: term.c decide gia' a runtime tra gconsole e vga guardando fb_is_active(), e il ramo testuale esisteva come fallback. Mancava solo far scegliere l'utente
  • Trappola trovata: il menu mostra "[1] Text / [2] Graphic" ma quelli sono i TASTI — la funzione ritorna 0=text, 1=graphic. Il primo tentativo mandava entrambi i rami in GUI
  • v0.2.3.1 — 6 app native su Desktop1: Disk Info (lsblk), NATSmart (smartmontools), Hardware (inxi/lshw), MemTest (memtester), Monitor (htop), Sensors (lm-sensors)
  • Tutte scritte da zero sopra driver ATA, SMART reale, CPUID, PCI, PMM, ACPI. Nessun port: dove un dato non c'e' viene dichiarato N/A col motivo, mai inventato
  • Fix: con 11 icone la colonna singola usciva sotto la taskbar. Introdotta griglia a due colonne, aggiornato anche l'hit-test del click
boot selector: TEXT o GUI, un solo binario
boot selector
Desktop1: 11 app su due colonne
desktop1 app
CANARY v0.2.3.2 Fix boot anim testuale + 4 nuove app (15 icone) Ago 2026
  • FIX segnalato con screenshot: in modalita' TEXT logo e progress bar erano spostati a destra. Causa: boot_anim.c aveva SCREEN_COLS come #define compile-time (128 se GRAPHIC, 80 altrimenti) — ma la build unificata compila sempre GRAPHIC, quindi centrava su 128 colonne su uno schermo da 80
  • Fix: SCREEN_COLS/ROWS ora decisi a runtime da fb_is_active(), come fa gia' term_*
  • "make text" ora deprecato con avviso: dalla v0.2.3.0 un solo binario fa entrambe le modalita'. Osservazione giusta dell'utente — continuavo a compilarlo per abitudine
  • 4 nuove app native: Power (powertop → CPUID leaf 6 + MSR), PCI List (lspci → pci_scan), Disk Usage (ncdu → N.A.T.FS), Hex View (xxd → MBR reale in hex+ASCII)
  • Desktop1 passa a 15 icone su tre colonne
boot text ora centrato su 80 colonne
fix boot anim
Desktop1: 15 app native
15 app
CANARY v0.2.3.3 Cartelle su Desktop1 + Task List e Boot Log Ago 2026
  • Desktop riorganizzato: da 15 icone sparse a 2 app dirette (Terminal, Notepad) + 4 cartelle tematiche in colonna ordinata
  • Cartelle: Dischi (4 app), Diagnostica (6 app), Sistema (3 app), File (2 app)
  • Doppio click sulla cartella apre una finestra con icona e descrizione di ogni app; doppio click sulla riga la lancia. Cartella gia' aperta viene portata in primo piano invece di duplicarsi
  • 2 nuove app: Task List (ps/top → scheduler reale, evidenzia il task corrente) e Boot Log (journalctl → stato verificabile di PMM, heap, VFS, N.A.T.FS, framebuffer)
  • Boot Log non inventa un log finto: ogni riga riflette lo stato reale a runtime, incluso quando qualcosa NON e' montato
  • Totale 17 app native su Desktop1
desktop riordinato: 2 app + 4 cartelle
cartelle
CANARY v0.2.3.4 / v0.2.3.4.1 Fix kernel panic + boot anim testuale risolta Ago 2026
  • KERNEL PANIC RISOLTO: "INT 0x6 Invalid Opcode" dopo il login. Il kernel era cresciuto a 1247 settori ma stage2.asm ne carica solo 1152 — gli ultimi 95 (~48 KB) non venivano mai caricati, restava spazzatura eseguita. Fix: compilazione -Os, da 638 KB a 454 KB con ~130 KB di margine
  • BOOT ANIM TESTUALE RISOLTA: nelle due versioni precedenti avevo sbagliato diagnosi. Verificato via UART che fb_is_active() e SCREEN_COLS erano corretti. La causa vera: put_at() e hpe_put() prendevano il carattere come "char" (signed su x86), ma l'animazione usa caratteri CP437 > 127 (219 blocco, 176 ombra, 196 linea) che diventavano negativi corrompendo il byte attributo — da qui il riquadro bianco
  • Fix: parametro cambiato in unsigned char. Ripristinata anche la scrittura VGA diretta quando il framebuffer non e' attivo (prima passava per term_* carattere per carattere)
  • Fix banner ROM: mostrava versione vuota perche' usava system_version, sovrascritta da init_system_version() leggendo dal disco
  • 3 nuove app: Uptime (timer PIT), VFS Tree (albero filesystem), About (neofetch-style). Totale 20 app native
boot anim testuale finalmente corretta
boot anim fix
DEV v0.2.3.5 Double buffering, UI modernizzata, CANARY → DEV Ago 2026
  • SFARFALLIO RISOLTO: fb_present() era un no-op ("framebuffer diretto, niente double buffering"). Il desktop ridisegnava tutto ogni 16 ms direttamente sul framebuffer visibile — lo schermo si puliva e ricomponeva 60 volte al secondo. Con l'overlay di debug peggiorava perche' c'era piu' da disegnare
  • Fix: double buffering vero. Backbuffer a 16 MB (dentro i 64 MB identity-mapped, sotto il limite anche con -m 64M); se la risoluzione non ci sta si scrive diretto sul LFB come prima, nessuna regressione
  • Boot selector rifatto: layout centrato con riquadri veri, opzione predefinita evidenziata, barra di avanzamento del timeout (prima i 5 secondi scorrevano senza riscontro visivo)
  • Ricomparso il bug del char signed nei bordi CP437: risolto scrivendo le celle VGA direttamente con unsigned char
  • Desktop1: sfondo con gradiente verticale invece del colore piatto
  • 2 nuove app: CPU Info (lscpu) e Display (xrandr). Totale 22 app native
  • Passaggio da CANARY a DEV: watermark NATALIZIOOS_SERVER_EDITION_DEV
boot selector modernizzato
boot selector
desktop con gradiente, rendering pulito
desktop
DEV v0.2.3.5.1 Desktop1 ridisegnato — chrome professionale Ago 2026
  • Il gradiente della v0.2.3.5 non bastava: qui e' stato rifatto il chrome, cioe' tutto cio' che circonda il contenuto
  • Nuove primitive: dt_fill_round (angoli smussati), dt_fill_grad (gradienti), dt_drop_shadow (ombre sfumate). Prima ogni elemento era un rettangolo pieno
  • Finestre: ombra da rettangolo nero pieno a tre cornici progressive; titlebar da tinta unita satura a header chiaro con gradiente e barra di accento sul bordo superiore; pulsante di chiusura ora rosso perche' e' l'azione distruttiva
  • Taskbar: gradiente verticale al posto del rettangolo piatto col bordo ciano acceso; Start con angoli smussati, icona e accento blu quando aperto
  • Icone: pastiglia scura dietro l'etichetta, il testo perdeva contrasto sul gradiente
  • Palette rivista: tinte meno sature, un solo colore di accento, rosso riservato alla chiusura
  • 2 nuove app: Utenti (tabella account, evidenzia il corrente) e Aspetto (mostra la palette effettiva). Totale 24 app native
Desktop1 con la nuova UI
desktop ridisegnato
DEV v0.2.3.5.3 → v0.2.3.6.2 Aggiornamento software, indagine ATA, fix app e Desktop2 Set 2026
  • v0.2.3.5.3 — Fix: la schermata di esito dell'update spariva subito (i tasti in coda venivano consumati dall'attesa finale): aggiunto keyboard_flush. Propagazione della versione: init_system_version() scriveva una stringa VUOTA se il settore era vuoto — era la causa del banner "System ROM: NAT-32 v" senza versione. Rollback: se l'applicazione e' parziale la versione NON viene promossa
  • v0.2.3.6 — ata_write_sector non scriveva davvero: mancavano il ritardo di 400 ns dopo la selezione del drive e il controllo del bit ERR (restituiva sempre "successo" anche fallendo). Verificata con scrittura+rilettura e persistenza confermata sul file immagine
  • Trovate due collisioni nella mappa del disco: l'area di staging kernel si sovrapponeva ai settori di versione, e il backup kernel era previsto a LBA 4096 su un disco di 3072 settori — cioe' fuori dal disco. Disco portato a 12288 settori (6 MB)
  • v0.2.3.6.1 — Indagine sul blocco dell'update: tracciato via UART, il pacchetto viene letto e interpretato correttamente (path/size/lba esatti), ma la SECONDA lettura ATA si blocca. Scartate quattro ipotesi (stack overflow, IRQ ATA non gestiti, FLUSH CACHE, parsing). Causa non trovata: modulo riportato alla versione verificata funzionante
  • v0.2.3.6.2 — Le 4 app di Sistema non si vedevano: la finestra cartella era alta 190 px e a 20 px per riga mostrava solo 7 voci su 11 — le ultime erano disegnate fuori dai bordi. Altezza ora calcolata sul numero di voci
  • Desktop2 bloccato a meta' schermo: il double buffering del kernel confliggeva col buffer interno di desktop2, che fa aggiornamenti parziali. Disattivato li'; Desktop1 continua a usarlo
DEV v0.2.3.7 Watermark, overlay debug spegnibile, 2 nuove app Set 2026
  • WATERMARK senza versione: init_system_version() scriveva una stringa VUOTA se il settore era vuoto. Questa correzione era gia' stata fatta in v0.2.3.5.3 ma e' andata persa in v0.2.3.6.1, quando ho ripristinato l'intero file per rimediare a un'altra regressione — il rischio del ripristino in blocco
  • Ora il valore viene validato E il watermark ha un fallback esplicito sulla versione di compilazione: non puo' piu' restare vuoto qualunque cosa succeda a monte
  • Rimessa l'ORA di build accanto alla data, per distinguere due build dello stesso giorno. Verificato: "Build v0.2.3.6.2-DEV (Sep 2 2026 09:40:08)"
  • Rimossa una scritta di debug che init_system_version() disegnava a y=500 durante il boot
  • Overlay di debug: era sempre acceso e copriva l'angolo in alto a destra. Ora parte spento, si accende col tasto 'd'
  • 2 nuove app (28 totali): Test Tastiera (showkey) e Prestazioni (sysbench). Quest'ultima dichiara a schermo che i valori non sono confrontabili con altri sistemi
desktop pulito, watermark corretto
v0.2.3.7
DEVv0.2.3.8 Lo sfarfallio non era mai stato risolto Set 2026
  • Desktop1 sfarfallava ancora: fb_set_double_buffer(1) era finito dentro dt_launch_app(), nel ramo di USCITA dal Notepad. Si attivava solo dopo aver aperto e chiuso il blocco note — per tutto il resto del tempo il desktop disegnava diretto sul framebuffer. Il fix di tre versioni fa non ha mai avuto effetto
  • Nei miei test l'avevo "verificato" guardando uno screenshot statico, che per definizione non puo' mostrare uno sfarfallio
  • Overlay di debug incoerente: spento su Desktop1 (senza modo di accenderlo), acceso di default su Desktop2. Ora entrambi partono spenti con un pulsante DEBUG in taskbar — soluzione che Desktop2 aveva gia'
  • 2 nuove app (30 totali): Orologio (CMOS RTC) e Risoluzione (modalita' video). Quest'ultima mostra le modalita' ma non offre il cambio a caldo: il desktop e' gia' disegnato per la risoluzione corrente
taskbar col pulsante DEBUG
v0.2.3.8
DEVv0.2.3.9 App Comandi e Sessione Set 2026
  • Comandi: elenco sfogliabile di tutti i comandi della shell con descrizione. Non ha una lista scritta a mano — legge shell_commands[], la stessa tabella che la shell usa per il dispatch, quindi un comando nuovo compare automaticamente
  • Sessione: utente, UID, privilegi, home, directory corrente e tipo di terminale
  • Totale 32 app native su Desktop1
DEVv0.2.4.0 3 nuove app diagnostiche (35 totali) Set 2026
  • Log Sistema (dmesg): stato verificabile di 8 sottosistemi. Non mostra uno storico perche' nel kernel non esiste un buffer di log — i messaggi vanno su UART e non sono conservati. Invece di inventarlo, ricostruisce lo stato adesso
  • Porte I/O (/proc/ioports): mappa delle porte usate dai driver. E' statica ed e' corretto che lo sia: su x86 le porte legacy sono fisse per architettura
  • Integrita' (md5sum): checksum di bootloader, kernel e settore versione, confrontabile fra due avvii. Dichiara a schermo il proprio limite — rileva corruzione accidentale, non manomissioni
DEVv0.2.4.1 Firmware NATALIZIOFIR 6.10 — nove bug e avvio completo Set 2026

Il firmware ora avvia NATALIZIOOS. Catena verificata: firmware → kernel → login grafico → menu ambienti → desktop.

  • Non compilava affatto: tre direttive db mancanti e un operando di dimensione errata
  • add bx, 512 con BX a 16 bit: dopo 128 settori il kernel si sovrascriveva. Corretto avanzando il segmento
  • jmp wait_for_boot riazzerava il contatore del timeout: restava sullo splash all'infinito
  • dd gdt_start era un offset, non l'indirizzo lineare: stage2 sta a 0x8000
  • 1152 settori caricati arrivavano a 0xA0000, la memoria video
  • Il volume FAT veniva scritto DOPO il kernel e lo cancellava: a LBA 2000 c'erano solo zeri mentre lo script dichiarava successo
  • jmp CODE_SEG:protected_mode non rilocato: saltava a 0x1bf, dentro la tabella dei vettori di interrupt
  • Il bug finale: call KERNEL_TARGET con una costante viene assemblato da NASM come call RELATIVO. Saltava a 0x6790 invece che a 0x100000. Corretto con mov eax, KERNEL_TARGET + call eax
  • Metodo: gli screenshot mostravano il sintomo senza la causa. La svolta con -d int,cpu_reset, -d exec e xp /8xb 0x100000 dal monitor
login avviato dal firmware
login da firmware
desktop raggiunto
desktop
DEVv0.2.4.2 Catena completa: firmware → boot selector → sistema Set 2026

Sequenza intera verificata dall'accensione al desktop.

  • Il boot selector c'era gia' e funzionava: nei test della v0.2.4.1 non lo vedevo perche' scade dopo 5 secondi e i miei screenshot arrivavano dopo. Catturato ora a 7 secondi dall'avvio
  • NATALIZIOFIR 6.10 → boot selector NATALIZIOOS → modalita' scelta
  • Tasto 1 → boot animation testuale → login testuale: verificato
  • Tasto 2 o timeout → boot grafico → login → menu ambienti → Desktop: verificato
  • Nota sui tempi: la finestra per scegliere la modalita' testuale sono i 5 secondi del timeout, che scattano ~4 secondi dopo l'avvio del firmware. Nei primi test premevo troppo tardi e sembrava che la scelta non funzionasse
boot selector dopo il firmware
boot selector
DEVv0.2.4.2 Il firmware NATALIZIOFIR avvia NATALIZIOOS Set 2026

Catena completa funzionante: NATALIZIOFIR 6.10 (POST, Setup F2, Boot Menu F8, Diagnostica F9/F10) → Stage2 NATALIZIOOS → boot selector → login → menu ambienti → Desktop. Un comando: make run-firmware

  • Tasti fantasma: lo splash rilevava i tasti con INT 16h, che segnalava dati anche senza pressioni. Uno scancode spurio 0x3C (F2) o 0x43 (F9) faceva aprire da solo Setup o Diagnostica, da cui partiva l'aggiornamento firmware che si piantava. Individuato leggendo i registri a macchina ferma (CS=0800 EIP=4907) e incrociandoli col listato NASM: era la riga .sa_fail_wr. Corretto leggendo i puntatori del buffer tastiera del BIOS — F2/F8/F9/F10 restano funzionanti
  • int 0x13 che non ritornava: i dati arrivavano a 0x7E00 (verificato byte per byte) ma l'esecuzione non proseguiva. Risolto eliminando la lettura: lo Stage2 NATALIZIOOS e' incorporato dentro stage2 del firmware a offset 0xC000, gia' in memoria a 0x14000. Il passaggio e' una copia piu' un salto, senza toccare il disco
  • Architettura: il firmware non carica il kernel, carica il bootloader — come un computer vero
  • Altri difetti corretti: kernel assente dall'immagine (stub 86 byte), FAT12 dichiarato FAT16, tabella FAT oltre i 64 KB, GDT da offset, salto non rilocato, call relativo, vettore INT 13h mai salvato, dap_lba_high sporco, splash e boot selector senza timeout
NATALIZIOFIR 6.10
firmware
login avviato dal firmware
login
DEVv0.2.4.3 Otto nuove applicazioni Set 2026

Otto app distribuite nelle cartelle per argomento, non ammucchiate in una sola.

Diagnostica

  • Interrupt: legge le maschere del PIC (0x21 e 0xA1) e mostra quali delle sedici linee IRQ sono attive, con l'assegnazione di ciascuna
  • Heap: memoria dinamica del kernel, con barra che diventa rossa oltre l'85%
  • Vel. Disco: misura reale, legge 128 settori consecutivi cronometrando col PIT e riporta KB/s

Sistema

  • Mappa RAM: le regioni E820 lette dalla tabella che il bootloader lascia a 0x7000, con base, dimensione, tipo e totale utilizzabile
  • Limiti: capienze del sistema affiancate all'uso corrente, in rosso oltre l'80%

File

  • Calendario: mese corrente dal CMOS con il giorno di oggi evidenziato, formula di Zeller e anni bisestili
  • Cronometro: minuti, secondi e centesimi sui tick del PIT
  • Convertitore: decimale, esadecimale, binario, ottale e due conversioni di unita'

DT_FOLDER_MAX_APPS alzato da 14 a 16: Diagnostica e Sistema erano a 13 e le nuove non ci stavano.

Desktop v0.2.4.3
desktop
DEVv0.2.4.4 Data, cruscotto, interfaccia Set 2026

Tre interventi distinti.

Data della boot animation

08/2026 → 09/2026, corretta in drivers/boot_anim.c (due punti) e kernel/gfxboot.c.

Cruscotto in Desktop 2

Accanto al pulsante DEBUG, che resta intatto, c'e' ora CRUSCOTTO: apre una sovrapposizione a schermo intero con cinque pannelli sullo stato del sistema. Il desktop sotto non viene toccato.

  • Panoramica: memoria, task, uptime con barre di occupazione
  • Sistema: versione, architettura, frame, tick del PIT
  • Memoria: totale, uso, libera e andamento degli ultimi 60 secondi
  • Dischi: controller ATA, lettura LBA 0, firma di avvio, primi byte
  • Attivita': task, tempo acceso, battito del sistema

Tutti i valori sono letti dal sistema, non scritti a mano. Si naviga con i tasti 1-5 o cliccando la barra laterale; ESC chiude.

Nota: Desktop 2 ha sotto quasi 13.000 righe e 28 applicazioni in desktop2_core/. Il file kernel/desktop2.c e' solo il ponte verso mouse, tastiera e framebuffer.

Cruscotto di sistema, pannello Panoramica
Cruscotto di sistema, pannello Panoramica
DEVv0.2.4.5 Sfondo di Desktop 2 ridisegnato Set 2026

Sfondo : cielo al tramonto e catena montuosa. Disegnato, non fotografato: una foto da 1024×768 occuperebbe megabyte dentro il kernel, qui il paesaggio viene calcolato a ogni disegno e costa qualche centinaio di byte.

  • Cielo: sfumatura dal violetto al pesca
  • Nuvole: tre fasce a quote diverse
  • Monti: tre piani, dai lontani in foschia ai vicini in ombra
  • Neve: solo sulle vette oltre una quota, spessore crescente e bordo sfumato
  • Luce del tramonto: sui versanti rivolti a destra

Il profilo nasce da una funzione pseudo-casuale deterministica: stesso profilo a ogni avvio, nessuna tabella da memorizzare.

Due correzioni durante la lavorazione. Le onde da 16 pixel facevano sembrare i monti una lama di sega: portate a 190, con interpolazione morbida al posto di quella lineare. E la luce del tramonto misurava la pendenza su un pixel solo, lasciando strisce verticali sulle pareti: ora si calcola su una finestra di venti pixel.

Paesaggio calcolato: cielo, monti, neve
Paesaggio calcolato: cielo, monti, neve
DEVv0.2.4.6 Interfaccia di Desktop 2 ammodernata Set 2026

Nella versione precedente era cambiato solo lo sfondo. Qui cambia l'interfaccia.

Barra superiore

Da gradiente beige con testo nero a barra scura traslucida con testo chiaro: si intuisce il paesaggio dietro. La trasparenza e' calcolata a mano mescolando ogni pixel col nero al 72%, perche' il framebuffer non ha un canale alfa.

Dock

Da pannello crema a scuro traslucido, stessa tecnica. Gli angoli arrotondati sono rispettati nella fusione, altrimenti la trasparenza sbordava. Icone da 40 a 52 pixel, angoli da 8 a 12.

Finestre

Barra del titolo da 22 a 28 pixel, gradiente quasi piatto, pallini da raggio 5 a 6 e piu' distanziati.

Barra e dock scuri traslucidi
Barra e dock scuri traslucidi
DEVv0.2.4.7 Icone ed etichette Set 2026

Icone con volume

Le piastrelle erano quadrati di tinta piatta con un lucido in cima. Ora hanno una sfumatura verticale su tutta l'altezza e un filo bianco sul bordo superiore. La sfumatura rispetta gli angoli arrotondati sia sopra che sotto.

Etichette leggibili

Con lo sfondo a montagne il testo bianco spariva sulle creste innevate e sul cielo chiaro, e l'ombra da sola non bastava. Ogni nome sta ora su una pastiglia semitrasparente con angoli smussati: non copre il paesaggio, ma si legge ovunque capiti l'icona.

Sul testo

L'antialiasing non e' stato fatto: il font e' bitmap 8×16, ogni carattere e' una griglia di punti accesi o spenti. Per ammorbidirlo servirebbe un font nuovo con livelli di grigio e un motore di disegno che li usi, un lavoro a se'. Qui e' stato risolto il problema vero, che era il contrasto.

Icone con volume, etichette su pastiglia
Icone con volume, etichette su pastiglia
DEVv0.2.4.8 Tremolio risolto: composizione fuori campo Set 2026

Segnalato: su Desktop 2 il cruscotto e il pulsante DEBUG tremolano.

La causa

A ogni fotogramma desktop2_core disegnava tutto in un buffer, d2_blit lo copiava sullo schermo, e poi cruscotto e pulsanti venivano disegnati direttamente sulla superficie visibile. Al giro dopo la copia li cancellava e venivano ridisegnati: quella finestra fra cancellazione e ridisegno si vedeva a occhio.

La correzione

Non serve una GPU virtuale, ma l'idea di fondo era giusta: mancava il doppio buffer per le sovrapposizioni. In drivers/fb.c la destinazione di disegno e' ora reindirizzabile; tutte le primitive passano da fb_put_pixel, quindi e' bastato aggiungere il reindirizzamento li'.

fb_set_target(buf, w, h, pitch) disegna nel buffer, fb_reset_target() torna allo schermo. Desktop 2 compone cruscotto e pulsanti dentro il buffer prima della copia: lo schermo riceve un fotogramma gia' finito e non vede mai uno stato intermedio.

Verifica

Quattro fotogrammi consecutivi, impronta della barra laterale del cruscotto: c7d3e08bc1 quattro volte, identiche. Nella zona dei pulsanti 658 pixel chiari in tutti i fotogrammi; prima il conteggio variava.

Il meccanismo non e' specifico di Desktop 2: qualunque parte del sistema puo' comporre fuori campo e riversare in un colpo solo.

Cruscotto stabile, senza tremolio
cruscotto
DEVv0.2.4.9 Antialiasing del testo e adattatore video virtuale Set 2026

Antialiasing: font con livelli di copertura

Il font resta 6×8, ma ogni pixel ha ora una copertura da 0 a 255 invece che acceso/spento. Ogni glifo viene ingrandito tre volte con scale3x, che sui contorni obliqui inserisce i pixel intermedi; il risultato a 18×24 viene ridotto contando quanti dei nove sotto-pixel sono accesi.

.###..          099900
#...#.          930390
#####.   ->     999990
#...#.          930390
#...#.          900090

Le aste restano piene, solo i bordi obliqui prendono valori intermedi. Sfocare e basta non andava bene: la sfocatura ammorbidisce tutto, aste comprese, e a questa dimensione il testo diventa illeggibile. Costo: 6 KB di tabella.

Adattatore video virtuale

Nuovo drivers/vgpu.c, nello spirito del Basic Display Adapter: chi disegna non parla piu' con lo schermo ma con un adattatore che gli presta una superficie e decide quando mandarla a video. Niente stati intermedi, copia della sola zona cambiata, e misure leggibili. Nuovo pannello Video nel cruscotto (tasto 6).

Due difetti trovati collegandolo

fb_clear e fb_fill_rect scrivevano diritte in memoria video saltando fb_put_pixel: il riempimento finiva sullo schermo mentre il testo finiva nel buffer, e la sovrapposizione appariva trasparente.

Rosso e blu risultavano invertiti: la superficie di Desktop 2 tiene il rosso nel byte basso, fb_put_pixel in alto. Aggiunto fb_set_target_ex(..., swap_rb), con conversione in scrittura e in lettura cosi' anche la mescolanza dell'antialiasing resta corretta.

Pannello Video: 151 fotogrammi contati
vgpu
DEVv0.2.4.10 L'adattatore video vale per tutto il sistema Set 2026

Nella 0.2.4.9 l'adattatore serviva solo a Desktop 2. Ora e' un servizio comune.

Il driver video aveva gia' un doppio buffer (fb_set_double_buffer e fb_present), ma veniva acceso a mano in punti sparsi e non misurava nulla. Nessuno lo accendeva per il menu di avvio o per Desktop 1.

vgpu_sys_enable();
vgpu_sys_begin();
   ...disegno...
vgpu_sys_present();

Acceso all'ingresso di Desktop 1, Desktop 2 e del menu di avvio. Tutte le chiamate sparse a fb_present() passano ora dall'adattatore. Chi non lo usa continua a funzionare come prima: l'adattatore non si impone a nessuno.

Misure comuni

Il pannello Video distingue fotogrammi di Desktop 2 e fotogrammi totali. Nella prova: 302 totali contro 152 del solo Desktop 2 — la differenza e' il resto del sistema, che prima non veniva contato.

Modalita' sistema attiva, 302 fotogrammi
vgpu sistema
DEVv0.2.4.11 Due stili di resa per tutto il sistema Set 2026

L'adattatore video non decide piu' solo come il fotogramma arriva a video, ma anche con quale resa viene disegnato.

  • Base — l'aspetto attuale: tinte piatte, bordi netti, nessun costo aggiuntivo. E' il predefinito.
  • Realistica — profondita': sfumature al posto delle tinte piatte, ombre sotto gli elementi sollevati, luce che cala verso i bordi.

Uno solo per tutto il sistema

Cambiando stile cambiano insieme Desktop 1 (sfondo con caduta di luce, ombre piu' profonde), Desktop 2 (tramonto piu' caldo, barre del titolo sfumate), il login (sfondo sfumato invece di tinta unica, in 5 punti) e le finestre.

Si sceglie dal cruscotto, pannello Video, con due riquadri cliccabili, oppure col tasto S.

Aiuti per chi vuole adeguarsi

vgpu_shade(rgb, delta)
vgpu_panel(x, y, w, h, rgb)
vgpu_shadow(x, y, w, h, profondita)

In stile base non fanno nulla e costano un confronto. vgpu_shadow legge il pixel e lo scurisce invece di scriverci sopra un grigio: cosi' l'ombra segue il colore di cio' su cui cade.

Onesta' sulla copertura: boot animation e shell includono l'adattatore ma non lo interrogano ancora. Sono collegate per essere pronte, ma la resa realistica li' e' ancora da fare.

Scelta dello stile nel pannello Video
stili
DEVv0.2.4.12 Supporto fino a 1 GB di RAM Set 2026

Lo stesso binario funziona con 32, 64, 128, 256 MB e 1 GB.

Due limiti, non uno

Il gestore della memoria fisica aveva un tetto di 128 MB: alzarlo costa solo la mappa dei frame liberi, 32 KB per 1 GB. Ma la paginazione ne mappava 64, cioe' meno di quanto il gestore dichiarasse gia' di reggere. Portata da 16 a 256 tabelle: statiche, perche' la paginazione si accende prima che il gestore sia utilizzabile, e in memoria non inizializzata, quindi il binario non cresce.

Il difetto scoperto alzando il tetto

total_frames valeva sempre il tetto gestibile, non la memoria installata. Con il tetto a 128 MB l'errore passava inosservato; a 1 GB una macchina con 32 MB dichiarava 1 GB totali e zero liberi.

             prima                dopo
-m 32M    1024 MB / 0 liberi    31 MB
-m 256M   1024 MB / 221 liberi  255 MB / 221 liberi
-m 1024M  1024 MB / 999 liberi  1029 MB / 999 liberi

Verifica: avvio completo fino al login con tutte e cinque le dimensioni, pannello Memoria controllato su 32, 64, 256 e 1024 MB.

Pannello Memoria con 1 GB installato
1GB
DEVv0.2.4.13 La resa realistica adesso si vede Set 2026

Segnalato: la modalita' realistica non era realistica. Era vero — qualche sfumatura e un'ombra, differenze troppo sottili per meritare quel nome.

Sei effetti nuovi nell'adattatore

  • vignette — scurisce verso i bordi: e' il motivo per cui una foto sembra una foto e non un campione di colore
  • grain — micro-variazioni di luminosita': nessuna superficie reale e' perfettamente uniforme, e l'occhio se ne accorge
  • bevel — filo chiaro sopra e scuro sotto: fa sembrare un rettangolo un oggetto con spessore
  • glass — banda di luce obliqua: dice che la superficie e' lucida
  • ao — scurisce dove due superfici si incontrano
  • glow — bagliore attorno agli elementi attivi

Dove sono applicati

Sfondo di Desktop 2 (grana e vignettatura — senza queste il paesaggio resta "disegnato" per quanto siano curati i monti), barre del titolo (smusso e riflesso), dock (riflesso, occlusione e ombra: diventa un pannello di vetro appoggiato sopra il desktop), icone (smusso, lucido, ombra).

Nota tecnica: la grana usa una funzione pseudo-casuale deterministica — stesso punto, stesso valore — altrimenti il fondo tremolerebbe a ogni fotogramma. Il riflesso ricava l'inclinazione dalla somma di riga e colonna, e la banda si assottiglia scendendo.

Resa realistica: vignettatura, grana, spessore
realistica
DEVv0.2.5.0 Tre driver nuovi: processore, orologio, altoparlante Set 2026

drivers/cpu.c — identificazione del processore

La schermata di avvio dichiarava "QEMU Virtual CPU", "version 2.5+", "1 vCPU" scritti a mano nel sorgente: restavano quelli su qualunque macchina, esattamente come la memoria che diceva sempre 128 MB. Ora produttore, nome commerciale, famiglia, modello e sette capacita' (FPU, TSC, MSR, PAE, APIC, SSE, SSE2) vengono letti via CPUID.

Frequenza misurata, non dichiarata: si contano i cicli del processore in un decimo di secondo scandito dal timer. Sulla macchina di prova: 2099 MHz. Prima di usare CPUID il driver verifica che esista, provando a cambiare il bit 21 di EFLAGS — sui 386 quel bit e' fisso, ed e' cosi' che si distinguono.

drivers/rtc.c — orologio di sistema

La lettura dell'orologio era duplicata in quattro punti (commands.c, desktop.c, gfxshell_main.c, dos.c), con piccole differenze fra le copie. Qui ce n'e' una sola, che gestisce le due insidie del CMOS: l'orologio puo' essere in mezzo a un aggiornamento mentre lo si interroga, e restituisce i valori in BCD, dove 0x25 significa 25 e non 37.

drivers/speaker.c — altoparlante di sistema

Tre segnali riconoscibili: due note ascendenti per l'esito positivo, due discendenti per l'errore, tre brevi per l'attenzione. Non serve a fare musica: un errore di avvio che si sente e' piu' utile di uno che scorre via.

Nuovo pannello Processore nel cruscotto (tasto 7): mostra quello che i driver leggono, cosi' i dati sono verificabili invece che dichiarati.

Pannello Processore: dati reali dai nuovi driver
driver
DEVv0.2.5.1 Perche' le build arrivavano rotte Set 2026

Due compilatori diversi

Qui si compila con gcc, sulla macchina di sviluppo con clang. kernel/desktop2.c usava pmm_used_frames, task_count, heap_total, heap_used e ata_read_sector senza averne la dichiarazione: per gcc e' un avviso e la build passa, per clang e' un errore e si ferma. Ecco perche' ogni pacchetto arrivava da riparare a mano.

Aggiunti gli include mancanti, e soprattutto -Werror=implicit-function-declaration nel Makefile: ora gcc e' severo quanto clang. La seconda correzione conta piu' della prima — impedisce che se ne ripresenti un altro dello stesso tipo.

Modalita' realistica: era inusabile

Vignettatura e grana chiamavano fb_get_pixel e fb_put_pixel per ogni pixel: a 1024×768 sono oltre un milione e mezzo di chiamate a funzione per fotogramma. Riscritte con accesso diretto alla superficie tramite puntatore.

stile base        302 fotogrammi
stile realistico  304 fotogrammi

Errore in modalita' testo: non riprodotto. Il page fault durante "Paging (4 KB)" qui non si verifica, provato con 64 MB sia dall'immagine testuale sia da quella grafica. Queste sono pero' le prime build compilate con i controlli severi attivi: e' possibile che il guasto dipendesse da come erano state sistemate a mano le dichiarazioni mancanti.

304 fotogrammi in modalita' realistica
prestazioni
DEVv0.2.5.2 passo 1: runtime Objective-C Set 2026

L'archivio di NATALIZIOOS(2023) contiene 1.558.070 righe di C e Objective-C. AppKit — la parte che serve per il terzo desktop — poggia su quattro strati che NATALIZIOOS(2026) non ha: runtime a oggetti, Foundation, Mach e Display PostScript. Il runtime e' il primo: senza, non si compila una sola riga del resto.

Strutture identiche all'originale

Stessi campi, stesso ordine, stessi nomi di src/objc-1/objc.h. Non e' pignoleria: il codice di AppKit accede a questi campi direttamente, e un ordine diverso lo farebbe leggere nel posto sbagliato.

Tre meccanismi

  • Selettori — un nome di metodo ridotto a un numero: confrontare due metodi costa un confronto di puntatori invece di scorrere due stringhe
  • Registro delle classi — ogni classe si ritrova dal nome
  • Invio dei messaggi — si cerca nella classe, poi nelle superclassi, con una cache a 64 posizioni

La prova

Il pannello non mostra numeri dichiarati: crea davvero una classe Punto, sottoclasse di Object, ne fa un'istanza e le manda messaggi. La riga che conta e' "ereditato da Object": name non e' definito in Punto, viene trovato risalendo la catena. E' l'ereditarieta' che funziona.

Cosa manca

Categorie, protocolli, posing, caricamento dinamico, eccezioni, conteggio dei riferimenti. E objc_msgSend e' scritto in C: passa bene i primi due argomenti, mentre l'originale e' in assembly e li passa tutti senza toccarli. Serve la versione in assembly prima di Foundation.

Classe creata e messaggi inviati dal vivo
runtime objc
DEVv0.2.5.2 passo 1: runtime Objective-C Set 2026

Perche' si parte da qui

L'archivio contiene **1.558.070 righe** di C e Objective-C. AppKit -

la parte che serve per il terzo desktop - e' scritta in

Objective-C e poggia su quattro strati che NATALIZIOOS(2026) non ha:

runtime a oggetti, Foundation, Mach e Display PostScript.

Il runtime e' il primo: senza, non si compila una sola riga del

resto. E' l'unico punto di partenza possibile.

Cosa c'e' in questo passo

`objc/objc.h` e `objc/runtime.c` — **le strutture rispettano

esattamente quelle originali** (`src/objc-1/objc.h` e `objc-class.h`):

stessi campi, stesso ordine, stessi nomi. Non e' pignoleria: il

codice di AppKit accede a questi campi direttamente, e un ordine

diverso lo farebbe leggere nel posto sbagliato.

**Tre meccanismi, in ordine di dipendenza:**

1. **Selettori** — un nome di metodo ridotto a un numero:

`"setTitle:"` diventa sempre lo stesso puntatore in tutto il

sistema, e confrontare due metodi costa un confronto di puntatori

invece di scorrere due stringhe.

2. **Registro delle classi** — ogni classe si ritrova dal nome, come

serve a `objc_getClass`.

3. **Invio dei messaggi** — dato un oggetto e un selettore, trovare

il codice: si guarda nella classe, poi nella superclasse, fino

alla radice. Con una cache a 64 posizioni, perche' senza sarebbe

lentissimo.

`objc/Object.c` — la classe radice, come nell'originale: `alloc`,

`init`, `free`, `self`, `class`, `name`, `respondsTo:`.

La prova

Nuovo pannello **Objective-C** nel cruscotto (tasto 8). Non mostra

numeri dichiarati: **crea davvero una classe** (`Punto`, sottoclasse

di `Object`), ne fa un'istanza e le manda messaggi.

Classi registrate 2

Selettori 9

Messaggi inviati 705

Trovati in cache 700

[Punto alloc] init Punto

ereditato da Object Punto <- name non esiste in Punto

risponde a init si

risponde a volare no

La riga che conta e' la seconda: `name` non e' definito in `Punto`,

viene trovato risalendo la catena delle superclassi. E' l'ereditarieta'

che funziona.

Cosa manca, detto subito

Categorie, protocolli, posing, caricamento dinamico, eccezioni,

conteggio dei riferimenti. E `objc_msgSend` e' scritto in C: passa

bene i primi due argomenti, mentre l'originale e' in assembly e li

passa tutti senza toccarli. Serve la versione in assembly prima di

arrivare a Foundation.

La strada davanti

Classe creata e messaggi inviati
v0.2.5.2
DEVv0.2.5.3 passo 2: objc_msgSend in assembly Set 2026

Il difetto che bloccava tutto

La versione in C del passo 1 chiamava il metodo passandogli solo

`self` e `_cmd`. Qualunque metodo con argomenti veri - e in AppKit

sono la maggioranza - riceveva spazzatura.

La soluzione, la stessa dell'originale

`objc_msgSend` **non deve copiare gli argomenti: deve lasciarli dove

sono e saltare al metodo**. Il metodo trova la pila esattamente come

l'ha preparata il chiamante, e quando finisce torna direttamente a

lui.

Dal C non si puo' esprimere "salta a questa funzione lasciando

intatti gli argomenti". Per questo l'originale

(`src/objc-1/objc-msg-i386-nolock.s`) e' in assembly, e per questo lo

e' anche il nostro `objc/msgsend.asm`.

Il dettaglio che fa tutto e' l'ultima istruzione: **`jmp`, non

`call`**. La pila resta quella del chiamante.

Divisione dei compiti

Separare le due cose permette di scrivere in C la parte complicata e

in assembly solo la parte che il C non sa esprimere.

`objc_msgSendSuper`

Con `super` la ricerca non parte dalla classe del ricevente ma da

quella indicata, e il metodo deve ricevere **l'oggetto vero, non la

struttura**: il primo argomento sulla pila viene sostituito prima del

salto. E' cio' che fa funzionare `[super init]`.

La prova

Nel pannello Objective-C due righe nuove, con metodi che prendono

argomenti:

[p setX:30 y:12] somma 42

[p moltiplica:2 per:3 per:7] 42

Entrambe in verde. Il secondo caso ha **tre** argomenti: il terzo era

quello che con la versione in C si perdeva sempre.

Cosa manca ancora del runtime

Inoltro dei messaggi (`forward::`), categorie, protocolli, posing,

caricamento dinamico, eccezioni, conteggio dei riferimenti. Quando un

metodo non esiste si restituisce `nil` invece di invocare l'inoltro.

Argomenti passati intatti: due volte 42
v0.2.5.3
DEVv0.2.5.4 passo 3: Foundation Set 2026

Lo strato su cui AppKit poggia direttamente. Tre classi, nell'ordine

in cui dipendono l'una dall'altra.

NSObject

Ciclo di vita, identita', interrogazione. I nomi dei metodi sono

quelli originali (`src/Kits/Foundation/NSObject.h`): `retain`,

`release`, `dealloc`, `retainCount`, `isEqual:`, `hash`,

`isKindOfClass:`, `isMemberOfClass:`, `respondsToSelector:`,

`description`. AppKit li chiama cosi', e cambiarli significherebbe

riscrivere AppKit.

**Conteggio dei riferimenti**, come nell'originale: ogni oggetto sa

da quanti e' trattenuto e si libera quando nessuno lo trattiene piu'.

Il contatore sta subito dopo `isa`, quindi tutte le sottoclassi lo

ereditano e `retain`/`release` funzionano su qualunque oggetto senza

che debbano occuparsene.

`isKindOfClass:` risponde vero anche per le superclassi,

`isMemberOfClass:` solo per la classe esatta: la differenza conta e

viene verificata.

NSString

Testo immutabile. **Nota onesta**: l'originale usa `unichar` a 16

bit, qui il testo e' a 8 bit. Il passaggio a Unicode e' rimandato

perche' AppKit funziona anche cosi', e la conversione va fatta una

volta sola quando servira' davvero.

NSArray

Elenco a capacita' crescente. L'originale distingue `NSArray` da

`NSMutableArray`; qui c'e' una classe sola con i metodi di entrambe.

**L'elenco trattiene cio' che contiene e lo rilascia quando viene

distrutto**: e' la regola di Foundation, e AppKit ci conta.

La prova

Nuovo pannello **Foundation** (tasto 9). Crea oggetti veri e verifica

ogni comportamento:

testo NATALIZIOOS

length 11

isEqualToString: si

isKindOfClass: NSObject si

count dopo due add 2

objectAtIndex:1 NATALIZIOOS

retainCount dopo l'add 2 <- l'array trattiene

dopo release dell'array 1 <- e rilascia

Oggetti creati 348

Ancora vivi 0 <- nessuna perdita

Tutte le righe in verde. Quella che conta di piu' e' l'ultima: 348

oggetti creati e tutti distrutti, nessuna perdita di memoria.

Dove siamo

Foundation: 348 oggetti, 0 rimasti vivi
v0.2.5.4
DEVv0.2.5.5 passo 4: due motori di disegno Set 2026

Come richiesto: **entrambi, non uno al posto dell'altro.**

PostScript

Il modello ispirato a NeXT: si costruisce un tracciato (`PSmoveto`,

`PSlineto`, `PScurveto`), si imposta lo stato grafico (colore,

spessore, trasformazione) e poi si riempie o si contorna.

I nomi sono identici a quelli di `src/Kits/AppKit/psops.h`, cosi' il

codice di AppKit compila senza modifiche.

  • **Riempimento a scansione**: per ogni riga si trovano le

intersezioni col tracciato, si ordinano e si riempie fra coppie

consecutive. Gestisce anche le forme concave.

  • **Curve di Bezier** approssimate a sedici segmenti: di piu' non si

distinguerebbero, di meno si vedrebbero gli spigoli.

  • **`PSgsave`/`PSgrestore`** impilano lo stato grafico: e' il

meccanismo su cui AppKit costruisce il disegno annidato delle

viste.

Diretto

Rettangoli e linee scritti subito nel framebuffer, senza tracciato

ne' trasformazioni. Non sa fare curve ne' rotazioni, ma dove serve un

rettangolo pieno costa una frazione.

Perche' entrambi

Il PostScript serve a far girare AppKit senza toccarlo; il diretto

serve dove la velocita' conta piu' della fedelta'. **Il terzo desktop

potra' scegliere per ogni elemento**, invece di subire un compromesso

unico.

Sotto, i due condividono lo stesso rasterizzatore e la stessa

superficie: cambiare motore non cambia dove si disegna.

Un difetto trovato disegnando

I colori uscivano invertiti: il blu diventava arancione. La

superficie di lavoro tiene il rosso nel byte basso, mentre `RGB()` lo

mette in alto. Lo scambio ora avviene **una volta sola dentro il

motore**, invece di lasciarlo da ricordare a ogni chiamata.

Verificato campionando i pixel: poligono PostScript e rettangolo

diretto danno entrambi `(0, 140, 216)`, il blu atteso.

Il controllo severo ha gia' pagato

`-Werror=implicit-function-declaration`, aggiunto nella 0.2.5.1, ha

fermato la compilazione su `RGB` non dichiarata in `gfx/render.c`.

Con gcc permissivo sarebbe passata, e sarebbe arrivata rotta.

Dove siamo

I due motori affiancati
v0.2.5.5
DEVv0.2.5.6 passo 5: AppKit Set 2026

Le tre classi che reggono un'interfaccia.

L'idea che tiene insieme tutto

**La gerarchia delle viste.** Ogni vista ha coordinate relative al

genitore, e disegnandosi sposta l'origine per i figli. Cosi' una

vista **non deve sapere dove si trova sullo schermo**: disegna sempre

come se fosse a zero.

E' qui che serve `PSgsave`/`PSgrestore` del passo 4: la traslazione

si impila scendendo nell'albero e si toglie risalendo. Senza quella

coppia, il disegno annidato non sarebbe esprimibile.

NSView

`initWithFrame:`, `addSubview:`, `frame`, `drawRect:`, `display`.

`drawRect:` disegna la vista in coordinate proprie. `display` disegna

se stessa e poi scende nei figli, salvando e ripristinando lo stato

grafico a ogni livello.

NSWindow

`initWithContentRect:`, `setTitle:`, `setContentView:`,

`contentView`, `display`.

La finestra disegna la propria cornice e la barra del titolo, poi

lascia fare alla vista di contenuto - che non sa nulla della cornice.

NSApplication

`sharedApplication`, `addWindow:`, `windows`, `run`.

**Nota onesta**: nell'originale `run` e' il ciclo degli eventi e non

ritorna. Qui disegna tutte le finestre e ritorna, perche' il ciclo

degli eventi appartiene al desktop che ospitera' AppKit, non ad

AppKit stesso finche' non c'e' un sistema di eventi.

La prova

Nuovo pannello **AppKit** (tasto A). Costruisce una finestra vera con

viste annidate su tre livelli e la fa disegnare da `NSApplication`:

Viste create 180

drawRect: chiamate 4140

Profondita' massima 4

A schermo si vede la finestra con barra del titolo, vista di

contenuto, un pannello blu **con dentro** un bottone verde, e una

spia rossa accanto.

Il bottone verde e' figlio del pannello blu, che e' figlio del

contenuto: **tre traslazioni annidate**, e compare nel posto giusto.

E' la verifica che conta.

Cosa manca di AppKit

Eventi (clic, tastiera), `NSResponder` e la catena dei responsabili,

`NSControl` e `NSButton`, testo, ridisegno selettivo delle sole zone

sporche. Per ora ogni `display` ridisegna tutto.

Dove siamo

Viste annidate su tre livelli
v0.2.5.6
DEVv0.2.5.7 Desktop 3, costruito su AppKit Set 2026

L'ultimo passo. Nuova voce **DESKTOP 3** nel menu di avvio.

Non imita AppKit: e' costruito su AppKit

Ogni elemento che si vede e' un `NSView`, ogni finestra e' una

`NSWindow`, e il disegno passa dal motore PostScript. Il codice non

disegna rettangoli: compone un albero di viste e chiede a

`NSApplication` di disegnarlo.

id menu = NSWindow_new(NSMakeRect(20, 20, 170, 200), "NATALIZIOOS");

id menu_v = NSView_new(NSMakeRect(0, 0, 170, 178), NX_PANNELLO);

NSView_setDrawer(menu_v, nx_pannello);

NSWindow_setContentView(menu, menu_v);

NSApp_addWindow(g_app, menu);

L'aspetto

Grigi ispirato a NeXT, bordi in rilievo ottenuti con due fili - uno chiaro

sopra e a sinistra, uno scuro sotto e a destra. Menu staccato in alto

a sinistra, non una barra in cima allo schermo: e' cosi' che

funzionava Rhapsody. Dock verticale a destra.

Tre finestre: il menu, un Workspace a tre colonne e un Inspector con

campi e pulsanti.

Un difetto trovato all'ultimo

Le finestre non comparivano: il motore era attaccato alla superficie

di Desktop 2, che qui non esiste. Con il doppio buffer attivo

`fb_get_address()` restituisce il buffer nascosto, ed e' li' che deve

finire il disegno. Attaccandolo altrove, AppKit componeva

correttamente in una memoria che nessuno mostrava.

Cosa manca

Il testo non passa ancora da AppKit - manca `NSFont` - quindi le

etichette si scrivono sopra. E non ci sono eventi: le finestre si

vedono ma non si cliccano.

bilancio onesto

I file di AppKit contengono solo `// TODO: Implement this method`:

Apple apri' il kernel e gli strumenti, non i framework applicativi.

Nei Kits ci sono **1.355 metodi** da implementare.

Dove il codice esisteva l'ho seguito; dove non esisteva ho

implementato le firme esatte delle intestazioni. Se un domani salta

fuori l'implementazione vera, entra al posto della nostra senza che

nulla intorno cambi.

Desktop 3: menu, Workspace, Inspector
v0.2.5.7
DEVv0.2.5.8 Il primo strumento davvero portato Set 2026

Avevo guardato solo i Kits e concluso troppo in fretta. Censendo

**tutto** l'archivio, il quadro e' opposto: i segnaposto sono

l'eccezione.

Su 85 cartelle, solo i Kits sono vuoti. Tutto il resto - kernel,

driver, comandi, compilatore, librerie - e' codice vero.

`wc` portato, non riscritto

`ported/wc.c` viene da

La logica di conteggio e' quella dell'originale, riga per riga. Sono

cambiate due cose sole, e solo perche' qui non esistono:

open/read -> vfs_finddir/vfs_read

stdout -> la funzione di stampa del chiamante

**I commenti dell'autore originale sono rimasti**, compreso quello

che spiega perche' il conteggio delle righe e' separato da quello

delle parole:

*line counting is split out because it's a lot faster to get lines

than to get words, since the word count requires some logic.*

Senza quel commento sembrerebbe una duplicazione inutile.

Nel Desktop 3

Nuova finestra AppKit che mostra l'esito:

$ wc readme

righe 10

parole 44

caratteri 307

Numeri veri, calcolati dal codice originale sul filesystem di

NATALIZIOOS.

La strada che si apre

Con Commands, Libc e i driver tutti veri, c'e' molto da portare: gli

altri comandi di testo, le funzioni di libreria che ci mancano, i

driver per dispositivi che non gestiamo. `wc` e' il primo, e serve a

dimostrare che il percorso funziona.

wc portato, in esecuzione
v0.2.5.8
DEVv0.2.5.9 Dieci strumenti Set 2026

Cosa puo' entrare e cosa no

Da `src/Libc-1/string.subproj` — sette funzioni

`strstr`, `strspn`, `strcspn`, `strpbrk`, `memchr`, `strcasecmp`,

`strsep`.

Il corpo di ciascuna e' quello dell'originale. Le modifiche sono tre,

tutte obbligate:

  • argomenti da forma K&R a forma moderna, perche' il compilatore non

accetta piu' la vecchia

  • `register` tolto: oggi il compilatore decide meglio da solo
  • prefisso `nat_` per non scontrarsi con le versioni gia' presenti

Da `src/Commands` — quattro comandi

`basename`, `dirname`, `rev`, `head`, `cksum`.

**I commenti degli autori sono rimasti.** In `basename` e `dirname` i

numeri fra parentesi sono i passi della specifica POSIX, come li

aveva annotati chi scrisse il codice:

*(3) If there are any trailing slash characters in string, they

shall be removed.*

Sono quelli che spiegano il perche' delle scelte. Riscriverli sarebbe

perdere il senso del port.

Una scelta dichiarata

`cksum` nell'originale usa una tabella di 256 voci precalcolate. Qui

il CRC si calcola al volo: 8 KB di tabella per una funzione usata di

rado non valgono lo spazio. E' l'unica divergenza logica, ed e'

segnata nel codice.

Nel Desktop 3

La finestra "Strumenti" mostra gli esiti veri:

$ wc readme

righe 10 parole 44 caratteri 307

$ basename /usr/local/bin/natalizioos

natalizioos

$ dirname /usr/local/bin/natalizioos

/usr/local/bin

$ cksum readme -> 3568399731

strstr/strspn/strcasecmp: S 6 =

Tutti calcolati dal codice di Berkeley sul filesystem di NATALIZIOOS.

Dieci strumenti portati
v0.2.5.9
DEVv0.2.5.10 Il ponte Mach Set 2026

Il problema, e perche' non si risolve portando il kernel

Il codice di NATALIZIOOS(2023) chiama Mach: `vm_allocate`, `task_self`,

`msg_send`. Il kernel Mach non puo' entrare in NATALIZIOOS, perche'

due kernel non convivono - portarlo significherebbe sostituire

NATALIZIOOS, non aggiungergli qualcosa.

**La soluzione non e' portare il kernel: e' portare la sua

interfaccia.**

`mach/mach_bridge.c` offre le chiamate Mach con i nomi e le firme

originali, e le serve con quello che NATALIZIOOS(2026) ha gia':

**NATALIZIOOS(2026) non e' stato modificato di una riga.** Questo e' uno

strato sopra, che traduce.

Un conflitto che il ponte ha dovuto risolvere

NATALIZIOOS(2026) ha gia' un `task_t`: e' la sua struttura di processo.

Mach ha un `task_t` che e' un numero di porta. Stesso nome, due

significati.

Il ponte usa `mach_task_port_t` internamente, cosi' i due vocabolari

convivono senza che nessuno dei due debba cambiare. E' esattamente il

lavoro che un ponte deve fare.

Due differenze dichiarate

**`vm_allocate`**: Mach alloca a pagine e puo' collocare la regione

dove vuole; qui la richiesta va all'heap, che lavora a byte. Per il

codice che chiama `vm_allocate` per avere memoria - cioe' quasi tutto

  • la differenza non si vede.

**`msg_receive`**: in Mach puo' attendere finche' un messaggio

arriva. Qui ritorna subito se la coda e' vuota, perche' senza

processi non ci sarebbe nessuno a riempirla mentre si aspetta:

attendere bloccherebbe il sistema per sempre.

La prova, nel Desktop 3

Ponte Mach (codice Mach, kernel nostro)

vm_allocate ok msg_send/receive ok

host_info -> 255 MB

Il messaggio inviato con `msg_send` viene riletto da `msg_receive`

con lo stesso identificativo. E `host_info`, interrogato in

linguaggio Mach, risponde con la memoria reale della macchina letta

dal nostro `pmm`.

Sul compilatore e i driver

`cc-1` richiede processi e memoria virtuale per processo: prima

servono quelli. I driver sono scritti per DriverKit, il modello a

oggetti ispirati a NeXT: ora che c'e' il runtime Objective-C, DriverKit

diventa costruibile - e' il passo che apre quella strada.

Ponte Mach: host_info -> 255 MB
v0.2.5.10
DEVv0.2.5.11 DriverKit Set 2026

Perche' serviva

I 226.014 righe di `src/drivers-i386` sono scritte per DriverKit, il

modello a oggetti con cui NeXT scriveva i driver. Senza, quel codice

non ha su cosa poggiare. Ora che c'e' il runtime Objective-C,

DriverKit e' costruibile - ed e' la porta d'ingresso a quella parte

dell'archivio.

L'idea di DriverKit

Un driver e' un **oggetto** che discende da `IODevice`. Il sistema lo

interroga con `+probe` per sapere se il dispositivo c'e'; se

risponde di si', ne crea un'istanza e la registra.

Cosi' **aggiungere un driver non significa modificare il kernel:

significa aggiungere una classe.**

Gerarchia dall'originale (`src/driverkit-3/driverkit/`):

Object

IODevice base di tutti

IODirectDevice accesso diretto all'hardware

IOAudio, IONetwork, IODisk...

Metodi da `IODevice.h`: `+probe`, `+deviceStyle`, `-setName:`,

`-name`, `-setUnit:`, `-unit`.

Tre driver veri

Non sono finti: ciascuno interroga l'hardware attraverso i driver di

NATALIZIOOS e risponde di conseguenza.

Nel Desktop 3:

DriverKit: dispositivi rilevati

AuthenticAMD

ATA hd0

Memoria fisica

Il primo nome arriva dal driver CPU, che l'ha letto dal processore:

non e' scritto da nessuna parte nel codice di DriverKit.

Cosa apre

Ora i driver dell'archivio hanno la base su cui poggiano. Portarli

diventa un lavoro meccanico: sostituire le chiamate all'hardware di

NeXT con le nostre, lasciando la struttura a oggetti intatta.

DriverKit: tre dispositivi rilevati
v0.2.5.11
DEVv0.2.5.12 Secondo lotto di comandi Set 2026

Da `src/Commands/text_cmds` e `file_cmds`:

`cmp` confrontato con se stesso deve dare 0: e' la verifica che il

confronto funzioni davvero, non che si limiti a dire sempre "uguali".

**Commenti originali conservati.** In `uniq`, "If different, print;

set previous to new value" e' dell'autore e descrive esattamente il

cuore del ciclo.

Stato del port

DEVv0.2.5.13 Terzo lotto: stdlib Set 2026

Da `src/Libc-1/stdlib.subproj/`: `strtol`, `atoi`, `atol`, `abs`,

`labs`, `div`, `bsearch`, `qsort`.

Sono le funzioni che il resto dell'archivio usa di continuo: senza,

portare altri comandi significa riscriverle ogni volta.

`strtol` e il traboccamento

Il meccanismo di `cutoff`/`cutlim` e' dell'originale, ed e' il pezzo

piu' interessante del file: invece di moltiplicare e poi accorgersi

del traboccamento, si calcola **prima** il valore oltre il quale

moltiplicare traborderebbe.

E' il modo corretto di farlo quando non esiste un tipo piu' grande in

cui rifugiarsi. Il commento dell'autore lo spiega ed e' rimasto.

Una divergenza dichiarata

`qsort` nell'originale e' l'ordinamento di Bentley e McIlroy, con

gestione speciale degli elementi uguali e dei vettori quasi

ordinati. Qui c'e' lo stesso algoritmo - mediana di tre come perno,

partizione, ricorsione - senza quelle ottimizzazioni: contano su

vettori da milioni di elementi, e qui non ne esistono.

La mediana di tre invece l'ho tenuta, perche' serve a evitare il caso

peggiore su un vettore gia' ordinato, che capita spesso.

La prova

strtol/atoi/qsort/bsearch: 255 493 -42 S S

  • `strtol("0xFF", 0)` &rarr; 255, base dedotta esadecimale
  • `strtol("0755", 0)` &rarr; 493, base dedotta ottale
  • `atoi("-42")` &rarr; -42
  • `qsort` su nove numeri &rarr; ordinati
  • `bsearch` trova il 7 &rarr; si

Le due basi dedotte sono la verifica che conta: dimostrano che la

logica dello zero iniziale funziona, non solo la conversione.

Stato

DEVv0.2.5.14 Quarto lotto: cut, tr, comm, col, banner Set 2026

Da `src/Commands/text_cmds/`.

cut/tr/col/comm: 10 71 0 10

L'ultimo numero e' la verifica che conta: confrontando un file con se

stesso, tutte e dieci le righe devono risultare in comune e nessuna

esclusiva. Se `comm` sbagliasse il confronto, quel numero non

tornerebbe.

Due dettagli dagli originali

**`tr`** usa una tabella di 256 voci: si prepara una volta e poi la

sostituzione costa una lettura per carattere, invece di cercare ogni

volta dentro la stringa. E se `string2` e' piu' corta, l'ultimo

carattere si ripete - comportamento dell'originale, non una

semplificazione.

**`cut`** scorre carattere per carattere contando la colonna, come

nella funzione `c_cut` originale, invece di spezzare la riga in

campi: e' piu' semplice e non alloca nulla.

Una divergenza dichiarata

**`banner`** nell'originale ha una tabella col disegno di ogni

lettera. Qui c'e' un tratto semplice: quella tabella e' 4 KB per una

funzione di puro ornamento. Il disegno non e' quello dell'originale,

ed e' segnato nel codice.

Totale portato finora

  • **15 comandi** da `Commands`
  • **15 funzioni** da `Libc` (7 stringa + 8 stdlib)
  • Runtime Objective-C, Foundation, AppKit, DriverKit, ponte Mach
DEVv0.2.5.15 Quinto lotto: fnmatch Set 2026

Da `src/Libc-1/gen.subproj/fnmatch.c`, piu' `difftime` e le funzioni

di classificazione dei caratteri.

Perche' fnmatch conta

E' la corrispondenza con caratteri jolly: e' cosi' che la shell

capisce cosa significa `*.txt`. Serve a qualunque comando debba

selezionare file per modello - `ls` con argomenti, `find`,

l'espansione della riga di comando.

Otto prove, otto corrette

fnmatch (jolly, intervalli, negazione): 8/8

Le due righe con `[a-n]` e `[a-m]` sono la verifica che conta: la

stessa stringa deve dare esiti opposti, e la differenza sta in un

solo carattere dell'intervallo.

Il pezzo interessante dell'originale

Il caso di `*` non prova tutte le posizioni alla cieca: prima

accorpa gli asterischi consecutivi, poi tratta a parte il caso in cui

l'asterisco sia in fondo, dove la risposta e' immediata. Solo se

nessuna di queste scorciatoie vale, ricorre.

E' rimasta anche l'attribuzione originale nel commento di

`rangematch`, dove J.T. Conklin spiega perche' `^` viene trattato

come `!` nonostante la specifica lo lasci indefinito.

Una divergenza dichiarata

Le funzioni di classificazione (`isalnum`, `ispunct`, `isprint`,

`iscntrl`, `isxdigit`) nell'originale sono una tabella di 256 voci

coi bit delle classi. Qui sono confronti diretti: la tabella serve a

gestire le codifiche locali, che NATALIZIOOS non ha.

Totale portato

  • **15 comandi** da `Commands`
  • **21 funzioni** da `Libc` (7 stringa + 8 stdlib + 6 gen)
  • Runtime Objective-C, Foundation, AppKit, DriverKit, ponte Mach
DEVv0.2.5.16 Sesto lotto: memoria e strmode Set 2026

Da `src/Libc-1/string.subproj/`: `memcmp`, `memccpy`, `memmove`,

`strdup`, `strmode`, `strerror`.

memcmp/memmove/strdup: ok strmode 0644 -> -rw-r--r--

Le verifiche scelte apposta

**`memcmp` deve ordinare, non solo dire uguale o diverso.** Le tre

prove controllano che `"abc" < "abd"`, che `"abd" > "abc"` e che

`"abc" == "abc"`: e' la differenza fra un confronto usabile per

ordinare e uno che serve solo a sapere se due blocchi coincidono.

**`memmove` con aree sovrapposte.** E' il caso che distingue

`memmove` da `memcpy`: copiando in avanti si sovrascrive cio' che non

e' ancora stato letto. La prova sposta un blocco su se stesso di due

posizioni e verifica che il contenuto sia arrivato intatto.

**`strmode 0644`** deve dare esattamente `-rw-r--r--`: e' la forma

che si vede in `ls -l`, con le undici posizioni fisse - tipo, tre

terne di permessi, e lo spazio finale che l'originale lascia.

Una divergenza dichiarata

`strerror` nell'originale legge da una tabella di messaggi di

sistema. Qui la tabella e' ridotta agli errori che NATALIZIOOS puo'

davvero produrre: elencare errori che non capitano mai sarebbe solo

spazio sprecato.

Totale portato

  • **15 comandi** da `Commands`
  • **27 funzioni** da `Libc` (7 stringa + 8 stdlib + 6 gen + 6 memoria)
  • Runtime Objective-C, Foundation, AppKit, DriverKit, ponte Mach
DEVv0.2.5.17 Settimo lotto: stampa formattata Set 2026

Da `src/Libc-1/stdio.subproj/vfprintf.c`.

Cosa e' stato portato, e cosa no

L'originale e' **857 righe** e poggia sui flussi (`FILE *`), che qui

non esistono. Ho portato il **cuore**: l'analisi del formato e la

conversione, che sono la parte riusabile e il grosso della logica.

Scrive in un buffer invece che su un flusso.

Resta fuori: i numeri in virgola mobile (senza FPU nel kernel non

avrebbero senso), la larghezza presa da argomento con `*`, le

conversioni di tipo lungo lungo.

Il pezzo interessante: `__ultoa`

Il numero si costruisce **all'indietro**, partendo dalla fine del

buffer. Cosi' non serve sapere prima quante cifre verranno.

E per la base 10 l'originale fa una cosa che sembra strana finche'

non si legge il commento:

*On many machines, unsigned arithmetic is harder than signed

arithmetic, so we do at most one unsigned mod and divide; this is

sufficient to reduce the range of the value to LONG_MAX before

using signed arithmetic.*

Una sola divisione senza segno per scendere sotto `LONG_MAX`, poi

tutto il resto con segno. E' un'ottimizzazione che oggi conta meno,

ma il commento spiega perche' c'e', e l'ho tenuta.

Sei verifiche, sei corrette

snprintf: 6/6 DEADBEEF -17 ab |

La seconda e' quella che smaschera le versioni scritte in fretta:

con il riempimento a zeri il segno deve uscire **prima**, altrimenti

si ottiene `000-42`.

Totale portato

  • **15 comandi** da `Commands`
  • **29 funzioni** da `Libc` (stringa, stdlib, gen, memoria, stampa)
  • Runtime Objective-C, Foundation, AppKit, DriverKit, ponte Mach
snprintf: 6 verifiche su 6
v0.2.5.17
DEVv0.2.5.18 Finestra scorrevole Set 2026

Segnalato: la finestra "Strumenti portati" sfora, e le ultime righe

finiscono sopra la barra in fondo allo schermo.

La causa

Ogni riga veniva stampata a una **coordinata fissa**, calcolata a

mano: `fb_puts(250, 470 + i * 18, ...)`. Con diciotto stampe sparse,

ogni lotto nuovo spostava tutto e prima o poi il contenuto doveva

uscire dalla finestra. Non era un caso: era inevitabile.

La correzione

Le righe si raccolgono ora in un elenco, e se ne disegnano solo

quelle che ci stanno, a partire dalla posizione di scorrimento.

d3_riga_add("$ wc readme", RGB(0x11,0x11,0x11));

d3_riga_num(" righe ", g_wc.linee, ...);

**Aggiungere un lotto non richiede piu' di ricalcolare nulla**: si

accoda e basta. E' la ragione vera per cui valeva la pena

rifarlo, non solo il difetto visibile.

Cosa si vede ora

  • Il contenuto resta **dentro** la finestra
  • **Barra di scorrimento** a destra, col cursore proporzionale
  • **Frecce su e giu'** per scorrere

Ventotto righe di contenuto in una finestra che ne mostra sedici:

scorrendo compaiono DriverKit e il ponte Mach.

Nota

Le righe si costruiscono con `nat_snprintf`, la stampa formattata

portata nella versione precedente. Il codice portato dall'archivio

ha iniziato a servire al sistema stesso, non solo alle prove.

Finestra scorrevole
v0.2.5.18
DEVv0.2.5.19 Il primo driver vero dell'archivio Set 2026

Da src/drivers-i386/sound/drvBeepSound/Beep.drvproj/Beep.lksproj/Beep.m: 481 righe, zero segnaposto.

Perche' proprio questo

Usa la porta 0x61 dell'altoparlante, la stessa del nostro drivers/speaker.c. Il ponte fra i due mondi qui e' minimo: serve a dimostrare che il percorso funziona prima di affrontarne uno complicato.

Cosa e' stato mantenuto

Le cinque sequenze coi loro rapporti di frequenza, che sono la parte caratteristica del driver:

Plain    1 nota,  rapporto  1:1
Blip     2 note,  rapporto  3:4     quarta giusta discendente
Up       8 note,  rapporto 17:16    leggermente crescente
Down     8 note,  rapporto 15:16    leggermente calante
Octave   2 note,  rapporto  2:1     salto di ottava

Quei rapporti sono di NeXT, e i commenti che spiegano che intervallo musicale rappresentano sono rimasti: senza, sarebbero numeri arbitrari.

Mantenuti anche stringToStyle, i limiti 20 Hz - 20 kHz, e la struttura a classe DriverKit.

Cosa e' cambiato, e perche'

L'attesa fra le note. L'originale usa thread_set_timeout e thread_block, cioe' sospende il thread. Senza processi qui si attende sul timer di sistema.

Il pilotaggio della porta passa da drivers/speaker.c invece che da outb diretto: cosi' il driver rispetta l'interruttore del silenzio che il sistema ha gia'. E' una divergenza che migliora il comportamento, non un compromesso.

Nel Desktop 3

DriverKit rileva ora quattro dispositivi:

AuthenticAMD
ATA hd0
Memoria fisica
Beep (altoparlante)     <- portato dall'archivio

E le cinque sequenze coi rapporti originali.

Cosa dimostra

I 226.014 righe di drivers-i386 non sono piu' teoricamente portabili: una l'abbiamo portata. Il metodo e' quello - mantenere la struttura a oggetti e le costanti, sostituire solo le chiamate all'hardware di NeXT con le nostre.

nessuna schermata di questa versione
DEVv0.2.5.20 Il primo pezzo dei Kits Set 2026

Da src/Kits/SoundKit/SoundMeter.m: 440 righe, zero segnaposto.

I Kits non sono tutti vuoti

AppKit e Foundation sono scheletri, ma SoundKit ha codice vero: 13 file su 178. E' l'unica parte dei Kits portabile, ed e' quella da cui si comincia.

Perche' si e' potuto portare adesso

SoundMeter e' una NSView, e NSView c'e' dal passo 5. Disegna con PSrectfill, e il motore PostScript c'e' dal passo 4.

Tutto cio' su cui poggia esisteva gia'. E' il momento in cui il lavoro fatto prima comincia a rendere: il primo file dei Kits si e' innestato senza dover costruire nulla di nuovo.

Mantenuto dall'originale

smoothValue — il valore sale subito ma scende con calma: la media pesata sugli ultimi tre campioni vale solo quando il livello CALA. Cosi' l'indicatore segue i picchi senza tremolare sulle pause. E' una riga sola, ma e' la differenza fra un indicatore usabile e uno che balla.

prepareValueForDisplay con la scala mu-law. L'orecchio percepisce il volume in modo logaritmico: un raddoppio di potenza non suona "doppio". La mu-law comprime la scala seguendo quella percezione. Nel pannello si vede il risultato:

scala mu-law: 0.25 -> 62%, 0.50 -> 78%, 1.00 -> 100%

Non lineare: meta' potenza da' il 78%, non il 50%. E' cosi' che l'orecchio sente.

Mantenuti anche PEAK_WIDTH = 3.0, i margini di 5 pixel e la struttura del disegno.

Cambiato

  • i colori da NSColor a valori diretti, perche' NSColor non e' ancora portato
  • mu-law calcolata invece che presa da tabella

Nel Desktop 3

Nella finestra Inspector c'e' l'indicatore vero: barra verde del livello, cursore giallo del picco, e si muove. Verificato su due fotogrammi a tre secondi di distanza: 532 e 511 pixel accesi.

nessuna schermata di questa versione
DEVv0.2.5.21 Commands: look, jot, expr, du Set 2026

Priorita' Commands, come richiesto.

expr — la precedenza sta nella struttura

L'originale usa un analizzatore generato da yacc; qui c'e' la discesa ricorsiva scritta a mano, con la stessa grammatica:

espressione := termine  { + | -  termine }
termine     := fattore  { * | / | %  fattore }
fattore     := numero | ( espressione )

La precedenza non e' scritta da nessuna parte: nasce dal fatto che il termine si valuta piu' in basso nella discesa. E' lo stesso principio della grammatica originale.

Le verifiche lo dimostrano:

expr: 2+3*4=14   (2+3)*4=20   100%7--3=5

Il primo risultato sarebbe 20 se la precedenza non funzionasse. Il secondo mostra che le parentesi la scavalcano. Il terzo prova modulo e numeri negativi insieme.

look — ricerca binaria su file ordinato

E' il comando dietro al controllo ortografico: su un dizionario di centomila parole, la ricerca binaria costa circa diciassette confronti invece di centomila.

Struttura dell'originale conservata: prima la binaria per avvicinarsi, poi la lineare per trovare esattamente. La binaria da sola non basta perche' possono esserci piu' righe corrispondenti.

jot e du

jot 6 10 5: 10 15 20 25 30 35
du readme: 1 blocchi da 512

du arrotonda al blocco superiore, come l'originale.

Un intoppo

Le righe nuove non comparivano: erano state accodate dopo DriverKit, cioe' oltre la parte visibile senza scorrere. Spostate sopra. Il difetto era nella posizione, non nel codice - ma senza guardare lo schermo avrei detto che non funzionavano.

Totale

  • 19 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.22 Commands: sort, nl, paste, sum Set 2026

Due pezzi portati che lavorano insieme

sort usa qsort, portato dal lotto stdlib. E' la prima volta che un comando dell'archivio chiama una funzione di libreria dell'archivio, come faceva nell'originale.

sort (con qsort portato): in ordine

La verifica non si fida dell'esito: riordina e poi ricontrolla riga per riga che ogni elemento sia minore o uguale al successivo. Se qsort sbagliasse, non basterebbe che il comando finisca senza errori.

sum — la rotazione che conta la posizione

E' la somma di controllo storica di BSD, piu' semplice di cksum e usata prima che esistesse.

Il punto interessante: prima di sommare ogni byte, si ruota il totale di un bit a destra. Senza quella rotazione "ab" e "ba" darebbero lo stesso risultato: e' cosi' che la posizione entra nel conto.

nl e paste

nl numera le righe non vuote, allineate a destra in sei colonne, come il comportamento predefinito dell'originale. paste affianca le righe di due file separandole con un carattere.

Di nl l'originale ha molte opzioni - quali righe numerare, il formato, la larghezza. Qui c'e' il comportamento senza argomenti: e' dichiarato nel codice.

Totale

  • 23 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.23 Commands: strings, od, tee, yes, true, false Set 2026
strings: 8   yes: 3   true/false: 0/1 ok

strings

Estrae le sequenze di caratteri stampabili da un file. E' il modo piu' rapido di capire cosa fa un eseguibile senza disassemblarlo: i messaggi, i nomi dei file, le stringhe di formato sono tutte li'.

La lunghezza minima predefinita e' 4: sotto quella soglia uscirebbe troppo rumore, ed e' la scelta dell'originale.

od

Mostra il file in esadecimale, con l'offset a sette cifre davanti e i caratteri a fianco - il punto per quelli non stampabili. L'originale supporta molti formati; qui c'e' quello che si usa davvero per guardare dentro un file.

true e false

I due comandi piu' brevi di UNIX: non fanno nulla, uno riesce e l'altro fallisce. La verifica controlla che diano 0 e 1: se fossero uguali, gli script non potrebbero distinguerli, ed e' tutto cio' che serve loro.

tee e yes

tee prende il nome dal raccordo a T dell'idraulica, e fa la stessa cosa: il flusso prosegue e intanto una copia va altrove.

yes ripete una stringa - sembra inutile finche' non serve rispondere "y" a un comando che fa cento domande.

Copia di sicurezza

Da questa versione il pacchetto viene salvato negli output a ogni giro, non solo in /tmp: quella cartella si perde a ogni reset dell'ambiente, gli output no.

Totale

  • 29 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.24 Commands: grep, cat, split, echo Set 2026
grep: 1 trovate, -v 9, jolly 0   (somma 10 = righe)

grep costruito su due pezzi gia' portati

L'originale usa il motore di espressioni regolari completo (src/Libc-1/regex.subproj, 3.207 righe). Qui ci sono due modi, e entrambi poggiano su codice gia' portato dall'archivio:

ModoUsa
testo semplicenat_strstr, dal lotto stringhe
con jollynat_fnmatch, dal lotto gen

Le espressioni regolari vere arriveranno quando il motore sara' portato: e' una divergenza dichiarata, non una svista.

La verifica scelta apposta

grep trova 1 riga, grep -v ne trova 9, e la somma fa esattamente le 10 righe del file. E' la prova che -v sia il complemento esatto: un conteggio sbagliato da una parte o dall'altra romperebbe la somma.

cat, split, echo

cat -n -s numera le righe e accorpa le vuote consecutive: sono le due opzioni dell'originale che cambiano davvero l'uscita.

echo -n non mette la nuova riga in fondo. E' l'unica opzione dell'originale, ed e' la differenza fra un prompt che resta sulla stessa riga e uno che va a capo.

split e' dichiarato incompleto: conta in quante parti andrebbe diviso il file e quanto e' grande l'ultima, ma non scrive xaa, xab, xac come l'originale, perche' il VFS non ha ancora la creazione di file.

Totale

  • 33 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.25 Commands: date, cal, test, sleep Set 2026
date (dal driver RTC): Sab 19 Set 2026  11:27:30
test: 6 relazioni numeriche + 2 su stringa -> tutte corrette
cal (mese corrente): Settembre 2026

Tre pezzi portati che collaborano

date e cal usano il driver rtc, portato nella v0.2.5.0. Non ricevono l'ora come argomento: la chiedono al sistema, come fanno nell'originale.

E cal usa rtc_weekday, che implementa Zeller: il lavoro vero di cal e' sapere in che giorno cade il primo del mese, perche' da li' tutto il resto e' conseguenza. L'originale ha la sua day_in_week; qui la funzione c'era gia' portata.

Il giorno della settimana nella data - Sab per il 19 settembre 2026 - e' la verifica che Zeller funzioni: un errore di un giorno sarebbe subito visibile.

test e lo zero che significa vero

E' il comando dietro alle condizioni della shell: [ 5 -gt 3 ].

Ritorna 0 se vero e 1 se falso, al contrario di quanto verrebbe naturale: negli script lo zero significa riuscita, e test deve parlare quella lingua.

Otto verifiche: le sei relazioni numeriche (-eq -ne -gt -ge -lt -le) e due su stringa (= e !=), ciascuna controllata in entrambi i versi - che 5 -gt 3 dia 0 e che 3 -gt 5 dia 1. Un confronto sempre vero passerebbe meta' delle prove.

Totale

  • 37 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.26 Commands: fmt, colrm, unexpand, pr Set 2026
fmt -40: 10 righe, nessuna parola spezzata: si
colrm 1-5: 40 colonne tolte   unexpand: 0 tab   pr -5: 2 pagine

fmt e la differenza con fold

Entrambi spezzano le righe lunghe, ma:

  • fold taglia dove capita, anche in mezzo a una parola
  • fmt riempie fino alla larghezza rispettando le parole

La verifica non si fida del conteggio: scorre il risultato carattere per carattere e controlla che nessuna riga superi le 40 colonne. Un fmt che spezzasse le parole produrrebbe lo stesso numero di righe, quindi contarle non basterebbe.

Mantenuti dall'originale anche la conservazione delle righe vuote come separatori di paragrafo.

colrm e unexpand

colrm e' il complemento di cut: cut tiene le colonne scelte, colrm le elimina.

unexpand e' il contrario di expand, ma con un limite preciso dell'originale: tocca solo gli spazi in testa alla riga, non quelli in mezzo. Cambiare anche quelli spezzerebbe l'allineamento delle tabelle.

pr usa date

pr prepara il file per la stampa con intestazione e numero di pagina. La data nell'intestazione arriva da nat_date, portato nel lotto precedente, che a sua volta legge dal driver rtc.

Tre pezzi portati in fila: pr chiama date, che chiama il driver.

Totale

  • 41 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.27 Commands: join, column, ul, lam Set 2026
join: 10 unite   column 3x20: 4 righe   ul: 0   lam: 10

join e il perche' dei file ordinati

Unisce le righe di due file che hanno lo stesso primo campo: e' l'operazione che nei database si chiama giunzione.

L'originale pretende che i file siano ordinati, e non e' un capriccio: e' quello che permette di scorrerli una volta sola, avanzando nell'uno o nell'altro a seconda del confronto. Senza l'ordinamento servirebbe rileggere un file da capo per ogni riga dell'altro.

La verifica unisce il file con se stesso: ogni riga ha il primo campo uguale al proprio, quindi tutte e dieci devono unirsi. Se il confronto sbagliasse, il numero non tornerebbe.

ul e le telescriventi

Interpreta le sottolineature fatte con ritorno a capo e sovrascrittura: a\bA significa A sottolineata.

Nasce dalle telescriventi, dove si sottolineava tornando indietro e battendo l'underscore sopra il carattere. Quel \b e' proprio il ritorno del carrello. Sul file di prova ne trova zero, ed e' corretto: non ne contiene.

column e lam

column dispone le righe su piu' colonne riempiendo per righe: dieci righe in tre colonne danno quattro righe di uscita.

lam affianca due file senza separatori - e' la differenza con paste, che il separatore lo mette.

Totale

  • 45 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.28 Commands: ls, find, file, pwd Set 2026
ls: 5 voci   find *.txt: 3   file readme: testo ASCII
    drw-r-xr-x      0 disk
    drw-r-xr-x      0 docs

I primi comandi che lavorano sulle directory, non sul contenuto dei file. E usano due pezzi gia' portati.

ls -l usa strmode

I permessi nel formato drw-r-xr-x arrivano da strmode, portato nel lotto memoria. E' la stessa funzione che usa ls nell'originale, non una riscrittura: era gia' li' perche' serviva a questo.

find usa fnmatch

Il confronto *.txt passa da fnmatch, portato nel lotto gen. Anche qui, e' la funzione che usa find nell'originale.

L'originale scende ricorsivamente nell'albero e valuta un'espressione di condizioni; qui c'e' la ricerca per nome, che e' l'uso piu' frequente, con la discesa ricorsiva limitata a otto livelli.

file e l'ordine delle prove

Indovina il tipo guardando l'inizio del file. L'originale consulta /etc/magic, un elenco di firme; qui ci sono quelle essenziali - ELF, MZ, settore di avvio, Mach-O.

La regola di fondo e' dell'originale: prima le firme note, poi si guarda se il contenuto e' tutto stampabile, e solo alla fine si dichiara "dati". Invertire l'ordine farebbe passare per testo un eseguibile che comincia con caratteri leggibili.

pwd risale la catena

Il nodo sa chi e' il padre, non i figli del percorso: si risale fino alla radice raccogliendo i nomi, poi si scrive al contrario. E' il modo dell'originale.

Totale

  • 49 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.29 Commands: uname, hostname, id, env, uptime Set 2026
uname -a: NATALIZIOOS 0.2.5.28-DEV i386 AuthenticAMD 255 MB
id: uid=0(root) gid=0(wheel)   acceso da 0:00:47   env: 4 variabili

uname mette insieme tre strati

Una riga sola, e viene da tre posti diversi - tutti portati:

PezzoDa dove
versioneil sistema
AuthenticAMDdriver cpu (v0.2.5.0)
255 MBhost_info del ponte Mach (v0.2.5.10)

Nell'originale uname interroga il kernel Mach. Qui fa la stessa strada, con i nostri strati sotto.

env e la verifica che smaschera

L'ambiente regge scrittura e rilettura. La prova non si limita a scrivere e rileggere: scrive due volte la stessa variabile e controlla che la seconda sostituisca invece di aggiungere un doppione. Poi verifica che una variabile inesistente dia zero, perche' un getenv che restituisse sempre qualcosa passerebbe la prima meta' della prova.

Nota dichiarata: senza processi non c'e' un ambiente ereditato, quindi la tabella e' del sistema. L'interfaccia pero' e' quella giusta, e quando i processi ci saranno ogni processo avra' la sua.

Totale

  • 55 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.30 Commands: sed e tsort Set 2026
sed s/e/E/: 7 senza g, 13 con g
tsort: libA libB libC libD   (ciclo rilevato: si)

sed e il modificatore che cambia tutto

L'originale ha un linguaggio intero: indirizzi, cicli, buffer di appoggio, espressioni regolari. Qui c'e' il comando s, la sostituzione, che e' quello per cui sed si usa quasi sempre.

Con g sostituisce tutte le occorrenze della riga, senza solo la prima. La verifica lo misura: 7 senza, 13 con. Se i due numeri coincidessero, il modificatore non funzionerebbe - e un test che guardasse solo "ha sostituito qualcosa" non se ne accorgerebbe.

La ricerca usa nat_strstr, portato dal lotto stringhe. Le espressioni regolari arriveranno col motore.

tsort e il ciclo

Ordinamento topologico: date coppie "A viene prima di B", produce un ordine che le rispetta tutte. In UNIX serve a decidere l'ordine di collegamento delle librerie - se libA usa libB, libB va dopo.

L'algoritmo e' quello dell'originale: si prende ripetutamente un elemento senza predecessori, lo si emette, si tolgono i suoi archi.

Il caso che conta e' il ciclo. Se a un certo punto nessun elemento ha zero predecessori ma ne restano, il grafo ha un ciclo e un ordine valido non esiste. L'originale lo segnala invece di produrne uno falso, e la verifica prova entrambi i casi: un grafo sano che deve dare libA libB libC libD, e uno con X->Y->Z->X che deve essere riconosciuto come ciclico.

Totale

  • 57 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.31 Commands: dc, factor, primes Set 2026
dc: "3 4 + 2 *"=14   "2 10 ^"=1024   div/0 segnalata: si
factor 360: 2 2 2 3 3 5 (6 fattori)
primes 2-50: 15 trovati

dc e il perche' della notazione polacca inversa

Si scrivono prima gli operandi e poi l'operatore: 3 4 + fa 7.

Sembra scomodo, ma non serve nessuna regola di precedenza e nessuna parentesi: l'ordine e' gia' nella scrittura. E' il motivo per cui dc e' piu' vecchio di bc, che al confronto e' solo un traduttore verso dc.

Una pila e un ciclo: il programma piu' semplice che calcoli espressioni qualsiasi. La verifica prova anche la divisione per zero, che deve essere segnalata invece di produrre un numero a caso.

factor e il perche' ci si ferma alla radice

Il ciclo si arresta quando il divisore supera la radice, ed e' il punto dell'originale che vale la pena capire:

> se n non ha divisori fino alla sua radice, allora e' primo - > perche' se avesse un divisore piu' grande della radice, il > quoziente sarebbe piu' piccolo e l'avremmo gia' trovato.

factor 360 da' 2 2 2 3 3 5: e 2³ x 3² x 5 fa esattamente 360.

primes e il crivello

Invece di provare se ogni numero e' primo, si cancellano i multipli di quelli che si trovano.

Il crivello parte da d*d, non da 2*d: i multipli piu' piccoli sono gia' stati cancellati da divisori minori. E' la stessa ragione per cui factor si ferma alla radice - lo stesso principio, applicato al contrario.

Totale

  • 60 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.32 Il testo resta dentro la finestra Set 2026

Segnalato: le scritture escono dalle finestre.

La causa, in numeri

L'area interna della finestra e' larga 424 pixel e il font e' 8 pixel: ci stanno 53 caratteri. Le righe erano lunghe fino a 72.

Ogni riga oltre i 53 caratteri sforava di 19, cioe' 152 pixel che finivano sopra il desktop.

Perche' non ho troncato

Troncare sarebbe stato piu' semplice, ma le righe finiscono quasi tutte con il risultato della verifica: nl: 8 righe numerate, div/0 segnalata: si, echo -n: 5. Tagliare la coda avrebbe fatto sparire proprio il numero che conta.

Come vanno a capo

Le righe troppo lunghe si spezzano su uno spazio, per non tagliare le parole a meta', e la continuazione rientra di due spazi cosi' si vede che appartiene alla riga sopra.

Se nei primi caratteri non c'e' nessuno spazio si taglia comunque: meglio una parola spezzata che una riga fuori dal bordo.

Verifica

Contati i pixel di testo oltre il bordo destro della finestra: zero. Prima erano centinaia.

L'elenco e' passato da 64 a 96 righe di capienza, perche' andando a capo il numero di righe cresce.

nessuna schermata di questa versione
DEVv0.2.5.33 Documentazione che non invecchia, e quattro comandi Set 2026

Correzione dopo la prima stesura: il README era stato riscritto da capo invece che aggiornato, perdendo le sezioni precedenti. Ripristinato dall'originale: restano "Quick Start", "Comandi utili" e tutta la cronologia di "Aggiornamenti recenti" fino alla v0.2.2.4. Le sezioni nuove sono state aggiunte, non sostituite.

BUILD_STATUS.md generato, non scritto

Nuovo scripts/stato_build.py e passo make stato.

Il problema dei documenti di stato e' che si scrivono una volta e poi nessuno li aggiorna: dopo tre versioni raccontano un sistema che non esiste piu'. Questo conta i file: righe di codice, funzioni portate, esito di make graphic e make text eseguiti sul momento.

Include anche cosa non e' portabile e perche' - Mach, il compilatore, i Kits vuoti - cosi' chi legge non se lo chiede.

README.md riscritto

Era fermo alla v0.2.2.5.2, trenta versioni fa: descriveva un sistema senza port, senza Desktop 3, senza driver nuovi.

Ora racconta i tre ambienti, con quello che e' entrato e quello che non puo' entrare, e le licenze - BSD per Libc e Commands, Apple Public Source License per runtime e DriverKit, con le note di copyright conservate nei file.

Quattro comandi

cmp -l: 0 differenze   touch: esiste   wc -L: 44 colonne su 10 righe
comm -3 (solo comuni): 10 righe

cmp -l elenca ogni differenza coi due byte in ottale, non solo dove il confronto fallisce: e' la versione che serve a capire *cosa* e' cambiato.

comm a colonne restituisce le righe invece di contarle, con le tre opzioni -1 -2 -3 che si sopprimono a vicenda come nell'originale.

touch e' dichiarato incompleto: il VFS non conserva le date ne' permette la creazione, quindi riferisce solo se il file esiste. Il comando e' pronto per quando il VFS crescera'.

Totale

  • 64 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.34 Commands: tr -d, rs, xargs, apply Set 2026
tr -d: 71 tolti, con -s 71   rs 4col: 2 righe
xargs -n4: 3 blocchi   apply: 3

xargs e apply, opposti

xargs raggruppa: prende un elenco e ne fa blocchi di n elementi. Nell'originale ogni blocco diventa una riga di comando, ed e' il motivo per cui esiste - le righe di comando hanno una lunghezza massima, e passare diecimila nomi di file in una volta sola non funziona.

apply separa: costruisce una riga per ogni elemento, sostituendo un segnaposto. E' il predecessore di xargs, e fa la cosa opposta.

Nove elementi con -n4 danno tre blocchi: 4+4+1.

Divergenza dichiarata: senza processi qui non si esegue nulla, si raggruppa e basta. Il raggruppamento pero' e' quello dell'originale, ed e' la parte che conta.

tr -d e -s insieme

Il tr del primo lotto sostituiva; questo cancella. Con -s accorpa anche le ripetizioni che restano: e' la pulizia tipica di un testo, togliere i caratteri indesiderati e comprimere gli spazi multipli.

rs

Rimodella un flusso di valori in righe e colonne - il nome sta per "reshape". Otto valori in quattro colonne danno due righe.

Totale

  • 68 comandi da Commands
  • 29 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.35 rand e random, e perche' BSD ne scrisse due Set 2026
ultimo bit, cambi su 1000: rand 100%, random 49%

Portati entrambi da src/Libc-1/stdlib.subproj/, e la differenza fra loro e' il motivo per cui il secondo esiste.

rand e il suo difetto

Tre righe: una moltiplicazione, una somma, un resto.

Il problema e' noto e serio: i bit bassi hanno periodo cortissimo. L'ultimo si alterna 0,1,0,1; il penultimo ha periodo quattro.

Chi scrive rand() % 2 per tirare una moneta ottiene testa e croce alternate, mai due teste di fila. La misura lo mostra: cambia il 100% delle volte.

E' rimasto nella libreria per compatibilita', non perche' vada bene.

random e la retroazione additiva

Invece di un solo valore tiene uno stato di 31 numeri, e ogni volta somma due elementi distanti fra loro:

stato[f] += stato[r];
risultato = (stato[f] >> 1);   /* chucking least random bit */

Quel >> 1 e' commentato cosi' dall'autore originale, e scarta proprio il bit che in rand aveva periodo due.

Il polinomio x^31 + x^3 + 1 da' un periodo di circa 2^69, contro i 2^31 di rand. Misurato: 49% di cambi, che e' quello che ci si aspetta da una sequenza casuale.

srandom scarta i primi 310 valori, come l'originale: serve a far dimenticare allo stato la regolarita' del riempimento iniziale.

Il controllo severo ha colto un altro errore

nat_random veniva usata da srandom prima di essere definita. Con gcc permissivo sarebbe passata come avviso; -Werror=implicit-function-declaration ha fermato la build.

E' la terza volta che quel controllo intercetta qualcosa che sarebbe arrivato rotto.

Totale

  • 68 comandi da Commands
  • 33 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.36 getopt Set 2026
getopt "-a -bc -f nome resto": a b c f=nome
  optind fermo sul primo non-opzione: si

Perche' conta

E' la funzione che analizza le opzioni della riga di comando, e la ragione per cui tutti i comandi UNIX si comportano allo stesso modo: -a, -bc raggruppate, -f con argomento attaccato o staccato, e -- che chiude le opzioni.

Nei lotti precedenti ogni comando riceveva le opzioni come parametri separati, perche' getopt non c'era. Ora c'e', e i comandi possono analizzare una riga vera.

La prova sceglie il caso difficile

-bc raggruppate sono quello che distingue un getopt vero da un confronto carattere per carattere: devono diventare due opzioni, b e c, non una sola.

E optind deve fermarsi sul primo argomento che non e' un'opzione - resto, in posizione 5 - perche' e' da li' che il comando riprende a leggere i suoi argomenti veri.

Le variabili globali, coi nomi originali

optind, optarg, optopt, opterr, optreset: sono quelle che ogni comando UNIX si aspetta di trovare.

Unica modifica: i messaggi d'errore non vanno su stderr, che qui non esiste, ma in un buffer che il chiamante puo' leggere.

Totale

  • 68 comandi da Commands
  • 35 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.37 strftime Set 2026
%F %T           -> 2026-09-20 17:12:35
%A %e %B %Y     -> Domenica 20 Settembre 2026
%I:%M %p (%j)   -> 05:12 PM (263)

La funzione dietro a ogni data leggibile in un sistema UNIX. I dati arrivano dal driver rtc portato nella v0.2.5.0.

Le verifiche scelte

Tre formati che controllano cose diverse:

  • %F %T — il formato ISO, cifre e separatori
  • %A %e %B %Y — i nomi, e soprattutto il giorno della settimana, che viene calcolato con Zeller e sarebbe subito sbagliato di un giorno se la formula avesse un errore
  • %I:%M %p (%j) — le ore in formato 12, dove mezzanotte e' 12 e non 0, e il giorno dell'anno

Le 17:12 diventano 05:12 PM, e il 263esimo giorno del 2026 torna.

Cosa manca, e perche'

L'originale ha 33 specificatori. Qui ci sono quelli che hanno senso senza tabelle di localizzazione. Non gestiti:

Motivo
%c %x %Xdipendono dalla localizzazione
%Z %zrichiedono il fuso orario
%U %Wnumero della settimana, servono le regole ISO
%ssecondi dall'epoca, serve una data di riferimento

Quelli non gestiti vengono lasciati passare inalterati invece di produrre un valore sbagliato: chi legge l'uscita vede %Z e capisce che manca, invece di fidarsi di un fuso inventato.

Un errore colto dal controllo severo

desktop3.c usava rtc_time_t senza includere l'header del driver. Quarta volta che -Werror=implicit-function-declaration ferma qualcosa prima che arrivi a destinazione.

Totale

  • 68 comandi da Commands
  • 36 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.38 Commands: dd, hexdump -C, cksum System V Set 2026
dd bs=128 conv=ucase: 2+1 blocchi   "NATALIZIOOS RAMFS - "
cksum System V: 24523   (cksum CRC: 3568399731, sum BSD: 57676)

dd e il rapporto "n+m"

La sintassi strana - if=, of=, bs= invece delle opzioni con trattino - viene da JCL, il linguaggio di controllo dei mainframe IBM: dd fu scritto per leggere nastri che arrivavano da quelle macchine, e ne ereditò il modo di parlare.

Il rapporto finale ha un significato preciso: n blocchi letti interi, m parziali. Un file da 307 byte in blocchi da 128 ne da' due interi e uno parziale — ed e' quello che si legge.

conv=swab scambia i byte a coppie: serviva a leggere nastri scritti da macchine con l'ordine inverso. Oggi sembra esotico, ma e' la ragione per cui dd esiste.

Tre somme di controllo, tre epoche

Lo stesso file, tre algoritmi diversi:

ComandoRisultatoMetodo
sum (BSD)57676rotazione e somma a 16 bit
cksum (POSIX)3568399731CRC a 32 bit
cksum -o1 (System V)24523somma con ripiegamento del riporto

Ognuno risolve lo stesso problema con i mezzi della sua epoca, e averli affiancati mostra l'evoluzione meglio di qualunque spiegazione.

hexdump -C

I sedici byte sono divisi in due gruppi da otto, e non e' estetica: rende possibile contarli a colpo d'occhio senza seguirli uno per uno.

Totale

  • 71 comandi da Commands
  • 36 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.39 Lotto ampio: dodici comandi Set 2026
tac 10   nl -ba 10   head -c20 20   tail -c20 20   grep -c 1
sort -r 10   sort -u 9   wc -w 44   tr -c: 267 sostituiti
seq 1 3 10: 4 valori   basename -s .txt: programma

Dodici in un colpo invece di tre o quattro: sono tutti brevi e si somigliano nella struttura - leggono, trasformano, scrivono - quindi ha senso portarli insieme.

ComandoCosa aggiunge
taccat al contrario, dall'ultima riga
nl -banumera tutte le righe, anche le vuote
head -c / tail -ci primi e gli ultimi byte, non righe
grep -c / grep -lconta soltanto, o dice solo il nome del file
sort -r / sort -uordine inverso, e senza doppioni
wc -wsolo le parole
seqcome jot, con la sintassi GNU
basename -stoglie anche il suffisso
tr -ccomplemento: agisce sui caratteri non elencati

Due numeri che raccontano qualcosa

sort -r 10 contro sort -u 9: c'e' una riga duplicata nel file, e -u la toglie. Se i due numeri fossero uguali, -u non starebbe facendo nulla.

tr -c con le sole maiuscole: 267 sostituzioni su 307 caratteri. Il complemento agisce su tutto il resto - minuscole, spazi, punteggiatura - ed e' la logica opposta a quella di tr normale.

Totale

  • 83 comandi da Commands
  • 36 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.40 Secondo lotto ampio: altri dodici comandi Set 2026
grep -n 1   grep -i 1   wc -m 307   head -q 3   cut -f1 10
uniq -d / uniq -u   find -type d/f   ls -aR   du -s   df
ComandoCosa aggiunge
grep -nil numero di riga davanti
grep -isenza distinguere maiuscole
wc -mi caratteri
head -qsenza intestazione col nome file
cut -fper campi delimitati, non per colonne fisse
uniq -dsolo le righe duplicate
uniq -usolo quelle che non si ripetono
find -typefiltra file o directory
find -sizefiltra per dimensione
ls -aRnascosti, e discesa ricorsiva
du -sil totale invece del dettaglio
dfspazio del filesystem

cut -c e cut -f, due modi diversi

-c taglia posizioni fisse, -f taglia campi delimitati. Su un file con colonne di larghezza diversa solo -f funziona, ed e' il motivo per cui esistono entrambi.

uniq -d e uniq -u sono complementari

Uno mostra solo cio' che si ripete, l'altro solo cio' che e' unico. Insieme coprono tutte le righe senza sovrapporsi - ed e' la verifica: la loro somma deve tornare.

grep -i verificato al contrario

La ricerca usa "natalizioos" tutto minuscolo contro un file che contiene NATALIZIOOS maiuscolo: se la conversione non funzionasse, il conteggio sarebbe zero invece di uno.

Totale

  • 95 comandi da Commands
  • 36 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.41 Terzo lotto ampio: sed, awk e altri dieci Set 2026
sed -n 1p: NATALIZIOOS RAMFS - filesystem RAM (root /).
sed 1d 9   sed 2,5p 4   awk $1 8   awk NF max 7   tail -3 3
grep -w file 0  contro  grep file 1   wc totale 20 righe 614 byte
rev NATALIZIOOS -> SOOIZILATAN   printf \t\n: 4 byte

Verifica: 21 righe verdi, 0 rosse.

grep -w, la verifica piu' chiara del lotto

La parola file nel testo compare solo dentro filesystem.

  • grep file la trova: 1
  • grep -w file no: 0

E' esattamente la differenza che -w deve fare. Cercare la con grep normale trova anche parlare; con -w i bordi devono essere non alfanumerici, e filesystem non passa.

sed e awk, le espressioni che si usano davvero

Sono entrambi linguaggi interi. Qui ci sono le forme che coprono quasi tutto l'uso reale:

FormaCosa fa
sed -n Npsolo la riga N
sed Ndtutte tranne la N
sed A,Bple righe fra A e B
awk '{print $N}'il campo N di ogni riga
awk NFquanti campi ha una riga

awk separa sui gruppi di spazi, non su ogni spazio: due spazi consecutivi non producono un campo vuoto. E' il comportamento dell'originale, e cambia il risultato su qualunque testo allineato.

Gli altri

grep -x (la riga intera deve corrispondere), wc su piu' file col totale, expand -t con passo scelto, rev su stringa, e il printf della shell con le sequenze \t \n \r.

Totale

  • 107 comandi da Commands
  • 36 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.42 Il conteggio corretto, e sei comandi Set 2026

Una correzione sui numeri

Le versioni precedenti dichiaravano 107 comandi. Il numero contava le opzioni come comandi separati: grep, grep -c, grep -n, grep -i, grep -w, grep -x risultavano sei.

I comandi distinti portati sono 77 con questo lotto.

Quanti ne restano

Nell'archivio ci sono 334 comandi con codice C. Ne restano 257, ma non sono tutti uguali:

CategoriaQuantiCosa li blocca
Portabili ora~152niente
Rete (AppleTalk, TCP/IP)53manca lo stack di rete
Scrittura su disco27il VFS non scrive
Processi e demoni19mancano i processi
Utenti e password9manca un sistema di permessi
Shell complete6centinaia di migliaia di righe, e vogliono i processi

La divisione e' approssimata: e' fatta per parole chiave nel nome, e qualcuno fra i "portabili" si rivelera' bloccato aprendolo.

Il dato che conta: i ~114 bloccati non si sbloccano portando comandi, ma aggiungendo al sistema scrittura nel VFS e processi. Con quelle due cose ne entrerebbero una cinquantina di colpo.

I sei comandi

arch: i386   domainname: natalizioos.local
checknr: bilanciato 0 errori, lasciato aperto 1

checknr controlla che i cambi di carattere nroff siano bilanciati: ogni \fB deve tornare a \fR. Nei manuali, dimenticarne uno trasformava in grassetto tutto il resto. Verificato nei due versi: zero errori sul testo corretto, uno su quello lasciato aperto.

asa interpreta i caratteri di controllo carrello di FORTRAN - il primo carattere di ogni riga diceva alla stampante a catena cosa fare prima di stamparla. colcrt toglie le sovrapposizioni che la stampante usava per sottolineare. calendar mostra le righe di un file di promemoria con la data di oggi, letta dal driver rtc.

Detto chiaramente: asa, colcrt e calendar sono compilati ma non ancora verificati a schermo. arch, domainname e checknr si'.

Totale

  • 77 comandi distinti da Commands (su 334)
  • 36 funzioni da Libc
  • Runtime, Foundation, AppKit, DriverKit, ponte Mach, SoundKit, driver Beep
nessuna schermata di questa versione
DEVv0.2.5.43 La scrittura non era bloccata Set 2026

Un blocco che non esisteva

Nella v0.2.5.42 ventisette comandi erano classificati come "bloccati: il VFS non scrive". Era sbagliato. vfs_write, vfs_create e vfs_unlink esistevano gia' nel ramfs - non erano stati verificati prima di dichiarare il blocco.

Otto comandi che modificano il filesystem

mkdir, touch (ora completo: crea il file), cp, rm, rmdir, mv, chmod, chown, ln.

SCRITTURA: voci 6 -> 9 dopo mkdir/touch/cp -> 6 dopo rm
cp 307 byte   cp su se stesso: rifiutato   rmdir non vuota: rifiutato
mv: 307 byte, sorgente sparita: si   chmod: ok

La verifica guarda lo stato del filesystem, non si fida dei valori di ritorno: il numero di voci deve salire di tre e tornare esattamente al punto di partenza.

Comportamenti dell'originale mantenuti

  • cp rifiuta di copiare un file su se stesso: aprirebbe la destinazione troncandola, e a quel punto la sorgente sarebbe gia' vuota
  • rm senza -r non tocca le directory
  • rmdir rifiuta le directory non vuote
  • mv cancella la sorgente solo se la copia e' riuscita, per non perdere il file a meta'

Un errore nella prova, trovato dalla prova

La prima versione verificava il rifiuto di rmdir usando la directory di sistema disk, data per piena. Era vuota - e' un punto di montaggio - e rmdir l'ha cancellata correttamente. Il conteggio finale e' sceso a 5 invece di tornare a 6, e la riga e' uscita rossa.

Il codice di scrittura era giusto; era sbagliata la prova. Ora si crea da sola la sua directory piena. La regola: mai usare directory di sistema come materiale di prova.

Divergenze dichiarate

  • mv fa copia piu' cancellazione: il ramfs non ha il rename
  • ln fa una copia: il ramfs non ha i collegamenti, quindi modificando uno dei due l'altro non cambia

Stato del conteggio

Comandi distinti portati85 su 334
Categoria "scrittura su disco"sbloccata - era un falso blocco
Ancora bloccati davverorete, processi, utenti, shell
nessuna schermata di questa versione
DEVv0.2.5.44 Secondo falso blocco: gli utenti Set 2026
UTENTI: root   gruppi: wheel users   registrati: 2
id: uid=0(root) gid=0(wheel) groups=0(wheel),100(users)
sudo permesso: si   su root: ok   passwd con vecchia errata: rifiutata

Un altro blocco che non esisteva

Nove comandi erano classificati come "bloccati: manca un sistema di permessi". Falso. kernel/users.c esisteva gia', con:

  • uid e flag di amministratore
  • password con sale e hash, non in chiaro
  • users_login, users_add, users_remove, users_passwd
  • password_validate per i requisiti minimi
  • sudoers_check per i permessi elevati
  • current_user, l'utente connesso

E' il secondo falso blocco dopo la scrittura nel VFS. In entrambi i casi avevo dichiarato mancante qualcosa senza aprire il file - e in entrambi i casi il codice c'era gia'.

Otto comandi

whoami, id (ora coi dati veri), groups, users, passwd, su, logname, sudo.

Una sola via per l'autenticazione. su e passwd verificano passando da users_login, la stessa funzione della schermata di accesso: se ce ne fossero due, prima o poi divergerebbero e una delle due sarebbe piu' debole.

Le verifiche scelte

  • passwd con la vecchia password sbagliata deve fallire con -1, non accettare in silenzio. E' il controllo che distingue un passwd vero da uno che cambia e basta
  • su verso se stessi da root passa senza password, come nell'originale: root non deve dimostrare nulla
  • id mostra i gruppi multipli solo per gli amministratori

Divergenza dichiarata

sudo risponde se il permesso ci sarebbe, ma non esegue nulla: senza processi non c'e' cosa eseguire.

Stato dei blocchi

BloccoStato
Scrittura su discofalso, sbloccato nella 0.2.5.43
Utenti e permessifalso, sbloccato qui
Processi (19 + compilatore + shell)reale
Rete (53)reale

Comandi distinti portati: 93 su 334.

nessuna schermata di questa versione
DEVv0.2.5.45 Terzo falso blocco, e il blocco vero Set 2026
ESECUZIONE: file su testo -> non e' un ELF
which: /readme   nice 99 limitato a 19   printenv PATH: /bin:/usr/bin

Terzo falso blocco: i processi

Diciannove comandi erano classificati come "bloccati: mancano i processi". kernel/elf.c sa gia' caricare ed eseguire binari ELF - elf_load, elf_execute, elf_unload - e la shell li usa gia' in commands.c.

Terzo caso su tre in cui avevo dichiarato mancante qualcosa senza aprire il file. Non e' una svista isolata: e' un modo di procedere sbagliato, e da qui in avanti verifico prima di classificare.

Ma qui il blocco esiste davvero, ed e' piu' preciso

Non manca l'esecuzione: manca la concorrenza.

Un programma eseguito qui gira fino alla fine e restituisce un valore. Non c'e' un secondo programma che gira insieme, ne' un identificativo di processo da elencare o terminare.

Quindi la divisione vera e':

ComandiStato
time, which, nice, nohup, file su ELFentrano: serve eseguire, e si puo'
ps, kill, top, renicerestano fuori: servono processi concorrenti

E' una distinzione che prima non avevo, e che vale piu' di un conteggio: dice esattamente cosa costruire per sbloccare il resto.

Sei comandi

time, which, file sull'ELF, nice, printenv con nome, basename -a.

file sull'ELF legge l'intestazione e dice per quale macchina e' fatto il binario. Su un sistema a 32 bit, provare a eseguire un ELF a 64 bit e' l'errore piu' frequente: meglio accorgersene prima.

Divergenze dichiarate

  • time riporta solo il tempo reale. L'originale ne da' tre - reale, utente, sistema - ma senza contabilita' separata fra modo utente e kernel gli altri due sarebbero inventati
  • nice registra la priorita' e la limita fra -20 e 19, ma senza concorrenza non ha effetto sull'esecuzione
  • which cerca nella radice: il VFS non ha ancora /bin

Stato dei blocchi

BloccoVerdetto
Scrittura su discofalso
Utenti e permessifalso
Esecuzione di programmifalso
Concorrenza (ps, kill, top)reale
Rete (53 comandi)reale

Comandi distinti portati: 99 su 334.

nessuna schermata di questa versione
DEVv0.2.5.46 ps, kill, top, renice Set 2026
  PID STATO     NOME
    0 in corso  main
    2 pronto    prova_due
    1 pronto    prova_uno

kill: sistema rifiutato, task vero ok, due volte rifiutato
top: 3 task totali, 2 pronti, 1 in corso, 0 finiti

Quarto falso blocco

Li avevo dichiarati impossibili per mancanza di concorrenza. kernel/task.c ha gia' una tabella di task con identificativo, nome, stato e lista concatenata, piu' task_current e task_list_head per scorrerla.

Quattro volte su quattro ho classificato come mancante qualcosa che c'era: scrittura nel VFS, utenti, esecuzione di programmi, concorrenza. La regola che ne esce: aprire il file prima di dichiarare il blocco.

Quello che manca davvero e' la preemption

I task cedono il controllo con task_yield, non vengono interrotti dal timer. Per questi quattro comandi non cambia nulla - elencare, terminare e misurare funzionano uguale - ma cambia per la priorita': senza preemption renice non influenza l'ordine di esecuzione.

Le regole dell'originale mantenute

  • kill rifiuta il task 0: e' quello di sistema, terminarlo fermerebbe la macchina
  • kill rifiuta un task gia' finito, invece di dire che e' andato bene
  • renice limita fra -20 e 19

La verifica prova kill in tutti e tre i casi: sul task di sistema, su uno vero, e sullo stesso due volte. Un kill che dicesse sempre "ok" passerebbe solo il secondo.

Un difetto trovato dalla prova

ps e top non mostravano nulla: nel contesto del Desktop 3 la lista dei task risultava vuota, anche se task_init viene chiamato nella sequenza di avvio. Ora si inizializza se serve, invece di dare per scontato che sia pronta.

Divergenze dichiarate

  • top non mostra la percentuale di processore per task: senza preemption un task che non cede il controllo risulterebbe al 100%, e sarebbe un numero senza senso
  • renice tiene la priorita' in una tabella affiancata, perche' task_t non ha quel campo e aggiungerlo significherebbe modificare il nucleo. Quando lo scheduler diventera' a priorita', il valore sara' gia' li'

Comandi distinti portati: 103 su 334.

nessuna schermata di questa versione
DEVv0.2.5.47 Rete: il blocco e' reale, ma non totale Set 2026
RETE: natalizioos.natalizioos.local   dispositivi PCI: 6
ifconfig -a:
  eth0: 8086:100e  Intel 82540EM (e1000)
  lo0: 127.0.0.1  anello locale
ping 127.0.0.1: 64 byte da 127.0.0.1: tempo=0.0 ms
ping 8.8.8.8: irraggiungibile (manca lo stack TCP/IP)

Questa volta il blocco c'e' davvero

Dopo quattro falsi blocchi ho guardato prima di dichiarare. Qui non c'e' ne' uno stack TCP/IP ne' un driver Ethernet: i comandi che scambiano pacchetti - telnet, ftp, rlogin - restano fuori e non si possono fingere.

Ma due cose ci sono

  • il PCI riconosce le schede di rete dalla classe 0x02
  • drivers/restapi.c parla gia' un protocollo su seriale

Bastano per i comandi che descrivono la rete invece di usarla.

Cinque comandi

ifconfig -a, lspci, hostname -f, ping, netstat -i.

ifconfig legge davvero dal bus: 8086:100e e' l'identificativo della scheda Intel e1000 emulata da QEMU, non un valore scritto a mano. Cambiando scheda nella riga di comando di QEMU, cambia qui.

ping distingue i due casi: verso 127.0.0.1 risponde, perche' l'anello locale non lascia mai la macchina e la risposta e' per definizione immediata - ed e' quello che fa anche l'originale, che sull'anello locale funziona pure senza schede. Verso qualunque altro indirizzo fallisce e dice perche', invece di inventare un tempo di risposta.

La riga che conta di piu'

ping 8.8.8.8: irraggiungibile (manca lo stack TCP/IP)

Sarebbe stato facile far rispondere tutto. Un ping che risponde sempre e' peggio di uno che manca: nasconde il problema invece di segnalarlo.

Cosa servirebbe per il resto

Un driver per la e1000 - che il PCI gia' vede - e uno stack TCP/IP. Con quelli entrerebbero i 48 comandi di rete rimasti.

Comandi distinti portati: 108 su 334.

nessuna schermata di questa versione
DEVv0.2.5.48 LAVORO IN CORSO, non una versione finita Set 2026

Questo pacchetto non e' completo. Viene consegnato solo per non perdere il lavoro: tutto stava in /tmp, che si azzera a ogni reset dell'ambiente.

Cosa funziona

Kernel 11151 settori, intatto, arriva al login
Kernel 2~323 settori, Desktop 3 a 1024x768
Boot selectorpremi 3 -> carica dal settore 2048
Driver e1000trasmette (provato con cattura pacchetti)
Spazio libero828 settori = 414 KB

Il muro che non c'e' piu'

Il kernel 1 era a 1151 settori su 1151 utilizzabili: la BIOS lo carica in modo reale fra 0x10000 e 0xA0000, e li' finisce lo spazio. Non poteva crescere di un byte, e qualunque aggiunta rompeva l'avvio senza un messaggio.

La soluzione e' un secondo kernel caricato dal bootloader da un altro settore, con dentro solo cio' che serve al Desktop 3. Nasce mezzo vuoto, e la rete ha finalmente dove stare.

settore 9      kernel 1   shell + Desktop 1 + Desktop 2
settore 2048   kernel 2   Desktop 3 + rete

Quattro difetti veri trovati e corretti

  1. Bus mastering mancante - pci_write_config_dword non esisteva nel driver PCI. Senza, la scheda non puo' leggere la memoria: consumava i descrittori e sul cavo non usciva nulla
  2. Descrittori non volatile - li scrive la scheda in DMA, e il compilatore teneva lo stato in un registro
  3. Descrittore RX di 15 byte invece di 16 - il checksum e' a 16 bit, non a 8. La scheda rifiutava la lunghezza dell'anello in silenzio: RDLEN tornava 384 invece di 512
  4. Sequenza di inizializzazione fuori ordine - riscritta seguendo il manuale

Il terzo l'ho trovato solo rileggendo ogni registro dopo averlo scritto. E' il metodo giusto per questo codice, ed e' annotato nei commenti.

Cosa NON funziona

La ricezione. RDH resta a zero: la scheda non scrive nell'anello, anche con configurazione valida, bus mastering attivo, filtri disattivati e modalita' promiscua.

Provato e scartato, ognuno verificato: volatile, bit di validita' del filtro, tabella multicast, ordine RCTL/RDT in entrambi i versi, ripristino con CTRL.RST, anelli da 8 e da 32 descrittori, riscrittura completa della sequenza.

Il punto da cui ripartire: scriviamo RCTL = 0x0400801A e rileggendo sembra tornare 0x0400601A. Va verificato in DECIMALE, perche' a schermo i caratteri esadecimali si possono confondere. Se i due numeri sono davvero diversi, la scheda interpreta la scrittura in modo inatteso - ed e' li' la risposta.

Cosa manca per i 48 comandi

Ricezione funzionante, poi ARP completo, IPv4, ICMP (ping vero), UDP e TCP. Sono migliaia di righe, e vanno scritte con calma: lo spazio adesso c'e'.

nessuna schermata di questa versione
DEVv0.2.5.51 Stack di rete: IPv4, ICMP, UDP, TCP Set 2026

Lavoro in corso. La ricezione e' ancora ferma, ma tutto cio' che le sta sopra e' costruito e provato con risposte vere dall'altro capo.

Il cambio di strategia che ha sbloccato tutto

La ricezione era bloccata e insistevo su quella. Ma la costruzione di un pacchetto si verifica senza riceverne indietro: se il nostro e' ben formato, l'altro capo risponde - e la cattura del traffico lo mostra.

Nessuno risponde a un pacchetto con l'intestazione sbagliata o il checksum errato. Quindi la risposta e' una prova.

Cosa e' stato costruito e verificato

 5: USCITA  IPv4  42B  ICMP richiesta eco
 6: ENTRATA IPv4  60B  ICMP RISPOSTA ECO
 9: USCITA  IPv4  71B  UDP 40000->53  DNS
12: ENTRATA IPv4 103B  UDP 53->40000  DNS
10: USCITA  TCP 45000->80  SYN
11: ENTRATA TCP 80->45000  ACK+RST
LivelloVerifica
IPv4il gateway risponde al ping: intestazione e checksum corretti
ICMPrichiesta di eco accettata, risposta ricevuta
UDPil risolutore DNS risponde con 103 byte
TCPil SYN riceve RST+ACK: "capito, porta chiusa" - prova che il segmento e' valido

Tre cose imparate, scritte nei commenti

Il checksum di Internet ha una proprieta' comoda: sommando anche il checksum stesso, un pacchetto integro da' sempre zero. Il ricevente non ricalcola e confronta - somma tutto e guarda se viene zero.

UDP e TCP sommano anche gli indirizzi IP, che non stanno nel loro pacchetto. Serve a scoprire un pacchetto consegnato al destinatario sbagliato. Quei campi formano una pseudo-intestazione che esiste solo per il calcolo e non viene mai spedita.

La lunghezza dell'intestazione TCP si misura in parole da 32 bit, non in byte, e sta nei quattro bit alti: venti byte si scrivono 0x50. Metterci 20 e' l'errore classico, e il pacchetto viene scartato senza un messaggio.

La ricezione: diagnosi chiusa

I byte grezzi del primo descrittore, dopo che le risposte sono arrivate sul cavo:

desc0 grezzo: 0 0 0 0 0 0 0 0

Tutti zeri. Non e' un campo letto male: la scheda non scrive proprio. Con:

  • indirizzo dell'anello nella scheda identico al nostro (verificato)
  • descrittore di 16 byte (verificato)
  • RDLEN=512 accettato, RDT=31, RCTL esatto al bit
  • bus mastering attivo, modalita' promiscua, collegamento su

Dieci ipotesi provate, nessuna ha cambiato il comportamento. Il prossimo passo e' leggere il sorgente della e1000 dell'emulatore e vedere quale controllo fallisce, invece di indovinarlo.

Stato

Kernel 11151 settori, intatto
Kernel 2328 settori, Desktop 3 a 1024x768
Spazio libero823 settori = 411 KB
Passi fatti3, 4, 5 su 6
Passo aperto1 (ricezione), e con esso il 2
nessuna schermata di questa versione
DEVv0.2.5.52 Interprete dei pacchetti Set 2026

ported/rete_rx.c: dato un fotogramma grezzo, dice se e' ARP, eco ICMP, UDP, TCP o da scartare. I controlli vengono prima delle letture: una trama troncata e' il caso normale di un attacco. Autoprova 8/8, con casi negativi (trama corta, checksum rotto, tipo ignoto).

nessuna schermata di questa versione
DEVv0.2.5.53 Primi comandi di rete Set 2026

ported/rete_comandi.c: ping, arping, nslookup, traceroute, netstat -s, nc, in sola trasmissione - dicevano "risposte non ancora leggibili" invece di inventarle. Interprete DNS con la compressione dei nomi (0xC0): provato su una risposta vera.

nessuna schermata di questa versione
DEVv0.2.5.54 Instradamento Set 2026

ported/rete_comandi2.c: tabella delle rotte, vince il prefisso piu' lungo e la predefinita /0 perde sempre. route, telnet, ftp, whois, ntpdate, tftp (in sola trasmissione). Autoprova 4/4.

nessuna schermata di questa versione
DEVv0.2.5.55 inet_aton e ipcalc Set 2026

ported/rete_inet.c: inet_aton accetta le forme abbreviate BSD ("127.1" = 127.0.0.1), rifiuta 256 e i caratteri in coda. ipcalc: rete, broadcast, maschera, ospiti (/31 e /32 ne hanno zero). Autoprova 9/9.

nessuna schermata di questa versione
DEVv0.2.5.56 Libc di rete Set 2026

ported/rete_libc.c: ether_aton/ether_ntoa, getservbyname/ getservbyport (22 servizi), gethostbyname/gethostbyaddr (file locale prima del DNS), htons/htonl. Autoprova 12/12: l'unico errore trovato era nell'indice della prova, non nella funzione.

nessuna schermata di questa versione
DEVv0.2.5.57 Macchina a stati TCP Set 2026

ported/rete_tcp_stati.c: gli undici stati di RFC 793 e le transizioni guidate dai fanioni. RST ha la precedenza su tutto; TIME_WAIT dura 60 s. Autoprova 14/14, compreso cio' che NON deve succedere (un ACK in SYN_SENT non apre la connessione). netstat -a.

nessuna schermata di questa versione
DEVv0.2.5.58 Socket e aritmetica di sequenza Set 2026

ported/rete_socket.c: descrittori di socket (il descrittore 0 e' valido), connect, send, close. I numeri di sequenza TCP ripartono da zero dopo 2^32: si confronta la differenza con segno, non i valori. Un send TCP senza handshake completato e' rifiutato (-3), non finto. Autoprova 15/15.

*Nota: questa voce e le sei seguenti sono state scritte in v0.2.5.59. Il lavoro dalla 0.2.5.52 alla 0.2.5.58 stava in /tmp e si e' perso con un azzeramento dell'ambiente; i file sono stati riscritti identici e le autoprove ripassate tutte.*

nessuna schermata di questa versione
DEVv0.2.5.59 La ricezione funziona: la rete risponde Set 2026

Lavoro in corso verso i 48 comandi di rete, ma il muro e' caduto: la scheda e1000 riceve, e sopra la ricezione ora c'e' una pila vera.

RICEZIONE: dopo 1100 ms, prima trama 64 B: risposta ARP
ARP 10.0.2.2 -> 52:55:0a:00:02:02  (1 giri)
$ ping -c 3 10.0.2.2
  64 byte da 10.0.2.2: icmp_seq=0 ttl=255 tempo=0.457 ms
  64 byte da 10.0.2.2: icmp_seq=1 ttl=255 tempo=0.085 ms
  64 byte da 10.0.2.2: icmp_seq=2 ttl=255 tempo=0.075 ms
  3 trasmessi, 3 ricevuti, 0% persi
$ nslookup example.com
  Nome:      example.com
  Indirizzo: 172.66.147.243
$ traceroute -m 3 10.0.2.2
   1  10.0.2.2  0.658 ms
   2  127.0.0.1  0.310 ms
$ nc -z 10.0.2.2 80
  10.0.2.2:80 - rifiutata: RST in 0.461 ms (porta chiusa)
$ dhclient eth0
  DHCP: offerta 10.0.2.15 da 10.0.2.2
  DHCP: confermato 10.0.2.15/255.255.255.0  router 10.0.2.2
  DHCP: DNS 10.0.2.3  durata 86400 s

La causa del silenzio: un timer di un secondo

Dieci ipotesi sui registri, tutte scartate. La risposta era nel sorgente dell'emulatore (hw/net/e1000.c di QEMU 8.2), letto invece che indovinato:

set_rx_control():    timer_mod(s->flush_queue_timer, adesso + 1000 ms);
e1000_can_receive(): return ... && !timer_pending(s->flush_queue_timer);

Ogni scrittura di RCTL arma un timer di un secondo, e finche' corre la scheda dice "non posso ricevere": i pacchetti restano in coda nell'emulatore e arrivano tutti insieme allo scadere. Noi leggevamo entro pochi millisecondi dall'abilitazione, e l'elenco del Desktop 3 si costruisce una volta sola: guardavamo sempre dentro quel secondo. I registri erano giusti da tre versioni.

Ora e1000_attendi_rx() aspetta che la finestra si chiuda, con un tetto: se il tempo non scorresse, esce invece di bloccare.

Il timer era fermo: le interruzioni spente

Aspettare un secondo richiede un orologio che cammini. Il kernel 2 non accendeva mai le interruzioni: il kernel 1 lo fa subito dopo timer_init, il kernel 2 no. Si accendevano per caso alla prima creazione di un task (task_trampoline esegue sti). Ora sti e' esplicito, dopo fb_init, perche' il gestore del mouse disegna.

La pila: rete_pila.c

Ogni trama passa da rete_elabora(), che la riconosce con rx_interpreta() (provato 8/8 in v0.2.5.52) e la smista:

ArrivaCosa succede
ARPla tabella impara (regola "merge" di RFC 826); se chiedono di noi, rispondiamo
ICMP ecorispondiamo: NATALIZIOOS ora si puo' "pingare"
ICMP rispostaannotata con l'ora di partenza scritta nei dati, come fa ping.c
ICMP erroreirraggiungibile / TTL scaduto, per traceroute
UDPin coda sulla porta in ascolto
TCPalla connessione giusta: SYN, SYN+ACK, ACK, dati, FIN, RST

La macchina a stati TCP e' la stessa tcp_transizione() provata 14/14 in v0.2.5.57, e l'aritmetica di sequenza e' quella provata 15/15 in v0.2.5.58.

ARP vero, e il MAC provvisorio va in pensione

ip_mac_per() sceglie il prossimo salto con la tabella delle rotte (prefisso piu' lungo), poi chiede ad ARP. UDP, TCP e ICMP la usano tutti: se ARP non risponde il pacchetto non parte (-4), invece di partire verso un indirizzo inventato. ip_mac_provvisorio() resta, solo per costruire il pacchetto di prova dell'interprete.

Comandi con le risposte (rete_comandi3.c)

ping, arping, nslookup, traceroute, nc -z, una GET HTTP, dhclient e netstat -s fanno il loro lavoro per intero. Il ping misura il tempo come l'originale: l'ora di partenza sta nei 56 byte di dati, l'altro capo la rimanda, all'arrivo si fa la differenza. traceroute usa sonde UDP verso porte alte a TTL crescente, come traceroute.tproj.

Due difetti che nessuno vedeva

Il Desktop 3 aveva 196 righe su un massimo di 96: le ultime 100 - fra cui DriverKit, SoundMeter, Beep, Mach e gli utenti - venivano scartate senza avviso. Nel kernel 2 il limite ora e' 400 (il kernel 1 resta a 96) e l'elenco si copia sulla seriale, per verificarlo parola per parola invece che con l'OCR.

Gli utenti del kernel 2 erano vuoti: nessuno chiamava users_init(). Non si vedeva perche' quelle righe erano oltre la 96esima.

Il file system scriveva sopra il kernel 1

Scoperto controllando dove si scrive sul disco:

AreaPrimaDentro il kernel 1?Ora
N.A.T. FSsettore 100si'8192
sudoerssettore 98si'4095
patch (Makefile)settore 200si'4200

Il kernel 1 sta sul disco dal settore 9 e occupa oltre 1050 settori. Al primo avvio natfs_init() trovava codice al posto del superblocco, formattava, e scriveva sopra il kernel. Verificato: dopo un avvio l'immagine differiva da kernel.bin proprio al byte (100-9)*512. Quando il file system fu scritto il kernel era piccolo; e' il kernel che gli e' cresciuto addosso. Ora, dopo un avvio del kernel 1 e uno del kernel 2 sulla stessa immagine, entrambi i kernel sul disco sono identici byte per byte ai file compilati.

I file salvati nel vecchio N.A.T. FS non si recuperano: stavano sopra il kernel e venivano cancellati a ogni ricompilazione.

Altro

  • avvio: la scelta del kernel non aspetta piu' per sempre. Dalla 0.2.5.48 il kernel 1 partiva solo premendo un tasto; ora dopo 5 secondi parte da solo (contatore della BIOS a 0040:006C)
  • Desktop 3 (kernel 2): PagSu/PagGiu/Inizio/Fine per scorrere; il kernel 1 non li riceve, la sua shell li stamperebbe come caratteri
  • la versione del kernel 2 si legge dal Makefile del kernel 1
  • make ora mette nell'immagine anche il kernel 2: prima serviva make -f Makefile.k2 a parte, e senza, il tasto 3 caricava settori vuoti. make graphic comincia con clean, che cancellava anche il kernel 2: ora viene ricompilato in coda
  • il kernel 2 si compila con clang se c'e', come il kernel 1 (prima solo gcc -m32, che sulla macchina di sviluppo non c'e'): provato con entrambi, 340 settori con clang e 382 con gcc
  • rete.h: dichiarazioni comuni dei moduli di rete

I 48 comandi, contati per nome

Il 53 delle versioni passate era la categoria "rete" del conteggio dell'archivio. Contati uno per uno: in network_cmds e at_cmds ci sono 51 comandi di rete con codice C - 42 TCP/IP, 7 AppleTalk, bpwhoami, tn3270 - piu' lspci e hostname -f fuori da quelle cartelle. Tolti i 5 descrittivi della v0.2.5.47, restano esattamente 48:

TCP/IP   arp domainname ftp logger makedbm newclient nfsstat nslookup
         rcp rdist revnetgroup rlogin route rpcinfo rsh rstat ruptime
         rwho slattach sliplogin spray startslip stdethers stdhosts talk
         tcpdump telnet tftp traceroute trpt trsp wall ypcat ypmatch
         yppoll yppush ypset ypwhich ypxfr
AppleTalk appleping appletalk at_cho_prn atlookup atprint atstatus aurpd
altri    bpwhoami tn3270

Con risposte vere da questa versione: arp, nslookup, traceroute. route ha la tabella ma non ancora i sottocomandi.

Stato

Kernel 11060 settori (clang), parte, login verificato
Kernel 2382 settori su 952 utilizzabili (a 3000 inizia il backup)
Ricezionefunziona
Comandi di rete3 su 48 completi, piu' ping/netstat/dhclient
La rete risponde: ping, arping, nslookup
v0.2.5.59 - La rete risponde: ping, arping, nslookup
DEVv0.2.5.60 Il terminale di rete: i comandi si scrivono a mano Set 2026

Nel Desktop 3 del kernel 2, in fondo alla finestra degli strumenti, c'e' una riga di comando. Si scrive, Invio esegue, l'uscita si accoda all'elenco:

rete> ping -c 2 gateway
  64 byte da 10.0.2.2: icmp_seq=0 ttl=255 tempo=0.210 ms
  64 byte da 10.0.2.2: icmp_seq=1 ttl=255 tempo=0.080 ms
rete> route add 192.168.5.0/24 10.0.2.2
  add net 192.168.5.0/24: gateway 10.0.2.2
rete> route get 192.168.5.7
     route to: 192.168.5.7
      gateway: 10.0.2.2
rete> tcpdump -c 4 icmp
  41.380633 10.0.2.15 > 10.0.2.2: icmp: echo request
  41.380655 10.0.2.2 > 10.0.2.15: icmp: echo reply
  44.117833 10.0.2.2 > 10.0.2.15: icmp: time exceeded in-transit
  44.118028 127.0.0.1 > 10.0.2.15: icmp: 10.0.2.2 udp port 33436 unreachable

aiuto elenca i comandi. I nomi si risolvono come fa gethostbyname(): numerici, poi la tabella locale (localhost, gateway, dns, natalizioos), poi il DNS.

telnet: una sessione vera

telnet host porta apre una sessione: le righe scritte vanno all'altro capo, cio' che arriva si stampa in blu. Le opzioni si negoziano come vuole RFC 854 - si accetta che sia il server a fare l'eco e a sopprimere il "go ahead", si rifiuta il resto. Provato contro un server che chiede ECHO, SGA e TERMINAL-TYPE:

dal server:   IAC WILL ECHO, IAC WILL SGA, IAC DO TTYPE
risposta:     IAC DO ECHO,   IAC DO SGA,   IAC WONT TTYPE

e poi una conversazione intera, fino alla chiusura dall'altro capo: SYN, SYN+ACK, ACK, dati nei due versi, FIN, ACK, FIN. ESC chiude la sessione senza uscire dal desktop.

tcpdump

Porta il formato delle righe di print-arp.c, print-icmp.c, print-udp.c, print-tcp.c e print-domain.c dell'archivio. Le trame vengono da un registratore sempre acceso nella pila (le ultime 48, in entrata e in uscita, 320 byte ciascuna): mostra cio' che e' gia' successo. Filtro per protocollo: arp, icmp, udp, tcp.

Gli altri comandi di questo lotto

ComandoCosa fa
arp -a / -d / -stabella nel formato dell'originale, ? (ip) at mac
route add/delete/getsulla tabella a prefisso piu' lungo
netstat -a/-s/-r/-iconnessioni TCP vere, porte UDP in ascolto, contatori
ifconfigflag, indirizzo, MAC, contatori della scheda
logger -p fac.liv -t tagregistro locale + datagramma syslog UDP 514
domainnamelegge e imposta il dominio NIS
wallintestazione dell'originale, a schermo e sulla seriale
nc, http, dhclientsessione TCP grezza, GET, DHCP

La finestra si allarga

A 52 colonne le righe di tcpdump si spezzavano in due o finivano sotto la barra di scorrimento. Nel kernel 2 la finestra degli strumenti e' larga 720 pixel (84 colonne); nel kernel 1 resta com'era.

I 48: a che punto siamo

Completi: arp, domainname, logger, nslookup, route, tcpdump, telnet, traceroute, wall - 9 su 48. Parziali: ftp, tftp. Il conto esatto sta in BUILD_STATUS.md, letto dai marcatori COMANDO_RETE: nei sorgenti.

Il terminale di rete: traceroute, tcpdump, nslookup
v0.2.5.60 - Il terminale di rete: traceroute, tcpdump, nslookup
ping e tcpdump icmp
v0.2.5.60 - ping e tcpdump icmp
DEVv0.2.5.61 Trasferire file: tftp, ftp, rcp; e TCP che ritrasmette Set 2026
rete> tftp gateway get grande.bin
  Received 3000 bytes in 0.001215 seconds -> grande.bin
rete> sum grande.bin
  25221     3 grande.bin              <- identico al file originale
rete> ftp gateway 2121
  220 Server FTP di prova pronto
  230 Login successful.
ftp> get dati.bin
  227 Entering Passive Mode (127,0,0,1,215,153).
  150 Opening BINARY mode data connection for dati.bin (10000 bytes).
  226 Transfer complete.
  10000 bytes received in 0.000389 seconds
rete> rcp gateway:leggimi.txt copia.txt
  gateway:leggimi.txt -> copia.txt  (44 byte)
rete> rsh gateway -l ospite whoami
  ospite

I file ricevuti si scrivono davvero nel file system (VFS) e si spediscono leggendoli da li'. Ogni trasferimento e' stato verificato con sum sui due lati: stesse somme, byte per byte.

tftp (tftp.tproj, RFC 1350)

get e put. Il server risponde da una porta nuova (la "TID") e da li' si parla con quella; un pacchetto da un'altra porta riceve l'errore 5, come vuole il protocollo. Blocchi da 512, l'ultimo corto; ritrasmissione dopo 1 s, 5 tentativi; errori del server stampati col loro codice (Error code 1: File not found).

make run-gui avvia ora QEMU con il server TFTP interno, che serve la cartella tftpboot/ del progetto: tftp gateway get benvenuto.txt funziona subito. Il flag si aggiunge solo se QEMU ha la rete "user".

ftp (ftp.tproj, RFC 959)

Sessione con il prompt ftp>: ls, dir, get, put, cd, cdup, pwd, ascii, binary, system, status, user, quote, quit. Accesso anonimo come ftp -a. Dati in modo passivo; se il server indica un indirizzo che da qui non ha senso (127.x dietro la traduzione dell'emulatore) si usa quello della connessione di controllo, come fanno i client moderni.

rsh, rcp, rlogin (i comandi "r" di Berkeley)

La fiducia di questi protocolli sta nella porta privilegiata: il server crede al nome utente se la connessione viene da sotto la 1024. Le porte si prendono dalla 1023 in giu', come rresvport(). rcp e' rsh che lancia rcp -f o rcp -t sull'altra macchina, poi le righe C0644 lunghezza nome e gli zeri di conferma. rlogin apre una sessione come telnet.

TCP: ritrasmissione, finestra, traccia

Per trasferire file non bastava piu' il TCP "da conversazione":

  • ritrasmissione: cio' che e' spedito e non confermato resta in un buffer; senza conferma entro l'RTO (500 ms che raddoppiano) si rispedisce. Vale per dati, SYN e FIN; dopo 6 tentativi la connessione e' persa
  • finestra: si annuncia lo spazio vero del buffer di ricezione e si rispetta quello dell'altro
  • aggiornamento della finestra: la prima versione annunciava la finestra riaperta solo col buffer quasi pieno. Con segmenti da 1440 byte la finestra si fermava a 992 senza superare la soglia: l'altro aspettava il suo timer di persistenza, e 10 KB via FTP ci mettevano 4,7 secondi. Trovato nella cattura dei pacchetti; ora si annuncia appena la finestra cresce di un quarto del buffer (RFC 1122): 0,4 ms
  • il FIN aspetta i dati: un FIN arrivato con dati rimasti fuori dal buffer non chiude piu' la connessione

trpt (trpt.tproj)

La pila tiene una traccia degli ultimi 40 passi TCP, e trpt la stampa nel formato dell'originale:

818 SYN_SENT:output c739dd2e@0(win=2000)<SYN> -> SYN_SENT
818 SYN_SENT:input 19d1601@c739dd2f(win=ffff)<SYN,ACK> -> ESTABLISHED
818 ESTABLISHED:output [c739dd2f..c739dd47)@19d1602(win=2000)<ACK,PUSH> -> ESTABLISHED
839 ESTABLISHED:input 19d164d@c739dd53(win=ffff)<ACK,FIN> -> CLOSE_WAIT

Anche nel terminale

ls, cat, sum: per vedere e verificare cio' che si e' scaricato.

I 48

Completi: arp, domainname, ftp, logger, nslookup, rcp, rlogin, route, rsh, tcpdump, telnet, tftp, traceroute, trpt, wall - 15 su 48.

Come e' stato provato

Contro server veri sulla macchina che ospita l'emulatore: il server TFTP interno di QEMU, e server FTP, rshd e rlogind minimi scritti per la prova. Sulla tua macchina ftp, rsh, rlogin risponderanno "Connection refused" finche' non ci gira un server: e' la risposta vera, non un difetto.

tftp: un file dalla macchina ospite
v0.2.5.61 - tftp: un file dalla macchina ospite
ftp e http
v0.2.5.61 - ftp e http
trpt: la traccia di una connessione TCP
v0.2.5.61 - trpt: la traccia di una connessione TCP
DEVv0.2.5.62 RPC di Sun e NIS: la macchina diventa anche server Set 2026
rete> rpcinfo -p
   program vers proto   port
    100000    2   udp    111  portmapper
    100001    3   udp   1002  rstatd
    100012    1   udp   1003  sprayd
    100026    1   udp   1004  bootparam
    100004    2   udp   1005  ypserv
    100007    2   udp   1006  ypbind
rete> ypcat hosts
  127.0.0.1 localhost
  10.0.2.15 natalizioos
  10.0.2.2 gateway
  10.0.2.3 dns
rete> ypmatch gateway dns hosts
  10.0.2.2 gateway
  10.0.2.3 dns
rete> yppoll hosts
  Map hosts.byname has order number 1790435514.
  The master server is natalizioos.
rete> bpwhoami
  host_name: natalizioos
  domain_name: natalizioos
  router: 10.0.2.2

Sun RPC (rete_rpc.c)

Mezza rete Unix degli anni '80 e '90 - NFS, NIS, rstat, spray, bootparam - parla RPC di Sun (RFC 1057) con argomenti in XDR (RFC 1014). Il client ritrasmette ogni secondo come clntudp_call, riconosce la "porta irraggiungibile" e la riporta come clnt_perror. Le strutture seguono campo per campo le definizioni dell'archivio, Librpcsvc-1/rstat.x, spray.x, bootparam_prot.x, yp.x.

NATALIZIOOS e' anche server: portmapper (con SET, UNSET, GETPORT, DUMP e CALLIT, che inoltra le chiamate in broadcast), rstatd, sprayd, bootparamd, ypserv, ypbind. Rispondono a questa macchina e a chiunque sulla rete.

lo0: la macchina parla con se' stessa

Un pacchetto per 127.x o per il nostro indirizzo non esce dalla scheda: entra in una coda e rientra dallo smistamento, come if_loop.c nel kernel BSD. ping localhost, rpcinfo -p localhost, ypcat verso il ypserv locale. E, come un sistema vero, a un datagramma per una porta dove nessuno ascolta si risponde "porta irraggiungibile".

NIS, le "pagine gialle"

Dominio natalizioos, servito da questa macchina. Le mappe si costruiscono dai file di testo con la catena del Makefile di /var/yp, fatta dagli strumenti di questo stesso lotto: stdhosts e stdethers normalizzano /etc/hosts e /etc/ethers, revnetgroup inverte /etc/netgroup (seguendo i gruppi annidati), makedbm scrive le mappe. I file in /etc si creano al primo avvio della rete, solo se mancano.

Client: ypwhich (-m, -x), ypcat (-k, soprannomi come hosts = hosts.byname), ypmatch, yppoll, ypset, ypxfr (confronta i numeri d'ordine, -f forza), yppush (legge ypservers).

Un dettaglio trovato nell'archivio: yp.x dichiara la risposta come *stato, chiave, valore*, ma ypserv_xdr.c - e ogni server Sun - mette prima il valore e poi la chiave. FreeBSD lo chiama "STUPID_SUN_BUG". Vale cio' che viaggia sul filo, e cosi' e' fatto qui.

Un difetto trovato provando

ypset andava in "Timed out" dopo un centinaio di chiamate riuscite. Le porte dei client RPC salivano fino alla 1020 e arrivavano alla 1002 di rstatd: la risposta finiva al servizio invece che al client. Ora i client usano 700-899, lontano dai servizi.

Anche

rstat (ora, tempo acceso e carico nel formato dell'originale), spray (raffica senza risposta, poi il conteggio del server), rpcinfo -u e -b (broadcast via CALLIT), bpwhoami, nfsstat (contatori RPC veri; NFS a zero, perche' nulla e' montato via NFS).

I 48

31 su 48 completi. Restano: newclient, rdist, ruptime, rwho, slattach, sliplogin, startslip, talk, trsp, tn3270 e i 7 di AppleTalk.

NIS e RPC: ypxfr, ypwhich, spray, rpcinfo
v0.2.5.62 - NIS e RPC: ypxfr, ypwhich, spray, rpcinfo
nfsstat e rpcinfo
v0.2.5.62 - nfsstat e rpcinfo
DEVv0.2.5.63 talk, rwho, ruptime, rdist, newclient; TCP anche da server Set 2026
rete> talk mario@gateway
  [Checking for invitation on caller's machine]
  [Connection established]
  ciao root, sono mario
talk> ciao mario, qui NATALIZIOOS
  mario ha letto: ciao mario, qui NATALIZIOOS
rete> ruptime
  natalizioos   up     0:01,     1 user,   load 0.00, 0.00, 0.00
rete> rwho
  root     natalizioos:console  Sep 26 15:26
rete> rdist -c benvenuto.txt gateway:/tmp/copia.txt
  updating host gateway
  installing: benvenuto.txt
rete> newclient pippo pluto
  Installing /private/tftpboot/mach
  Creating /clients/pippo...[OK]
  Customizing /clients/pippo/etc/fstab...[OK]

talk e talkd (talk.tproj, talkd.tproj, protocols/talkd.h)

Il protocollo "ntalk" di 4.3BSD in due tempi: i demoni si scambiano messaggi di controllo da 84 byte (LOOK_UP, LEAVE_INVITE, ANNOUNCE, DELETE, con gli indirizzi nel vecchio osockaddr), poi chi e' invitato si collega in TCP e ciascuno manda i suoi tre tasti di modifica. Provato in tutti i casi: invito trovato e conversazione, "Your party is not logged on", annuncio e attesa con ritiro dell'invito (DELETE) quando si rinuncia.

NATALIZIOOS ha il suo talkd: un annuncio arriva sul terminale del Desktop 3 con le parole di announce.c - "Message from Talk_Daemon@natalizioos ... talk: respond with: talk root@...".

rwho, ruptime, rwhod (protocols/rwhod.h)

Ogni tre minuti la macchina grida in broadcast sulla porta 513 chi e', da quando e' accesa e chi e' collegato; rwhod raccoglie le grida degli altri in /var/rwho/whod.<macchina>, e rwho e ruptime leggono quei file nel formato dell'originale. Una macchina che tace da 11 minuti e' "down".

rdist (rdist.tproj)

rdist -c file ... host:destinazione lancia rdist -Server sull'altra macchina con rsh e le parla nel protocollo versione 3: V3, t bersaglio, Q domanda, R file, conferme a zero. Il secondo invio dello stesso file risponde "gia' aggiornato", come l'originale che confronta lunghezza e data.

newclient (newclient.tproj)

Lo script csh di NeXT (1988) che prepara un client NetBoot, rifatto: la radice in /clients/<nome>, fstab con SERVER e CLIENT sostituiti, file di swap, e in /private/tftpboot il kernel e il caricatore veri - il kernel 2 letto dal disco dal settore 2048 e lo stage2 dai settori 1-8. Verificato con sum: identici ai file dell'immagine. Il VFS non ha collegamenti simbolici: dove l'originale ne fa uno, si scrive il percorso a cui punterebbe, e lo si dice.

TCP dalla parte del server

Una porta in ascolto accetta un SYN, risponde SYN+ACK (ritrasmesso se serve) e diventa ESTABLISHED col terzo ACK; tcp_accetta() consegna la connessione. Serve a talk, e servira' ai demoni.

La rete lavora sempre

Il Desktop 3 chiama la pila a ogni giro, anche quando nessuno scrive: e' cosi' che la macchina risponde a ping, RPC, talk e rwho degli altri, e che gli annunci arrivano da soli sul terminale.

Un limite messo in chiaro

Il bss del kernel 2 era arrivato a 3,3 MB, e l'heap comincia a 4 MB. Cinque buffer da 64 KB per i trasferimenti sono diventati uno solo (un trasferimento alla volta), e all'avvio il kernel 2 controlla il margine e si ferma con un messaggio se il bss arrivasse all'heap. Oggi: 958 KB di margine.

I 48

36 su 48. Restano: slattach, startslip, sliplogin, trsp, tn3270 e i 7 di AppleTalk.

talk fra due terminali
v0.2.5.63 - talk fra due terminali
newclient: un client senza disco
v0.2.5.63 - newclient: un client senza disco
DEVv0.2.5.64 SLIP: slattach, startslip, sliplogin; sl0 sulla seconda seriale Set 2026
rete> startslip -s ATDT5551234 tty01 natalizio segreto
  Sent login: natalizio
  Sent password: ********
  sl0 in linea: configura con "ifconfig sl0 locale remoto"
rete> ifconfig sl0 10.1.0.1 10.1.0.2
  sl0: flags=8011<UP,POINTOPOINT,MULTICAST> mtu 296
        inet 10.1.0.1 --> 10.1.0.2 netmask 0xffffff00
rete> ping -c 2 10.1.0.2
  64 byte da 10.1.0.2: icmp_seq=0 ttl=64 tempo=42.705 ms
rete> tcpdump -c 4 -i sl0
  O 89.232984 10.1.0.1 > 10.1.0.2: icmp: echo request
  I 89.276828 10.1.0.2 > 10.1.0.1: icmp: echo reply
rete> sliplogin Snatalizioos
  starting slip login for Snatalizioos
  /etc/slip.login: sl0 10.1.0.1 --> 10.1.0.2 netmask 0xffffff00

SLIP (RFC 1055) e l'interfaccia sl0 (ported/rete_slip.c)

IP su una linea seriale, come ci si collegava con un modem: i pacchetti IP cosi' come sono, separati dal byte END (0xC0), con ESC (0xDB) davanti ai byte che somigliano ai separatori. La linea e' la seconda porta seriale (COM2, 0x2F8, tty01 per BSD); la prima resta la console di diagnostica. Dal kernel dell'archivio (if_sl.c): mtu 296 (SLMTU), pacchetti in arrivo fino a 1900 byte (SLMAX), e link1 che non lascia passare ICMP sulla linea (SC_NOICMP).

  • slattach tty01 [baud]: la linea in modo SLIP, velocita' della tabella dell'originale; tty00 e' rifiutata (e' la console).
  • startslip: le opzioni dell'originale (-d -b -s -p -A -F), la stringa di chiamata al modem, poi il dialogo col terminal server letto con la stessa getline(): una riga finisce a capo o ai due punti, e si confronta dal secondo carattere (ogin:, assword:, con -A il prompt dell'Annex e sername:). Con -d si vede tutto il dialogo, come nell'originale; la password resta coperta.
  • sliplogin [nome]: cerca il nome in /etc/slip.hosts e esegue lo script /etc/slip.login con i parametri dell'originale (unita', velocita', nome, indirizzi, maschera, opzioni): la riga /sbin/ifconfig sl$1 inet $4 $5 netmask $6 configura davvero sl0. I due file, se mancano, nascono da quelli della distribuzione (slip.hosts con una voce per questa macchina). Nel registro di sistema: "attaching slip unit 0 for ...", "SLIP access denied for ...".
  • ifconfig sl0 [inet] locale remoto [netmask m] [up|down] [link1|-link1]; la maschera anche esadecimale (0xffffff00), come in slip.hosts. ifconfig senza argomenti mostra anche sl0.
  • netstat -i ha la riga di sl0, route print la rotta /32 verso l'altro capo, tcpdump -i sl0 le sole trame della linea, con I e O davanti come sl_if_print() dell'originale (-i eth0, -i lo0 anche).

La pila sceglie l'uscita dalla tabella delle rotte: verso l'altro capo di sl0 il pacchetto esce dalla linea con l'indirizzo di sl0 come mittente (come in_pcbconnect()), e TCP taglia i segmenti a 256 byte di dati (mtu 296 - 40). I pacchetti che arrivano dalla linea entrano nello smistamento di sempre: ping, UDP, TCP, servizi, tcpdump.

Provato con un altro capo vero su COM2 (tools/slip_peer.py: modem Hayes, terminal server, poi SLIP): ping 3/3 e i ping dell'altro capo verso di noi 3/3 con dentro 0xC0 e 0xDB (le sostituzioni nei due sensi), traceroute a un salto, nc 10.1.0.2 13 (TCP daytime), rcp /etc/slip.login 10.1.0.2:/tmp/sl spezzato in segmenti da 256 + 70 byte e sum uguale ai due capi (11047 1). Nel Desktop 3 una riga nuova prova lo stesso codice senza la linea: SLIP (RFC 1055): 9/9.

make run-slip collega la COM2 a tools/slip_peer.py 5555.

Differenze dichiarate: niente compressione delle intestazioni TCP (Van Jacobson, link0/link2: i pacchetti compressi in arrivo si scartano); niente frammentazione IP; startslip fa un tentativo solo, con 10 secondi per ogni domanda del server, invece di sei tentativi con attese che raddoppiano (un'ora): qui e' un comando del terminale, non un demone.

TCP: due correzioni trovate provando SLIP

  • Segmenti da 512 byte rifiutati: la pila spedisce segmenti fino a TCP_MSS (512), ma tcp_invia() rifiutava tutto oltre 500 byte. Ogni invio piu' lungo di 500 byte si fermava al primo segmento: ftp put, rcp e rdist verso un server funzionavano solo con file piccoli (quelli provati finora erano da 77 byte). Ora il limite e' 546: put dati.bin da 10000 byte arriva identico (cmp sull'ospite).
  • RST dopo ogni chiusura: tcp_libera() cancellava la connessione subito dopo il nostro FIN, e l'ACK finale dell'altro capo trovava una connessione sparita: partiva un RST. Ora una connessione chiusa dall'applicazione resta "orfana" finche' la chiusura non finisce, come il PCB di BSD dopo close(). In tcpdump la chiusura e' pulita: FIN, FIN, ACK.
  • Messaggi: nc/telnet dicevano "nessuna connessione libera" anche quando il SYN non poteva partire; ora "connect: No route to host". ping distingue "sendto: No route to host (sl0 senza linea)".

Compilazione

  • Makefile.k2 scrive il kernel 2 nell'immagine sempre, non solo quando lo ricollega: un'immagine appena rifatta da make con il kernel 2 gia' compilato restava senza kernel 2 (settori vuoti al tasto 3).
  • Se la versione cambia, il kernel 2 si ricompila tutto (un file di timbro con la versione).
  • Kernel 2: 530 settori su 952 (il backup comincia al 3000).

Comandi di rete completi: 39 su 48. Restano trsp, tn3270 e i sette di AppleTalk.

SLIP: startslip chiama, nc sulla linea
v0.2.5.64 - SLIP: startslip chiama, nc sulla linea
sl0 configurata, ping sulla seriale
v0.2.5.64 - sl0 configurata, ping sulla seriale
DEVv0.2.5.65 tn3270: un terminale IBM 3278 per parlare con un mainframe Set 2026
rete> tn3270 10.0.2.2 3270
  [3270: IBM-3278-2 negoziato - BINARY ed EOR nei due sensi]
  +--------------------------------------------------------------------------------+
  | NATALIZIO MVS/ESA  -  SISTEMA DI PROVA PER TN3270                              |
  |  USERID   ===> ________                                                        |
  |  PASSWORD ===>                                                                 |
  | IKJ56700A ENTER USERID -                                                       |
  +--------------------------------------------------------------------------------+
   4B         cursore 10,17  | = TAB  :pf1-24 :pa1-3 :clear  ESC esce
tn3270> ibmuser|sys1
  | IKJ56455I IBMUSER LOGON IN PROGRESS AT 18:27:57 ON SEPTEMBER 26, 2026          |
  | READY                                                                          |
tn3270> listcat
  | IN CATALOG:CATALOG.MASTER                                                      |
  | IBMUSER.NATALIZ.DATI                                                           |

tn3270 (ported/rete_tn3270.c, da network_cmds/tn3270)

Un mainframe IBM non manda righe di testo ma schermate intere, con campi protetti e campi da riempire; il terminale le disegna, si scrive nei campi e si preme un tasto di attenzione, e all'host torna solo cio' che e' cambiato. Dall'archivio (Berkeley, 1988-93), portati con la loro logica:

  • ctlr/hostctlr.h e ctlr/screen.h: ordini, attributi, comandi, codici AID, le macro dello schermo (campi, indirizzi a 12 bit con la tabella CIA, il bit MDT dei campi modificati);
  • ctlr/outbound.c: DataFromNetwork(), il flusso dati dall'host - Write, Erase/Write, Erase All Unprotected, Read Modified, Read Buffer; gli ordini SF, SBA, IC, PT, RA, EUA, GE;
  • ctlr/inbound.c: OneCharacter() (con il salto automatico dei campi "autoskip" e il blocco dei campi numerici), Tab(), DoReadModified() con SendField(), DoReadBuffer();
  • api/ebc_disp.c, api/asc_ebc.c: le tabelle EBCDIC vere;
  • telnet.tproj/tn3270.c: il tipo di terminale IBM-3278-2, poi BINARY ed EOR nei due sensi; i record finiscono con IAC EOR.

La schermata 24x80 si disegna nel terminale di rete, dentro una cornice, con la riga di stato del 3278 (4B, X SYSTEM quando la tastiera e' bloccata in attesa dell'host, la posizione del cursore). I campi da riempire si vedono come ____, quelli "non display" (la password) restano vuoti. La tastiera, al posto di map3270: la riga scritta va nel campo del cursore, | e' TAB, Invio e' ENTER, :pf1 ... :pf24, :pa1 ... :pa3, :clear, :tab, :home. Se l'host non parla 3270 si resta in telnet semplice, come l'originale.

Provato contro un piccolo mainframe (tools/mainframe_3270.py: logon IBMUSER/SYS1, poi TSO con TIME, LISTCAT, LOGOFF): negoziazione, password sbagliata e giusta, tre comandi, uscita. Il mainframe riceve i record decodificati come li manderebbe un 3278 vero - AID, cursore, SBA e campo per ogni campo modificato. Un dettaglio vero: scrivere nove lettere in un campo di otto fa saltare il cursore al campo dopo (e, all'ultimo campo, di nuovo al primo), come su un 3278.

Nel Desktop 3 una riga nuova prova il controllore senza rete: 3270 (tn3270): 8/8 - Erase/Write, SF, SBA, IC, RA, la tastiera e la risposta Read Modified byte per byte.

Differenze dichiarate: solo il modello 2 (24x80); gli ordini SFE, SA e MF, che l'originale rifiuta, qui si accettano tenendo solo l'attributo base del campo (niente colori); gli indirizzi a 14 bit si capiscono oltre a quelli a 12; niente modo "trasparente" e niente API per i programmi. Corretto un ciclo senza fine dell'originale in Erase All Unprotected (il for con ScreenInc non arrivava mai oltre l'ultima posizione).

Comandi di rete completi: 40 su 48. Restano trsp e i sette di AppleTalk.

tn3270: fine della sessione TSO
v0.2.5.65 - tn3270: fine della sessione TSO
DEVv0.2.5.66 XNS: IDP e SPP della Xerox, e trsp Set 2026
rete> spp -e 02:00:00:00:00:02
  ECHO 0.20000000002H.2H: 42 byte di dati
  74 byte da 0.20000000002H.2H: seq=0 tempo=0.898 ms
rete> spp 02:00:00:00:00:02
  Connected to 0.20000000002H.400H (sid 1, did 431f).
spp> ciao xerox
  CIAO XEROX
rete> trsp
  spp_debx=41
  32abcc:
  890 LISTEN:user    req=CONNECT -> SYN_SENT
  890 SYN_SENT:output
        seq = 0, ack = 0, alo = 4, len = 2a, flags=c0<SP_SP,SP_SA>, sid = 1, did = 0, dt = 0,
        sna=<net:00,host:525400123456,port:bb8>  dna=<net:00,host:020000000002,port:400> -> SYN_SENT
  890 SYN_SENT:input
        seq = 0, ack = 0, alo = 3, len = 2a, flags=c0<SP_SP,SP_SA>, sid = 431f, did = 1, dt = 0, ...

XNS nel kernel 2 (ported/rete_xns.c, da kernel-7/bsd/netns)

Prima di TCP/IP negli uffici c'era XNS, la rete della Xerox (1980): da li' vengono IPX di Novell e l'idea stessa di "socket" numerati. Il kernel dell'archivio ha ancora la pila XNS di 4.4BSD, e trsp e' il suo trpt: stampa la traccia delle connessioni SPP. Per avere una traccia vera serviva la pila vera, e c'e':

  • IDP (ns_input.c): 30 byte, indirizzi rete + host (l'indirizzo della scheda: niente ARP) + socket, trama Ethernet di tipo 0x0600. La somma di controllo XNS di ns_cksum.c - somma e rotazione di un bit a ogni parola, cosi' l'ordine delle parole conta - verificata in entrata e messa in uscita.
  • Echo ed Error (ns_error.c): l'eco sul socket 2 risponde anche quando nessuno ascolta ("Yes, this is a wart!" dice il sorgente); un pacchetto per un socket che non c'e' torna indietro come errore NOSOCK con l'inizio del colpevole.
  • SPP (spp_usrreq.c): connessioni affidabili che numerano i pacchetti invece dei byte. spp_input() con LISTEN, SYN_RECEIVED, SYN_SENT; spp_reass() con le conferme, la finestra ("alo") e le conferme doppie; spp_output() con la coda di spedizione, la finestra di congestione e i pacchetti di sistema; i timer lenti (ritrasmissione, persistenza, keep-alive) con il loro raddoppio; il controllo di spp_hardnosed.
  • spp_trace() (spp_debug.c): il registro circolare di 100 voci, con l'indirizzo vero del blocco di controllo, come nel kernel.

trsp (trsp.c) legge quel registro e lo stampa come l'originale: -a gli indirizzi, -s lo stato di sequenza, -t i timer, -j solo gli indirizzi dei blocchi, -p un blocco solo, -z azzera.

Per avere connessioni da tracciare: spp host [socket], una sessione come nc (riga spedita, risposta stampata, ESC chiude con la convenzione Xerox end/end reply, tipi 0xfe/0xff), e spp -e host, l'eco XNS. La macchina ha un servizio di eco SPP sul socket 1024. spp non e' nella lista dei 48: e' l'aggiunta che serve a trsp. Gli indirizzi si scrivono come ns_ntoa() dell'archivio (0.20000000002H.400H) e si leggono anche nella forma della scheda (02:00:00:00:00:02).

Provato con un'altra macchina sullo stesso cavo. QEMU mette la scheda su un hub con due porte: la rete di sempre e un socket UDP che porta le trame Ethernet grezze a tools/xns_peer.py, una macchina XNS scritta a parte. Eco 3/3 (la somma verificata dall'altra parte), connessione SPP in tre tempi, dati avanti e indietro, chiusura end/end reply, "Connection refused" su un socket vuoto, e la ritrasmissione: con --perdi l'altra macchina butta il primo pacchetto di dati e SPP lo ripete un secondo dopo. Anche in locale (spp natalizioos, attraverso la coda di anello XNS) contro il nostro servizio di eco. make run-xns prepara il cavo in piu'.

tcpdump mostra le trame XNS come l'originale, che per IDP non ha un decodificatore: indirizzi, "ns", lunghezza, parole esadecimali.

Nel Desktop 3 una riga nuova: XNS (IDP/SPP): 6/6 - la somma a rotazione (un bit cambiato si vede, due parole scambiate anche) e ns_ntoa() con il ritorno.

Differenze dichiarate: niente inoltro (non siamo un router XNS) e niente RIP; i pacchetti SPP fuori ordine si scartano invece di aspettare in coda (l'altro capo li ripete); la chiusura end/end reply, che BSD lasciava ai programmi, la fa spp.

Altro

  • Il terminale di rete del Desktop 3 ha un buffer d'uscita da 8 KB (era 4): trsp e le schermate di tn3270 ne vogliono di piu'.

Comandi di rete completi: 41 su 48. Restano i sette di AppleTalk.

trsp: la traccia di una connessione SPP
v0.2.5.66 - trsp: la traccia di una connessione SPP
tcpdump con le trame XNS
v0.2.5.66 - tcpdump con le trame XNS
DEVv0.2.5.67 AppleTalk e aurpd: i 48 comandi di rete sono completi Set 2026
rete> appletalk -u -q
  AppleTalk now online: 100.115 (0x0064.73), zona NATALIZIO
rete> atstatus 'LaserWriter Ufficio'
  Default printer is:LaserWriter Ufficio:LaserWriter@NATALIZIO
  status: idle
rete> atprint < /etc/hosts
  atprint: printing on LaserWriter Ufficio:LaserWriter@NATALIZIO.
rete> aurpd
  aurpd: capo locale 10.0.2.15, porta UDP 387
    esportate: 100-101 (NATALIZIO, Ufficio, Laboratorio)
    tunnel 1 con 10.0.2.2: aperto; di la': 200 (Remoto), 300-301 (Remoto, Magazzino)
rete> appletalk -t
  start-stop    net:node   d  p  PBTZ GSBU zones
    100-101       0:0      0  0  1001 1000  1,2,3
      0-200       0:1      1  1  0001 1000  4
    300-301       0:1      2  1  0001 1000  4,5
rete> appleping c8.5 599 2
  599 bytes from 00c8.05.04: hop= 1, time=   0. ms
rete> atlookup "=:=@Remoto"
  Found 2 entries in zone Remoto
  00c8.05.fa	Mac Remoto:Macintosh
  012d.09.fb	Server Remoto:AFPServer

AppleTalk nel kernel 2 (ported/rete_atalk.c, ported/rete_atcmd.c)

La rete dei Macintosh (1985): niente configurazione, i nodi si scelgono l'indirizzo da soli, le stampanti si trovano per nome. Dal kernel AppleTalk di Rhapsody (kernel-7/bsd/netat) e dalla sua libreria (LibcAT-1), con i loro formati sul filo:

  • EtherTalk fase 2: trame 802.3 con l'intestazione SNAP (AA AA 03, 08-00-07 80-9B per DDP, 00-00-00 80-F3 per AARP), il broadcast 09:00:07:FF:FF:FF e il multicast di ogni zona.
  • AARP (ddp_aarp.c): l'indirizzo si sceglie a caso e si sonda 20 volte ogni 1/30 di secondo; se qualcuno risponde e' occupato e se ne prova un altro. Prima nell'intervallo di avvio (0xFF00-0xFFFE), poi, saputo il cavo dal router, di nuovo nel cavo vero.
  • DDP (ddp.c): l'intestazione lunga di 13 byte, la somma a rotazione verificata in entrata, i socket.
  • RTMP, ZIP, NBP, AEP (ddp_rtmp.c, ddp_r_zip.c, ddp_nbp.c, ddp_aep.c): il router del cavo, GetNetInfo per la zona, le ricerche per nome (BrRq al router o LkUp in broadcast, i jolly = e ≈), il nome di questa macchina (natalizioos:NATALIZIOOS), l'eco.
  • ATP e PAP (atp_*.c, pap_*.c): transazioni con fino a 8 risposte, e sopra il protocollo delle LaserWriter: OpenConn, SendData, dati da 512 byte, EOF, CloseConn, SendStatus.

I sei comandi, dai sorgenti di Commands/at_cmds:

ComandoCosa fa
appletalk-u accende la pila, -d la spegne, -n -s -p -a -z come l'originale; -t la tabella delle rotte con aurpd acceso
applepingl'eco AEP verso net.node (in esadecimale, come l'originale) o verso un nome NBP, con tempi e salti
atlookuple ricerche NBP, i jolly, -z le zone (ZIP GetZoneList via ATP)
at_cho_prnsceglie la stampante predefinita: zone, stampanti della zona, scelta in /etc/atalk/atprint.dat
atstatuslo stato di una stampante (PAP SendStatus)
atprintstampa cio' che arriva con < file (PAP, pacchetti da 512 byte)

aurpd: AppleTalk attraverso Internet (ported/rete_aurp.c)

AURP (RFC 1504, 1993) unisce due reti AppleTalk lontane: i router ai due capi di un tunnel UDP (porta 387) si passano i pacchetti DDP chiusi in un'intestazione di dominio di 22 byte, e si dicono quali reti e quali zone stanno dalla loro parte - una volta sola, poi soltanto i cambiamenti. Portato da aurpd.c (il comando) e dal protocollo del kernel (aurp_open.c, aurp_ri.c, aurp_zi.c, aurp_tickle.c, aurp_rd.c, aurp_misc.c, aurp_tx.c, aurp_rx.c):

  • /etc/aurp_tunnel.cfg letto come l'originale: port, local, remote (piu' d'uno), hide, export, anche abbreviate (strpfx(): basta r per remote); gli errori con il loro testo ("unknown keyword", "Hiding/Export conflict", "no remote' keyword"). La prima volta il file d'esempio di aurp_tunnel.cfg(5) con gli indirizzi di questa rete. Opzioni -d (ferma, con Router-Down), -q, -l log`.
  • Due connessioni per tunnel, una per senso: Open-Req/Open-Rsp, RI-Req/RI-Rsp/RI-Ack con i numeri di sequenza, RI-Upd per i cambiamenti (rete aggiunta, tolta, distanza cambiata), ZI-Req/ZI-Rsp con le tuple di zona nel formato lungo e in quello ottimizzato, Tickle ogni 30 secondi di silenzio, Router-Down alla chiusura. I tentativi e i tempi sono quelli dell'originale (Open-Req 10 volte ogni 3 s, il capo di la' dato per perso dopo 4 tickle senza risposta).
  • Il router: l'originale vuole AppleTalk in modo router, che qui non c'e'. Questo nodo fa da router soltanto fra il cavo e il tunnel: impara dal router del cavo (RTMP e ZIP Query) le reti da esportare e le loro zone, annuncia sul cavo quelle di la' (RTMP ogni 10 secondi, e a distanza 31 quelle appena tolte), inoltra i pacchetti nei due sensi con un salto in piu', aspetta la risposta AARP prima di consegnare sul cavo, trasforma le FwdReq in LkUp al multicast della zona e le BrRq dei nodi del cavo in FwdReq verso le reti di la'.
  • appletalk -t e appletalk -z mostrano la tabella delle rotte e delle zone come showRoutes() e showZones() dell'originale; le zone di la' compaiono anche in atlookup -z e in at_cho_prn.

Dove l'originale ha un errore evidente c'e' la correzione, dichiarata nel sorgente: il controllo di local che non scattava mai (guardava una variabile che nessuno impostava), le reti nuove a distanza 0 in un RI-Upd scartate, l'evento nullo letto come evento.

Provato con tre macchine intorno. tools/at_peer.py e' la rete AppleTalk sul cavo in piu' (router 100.1, zone NATALIZIO, Ufficio e Laboratorio, una LaserWriter, due Mac) e, come un router vero, impara le rotte dagli RTMP degli altri; tools/aurp_peer.py e' il capo del tunnel di la' (reti 200 e 300-301, un Mac, un server, una LaserWriter). Dal nostro lato: tunnel aperto, rotte e zone nei due sensi, eco anche con pacchetti interi da 599 byte, ricerca NBP in una zona di la', stato della stampante di la', la rete 102 comparsa sul cavo mandata di la' con un RI-Upd e la sua zona, il Tickle dopo 60 secondi di silenzio, aurpd -d e appletalk -d con il Router-Down. Dal lato di la' (--prove, 18 prove su 18): la ricerca dei nomi del nostro cavo, l'eco verso di noi e verso la LaserWriter del cavo (inoltrata da noi), la rete 400 aggiunta e poi tolta. E un Mac del cavo che ci usa come router per cercare i nomi di la' (--brrq): le risposte gli arrivano con due salti. make run-at prepara tutto.

tcpdump e il terminale

  • tcpdump stampa AppleTalk come print-atalk.c (RTMP, ZIP, NBP, ATP, AEP, AARP) e ha i filtri atalk, aarp e ns.
  • Nel terminale di rete le virgolette "..." e '...' tengono insieme gli argomenti con gli spazi (i nomi NBP ne sono pieni), e < file manda un file all'ingresso del comando (atprint < /etc/hosts).
  • Nel Desktop 3 due righe nuove: AppleTalk (DDP/NBP): 7/7 e AURP (aurpd): 6/6 (le tuple RI e ZI, il formato ottimizzato, un RI-Upd).

Correzioni

  • make ricompila il kernel 2 anche quando cambiano solo i suoi file (il bersaglio kernel2): prima si poteva provare un binario vecchio.
  • UDP: i dati di un datagramma erano al massimo 400 byte, e tftp put di un file oltre i 396 byte non partiva (i blocchi sono di 516). Ora il limite e' quello dell'interfaccia: 1472 byte su eth0, 268 su sl0. Provato: 650 byte in due blocchi, da 516 e 142.
  • tftp get, ftp get e rcp con un percorso (/etc/file) mettevano il file nella radice con quel nome intero, e nessuno lo ritrovava: ora va nella sua cartella.
  • Il server UDP di un servizio si puo' spegnere (udp_servizio_togli): dopo aurpd -d la porta 387 risponde di nuovo "porta irraggiungibile".
  • atprint senza una stampante scelta diceva atstatus: No default printer selected...: ora, come get_chooserPrinter() di chooser.c, il messaggio porta il nome di chi chiama.

Comandi di rete completi: 48 su 48. Il kernel 2 e' a 698 settori su 952, con 674 KB fra la fine dei suoi dati e l'heap. Il kernel 1 non e' toccato e parte come prima.

AppleTalk sul cavo: atlookup, atprint
v0.2.5.67 - AppleTalk sul cavo: atlookup, atprint
aurpd apre il tunnel; appletalk -t, le rotte
v0.2.5.67 - aurpd apre il tunnel; appletalk -t, le rotte
Attraverso il tunnel: eco, nomi, stampante di la'
v0.2.5.67 - Attraverso il tunnel: eco, nomi, stampante di la'
DEVv0.2.5.68 Il kernel 2 dal menu, la shell testuale riparte, bastano 16 MB Set 2026
NATALIZIOOS v0.2.5.68 - Stage1
LBA ok, loading Stage2...
NATALIZIOOS v0.2.5.68 - Stage2 active (real mode)
...                                  (niente piu' "[1] completo [3] Desktop 3")
login root / root  ->  menu  ->  5  DESKTOP 3
  Avvio del kernel 2: Desktop 3 con la rete...
  [k2] avviato dal menu del kernel 1
  [k2] utente: root
rete> ifconfig
  eth0: flags=863<UP,BROADCAST,RUNNING,SIMPLEX> mtu 1500
        inet 10.0.2.15 netmask 0xffffff00 broadcast 10.0.2.255
ESC  ->  [k2] ESC: ritorno al menu del kernel 1  ->  login  ->  menu

Il kernel 2 si avvia dal menu, non all'accensione

Fino alla 0.2.5.67 il kernel 2 si sceglieva al prompt di stage2, all'accensione ("[1] completo [3] Desktop 3 + rete"). Ora si parte sempre dal kernel 1, e il kernel 2 si apre dal menu, alla voce DESKTOP 3, insieme a Desktop 1, Desktop 2 e alle due shell.

  • Il prompt in stage2 non si assembla piu' (boot/stage2.asm): il codice resta, dentro %ifdef SCELTA_KERNEL_ALL_AVVIO (con nasm -DSCELTA_KERNEL_ALL_AVVIO torna). Senza, stage2 carica il kernel 1 senza aspettare.
  • Un settore che descrive ogni kernel, scritto dai Makefile accanto al kernel: dove sta, quanti byte, il CRC (lo stesso numero del comando cksum) e la versione (kernel/descrittore_kernel.asm). Settore 2046 per il kernel 1, 2047 per il kernel 2.
  • Il salto (kernel/salto_kernel.c, kernel/trampolino.asm): il kernel 1 legge il kernel 2 dal disco in memoria bassa (0x10000, come stage2), controlla il CRC, poi copia un pezzetto di codice a 0x9000 e lo chiama. Quel codice spegne la paginazione, carica una GDT sua e una IDT vuota, copia il kernel 2 a 1 MB e lo chiama esattamente come stage2 (push mappa E820; call 0x100000): il kernel 2 non si accorge di non essere partito da li'.
  • La casella fra i kernel (0x7B00): chi salta scrive perche' salta e chi era collegato. Il kernel 2 apre il Desktop 3 con lo stesso utente del login del kernel 1 (id -> uid=0(root)). Stage2 la azzera a ogni accensione, cosi' un riavvio non trova messaggi vecchi.
  • ESC nel Desktop 3 torna al menu: il kernel 2 ferma la scheda di rete (e1000_ferma(): la e1000 scrive da sola in memoria i pacchetti in arrivo, e quella memoria sta per diventare del kernel 1), rilegge il kernel 1, controlla il CRC e salta. Il kernel 1 capisce dalla casella che si torna dal Desktop 3 e va dritto al login e al menu, senza scelta testo/grafica e senza animazione.
  • Se qualcosa manca: kernel 2 assente o rovinato -> un messaggio ("CRC sbagliato: il kernel sul disco e' rovinato") e si apre il Desktop 3 interno del kernel 1, come prima. Kernel 1 non trovato al ritorno -> la macchina si riavvia (e all'accensione parte il kernel 1).
  • I tasti del menu: le card sono sei e il numero sulla card e' il tasto. Prima il 5 apriva l'AGGIORNAMENTO (la card 6) e il Desktop 3 si raggiungeva solo con le frecce; ora 5 = Desktop 3, 6 = Aggiornamento, e in fondo "1-6 to select".
  • Compilati insieme: make fa il kernel 1, il kernel 2 e i due descrittori, e li scrive nella stessa immagine. Makefile.k2 legge la versione con sed invece di grep -oP (le espressioni Perl non ci sono dappertutto).

La shell testuale: lo stack stava dentro le tabelle di pagina

Scegliendo la shell testuale il sistema si fermava con un errore di pagina, EIP=0x1C7E7003. La causa:

  • stage2 lascia lo stack a 0x200000 (2 MB);
  • il kernel 1 e' caricato a 1 MB e la sua bss comincia poco sopra 1,5 MB: a 2 MB c'erano gia' le sue variabili, e precisamente l'array initial_page_tables di paging.c (da 0x18E000 a 0x28E000);
  • la shell testuale chiama paging_init(), che riempie quelle tabelle: la voce della pagina 0x1C7E7000 (valore 0x1C7E7003) finiva sopra un indirizzo di ritorno, e al ret la CPU saltava li'.

E' anche la causa del vecchio errore del kernel 2 con la paginazione (EIP=0x327FFFFF, commento in kernel2/main.c). Ora kernel/entry.asm, dopo aver azzerato la bss, passa a uno stack tutto suo (256 KB nella bss; la funzione piu' pesante, misurata con -fstack-usage, ne usa 8,4). Vale per tutti e tre i binari: kernel 1, kernel 2 e immagine solo testo.

16, 32, 64 MB (e oltre)

Il kernel 1 occupava 30,5 MB di RAM: 0,5 di codice e 30 di bss, e quasi tutta era del Desktop 2 (un'arena da 16 MB, un buffer video da 9 MB per 1920x1200, 1,8 MB di immagini). Con 16 MB non ci stava, e con 32 MB c'era un secondo guaio: l'heap del kernel, fisso a 4 MB, cadeva in mezzo all'arena del Desktop 2.

  • Kernel 1: da 30,5 a 4,2 MB. L'arena e il buffer del Desktop 2 si prendono dall'heap alla prima apertura, PRIMA di riaprire le finestre della sessione (desktop2_core/memory.c, framebuffer.c, img.c): il buffer per 1024x768, il modo piu' grande che il kernel sa impostare (3 MB invece di 9); l'arena da 16 MB come prima quando sta nella meta' dell'heap libero, altrimenti 8 o 4 MB (64, 32, 16 MB di RAM); le immagini solo quando qualcuno le carica davvero. Se l'heap non ha abbastanza, il Desktop 2 lo dice e torna al menu; se l'arena si esaurisce durante la sessione, la finestra nuova non si apre (prima un'app sarebbe partita scrivendo dall'indirizzo 0).
  • Heap dinamico (kernel/heap.c, kernel/pmm.c): comincia dopo la fine del kernel e prende la RAM libera e contigua, fino a 64 MB, lasciando 512 KB al gestore della memoria fisica; e' segnato come occupato, quindi nessuno ci finisce sopra. Con 16 MB installati: 11,2 MB di heap nel kernel 1, 11,8 nel kernel 2; con 64 MB, 59 MB. Richiamare heap_init (lo fa bootanim-text, che rifa' i passi dell'avvio) non azzera piu' un heap in uso.
  • natmemory non scrive piu' a 8-16 MB: quella zona "sicura" ora e' heap, e i motivi di prova avrebbero distrutto i blocchi in uso. La prova si fa su un blocco chiesto all'heap (fino a 8 MB, al massimo meta' del libero), restituito alla fine.
  • Il doppio buffer video non sta piu' fisso a 16 MB (drivers/fb.c): con 16 MB di RAM li' non c'era memoria (Desktop 1 e Desktop 3 disegnavano nel vuoto), e con l'heap dinamico sarebbe caduto in mezzo all'heap. Si prende dall'heap la prima volta che serve. Il Desktop 2, che non lo usa, non lo fa piu' allocare (vgpu_sys_enable_diretto), e dopo il Desktop 2 il Desktop 1 e il Desktop 3 ritrovano il doppio buffer: prima ripartivano senza, con lo sfarfallio.
  • make run-gui RAM=16 (o 32, 128...): la RAM dell'emulatore si sceglie da riga di comando. Predefinito 64, come prima.

Tastiera e mouse dopo il riavvio

"Solo la prima volta funzionano tastiera e mouse": dipendeva da quando si alzava il dito. mouse_init() chiedeva al controller PS/2 il suo byte di configurazione e leggeva il primo byte che trovava. Se nel controller c'era gia' un byte della tastiera - il rilascio del tasto premuto all'avvio, arrivato dopo il cli del bootloader - leggeva quello: il rilascio di "1" e' 0x82, quello di "3" e' 0x84, e riscritti come configurazione spengono l'interruzione della tastiera (bit 0) e la traduzione dei codici (bit 6).

Ora (drivers/mouse.c): porte spente, controller svuotato, poi la configurazione, con i bit giusti forzati invece che ricopiati; le risposte del mouse si accettano solo se vengono dal mouse (bit 5 dello stato); un mouse che non risponde ha la porta spenta, cosi' non tiene occupato il controller; il gestore del mouse legge sempre il byte, anche col mouse disabilitato. I gestori di tastiera e mouse ignorano le interruzioni senza un byte pronto (le lascia in attesa l'avvio a interruzioni spente: rileggere 0x60 dava un tasto o un clic fantasma). Se il controller non risponde ai comandi nuovi, la vecchia sequenza riaccende comunque la tastiera. Provato con tasti tenuti premuti fino a 2 secondi durante l'avvio e durante i salti fra i kernel, con i riavvii di QEMU e con tre accensioni di fila sulla stessa immagine.

In piu', la rotella: il mouse si accende in modo IntelliMouse (frequenze 200, 100, 80, poi "chi sei?": un mouse con la rotella risponde 3 e manda pacchetti da 4 byte; se la prova si interrompe a meta', un reset lo riporta a mouse normale). Nel Desktop 3 la rotella scorre la finestra dei comandi (3 righe per scatto); nel Desktop 2 arriva al core, che la usava gia' per testi, Writer, Sheets e Spotlight ma dal kernel riceveva sempre 0.

Le versioni

  • Stage1 e stage2 scrivono NATALIZIOOS v0.2.5.68 (prima stage1 diceva ancora v0.2.0.1.1.1): la versione arriva dal Makefile con nasm -DNAT_VERSIONE.
  • Il kernel mostrava 0.2.5.48-DEV (banner, login, watermark, uname), scritto a mano in natalizioos.h e rimasto fermo per venti versioni. Ora arriva dal Makefile (-DNAT_VERSIONE_MAKEFILE), in entrambi i kernel. La versione e' anche nella schermata di scelta testo/grafica.
  • Un "timbro" della versione nel Makefile: quando cambia si ricompila tutto, altrimenti i vecchi oggetti restavano col numero vecchio.

Anche

  • Il trampolino spegne gli NMI finche' il kernel nuovo non ha la sua IDT; il descrittore si controlla prima della moltiplicazione settori * 512, che con un numero assurdo traboccava; il Makefile avvisa se il kernel 1 supera 1150 settori (oltre, il ritorno dal Desktop 3 riavvia la macchina). Trovati da una rilettura indipendente del codice, insieme a natmemory, all'arena esaurita e al doppio buffer sprecato qui sopra.
  • Lo stage2 dell'immagine del firmware (make firmware-img, KERNEL_LBA = 15000) dalla 0.2.5.48 caricava il kernel dal settore 9, scritto a mano; ora usa KERNEL_LBA.
  • Le istruzioni che dicevano "tasto 3 all'avvio" (README, Makefile, tools/) dicono DESKTOP 3 nel menu, tasto 5.

Le prove

16 MB32 MB64 MB128 MB512 MB
Shell testuale (scelta 1 all'avvio), login, memOKOK, anche dopo riavvioOK
Menu: SHELL TESTUALEOKOKOK, tre accensioni
Menu: SHELL GRAFICAOKOKOK
Menu: Desktop 1OKOKOK
Menu: Desktop 2OKOKOKOKOK
Menu: Desktop 3 -> kernel 2, rete, ESC -> menuOKOKOK, tre giri di filaOKOK
Immagine solo testoOK
natmemory test (su un blocco dell'heap), poi memOK, 5,6 MBOK, 8 MB

Nel kernel 2 aperto dal menu passano tutte le prove automatiche della rete (socket 15/15, SLIP 9/9, 3270 8/8, XNS 6/6, AppleTalk 7/7, AURP 6/6, libc 12/12...) anche con 16 MB; ping, arp, ifconfig, netstat a mano. Con il kernel 2 assente o con un byte cambiato compaiono i messaggi giusti e si apre il Desktop 3 interno; senza il descrittore del kernel 1 il ritorno riavvia la macchina.

Stage1 e stage2 con la versione del Makefile, e niente prompt [1]/[3]
v0.2.5.68 - Stage1 e stage2 con la versione del Makefile, e niente prompt [1]/[3]
La scelta testo/grafica, con la versione
v0.2.5.68 - La scelta testo/grafica, con la versione
Il menu: 5 = DESKTOP 3, che avvia il kernel 2
v0.2.5.68 - Il menu: 5 = DESKTOP 3, che avvia il kernel 2
Il kernel 2 aperto dal menu con 16 MB di RAM: host_info 15 MB, la rete va
v0.2.5.68 - Il kernel 2 aperto dal menu con 16 MB di RAM: host_info 15 MB, la rete va
La shell testuale riparte, con 16 MB: heap di 11 MB dopo il kernel
v0.2.5.68 - La shell testuale riparte, con 16 MB: heap di 11 MB dopo il kernel
Il Desktop 2 con 16 MB: i suoi buffer ora vengono dall'heap
v0.2.5.68 - Il Desktop 2 con 16 MB: i suoi buffer ora vengono dall'heap
Kernel 2 rovinato sul disco: il CRC non torna, si apre il Desktop 3 interno
v0.2.5.68 - Kernel 2 rovinato sul disco: il CRC non torna, si apre il Desktop 3 interno
DEVv0.2.5.69_WATERMAKER Il watermark come quello di Call of Duty Set 2026
Call of Duty Multiplayer - Release COD_T6_R MP build 1.0.4 CL(1802446) CODPCAB2 Sun Jul 13 11:42:18 2014 win-x86 Ice9be6c
NATALIZIOOS Server - Dev NAT_V2_D K1 build 0.2.5.69_WATERMAKER CL(170) VM Sun Sep 27 20:37:20 2026 bare-x86 01df0e4b

La prima riga e' quella in fondo allo schermo di Call of Duty: Black Ops II. La seconda e' il watermark di NATALIZIOOS da questa versione: una riga sola, centrata in basso, azzurro acciaio, con i dati veri della build. Fino alla 0.2.5.68 c'era un pannello di tre righe grigie in basso a destra (NATALIZIOOS_SERVER_EDITION_DEV / For testing purposes only / Build v...).

Il nome della build

La versione e' 0.2.5.69 e la build si chiama _WATERMAKER. Nel Makefile, accanto a NATALIZIOOS_VERSION, c'e' una riga nuova:

NATALIZIOOS_VERSION  := 0.2.5.69
NATALIZIOOS_SUFFISSO := _WATERMAKER

Il nome arriva dappertutto dove prima c'era -DEV: stage1 e stage2, la scelta testo/grafica, il banner delle shell, i descrittori dei due kernel (settori 2046 e 2047) e il watermark. In natalizioos.h -DEV resta il valore predefinito, se si compila senza Makefile. Anche il "timbro" della versione contiene il nome: cambiandolo si ricompila tutto. Per la build dopo basta cambiare quella riga, o lasciarla vuota.

La riga, campo per campo

Call of DutyNATALIZIOOSChe cos'e'
Call of Duty MultiplayerNATALIZIOOS Serveril prodotto
ReleaseDevil tipo di build
COD_T6_RNAT_V2_Dla serie (0.2) e D di dev
MPK1, K2, TXquale eseguibile: kernel 1, kernel 2 (Desktop 3), immagine solo testo
build 1.0.4build 0.2.5.69_WATERMAKERversione e nome della build
CL(1802446)CL(170)il numero della build
CODPCAB2VMil computer che ha compilato
Sun Jul 13 11:42:18 2014Sun Sep 27 20:37:20 2026data e ora della build
win-x86bare-x86la piattaforma: x86 senza un sistema sotto
Ice9be6c01df0e4bl'impronta dei sorgenti

Nel kernel 1 la versione e' quella che l'aggiornamento software tiene sul disco (settore 2000), come prima; su un disco nuovo e' quella di compilazione.

Da dove vengono i dati

  • scripts/costruzione.sh (nuovo) scrive un piccolo file C, costruzione.c, con i cinque dati: kernel, numero, computer, data, impronta. Lo lanciano i Makefile subito prima di collegare un kernel, quindi data e ora sono quelle della build. Prima la data era __DATE__ __TIME__ di gfx_watermark.c, cioe' dell'ultima volta che si era ricompilato quel file. Usa solo comandi POSIX (date, find, sort, cksum...): gira uguale in MSYS2, in WSL e su Linux.
  • Il numero (CL) sta in costruzione.numero, un file nuovo nella cartella principale (partito da 163). Sale di uno ogni volta che il kernel 1, o l'immagine solo testo, si ricollega. Il kernel 2 prende il numero del kernel 1: make glielo passa, e make -f Makefile.k2 da solo lo legge da build-graphic/costruzione.c. Cosi' la stessa build ha lo stesso numero nei due kernel. Un file cambiato solo nel kernel 2 (tutta la rete) ricollega anche il kernel 1, e tutti e due prendono il numero nuovo; un make senza modifiche non cambia niente. Il numero non torna mai indietro.
  • Il computer e' uname -n in maiuscolo, al massimo 12 caratteri: cosi' la riga sta in 1024 punti (126 caratteri su 128 colonne). In MSYS2 e in WSL e' il nome del PC; si sceglie a mano con make NAT_MACCHINA=NATPCAB1. Le build di questa versione dicono VM, il nome della macchina dove sono state compilate.
  • L'impronta e' il CRC (lo stesso numero di cksum) di tutti i .c, .h e .asm dei due kernel, in ordine fisso: due build dello stesso sorgente hanno la stessa impronta, un carattere cambiato la cambia.

Dove si vede

  • Shell grafica, Desktop 1 (sopra la barra) e Desktop 2 (sopra il dock): una riga centrata, azzurro acciaio (#8EB4E8) con un'ombra scura spostata di un punto, come in Call of Duty. Su uno schermo stretto (640x480) va su due righe, ciascuna centrata.
  • Desktop 3, nei due kernel: fino alla 0.2.5.68 non aveva il watermark. Ora sta sopra la riga di aiuto in fondo; aperto dal menu, nel kernel 2, dice K2.
  • SHELL TESTUALE del menu (la console grafica, 128 colonne): una riga azzurra sulla penultima riga dello schermo.
  • Shell testuale VGA (scelta 1 all'avvio) e immagine solo testo: 80 colonne, la riga non ci sta e va su due righe, azzurre, sotto il banner. Nel kernel grafico la shell VGA non mostrava il watermark (si disegnava solo col framebuffer acceso): ora si'.

Il vecchio pannello resta

Il pannello a tre righe non e' stato tolto: sta in gfx_watermark.c dentro #ifdef NAT_WATERMARK_PANNELLO_VECCHIO, per la grafica e per il testo. Aggiungendo -DNAT_WATERMARK_PANNELLO_VECCHIO alle CFLAGS del Makefile torna quello di prima.

Anche

  • BUILD_STATUS.md ha una sezione nuova, "Numero di build e watermark": numero, computer, data e impronta di ciascun kernel, la riga come appare sullo schermo e il controllo che kernel 1 e kernel 2 abbiano lo stesso numero. Legge i dati da costruzione.c.
  • gfx_watermark_text_width() e' segnata unused: nella build solo testo nessuno la usa e dava un avviso.

Le prove

DoveCosa si vedeEsito
Scelta testo/grafica all'avviov0.2.5.69_WATERMAKEROK
Menu 1, SHELL GRAFICAla riga K1 ... CL(170) in bassoOK
Menu 2, SHELL TESTUALEla riga sulla penultima rigaOK
Menu 3, Desktop 1sopra la barraOK
Menu 4, Desktop 2sopra il dock, in tre fotogrammi di filaOK
Menu 5, Desktop 3 -> kernel 2K2 ... CL(170), ESC -> menuOK, anche con 16 MB
Shell testuale VGA (1 all'avvio)due righe, K1 ... CL(170)OK
Immagine solo testodue righe, TX ... CL(171)OK

E la build: make da pulito da' lo stesso numero ai due kernel; un secondo make non cambia niente; toccando solo un file della rete (ported/rete_udp.c) si ricollegano tutti e due col numero dopo; make -f Makefile.k2 da solo tiene il numero del kernel 1; make stato (grafica, testo, kernel 2) finisce con kernel 1 = kernel 2 = 170 e l'immagine solo testo a 171. Il pannello vecchio compila, in grafica e in testo. Kernel 1: 1070 settori su 1150; kernel 2: 710 su 952.

La riga in ogni ambiente: K1 nel kernel 1, K2 nel Desktop 3 del kernel 2, stesso CL(170)
v0.2.5.69 - La riga in ogni ambiente: K1 nel kernel 1, K2 nel Desktop 3 del kernel 2, stesso CL(170)
Desktop 3 aperto dal menu: il kernel 2 ha il watermark, sopra la riga di aiuto
v0.2.5.69 - Desktop 3 aperto dal menu: il kernel 2 ha il watermark, sopra la riga di aiuto
Desktop 1: la riga centrata sopra la barra
v0.2.5.69 - Desktop 1: la riga centrata sopra la barra
Desktop 2: la riga sopra il dock
v0.2.5.69 - Desktop 2: la riga sopra il dock
Shell grafica
v0.2.5.69 - Shell grafica
SHELL TESTUALE del menu: una riga, sulla penultima riga dello schermo
v0.2.5.69 - SHELL TESTUALE del menu: una riga, sulla penultima riga dello schermo
Shell testuale VGA (1 all'avvio): 80 colonne, la riga va su due
v0.2.5.69 - Shell testuale VGA (1 all'avvio): 80 colonne, la riga va su due
Immagine solo testo: TX, con il suo numero, CL(171)
v0.2.5.69 - Immagine solo testo: TX, con il suo numero, CL(171)
La scelta testo/grafica con il nome della build
v0.2.5.69 - La scelta testo/grafica con il nome della build
Con 16 MB: Desktop 3 nel kernel 2, poi ESC e di nuovo il menu
v0.2.5.69 - Con 16 MB: Desktop 3 nel kernel 2, poi ESC e di nuovo il menu
DEVv0.2.5.70 La risoluzione: 1920x1080, e si sceglie all'avvio Set 2026
                    N A T A L I Z I O O S
                       Server Edition
                        v0.2.5.70-DEV
                Seleziona la modalita' di avvio

       [1]  SHELL TESTUALE      VGA 80x25
       [2]  MODALITA' GRAFICA   < 1920x1080 >
      frecce sinistra/destra: risoluzione (resta salvata)

Fino alla 0.2.5.69 tutto il sistema grafico girava a 1024x768, scritto nel codice (fb_init(FB_MODE_1024x768) e fb_set_mode(FB_MODE_1024x768) all'ingresso di ogni ambiente). La risoluzione nuova e' 1920x1080 (Full HD, 16:9), e non e' l'unica: si sceglie all'avvio con le frecce fra 1024x768, 1280x800, 1600x900 e 1920x1080, e la scelta resta. 1024x768 non se ne va: e' uno dei quattro modi, ed e' quello che il sistema usa da solo con 16 MB di RAM.

Il nome della build torna "-DEV" (la 0.2.5.69 era "_WATERMAKER"): la riga NATALIZIOOS_SUFFISSO del Makefile.

I modi, e dove si sceglie

  • Il driver (drivers/fb.c, fb.h) conosce tre modi nuovi, FB_MODE_1280x800, FB_MODE_1600x900, FB_MODE_1920x1080, oltre a 640x480, 800x600 e 1024x768. Il BGA di QEMU accetta qualunque larghezza multipla di 8 finche' l'immagine sta nei 16 MB di memoria video: 1920x1080x4 = 7,9 MB. Le misure di un modo le dicono fb_modo_larghezza() e fb_modo_altezza(), il modo attuale fb_get_mode().
  • All'avvio, nella schermata "Seleziona la modalita' di avvio", le frecce sinistra e destra cambiano la risoluzione nel riquadro MODALITA' GRAFICA; il conto alla rovescia riparte, per dare il tempo di guardare. Anche INVIO sceglie la modalita' grafica.
  • La scelta sta sul disco, nel settore 2001 (libero, fra la versione al 2000 e i descrittori dei kernel al 2046-2047): la firma NATVIDEO, il modo, un numero di controllo e il nome ("1920x1080", si legge anche a occhio). Vale per i prossimi avvii e anche per il kernel 2. Il modulo nuovo e' kernel/schermo.c.
  • Il comando resolution conosce i modi nuovi (resolution 1920x1080, resolution 1600, resolution fullhd, resolution list) e salva la scelta se e' uno dei quattro; 640x480 e 800x600 restano come prima, solo per la sessione. resolution da solo dice quella attiva, quella salvata e la massima per la RAM.
  • Il kernel 2 imposta lo schermo prima di avere il disco. La risoluzione gliela passa il kernel 1 nella casella a 0x7B00 (un campo nuovo in fondo, modo_video): il Desktop 3 si apre nello stesso modo del menu. Al ritorno il kernel 1 rilegge la scelta dal disco.

Con poca RAM si scende da soli

Nel caso peggiore servono due immagini dello schermo in memoria: il doppio buffer (Desktop 1, Desktop 3, shell) e il buffer del Desktop 2. Piu' circa 10 MB per il resto: il kernel (4,2 MB), la riserva del gestore della memoria fisica, un'arena minima del Desktop 2 e un margine. schermo_modo_massimo() fa il conto sulla RAM che c'e', e la scelta (o il predefinito) si riduce a quello:

RAMRisoluzione massima
16 MB1024x768, come fino alla 0.2.5.69
da circa 19 MB1280x800
da circa 22 MB1600x900
da circa 27 MB1920x1080

Con make run-gui (64 MB) si parte a 1920x1080. Nel selettore d'avvio, con poca RAM, le frecce non vanno oltre il massimo e la riga sotto dice perche': "con 16 MB di RAM al massimo 1024x768".

Gli ambienti a tutto schermo

Tutto era disegnato per 1024x768. Dove le coordinate dipendevano gia' da fb_get_width()/fb_get_height() bastava lo schermo nuovo; dove erano scritte nel codice:

  • L'avvio grafico (kernel/gfxboot.c): la colonna di destra (logo, server, caselle) si aggancia al bordo destro, le righe iLO e la barra dei tasti al fondo, e i quattro tasti U, P, B, N si distribuiscono su tutta la larghezza.
  • Login, menu, informazioni, gestione utenti (kernel/gfxlogin.c): il blocco centrale (logo, sottotitolo, riquadro del login, finestra Admin) si centra, le righe di aiuto restano in fondo e le intestazioni restano larghe quanto lo schermo. Il visualizzatore dei README prende lo schermo che c'e' (a 1920x1080 42 righe invece di 25). Le sei card del menu si centrano fra intestazione e piede.
  • Shell grafica: il watermark e il riquadro DEBUG seguono lo schermo (erano disegnati per 1024x768 e a x = 800).
  • SHELL TESTUALE: la console grafica arriva a 240x67 caratteri (era al massimo 128x48, GCONSOLE_COLS/GCONSOLE_ROWS), e l'animazione d'avvio, disegnata per 80x25, si centra li'.
  • Desktop 1 (kernel/desktop.c): DT_W e DT_H sono lo schermo vero. Sfondo, barra, menu Start, finestre e trascinamento passavano gia' tutti da li'. Le finestre "Sistema" e "Risoluzione" dicono la risoluzione vera (erano scritte "1024x768") e la seconda elenca i sei modi, con quello attivo.
  • Desktop 2 (kernel/desktop2.c, desktop2_core/framebuffer.c): il core riceve lo schermo vero in init() e dispone da se' dock, barra e finestre. Il suo buffer si prende grande quanto lo schermo (3 MB a 1024x768, 7,9 MB a 1920x1080) e si rifa' se la risoluzione sale; l'adattatore video si ricollega a ogni apertura, con le misure nuove. I pulsanti CRUSCOTTO e DEBUG stanno in basso a destra alla stessa distanza di prima. Se l'heap non basta, prima si restituisce il doppio buffer degli altri ambienti, poi si scende di risoluzione fino a 1024x768.
  • Desktop 3, nei due kernel: dock a destra, riga di aiuto e watermark in fondo, e la finestra "Strumenti portati" si allunga verso il basso (nel kernel 2, a 1920x1080, si vedono 31 righe invece di 15).
  • Il watermark della 0.2.5.69 era gia' calcolato sullo schermo: resta centrato in fondo in ogni modo.

Le schermate disegnate a mano per 1024x768

ServerCfg, SeaOfSens (seaof gui), DynPower (dynpower gui), NATTour (tour), l'editor (edit, e il Notepad del Desktop 1), il display di NATMemory e le schermate dei tasti U, P, B, N dell'avvio hanno le coordinate nel codice, centinaia. Invece di riscriverle, il driver ha una vista (fb_set_vista, fb_vista_centrata): tutto il disegno si sposta in un riquadro 1024x768 al centro dello schermo e ce lo tiene, e fb_get_width/height rispondono con le misure del riquadro. Si vedono uguali a prima, con il nero intorno. A 1024x768 la vista non si accende e non cambia niente. Anche lo scorrimento della console (gconsole_scroll, che scrive diritto in memoria video) rispetta la vista.

La memoria del doppio buffer

Il doppio buffer si prendeva dall'heap sempre grande 3 MB (FB_BACKBUFFER_MAX, 1024x768). Ora e' grande quanto il modo attuale, e se ne serve uno piu' grande quello vecchio torna all'heap (kfree) e se ne prende uno nuovo. fb_libera_doppio_buffer() lo restituisce quando nessuno lo usa.

Trovati da una rilettura indipendente

Il codice di questa versione l'ha riletto un secondo agente, che l'ha anche compilato con gcc e provato in QEMU:

  • Con gcc il kernel 1 non ci stava piu'. gcc aggiunge da solo le tabelle .eh_frame (circa 62 KB), che nel binario piatto spingevano il kernel 1 oltre i 1152 settori che stage2 carica: le variabili inizializzate (system_version, lo stato del PostScript...) sarebbero partite a zero, senza nessun avviso. Con gcc e il linker GNU era gia' oltre il limite nella 0.2.5.69 (1164 settori). Ora kernel/linker.ld scarta quelle sezioni e i due Makefile passano a gcc -fno-asynchronous-unwind-tables -fno-unwind-tables: con gcc il kernel 1 fa 1036 settori. Con clang non cambia niente. E oltre 1152 settori la build si ferma con un errore invece di scrivere un'immagine che non parte intera.
  • Uscendo dal Desktop 1 con ESC lo schermo restava fermo sull'ultimo fotogramma del desktop: il Desktop 1 lascia acceso il doppio buffer, e il login veniva disegnato li', nascosto. Si scriveva la password alla cieca. C'era gia' nella 0.2.5.69; ora al ritorno al login il doppio buffer si spegne.
  • Il cambio di modo ferma le interruzioni mentre cambia le misure: il cursore lampeggiante della console, disegnato dal timer, poteva scrivere con le misure nuove nel doppio buffer vecchio.
  • La finestra "Risoluzione" del Desktop 1 e' piu' alta (i modi elencati sono sei); le righe del Desktop 3 interno non vanno oltre il fondo se lo schermo diventa piu' basso; il Desktop 2 ricollega l'adattatore prima del primo fotogramma (serviva alla grana dello stile realistico).

Anche

  • Le dipendenze nei Makefile (-MMD -MP): un make dopo aver cambiato solo un header, o solo kernel/graphic/natalizioos.c (che e' incluso dentro natalizioos_graphic.c), teneva gli oggetti vecchi e il kernel non cambiava. Ora il compilatore scrive i file .d accanto agli oggetti e il Makefile li legge.
  • Nel selettore d'avvio "1 o 2 per scegliere" non perde piu' l'ultima lettera (la barra e' una colonna piu' corta).
  • I testi che dicevano "1024x768" (il README e il blog del Desktop 2, la finestra About, le informazioni della shell grafica) dicono la risoluzione vera, o "da 1024x768 a 1920x1080".
  • Il menu a 1024x768 e' l'unica schermata che a 1024x768 cambia: le card salgono di 35 punti, e la sesta (AGGIORNAMENTO SOFTWARE) non finisce piu' sotto il piede.
  • BUILD_STATUS.md ha una sezione "Risoluzione": i modi, la memoria di un'immagine, la RAM minima e il settore 2001.

Le prove

RisoluzioneEsito
Avvio (64 MB): selettore, avvio grafico, login, menu1920x1080OK
Shell grafica, ServerCfg nel riquadro al centro1920x1080OK
SHELL TESTUALE: animazione centrata, 240x67, resolution1920x1080OK
Desktop 1, poi ESC: si vede il login1920x1080OK
Desktop 21920x1080OK
Desktop 3 -> kernel 2 -> ESC -> menu1920x1080OK
SeaOfSens, DynPower, NATTour, editor, tasto U all'avvionel riquadro 1024x768OK
Informazioni (I), README, gestione utenti (M), finestra Admin1920x1080OK
Frecce all'avvio -> 1600x900; riavvio: la scelta resta; -> 1024x7681600x900, 1024x768OK
resolution 1920x1080 dopo un Desktop 2 a 1024x768 (il buffer si rifa'), e il contrariotutte e dueOK
32 MB: Desktop 1, Desktop 2, kernel 21920x1080OK
24 MB: Desktop 1, Desktop 2, kernel 21600x900OK
16 MB: selettore limitato, Desktop 2, kernel 2 e ritorno1024x768OK
Shell testuale VGA (1 all'avvio), immagine solo testo80x25OK
Build con gcc: kernel 1 di 1036 settori, kernel 2 e ritorno1920x1080OK

Kernel 1: 1084 settori su 1150 (con clang); kernel 2: 717 su 952. Build 180 (kernel 1 e kernel 2), 181 l'immagine solo testo.

La scelta all'avvio: le frecce cambiano la risoluzione, e resta salvata
v0.2.5.70 - La scelta all'avvio: le frecce cambiano la risoluzione, e resta salvata
L'avvio grafico a 1920x1080: colonna destra al bordo, tasti su tutta la larghezza
v0.2.5.70 - L'avvio grafico a 1920x1080: colonna destra al bordo, tasti su tutta la larghezza
Il menu a 1920x1080: le sei card al centro, il piede in fondo
v0.2.5.70 - Il menu a 1920x1080: le sei card al centro, il piede in fondo
Il login, anche dopo ESC dal Desktop 1 (prima restava fermo il desktop)
v0.2.5.70 - Il login, anche dopo ESC dal Desktop 1 (prima restava fermo il desktop)
Desktop 1 a 1920x1080
v0.2.5.70 - Desktop 1 a 1920x1080
Desktop 2 a 1920x1080: dock, barra e finestre li dispone il core
v0.2.5.70 - Desktop 2 a 1920x1080: dock, barra e finestre li dispone il core
Desktop 3 nel kernel 2 a 1920x1080: la finestra degli strumenti piu' alta
v0.2.5.70 - Desktop 3 nel kernel 2 a 1920x1080: la finestra degli strumenti piu' alta
Shell grafica: watermark e riquadro DEBUG seguono lo schermo
v0.2.5.70 - Shell grafica: watermark e riquadro DEBUG seguono lo schermo
ServerCfg, disegnata per 1024x768: si apre in un riquadro al centro
v0.2.5.70 - ServerCfg, disegnata per 1024x768: si apre in un riquadro al centro
SHELL TESTUALE: 240x67 caratteri
v0.2.5.70 - SHELL TESTUALE: 240x67 caratteri
Scelta 1600x900 all'avvio: il menu a 1600x900
v0.2.5.70 - Scelta 1600x900 all'avvio: il menu a 1600x900
Con 16 MB di RAM le frecce si fermano a 1024x768, e la riga dice perche'
v0.2.5.70 - Con 16 MB di RAM le frecce si fermano a 1024x768, e la riga dice perche'
NEW WIKI_NATALIZIO_OS DALLA VERSIONE 0.2.5.71
×