Diky za clanek.
Pokusil jsem se prepsat program do "Forthu" a zkompilovat.
ZX_CONSTANT ORG 0x8000 INIT(60000) PRINT_Z({ZX_PAPER, ZX_RED, "Hello", ZX_INK, ZX_WHITE, "Speccy", ZX_FLASH, 1, "!"}) STOP
Prvni slovo "aktivuje" konstanty typu ZX_...
INIT a STOP obaluji program a nastavuji zasobnik navratovych adres a vraci registry pro basic.
Program musi uchovavat HL protoze je v nem TOS (top of stack).
DE, protoze je v nem NOS (next of stack).
Dalsi cell je v (SP) atd.
HL' protoze obsahuje index RAS (return adres stack).
Ostatni se da pouzit volne.
PRINT_Z je psane trosku jinak, protoze je delane jako funkce, za to o trosicku rychleji, protoze v kazdem cyklu neresi ret z a neztraci 5 taktu.
Mimochodem, retezce ukoncene nulou jsou podle nekterych programatoru ve Forthu spatne reseni. To je trochu flamewar tema. Ja si myslim, ze pokud se nesnazime s nema nejak pracovat a jen je tiskneme tak jsou v pohode.
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'ZX_CONSTANT ORG 0x8000 INIT(60000) PRINT_Z({ZX_PAPER, ZX_RED, "Hello", ZX_INK, ZX_WHITE, "Speccy", ZX_FLASH, 1, "!"}) STOP' ZX_EOL EQU 0x0D ; zx_constant end of line ZX_INK EQU 0x10 ; zx_constant colour ZX_PAPER EQU 0x11 ; zx_constant colour ZX_FLASH EQU 0x12 ; zx_constant 0 or 1 ZX_BRIGHT EQU 0x13 ; zx_constant 0 or 1 ZX_INVERSE EQU 0x14 ; zx_constant 0 or 1 ZX_OVER EQU 0x15 ; zx_constant 0 or 1 ZX_AT EQU 0x16 ; zx_constant Y,X ZX_TAB EQU 0x17 ; zx_constant # spaces ZX_BLACK EQU %000 ; zx_constant ZX_BLUE EQU %001 ; zx_constant ZX_RED EQU %010 ; zx_constant ZX_MAGENTA EQU %011 ; zx_constant ZX_GREEN EQU %100 ; zx_constant ZX_CYAN EQU %101 ; zx_constant ZX_YELLOW EQU %110 ; zx_constant ZX_WHITE EQU %111 ; zx_constant ORG 0x8000 ; === b e g i n === ld (Stop+1), SP ; 4:20 init storing the original SP value when the "bye" word is used ld L, 0x1A ; 2:7 init Upper screen call 0x1605 ; 3:17 init Open channel ld HL, 0xEA60 ; 3:10 init Return address stack = 60000 exx ; 1:4 init ld BC, string101 ; 3:10 print_z Address of null-terminated string101 call PRINT_STRING_Z ; 3:17 print_z Stop: ; stop ld SP, 0x0000 ; 3:10 stop restoring the original SP value when the "bye" word is used ld HL, 0x2758 ; 3:10 stop exx ; 1:4 stop ret ; 1:10 stop ; ===== e n d ===== ;------------------------------------------------------------------------------ ; Print C-style stringZ ; In: BC = addr ; Out: BC = addr zero + 1 rst 0x10 ; 1:11 print_string_z putchar with ZX 48K ROM in, this will print char in A PRINT_STRING_Z: ; print_string_z ld A,(BC) ; 1:7 print_string_z inc BC ; 1:6 print_string_z or A ; 1:4 print_string_z jp nz, $-4 ; 3:10 print_string_z ret ; 1:10 print_string_z STRING_SECTION: string101: db ZX_PAPER, ZX_RED, "Hello", ZX_INK, ZX_WHITE, "Speccy", ZX_FLASH, 1, "!", 0x00 size101 EQU $ - string101 ; seconds: 0 ;[35:167]
Dalsi varianta je pouzit retezec, ktery bude ukoncen tak ze posledni bajt bude mit 7. bit nastaveny na 1, takze se usetri bajt na retezec. Pouzije se slovo PRINT_I.
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'ZX_CONSTANT ORG 0x8000 INIT(60000) PRINT_I({ZX_PAPER, ZX_RED, "Hello", ZX_INK, ZX_WHITE, "Speccy", ZX_FLASH, 1, "!"}) STOP' ZX_EOL EQU 0x0D ; zx_constant end of line ZX_INK EQU 0x10 ; zx_constant colour ZX_PAPER EQU 0x11 ; zx_constant colour ZX_FLASH EQU 0x12 ; zx_constant 0 or 1 ZX_BRIGHT EQU 0x13 ; zx_constant 0 or 1 ZX_INVERSE EQU 0x14 ; zx_constant 0 or 1 ZX_OVER EQU 0x15 ; zx_constant 0 or 1 ZX_AT EQU 0x16 ; zx_constant Y,X ZX_TAB EQU 0x17 ; zx_constant # spaces ZX_BLACK EQU %000 ; zx_constant ZX_BLUE EQU %001 ; zx_constant ZX_RED EQU %010 ; zx_constant ZX_MAGENTA EQU %011 ; zx_constant ZX_GREEN EQU %100 ; zx_constant ZX_CYAN EQU %101 ; zx_constant ZX_YELLOW EQU %110 ; zx_constant ZX_WHITE EQU %111 ; zx_constant ORG 0x8000 ; === b e g i n === ld (Stop+1), SP ; 4:20 init storing the original SP value when the "bye" word is used ld L, 0x1A ; 2:7 init Upper screen call 0x1605 ; 3:17 init Open channel ld HL, 0xEA60 ; 3:10 init Return address stack = 60000 exx ; 1:4 init ld BC, string101 ; 3:10 print_i Address of string101 ending with inverted most significant bit call PRINT_STRING_I ; 3:17 print_i Stop: ; stop ld SP, 0x0000 ; 3:10 stop restoring the original SP value when the "bye" word is used ld HL, 0x2758 ; 3:10 stop exx ; 1:4 stop ret ; 1:10 stop ; ===== e n d ===== ;------------------------------------------------------------------------------ ; Print string ending with inverted most significant bit ; In: BC = addr string_imsb ; Out: BC = addr last_char + 1 rst 0x10 ; 1:11 print_string_i putchar with ZX 48K ROM in, this will print char in A PRINT_STRING_I: ; print_string_i ld A,(BC) ; 1:7 print_string_i inc BC ; 1:6 print_string_i or A ; 1:4 print_string_i jp p, $-4 ; 3:10 print_string_i and 0x7f ; 2:7 print_string_i rst 0x10 ; 1:11 print_string_i putchar with ZX 48K ROM in, this will print char in A ret ; 1:10 print_string_i STRING_SECTION: string101: db ZX_PAPER, ZX_RED, "Hello", ZX_INK, ZX_WHITE, "Speccy", ZX_FLASH, 1, "!" + 0x80 size101 EQU $ - string101 ; seconds: 0 ;[38:185]
Posledni varianta je zakladni PRINT, ktere vola ROM rutinu pro tisk retezce, ktera vyzaduje pokazde nastavit delku tisknuteho retezce, takze opakovane volani zabira nejvic mista.
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ls -l root_str*.bin -rw-rw-r-- 1 dworkin dworkin 50 Feb 21 02:59 root_str.bin -rw-rw-r-- 1 dworkin dworkin 56 Feb 21 02:58 root_stri.bin -rw-rw-r-- 1 dworkin dworkin 54 Feb 21 02:58 root_strz.bin dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$
PS: Preklad je napsan v pouhem makru (M4) a vsechny PRINT se snazi najit shodne retezce pokud se pouziji, aby se ulozil jen jeden. Proto ty slova vypadaji "trosku" jinak nez ve Forthu.
PRINT({"Retezec"}) misto ." Retezec"
PUSH(10) misto 10
ADD misto +
Vypadla me ta varianta s PRINT
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'ZX_CONSTANT ORG 0x8000 INIT(60000) PRINT({ZX_PAPER, ZX_RED, "Hello", ZX_INK, ZX_WHITE, "Speccy", ZX_FLASH, 1, "!"}) STOP' ZX_EOL EQU 0x0D ; zx_constant end of line ZX_INK EQU 0x10 ; zx_constant colour ZX_PAPER EQU 0x11 ; zx_constant colour ZX_FLASH EQU 0x12 ; zx_constant 0 or 1 ZX_BRIGHT EQU 0x13 ; zx_constant 0 or 1 ZX_INVERSE EQU 0x14 ; zx_constant 0 or 1 ZX_OVER EQU 0x15 ; zx_constant 0 or 1 ZX_AT EQU 0x16 ; zx_constant Y,X ZX_TAB EQU 0x17 ; zx_constant # spaces ZX_BLACK EQU %000 ; zx_constant ZX_BLUE EQU %001 ; zx_constant ZX_RED EQU %010 ; zx_constant ZX_MAGENTA EQU %011 ; zx_constant ZX_GREEN EQU %100 ; zx_constant ZX_CYAN EQU %101 ; zx_constant ZX_YELLOW EQU %110 ; zx_constant ZX_WHITE EQU %111 ; zx_constant ORG 0x8000 ; === b e g i n === ld (Stop+1), SP ; 4:20 init storing the original SP value when the "bye" word is used ld L, 0x1A ; 2:7 init Upper screen call 0x1605 ; 3:17 init Open channel ld HL, 0xEA60 ; 3:10 init Return address stack = 60000 exx ; 1:4 init push DE ; 1:11 print ld BC, size101 ; 3:10 print Length of string101 ld DE, string101 ; 3:10 print Address of string101 call 0x203C ; 3:17 print Print our string with ZX 48K ROM pop DE ; 1:10 print Stop: ; stop ld SP, 0x0000 ; 3:10 stop restoring the original SP value when the "bye" word is used ld HL, 0x2758 ; 3:10 stop exx ; 1:4 stop ret ; 1:10 stop ; ===== e n d ===== STRING_SECTION: string101: db ZX_PAPER, ZX_RED, "Hello", ZX_INK, ZX_WHITE, "Speccy", ZX_FLASH, 1, "!" size101 EQU $ - string101 ; seconds: 1 ;[32:150]
Pěkně napsané využití tisku znaků přes ROM.
Osobně jsem ZX ROM pro tisk znaků přestal používat už dávno, protože obvykle chci vypisovat mnohem rychleji, často bez barev a skoro vždy na všech 24 řádků, nejenom na 22, což by asi v článku mohlo být zmíněno. Vidím tam jen zmínku o otevření kanálu 2, ale nikoli jeho omezení. Resp. to, že omezení podprogramu RST16 je stejné, jako omezení příkazu PRINT v BASICu.
Taky mi tam chybí zmínka o velmi často používaném způsobu ukončení řetězce bitem 7 v 1, které dává smysl pokud používám jen ASCII se znaky 32 až 127, ušetří se tím byte v každém řetězci a zároveň je stále jednoduché a rychlé bit 7 testovat.
Na druhou stranu ukončení nulou mi dává možnost být rozmařilý a s použitím vlastního fontu vypisovat kompatibilním kódováním ISO8859-2 :) Ale i k tomu potřebuju vlastní kód, který nemá omezení nad 127, neřeší semigrafiku a tokeny BASICových příkazů.
Jo, je těžké do omezeného prostoru napsat vše :) I malý, starý a dobře prozkoumaný počítač má možnosti téměř neomezené :)
Díky za doplnění. Jinak například to nastavení sedmého bitu se dalo využít při tvorbě textovek (pokud už neměl člověk nějakou komprimaci), protože například názvy všech předmětů mohly být v jednom řetězci a "oddělené" jen nejvyšším bitem na začátku každého jména. Dobré řešení třeba v BASICech, které neměly pole stringů (+ se na každý předmět ušetřil minimálně jeden bajt).
Nastavení sedmého bitu používá mimochodem i samotná ZX ROM - viz tokeny příkazů od dekadické adresy 150. Akorát ten bit 7 je na posledním znaku, ne na prvním.
Prohledávání a detokenizaci mám např. ve svých ovladačích k tiskárnám, když jsem chtěl tisknout nejenom text pomocí LPRINT (ekvivalent PRINT #3), ale i výpis BASICového programu pomocí LLIST (ekvivalent LIST #3).
Hezká je textovka "...a to snad ne", kde se používá pětibitové kódování, ve kterém se nejčastější znaky kódují jedním pětibitem a tuším dvě speciální hodnoty slouží jako escape, co volí jednu z dalších dvou tabulek znaků.
Pak je tam taky krásně jednoduše řešená herní logika, kdy se akorát při průchodu obrazovkami různě překlápí bity ve stavovém stroji a podle nich se volí, která varianta textu se zobrazí.
nastaví si jednu nebo více paměťových buněk ze system vars.
https://worldofspectrum.org/ZXBasicManual/zxmanchap25.html
Třeba barva přes 23693 (dekadicky) apod.
třeba takto se dá vytisknout zpráva několikrát, pokaždé jinou barvou:
ENTRY_POINT equ $8000 ROM_OPEN_CHANNEL equ $1601 ROM_PRINT equ $203C ATTR_T equ 23695 org ENTRY_POINT start: ld A,2 ; číslo kanálu call ROM_OPEN_CHANNEL ; otevření kanálu číslo 2 (screen) ld B, 64 ; barva tisku ld HL, ATTR_T ; adresa systémové proměnné ATTR_T loop: ld (HL), B ; změna barvy tisku push BC ; uchovat BC ld DE, TEXT ; adresa prvního znaku v řetězci ld BC, TEXT_LENGTH ; délka textu call ROM_PRINT ; volání subrutiny v ROM pop BC ; obnovit BC djnz loop ; tisk další barvou ret ; ukončit program ; řetězec TEXT: db "Hello, speccy!"