RetroMagazine nr. 22 – Anno: 2020 – Linguaggio: Amstrad CPC – Locomotive Basic
Mentre ero alla ricerca dell’ispirazione per un articolo per il numero 22 di RetroMagazine, mi sono imbattuto in un programma scritto diversi anni fa da AMSDOS, un utente del forum di CPC Wiki.
Come si evince dalla data scritta nei commenti nelle prime righe del codice, la versione originale del programma è stata scritta nel lontano 1997. Dopo aver dato una rapida occhiata al codice mi sono subito reso conto che avrebbe fatto al caso mio e sarebbe stato un’ottima occasione anche per introdurre qualche nuovo concetto riguardante il Locomotive Basic.
Per chi fosse interessato, il codice originale si trova in questo thread sul forum di CPC Wiki:
https://www.cpcwiki.eu/forum/programming/memory-display/msg13921/#msg13921
Dopo aver copiato il codice sull’emulatore WinAPE ed aver dato il RUN, mi sono accorto che sì il programma funzionava perfettamente, ma anche che aveva bisogno di qualche aggiustatina per migliorarne la visibilità.
La versione originale infatti utilizzava la modalità grafica MODE 1, a 40 colonne, che personalmente reputo poco elegante, quindi la prima cosa che ho voluto fare è stata cambiarne l’aspetto grafico utilizzando la modalità MODE 2 ad 80 colonne.
Ovviamente questo cambiamente ha richiesto la modifica di tutto il resto dell’output video. In questo modo sono riuscito quindi a dividere lo schermo in due aree fisiche (in realtà sono 3, ma lo vedremo tra poco) ed a separare visivamente l’output del programma dall’area di interazione con l’utente.
Il comando WINDOW
Come ho già avuto modo di ribadire diverse volte, il Locomotive Basic è un linguaggio piuttosto potente ed il comando WINDOW ne è una chiara dimostrazione.
Vediamo qual è la sintassi di questo comando:
WINDOW[#‹stream expression›],[L],[R],[T],[B]
#STREAM: è possibile definire fino ad 8 fineste indipendenti che possono anche essere sovrapposte. La finestra numero 0 è la finestra principale dove messaggi di errore o di stato vengono visualizzati di default.
L = left column – colonna di sinistra
R = right column – colonna di destra (dipende dal modo grafico: 0 = 20 / 1 = 40 / 2 = 80)
T = top row – riga iniziale (sempre tra 1 to 25)
B = bottom row – riga finale (sempre tra 1 to 25)
La funzione WINDOW, come forse avrete già intuito, permette di creare una finestra virtuale all’interno dell’area video dove i comandi PRINT, INPUT… possono, tramite lo STREAM, interagire.
Come facilmente intuibile questo comando è molto potente perché tutte le istruzioni che vi verranno indirizzate tramite lo STREAM, avranno effetto soltanto nella finestra indicata e vi rimarranno confinate. Per esempio, definendo una finestra di 10 righe, lo scrolling avverrà entro questo limite, senza impattare il resto dello schermo. Molto utile nel caso volessimo suddividere lo schermo in aree logiche.
La versione originale faceva già uso delle finestre per separare l’output del programma, vediamo quindi come sono state adattate nella nuova versione.
La prima finestra, composta da 3 righe ed identificata con lo stream #1, viene utilizzata per stampare a video l’intestazione del programma.
Si vedano le righe da 110 a 130.
La seconda finestra, composta invece da 22 righe ed indentificata dalla stream #2, viene utilizzata per stampare l’output del programma. Come potrete facilmente notare eseguendo il programma, lo scrolling della seconda finestra è totalmente seprato dalla prima che rimane invece fissa.
Si veda la riga 180.
La terza ed ultima finestra, identificata dallo stream #3 è invece interamente dedicata all’interazione con l’utente. In questa finestra vengono richiesti i parametri da utilizzare per visualizzare la memoria (da quale indirizzo iniziare e quanti indirizzi visualizzare) e stampati i messaggi generati dal programma.
Si vedano le righe da 191 a 195.
Vorrei inoltre farvi notare come tutti i comandi che stampano a video, PRINT, INPUT… abbiano la possibilità di gestire lo stream dove redirigerne l’ouptut.
Il resto del programma
Il resto del programma è piuttosto semplice, in pratica tramite la funzione PEEK(n) andiamo a leggere il contenuto dell’indirizzo di memoria interessato e ne facciamo un dump in formato esadecimale (con annessa visualizzazione del carattere), decimale e binario.

Da notare che il Locomotive Basic ha anche a disposizione la funzione BIN$() che ci permette di risparmiare tempo nel convertire un intero in binario. Molto comodo.
BIN$ (,[])
Produce una stringa in formato binario rappresentante il valore di utilizzando il numero di caratteri binari come indicato da (in un range da 0 a 16).
Il valore deve essere compreso nel range: -32768 to 65535.
Un altra paio di particolarità, piuttosto semplici, da notare nel programma sono le righe 190 e 196.
La riga 190 effettua un controllo sull’indirizzo di memoria da leggere, se questo dovesse eccedere il valore #FFFF (65535), il programma deve essere interrotto perché altrimenti genererebbe un errore. Il CPC 464 (oggetto di questo test) indirizza fino a 64K di RAM. Nel caso volessimo testare lo stesso programma sul CPC 6128, dovremmo modificare questa riga con il valore 131072 ovvero 128K.
La riga 196 invece è fondamentale per ripristinare l’aspetto dell’interprete Basic prima della fine del programma. Da notare che ho intenzionalmente lasciato la modalità ad 80 colonne in quanto la trovo più piacevole per gli occhi.
Perché controllare il contenuto della memoria?
A questo punto però credo che vi starete facendo una domanda, perché controllare il contenuto della memoria? Beh, intanto perchè è figo… Ok, scherzo…
Attualmente questo genere di programmi sono piuttosto fini a se stessi, in quanto tutte le locazioni di memoria dei computer sono conosciute e mappate. In passato invece non era così e programmi del genere servivano agli smanettoni per capire come e dove le informazioni venissero memorizzate nella RAM del computer per poi poterne usufruire nei loro programmi.
Dove trovo adesso queste informazioni?
Oggigiorno possiamo trovare queste informazioni con una veloce ricerca su internet, mentre negli anni 80 e 90 era più difficile. Il caso dell’Amstrad CPC comunque era già un’eccezione per quegli anni, in quanto gran parte della documentazione fu resa disponibile al momento del lancio.
In questo libro potete trovare la documentazione della memoria del CPC464:
http://cpctech.cpc-live.com/docs/manual/s968se02.pdf
Provate a vedere cosa trovate dalla locazione 368 decimale (170 esadecimale) in poi…
Buon divertimento!

Titolo: Memory display
Piattaforma: Amstrad CPC
Linguaggio: Locomotive Basic
Versione originale: AMSDOS
Pubblicazione: PD – CP/M User
Anno: Luglio 1997
Modifica: Francesco Fiorentini
Anno: 2020
Note: Il programma e’ stato riscritto, vedi le informazioni nell’articolo.

Istruzioni
Inserite l’indirizzo di memoria da dove partire per fare il dump e la lunghezza in byte della mappatura.
Qui di seguito trovate il codice del gioco e del loader da copiare sul vostro Amstrad CPC.
Attenzione – Ci siamo resi conto che nel listato i caratteri ‘>’ e ‘<‘ potrebbero venir sostituiti dai rispettivi encoding html ‘>’ e ‘<’. Nel caso, sostituite questi valori direttamente su CBM prg Studio.
Listato: Battle of the cars – Amstrad CPC – Locomotive Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 10 '**********************************************
11 '* Memory Display
20 '* Public Domain - CP/M User
30 '* Written by AMSDOS 27/4/1997
40 '* Modified by Francesco Fiorentini 29/3/2020
42 '**********************************************
45 row=0
60 MODE 2:WINDOW 39,40,1,25:INK 3,2:INK 0,0:PAPER 3:CLS:BORDER 2
70 WINDOW#1,1,39,1,3:WINDOW#2,1,39,4,25:WINDOW#3,40,80,1,25:PAPER#1,0:INK 3,2
80 row=row+1:LOCATE#3,3,row:INPUT#3,"Enter Start Address ";start
90 row=row+1:LOCATE#3,3,row:INPUT#3,"Enter Length ";length
100 CLS#1:CLS#2:PAPER#2,0
101 REM WINDOW#1 è utilizzata per l'intestazione
110 PRINT #1,CHR$(150);STRING$(6,CHR$(154));CHR$(158);STRING$(10,CHR$(154));CHR$(158);STRING$(6,CHR$(154));CHR$(158);STRING$(10,CHR$(154));CHR$(156)
115 REM Attenzione agli spazi nella stringa sotto...
120 PRINT #1,CHR$(149);" Addr ";CHR$(149);" Hex Chr ";CHR$(149);" Dec ";CHR$(149)" Binary ";CHR$(149);CHR$(13)
130 PRINT #1,CHR$(147);STRING$(6,CHR$(154));CHR$(155);STRING$(10,CHR$(154));CHR$(155);STRING$(6,CHR$(154));CHR$(155);STRING$(10,CHR$(154))CHR$(153)
131 REM disassemblaggio delle memoria
140 length=length+start
150 FOR pr=start TO length
160 dis=PEEK(start)
170 char$=CHR$(1)+CHR$(dis)
179 REM WINDOW#2 è utilizzata per la stampa dei valori a video
180 PRINT #2,TAB(3);HEX$(start);TAB(11);HEX$(PEEK(pr),2);TAB(15);char$;TAB(20);PEEK(pr);TAB(28);BIN$(PEEK(pr),8)
190 start=start+1:if start > 65535 then goto 191 else NEXT pr
191 row=row+1:PAPER #2,0:LOCATE#3,3,row:PRINT#3,"Memory Map terminated."
192 row=row+1:PAPER #2,0:LOCATE#3,3,row:PRINT#3,"Continue to map? y/n"
193 answ$=INKEY$:IF answ$<>"Y" AND answ$<>"N" AND answ$<>"y" AND answ$<>"n" THEN 193
194 IF answ$ = "Y" or answ$ = "y" THEN GOTO 80
195 row=row+1:PAPER #2,0:LOCATE#3,3,row:PRINT#3,"Goodbye...":for i= 1 to 1000:next i
196 WINDOW 1,40,1,25:LOCATE 5,25:PAPER 0:MODE 2:BORDER 0:END
