; =====================================================================
; Listing Asemblerowy Systemu Operacyjnego w pamięci ROM komputera ZX81
; =====================================================================
; ----------------------------------------
; Data ostatniej aktualizacji: 13-LIS-2011
; ----------------------------------------
; Ten plik można poddać asemblacji bezpośrednio w TASM'e, który jest darmowo dostępny w sieci Internet. Można dokonywać w nim
; zmian w celu uzyskania nowych funkcji, np. w symulatorze ZX81, lecz miejsca na takie modyfikacje jest bardzo mało. To właśnie
; brak miejsca w 8KB pamięci ROM wymusił na programistach oszczędności, które niejednokrotnie zwalniają i tak już wolnego ZX-81.
; Dokumentacja nie jest kompletna i w miarę możliwości, a przede wszystkim czasu, będzie stopniowo rozbudowywana. Zawarte tutaj
; procedury są doskonałym przykładem technik programowania mikroprocesora Z80 i mogą być bardzo cennym źródłem wiedzy o tym układzie.
; Ze swej strony zmodyfikowałem plik dostępny w wersji angielskiej w Internecie, tak aby zastąpić etykiety adresowe etykietami
; słownymi. W tym celu został napisany prosty program w C++, który przeglądnął plik źródłowy, zapamiętał wszystkie etykiety,
; a następnie odpowiednio przekonwertował go na postać HTML, dodając hiperłącza, które umożliwiają szybkie poruszanie się po
; całym listingu. Zamieniłem również odwołania adresowe do zmiennych systemowych na ich nazwy mnemotechniczne. Sądzę, iż dzięki
; tym zabiegom kod stał się bardziej czytelny. Adresy etykiet są zawsze wypisane w komentarzu bezpośrednio nad nazwą etykiety
; w kodzie szesnastkowym i dziesiętnym.
;*************************************
;** Definicje zmiennych systemowych **
;*************************************
ERR_NR .EQU $4000 ; $4000 : 16384
FLAGS .EQU $4001 ; $4001 : 16385
ERR_SP .EQU $4002 ; $4002 : 16386
RAMTOP .EQU $4004 ; $4004 : 16388
MODE .EQU $4006 ; $4006 : 16390
PPC .EQU $4007 ; $4007 : 16391
VERSN .EQU $4009 ; $4009 : 16393
E_PPC .EQU $400A ; $400A : 16394
D_FILE .EQU $400C ; $400C : 16396
DF_CC .EQU $400E ; $400E : 16398
VARS .EQU $4010 ; $4010 : 16400
DEST .EQU $4012 ; $4012 : 16402
E_LINE .EQU $4014 ; $4014 : 16404
CH_ADD .EQU $4016 ; $4016 : 16406
X_PTR .EQU $4018 ; $4018 : 16408
STKBOT .EQU $401A ; $401A : 16410
STKEND .EQU $401C ; $401C : 16412
BREG .EQU $401E ; $401E : 16414
MEM .EQU $401F ; $401F : 16415
UNUSED1 .EQU $4020 ; $4020 : 16416
UNUSED2 .EQU $4021 ; $4021 : 16417
DF_SZ .EQU $4022 ; $4022 : 16418
S_TOP .EQU $4023 ; $4023 : 16419
LAST_K .EQU $4025 ; $4025 : 16421
DBOUNC .EQU $4027 ; $4027 : 16423
MARGIN .EQU $4028 ; $4028 : 16424
NXTLIN .EQU $4029 ; $4029 : 16425
OLDPPC .EQU $402B ; $402B : 16427
FLAGX .EQU $402D ; $402D : 16429
STRLEN .EQU $402E ; $402E : 16430
T_ADDR .EQU $4030 ; $4030 : 16432
SEED .EQU $4032 ; $4032 : 16434
FRAMES .EQU $4034 ; $4034 : 16436
COORDS .EQU $4036 ; $4036 : 16438
PR_CC .EQU $4038 ; $4038 : 16440
S_POSN .EQU $4039 ; $4039 : 16441
CDFLAG .EQU $403B ; $403B : 16443
PRBUFF .EQU $403C ; $403C : 16444
MEMBOT .EQU $405D ; $405D : 16477
;**********************************************
;** Część 1. PROCEDURY RESTARTU ORAZ TABLICE **
;**********************************************
; --------------------------------
; PUNKT WEJŚCIA DLA STARTU SYSTEMU
; --------------------------------
; Wszystkie mikroprocesory Z80 rozpoczynają wykonywanie programu asemblerowego od adresu 0. Przy starcie systemu ustawiony jest
; Tryb Przerwań 0, komputery ZX korzystają z Trybu Przerwań 1. Przerwania są wyłączone.
; $0000 : 0
START: OUT ($FD),A ; wyłącz generator przerwań niemaskowanych, jeśli ten ROM pracuje na sprzęcie ZX81.
; Jeśli sprzętem jest wcześniejszy ZX80, to operacja nie wywołuje żadnego efektu.
LD BC,$7FFF ; Ustaw BC na szczyt możliwej pamięci RAM. ZX81 może wykonywać kod maszynowy tylko
; poniżej adresu $8000, ponieważ wyższe adresy są wykorzystywane do sprzętowo-programowej
; obsługi obrazu telewizyjnego. Jeśli komputer ma więcej pamięci, to można ją wykorzystywać
; do przechowywania danych - jednakże bufor wyświetlania musi być poniżej $8000.
JP RAM_CHECK
; -----------------------
; PUNKT WEJŚCIA DLA BŁĘDU
; -----------------------
; Punkt wejścia natychmiastowej obsługi błędu. Komputery ZX wykonują ten sam kod maszynowy zarówno w trakcie interpretacji programu
; w języku BASIC, jak i podczas sprawdzania składni. Jeśli błąd powstał w trakcie pracy programu, tworzony jest krótki komunikat.
; Jeśli błąd powstał przy wprowadzania wiersza programu w języku BASIC lub przy wprowadzaniu danych itd., to znacznik błędu wskazuje
; dokładne położenie błędu.
; $0008 : 8
ERROR_1: LD HL,(CH_ADD) ; pobierz adres znaku z CH_ADD
LD (X_PTR),HL ; i ustaw wskaźnik błędu X_PTR.
JR ERROR_2 ; przeskocz do przodu, aby kontynuować w ERROR-2.
; -------------------------------------------------
; PUNKT WEJŚCIA DLA DRUKOWANIA ZNAKU W AKUMULATORZE
; -------------------------------------------------
; Drukowany jest znak w akumulatorze przy użyciu zapasowego zestawu rejestrów, zatem nie ma potrzeby zachowywania głównych rejestrów
; mikroprocesora.
; $0010 : 16
PRINT_A: AND A ; test zera, czyli spacji
JP NZ,PRINT_CH ; jeśli jest to znak różny od spacji, to drukujemy go w PRINT_CH.
JP PRINT_SP ; a spację drukujemy w PRINT_SP.
.BYTE $FF ; nieużywane
; ----------------------------------
; PUNKT WEJŚCIA DLA POBIERANIA ZNAKU
; ----------------------------------
; Pobrany zostaje znak adresowany zmienną systemową CH_ADD i jeśli jest on różny od spacji, to zostaje zwrócony. W przeciwnym razie
; zmienna CH_ADD jest zwiększana i pobrany zostanie kolejny znak aż do natrafienia na znak różny od spacji
; $0018 : 24
GET_CHAR: LD HL,(CH_ADD) ; pobieramy adres znaku do pary rejestrów HL
LD A,(HL) ; pobieramy do A znak o adresie z CH_ADD
; $001C : 28
TEST_SP: AND A ; sprawdzamy, czy jest spacją
RET NZ ; jeśli nie, to wracamy ze znakiem w A
NOP ; inaczej przechodzimy do następnej procedury - dwie instrukcje NOP wykonane
NOP ; zostają w 8 taktach
; ------------------------------------------
; PUNKT WEJŚCIA DLA POBRANIA KOLEJNEGO ZNAKU
; ------------------------------------------
; Adres znaku CH_ADD zostaje zwiększony o 1 i pobierany jest kolejny znak, który będzie zwrócony, jeśli nie jest spacją lub kursorem.
; W przeciwnym razie proces jest powtarzany.
; $0020 : 32
NEXT_CHAR: CALL CH_ADD_1 ; zwiększ CH_ADD i pobierz kolejny znak
JR TEST_SP ; ponownie dokonaj testu na spację
.BYTE $FF, $FF, $FF ; nieużywane
; --------------------------------------------------
; PUNKT WEJŚCIA DLA KALKULATORA ZMIENNOPRZECINKOWEGO
; --------------------------------------------------
; Następuje skok do rekurencyjnej procedury kalkulatora zmiennoprzecinkowego, który jest sterowany wewnętrznym językiem podobnym do
; FORTH'a i opartym na stosie. Za wywołaniem RST 28H następuje ciąg kodów bajtowych, które są poleceniami w języku kalkulatora.
; Kalkulator jest wykorzystywany do wyliczania wartości wszystkich funkcji arytmetycznych języka ZX81 BASIC.
; W pięciu pozostałych bajtach umieszczono procedurę obsługi rozkazu kalkulatora END_CALC, który kończy wykonywanie poleceń kalkulatora.
; $0028 : 40
FP_CALC: JP CALCULATE ; skocz bezpośrednio do procedury obsługi kalkulatora
; $002B : 43
END_CALC: POP AF ; usuń ze stosu adres powrotu do kalkulatora
EXX ; przełącz się na zapasowy zestaw rejestrów
EX (SP),HL ; jako adres powrotu wykorzystaj wskaźnik programu kalkulatora w HL'. Przy zakończeniu rekurencji poprzedni
; wskaźnik jest umieszczany w HL'
EXX ; przełącz na podstawowy zestaw rejestrów
RET ; powróć z obliczeń w kalkulatorze do programu
; ---------------------------------
; PUNKT WEJŚCIA TWORZENIA BC SPACJI
; ---------------------------------
; Wejście to wykorzystywane jest osiem razy do tworzenia w obszarze roboczym odpowiedniej liczby spacji, których liczba przekazywana
; jest w parze rejestrów BC.
; $0030 : 48
BC_SPACES: PUSH BC ; umieść liczbę spacji na stosie
LD HL,(E_LINE) ; pobierz do HL adres obszaru wiersza edycji ze zmiennej systemowej E_LINE
PUSH HL ; zachowaj ten adres na stosie
JP RESERVE ; kontynuuj w procedurze RESERVE.
; --------------------------
; PUNKT WEJŚCIA DLA PRZERWAŃ
; --------------------------
; Procedura obsługi przerwania w trybie 1 zajmuje się całkowicie tworzeniem obrazu telewizyjnego. Na ZX81 przerwania maskowane są
; włączone tylko w czasie wykonywania procedury obsługującej je. Ta procedura obsługi przerwań automatycznie wyłącza przerwania
; na samym początku i ostatnie przerwanie w kolejce kończy się zanim obsługa przerwań ponownie zostanie włączona. W pamięci ROM ZX81
; nie znajdziesz instrukcji DI wyłączającej obsługę przerwań maskowanych. Przerwanie maskowane jest wyzwalane, gdy bit 6 rejestru
; odświeżania mikroprocesora Z80 zmienia się ; z jedynki na 0. Dzieje się tak dlatego, iż linia adresowa A6 jest połączona z
; wejściem INT. W trakcie cyklu odświeżania na liniach adresowych A0-A8 umieszczana jest zawartość rejestru R. Z kolei mikroprocesor Z80
; testuje linię INT przy ostatnim cyklu wykonania instrukcji, gdy trwa jeszcze cykl odświeżania.
; Z80 zawsze będzie wykonywał instrukcję HALT (NEWLINE), gdy zdarzy się przerwanie. Instrukcja HALT w kółko wykonuje instrukcje NOP,
; lecz przy każdej z nich zawartość rejestru R jest zwiększana, podobnie jak przy każdej prostej instrukcji (młodsze 7 bitów jest
; zwiększane dwukrotnie dla instrukcji z przedrostkiem). Proces ten jest kontrolowany przez układ ULA - Sinclair Computer Logic Chip -
; wyprodukowany w firmie:
; Ferranti - ULA = Uncommitted Logic Array.
; Gdy wystąpi przerwanie w trybie 1 rejestr licznika rozkazów PC, który adresuje instrukcję za rozkazem NEWLINE/HALT w górnej połówce
; pamięci powyżej 32K, zostanie umieszczony na stosie maszynowym. Potrzebne są 193 przerwania do utworzenia końca 56-tej linii górnego
; marginesu oraz ; 192 linii środkowego obrazu TV, a chociaż każde przerwanie przerywa obsługę poprzedniego, nie wystąpi problem
; z rozrostem stosu maszynowego, ponieważ za każdym razem adresy powrotne są z niego usuwane.
; Licznik linii skanujących w rejestrze C zlicza w dół od 8 do 1 w czasie tworzenia każdego wiersza tekstu. Dla ostatniej linii
; marginesu górnego licznik w C jest ustawiany na 1.
; Czas wykonania kodu ma podstawowe znaczenie, ponieważ prawy margines, powrót strumienia elektronów oraz lewy margines zajmuje
; dokładnie 58 taktów zegara i przez tyle taktów wykonuje się ta procedura.
; $0038 : 56
INTERRUPT: DEC C ; (4) zmniejsz licznik linii skanujących w rejestrze C.
JP NZ,SCAN_LINE ; (10/10) jeśli C różne od 0, skocz naprzód do SCAN_LINE
POP HL ; (10) ustaw adres początku następnego wiersza w buforze wyświetlania
DEC B ; (4) zmniejsz licznik wierszy
RET Z ; (11/5) gdy obrazek jest skończony, powróć do L028B z wyłączonymi przerwaniami.
SET 3,C ; (8) załaduj licznik linii skanujących wartością 8.
; Uwaga: LD C,$8 ma 7 taktów zegara, co jest zbyt mało
; $0041 : 65
WAIT_INT: LD R,A ; (9) załaduj R początkową wartością $DD, która będzie zwiększana przy każdej instrukcji
EI ; (4) włącz obsługę przerwań. [ R ma teraz wartość $DE ].
JP (HL) ; (4) skocz do echa bufora wyświetlania w górnej połówce pamięci i wykonaj kody znaków $00 - $3F
; jako instrukcje NOP. Układ wideo w ZX81 potrafi odczytać te znaki i przy pomocy rejestru I umie
; zamienić matryce znaków z ROM na wiersze bajtów. Na końcu każdego wiersza zostanie napotkany
; kod NEWLINE/HALT zanim rejestr R zliczy do $FF. Jednakże to przejście z $FF na $80 wyzwala
; następne przerwanie. [R ma teraz wartość $DF]
; $0045 : 69
SCAN_LINE: POP DE ; (10) usuń adres za NEWLINE, ponieważ ten sam wiersz tekstu musi być "wykonany" 8 razy
RET Z ; (5) opóźnienie - instrukcja nigdy nie powoduje powrotu, ponieważ warunek jest zawsze fałszywy
JR WAIT_INT ; (12) z powrotem do WAIT_INT
; Uwaga. W komputerze z pamięcią mniejszą niż 4K RAM bufor wyświetlania będzie zwinięty, a powyższy mechanizm obsługuje zarówno pełny
; jak i zwinięty bufor. Przy pełnym buforze 32 znaki w wierszu jest traktowane jako instrukcje NOP, a rejestr odświeżania R zwiększy
; zawartość z $E0 (pierwszy znak) do $FF (ostatni znak) i przy następnej instrukcji HALT wystąpi przerwanie (R zmieni stan na $80).
; Przy zwiniętym buforze wyświetlania wykonywanie kodów zatrzyma się na instrukcji HALT, która cyklicznie rozpocznie wykonywanie NOP.
; Rejestr R będzie zwiększany, aż do osiągnięcia $FF, a następnie $80, co wyzwoli przerwanie. Działa to bezbłędnie dla wszystkich wierszy
; ekranu, które zawierają od 0 do 32 znaków zakończonych kodem NEWLINE/HALT. Jedna linia obrazu zajmuje zawsze 128 cykli zegara.
; ------------------------------
; PROCEDURA ZWIĘKSZANIA CH_ADD
; ------------------------------
; Procedura zwiększa zmienną systemową CH_ADD i kończy działanie, jeśli pod nowym adresem (CH_ADD) nie znajduje się znak kursora.
; W przeciwnym razie pobiera znak za kursorem. ZX81 umieszcza na pozycji kursora prawdziwy znak kursora - w przeciwieństwie do poprzedników
; i następców ZX81, gdzie stosowano jedynie wskaźnik.
; Na wyjściu mamy:
; A - pobrany znak
; HL - adres znaku
; $0049 : 73
CH_ADD_1: LD HL,(CH_ADD) ; pobierz zawartość zmiennej systemowej CH_ADD
; $004C : 76
TEMP_PTR1: INC HL ; zaadresuj następną pozycję
; $004D : 77
TEMP_PTR2: LD (CH_ADD),HL ; uaktualnij zmienną systemową CH_ADD
LD A,(HL) ; pobierz znak z nowej pozycji
CP $7F ; sprawdź, czy jest to znak kursora
RET NZ ; jeśli nie, powróć z procedury
JR TEMP_PTR1 ; cofnij się do TEMP_PTR1, aby pobrać następny znak
; ---------------------
; KONTYNUACJA 'ERROR_2'
; ---------------------
; Jest to kontynuacja punktu wejścia dla błędów ERROR_1. Jeśli błąd wystąpił w czasie wykonywania programu, to na stosie będzie odłożony
; adres numeru błędu, który należy wyświetlić, chyba że błąd wystąpił w trakcie wprowadzania danych. Jeśli błąd powstał w czasie
; sprawdzania składni, to na stosie będzie adres procedury edycji, a pozycja błędu pojawi się po ponownym wydruku zawartości spodu ekranu.
; $0056 : 86
ERROR_2: POP HL ; pobierz ze stosu adres powrotu, który wskazuje na bajt z kodem błędu, umieszczony tuż za rozkazem RST 08H.
LD L,(HL) ; pobierz kod błędu do L. HL nie jest już potrzebne
; $0058 : 88
ERROR_3: LD (IY+$00),L ; umieść kod błędu w zmiennej systemowej ERR_NR
LD SP,(ERR_SP) ; ustaw stos ze zmiennej ERR_SP
CALL SLOW_FAST ; wybierz tryb SLOW.
JP SET_MIN ; wyjdź do adresu na stosie poprzez procedurę SET_MIN.
.BYTE $FF ; nieużywane
; ------------------------------------------
; PROCEDURA OBSŁUGI PRZERWAŃ NIE MASKOWANYCH
; ------------------------------------------
; Techniczna sztuczka Jima Westwooda z wykorzystaniem przerwań niemaskowanych rozwiązała problem migania obrazu nękający użytkowników
; modelu ZX80, a dla komputera ZX81 podarowała tryb SLOW z jednoczesnymi obliczeniami oraz wyświetlaniem obrazu. Zwróć uwagę, iż dla tej
; funkcji oraz współdziałania w tworzeniu obrazu zarezerwowana została para rejestrów AF'. Przy zliczaniu linii obrazu telewizyjnego NMI
; nie wykorzystuje głównych rejestrów. Obwód elektroniczny dla generatora NMI jest zawarty wewnątrz układu ULA. Zliczanie w kierunku zera
; zajmuje jej 32 takty zegarowe.
; $0066 : 102
NMI: EX AF,AF' ; (4) przełącz się na kopię akumulatora dla procedury NMI
INC A ; (4) zwiększ zawartość licznika linii o 1
JP M,NMI_RET ; (10/10) jeśli wynik ujemny w kodzie U2, to skocz do NMI_RET, ponieważ będzie to część testu
; sprawdzającego, czy generacja NMI pracuje lub wartość pośrednia dla licznika pustych linii,
; zliczającego w przód na wartościach zanegowanych.
JR Z,NMI_CONT ; (12) naprzód do NMI_CONT, gdy licznik przekręcił się na 0
; Uwaga. Synchronizowanie NMI, gdy A zwiększane jest z zera na jeden zajmuje 7 taktów zegara dając w sumie 32 + 7 = 39 taktów na obieg
; procedury.
; $006D : 109
NMI_RET: EX AF,AF' ; (4) przełącz z licznika linii lub wyniku testu $80 na zwykły A
RET ; (10) powróć na chwilę do aplikacji użytkownika.
; To odgałęzienie jest wykonywane, gdy narysowana została 55 (lub 31) linia marginesu.
; $006F : 111
NMI_CONT: EX AF,AF' ; (4) przywróć główny akumulator.
PUSH AF ; (11) zapisz główne rejestru na stosie.
PUSH BC ; (11)
PUSH DE ; (11)
PUSH HL ; (11)
; następna procedura ustawiająca jest faktycznie potrzebna tylko po wygenerowaniu górnego zbioru pustych linii marginesu
LD HL,(D_FILE) ; (16) pobierz adres startu bufora obrazu ze zmiennej D_FILE wskazujący instrukcję
; NEWLINE/HALT umieszczoną na początku.
SET 7,H ; (8) przelicz adres na echo bufora w górnej połówce pamięci ponad 32K
HALT ; (1) HALT synchronizuje z NMI. Używa się go ze specjalnym obwodem połączonym
; z liniami HALT i WAIT mikroprocesora Z80 w celu wstrzymania na 1 takt zegara.
; ----------------------------------------------------------------------------
; Przerwanie NMI zostało wygenerowane - zaczynamy zliczanie. Strumień elektronów jest przy prawej krawędzi ekranu telewizyjnego.
; Najpierw obsługa NMI, podobna do instrukcji CALL = 17 taktów zegara.
; Teraz czas zużyty przez NMI na ścieżkę zero-jeden = 39 taktów.
; Powyższa instrukcja HALT = 1 takt.
; Dwie instrukcje poniżej = 19 taktów.
; Kod pod adresem 281H do instrukcji CALL włącznie = 43 takty.
; Wywołana procedura pod adresem 2B5H = 24 takty.
; -------------------------------------- ---
; W sumie wszystkie instrukcje Z80 = 143 takty.
; Tymczasem po stronie telewizora:
; Powrót poziomy strumienia elektronów = 15 taktów.
; Lewy margines o szerokości 8 znaków = 32 takty
; Tworzenie 75% linii skanowania z pierwszego NEWLINE = 96 takty
; --------------------------------------- ---
; 143 takty
; Ponieważ do czasu napotkania pierwszej instrukcji JP (HL), która wykona echo bufora wyświetlania, należy dodatkowo wyświetlić 8
; pozycji znakowych lewego marginesu, to rejestr odświeżania musi zawierać $F8. Idąc zatem wstecz i uwzględniając fakt, iż każda
; instrukcja zwiększa rejestr R o 1, dochodzimy do wniosku, iż rejestr R należy załadować wartością $F5.
OUT ($FD),A ; (11) zatrzymaj generator NMI.
JP (IX) ; (8) naprzód do 281H (po górnym marginesie) lub do 28FH
; **********************
; ** TABLICE KLAWISZY **
; **********************
; ------------------------------
; KODY ZNAKÓW BEZ KLAWISZA SHIFT
; ------------------------------
; $007E : 126
K_UNSHIFT:.BYTE $3F ; Z
.BYTE $3D ; X
.BYTE $28 ; C
.BYTE $3B ; V
.BYTE $26 ; A
.BYTE $38 ; S
.BYTE $29 ; D
.BYTE $2B ; F
.BYTE $2C ; G
.BYTE $36 ; Q
.BYTE $3C ; W
.BYTE $2A ; E
.BYTE $37 ; R
.BYTE $39 ; T
.BYTE $1D ; 1
.BYTE $1E ; 2
.BYTE $1F ; 3
.BYTE $20 ; 4
.BYTE $21 ; 5
.BYTE $1C ; 0
.BYTE $25 ; 9
.BYTE $24 ; 8
.BYTE $23 ; 7
.BYTE $22 ; 6
.BYTE $35 ; P
.BYTE $34 ; O
.BYTE $2E ; I
.BYTE $3A ; U
.BYTE $3E ; Y
.BYTE $76 ; NEWLINE
.BYTE $31 ; L
.BYTE $30 ; K
.BYTE $2F ; J
.BYTE $2D ; H
.BYTE $00 ; SPACE
.BYTE $1B ; .
.BYTE $32 ; M
.BYTE $33 ; N
.BYTE $27 ; B
; -----------------------------
; KODY ZNAKÓW Z KLAWISZEM SHIFT
; -----------------------------
; $00A5 : 165
K_SHIFT: .BYTE $0E ; :
.BYTE $19 ; ;
.BYTE $0F ; ?
.BYTE $18 ; /
.BYTE $E3 ; STOP
.BYTE $E1 ; LPRINT
.BYTE $E4 ; SLOW
.BYTE $E5 ; FAST
.BYTE $E2 ; LLIST
.BYTE $C0 ; ""
.BYTE $D9 ; OR
.BYTE $E0 ; STEP
.BYTE $DB ; <=
.BYTE $DD ; <>
.BYTE $75 ; EDIT
.BYTE $DA ; AND
.BYTE $DE ; THEN
.BYTE $DF ; TO
.BYTE $72 ; kursor w lewo
.BYTE $77 ; RUBOUT
.BYTE $74 ; GRAPHICS
.BYTE $73 ; kursor w prawo
.BYTE $70 ; kursor w górę
.BYTE $71 ; kursor w dół
.BYTE $0B ; "
.BYTE $11 ; )
.BYTE $10 ; (
.BYTE $0D ; $
.BYTE $DC ; >=
.BYTE $79 ; FUNCTION
.BYTE $14 ; =
.BYTE $15 ; +
.BYTE $16 ; -
.BYTE $D8 ; **
.BYTE $0C ; L
.BYTE $1A ; ,
.BYTE $12 ; >
.BYTE $13 ; <
.BYTE $17 ; *
; --------------------
; KODY ZNAKOWE FUNKCJI
; --------------------
; $00CC : 204
K_FUNCT: .BYTE $CD ; LN
.BYTE $CE ; EXP
.BYTE $C1 ; AT
.BYTE $78 ; KL
.BYTE $CA ; ASN
.BYTE $CB ; ACS
.BYTE $CC ; ATN
.BYTE $D1 ; SGN
.BYTE $D2 ; ABS
.BYTE $C7 ; SIN
.BYTE $C8 ; COS
.BYTE $C9 ; TAN
.BYTE $CF ; INT
.BYTE $40 ; RND
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $C2 ; TAB
.BYTE $D3 ; PEEK
.BYTE $C4 ; CODE
.BYTE $D6 ; CHR$
.BYTE $D5 ; STR$
.BYTE $78 ; KL
.BYTE $D4 ; USR
.BYTE $C6 ; LEN
.BYTE $C5 ; VAL
.BYTE $D0 ; SQR
.BYTE $78 ; KL
.BYTE $78 ; KL
.BYTE $42 ; PI
.BYTE $D7 ; NOT
.BYTE $41 ; INKEY$
; --------------------------
; KODY ZNAKÓW 'GRAFICZNYCH'
; --------------------------
; $00F3 : 243
K_GRAPH: .BYTE $08 ; grafika
.BYTE $0A ; grafika
.BYTE $09 ; grafika
.BYTE $8A ; grafika
.BYTE $89 ; grafika
.BYTE $81 ; grafika
.BYTE $82 ; grafika
.BYTE $07 ; grafika
.BYTE $84 ; grafika
.BYTE $06 ; grafika
.BYTE $01 ; grafika
.BYTE $02 ; grafika
.BYTE $87 ; grafika
.BYTE $04 ; grafika
.BYTE $05 ; grafika
.BYTE $77 ; RUBOUT
.BYTE $78 ; KL
.BYTE $85 ; grafika
.BYTE $03 ; grafika
.BYTE $83 ; grafika
.BYTE $8B ; grafika
.BYTE $91 ; negatyw )
.BYTE $90 ; negatyw (
.BYTE $8D ; negatyw $
.BYTE $86 ; grafika
.BYTE $78 ; KL
.BYTE $92 ; negatyw >
.BYTE $95 ; negatyw +
.BYTE $96 ; negatyw -
.BYTE $88 ; grafika
; --------------
; TABELE 'NAZW'
; --------------
; $0111 : 273
TOKENST: .BYTE $0F+$80 ; '?'+$80
.BYTE $0B,$0B+$80 ; ""
.BYTE $26,$39+$80 ; AT
.BYTE $39,$26,$27+$80 ; TAB
.BYTE $0F+$80 ; '?'+$80
.BYTE $28,$34,$29,$2A+$80 ; CODE
.BYTE $3B,$26,$31+$80 ; VAL
.BYTE $31,$2A,$33+$80 ; LEN
.BYTE $38,$2E,$33+$80 ; SIN
.BYTE $28,$34,$38+$80 ; COS
.BYTE $39,$26,$33+$80 ; TAN
.BYTE $26,$38,$33+$80 ; ASN
.BYTE $26,$28,$38+$80 ; ACS
.BYTE $26,$39,$33+$80 ; ATN
.BYTE $31,$33+$80 ; LN
.BYTE $2A,$3D,$35+$80 ; EXP
.BYTE $2E,$33,$39+$80 ; INT
.BYTE $38,$36,$37+$80 ; SQR
.BYTE $38,$2C,$33+$80 ; SGN
.BYTE $26,$27,$38+$80 ; ABS
.BYTE $35,$2A,$2A,$30+$80 ; PEEK
.BYTE $3A,$38,$37+$80 ; USR
.BYTE $38,$39,$37,$0D+$80 ; STR$
.BYTE $28,$2D,$37,$0D+$80 ; CHR$
.BYTE $33,$34,$39+$80 ; NOT
.BYTE $17,$17+$80 ; **
.BYTE $34,$37+$80 ; OR
.BYTE $26,$33,$29+$80 ; AND
.BYTE $13,$14+$80 ; <=
.BYTE $12,$14+$80 ; >=
.BYTE $13,$12+$80 ; <>
.BYTE $39,$2D,$2A,$33+$80 ; THEN
.BYTE $39,$34+$80 ; TO
.BYTE $38,$39,$2A,$35+$80 ; STEP
.BYTE $31,$35,$37,$2E,$33,$39+$80 ; LPRINT
.BYTE $31,$31,$2E,$38,$39+$80 ; LLIST
.BYTE $38,$39,$34,$35+$80 ; STOP
.BYTE $38,$31,$34,$3C+$80 ; SLOW
.BYTE $2B,$26,$38,$39+$80 ; FAST
.BYTE $33,$2A,$3C+$80 ; NEW
.BYTE $38,$28,$37,$34,$31,$31+$80 ; SCROLL
.BYTE $28,$34,$33,$39+$80 ; CONT
.BYTE $29,$2E,$32+$80 ; DIM
.BYTE $37,$2A,$32+$80 ; REM
.BYTE $2B,$34,$37+$80 ; FOR
.BYTE $2C,$34,$39,$34+$80 ; GOTO
.BYTE $2C,$34,$38,$3A,$27+$80 ; GOSUB
.BYTE $2E,$33,$35,$3A,$39+$80 ; INPUT
.BYTE $31,$34,$26,$29+$80 ; LOAD
.BYTE $31,$2E,$38,$39+$80 ; LIST
.BYTE $31,$2A,$39+$80 ; LET
.BYTE $35,$26,$3A,$38,$2A+$80 ; PAUSE
.BYTE $33,$2A,$3D,$39+$80 ; NEXT
.BYTE $35,$34,$30,$2A+$80 ; POKE
.BYTE $35,$37,$2E,$33,$39+$80 ; PRINT
.BYTE $35,$31,$34,$39+$80 ; PLOT
.BYTE $37,$3A,$33+$80 ; RUN
.BYTE $38,$26,$3B,$2A+$80 ; SAVE
.BYTE $37,$26,$33,$29+$80 ; RAND
.BYTE $2E,$2B+$80 ; IF
.BYTE $28,$31,$38+$80 ; CLS
.BYTE $3A,$33,$35,$31,$34,$39+$80 ; UNPLOT
.BYTE $28,$31,$2A,$26,$37+$80 ; CLEAR
.BYTE $37,$2A,$39,$3A,$37,$33+$80 ; RETURN
.BYTE $28,$34,$35,$3E+$80 ; COPY
.BYTE $37,$33,$29+$80 ; RND
.BYTE $2E,$33,$30,$2A,$3E,$0D+$80 ; INKEY$
.BYTE $35,$2E+$80 ; PI
; -----------------------------------------
; PROCEDURA 'UAKTUALNIANIA ODCZYTU-ZAPISU'
; -----------------------------------------
;
; HL jest zwiększane aż do momentu, gdy zrówna się z wartością przechowywaną w E_LINE
; $01FC : 508
LOAD_SAVE: INC HL ; zwiększ HL o 1
EX DE,HL ; przenieś HL do DE, chwilowo
LD HL,(E_LINE) ; pobierz adres wiersza edycyjnego do HL
SCF ; ustaw znacznik przeniesienia
SBC HL,DE ; porównaj HL z E_LINE, wynik w HL nas nie interesuje
EX DE,HL ; przywróć HL zwiększone o 1 z DE
RET NC ; wróć, jeśli należy załadować/zapisać więcej bajtów HL < E_LINE
POP HL ; inaczej usuń ze stosu adres powrotu
; -------------------------
; PROCEDURY 'WYŚWIETLANIA'
; ------------------------
;
; Sprawdzenie, w jakim trybie pracuje aktualnie komputer: FAST czy SLOW.
; Testowany jest znacznik SLOW - bit nr 6 zmiennej systemowej CDFLAG. Procedura kończy się powrotem, jeśli program pracuje
; w trybie FAST lub gdy obraz dla trybu SLOW jest niedostępny.
; $0207 : 519
SLOW_FAST: LD HL,CDFLAG ; Pobierz do HL adres zmiennej CDFLAG.
LD A,(HL) ; Załaduj jej zawartość do akumulatora.
RLA ; przesuń bit nr 6 na pozycję nr 7.
XOR (HL) ; bit 7 <- bit 6 xor bit 7
RLA ; umieść bit nr 7 w znaczniku przeniesienia.
RET NC ; jeśli bity były takie same, to powróć.
; Teraz sprawdzamy, czy rzeczywiście mamy do czynienia z ZX81 lub, czy jest to ZX80 z nowym ROM'em. Standardowy ZX80 nie
; posiadał generatora NMI.
LD A,$7F ; Załaduj do akumulatora %011111111
EX AF,AF' ; zachowaj to w AF'
LD B,$11 ; licznik, po którym powinno pojawić się przerwanie NMI, jeśli mamy do czynienia z ZX81.
OUT ($FE),A ; uruchom generator NMI.
; Uwaga: jeśli to jest ZX81, to procedura obsługi NMI powinna zwiększyć AF'
; $0216 : 534
LOOP_11: DJNZ LOOP_11 ; pętla, w którą wskoczy NMI = 16*13 cykli + 8 = 216 cykli.
OUT ($FD),A ; wyłącz generator NMI.
EX AF,AF' ; odtwórz wartość AF'.
RLA ; testuj bit 7 poprzedniego AF'
JR NC,NO_SLOW ; skocz naprzód do NO_SLOW, jeśli bit 7 jest wciąż 0
; Jeśli AF' zostało zwiększone, to generator NMI działa i tryb SLOW może być ustawiony.
SET 7,(HL) ; oznacza tryb SLOW - liczenie i wyświetlanie.
PUSH AF ; * zapisz główne rejestry
PUSH BC ; **
PUSH DE ; ***
PUSH HL ; ****
JR DISPLAY_1 ; przejdź naprzód do DISPLAY_1.
; $0226 : 550
NO_SLOW: RES 6,(HL) ; skasuj bit 6 zmiennej CDFLAG.
RET
; --------------------------
; GŁÓWNA PĘTLA WYŚWIETLANIA
; --------------------------
; Ta procedura jest wykonywana jeden raz dla każdej wyświetlanej ramki. Najpierw pobierany jest i zmniejszany o 1 licznik ramek.
; Gdy licznik osiągnie zero, następuje powrót.
; $0229 : 553
DISPLAY_1: LD HL,(FRAMES) ; pobierz 2-bajtową zmienną systemową FRAMES.
DEC HL ; zmniejsz licznik ramek.
; $022D : 557
DISPLAY_P: LD A,$7F ; przygotuj maskę
AND H ; wybierz bity 6-0 z H.
OR L ; połącz je z bitami L.
LD A,H ; przeładuj A wszystkimi bitami z H dla testu PAUSE. Poprzednie dwa rozkazy ustawiły znacznik Z.
; Uwaga: oba skoki muszą zająć dokładnie ten sam czas.
JR NZ,ANOTHER ; (12/7) naprzód do ANOTHER, jeśli bity 14-0 są niezerowe
RLA ; (4) testuj bit 15 zmiennej FRAMES.
JR OVER_NC ; (12) naprzód do OVER_NC z wynikiem
; $0237 : 567
ANOTHER: LD B,(HL) ; (7) Uwaga: wprowadza jedynie opóźnienie.
SCF ; (4) ustaw znacznik przeniesienia.
; Uwaga: skok do tego miejsca zajmuje albo (12)(7)(4) cykli, albo (7)(4)(12) cykli.
; $0239 : 569
OVER_NC: LD H,A ; (4) ustaw H na zero
LD (FRAMES),HL ; (16) uaktualnij zmienną systemową FRAMES
RET NC ; (11/5) wróć, jeśli FRAMES jest używane przez PAUSE
; $023E : 574
DISPLAY_2: CALL KEYBOARD ; procedura KEYBOARD wstawia rząd klawisza do H i kolumnę do L. Odczyt portów również
; rozpoczyna impuls synchronizacji ramki TV.
LD BC,(LAST_K) ; pobierz wartość ostatniego klawisza z LAST_K
LD (LAST_K),HL ; uaktualnij LAST_K nową wartością.
LD A,B ; do A wpisz poprzednią kolumnę - będzie $FF, jeśli nie naciśnięto żadnego klawisza
ADD A,$02 ; dodanie 2 ustawi przeniesienie, jeśli nie było klawisza.
SBC HL,BC ; odejmij z przeniesieniem te dwie wartości klawiszowe.
; Jeśli w obu był ten sam klawisz, to HL otrzyma wartość 0.
LD A,(DBOUNC) ; pobierz zmienną systemową DEBOUNCE
OR H ; i utwórz sumę logiczną z obu bajtami różnicy
OR L ; ustawiając znacznik zera dla nadchodzącego skoku.
LD E,B ; przenieś wartość kolumny do E
LD B,$0B ; a do B wstaw jedenaście
LD HL,CDFLAG ; W HL ustaw adres zmiennej systemowej CDFLAG
RES 0,(HL) ; wyzeruj bit 0 zmiennej CDFLAG
JR NZ,NO_KEY ; przeskocz naprzód, jeśli DEBOUNCE/różnicę >0 do NO-KEY
BIT 7,(HL) ; sprawdź bit liczenia i wyświetlania w CDFLAG
SET 0,(HL) ; ustaw na 1 bit 0 zmiennej CDFLAG.
RET Z ; wróć, jeśli bit 7 oznaczał tryb fast.
DEC B ; (4) zmniejsz licznik.
NOP ; (4) opóźnienie - 4 cykle zegara
SCF ; (4) ustaw znacznik przeniesienia
; $0264 : 612
NO_KEY: LD HL,DBOUNC ; pobierz adres zmiennej DEBOUNCE
CCF ; zaneguj znacznik przeniesienia
RL B ; obróć w lewo rejestr B pobierając bit przeniesienia C<-76543210<-C
; $026A : 618
LOOP_B: DJNZ LOOP_B ; pętla dopóki B>0 do LOOP-B
LD B,(HL) ; pobierz wartość DEBOUNCE do B
LD A,E ; przenieś wartość kolumny do A
CP $FE
SBC A,A
LD B,$1F
OR (HL)
AND B
RRA
LD (HL),A ; uaktualnij DEBOUNCE
OUT ($FF),A ; zakończ impuls synchronizacji ramki TV.
LD HL,(D_FILE) ; (12) ustaw w HL adres bufora wyświetlania z D_FILE
SET 7,H ; (8) ustaw bit 15, aby adresować echo bufora w górnej połówce RAM.
CALL DISPLAY_3 ; (17) procedura DISPLAY-3 wyświetla górny zbiór pustych linii
; -------------------
; PROCEDURA 'VIDEO-1'
; -------------------
; $0281 : 641
R_IX_1: LD A,R ; (9) nieszkodliwe opóźnienie lub coś bardzo sprytnego?
LD BC,$1901 ; (10) 25 wierszy, 1 linia skanująca w pierwszym.
LD A,$F5 ; (7) Ta wartość zostanie załadowana do R i zapewni, iż cykl rozpocznie się we właściwej
; części ekranu po pozycji znaku nr 32
CALL DISPLAY_5 ; (17) procedura DISPLAY-5 kończy bieżącą pustą linię a następnie tworzy obraz zawartości
; bufora wyświetlania wykorzystując przerwania INT. Ostatnie przerwanie powraca do następnego adresu.
; $028B : 651
L028B DEC HL ; ustaw HL na ostatnie NEWLINE/HALT.
CALL DISPLAY_3 ; procedura DISPLAY-3 wyświetla dolny zbiór pustych linii
; $028F : 655
R_IX_2: JP DISPLAY_1 ; wróć do DISPLAY-1
; --------------------------------------
; PROCEDURA 'WYŚWIETLANIA PUSTYCH LINII'
; --------------------------------------
; Ten podprogram wywoływany jest dwukrotnie (zobacz powyżej) do utworzenia najpierw pustych linii u góry obrazu telewizyjnego,
; a następnie pustych linii na spodzie obrazu.
; $0292 : 658
DISPLAY_3: POP IX ; pobierz adres powrotny do rejestru IX. będzie to albo L0281, albo L028F - zobacz wyżej.
LD C,(IY+$28) ; załaduj C wartością stałej systemowej MARGIN.
BIT 7,(IY+$3B) ; sprawdź w CDFLAG, czy działa tryb wyświetlania i liczenia.
JR Z,DISPLAY_4 ; w trybie FAST naprzód do DISPLAY-4
LD A,C ; przenieś MARGIN do A - 31d lub 55d.
NEG ; zaneguj
INC A
EX AF,AF' ; umieść zanegowaną liczbę pustych linii w A'
OUT ($FE),A ; włącz generator NMI.
POP HL ; ****
POP DE ; ***
POP BC ; **
POP AF ; * Odtwórz główne rejestry
RET ; powróć - koniec przerwania. Powrót nastąpi do programu użytkownika - BASIC lub kod maszynowy,
; który będzie przerywany przy każdym NMI.
; ----------------------
; PROCEDURA 'TRYBU FAST'
; ----------------------
; $02A9 : 681
DISPLAY_4: LD A,$FC ; (7) załaduj A z wartością opóźnienia dla R
LD B,$01 ; (7) tylko jeden wiersz.
CALL DISPLAY_5 ; (17) procedura DISPLAY-5
DEC HL ; (6) wskaż z powrotem na HALT.
EX (SP),HL ; (19) Niegroźne opóźnienie, jeśli stosowane w parze.
EX (SP),HL ; (19) Niegroźne opóźnienie.
JP (IX) ; (8) do L0281 lub L028F
; ---------------------
; PROCEDURA 'DISPLAY-5'
; ---------------------
; Ta procedura jest wywoływana z trybów SLOW i FAST do utworzenia środka obrazu TV. W trybie SLOW rejestr R jest zwiększany przy
; każdej instrukcji do wartości $F7 przy zakończeniu procedury. W trybie FAST wartość końcowa będzie równa $FF, a przerwanie pojawi
; się, gdy tylko Licznik Rozkazów osiągnie instrukcję HALT. (24 cykle zegara)
; $02B5 : 693
DISPLAY_5: LD R,A ; (9) Załaduj R z A. R = wolno: $F5 szybko: $FC
LD A,$DD ; (7) załaduj przyszłą wartość R. $F6 $FD
EI ; (4) Włącz przerwania $F7 $FE
JP (HL) ; (4) skocz do echa obrazu. $F8 $FF
; -------------------------------------
; PROCEDURA 'PRZESZUKIWANIA KLAWIATURY'
; -------------------------------------
; Klawiatura jest czytana w czasie okresu synchronizacji pionowej, gdy nie jest wyświetlany obraz. Odczyt portu z wyzerowanym najmłodszym
; bitem adresu, np. $FE rozpoczyna impuls synchronizacji pionowej.
; $02BB : 699
KEYBOARD: LD HL,$FFFF ; (16) przygotuj bufor na przyjęcie klawisza
LD BC,$FEFE ; (20) ustaw BC na port $FEFE. Rejestr B ze swoim wyzerowanym jednym bitem pełni rolę licznika do 8.
IN A,(C) ; (11) odczytaj port - na magistrali adresowej są umieszczane wszystkie 16 bitów. Start impulsu VSYNC.
OR $01 ; (7) ustaw prawy bit, aby ignorować klawisz SHIFT.
; $02C5 : 709
EACH_LINE: OR $E0 ; [7] OR %11100000
LD D,A ; [4] przenieś do D.
CPL ; [4] zaneguj - teraz znaczą tylko bity 4-0.
CP $01 ; [7] ustawia przeniesienie, jeśli A zawiera zero.
SBC A,A ; [4] $FF, jeśli $00, inaczej zero.
OR B ; [7] $FF lub port FE, FD, FB....
AND L ; [4] O ile nie naciśnięto kilku klawiszy, L wciąż będzie $FF. Jeśli naciśnięto więcej niż jeden klawisz,
; to A ma teraz nieważną wartość.
LD L,A ; [4] przenieś do L.
; teraz rozważ identyfikator kolumny.
LD A,H ; [4] będzie równe $FF, jeśli nie było poprzednich klawiszy.
AND D ; [4] 111xxxxx
LD H,A ; [4] przenieś A do H
; ponieważ tylko jeden klawisz może być naciśnięty, H będzie zawierało, jeśli ważne, jedną z wartości 11111110, 11111101, 11111011,
; 11110111, 11101111 od zewnętrznej kolumny, powiedzmy Q, do wewnętrznej kolumny, powiedzmy T.
RLC B ; [8] obróć licznik 8 / adres portu. ustawia przeniesienie, jeśli więcej do zrobienia.
IN A,(C) ; [10] odczytaj kolejną połówkę wiersza. Tym razem wszystkie pięć bitów.
JR C,EACH_LINE ; [12](7) kontynuuj w pętli, aż będzie zrobione
; Za drugim razem ostatnim odczytywanym wierszem jest SHIFT,Z,X,C,V.
RRA ; (4) sprawdź klawisz SHIFT - przeniesienie zostanie wyzerowane jeśli ten klawisz jest wciśnięty.
RL H ; (8) obróć w lewo H biorąc przeniesienie, co daje wartości kolumnowe -
; $FD, $FB, $F7, $EF, $DF.
; lub $FC, $FA, $F6, $EE, $DE z SHIFT.
; Teraz H identyfikuje kolumnę, a L identyfikuje wiersz w matrycy klawiatury.
; Teraz jest dobry moment na sprawdzenie, czy jest to komputer brytyjski lub amerykański. Wersja amerykańska posiada dodatkową diodę,
; która zeruje bit 6 odczytany z portu.
RLA ; (4) skompensuj test na klawisz SHIFT.
RLA ; (4) wyprowadź obrotem bit 7.
RLA ; (4) testuj bit 6.
SBC A,A ; (4) $FF lub $00 {USA}
AND $18 ; (7) $18 lub $00
ADD A,$1F ; (7) $37 lub $1F
; wynikiem jest albo 31 (USA), albo 55 (WB) pustych linii ponad i poniżej obrazu TV.
LD (MARGIN),A ; (13) uaktualnij zmienną systemową MARGIN
RET ; (10) powróć
; ---------------------------------
; PROCEDURA 'USTAWIANIA TRYBU FAST'
; ---------------------------------
; $02E7 : 743
SET_FAST: BIT 7,(IY+$3B) ; zmienna systemowa CDFLAG
RET Z
HALT ; Czekaj na przerwanie
OUT ($FD),A
RES 7,(IY+$3B) ; zmienna systemowa CDFLAG
RET
; ----------
; 'RAPORT-F'
; ----------
; $02F4 : 756
REPORT_F: RST 08H ; ERROR-1
.BYTE $0E ; Raport błędu: Brak nazwy programu.
; ------------------------
; PROCEDURA 'ROZKAZU SAVE'
; ------------------------
; $02F6 : 758
SAVE: CALL NAME
JR C,REPORT_F ; jeśli pusta nazwa, wróć do REPORT-F powyżej.
EX DE,HL
LD DE,$12CB ; pięć sekund czasu
; $02FF : 767
HEADER: CALL BREAK_1
JR NC,BREAK_2 ; do BREAK-2
; $0304 : 772
DELAY_1: DJNZ DELAY_1 ; pętla opóźniająca
DEC DE
LD A,D
OR E
JR NZ,HEADER ; wstecz do HEADER dla opóźnienia
; $030B : 779
OUT_NAME: CALL OUT_BYTE
BIT 7,(HL) ; test na zanegowany bit.
INC HL ; adres następnego znaku nazwy.
JR Z,OUT_NAME ; jęli brak zanegowanego bitu, wróć do OUT-NAME
; teraz zacznij zapisywać zmienne systemowe.
LD HL,VERSN ; ustaw start obszaru na VERSN, zachowując przy okazji RAMTOP i inne.
; $0316 : 790
OUT_PROG: CALL OUT_BYTE
CALL LOAD_SAVE
JR OUT_PROG ; pętla do OUT-PROG
; --------------------
; PROCEDURA 'OUT-BYTE'
; --------------------
; Ta procedura zapisuje bajt danych bit po bicie na domowy magnetofon.
; $031E : 798
OUT_BYTE: LD E,(HL) ; pobierz bajt do zapisu.
SCF ; ustaw znacznik przeniesienia - jako marker.
; $0320 : 800
EACH_BIT: RL E ; C < 76543210 < C
RET Z ; wróć, gdy przejdzie bit markera
SBC A,A ; $FF jeśli bit ustawiony lub $00 bez przeniesienia
AND $05 ; $05 $00
ADD A,$04 ; $09 $04
LD C,A ; przenieś licznik czasu do C. bit ustawiony ma dłuższy impuls niż bit wyzerowany.
; $0329 : 809
PULSES: OUT ($FF),A ; impuls do magnetofonu.
LD B,$23 ; ustaw stałą czasową
; $032D : 813
DELAY_2: DJNZ DELAY_2 ; samozapętlenie do DELAY-2
CALL BREAK_1 ; procedura BREAK-1 testuje klawisz BREAK.
; $0332 : 818
BREAK_2: JR NC,REPORT_D ; jeśli przerwa, to naprzód do REPORT-D
LD B,$1E ; ustaw wartość opóźnienia.
; $0336 : 822
DELAY_3: DJNZ DELAY_3 ; samozapętlenie do DELAY-3
DEC C ; zmniejsz licznik
JR NZ,PULSES ; wróć w pętli do PULSES
; $033B : 827
DELAY_4: AND A ; wyzeruj przeniesienie dla następnego testu bitu.
DJNZ DELAY_4 ; samozapętlenie do DELAY-4 (B jest zero - 256)
JR EACH_BIT ; wróć w pętli do EACH-BIT
; ------------------------
; PROCEDURA 'ROZKAZU LOAD'
; ------------------------
; $0340 : 832
LOAD: CALL NAME
; DE wskazuje początek nazwy w RAM.
RL D ; pobierz przeniesienie
RRC D ; teraz przeniesienie w bicie 7.
; $0347 : 839
NEXT_PROG: CALL IN_BYTE
JR NEXT_PROG ; pętla do NEXT-PROG
; -------------------
; PROCEDURA 'IN-BYTE'
; -------------------
; $034C : 844
IN_BYTE: LD C,$01 ; przygotuj licznik do ośmiu 00000001.
; $034E : 846
NEXT_BIT: LD B,$00 ; ustaw licznik na 256
; $0350 : 848
BREAK_3: LD A,$7F ; odczytaj wiersz klawiatury
IN A,($FE) ; z klawiszem SPACE.
OUT ($FF),A ; wyślij sygnał na ekran.
RRA ; sprawdź naciśnięcie SPACE.
JR NC,BREAK_4 ; jeśli tak, skocz naprzód do BREAK-4
RLA ; odwróć powyższy obrót bitów
RLA ; sprawdź bit taśmy.
JR C,GET_BIT ; jeśli ustawiony, idź naprzód do GET-BIT
DJNZ BREAK_3 ; wróć w pętli do BREAK-3
POP AF ; usuń adres powrotu.
CP D
; $0361 : 865
RESTART: JP NC,INITIAL ; jeśli D jest zero, skocz naprzód do INITIAL, aby zresetować system jeśli sygnał z taśmy
; przekroczył dozwolony czas, np. przy zatrzymaniu magnetofonu. Nie wystarczy zwykły raport błędów,
; ponieważ część zmiennych systemowych została nadpisana.
LD H,D ; w przeciwnym razie przenieś start nazwy
LD L,E ; do rejestru HL
; $0366 : 870
IN_NAME: CALL IN_BYTE ; procedura IN-BYTE jest pewnego rodzaju rekursją dla części nazwy. Odebrany bajt jest w C.
BIT 7,D ; czy nazwa jest pustym łańcuchem?
LD A,C ; przenieś bit do A.
JR NZ,MATCHING ; przy pustym łańcuchu naprzód do MATCHING
CP (HL) ; inaczej porównaj z łańcuchem w pamięci.
JR NZ,NEXT_PROG ; wróć przy niezgodności do NEXT-PROG (na pozór koniec procedury, lecz adres powrotny został usunięty).
; $0371 : 881
MATCHING: INC HL ; adresuj następny znak nazwy
RLA ; test na zanegowany bit.
JR NC,IN_NAME ; jeśli go brak, wróć do IN-NAME
; nazwa w całości zgadza się. Przejdź do odczytu danych, ale najpierw zwiększ starszy bajt E_LINE, która jest jedną ze zmiennych
; systemowych do załadowania. Ponieważ młodszy bajt jest odczytywany przed starszym, możliwe jest, że w fazie pośredniej błędna wartość
; mogłaby spowodować zbyt wczesne zakończenie ładowania - zobacz na test w LOAD/SAVE.
INC (IY+$15) ; zwiększ starszy bajt zmiennej systemowej E_LINE.
LD HL,VERSN ; rozpocznij ładowanie od zmiennej systemowej VERSN.
; $037B : 891
IN_PROG: LD D,B ; ustaw D na zero jako znacznik.
CALL IN_BYTE ; procedura IN-BYTE ładuje bajt
LD (HL),C ; wstaw zebrany bajt do pamięci.
CALL LOAD_SAVE
JR IN_PROG ; wróć w pętli do IN-PROG
; to odgałęzienie zbiera pełny bajt przed zwykłym opuszczeniem procedury IN-BYTE.
; $0385 : 901
GET_BIT: PUSH DE ; zachowaj
LD E,$94 ; wartość licznika czasu.
; $0388 : 904
TRAILER: LD B,$1A ; licznik na dwadzieścia sześć.
; $038A : 906
COUNTER: DEC E ; zmniejsz stan licznika.
IN A,($FE) ; wczytaj bit z magnetofonu
RLA
BIT 7,E
LD A,E
JR C,TRAILER ; z ustawionym przeniesieniem wróć do TRAILER
DJNZ COUNTER ; inaczej do COUNTER
POP DE
JR NZ,BIT_DONE ; do BIT-DONE
CP $56
JR NC,NEXT_BIT ; do NEXT-BIT
; $039C : 924
BIT_DONE: CCF ; zaneguj znacznik przeniesienia
RL C
JR NC,NEXT_BIT ; do NEXT-BIT
RET ; powróć z pełnym bajtem.
; jeśli w trakcie odczytu danych było przerwanie, wykonaj reset. Jeśli przerwanie w czasie oczekiwania na program z taśmy,
; to przerwa dozwolona.
; $03A2 : 930
BREAK_4: LD A,D ; przenieś znacznik do A.
AND A ; test na zero.
JR Z,RESTART ; jeśli tak, wróć do RESTART
; $03A6 : 934
REPORT_D: RST 08H ; ERROR-1
.BYTE $0C ; Raport błędu: PRZERWANIE - CONT powtarza
; --------------------------
; PROCEDURA 'NAZWY PROGRAMU'
; --------------------------
; Nazwa jest sprawdzana na raport C. Wybrany zostaje tryb FAST oraz ostatnia litera nazwy jest ustawiana w negatywie.
; $03A8 : 936
NAME: CALL SCANNING
LD A,(FLAGS)
ADD A,A
JP M,REPORT_C
POP HL
RET NC
PUSH HL
CALL SET_FAST
CALL STK_FETCH
LD H,D
LD L,E
DEC C
RET M
ADD HL,BC
SET 7,(HL) ; ustaw bit 7 ostatniego znaku nazwy - znak w negatywie oznacza koniec łańcucha nazwy
RET
; -----------------------
; PROCEDURA ROZKAZU 'NEW'
; -----------------------
; $03C3 : 963
NEW: CALL SET_FAST
LD BC,(RAMTOP) ; pobierz zawartość zmiennej systemowej RAMTOP
DEC BC ; ustaw na ostatni bajt systemowy
; ---------------------------
; PROCEDURA 'SPRAWDZANIA RAM'
; ---------------------------
; $03CB : 971
RAM_CHECK: LD H,B
LD L,C ; przenieś BC do HL
LD A,$3F
; $03CF : 975
RAM_FILL: LD (HL),$02 ; wypełnij pamięć liczbą $02
DEC HL
CP H ; sprawdź, czy cała pamięć już wypełniona
JR NZ,RAM_FILL ; jeśli nie, wypełniaj dalej
; $03D5 : 981
RAM_READ: AND A ; wyzeruj przeniesienie
SBC HL,BC
ADD HL,BC
INC HL
JR NC,SET_TOP
DEC (HL)
JR Z,SET_TOP
DEC (HL)
JR Z,RAM_READ
; $03E2 : 994
SET_TOP: LD (RAMTOP),HL ; ustaw zmienną systemową RAMTOP na pierwszy bajt ponad obszarem systemowym BASICU.
; -------------------------
; PROCEDURA 'INICJALIZACJI'
; -------------------------
; $03E5 : 997
INITIAL: LD HL,(RAMTOP) ; pobierz zmienną systemową RAMTOP.
DEC HL ; ustaw adres ostatniego bajtu systemowego.
LD (HL),$3E ; ustaw znacznik kończ $3E - zbyt duży dla starszego bajtu numeru wiersza.
DEC HL ; ustaw adres nieważnego, młodszego bajtu.
LD SP,HL ; i ustaw wskaźnik stosu na ten adres
DEC HL ; ustaw adres pierwszego bajtu na stosie procesora,
DEC HL ; który zostanie zapełniony przez następne CALL/PUSH.
LD (ERR_SP),HL ; ustaw wskaźnik stosu błędów ERR_SP na początek obecnie pustego stosu procesora.
; Teraz ustaw rejestr I, aby układy generacji obrazu wiedziały, gdzie odnaleźć zastaw znaków. Ten ROM wykorzystuje zestaw znaków
; tylko do drukowania na ZX Printer. Obraz TV tworzony jest przez zewnętrzne obwody wideo. Ta pamięć ROM 8KB może być używana jako
; rozszerzenie w ZX80 zamiast jego oryginalnej pamięci 4KB ROM, zatem obwody wideo mogą być na ZX80.
LD A,$1E ; dla tego ROM adres wynosi $1E00.
LD I,A ; ustaw rejestr I poprzez rejestr A.
IM 1 ; wybierz tryb 1 dla przerwań Z80.
LD IY,ERR_NR ; ustaw IY na początek RAM, aby można było indeksować zmienne systemowe.
LD (IY+$3B),$40 ; ustaw CDFLAG na 0100 0000. Bit 6 wskazuje wymagany tryb obliczeń i generacji obrazu.
LD HL,$407D ; pierwsza komórka za Zmiennymi Systemowymi - 16509 dziesiętnie.
LD (D_FILE),HL ; ustaw na tę wartość zmienną systemową D_FILE.
LD B,$19 ; ustaw minimalny ekran na 24 znaki NEWLINE, które nastąpią po początkowym NEWLINE.
; $0408 : 1032
LINE: LD (HL),$76 ; wstaw NEWLINE (instrukcja HALT)
INC HL ; zaadresuj następną pozycję.
DJNZ LINE ; wróć w pętli do LINE dla wszystkich 25 znaków NEWLINE
LD (VARS),HL ; ustaw zmienną systemową VARS na następny adres
CALL CLEAR ; procedura CLEAR ustawia znacznik końca $80 oraz wskaźniki dynamicznej pamięci E_LINE, STKBOT i STKEND.
; $0413 : 1043
N_L_ONLY: CALL CURSOR_IN ; procedura CURSOR-IN wstawia kursor i znacznik końca w wierszu edycji, ustawiając również rozmiar
; dolnych dwóch wierszy ekranu.
CALL SLOW_FAST ; procedura SLOW/FAST wybiera tryb obliczeń i wyświetlania
; ----------------------
; SEKCJA 'WYDRUK BASICU'
; ----------------------
; 'Górna' część obrazu jest tworzona przez wywołanie najpierw procedury polecenia CLS, a następnie program w języku BASIC jest wyświetlany
; od S-TOP. Użycie klawisza "kursor w dół" również powoduje przebudowę "górnej" części obrazu.
; $0419 : 1049
UPPER: CALL CLS
LD HL,(E_PPC) ; młodszy bajt zmiennej systemowej E_PPC
LD DE,(S_TOP) ; młodszy bajt zmiennej systemowej S_TOP
AND A
SBC HL,DE
EX DE,HL
JR NC,ADDR_TOP
ADD HL,DE
LD (S_TOP),HL ; młodszy bajt zmiennej systemowej S_TOP
; $042D : 1069
ADDR_TOP: CALL LINE_ADDR
JR Z,LIST_TOP
EX DE,HL
; $0433 : 1075
LIST_TOP: CALL LIST_PROG
DEC (IY+$1E) ; zmienna systemowa BERG
JR NZ,LOWER
LD HL,(E_PPC) ; młodszy bajt zmiennej systemowej E_PPC
CALL LINE_ADDR
LD HL,(CH_ADD) ; młodszy bajt zmiennej systemowej CH_ADD
SCF ; ustaw znacznik przeniesienia
SBC HL,DE
LD HL,S_TOP ; młodszy bajt zmiennej systemowej S_TOP
JR NC,INC_LINE
EX DE,HL
LD A,(HL)
INC HL
LDI
LD (DE),A
JR UPPER
; punkt wejścia dla 'kursor w dół'
; $0454 : 1108
DOWN_KEY: LD HL,E_PPC ; młodszy bajt zmiennej systemowej E_PPC
; $0457 : 1111
INC_LINE: LD E,(HL)
INC HL
LD D,(HL)
PUSH HL
EX DE,HL
INC HL
CALL LINE_ADDR
CALL LINE_NO
POP HL
; $0464 : 1124
KEY_INPUT: BIT 5,(IY+$2D) ; zmienna systemowa FLAGX
JR NZ,LOWER ; skocz naprzód do LOWER
LD (HL),D
DEC HL
LD (HL),E
JR UPPER
; -----------------------------
; SEKCJA 'KOPII WIERSZA EDYCJI'
; -----------------------------
; Procedura ta ustawia wiersz edycji jedynie z kursorem, gdy
; 1) Brakuje pamięci do edycji wiersza BASIC.
; 2) Podczas wprowadzania naciśnieto klawisz EDIT.
; Punktem wejścia jest LOWER
; $046F : 1135
EDIT_INP: CALL CURSOR_IN ; procedura CURSOR-IN ustawia wiersz edycji jedynie z kursorem.
; $0472 : 1138
LOWER: LD HL,(E_LINE) ; pobierz start wiersza edycji z E_LINE.
; $0475 : 1141
EACH_CHAR: LD A,(HL) ; pobierz znak z wiersza edycji.
CP $7E ; porównaj go ze znacznikiem liczby.
JR NZ,END_LINE ; jeśli różny, skocz naprzód do END-LINE
LD BC,$0006 ; inaczej sześć niewidocznych bajtów do usunięcia.
CALL RECLAIM_2
JR EACH_CHAR ; wróć do EACH-CHAR
; $0482 : 1154
END_LINE: CP $76
INC HL
JR NZ,EACH_CHAR
; $0487 : 1159
EDIT_LINE: CALL CURSOR ; procedura CURSOR ustawia kursor K lub L.
; $048A : 1162
EDIT_ROOM: CALL LINE_ENDS
LD HL,(E_LINE) ; pobierz zmienną systemową E_LINE
LD (IY+$00),$FF ; zmienna systemowa ERR_NR
CALL COPY_LINE
BIT 7,(IY+$00) ; zmienna systemowa ERR_NR
JR NZ,DISPLAY_6
LD A,(DF_SZ) ; zmienna systemowa DF_SZ
CP $18
JR NC,DISPLAY_6
INC A
LD (DF_SZ),A
LD B,A
LD C,$01
CALL LOC_ADDR
LD D,H
LD E,L
LD A,(HL)
; $04B1 : 1201
FREE_LINE: DEC HL
CP (HL)
JR NZ,FREE_LINE
INC HL
EX DE,HL
LD A,($4005) ; starszy bajt zmiennej systemowej RAMTOP
CP $4D
CALL C,RECLAIM_1
JR EDIT_ROOM
; -------------------------------
; SEKCJA 'OCZEKIWANIA NA KLAWISZ'
; -------------------------------
; $04C1 : 1217
DISPLAY_6: LD HL,$0000 ; zeruj HL
LD (X_PTR),HL ; wyzeruj zmienną systemową X_PTR
LD HL,CDFLAG ; pobierz CDFLAG do HL
BIT 7,(HL) ; sprawdź bit 7
CALL Z,DISPLAY_1 ; jeśli jest wyzerowany, wywołaj procedurę DISPLAY-1
; $04CF : 1231
SLOW_DISP: BIT 0,(HL) ; sprawdź bit 0
JR Z,SLOW_DISP ; jeśli wyzerowany, skocz do SLOW-DISP
LD BC,(LAST_K) ; pobierz do BC zmienną systemową LAST_K
CALL DEBOUNCE ; czekaj na wygaśnięcie drgań klawiatury
CALL DECODE ; dekoduj klawisz
JR NC,LOWER ; wróć do LOWER
; -------------------------------
; SEKCJA 'DEKODOWANIA KLAWIATURY'
; -------------------------------
; Zdekodowany kod klawisza jest w E a HL wskazuje na pozycję w tablicy klawiszy. D zawiera zero.
; $04DF : 1247
K_DECODE: LD A,(MODE) ; pobierz zawartość zmiennej systemowej MODE
DEC A ; testuj razem trzy wartości
JP M,FETCH_2 ; naprzód przy zerze do FETCH-2
JR NZ,FETCH_1 ; naprzód przy 2 do FETCH-1
; Pierwotną wartością było 1, a teraz jest zero.
LD (MODE),A ; uaktualnij zmienną systemową MODE
DEC E ; zmniejsz E do zakresu $00 - $7F
LD A,E ; przenieś je do A
SUB $27 ; odejmij 39, ustawiając przeniesienie, jeśli w zakresie 00 - 38
JR C,FUNC_BASE ; w takim razie idź do przodu do FUNC-BASE
LD E,A ; inaczej ustaw w E zmniejszoną wartość
; $04F2 : 1266
FUNC_BASE: LD HL,K_FUNCT ; adres tablicy K-FUNCT dla klawiszy funkcyjnych.
JR TABLE_ADD ; skocz naprzód do TABLE-ADD
; $04F7 : 1271
FETCH_1: LD A,(HL)
CP $76
JR Z,K_L_KEY
CP $40
SET 7,A
JR C,ENTER
LD HL,$00C7
; $0505 : 1285
TABLE_ADD: ADD HL,DE
JR FETCH_3
; $0508 : 1288
FETCH_2: LD A,(HL)
BIT 2,(IY+$01) ; zmienna systemowa FLAGS - tryb K lub L ?
JR NZ,TEST_CURS
ADD A,$C0
CP $E6
JR NC,TEST_CURS
; $0515 : 1301
FETCH_3: LD A,(HL)
; $0516 : 1302
TEST_CURS: CP $F0
JP PE,KEY_SORT
; $051B : 1307
ENTER: LD E,A
CALL CURSOR
LD A,E
CALL ADD_CHAR
; $0523 : 1315
BACK_NEXT: JP LOWER
; ---------------------------
; PROCEDURA 'DODAWANIA ZNAKU'
; ---------------------------
; $0526 : 1318
ADD_CHAR: CALL ONE_SPACE
LD (DE),A
RET
; ----------------------------
; PROCEDURA 'KLAWISZY KURSORA'
; ----------------------------
; $052B : 1323
K_L_KEY: LD A,$78
; $052D : 1325
KEY_SORT: LD E,A
LD HL,$0482 ; adres bazowy ED-KEYS
ADD HL,DE
ADD HL,DE
LD C,(HL)
INC HL
LD B,(HL)
PUSH BC
; $0537 : 1335
CURSOR: LD HL,(E_LINE) ; zmienna systemowa E_LINE
BIT 5,(IY+$2D) ; zmienna systemowa FLAGX
JR NZ,L_MODE
; $0540 : 1344
K_MODE: RES 2,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj użycie trybu K
; $0544 : 1348
TEST_CHAR: LD A,(HL)
CP $7F
RET Z
INC HL
CALL NUMBER
JR Z,TEST_CHAR
CP $26
JR C,TEST_CHAR
CP $DE
JR Z,K_MODE
; $0556 : 1366
L_MODE: SET 2,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj użycie trybu L
JR TEST_CHAR
; --------------------------
; PROCEDURA 'WYCZYŚĆ-JEDEN'
; --------------------------
; $055C : 1372
CLEAR_ONE: LD BC,$0001
JP RECLAIM_2
; -----------------------------
; TABLICA 'KLAWISZY EDYCYJNYCH'
; -----------------------------
; $0562 : 1378
ED_KEYS: .WORD UP_KEY ; Adres: $059F
.WORD DOWN_KEY ; Adres: $0454
.WORD LEFT_KEY ; Adres: $0576
.WORD RIGHT_KEY ; Adres: $057F
.WORD FUNCTION ; Adres: $05AF
.WORD EDIT_KEY ; Adres: $05C4
.WORD N_L_KEY ; Adres: $060C
.WORD RUBOUT ; Adres: $058B
.WORD FUNCTION ; Adres: $05AF
.WORD FUNCTION ; Adres: $05AF
; -------------------------
; PROCEDURA 'KURSOR W LEWO'
; -------------------------
; $0576 : 1398
LEFT_KEY: CALL LEFT_EDGE
LD A,(HL)
LD (HL),$7F
INC HL
JR GET_CODE
; --------------------------
; PROCEDURA 'KURSOR W PRAWO'
; --------------------------
; $057F : 1407
RIGHT_KEY: INC HL
LD A,(HL)
CP $76
JR Z,ENDED_2
LD (HL),$7F
DEC HL
; $0588 : 1416
GET_CODE: LD (HL),A
; $0589 : 1417
ENDED_1: JR BACK_NEXT
; -----------------------
; PROCEDURA 'WYMAZYWANIA'
; -----------------------
; $058B : 1419
RUBOUT: CALL LEFT_EDGE
CALL CLEAR_ONE
JR ENDED_1
; ------------------------
; PROCEDURA 'LEWA KRAWĘDŹ'
; ------------------------
; $0593 : 1427
LEFT_EDGE: DEC HL
LD DE,(E_LINE)
LD A,(DE)
CP $7F
RET NZ
POP DE
; $059D : 1437
ENDED_2: JR ENDED_1
; -------------------------
; PROCEDURA 'KURSOR W GÓRĘ'
; -------------------------
; $059F : 1439
UP_KEY: LD HL,(E_PPC)
CALL LINE_ADDR
EX DE,HL
CALL LINE_NO
LD HL,$400B ; ustaw HL na starszy bajt zmiennej systemowej E_PPC
JP KEY_INPUT ; skocz wstecz do KEY-INPUT
; -----------------------------
; PROCEDURA 'KLAWISZ FUNKCYJNY'
; -----------------------------
; $05AF : 1455
FUNCTION: LD A,E
AND $07
LD (MODE),A
JR ENDED_2
; --------------------------------
; PROCEDURA 'ZBIERZ NUMER WIERSZA'
; --------------------------------
; $05B7 : 1463
ZERO_DE: EX DE,HL
LD DE,DISPLAY_6 + 1 ; $04C2 - miejsce adresujące dwa zera
; $05BB : 1467
LINE_NO: LD A,(HL)
AND $C0
JR NZ,ZERO_DE
LD D,(HL)
INC HL
LD E,(HL)
RET
; ------------------------
; PROCEDURA 'KLAWISZ EDIT'
; ------------------------
; $05C4 : 1476
EDIT_KEY: CALL LINE_ENDS ; procedura LINE-ENDS czyści spód ekranu.
LD HL,EDIT_INP
PUSH HL ; ** umieszczane na stosie jako adres pętli w przypadku błędu.
BIT 5,(IY+$2D) ; testuj FLAGX
RET NZ ; w trybie wprowadzania skocz pośrednio do do $046F, EDIT-INP (zacznij jeszcze raz).
LD HL,(E_LINE) ; pobierz E_LINE
LD (DF_CC),HL ; i użyj do uaktualnienia kursora ekranowego DF_CC
; teraz RST $10 wydrukuje numery wierszy do wiersza edycji zamiast na ekran. Najpierw jednak należy się upewnić, że żaden znak newline
; nie pojawi się poza ekranem podczas drukowania numerów wierszy do tego wiersza edycyjnego.
LD HL,$1821 ; przygotuj wiersz 0, kolumnę 0
LD (S_POSN),HL ; uaktualnij S_POSN tymi podstawionymi wartościami.
LD HL,(E_PPC) ; pobierz bieżący wiersz z E_PPC, może to być nieistniejący wiersz, tj. ostatnio usunięty.
CALL LINE_ADDR ; procedura LINE-ADDR pobiera adres wiersza lub, gdy go brak, adres następnego wiersza.
CALL LINE_NO ; procedura LINE-NO pobiera numer wiersza, jeśli taki jest, do DE, pozostawiając HL wskazujące
; na drugi młodszy bajt.
LD A,D ; sprawdź, czy numer wiersza wynosi zero.
OR E
RET Z ; wróć, jeśli brak numeru wiersza - brak programu do edycji.
DEC HL ; wskazuj starszy bajt.
CALL OUT_NO ; procedura OUT-NO zapisuje numer do wiersza edycji.
INC HL ; wskazuj na bajty długości.
LD C,(HL) ; młodszy baj do C.
INC HL
LD B,(HL) ; starszy bajt do B.
INC HL ; wskazuj pierwszy znak w wierszu.
LD DE,(DF_CC) ; pobierz kursor pliku wyświetlania DF_CC
LD A,$7F ; przygotuj znak kursora.
LD (DE),A ; i wstaw go do wiersza edycji.
INC DE ; zwiększ adres przeznaczenia.
PUSH HL ; * zapisz początek BASIC.
LD HL,$001D ; 29 bajtów nadmiarowych.
ADD HL,DE ; dodaj adres kursora.
ADD HL,BC ; dodaj długość wiersza.
SBC HL,SP ; odejmij wskaźnik stosu.
POP HL ; * odtwórz wskaźnik początku BASIC.
RET NC ; jeśli brak miejsca, wróć do L046F EDIT-INP. Klawisz EDIT nie będzie działał.
LDIR ; inaczej skopiuj bajty z programu do wiersza edycji. Uwaga, ukryte wartości zmiennoprzecinkowe
; również są kopiowane do wiersza edycji
EX DE,HL ; przenieś wskaźnik wolnego miejsca do HL
POP DE ; ** usuń adres EDIT-INP ze stosu.
CALL SET_STK_B ; procedura SET-STK-B ustawia STKEND z HL.
JR ENDED_2 ; wstecz do ENDED-2 i po kolejnych 3 skokach do $0472, LOWER.
; Uwaga. Procedura LOWER usuwa ukryte wartości zmiennoprzecinkowe z wiersza edycji.
; ---------------------------
; PROCEDURA 'KLAWISZ NEWLINE'
; ---------------------------
; $060C : 1548
N_L_KEY: CALL LINE_ENDS
LD HL,LOWER ; przygotuj adres: LOWER
BIT 5,(IY+$2D) ; zmienna systemowa FLAGX
JR NZ,NOW_SCAN
LD HL,(E_LINE)
LD A,(HL)
CP $FF
JR Z,STK_UPPER
CALL CLEAR_PRB
CALL CLS
; $0626 : 1574
STK_UPPER: LD HL,UPPER
; $0629 : 1577
NOW_SCAN: PUSH HL ; zapisz na stosie adres procedury (LOWER lub UPPER).
CALL LINE_SCAN
POP HL
CALL CURSOR
CALL CLEAR_ONE
CALL E_LINE_NO
JR NZ,N_L_INP
LD A,B
OR C
JP NZ,N_L_LINE
DEC BC
DEC BC
LD (PPC),BC
LD (IY+$22),$02 ; zmienna systemowa DF_SZ
LD DE,(D_FILE)
JR TEST_NULL
; $064E : 1614
N_L_INP: CP $76
JR Z,N_L_NULL ; to N/L-NULL
LD BC,(T_ADDR)
CALL LOC_ADDR
LD DE,(NXTLIN)
LD (IY+$22),$02 ; zmienna systemowa DF_SZ
; $0661 : 1633
TEST_NULL: RST 18H ; POBIERZ-ZNAK
CP $76
; $0664 : 1636
N_L_NULL: JP Z,N_L_ONLY
LD (IY+$01),$80 ; zmienna systemowa FLAGS
EX DE,HL
; $066C : 1644
NEXT_LINE: LD (NXTLIN),HL
EX DE,HL
CALL TEMP_PTR2
CALL LINE_RUN
RES 1,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj, że drukarka nie jest używana
LD A,$C0
LD (IY+$19),A ; młodszy bajt zmiennej systemowej X_PTR
CALL X_TEMP
RES 5,(IY+$2D) ; zmienna systemowa FLAGX
BIT 7,(IY+$00) ; zmienna systemowa ERR_NR
JR Z,STOP_LINE
LD HL,(NXTLIN)
AND (HL)
JR NZ,STOP_LINE
LD D,(HL)
INC HL
LD E,(HL)
LD (PPC),DE
INC HL
LD E,(HL)
INC HL
LD D,(HL)
INC HL
EX DE,HL
ADD HL,DE
CALL BREAK_1
JR C,NEXT_LINE
LD HL,ERR_NR
BIT 7,(HL)
JR Z,STOP_LINE
LD (HL),$0C
; $06AE : 1710
STOP_LINE: BIT 7,(IY+$38) ; zmienna systemowa PR_CC
CALL Z,COPY_BUFF
LD BC,$0121
CALL LOC_ADDR
LD A,(ERR_NR)
LD BC,(PPC)
INC A
JR Z,REPORT
CP $09
JR NZ,CONTINUE
INC BC
; $06CA : 1738
CONTINUE: LD (OLDPPC),BC
JR NZ,REPORT
DEC BC
; $06D1 : 1745
REPORT: CALL OUT_CODE
LD A,$18
RST 10H ; PISZ-AKUMULATOR
CALL OUT_NUM
CALL CURSOR_IN
JP DISPLAY_6
; $06E0 : 1760
N_L_LINE: LD (E_PPC),BC
LD HL,(CH_ADD)
EX DE,HL
LD HL,N_L_ONLY ; adresuj: N/L-ONLY
PUSH HL
LD HL,(STKBOT)
SBC HL,DE
PUSH HL
PUSH BC
CALL SET_FAST
CALL CLS
POP HL
CALL LINE_ADDR
JR NZ,COPY_OVER
CALL NEXT_ONE
CALL RECLAIM_2
; $0705 : 1797
COPY_OVER: POP BC
LD A,C
DEC A
OR B
RET Z
PUSH BC
INC BC
INC BC
INC BC
INC BC
DEC HL
CALL MAKE_ROOM
CALL SLOW_FAST
POP BC
PUSH BC
INC DE
LD HL,(STKBOT)
DEC HL
LDDR ; kopiuj bajty
LD HL,(E_PPC)
EX DE,HL
POP BC
LD (HL),B
DEC HL
LD (HL),C
DEC HL
LD (HL),E
DEC HL
LD (HL),D
RET
; ----------------------------------
; PROCEDURY POLECEŃ 'LIST' I 'LLIST'
; ----------------------------------
; $072C : 1836
LLIST: SET 1,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj uzywanie drukarki
; $0730 : 1840
LIST: CALL FIND_INT
LD A,B ; pobierz starszy bajt numeru wiersza dostarczonego przez użytkownika.
AND $3F ; i ogranicz go do zakresu 1-16383.
LD H,A
LD L,C
LD (E_PPC),HL ; wynik umieść w zmiennej systemowej E_PPC
CALL LINE_ADDR
; $073E : 1854
LIST_PROG: LD E,$00
; $0740 : 1856
UNTIL_END: CALL OUT_LINE ; procedura OUT-LINE tworzy listing jednego wiersza w BASIC wracając wcześniej, gdy ekran staje się
; pełny lub napotka koniec programu.
JR UNTIL_END ; pętla do UNTIL-END
; ---------------------------------
; PROCEDURA 'WYDRUKU WIERSZA BASIC'
; ---------------------------------
; $0745 : 1861
OUT_LINE: LD BC,(E_PPC)
CALL CP_LINES
LD D,$92
JR Z,TEST_END
LD DE,$0000
RL E
; $0755 : 1877
TEST_END: LD (IY+$1E),E ; zmienna systemowa BERG
LD A,(HL)
CP $40
POP BC
RET NC
PUSH BC
CALL OUT_NO
INC HL
LD A,D
RST 10H ; PISZ-AKUMULATOR
INC HL
INC HL
; $0766 : 1894
COPY_LINE: LD (CH_ADD),HL
SET 0,(IY+$01) ; zmienna systemowa FLAGS - usuń wiodącą spację
; $076D : 1901
MORE_LINE: LD BC,(X_PTR)
LD HL,(CH_ADD)
AND A
SBC HL,BC
JR NZ,TEST_NUM
LD A,$B8
RST 10H ; PISZ-AKUMULATOR
; $077C : 1916
TEST_NUM: LD HL,(CH_ADD)
LD A,(HL)
INC HL
CALL NUMBER
LD (CH_ADD),HL
JR Z,MORE_LINE
CP $7F
JR Z,OUT_CURS
CP $76
JR Z,OUT_CH
BIT 6,A
JR Z,NOT_TOKEN
CALL TOKENS
JR MORE_LINE ; wróć w pętli do MORE-LINE
; $079A : 1946
NOT_TOKEN: RST 10H ; PISZ-AKUMULATOR
JR MORE_LINE ; wróć w pętli do MORE-LINE
; $079D : 1949
OUT_CURS: LD A,(MODE) ; pobierz zawartość zmiennej systemowej MODE
LD B,$AB ; przygotuj negatywowe F dla kursora funkcyjnego.
AND A ; test na zero
JR NZ,FLAGS_2 ; jeśli nie, skocz naprzód do FLAGS-2
LD A,(FLAGS) ; pobierz zmienną systemową FLAGS.
LD B,$B0 ; przygotuj negatywowe K dla kursora.
; $07AA : 1962
FLAGS_2: RRA ; 00000?00 -> 000000?0
RRA ; 000000?0 -> 0000000?
AND $01 ; 0000000? 0000000x
ADD A,B ; [F] -> [G] lub [K] -> [L]
CALL PRINT_SP ; procedura PRINT-SP drukuje znak
JR MORE_LINE ; wróć do MORE-LINE
; ------------------
; PROCEDURA 'LICZBA'
; ------------------
; Ta procedura sprawdza, czy znak w rejestrze jest równy znacznikowi liczby. Jeśli tak, to wartość w parze rejestrów HL jest zwiększana o 5,
; aby albo przeskoczyć liczbę zmiennoprzecinkową, albo zarezerwować pięć bajtów na taką liczbę.
; $07B4 : 1972
NUMBER: CP $7E ; znacznik liczby?
RET NZ ; wróć, jeśli nie
INC HL ; przesuń HL o pięć bajtów
INC HL
INC HL
INC HL
INC HL
RET
; -----------------------------------
; PROCEDURA 'DEKODOWANIA KLAWIATURY'
; -----------------------------------
; Różne 'wartości klawiszy' przechowywane w parze rejestrów BC są 'dekodowane' na normalne kody znakowe komputera ZX81 poprzez wyszukiwanie ich
; w tablicy klawiszy spod adresu $007E ($007D+1) - KUNSHIFT. Kod znaku jest określony przez (HL).
; $07BD : 1981
DECODE: LD D,$00
SRA B
SBC A,A
OR $26
LD L,$05
SUB L
; $07C7 : 1991
KEY_LINE: ADD A,L
SCF
RR C
JR C,KEY_LINE
INC C
RET NZ
LD C,B
DEC L
LD L,$01
JR NZ,KEY_LINE
LD HL,$007D
LD E,A
ADD HL,DE
SCF
RET
; -----------------------
; PROCEDURA 'DRUKOWANIA'
; -----------------------
; Dwie procedurki WRITE-CH i WRITE stanowią istotne części procedury drukującej. Jednakże zanim znak będzie mógł zostać wydrukowany, należy pobrać
; S-POSN, sprawdzić i w razie konieczności poszerzyć ekran. Stosowane są różne punkty wejścia do tej procedury, które związane są z zamianą
; kodów binarnych na kody znakowe komputera ZX81.
; $07DC : 2012
LEAD_SP: LD A,E
AND A
RET M
JR PRINT_CH
; $07E1 : 2017
OUT_DIGIT: XOR A
; $07E2 : 2018
DIGIT_INC: ADD HL,BC
INC A
JR C,DIGIT_INC
SBC HL,BC
DEC A
JR Z,LEAD_SP
; $07EB : 2027
OUT_CODE: LD E,$1C
ADD A,E
; $07EE : 2030
OUT_CH: AND A
JR Z,PRINT_SP
; $07F1 : 2033
PRINT_CH: RES 0,(IY+$01) ; uaktualnij zmienną FLAGS - sygnalizuj zezwolenia na wiodącą spację
; $07F5 : 2037
PRINT_SP: EXX
PUSH HL
BIT 1,(IY+$01) ; testuj zmienną FLAGS - czy drukarka w użyciu ?
JR NZ,LPRINT_A ; jeśli tak, drukuj do drukarki
CALL ENTER_CH
JR PRINT_EXX
; $0802 : 2050
LPRINT_A: CALL LPRINT_CH
; $0805 : 2053
PRINT_EXX: POP HL
EXX
RET
; $0808 : 2056
ENTER_CH: LD D,A
LD BC,(S_POSN)
LD A,C
CP $21
JR Z,TEST_LOW
; $0812 : 2066
TEST_N_L: LD A,$76
CP D
JR Z,WRITE_N_L
LD HL,(DF_CC)
CP (HL)
LD A,D
JR NZ,WRITE_CH
DEC C
JR NZ,EXPAND_1
INC HL
LD (DF_CC),HL
LD C,$21
DEC B
LD (S_POSN),BC
; $082C : 2092
TEST_LOW: LD A,B
CP (IY+$22) ; zmienna systemowa DF_SZ
JR Z,REPORT_5
AND A
JR NZ,TEST_N_L
; $0835 : 2101
REPORT_5: LD L,$04 ; 'brak miejsca na ekranie'
JP ERROR_3
; $083A : 2106
EXPAND_1: CALL ONE_SPACE
EX DE,HL
; $083E : 2110
WRITE_CH: LD (HL),A
INC HL
LD (DF_CC),HL
DEC (IY+$39) ; zmienna systemowa S_POSN_x
RET
; $0847 : 2119
WRITE_N_L: LD C,$21
DEC B
SET 0,(IY+$01) ; zmienna systemowa FLAGS - unieważnij wiodącą spację
JP LOC_ADDR
; ----------------------
; PROCEDURA 'LPRINT-CH'
; ----------------------
; Ten podprogram wysyła znak do drukarki ZX-Printer, umieszczając kod znaku w buforze drukarki.
; Uwaga. PR-CC zawiera młodszy bajt adresu bufora. Starszy bajt ma zawsze wartość stałą.
; $0851 : 2129
LPRINT_CH: CP $76 ; porównaj z NEWLINE.
JR Z,COPY_BUFF ; jeśli równe, skocz naprzód do COPY-BUFF
LD C,A ; kopię znaku umieść w C.
LD A,(PR_CC) ; pobierz adres bufora z PR_CC
AND $7F ; zignoruj bit 7, aby utworzyć prawdziwy adres.
CP $5C ; Porównaj z 33 kolumną
LD L,A ; utwórz młodszy bajt adresu bufora
LD H,$40 ; starszy bajt ma wartość stałą.
CALL Z,COPY_BUFF ; wywołaj procedurę COPY-BUFF, aby wysłać pełny bufor do drukarki, jeśli pierwsze 32 bajty są użyte.
; (to zresetuje HL na początek)
LD (HL),C ; umieść znak w buforze.
INC L ; zwiększ pozycję - nie będzie przekroczenia granicy 256.
LD (IY+$38),L ; uaktualnij zmienną systemową PR_CC automatycznie resetując bit 7, aby pokazać, że bufor nie jest pusty.
RET
; --------------------------
; PROCEDURA POLECENIA 'COPY'
; --------------------------
; Cały ekran znakowy jest kopiowany do drukarki ZX-Printer. Drukowane są wszystkie 24 wiersze tekstu/grafiki.
; $0869 : 2153
COPY: LD D,$16 ; przygotuj się do kopiowania 24 wierszy tekstu.
LD HL,(D_FILE) ; ustaw HL na poczatek pliku wideo ze zmiennej D_FILE.
INC HL
JR COPY_D ; naprzód do COPY_D
; Pojedynczy bufor znakowy jest kopiowany do drukarki ZX-Printer.
; $0871 : 2161
COPY_BUFF: LD D,$01 ; przygotuj się do kopiowania pojedynczego wiersza tekstu.
LD HL,PRBUFF ; ustaw HL na start bufora drukarki PRBUFF.
; obie ścieżki zbiegają się tutaj.
; $0876 : 2166
COPY_D: CALL SET_FAST
PUSH BC ; przechowaj BC, może być wiszący znak w C z LPRINT-CH
; $087A : 2170
COPY_LOOP: PUSH HL ; zachowaj wskaźnik pierwszego znaku. (*)
XOR A ; wyzeruj akumulator.
LD E,A ; ustaw licznik wierszy pikseli na zero, zakres 0-7.
; ta wewnętrzna pętla zajmuje się każdą poziomą linią pikseli.
; $087D : 2173
COPY_TIME: OUT ($FB),A ; wyzerowany bit 2 włącza silnik drukarki z nieaktywnym pisakiem - wyzerowany bit 7.
POP HL ; pobierz wskaźnik pierwszego znaku (*) dla pętli wewnętrznej.
; $0880 : 2176
COPY_BRK: CALL BREAK_1 ; sprawdź, czy użytkownik przerwał operację
JR C,COPY_CONT ; jeśli nie było naciśniętego klawisza, idź naprzód do COPY-CONT
; inaczej rejestr A będzie zawierał 11111111 0
RRA ; 0111 1111
OUT ($FB),A ; zatrzymaj silnik drukarki, wyłącz pisak.
; $0888 : 2184
REPORT_D2: RST 08H ; ERROR-1
.BYTE $0C ; Raport błędu: PRZERWANIE - CONT powtarza
; $088A : 2186
COPY_CONT: IN A,($FB) ; czytaj port drukarki.
ADD A,A ; testuj bity 6 i 7
JP M,COPY_END ; jeśli brak drukarki, skocz naprzód do COPY-END
JR NC,COPY_BRK ; wróć do COPY-BRK, jeśli pisak nie jest na pozycji
PUSH HL ; zachowaj wskaźnik pierwszego znaku (*)
PUSH DE ; zachowaj wiersz znaków i wiersz pikseli.
LD A,D ; do A licznik linii ?
CP $02 ; przy ostatniej linii ustawia przeniesienie.
SBC A,A ; teraz $FF przy ostatniej linii lub zero w innym przypadku.
; teraz sprytnie przygotuj maskę kontrolną dla drukarki, ustawiając bit 2(później przesunięty do 1) rejestru D, aby zwolnić drukarkę
; dla ostatnich dwóch linii pikseli( E = 6 i 7)
AND E ; AND z numerem linii 0-7
RLCA ; przesuń w lewo.
AND E ; AND jeszcze raz.
LD D,A ; zapisz maskę sterującą w D.
; $089C : 2204
COPY_NEXT: LD C,(HL) ; załaduj znak z ekranu lub z bufora.
LD A,C ; zapisz kopię w C dla późniejszego testu na negatyw.
INC HL ; uaktualnij wskaźnik dla następnego obiegu.
CP $76 ; czy znakiem jest NEWLINE ?
JR Z,COPY_N_L ; naprzód do COPY-N/L, jeśli tak
PUSH HL ; * inaczej zachowaj wskaźnik znaku.
SLA A ; (?) pomnóż przez 2
ADD A,A ; pomnóż przez 4
ADD A,A ; pomnóż przez 8
LD H,$0F ; załaduj do H połówkę adresu matryc znakowych.
RL H ; teraz $1E lub $1F (z przeniesieniem)
ADD A,E ; dodaj numer linii 0-7
LD L,A ; teraz HL adresuje bajt źródłowy w matrycy
RL C ; testuj znak, ustawiając przeniesienie, jeśli jest negatywem.
SBC A,A ; akumulator teraz ma zawartość $00 dla normalnych znaków lub $FF dla negatywów.
XOR (HL) ; połącz ze wzorem bitowym z końca ROM.
LD C,A ; przenieś bajt do C.
LD B,$08 ; licznik 8 bitów do wyprowadzenia.
; $08B5 : 2229
COPY_BITS: LD A,D ; pobierz maskę sterowania szybkością z D.
RLC C ; umieść bit do wyprowadzenia w przeniesieniu.
RRA ; umieść go w bicie 7, bit prędkości w bicie 1
LD H,A ; zapisz przygotowaną maskę w rejestrze H.
; $08BA : 2234
COPY_WAIT: IN A,($FB) ; czytaj port drukarki
RRA ; czekaj na sygnał wyrównania z enkodera.
JR NC,COPY_WAIT ; jeśli brak, czekaj w pętli
LD A,H ; bajt kontrolny do A.
OUT ($FB),A ; i wyprowadź go do portu drukarki.
DJNZ COPY_BITS ; w pętli wyprowadź wszystkie 8 bitów
POP HL ; odzyskaj wskaźnik znaku.
JR COPY_NEXT ; skocz wstecz do COPY-NEXT, aby skopiować przyległy znak
; Napotkany został znak NEWLINE, który albo występuje za wierszem tekstu, albo jest pierwszym znakiem ekranu lub wiersza drukarki.
; $08C7 : 2247
COPY_N_L: IN A,($FB) ; czytaj port drukarki.
RRA ; czekaj na sygnał z enkodera.
JR NC,COPY_N_L ; jeśli go brak, czekaj w pętli
LD A,D ; przenieś maskę prędkości do A.
RRCA ; przesuń bit prędkości na pozycję 1. Bit 7 jest ustawiony na 0 - sterowanie pisakiem.
OUT ($FB),A ; ustaw prędkość drukarki.
POP DE ; odtwórz wiersz znaku oraz wiersz pikseli.
INC E ; zwiększ wiersz pikseli 0-7.
BIT 3,E ; sprawdź, czy osiągnięto wartość 8.
JR Z,COPY_TIME ; jeśli nie, wróć do COPY-TIME
; skończono 8 linii pikseli, wiersz tekstu.
POP BC ; pozbądź się niepotrzebnego już znacznika pierwszego znaku
DEC D ; zmniejsz licznik wiersza tekstu.
JR NZ,COPY_LOOP ; jeśli nie zero, cofnij się do COPY-LOOP
LD A,$04 ; zatrzymaj silnik drukarki, który już pracuje wolno.
OUT ($FB),A ; zapisz do portu drukarki.
; $08DE : 2270
COPY_END: CALL SLOW_FAST
POP BC ; odzyskaj zachowaną parę rejestrów BC.
; ----------------------------------------
; PROCEDURA 'CZYSZCZENIA BUFORA DRUKARKI'
; ----------------------------------------
; Ten podprogram ustawia 32 bajty bufora drukarki na zero (spacje), a 33-cim znakiem staje się NEWLINE. Wykonywane to jest po przesłaniu
; zawartości bufora do drukarki, lecz dodatkowo również po wydrukowaniu na niej 24 wierszy ekranowych.
; Uwaga. To jest błąd logiczny, ponieważ ostatnia operacja wcale nie wymaga bufora. Logicznie ujmując, powinno być możliwe takie użycie:
; 10 LPRINT "HELLO ";
; 20 COPY
; 30 LPRINT ; "WORLD"
; i oczekiwanie, że cała wiadomość pojawi się na drukarce. Co zadziwiające, ten błąd logiczny nigdy nie został wykryty, a chociaż można się
; kłócić, czy powyższy program daje błąd, to powtórzenie tego błędu na Spectrum już jest bez żadnych wątpliwości błędem oprogramowania.
; Ponieważ bufor drukarki ma stały adres na końcu obszaru zmiennych systemowych, a pozycja druku jest w zakresie od $3C do $5C, to bit 7
; zmiennej systemowej jest ustawiany na 1, aby zasygnalizować pusty bufor i automatycznie zresetować się, gdy zmienna ta jest uaktualniana
; jakąkolwiek pozycją druku - sprytnie.
; $08E2 : 2274
CLEAR_PRB: LD HL,$405C ; adres ustawiony na koniec PRBUFF
LD (HL),$76 ; umieść NEWLINE na ostatniej pozycji.
LD B,$20 ; przygotuj się do wyzerowania 32 poprzedzających znaków.
; $08E9 : 2281
PRB_BYTES: DEC HL ; zmniejsz adres - mogłoby być DEC L.
LD (HL),$00 ; umieść bajt zero.
DJNZ PRB_BYTES ; pętla do PRB-BYTES dla wszystkich 32 bajtów
LD A,L ; pobierz pozycję wydruku znaku.
SET 7,A ; zasygnalizuj, że bufor jest pusty.
LD (PR_CC),A ; uaktualnij jednobajtową zmienną PR_CC
RET
; --------------------
; PROCEDURA 'PISZ NA'
; --------------------
; Procedura sprawdza poprawność parametrów dostarczonych poleceniu PRINT AT. Jeśli parametry są złe, sygnalizowany jest błąd B, inaczej otrzymywane są
; poprawne wartości S-POSN i DF-CC poprzez użycie procedury LOC_ADDR.
; $08F5 : 2293
PRINT_AT: LD A,$17 ; sprawdź, czy współrzędna wierszowa jest w zakresie
SUB B ; od 0 do 23
JR C,WRONG_VAL ; jeśli nie, skocz do WRONG-VAL
; $08FA : 2298
TEST_VAL: CP (IY+$22) ; porównaj wynik z liczbą wierszy w dolnej części ekranu
JP C,REPORT_5 ; jeśli pozycja na nie wkracza, zgłoś błąd skacząc do REPORT-5
INC A
LD B,A
LD A,$1F ; teraz sprawdź tak samo współrzędną kolumnową
SUB C ; powinna być w zakresie od 0 do 31
; $0905 : 2309
WRONG_VAL: JP C,REPORT_B ; jeśli nie jest, skocz do REPORT-B
ADD A,$02
LD C,A
; $090B : 2315
SET_FIELD: BIT 1,(IY+$01) ; zmienna systemowa FLAGS - drukarka w użyciu
JR Z,LOC_ADDR
LD A,$5D
SUB C
LD (PR_CC),A
RET
; -------------------------------------
; PROCEDURA 'ADRESU POZYCJI NA EKRANIE'
; -------------------------------------
; Ta ważna procedura ustawia wartość DF-CC dla zadanej pozycji na ekranie. Jeśli obraz jest zwinięty i z tego powodu nie zawiera tej pozycji,
; to pożądana linia zostaje rozwinięta.
; $0918 : 2328
LOC_ADDR: LD (S_POSN),BC
LD HL,(VARS)
LD D,C
LD A,$22
SUB C
LD C,A
LD A,$76
INC B
; $0927 : 2343
LOOK_BACK: DEC HL
CP (HL)
JR NZ,LOOK_BACK
DJNZ LOOK_BACK
INC HL
CPIR
DEC HL
LD (DF_CC),HL
SCF
RET PO
DEC D
RET Z
PUSH BC
CALL MAKE_ROOM
POP BC
LD B,C
LD H,D
LD L,E
; $0940 : 2368
EXPAND_2: LD (HL),$00
DEC HL
DJNZ EXPAND_2
EX DE,HL
INC HL
LD (DF_CC),HL
RET
; ---------------------------------------------
; PROCEDURA 'ROZWIJANIA NAZW POLECEŃ I FUNKCJI'
; ---------------------------------------------
; Kody znaków odnoszące się do nazw poleceń i funkcji są rozwijane za pomocą tej procedury. Adres każde 'rozwiniętej nazwy' w tablicy nazw jest
; znajdowany przy pomocy procedury TOKEN_ADD. Wiodąca spacja zostaje wydrukowana, jeśli określa ją bit 0 zmiennej FLAGS, następnie zostają
; wydrukowane litery nazwy i na końcu jest dodawana spacja, jeśli jest potrzebna.
; $094B : 2379
TOKENS: PUSH AF ; zachowaj kod znaku
CALL TOKEN_ADD ; procedura TOKEN_ADD wyszukuje adres nazwy w tablicy nazw wg kodu znaku. Adres w BC.
JR NC,ALL_CHARS ; jeśli wiodąca spacja nie występuje, przejdź do drukowania liter nazwy
BIT 0,(IY+$01) ; zmienna systemowa FLAGS - jeśli ustawione, wiodąca spacja
JR NZ,ALL_CHARS
XOR A ; zeruj akumulator otrzymując kod znaku spacji
RST 10H ; PISZ-AKUMULATOR - wypisz spację wiodącą
; $0959 : 2393
ALL_CHARS: LD A,(BC) ; pobierz znak nazwy do akumulatora
AND $3F ; jeśli jest to ostatni znak nazwy, to pozbądź się bitu negatywu
RST 10H ; PISZ-AKUMULATOR - wypisz znak
LD A,(BC) ; ponownie pobierz znak
INC BC ; przesuń wskaźnik na następną literę nazwy w tablicy
ADD A,A ; testuj bit negatywowy, jeśli ustawiony, to był to ostatni znak nazwy
JR NC,ALL_CHARS ; jeśli bit nie ustawiony, kontynuuj wyświetlanie liter nazwy
POP BC
BIT 7,B
RET Z
CP $1A
JR Z,TRAIL_SP
CP $38
RET C
; $096D : 2413
TRAIL_SP: XOR A
SET 0,(IY+$01) ; zmienna systemowa FLAGS - Usuń wiodącą wiodącą spację
JP PRINT_SP
; $0975 : 2421
TOKEN_ADD: PUSH HL
LD HL,TOKENST ; Adres TOKENÓW
BIT 7,A ; sprawdź kod TOKENA
JR Z,TEST_HIGH
AND $3F
; $097F : 2431
TEST_HIGH: CP $43
JR NC,FOUND
LD B,A
INC B
; $0985 : 2437
WORDS: BIT 7,(HL)
INC HL
JR Z,WORDS
DJNZ WORDS
BIT 6,A
JR NZ,COMP_FLAG
CP $18
; $0992 : 2450
COMP_FLAG: CCF
; $0993 : 2451
FOUND: LD B,H
LD C,L
POP HL
RET NC
LD A,(BC)
ADD A,$E4
RET
; --------------------------
; PROCEDURA 'JEDNO MIEJSCE'
; --------------------------
; Procedura ta jest wywoływana zawsze, gdy jest potrzebne jedno miejsce w obszarze programu lub w obrębie ekranu.
; $099B : 2459
ONE_SPACE: LD BC,$0001 ; liczba bajtów do zarezerwowania
; -------------------------
; PROCEDURA 'ZRÓB MIEJSCE'
; -------------------------
; Procedura ta tworzy BC miejsc od pozycji (HL)
; $099E : 2462
MAKE_ROOM: PUSH HL ; zachowaj HL
CALL TEST_ROOM ; sprawdź, czy jest tyle wolnej pamięci
POP HL ; odzyskaj HL
CALL POINTERS
LD HL,(STKEND) ; pobierz adres końca używanej pamięci
EX DE,HL ; adres ten będzie źródłem
LDDR ; przesuń bajty w górę robiąc miejsce
RET
; ----------------------
; PROCEDURA 'WSKAŹNIKI'
; ----------------------
; Jeśli jakieś wskaźniki muszą być zmieniane, zostaje wywołana ta procedura z wielkością zmiany w BC, a HL określającym wskaźniki do zmiany. Wszystkie
; wskaźniki wskazujące poniżej HL nie zostaną zmienione.
; $09AD : 2477
POINTERS: PUSH AF
PUSH HL
LD HL,D_FILE
LD A,$09
; $09B4 : 2484
NEXT_PTR: LD E,(HL)
INC HL
LD D,(HL)
EX (SP),HL
AND A
SBC HL,DE
ADD HL,DE
EX (SP),HL
JR NC,PTR_DONE
PUSH DE
EX DE,HL
ADD HL,BC
EX DE,HL
LD (HL),D
DEC HL
LD (HL),E
INC HL
POP DE
; $09C8 : 2504
PTR_DONE: INC HL
DEC A
JR NZ,NEXT_PTR
EX DE,HL
POP DE
POP AF
AND A
SBC HL,DE
LD B,H
LD C,L
INC BC
ADD HL,DE
EX DE,HL
RET
; -------------------------
; PROCEDURA 'ADRES WIERSZA'
; -------------------------
; Dla danego numeru wiersza w HL procedura wyznacza jego adres w obszarze programu lub adres następnego wiersza, jeśli wskazany wiersz
; nie występuje w programie.
; $09D8 : 2520
LINE_ADDR: PUSH HL ; zachowaj numer wiersza
LD HL,$407D ; umieść w HL początek obszaru programu w języku BASIC
LD D,H ; skopiuj HL do DE
LD E,L
; $09DE : 2526
NEXT_TEST: POP BC ; odtwórz numer wiersza w BC
CALL CP_LINES ; porównaj numer w BC z numerem w programie, wskazywanym przez HL
RET NC ; jeśli BC <= HL, zakończ - HL wskazuje wiersz
PUSH BC ; zachowaj na stosie numer wiersza
CALL NEXT_ONE ; przesuń się na początek następnego wiersza
EX DE,HL
JR NEXT_TEST ; kontynuuj szukanie
; -----------------------------------
; PROCEDURA 'PORÓWNAJ NUMERY WIERSZY'
; -----------------------------------
; Numer wiersza w (HL) jest porównywany z numerem w BC. Numery wierszy są przechowywane w porządku odwrotnym do normalnego przechowywania
; liczb 16 bitowych w Z80 - MSB LSB. Dzięki temu procedura porównania nieco się upraszcza.
; $09EA : 2538
CP_LINES: LD A,(HL) ; porównaj starsze bajty (HL) z BC
CP B
RET NZ ; jeśli różne, wróć
INC HL ; przejdź do młodszych bajtów
LD A,(HL) ; porównaj je
DEC HL ; przywróć HL
CP C
RET
; ---------------------------------------
; PROCEDURA 'NASTĘPNY WIERSZ LUB ZMIENNA'
; ---------------------------------------
; Ta procedura bardzo sprytnie znajduje początek kolejnego wiersza języka BASIC lub początek następnej zmiennej w obszarze zmiennych.
; Numery wierszy są rozpoznawane przez wartość starszego bajtu mniejszą od $40, a różne typy zmiennych przez bity 6 i 7 w ich nazwach.
; $09F2 : 2546
NEXT_ONE: PUSH HL
LD A,(HL)
CP $40
JR C,LINES
BIT 5,A
JR Z,NEXT_O_4
ADD A,A
JP M,NEXT_FIVE
CCF
; $0A01 : 2561
NEXT_FIVE: LD BC,$0005
JR NC,NEXT_LETT
LD C,$11
; $0A08 : 2568
NEXT_LETT: RLA
INC HL
LD A,(HL)
JR NC,NEXT_LETT
JR NEXT_ADD
; $0A0F : 2575
LINES: INC HL
; $0A10 : 2576
NEXT_O_4: INC HL
LD C,(HL)
INC HL
LD B,(HL)
INC HL
; $0A15 : 2581
NEXT_ADD: ADD HL,BC
POP DE
; -------------------
; PROCEDURA 'RÓŻNICA'
; -------------------
; Ta procedura znajduje różnicę wartości w parach rejestrów HL i DL. Wynik jest zwracany w parze rejestrów BC
; $0A17 : 2583
DIFFER: AND A ; zeruj przeniesienie
SBC HL,DE ; HL = HL - DE
LD B,H ; różnica do BC
LD C,L
ADD HL,DE ; przywróć poprzednią wartość HL
EX DE,HL
RET
; -------------------------
; PROCEDURA 'DOLNE WIERSZE'
; -------------------------
; Procedura czyści dolne wiersze ekranu.
; $0A1F : 2591
LINE_ENDS: LD B,(IY+$22) ; zmienna systemowa DF_SZ
PUSH BC
CALL B_LINES
POP BC
DEC B
JR B_LINES
; -----------------------
; PROCEDURA ROZKAZU 'CLS'
; -----------------------
; Procedura czyści zawartość ekranu
; $0A2A : 2602
CLS: LD B,$18 ; do B załaduj liczbę wierszy na ekranie $18 = 24
; Znajdowany jest adres części ekranu do wyczyszczenia oraz wykonany zostaje test, czy system zawiera mniej, czy więcej 3 1/4 KB pamięci RAM.
; $0A2C : 2604
B_LINES: RES 1,(IY+$01) ; zmienna systemowa FLAGS - Sygnalizuj brak drukarki
LD C,$21
PUSH BC
CALL LOC_ADDR
POP BC
LD A,($4005) ; starszy bajt zmiennej systemowej RAMTOP
CP $4D ; sprawdź, czy ekran jest zwinięty
JR C,COLLAPSED ; jeśli tak, skocz naprzód do COLLAPSED
; W przypadku rozwiniętego pliku obrazu odpowiednia liczba spacji zostaje wydrukowana, aby wyczyścić założoną liczbę wierszy
SET 7,(IY+$3A) ; zmienna systemowa S_POSN_y
; $0A42 : 2626
CLEAR_LOC: XOR A ; przygotuj spację
CALL PRINT_SP ; wydrukuj spację
LD HL,(S_POSN)
LD A,L
OR H
AND $7E
JR NZ,CLEAR_LOC
JP LOC_ADDR
; Jeśli plik obrazu jest zwinięty, zostaje użyta instrukcja LDIR do skopiowania znaku nowego wiersza - N/L tyle razy, ile przechowuje rejestr C
; (poprzednio B). Następnie zostaje pobrana zmienna VARS i zwrócona nadmiarowa pamięć w obszarze obrazu.
; $0A52 : 2642
COLLAPSED: LD D,H
LD E,L
DEC HL
LD C,B
LD B,$00
LDIR
LD HL,(VARS)
; ------------------------
; PROCEDURA 'ODZYSKIWANIA'
; ------------------------
; Najpierw zmieniane są wskaźniki, a następnie określony obszar pamięci RAM zostaje odzyskany przez użycie instrukcji LDIR do nadpisania
; niechcianej części pamięci RAM.
; $0A5D : 2653
RECLAIM_1: CALL DIFFER
; $0A60 : 2656
RECLAIM_2: PUSH BC
LD A,B
CPL
LD B,A
LD A,C
CPL
LD C,A
INC BC
CALL POINTERS
EX DE,HL
POP HL
ADD HL,DE
PUSH DE
LDIR
POP HL
RET
; ---------------------------------
; PROCEDURA 'NUMERU WIERSZA EDYCJI'
; ---------------------------------
; Ta procedura jest wykorzystywana do określenia, czy bieżący wiersz edycji rozpoczyna się poprawnym numerem od 1 do 9999. Wskaźnik CH_ADD
; jest tymczasowo używany do wskazywania wzdłuż wiersza edycji. Powrót następuje, jeśli jest wykonywany rozkaz INPUT. Do pobrania numeru wiersza
; jest najpierw wywoływana procedura INT_TO_FP, a następnie procedura FP_TO_BC do stworzenia wartości całkowitej w BC. Wartość ta jest dalej
; sprawdzana na zakres 1-9999. Procedura wychodzi poprzez procedurę SET_MEM, która zeruje STKEND.
; $0A73 : 2675
E_LINE_NO: LD HL,(E_LINE)
CALL TEMP_PTR2
RST 18H ; POBIERZ-ZNAK
BIT 5,(IY+$2D) ; zmienna systemowa FLAGX
RET NZ
LD HL,MEMBOT
LD (STKEND),HL
CALL INT_TO_FP ; przekształć numer wiersza w liczbę zmiennoprzecinkową na stosie kalkulatora
CALL FP_TO_BC ; pobierz liczbę zmiennoprzecinkową ze stosu kalkulatora i umieść ją w BC jako liczbę całkowitą
JR C,NO_NUMBER ; jeśli nie jest to poprawna liczba, skocz do raportu błędu
LD HL,$D8F0 ; wartość '-10000'
ADD HL,BC ; sprawdź, czy numer mieści się w zakresie 1-9999
; $0A91 : 2705
NO_NUMBER: JP C,REPORT_C ; jeśli zły numer, zgłoś błąd C
CP A
JP SET_MIN
; -------------------------------------------------
; PROCEDURY WYŚWIETLANIA 'RAPORTU I NUMERU WIERSZA'
; -------------------------------------------------
; Punkt wejścia OUT_NUM jest używany do wyświetlania numerów wierszy z raportem błędu
; $0A98 : 2712
OUT_NUM: PUSH DE
PUSH HL
XOR A
BIT 7,B
JR NZ,UNITS
LD H,B
LD L,C
LD E,$FF
JR THOUSAND
; Punkt wejścia OUT_NO jest używany do wyświetlania numerów wierszy na początku wierszy języka BASIC.
; $0AA5 : 2725
OUT_NO: PUSH DE
LD D,(HL)
INC HL
LD E,(HL)
PUSH HL
EX DE,HL
LD E,$00 ; ustaw E na wiodącą spację
; $0AAD : 2733
THOUSAND: LD BC,$FC18
CALL OUT_DIGIT
LD BC,$FF9C
CALL OUT_DIGIT
LD C,$F6
CALL OUT_DIGIT
LD A,L
; $0ABF : 2751
UNITS: CALL OUT_CODE
POP HL
POP DE
RET
; --------------------------
; PROCEDURA 'USUŃ ZE STOSU'
; --------------------------
; Ten podprogram jest używany do wcześniejszego powrotu z procedury, gdy sprawdzana jest składnia. W ZX-81 te same procedury, które wykonują
; polecenia, również sprawdzają składnie w wierszu edycyjnym. Pozwala to dokładnie umieścić znacznik błędu w miejscu, gdzie naruszona jest składnia.
; Sekwencja CALL SYNTAX-Z ; RET Z może być zastąpiona przez wywołanie tego podprogramu, chociaż nie zastąpił on wszystkich wystąpień powyższych
; dwóch instrukcji.
; $0AC5 : 2757
UNSTACK_Z: CALL SYNTAX_Z ; procedura SYNTAX-Z zwraca ustawiony znacznik zera, gdy sprawdza składnię.
POP HL ; pobierz adres powrotny.
RET Z ; jeśli jest sprawdzana składnia, wróć do poprzedniej procedury wywołującej
JP (HL) ; inaczej skocz do wywołującej procedury, jak zrobiłby to rozkaz RET.
; ----------------------------
; PROCEDURA POLECENIA 'LPRINT'
; ----------------------------
; Bit 1 zmiennej FLAGS jest zawsze ustawiony na 1 przy drukowaniu na drukarce.
; $0ACB : 2763
LPRINT: SET 1,(IY+$01) ; zmienna systemowa FLAGS - informuj o używaniu drukarki
; ---------------------------
; PROCEDURA POLECENIA 'PRINT'
; ---------------------------
; Test, czy jest sam rozkaz PRINT
; $0ACF : 2767
PRINT: LD A,(HL) ; pobierz znak do drukowania
CP $76 ; NEWLINE?
JP Z,PRINT_END ; jeśli tak, skocz na koniec procedury
; Tworzona jest teraz pętla, która zajmie się kolejnymi elementami wiersza polecenia PRINT. Najpierw sprawdzane jest, czy kolejny znak
; to przecinek lub średnik - oba posiadają specyficzną funkcję w instrukcji PRINT.
; $0AD5 : 2773
PRINT_1: SUB $1A
ADC A,$00
JR Z,SPACING
; Teraz sprawdzane jest, czy następnym znakiem jest znak AT.
CP $A7
JR NZ,NOT_AT ; jeśli nie, przeskocz naprzód do NOT_AT
RST 20H ; pobierz następny znak
CALL CLASS_6 ; oblicz następne wyrażenie - numer wiersza
CP $1A ; sprawdź, czy separatorem jest przecinek
JP NZ,REPORT_C ; jeśli nie, zgłoś błąd C
RST 20H ; pobieramy kolejny znak
CALL CLASS_6 ; oblicz następne wyrażenie - numer kolumny
CALL SYNTAX_ON ; wykonaj test, czy wiersz jest wykonywany lub czy jest sprawdzana składnia. Jeśli jest tylko sprawdzana
; składnia, zostanie wykonany skok pośredni do PRINT_ON
; Numer kolumny i wiersza znajduje się na stosie kalkulatora, jednakże muszą one zostać zamienione miejscami.
RST 28H ; KALKULATOR
.BYTE $01 ; zamień
.BYTE $34 ; koniec-obliczeń
CALL STK_TO_BC ; pobierz do pary rejestrów BC dwie wartości ze stosu kalkulatora
CALL PRINT_AT ; ustaw pozycję wydruku
JR PRINT_ON ; kontynuuj drukowanie
; $0AFA : 2810
NOT_AT: CP $A8 ; sprawdź, czy drukowany znak to TAB
JR NZ,NOT_TAB ; jeśli nie, przeskocz wprzód do NOT_TAB
RST 20H ; pobierz kolejny znak
CALL CLASS_6 ; oblicz wartość wyrażenia
CALL SYNTAX_ON ; sprawdź, czy jest sprawdzana składnia. Jeśli tak, skocz do PRINT_ON
CALL STK_TO_A ; pobierz ze stosu kalkulatora wartość tabulatora do akumulatora
JP NZ,REPORT_B ; w przypadku błędu, wyjdź przez RAPORT_B
AND $1F ; inaczej sprawdź poprawność argumentu TAB
LD C,A
BIT 1,(IY+$01) ; zmienna systemowa FLAGS - drukarka w użyciu?
JR Z,TAB_TEST
SUB (IY+$38) ; zmienna systemowa PR_CC
SET 7,A
ADD A,$3C
CALL NC,COPY_BUFF
; $0B1E : 2846
TAB_TEST: ADD A,(IY+$39) ; zmienna systemowa S_POSN_x
CP $21
LD A,($403A) ; zmienna systemowa S_POSN_y
SBC A,$01
CALL TEST_VAL ; znajdź nowe wartości dla DF_CC i S_POSN
SET 0,(IY+$01) ; zmienna systemowa FLAGS - zlikwiduj wiodącą spację
JR PRINT_ON ; kontynuuj wydruk
; $0B31 : 2865
NOT_TAB: CALL SCANNING ; oblicz wartość wyrażenia, wynik umieść na stosie kalkulatora
CALL PRINT_STK ; wyświetl ostatnią wartość ze stosu kalkulatora
; Procedura teraz sprawdza, czy w wierszu polecenia PRINT występuje kolejne wyrażenie
; $0B37 : 2871
PRINT_ON: RST 18H ; POBIERZ-ZNAK
SUB $1A
ADC A,$00
JR Z,SPACING
CALL CHECK_END
JP PRINT_END
; Tutaj rozdzielane są dwa znaki sterujące wydrukiem - przecinek i średnik
; $0B44 : 2884
SPACING: CALL NC,FIELD
RST 20H ; NASTĘPNY-ZNAK
CP $76
RET Z
JP PRINT_1
; Ten podprogram powoduje skok do PRINT_ON w czasie sprawdzania składni
; $0B4E : 2894
SYNTAX_ON: CALL SYNTAX_Z ; czy jest sprawdzana składnia?
RET NZ ; jeśli nie, wróć
POP HL ; usuń ze stosu adres powrotu
JR PRINT_ON ; kontynuuj przeglądanie wiersza wydruku
; Procedura drukuje zawartość szczytu stosu
; $0B55 : 2901
PRINT_STK: CALL UNSTACK_Z ; jeśli jest sprawdzana składnia, nastąpi wyjście z procedury
BIT 6,(IY+$01) ; zmienna systemowa FLAGS - wynik liczbowy czy znakowy?
CALL Z,STK_FETCH ; - pobierz ze stosu parametry łańcucha. Łańcuchy i liczby obsługuj osobno
JR Z,PR_STR_4 ; jeśli się udało, skocz do obsługi łańcuchów
JP PRINT_FP ; inaczej obsłuż wydruk liczby.
; Długość łańcucha jest umieszczona w parze rejestrów BC, a adres początku łańcucha znajduje się w parze rejestrów DE.
; $0B64 : 2916
PR_STR_1: LD A,$0B ; znak cudzysłowu
; $0B66 : 2918
PR_STR_2: RST 10H ; PISZ-AKUMULATOR
; $0B67 : 2919
PR_STR_3: LD DE,(X_PTR) ; pobierz wskaźnik łańcucha ze zmiennej systemowej X_PTR
; $0B6B : 2923
PR_STR_4: LD A,B ; sprawdź, czy koniec łańcucha
OR C
DEC BC ; zmniejsz długość łańcucha
RET Z ; powróć, jeśli wydrukowano wszystkie znaki w łańcuchu
LD A,(DE) ; pobierz znak z łańcucha
INC DE ; zwiększ wskaźnik łańcucha
LD (X_PTR),DE ; zachowaj go w zmiennej systemowej X_PTR
BIT 6,A ; sprawdź, czy znak jednoliterowy
JR Z,PR_STR_2 ; jeśli tak, drukuj go
CP $C0 ; podwójny cudzysłów - drukuj jako zwykły cudzysłów
JR Z,PR_STR_1
PUSH BC ; zachowaj długość łańcucha
CALL TOKENS ; wydrukuj znak wieloliterowy
POP BC ; odtwórz długość łańcucha
JR PR_STR_3 ; kontynuuj drukowanie łańcucha
; $0B84 : 2948
PRINT_END: CALL UNSTACK_Z ; jeśli sprawdzana jest składnia, nastąpi wyjście z procedury
LD A,$76 ; inaczej drukuj znak końca wiersza
RST 10H ; PISZ-AKUMULATOR
RET
; Tutaj znajdowana jest właściwa wartość zmiennej systemowej S_POSN oraz w razie potrzeby również PR_CC.
; $0B8B : 2955
FIELD: CALL UNSTACK_Z ; wróć przy sprawdzaniu składni
SET 0,(IY+$01) ; zmienna systemowa FLAGS - usuń wiodącą spację
XOR A ; kod spacji
RST 10H ; wyświetl spację
LD BC,(S_POSN)
LD A,C
BIT 1,(IY+$01) ; zmienna systemowa FLAGS - drukarka w użyciu?
JR Z,CENTRE
LD A,$5D
SUB (IY+$38) ; zmienna systemowa PR_CC
; $0BA4 : 2980
CENTRE: LD C,$11
CP C
JR NC,RIGHT
LD C,$01
; $0BAB : 2987
RIGHT: CALL SET_FIELD
RET
; ---------------------------------
; PROCEDURY POLECEŃ 'PLOT I UNPLOT'
; ---------------------------------
; $0BAF : 2991
PLOT_UNP: CALL STK_TO_BC
LD (COORDS),BC
LD A,$2B
SUB B
JP C,REPORT_B
LD B,A
LD A,$01
SRA B
JR NC,COLUMNS
LD A,$04
; $0BC5 : 3013
COLUMNS: SRA C
JR NC,FIND_ADDR
RLCA
; $0BCA : 3018
FIND_ADDR: PUSH AF
CALL PRINT_AT
LD A,(HL)
RLCA
CP $10
JR NC,TABLE_PTR
RRCA
JR NC,SQ_SAVED
XOR $8F
; $0BD9 : 3033
SQ_SAVED: LD B,A
; $0BDA : 3034
TABLE_PTR: LD DE,P_UNPLOT
LD A,(T_ADDR)
SUB E
JP M,PLOT
POP AF
CPL
AND B
JR UNPLOT
; $0BE9 : 3049
PLOT: POP AF
OR B
; $0BEB : 3051
UNPLOT: CP $08
JR C,PLOT_END
XOR $8F
; $0BF1 : 3057
PLOT_END: EXX
RST 10H ; PISZ-AKUMULATOR
EXX
RET
; -----------------------
; PROCEDURA 'STACK-TO-BC'
; -----------------------
; Procedura pobiera dwie liczby zmiennoprzecinkowe ze stosu kalkulatora, zamienia je na liczby całkowite o zakresie 0-255 i umieszcza
; w parze rejestrów BC
; $0BF5 : 3061
STK_TO_BC: CALL STK_TO_A
LD B,A
PUSH BC
CALL STK_TO_A
LD E,C
POP BC
LD D,C
LD C,A
RET
; ----------------------
; PROCEDURA 'STACK-TO-A'
; ----------------------
; Procedura pobiera liczbę zmiennoprzecinkową ze stosu kalkulatora, zamienia ją na liczbę całkowitą o zakresie 0-255 i umieszcza w rejestrze A
; $0C02 : 3074
STK_TO_A: CALL FP_TO_A
JP C,REPORT_B
LD C,$01
RET Z
LD C,$FF
RET
; ------------------------------
; PROCEDURA 'PRZEWIJANIA EKRANU'
; ------------------------------
; $0C0E : 3086
SCROLL: LD B,(IY+$22) ; zmienna systemowa DF_SZ
LD C,$21
CALL LOC_ADDR
CALL ONE_SPACE
LD A,(HL)
LD (DE),A
INC (IY+$3A) ; zmienna systemowa S_POSN_y
LD HL,(D_FILE)
INC HL
LD D,H
LD E,L
CPIR
JP RECLAIM_1
; -----------------
; TABLICE 'SKŁADNI'
; -----------------
; i) TAblica przesunięć
; $0C29 : 3113
OFFSET_T:.BYTE P_LPRINT - $ ; 8B przesunięcie pod adres P-LPRINT
.BYTE P_LLIST - $ ; 8D przesunięcie pod adres P-LLIST
.BYTE P_STOP - $ ; 2D przesunięcie pod adres P-STOP
.BYTE P_SLOW - $ ; 7F przesunięcie pod adres P-SLOW
.BYTE P_FAST - $ ; 81 przesunięcie pod adres P-FAST
.BYTE P_NEW - $ ; 49 przesunięcie pod adres P-NEW
.BYTE P_SCROLL - $ ; 75 przesunięcie pod adres P-SCROLL
.BYTE P_CONT - $ ; 5F przesunięcie pod adres P-CONT
.BYTE P_DIM - $ ; 40 przesunięcie pod adres P-DIM
.BYTE P_REM - $ ; 42 przesunięcie pod adres P-REM
.BYTE P_FOR - $ ; 2B przesunięcie pod adres P-FOR
.BYTE P_GOTO - $ ; 17 przesunięcie pod adres P-GOTO
.BYTE P_GOSUB - $ ; 1F przesunięcie pod adres P-GOSUB
.BYTE P_INPUT - $ ; 37 przesunięcie pod adres P-INPUT
.BYTE P_LOAD - $ ; 52 przesunięcie pod adres P-LOAD
.BYTE P_LIST - $ ; 45 przesunięcie pod adres P-LIST
.BYTE P_LET - $ ; 0F przesunięcie pod adres P-LET
.BYTE P_PAUSE - $ ; 6D przesunięcie pod adres P-PAUSE
.BYTE P_NEXT - $ ; 2B przesunięcie pod adres P-NEXT
.BYTE P_POKE - $ ; 44 przesunięcie pod adres P-POKE
.BYTE P_PRINT - $ ; 2D przesunięcie pod adres P-PRINT
.BYTE P_PLOT - $ ; 5A przesunięcie pod adres P-PLOT
.BYTE P_RUN - $ ; 3B przesunięcie pod adres P-RUN
.BYTE P_SAVE - $ ; 4C przesunięcie pod adres P-SAVE
.BYTE P_RAND - $ ; 45 przesunięcie pod adres P-RAND
.BYTE P_IF - $ ; 0D przesunięcie pod adres P-IF
.BYTE P_CLS - $ ; 52 przesunięcie pod adres P-CLS
.BYTE P_UNPLOT - $ ; 5A przesunięcie pod adres P-UNPLOT
.BYTE P_CLEAR - $ ; 4D przesunięcie pod adres P-CLEAR
.BYTE P_RETURN - $ ; 15 przesunięcie pod adres P-RETURN
.BYTE P_COPY - $ ; 6A przesunięcie pod adres P-COPY
; ii) Tablica parametrów
; $0C48 : 3144
P_LET: .BYTE $01 ; Klasa-01 - wymagana zmienna
.BYTE $14 ; Separator: '='
.BYTE $02 ; Klasa-02 - musi wystąpić wyrażenie liczbowe lub łańcuchowe
; $0C4B : 3147
P_GOTO: .BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD GOTO ; Adres: $0E81; Adres: GOTO
; $0C4F : 3151
P_IF: .BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $DE ; Separator: 'THEN'
.BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD IF ; Adres: $0DAB; Adres: IF
; $0C54 : 3156
P_GOSUB: .BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD GOSUB ; Adres: $0EB5; Adres: GOSUB
; $0C58 : 3160
P_STOP: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD STOP ; Adres: $0CDC; Adres: STOP
; $0C5B : 3163
P_RETURN:.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD RETURN ; Adres: $0ED8; Adres: RETURN
; $0C5E : 3166
P_FOR: .BYTE $04 ; Klasa-04 - musi wystąpić zmienna jednoliterowa
.BYTE $14 ; Separator: '='
.BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $DF ; Separator: 'TO'
.BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD FOR ; Adres: $0DB9; Adres: FOR
; $0C66 : 3174
P_NEXT: .BYTE $04 ; Klasa-04 - musi wystąpić zmienna jednoliterowa
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD NEXT ; Adres: $0E2E; Adres: NEXT
; $0C6A : 3178
P_PRINT: .BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD PRINT ; Adres: $0ACF; Adres: PRINT
; $0C6D : 3181
P_INPUT: .BYTE $01 ; Klasa-01 - wymagana zmienna
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD INPUT ; Adres: $0EE9; Adres: INPUT
; $0C71 : 3185
P_DIM: .BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD DIM ; Adres: $1409; Adres: DIM
; $0C74 : 3188
P_REM: .BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD REM ; Adres: $0D6A; Adres: REM
; $0C77 : 3191
P_NEW: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD NEW ; Adres: $03C3; Adres: NEW
; $0C7A : 3194
P_RUN: .BYTE $03 ; Klasa-03 - może wystąpić wyrażenie liczbowe, inaczej wartość zero
.WORD RUN ; Adres: $0EAF; Adres: RUN
; $0C7D : 3197
P_LIST: .BYTE $03 ; Klasa-03 - może wystąpić wyrażenie liczbowe, inaczej wartość zero
.WORD LIST ; Adres: $0730; Adres: LIST
; $0C80 : 3200
P_POKE: .BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $1A ; Separator: ','
.BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD POKE ; Adres: $0E92; Adres: POKE
; $0C86 : 3206
P_RAND: .BYTE $03 ; Klasa-03 - może wystąpić wyrażenie liczbowe, inaczej wartość zero
.WORD RAND ; Adres: $0E6C; Adres: RAND
; $0C89 : 3209
P_LOAD: .BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD LOAD ; Adres: $0340; Adres: LOAD
; $0C8C : 3212
P_SAVE: .BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD SAVE ; Adres: $02F6; Adres: SAVE
; $0C8F : 3215
P_CONT: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD CONT ; Adres: $0E7C; Adres: CONT
; $0C92 : 3218
P_CLEAR: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD CLEAR ; Adres: $149A; Adres: CLEAR
; $0C95 : 3221
P_CLS: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD CLS ; Adres: $0A2A; Adres: CLS
; $0C98 : 3224
P_PLOT: .BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $1A ; Separator: ','
.BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD PLOT_UNP ; Adres: $0BAF; Adres: PLOT/UNP
; $0C9E : 3230
P_UNPLOT:.BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $1A ; Separator: ','
.BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD PLOT_UNP ; Adres: $0BAF; Adres: PLOT/UNP
; $0CA4 : 3236
P_SCROLL:.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD SCROLL ; Adres: $0C0E; Adres: SCROLL
; $0CA7 : 3239
P_PAUSE: .BYTE $06 ; Klasa-06 - musi wystąpić wyrażenie liczbowe
.BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD PAUSE ; Adres: $0F32; Adres: PAUSE
; $0CAB : 3243
P_SLOW: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD SLOW ; Adres: $0F2B; Adres: SLOW
; $0CAE : 3246
P_FAST: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD FAST ; Adres: $0F23; Adres: FAST
; $0CB1 : 3249
P_COPY: .BYTE $00 ; Klasa-00 - brak dalszych parametrów
.WORD COPY ; Adres: $0869; Adres: COPY
; $0CB4 : 3252
P_LPRINT:.BYTE $05 ; Klasa-05 - zmienna składnia w całości sprawdzana przez procedurę
.WORD LPRINT ; Adres: $0ACB; Adres: LPRINT
; $0CB7 : 3255
P_LLIST: .BYTE $03 ; Klasa-03 - może wystąpić wyrażenie liczbowe, inaczej wartość zero
.WORD LLIST ; Adres: $072C; Adres: LLIST
; ------------------------------
; PROCEDURA 'SKANOWANIE WIERSZA'
; ------------------------------
; $0CBA : 3258
LINE_SCAN: LD (IY+$01),$01 ; zmienna systemowa FLAGS
CALL E_LINE_NO
; $0CC1 : 3265
LINE_RUN: CALL SET_MIN
LD HL,ERR_NR
LD (HL),$FF
LD HL,FLAGX
BIT 5,(HL)
JR Z,LINE_NULL
CP $E3 ; 'STOP' ?
LD A,(HL)
JP NZ,INPUT_REP
CALL SYNTAX_Z
RET Z
RST 08H ; ERROR-1
.BYTE $0C ; Raport Błędu: PRZERWANIE - CONT powtarza
; --------------------------
; PROCEDURA POLECENIA 'STOP'
; --------------------------
; $0CDC : 3292
STOP: RST 08H ; ERROR-1
.BYTE $08 ; Raport Błędu: polecenie STOP
; przetwarzanie wiersza jest kontynuowane sprawdzeniem, czy następują jedynie spacje z kończącym je znakiem NEWLINE. Rozkaz IF również zagląda tutaj,
; jeśli wyrażenie testowe ma wartość true, aby wykonać polecenie za THEN, lecz polecenia tego wcale nie musi być, zatem
; 10 IF 1=1 THEN
; przechodzi test składniowy na wszystkich komputerach ZX.
; $0CDE : 3294
LINE_NULL: RST 18H ; POBIERZ-ZNAK
LD B,$00 ; przygotuj wcześniej indeks
CP $76 ; porównaj z NEWLINE.
RET Z ; wróć, jeśli jest równe.
LD C,A ; przenieś znak do C.
RST 20H ; NASTĘPNY-ZNAK - przesuwa na kolejną pozycję.
LD A,C ; znak do A
SUB $E1 ; odejmij kod 'LPRINT' - najmniejszy kod rozkazu.
JR C,REPORT_C2 ; jeśli mniejszy, skocz do REPORT-C2
LD C,A ; zmniejszony kod do C
LD HL,OFFSET_T ; ustaw w HL adres tablicy przesunięć.
ADD HL,BC ; indeksuj w tablicy.
LD C,(HL) ; pobierz przesunięcie
ADD HL,BC ; indeksuj w tablicy parametrów.
JR GET_PARAM
; $0CF4 : 3316
SCAN_LOOP: LD HL,(T_ADDR)
; -> Punkt wejścia do pętli skanującej
; $0CF7 : 3319
GET_PARAM: LD A,(HL)
INC HL
LD (T_ADDR),HL
LD BC,SCAN_LOOP ; Adres: SCAN-LOOP
PUSH BC ; jest umieszczany na stosie maszynowym.
LD C,A
CP $0B
JR NC,SEPARATOR
LD HL,CLASS_TBL ; adres tablicy klas.
LD B,$00
ADD HL,BC
LD C,(HL)
ADD HL,BC
PUSH HL
RST 18H ; POBIERZ-ZNAK
RET ; pośredni skok do procedury klas a poprzez późniejszy RET do SCAN-LOOP
; ---------------------
; PROCEDURA 'SEPARATOR'
; ---------------------
; $0D10 : 3344
SEPARATOR: RST 18H ; POBIERZ-ZNAK
CP C
JR NZ,REPORT_C2 ; REPORT-C2 - Nonsens w języku BASIC
RST 20H ; NASTĘPNY-ZNAK
RET
; ----------------------
; TABLICA 'KLAS POLECEŃ'
; ----------------------
;
; $0D16 : 3350
CLASS_TBL:.BYTE CLASS_0 - $ ; 17 przesunięcie pod adres CLASS-0
.BYTE CLASS_1 - $ ; 25 przesunięcie pod adres CLASS-1
.BYTE CLASS_2 - $ ; 53 przesunięcie pod adres CLASS-2
.BYTE CLASS_3 - $ ; 0F przesunięcie pod adres CLASS-3
.BYTE CLASS_4 - $ ; 6B przesunięcie pod adres CLASS-4
.BYTE CLASS_5 - $ ; 13 przesunięcie pod adres CLASS-5
.BYTE CLASS_6 - $ ; 76 przesunięcie pod adres CLASS-6
; --------------------------
; PROCEDURA 'SPRAWDŹ KONIEC'
; --------------------------
; Sprawdź koniec polecenia i czy nie ma nadmiarowych znaków za poprawnie sparsowanym poleceniem. Ponieważ w wierszu dozwolone jest tylko jedno
; polecenie, to jedynym dozwolonym znakiem za rozkazem może być NEWLINE.
; $0D1D : 3357
CHECK_END: CALL SYNTAX_Z
RET NZ ; w czasie wykonania wróć.
POP BC ; inaczej usuń adres powrotny.
; $0D22 : 3362
CHECK_2: LD A,(HL) ; pobierz znak.
CP $76 ; sprawdź, czy NEWLINE.
RET Z ; wróć, jeśli tak.
; $0D26 : 3366
REPORT_C2: JR REPORT_C ; REPORT-C2 - Nonsens w języku BASIC
; -------------------------
; KLASY ROZKAZÓW 03, 00, 05
; -------------------------
; $0D28 : 3368
CLASS_3: CP $76
CALL NO_TO_STK
; $0D2D : 3373
CLASS_0: CP A
; $0D2E : 3374
CLASS_5: POP BC
CALL Z,CHECK_END
EX DE,HL
LD HL,(T_ADDR)
LD C,(HL)
INC HL
LD B,(HL)
EX DE,HL
; $0D3A : 3386
CLASS_END: PUSH BC
RET
; -----------------------------
; KLASY ROZKAZÓW 01, 02, 04, 06
; -----------------------------
; $0D3C : 3388
CLASS_1: CALL LOOK_VARS
; $0D3F : 3391
CLASS_4_2: LD (IY+$2D),$00 ; zmienna systemowa FLAGX
JR NC,SET_STK
SET 1,(IY+$2D) ; zmienna systemowa FLAGX
JR NZ,SET_STRLN
; $0D4B : 3403
REPORT_2: RST 08H ; ERROR-1
.BYTE $01 ; Raport Błędu: Zmienna nie znaleziona
; $0D4D : 3405
SET_STK: CALL Z,STK_VAR
BIT 6,(IY+$01) ; zmienna systemowa FLAGS - wynik liczbowy czy znakowy?
JR NZ,SET_STRLN
XOR A
CALL SYNTAX_Z
CALL NZ,STK_FETCH
LD HL,FLAGX
OR (HL)
LD (HL),A
EX DE,HL
; $0D63 : 3427
SET_STRLN: LD (STRLEN),BC
LD (DEST),HL
; PROCEDURA POLECENIA 'REM'
; $0D6A : 3434
REM: RET
; $0D6B : 3435
CLASS_2: POP BC
LD A,(FLAGS)
; $0D6F : 3439
INPUT_REP: PUSH AF
CALL SCANNING
POP AF
LD BC,LET
LD D,(IY+$01) ; zmienna systemowa FLAGS
XOR D
AND $40
JR NZ,REPORT_C
BIT 7,D
JR NZ,CLASS_END
JR CHECK_2
; $0D85 : 3461
CLASS_4: CALL LOOK_VARS
PUSH AF
LD A,C
OR $9F
INC A
JR NZ,REPORT_C
POP AF
JR CLASS_4_2
; $0D92 : 3474
CLASS_6: CALL SCANNING
BIT 6,(IY+$01) ; zmienna systemowa FLAGS - wynik liczbowy czy znakowy?
RET NZ
; $0D9A : 3482
REPORT_C: RST 08H ; ERROR-1
.BYTE $0B ; Raport błędu - Nonsens w języku BASIC
; --------------------------
; PROCEDURA 'LICZBA NA STOS'
; --------------------------
; $0D9C : 3484
NO_TO_STK: JR NZ,CLASS_6 ; wróć do CLASS-6 z niezerową liczbą.
CALL SYNTAX_Z
RET Z ; wróć, jeśli sprawdza się składnię.
; w czasie wykonania umieszczane jest na stosie kalkulatora zero, jako wartość standardowa.
RST 28H ; KALKULATOR
.BYTE $A0 ; na-stos-zero
.BYTE $34 ; koniec-obliczeń
RET
; ----------------------
; PROCEDURA 'SKŁADNIA-Z'
; ----------------------
; Ta procedura wraca z ustawionym znacznikiem Z, jeśli sprawdzana jest składnia. Wywołanie tej procedury zajmuje 3 bajty w porównaniu z 4,
; gdyby test był wykonywany na miejscu. Oczywiście cierpi na tym szybkość działania ZX-81.
; $0DA6 : 3494
SYNTAX_Z: BIT 7,(IY+$01) ; testuj FLAGS - jedynie sprawdzana składnia?
RET
; ------------------------
; PROCEDURA POLECENIA 'IF'
; ------------------------
; W czasie wykonania procedury klas wyliczyły wartość warunku i wynik, prawda lub fałsz, znajduje się na stosie kalkulatora.
; $0DAB : 3499
IF: CALL SYNTAX_Z ; wyjdź przy sprawdzaniu składni
JR Z,IF_END ; naprzód do IF-END w przypadku sprawdzania składni
; inaczej usuń wartość logiczną ze stosu kalkulatora.
RST 28H ; KALKULATOR
.BYTE $02 ; usuń
.BYTE $34 ; koniec-obliczeń
; rejestr DE wskazóje wykładnik wartości zmiennoprzecinkowej.
LD A,(DE) ; pobierz wykładnik
AND A ; sprawdź, czy zero - FALSE.
RET Z ; wróć, jeśli tak.
; $0DB6 : 3510
IF_END: JP LINE_NULL ; skocz wstecz do LINE-NULL
; -------------------------
; PROCEDURA POLECENIA 'FOR'
; -------------------------
; $0DB9 : 3513
FOR: CP $E0 ; czy bieżący znak jest poleceniem 'STEP' ?
JR NZ,F_USE_ONE ; jeśli nie, to naprzód do F-USE-ONE
RST 20H ; NASTĘPNY-ZNAK
CALL CLASS_6 ; procedura CLASS-6 umieszcza liczbę na stosie
CALL CHECK_END
JR F_REORDER
; $0DC6 : 3526
F_USE_ONE: CALL CHECK_END
RST 28H ; KALKULATOR
.BYTE $A1 ; jeden-na-stos
.BYTE $34 ; koniec-obliczeń
; $0DCC : 3532
F_REORDER: RST 28H ; KALKULATOR v, l, s.
.BYTE $C0 ; stos-do-pamięci-0 v, l, s.
.BYTE $02 ; usuń v, l.
.BYTE $01 ; zamień l, v.
.BYTE $E0 ; na-stos-paimięć-0 l, v, s.
.BYTE $01 ; zamień l, s, v.
.BYTE $34 ; koniec-obliczeń l, s, v.
CALL LET
LD (MEM),HL ; ustaw MEM na adresowaną zmienną.
DEC HL ; wskaż na znak.
LD A,(HL)
SET 7,(HL)
LD BC,$0006
ADD HL,BC
RLCA
JR C,F_LMT_STP
SLA C
CALL MAKE_ROOM
INC HL
; $0DEA : 3562
F_LMT_STP: PUSH HL
RST 28H ; KALKULATOR
.BYTE $02 ; usuń
.BYTE $02 ; usuń
.BYTE $34 ; koniec-obliczeń
POP HL
EX DE,HL
LD C,$0A ; ma być przesunięte dziesięć bajtów.
LDIR ; kopiuj bajty
LD HL,(PPC) ; ustaw HL na zmienną systemową PPC - bieżący wiersz.
EX DE,HL ; przenieś do DE, wskaźnik zmiennej do HL.
INC DE ; początek pętli będzie na następnym wierszu
LD (HL),E
INC HL
LD (HL),D
CALL NEXT_LOOP ; procedura NEXT-LOOP zajmuje się wstępnym obiegiem.
RET NC ; jeśli możliwe, wróć.
; inaczej program wykonuje się od punktu za pierwszym pasującym rozkazem NEXT.
BIT 7,(IY+$08) ; sprawdź starszy bajt PPC
RET NZ ; wróć, jeśli powyżej 32767 ???
LD B,(IY+$2E) ; pobierz nazwę zmiennej ze STRLEN
RES 6,B ; zrób ją prawdziwą literą.
LD HL,(NXTLIN) ; ustaw HL z NXTLIN
; teraz wejdź do pętli szukającej pasującego NEXT.
; $0E0E : 3598
NXTLIN_NO: LD A,(HL) ; pobierz starszy bajt numeru wiersza.
AND $C0 ; wymaskuj dolne bity $3F
JR NZ,FOR_END ; skocz na koniec do FOR-END
PUSH BC ; zachowaj literę
CALL NEXT_ONE ; procedura NEXT-ONE znajdzie następny wiersz.
POP BC ; odzyskaj literę
INC HL ; przejdź nad numerem wiersza
INC HL ; aż do długości
INC HL ; wiersza
CALL TEMP_PTR1 ; procedura TEMP-PTR1 ustawia CH_ADD
RST 18H ; POBIERZ-ZNAK
CP $F3 ; porównaj z 'NEXT'.
EX DE,HL ; następny wiersz do HL.
JR NZ,NXTLIN_NO ; jeśli brak zgodności, wstecz do NXTLIN-NO
EX DE,HL ; odtwórz wskaźnik
RST 20H ; NASTĘPNY-ZNAK idzie naprzód i pobiera literę do A.
EX DE,HL ; zachowaj wskaźnik
CP B ; porównaj z nazwą zmiennej.
JR NZ,NXTLIN_NO ; jeśli nie pasuje, wróć do NXTLIN-NO
; $0E2A : 3626
FOR_END: LD (NXTLIN),HL ; uaktualnij zmienną systemową NXTLIN
RET
; --------------------------
; PROCEDURA POLECENIA 'NEXT'
; --------------------------
; $0E2E : 3630
NEXT: BIT 1,(IY+$2D) ; zmienna systemowa FLAGX
JP NZ,REPORT_2
LD HL,(DEST)
BIT 7,(HL)
JR Z,REPORT_1
INC HL
LD (MEM),HL
RST 28H ; KALKULATOR
.BYTE $E0 ; na-stos-paimięć-0
.BYTE $E2 ; na-stos-paimięć-2
.BYTE $0F ; dodaj
.BYTE $C0 ; stos-do-pamięci-0
.BYTE $02 ; usuń
.BYTE $34 ; koniec-obliczeń
CALL NEXT_LOOP
RET C
LD HL,(MEM)
LD DE,$000F
ADD HL,DE
LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL
JR GOTO_2
; $0E58 : 3672
REPORT_1: RST 08H ; ERROR-1
.BYTE $00 ; Raport Błędu: NEXT bez FOR
; ----------------------
; PROCEDURA 'PĘTLA NEXT'
; ----------------------
; $0E5A : 3674
NEXT_LOOP: RST 28H ; KALKULATOR
.BYTE $E1 ; na-stos-paimięć-1
.BYTE $E0 ; na-stos-paimięć-0
.BYTE $E2 ; na-stos-paimięć-2
.BYTE $32 ; mniej-niż-0
.BYTE $00 ; skocz-przy-prawdzie
.BYTE $02 ; do $0E62, LMT-V-VAL
.BYTE $01 ; zamień
; $0E62 : 3682
LMT_V_VAL: .BYTE $03 ; odejmij
.BYTE $33 ; większe-niż-0
.BYTE $00 ; skocz-przy-prawdzie
.BYTE $04 ; do $0E69, IMPOSS
.BYTE $34 ; koniec-obliczeń
AND A ; zeruj znacznik przeniesienia
RET
; $0E69 : 3689
IMPOSS: .BYTE $34 ; koniec-obliczeń
SCF
RET
; --------------------------
; PROCEDURA POLECENIA 'RAND'
; --------------------------
; Z ZX-81 rozkaz nazywa się RAND, w ZX-Spectrum jest to Randomize. W obu tych przypadkach procedura obsługi jest identyczna - ustawia zmienną
; systemową SEED dostarczoną wartością lub wartością opartą na czasie, jeśli nie dostarczono żadnej liczby lub liczba ta jest równa zero.
; $0E6C : 3692
RAND: CALL FIND_INT
LD A,B ; sprawdź, czy wartość jest równa zero
OR C
JR NZ,SET_SEED ; naprzód do SET-SEED, jeśli różne od zera
LD BC,(FRAMES) ; inaczej pobierz zmienną systemową FRAMES
; $0E77 : 3703
SET_SEED: LD (SEED),BC ; uaktualnij zmienną systemową SEED
RET
; --------------------------
; PROCEDURA POLECENIA 'CONT'
; --------------------------
; Kolejny skrócony rozkaz. Miejsca w ROM musiało bardzo brakować. Kontynuuje od wiersza, który był ustawiony w momencie naciśnięcia klawisza BREAK.
; Po STOP jest to następny wiersz, inaczej program by się od razu zatrzymał.
; $0E7C : 3708
CONT: LD HL,(OLDPPC) ; ustaw HL ze zmiennej systemowej OLDPPC
JR GOTO_2
; --------------------------
; PROCEDURA POLECENIA 'GOTO'
; --------------------------
; Nazwa również ucierpiała z powodu braku miejsca w ROM i nie ma w niej spacji pomiędzy GO i TO, jak w ZX-Spectrum. To samo dotyczy również
; polecenia GOSUB.
; $0E81 : 3713
GOTO: CALL FIND_INT
LD H,B
LD L,C
; $0E86 : 3718
GOTO_2: LD A,H
CP $F0
JR NC,REPORT_B
CALL LINE_ADDR
LD (NXTLIN),HL
RET
; --------------------------
; PROCEDURA POLECENIA 'POKE'
; --------------------------
; $0E92 : 3730
POKE: CALL FP_TO_A
JR C,REPORT_B ; naprzód z nadmiarem do REPORT-B
JR Z,POKE_SAVE ; jeśli dodatnie, naprzód do POKE-SAVE
NEG ; zmień znak na przeciwny
; $0E9B : 3739
POKE_SAVE: PUSH AF ; zachowaj wartość
CALL FIND_INT ; procedura FIND-INT pobiera adres do BC wywołując procedurę błędu przy nadmiarze lub wartości ujemnej
POP AF ; odzyskaj wartość
; Uwaga, następne dwie instrukcje są pozostałością po ZX80 i nie powinno ich tytaj być, ale są.
BIT 7,(IY+$00) ; sprawdź ERR_NR - czy wciąż $FF ?
RET Z ; jeśli nie, wróć z błędem.
LD (BC),A ; wpisz wartość pod wskazany adres.
RET
; -----------------------------------
; PROCEDURA 'ZNAJDŹ LICZBĘ CAŁKOWITĄ'
; -----------------------------------
; $0EA7 : 3751
FIND_INT: CALL FP_TO_BC
JR C,REPORT_B ; naprzód z nadmiarem do REPORT-B
RET Z ; wróć, jeśli dodatnie (0-65535).
; $0EAD : 3757
REPORT_B: RST 08H ; ERROR-1
.BYTE $0A ; Raport Błędu: Liczba całkowita poza zakresem
; -------------------------
; PROCEDURA POLECENIA 'RUN'
; -------------------------
; $0EAF : 3759
RUN: CALL GOTO
JP CLEAR
; ---------------------------
; PROCEDURA POLECENIA 'GOSUB'
; ---------------------------
; $0EB5 : 3765
GOSUB: LD HL,(PPC)
INC HL
EX (SP),HL
PUSH HL
LD (ERR_SP),SP ; ustaw wskaźnik stosu błędu - ERR_SP
CALL GOTO
LD BC,$0006
; ---------------------------
; PROCEDURA 'SPRAWDŹ MIEJSCE'
; ---------------------------
;
; $0EC5 : 3781
TEST_ROOM: LD HL,(STKEND)
ADD HL,BC
JR C,REPORT_4
EX DE,HL
LD HL,$0024
ADD HL,DE
SBC HL,SP
RET C
; $0ED3 : 3795
REPORT_4: LD L,$03
JP ERROR_3
; ----------------------------
; PROCEDURA POLECENIA 'RETURN'
; ----------------------------
; $0ED8 : 3800
RETURN: POP HL
EX (SP),HL
LD A,H
CP $3E
JR Z,REPORT_7
LD (ERR_SP),SP
JR GOTO_2 ; wstecz do GOTO-2
; $0EE5 : 3813
REPORT_7: EX (SP),HL
PUSH HL
RST 08H ; ERROR-1
.BYTE $06 ; Raport Błędu: RETURN bez GOSUB
; ---------------------------
; PROCEDURA POLECENIA 'INPUT'
; ---------------------------
; $0EE9 : 3817
INPUT: BIT 7,(IY+$08) ; starszy bajt zmiennej systemowej PPC_hi
JR NZ,REPORT_8
CALL X_TEMP
LD HL,FLAGX
SET 5,(HL)
RES 6,(HL)
LD A,(FLAGS)
AND $40
LD BC,$0002
JR NZ,PROMPT
LD C,$04
; $0F05 : 3845
PROMPT: OR (HL)
LD (HL),A
RST 30H ; BC-MIEJSC
LD (HL),$76
LD A,C
RRCA
RRCA
JR C,ENTER_CUR
LD A,$0B
LD (DE),A
DEC HL
LD (HL),A
; $0F14 : 3860
ENTER_CUR: DEC HL
LD (HL),$7F
LD HL,(S_POSN)
LD (T_ADDR),HL
POP HL
JP LOWER
; $0F21 : 3873
REPORT_8: RST 08H ; ERROR-1
.BYTE $07 ; Raport Błędu: Koniec pliku
; ---------------------------
; PROCEDURA POLECENIA 'PAUSE'
; ---------------------------
; $0F23 : 3875
FAST: CALL SET_FAST
RES 6,(IY+$3B) ; zmienna systemowa CDFLAG
RET
; --------------------------
; PROCEDURA POLECENIA 'SLOW'
; --------------------------
;
; $0F2B : 3883
SLOW: SET 6,(IY+$3B) ; zmienna systemowa CDFLAG
JP SLOW_FAST
; ---------------------------
; PROCEDURA POLECENIA 'PAUSE'
; ---------------------------
; $0F32 : 3890
PAUSE: CALL FIND_INT
CALL SET_FAST
LD H,B
LD L,C
CALL DISPLAY_P
LD (IY+$35),$FF ; starszy bajt zmiennej systemowej FRAMES
CALL SLOW_FAST
JR DEBOUNCE
; -------------------
; PROCEDURA 'PRZERWA'
; -------------------
;
; $0F46 : 3910
BREAK_1: LD A,$7F ; czytaj port $7FFE - klawisze B,N,M,.,SPACJA.
IN A,($FE)
RRA ; jeśli naciśnięto spację, będzie ustawione przeniesienie
; --------------------
; PROCEDURA 'DEBOUNCE'
; --------------------
;
; $0F4B : 3915
DEBOUNCE: RES 0,(IY+$3B) ; uaktualnij zmienną systemową CDFLAG
LD A,$FF
LD (DBOUNC),A
RET
; ----------------------
; PROCEDURA 'SKANOWANIA'
; ----------------------
; To ta procedura rekurencyjna daje ZX81 jego moc. O ile w systemie jest wystarczająca ilość pamięci, może on obliczać wartości wyrażeń
; o nieograniczonej złożoności.
; Uwaga. Ponieważ nie ma jednoargumentowego plusa, więc PRINT +1 daje błąd składniowy. Na spektrum to działa, lecz również działa PRINT + "STRING".
; $0F55 : 3925
SCANNING: RST 18H ; POBIERZ-ZNAK
LD B,$00 ; wyzeruj rejestr B
PUSH BC ; umieść zero na stosie jako priorytetowy znacznik końca
; $0F59 : 3929
S_LOOP_1: CP $40 ; sprawdź ze znakiem 'RND'
JR NZ,S_TEST_PI ; jeśli się nie zgadza, naprzód do S-TEST-PI
; -------------
; FUNKCJA 'RND'
; -------------
CALL SYNTAX_Z ; wyjdź, jeśli jest sprawdzana składnia
JR Z,S_JPI_END ; przy sprawdzaniu składni skocz naprzód do S-JPI-END
LD BC,(SEED) ; pobierz zmienną systemową SEED
CALL STACK_BC ; i umieść ją na stosie kalkulatora
RST 28H ; KALKULATOR seed.
.BYTE $A1 ; jeden-na-stos seed,1.
.BYTE $0F ; dodaj seed+1.
.BYTE $30 ; dane-na-stos seed+1,75.
.BYTE $37 ; Wykładnik: $87, Bajty: 1
.BYTE $16 ;(+00,+00,+00)
.BYTE $04 ; mnóż (seed+1)*75.
.BYTE $30 ; dane-na-stos (seed+1)*75,65537.
.BYTE $80 ; Bajty: 3
.BYTE $41 ; Wykładnik $91
.BYTE $00,$00,$80 ;(+00)
.BYTE $2E ; n-modulo-m reszta,iloraz.
.BYTE $02 ; usuń reszta z dzielenia (seed+1)*75/65537.
.BYTE $A1 ; jeden-na-stos reszta,1.
.BYTE $03 ; odejmij reszta-1.
.BYTE $2D ; duplikuj reszta-1,reszta-1.
.BYTE $34 ; koniec-obliczeń
CALL FP_TO_BC
LD (SEED),BC ; uaktualnij zmienną systemową SEED
LD A,(HL) ; HL adresuje wykładnik ostatniej wartości
AND A ; testuj zero
JR Z,S_JPI_END ; jeśli zero, skocz naprzód do S-JPI-END
SUB $10 ; inaczej zmniejsz wykładnik o 16
LD (HL),A ; co daje podzielenie ostatniej wartości o 65536
; $0F8A : 3978
S_JPI_END: JR S_PI_END
; $0F8C : 3980
S_TEST_PI: CP $42 ; znak 'PI'
JR NZ,S_TST_INK ; jeśli nie, naprzód do S-TST-INK
; ---------------
; OBLICZANIE 'PI'
; ---------------
CALL SYNTAX_Z ; wyjdź przy sprawdzaniu składni
JR Z,S_PI_END ; jeśli sprawdzanie składni, naprzód do S-PI-END
RST 28H ; KALKULATOR
.BYTE $A3 ; na-stos-pi/2
.BYTE $34 ; koniec-obliczeń
INC (HL) ; zwiększ wykładnik, co na stosie da PI.
; $0F99 : 3993
S_PI_END: RST 20H ; NASTĘPNY-ZNAK - przesuwa do przodu wskaźnik znaków
JP S_NUMERIC ; skocz naprzód so S-NUMERIC, aby przed kontynuacją ustawić znacznik
; informujący o wyniku liczbowym.
; $0F9D : 3997
S_TST_INK: CP $41 ; porównaj ze znakiem 'INKEY$'
JR NZ,S_ALPHANUM ; jeśli się nie zgadza, naprzód do S-ALPHANUM
; -------------------
; OBLICZANIE 'INKEY$'
; -------------------
CALL KEYBOARD
LD B,H
LD C,L
LD D,C
INC D
CALL NZ,DECODE
LD A,D
ADC A,D
LD B,D
LD C,A
EX DE,HL
JR S_STRING
; $0FB2 : 4018
S_ALPHANUM: CALL ALPHANUM
JR C,S_LTR_DGT ; naprzód, gdy alfanumeryczne do S-LTR-DGT
CP $1B ; czy znak to '.' ?
JP Z,S_DECIMAL ; jeśli tak, skocz naprzód do S-DECIMAL
LD BC,$09D8 ; przygotuj operację 'odejmowania' o priorytecie 09
CP $16 ; czy znak to '-' ?
JR Z,S_PUSH_PO ; jeśli tak, to naprzód do S-PUSH-PO
CP $10 ; czy znak to '(' ?
JR NZ,S_QUOTE ; jeśli tak, naprzód do S-QUOTE
CALL CH_ADD_1 ; procedura CH-ADD+1 przesuwa do przodu wskaźnik znaków.
CALL SCANNING ; rekursywnie wywołaj procedurę SCANNING, aby obliczyć wartość podwyrażenia
CP $11 ; czy znak to ')' ?
JR NZ,S_RPT_C ; jeśli nie, skocz naprzód do S-RPT-C
CALL CH_ADD_1 ; procedura CH-ADD+1 przesuwa wskaźnik znaków
JR S_J_CONT_3 ; skok względny do S-JP-CONT3, a później do S-CONT3
; rozważ łańcuch w cudzysłowach, np. PRINT "HURA!"
; Uwaga, cudzysłowy nie są dozwolone wewnątrz łańcucha.
; $0FD6 : 4054
S_QUOTE: CP $0B ; czy znakiem jest cudzysłów (") ?
JR NZ,S_FUNCTION ; jeśli nie, naprzód do S-FUNCTION
CALL CH_ADD_1 ; procedura CH-ADD+1 przesuwa wskaźnik znaków
PUSH HL ; * zachowaj początek łańcucha.
JR S_QUOTE_S
; $0FE0 : 4064
S_Q_AGAIN: CALL CH_ADD_1 ; procedura CH-ADD+1 przesuwa wskaźnik znaków
; $0FE3 : 4067
S_QUOTE_S: CP $0B ; czy znakiem jest '"' ?
JR NZ,S_Q_NL ; jeśli nie, naprzód do S-Q-NL
POP DE ; * odzyskaj poczatek łańcucha
AND A ; przygotuj się do odejmowania
SBC HL,DE ; od bieżącej pozycji odejmij start
LD B,H ; przenieś tą długość
LD C,L ; do pary rejestrów BC
; $0FED : 4077
S_STRING: LD HL,FLAGS ; zaadresuj zmienną systemową FLAGS
RES 6,(HL) ; sygnalizuj wynik łańcuchowy
BIT 7,(HL) ; sprawdź, czy testowana jest składnia
CALL NZ,STK_STO ; w czasie wykonania procedura STK-STO-$ umieszcza na stosie deskryptor łańcucha - start DE, długość BC.
RST 20H ; NASTĘPNY-ZNAK - przesuń wskaźnik.
; $0FF8 : 4088
S_J_CONT_3: JP S_CONT_3
; Należy rozważyć łańcuch bez zamykającego cudzysłowu.
; $0FFB : 4091
S_Q_NL: CP $76 ; porównaj z NEWLINE
JR NZ,S_Q_AGAIN ; jeśli różne, wróć z powrotem do S-Q-AGAIN
; $0FFF : 4095
S_RPT_C: JP REPORT_C
; $1002 : 4098
S_FUNCTION: SUB $C4 ; odejmij 'CODE' zmniejszając kod znaku
; od CODE do '<>', zakres $00 - $XX
JR C,S_RPT_C ; jeśli mniej, wróć do S-RPT-C
; sprawdź NOT, ostatnią funkcję w zestawie znaków.
LD BC,$04EC ; przygotuj priorytet $04, operacja 'not'
CP $13 ; porównaj z 'NOT' ( - CODE)
JR Z,S_PUSH_PO ; jeśli się zgadza, skocz naprzód do S-PUSH-PO
JR NC,S_RPT_C ; jeśli jest cokolwiek więcej, wstecz do S-RPT-C
; inaczej mamy do czynienia z funkcją od 'CODE' do 'CHR$'
LD B,$10 ; priorytet 16 wiąże wszystkie funkcje z argumentami, usuwając potrzebę stosowania nawiasów.
ADD A,$D9 ; dodaj $D9, aby uzyskać zakres od $D9 do $EB bit 6 jest ustawiony, aby pokazywać argument liczbowy.
; bit 7 jest ustawiony, aby pokazywać wynik liczbowy.
; teraz wyreguluj te standardowe znaczniki argumentów/wyników.
LD C,A ; zachowaj kod w C
CP $DC ; rozdziel 'CODE', 'VAL', 'LEN'
JR NC,S_NO_TO ; jeśli argument jest łańcuchem, przejdź naprzód do S-NO-TO-$
RES 6,C ; sygnalizuj łańcuchowy argument.
; $101A : 4122
S_NO_TO: CP $EA ; wyizoluj górę zakresu: 'STR$' i 'CHR$'
JR C,S_PUSH_PO ; z innymi przejdź naprzód do S-PUSH-PO
RES 7,C ; sygnalizuj wynik łańcuchowy.
; $1020 : 4128
S_PUSH_PO: PUSH BC ; umieść na stosie priorytet/operację
RST 20H ; NASTĘPNY-ZNAK
JP S_LOOP_1 ; skocz wstecz do S-LOOP-1
; $1025 : 4133
S_LTR_DGT: CP $26 ; porównaj z 'A'.
JR C,S_DECIMAL ; jeśli poniżej, skocz naprzód do S-DECIMAL
CALL LOOK_VARS ; poszukaj zmiennej
JP C,REPORT_2 ; jeśli nie znaleziono, wstecz do REPORT-2 przy sprawdzaniu składni, zmienna jest zawsze 'znaleziona'.
CALL Z,STK_VAR ; procedura STK-VAR umieszcza na stosie parametry łańcuchowe lub zwraca położenie komórki, jeśli parametry są liczbowe
LD A,(FLAGS) ; pobierz FLAGS
CP $C0 ; porównaj z wynikiem/argumentem liczbowym
JR C,S_CONT_2 ; jeśli nie jest liczbowy, idź do S-CONT-2
INC HL ; zaadresuj liczbową zawartość zmiennej
LD DE,(STKEND) ; ustaw przeznaczenie na STKEND
CALL MOVE_FP ; procedura MOVE-FP umieszcza na stosie pięć bajtów
EX DE,HL ; przenieś nową wolną pozycję z DE do HL.
LD (STKEND),HL ; uaktualnij zmienną systemową STKEND
JR S_CONT_2
; Procedura skanowania liczby dziesiętnej jest wywoływana, gdy w wyrażeniu zostanie napotkana cyfra lub znak kropki. Gdy sprawdzana jest składnia,
; to "ukryta" liczba zmiennoprzecinkowa jest umieszczana w wierszu BASIC za zapisem liczby. W czasie wykonywania programu cyfry są pomijane,
; a pobrana zostaje liczba zmiennoprzecinkowa.
; $1047 : 4167
S_DECIMAL: CALL SYNTAX_Z ; wyjdź przy sprawdzaniu składni
JR NZ,S_STK_DEC ; w czasie wykonania naprzód do S-STK-DEC
CALL DEC_TO_FP
RST 18H ; POBIERZ-ZNAK przesuwa HL poza cyfry
LD BC,$0006 ; potrzebne jest sześć komórek.
CALL MAKE_ROOM ; zrób na nie miejsce
INC HL ; ustaw HL na pierwszą komórkę
LD (HL),$7E ; wstaw znacznik liczby o kodzie 126
INC HL ; przejdź dalej
EX DE,HL ; przenieś adres docelowy do DE.
LD HL,(STKEND) ; ustaw HL na STKEND, które wskazuje na pierwszą komórkę za "ostatnią wartością"
LD C,$05 ; do przesunięcia pięć bajtów
AND A ; wyzeruj znacznik przeniesienia
SBC HL,BC ; odejmij 5, wskazując na "ostatnią wartość"
LD (STKEND),HL ; uaktualnij STKEND, co skutkuje usunięciem liczby
LDIR ; przekopiuj wartość tych pięciu bajtów
EX DE,HL ; wskaźnik BASIC do HL, który może wskazywać biały znak za liczbą
DEC HL ; teraz wskazuje ostatni z pięciu bajtów.
CALL TEMP_PTR1 ; procedura TEMP-PTR1 przesuwa wskaźnik na kolejny znak pomijając wszystkie znaki białe
JR S_NUMERIC ; naprzód do S-NUMERIC, sygnalizując wynik liczbowy
; w czasie wykonania programu skok trafia w to miejsce, gdy zostanie napotkana cyfra lub kropka.
; $106F : 4207
S_STK_DEC: RST 20H ; NASTĘPNY-ZNAK
CP $7E ; porównaj ze znacznikiem liczby
JR NZ,S_STK_DEC ; cofnij się w pętli do S-STK-DEC aż znak zostanie odnaleziony pomijając wszystkie cyfry
INC HL ; wskaż pierwszy z pięciu ukrytych bajtów.
LD DE,(STKEND) ; ustaw przeznaczenie ze zmiennej systemowej STKEND
CALL MOVE_FP ; procedura MOVE-FP umieszcza liczbę na stosie.
LD (STKEND),DE ; uaktualnij zmienne systemowe STKEND
LD (CH_ADD),HL ; oraz CH_ADD.
; $1083 : 4227
S_NUMERIC: SET 6,(IY+$01) ; uaktualnij FLAGS - sygnalizuj wynik liczbowy
; $1087 : 4231
S_CONT_2: RST 18H ; POBIERZ-ZNAK
; $1088 : 4232
S_CONT_3: CP $10 ; porównaj z nawiasem otwierającym '('
JR NZ,S_OPERTR ; jeśli nie, skocz naprzód do S-OPERTR
BIT 6,(IY+$01) ; testuj FLAGS - wynik liczbowy czy łańcuchowy?
JR NZ,S_LOOP ; jeśli liczbowy, naprzód do S-LOOP
; inaczej mamy do czynienia z łańcuchem znaków
CALL SLICING
RST 20H ; NASTĘPNY-ZNAK
JR S_CONT_3 ; wróć do S-CONT-3
; Znak jest teraz przetwarzany, aby utworzył odpowiednik w tablicy literałów kalkulatora. Jest to dosyć zawiłe, dlatego w ZX-Spectrum wprowadzono
; w tym miejscu prostą tablicę translacji.
; $1098 : 4248
S_OPERTR: LD BC,$00C3 ; standardowo przygotuj operator 'odejmowania'. Również ustaw B na zero do późniejszego indeksowania.
CP $12 ; czy znakiem jest '>' ?
JR C,S_LOOP ; jeśli mniej, skocz naprzód do S-LOOP, ponieważ osiągnięty został koniec sensownych wyrażeń
SUB $16 ; czy znakiem jest '-' ?
JR NC,SUBMLTDIV ; z - * / AND '**' '<>' skocz naprzód do SUBMLTDIV
ADD A,$0D ; pozostałe zwiększ o trzynaście $09 ">' do $0C '+'
JR GET_PRIO ; skocz naprzód do GET-PRIO
; $10A7 : 4263
SUBMLTDIV: CP $03 ; wyizoluj $00 '-', $01 '*', $02 '/'
JR C,GET_PRIO ; naprzód do GET-PRIO, jeśli tak
; inaczej być może pierwotne do $D8 '**' do $DD '<>' już zmniejszone o $16
SUB $C2 ; dając zakres od $00 do $05
JR C,S_LOOP ; naprzód do S-LOOP, jeśli mniej
CP $06 ; testuj górną granicę na nonsens, również
JR NC,S_LOOP ; naprzód do S-LOOP, jeśli tak
ADD A,$03 ; zwiększ o 3, aby otrzymać połączone operatory
; $00 '-'
; $01 '*'
; $02 '/'
; $03 '**'
; $04 'OR'
; $05 'AND'
; $06 '<='
; $07 ">='
; $08 '<>'
; $09 ">'
; $0A '<'
; $0B '='
; $0C '+'
; $10B5 : 4277
GET_PRIO: ADD A,C ; dodaj do standardowej operacji 'sub' ($C3)
LD C,A ; i bajt operatora umieść w C.
LD HL,TBL_PRI-$C3 ; teoretyczna podstawa tablicy priorytetów.
ADD HL,BC ; dodaj C ( B jest równe zero)
LD B,(HL) ; pobierz priorytet do B
; $10BC : 4284
S_LOOP: POP DE ; odtwórz poprzednią zawartość
LD A,D ; załaduj A priorytetem.
CP B ; czy jest wyższy priorytet
JR C,S_TIGHTER ; jeśli tak, skocz naprzód do S-TIGHTER
AND A ; czy oba priorytety zero?
JP Z,GET_CHAR ; jeśli zero, wyjdź poprzez GET-CHAR
PUSH BC ; zapisz na stosie bieżące wartości
PUSH DE ; zapisz na stosie ostatnie wartości
CALL SYNTAX_Z ; czy sprawdzana składnia?
JR Z,S_SYNTEST ; jeśli sprawdzana składnia, naprzód do S-SYNTEST
LD A,E ; pobierz ostatnią operację
AND $3F ; zamaskuj bity wskaźnikowe, aby otrzymać prawdziwy literał kalkulatora
LD B,A ; umieść w rejestrze B dla BREG
; wykonaj pojedynczą operację
RST 28H ; KALKULATOR
.BYTE $37 ; fp-calc-2
.BYTE $34 ; koniec-obliczeń
JR S_RUNTEST ; naprzód do S-RUNTEST
; $10D5 : 4309
S_SYNTEST: LD A,E ; przenieś wymaskowany operator do A
XOR (IY+$01) ; wykonaj operację xor z FLAGS, wyniki zresetują bit 6
AND $40 ; testuj bit 6
; $10DB : 4315
S_RPORT_C: JP NZ,REPORT_C ; wstecz do REPORT-C, jeśli wyniki się nie zgadzają.
; w czasie wykonania przenieś bit 7 operatora do bitu 6 zmiennej FLAGS
; $10DE : 4318
S_RUNTEST: POP DE ; przywróć ostatnią operację.
LD HL,FLAGS ; zaadresuj zmienną systemową FLAGS
SET 6,(HL) ; załóż wynik liczbowy
BIT 7,E ; testuj oczekiwany wynik w operacji
JR NZ,S_LOOPEND ; naprzód do S-LOOPEND, jeśli jest liczbowy
RES 6,(HL) ; zeruj, aby zasygnalizować wynik łańcuchowy
; $10EA : 4330
S_LOOPEND: POP BC ; odtwórz bieżące wartości
JR S_LOOP ; wstecz do S-LOOP
; $10ED : 4333
S_TIGHTER: PUSH DE ; zachowaj ostatnie wartości i zajmij się bieżącymi
LD A,C ; pobierz bieżący operator
BIT 6,(IY+$01) ; testuj FLAGS - wynik liczbowy czy łańcuchowy?
JR NZ,S_NEXT ; jeśli liczbowy, naprzód do S-NEXT
AND $3F ; usuń bity znaczników, aby uzyskać czysty literał
ADD A,$08 ; dodaj 8 - rozszerzanie literału liczbowego do odpowiadających mu literałów łańcuchowych
LD C,A ; umieść czysty literał z powrotem w C.
CP $10 ; porównaj z 'AND'
JR NZ,S_NOT_AND ; jeśli różne, naprzód do S-NOT-AND
SET 6,C ; ustaw argument liczbowy wymagany dla 'AND'
JR S_NEXT ; naprzód do S-NEXT
; $1102 : 4354
S_NOT_AND: JR C,S_RPORT_C ; jeśli mniej niż 'AND', to wstecz do S-RPORT-C. Nonsens, jeśli '-', '*' itp.
CP $17 ; porównaj z literałem 'strs-add'
JR Z,S_NEXT ; jeśli się zgadza, naprzód, sygnalizując wynik łańcuchowy
SET 7,C ; dla pozostałych ustaw bit na liczbowe (logiczne)
; $110A : 4362
S_NEXT: PUSH BC ; zapisz na stosie 'bieżące' wartości
RST 20H ; NASTĘPNY-ZNAK
JP S_LOOP_1 ; skocz wstecz do S-LOOP-1
; ---------------------
; 'TABLICA PRIORYTETÓW'
; ---------------------
; $110F : 4367
TBL_PRI: .BYTE $06 ; '-'
.BYTE $08 ; '*'
.BYTE $08 ; '/'
.BYTE $0A ; '**'
.BYTE $02 ; 'OR'
.BYTE $03 ; 'AND'
.BYTE $05 ; '<='
.BYTE $05 ; '>='
.BYTE $05 ; '<>'
.BYTE $05 ; '>'
.BYTE $05 ; '<'
.BYTE $05 ; '='
.BYTE $06 ; '+'
; ---------------------
; PROCEDURA 'LOOK-VARS'
; ---------------------
; $111C : 4380
LOOK_VARS: SET 6,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj wynik liczbowy
RST 18H ; POBIERZ-ZNAK
CALL ALPHA
JP NC,REPORT_C
PUSH HL
LD C,A
RST 20H ; NASTĘPNY-ZNAK
PUSH HL
RES 5,C
CP $10
JR Z,V_RUN_SYN
SET 6,C
CP $0D
JR Z,V_STR_VAR
SET 5,C
; $1139 : 4409
V_CHAR: CALL ALPHANUM
JR NC,V_RUN_SYN
RES 6,C
RST 20H ; NASTĘPNY-ZNAK
JR V_CHAR ; z powrotem w pętli do V-CHAR
; $1143 : 4419
V_STR_VAR: RST 20H ; NASTĘPNY-ZNAK
RES 6,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj wynik łańcuchowy
; $1148 : 4424
V_RUN_SYN: LD B,C
CALL SYNTAX_Z ; wyjdź przy sprawdzaniu składni
JR NZ,V_RUN ; naprzód do V-RUN
LD A,C
AND $E0
SET 7,A
LD C,A
JR V_SYNTAX ; naprzód do V-SYNTAX
; $1156 : 4438
V_RUN: LD HL,(VARS)
; $1159 : 4441
V_EACH: LD A,(HL)
AND $7F
JR Z,V_80_BYTE
CP C
JR NZ,V_NEXT
RLA
ADD A,A
JP P,V_FOUND_2
JR C,V_FOUND_2
POP DE
PUSH DE
PUSH HL
; $116B : 4459
V_MATCHES: INC HL
; $116C : 4460
V_SPACES: LD A,(DE)
INC DE
AND A
JR Z,V_SPACES ; wstecz do V-SPACES
CP (HL)
JR Z,V_MATCHES ; wstecz do V-MATCHES
OR $80
CP (HL)
JR NZ,V_GET_PTR ; naprzód do V-GET-PTR
LD A,(DE)
CALL ALPHANUM
JR NC,V_FOUND_1 ; naprzód do V-FOUND-1
; $117F : 4479
V_GET_PTR: POP HL
; $1180 : 4480
V_NEXT: PUSH BC
CALL NEXT_ONE
EX DE,HL
POP BC
JR V_EACH ; wstecz do V-EACH
; $1188 : 4488
V_80_BYTE: SET 7,B
; $118A : 4490
V_SYNTAX: POP DE
RST 18H ; POBIERZ-ZNAK
CP $10
JR Z,V_PASS ; naprzód do V-PASS
SET 5,B
JR V_END ; naprzód do V-END
; $1194 : 4500
V_FOUND_1: POP DE
; $1195 : 4501
V_FOUND_2: POP DE
POP DE
PUSH HL
RST 18H ; POBIERZ-ZNAK
; $1199 : 4505
V_PASS: CALL ALPHANUM
JR NC,V_END ; jeśli alfanumeryczne, naprzód do V-END
RST 20H ; NASTĘPNY-ZNAK
JR V_PASS ; wstecz do V-PASS
; $11A1 : 4513
V_END: POP HL
RL B
BIT 6,B
RET
; -------------------
; PROCEDURA 'STK-VAR'
; -------------------
; $11A7 : 4519
STK_VAR: XOR A
LD B,A
BIT 7,C
JR NZ,SV_COUNT ; naprzód do SV-COUNT
BIT 7,(HL)
JR NZ,SV_ARRAYS ; naprzód do SV-ARRAYS
INC A
; $11B2 : 4530
SV_SIMPLE: INC HL
LD C,(HL)
INC HL
LD B,(HL)
INC HL
EX DE,HL
CALL STK_STO
RST 18H ; POBIERZ-ZNAK
JP IS_SV_SLICE ; skocz naprzód do SV-SLICE?
; $11BF : 4543
SV_ARRAYS: INC HL
INC HL
INC HL
LD B,(HL)
BIT 6,C
JR Z,SV_PTR ; naprzód do SV-PTR
DEC B
JR Z,SV_SIMPLE ; naprzód do SV-SIMPLE$
EX DE,HL
RST 18H ; POBIERZ-ZNAK
CP $10
JR NZ,REPORT_3 ; naprzód do REPORT-3
EX DE,HL
; $11D1 : 4561
SV_PTR: EX DE,HL
JR SV_COUNT ; naprzód do SV-COUNT
; $11D4 : 4564
SV_COMMA: PUSH HL
RST 18H ; POBIERZ-ZNAK
POP HL
CP $1A
JR Z,SV_LOOP ; naprzód do SV-LOOP
BIT 7,C
JR Z,REPORT_3 ; naprzód do REPORT-3
BIT 6,C
JR NZ,SV_CLOSE ; naprzód do SV-CLOSE
CP $11
JR NZ,SV_RPT_C ; naprzód do SV-RPT-C
RST 20H ; NASTĘPNY-ZNAK
RET
; $11E9 : 4585
SV_CLOSE: CP $11
JR Z,SV_DIM ; naprzód do SV-DIM
CP $DF
JR NZ,SV_RPT_C ; naprzód do SV-RPT-C
; $11F1 : 4593
SV_CH_ADD: RST 18H ; POBIERZ-ZNAK
DEC HL
LD (CH_ADD),HL
JR SV_SLICE ; naprzód do SV-SLICE
; $11F8 : 4600
SV_COUNT: LD HL,$0000
; $11FB : 4603
SV_LOOP: PUSH HL
RST 20H ; NASTĘPNY-ZNAK
POP HL
LD A,C
CP $C0
JR NZ,SV_MULT ; naprzód do SV-MULT
RST 18H ; POBIERZ-ZNAK
CP $11
JR Z,SV_DIM ; naprzód do SV-DIM
CP $DF
JR Z,SV_CH_ADD ; wstecz do SV-CH-ADD
; $120C : 4620
SV_MULT: PUSH BC
PUSH HL
CALL DE__DE_1
EX (SP),HL
EX DE,HL
CALL INT_EXP1
JR C,REPORT_3 ; naprzód do REPORT-3
DEC BC
CALL GET_HL_DE
ADD HL,BC
POP DE
POP BC
DJNZ SV_COMMA ; skocz w pętli wstecz do SV-COMMA
BIT 7,C
; $1223 : 4643
SV_RPT_C: JR NZ,SL_RPT_C
PUSH HL
BIT 6,C
JR NZ,SV_ELEM ; naprzód do SV-ELEM$
LD B,D
LD C,E
RST 18H ; POBIERZ-ZNAK
CP $11 ; czy znakiem jest ')' ?
JR Z,SV_NUMBER ; jeśli tak, przesuń się naprzód do SV-NUMBER
; $1231 : 4657
REPORT_3: RST 08H ; ERROR-1
.BYTE $02 ; Raport Błędu: zły indeks
; $1233 : 4659
SV_NUMBER: RST 20H ; NASTĘPNY-ZNAK
POP HL
LD DE,$0005
CALL GET_HL_DE
ADD HL,BC
RET
; $123D : 4669
SV_ELEM: CALL DE__DE_1
EX (SP),HL
CALL GET_HL_DE
POP BC
ADD HL,BC
INC HL
LD B,D
LD C,E
EX DE,HL
CALL STK_ST_0
RST 18H ; POBIERZ-ZNAK
CP $11 ; czy jest to ')' ?
JR Z,SV_DIM ; jeśli tak, naprzód do SV-DIM
CP $1A ; czy jest to ',' ?
JR NZ,REPORT_3 ; jeśli nie, wstecz do REPORT-3
; $1256 : 4694
SV_SLICE: CALL SLICING
; $1259 : 4697
SV_DIM: RST 20H ; NASTĘPNY-ZNAK
; $125A : 4698
IS_SV_SLICE: CP $10
JR Z,SV_SLICE ; wstecz do SV-SLICE
RES 6,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj wynik łańcuchowy
RET
; -------------------
; PROCEDURA 'SLICING'
; -------------------
; $1263 : 4707
SLICING: CALL SYNTAX_Z ; wyjdź przy sprawdzaniu składni
CALL NZ,STK_FETCH
RST 20H ; NASTĘPNY-ZNAK
CP $11 ; czy jest to ')' ?
JR Z,SL_STORE ; jeśli tak, naprzód do SL-STORE
PUSH DE
XOR A
PUSH AF
PUSH BC
LD DE,$0001
RST 18H ; POBIERZ-ZNAK
POP HL
CP $DF ; czy jest to 'TO' ?
JR Z,SL_SECOND ; jeśli tak, naprzód do SL-SECOND
POP AF
CALL INT_EXP2
PUSH AF
LD D,B
LD E,C
PUSH HL
RST 18H ; POBIERZ-ZNAK
POP HL
CP $DF ; czy jest to 'TO' ?
JR Z,SL_SECOND ; jeśli tak, naprzód do SL-SECOND
CP $11
; $128B : 4747
SL_RPT_C: JP NZ,REPORT_C
LD H,D
LD L,E
JR SL_DEFINE ; naprzód do SL-DEFINE
; $1292 : 4754
SL_SECOND: PUSH HL
RST 20H ; NASTĘPNY-ZNAK
POP HL
CP $11 ; czy jest to ')' ?
JR Z,SL_DEFINE ; jeśli tak, naprzód do SL-DEFINE
POP AF
CALL INT_EXP2
PUSH AF
RST 18H ; POBIERZ-ZNAK
LD H,B
LD L,C
CP $11 ; czy jest to ')' ?
JR NZ,SL_RPT_C ; jeśli nie, wstecz do SL-RPT-C
; $12A5 : 4773
SL_DEFINE: POP AF
EX (SP),HL
ADD HL,DE
DEC HL
EX (SP),HL
AND A
SBC HL,DE
LD BC,$0000
JR C,SL_OVER ; naprzód do SL-OVER
INC HL
AND A
JP M,REPORT_3 ; wstecz do REPORT-3
LD B,H
LD C,L
; $12B9 : 4793
SL_OVER: POP DE
RES 6,(IY+$01) ; zmienna systemowa FLAGS - sygnalizuj wynik łańcuchowy
; $12BE : 4798
SL_STORE: CALL SYNTAX_Z
RET Z ; wróć przy sprawdzaniu składni
; ----------------------------
; PROCEDURA 'UMIEŚĆ NA STOSIE'
; ----------------------------
; Umieszcza na szczycie stosu liczbę przekazaną w rejestrach:
; A - wykładnik
: DE:BC - mantysa
; $12C2 : 4802
STK_ST_0: XOR A ; zeruj wykładnik
; $12C3 : 4803
STK_STO: PUSH BC ; zachowaj fragment liczby
CALL TEST_5_SP ; sprawdź, czy w pamięci jest miejsce na liczbę
POP BC ; odtwórz fragment liczby
LD HL,(STKEND) ; adresuj koniec stosu
LD (HL),A ; prześlij wykładnik
INC HL
LD (HL),E ; prześlij pozostałe 4 bajty liczby DE:BC
INC HL
LD (HL),D
INC HL
LD (HL),C
INC HL
LD (HL),B
INC HL ; teraz HL wskazuje puste miejsce na stosie
LD (STKEND),HL ; uaktualnij STKEND
RES 6,(IY+$01) ; uaktualnij FLAGS - sygnalizuj wynik łańcuchowy
RET
; -------------------
; PROCEDURY 'INT EXP'
; -------------------
;
; $12DD : 4829
INT_EXP1: XOR A
; $12DE : 4830
INT_EXP2: PUSH DE
PUSH HL
PUSH AF
CALL CLASS_6
POP AF
CALL SYNTAX_Z ; wyjdź przy sprawdzaniu składni
JR Z,I_RESTORE ; przy sprawdzaniu składni naprzód do I-RESTORE
PUSH AF
CALL FIND_INT
POP DE
LD A,B
OR C
SCF
JR Z,I_CARRY ; naprzód do I-CARRY
POP HL
PUSH HL
AND A
SBC HL,BC
; $12F9 : 4857
I_CARRY: LD A,D
SBC A,$00
; $12FC : 4860
I_RESTORE: POP HL
POP DE
RET
; ---------------------
; PROCEDURA 'DE,(DE+1)'
; ---------------------
; INDEXUJ I ŁADUJ
; Jest to emulacja instrukcji procesora 6800 LDX 1,X, która ładuje wartość 2 bajtową z pamięci do rejestru ją indeksującego. Zwykle takich operacji
; nie opłaca się pisać jako podprogramów, a ta nie oszczędza ani miejsca, ani czasu. Widocznie programista był ograniczony czasowo i pamięciowo.
; $12FF : 4863
DE__DE_1: EX DE,HL ; przenieś adres indeksu do HL.
INC HL ; zwiększ, aby zaadresować słowo.
LD E,(HL) ; pobierz młodszy bajt słowa.
INC HL ; indeksuj starszy bajt słowa
LD D,(HL) ; i pobierz go
RET ; wróć z DE = słowo
; ---------------------
; PROCEDURA 'GET-HL*DE'
; ---------------------
; $1305 : 4869
GET_HL_DE: CALL SYNTAX_Z ; czy sprawdzana jest składnia?
RET Z ; jeśli tak, to wróć
PUSH BC ; zachowaj BC
LD B,$10 ; do B licznik bitów = 16
LD A,H ; starszy bajt do A
LD C,L ; młodszy do C
LD HL,$0000
; $1311 : 4881
HL_LOOP: ADD HL,HL ; sprawdź, czy HL nie za duże przesuwając bity w lewo
JR C,HL_END ; jeśli tak, skocz naprzód do HL-END
RL C ; przesuń bity starego HL w lewo
RLA
JR NC,HL_AGAIN ; jeśli brak przeniesienia, skocz naprzód do HL-AGAIN
ADD HL,DE ; dodaj do HL DE
; $131A : 4890
HL_END: JP C,REPORT_4 ; jeśli za duże, skocz do REPORT-4
; $131D : 4893
HL_AGAIN: DJNZ HL_LOOP ; wstecz w pętli do HL-LOOP
POP BC ; odtwórz BC
RET
; ---------------
; PROCEDURA 'LET'
; ---------------
; $1321 : 4897
LET: LD HL,(DEST) ; ustaw wskaźnik znaków
BIT 1,(IY+$2D) ; zmienna systemowa FLAGX
JR Z,L_EXISTS
LD BC,$0005
; $132D : 4909
L_EACH_CH: INC BC
; test
; $132E : 4910
L_NO_SP: INC HL ; przesuń wskaźnik znaków
LD A,(HL) ; pobierz znak
AND A ; sprawdź, czy spacja
JR Z,L_NO_SP ; jeśli tak, to wstecz do L-NO-SP
CALL ALPHANUM ; sprawdź, czy znak lub cyfra
JR C,L_EACH_CH
CP $0D ; czy jest to '$' ?
JP Z,L_NEW ; jeśli tak, naprzód do L-NEW$
RST 30H ; BC-MIEJSC
PUSH DE
LD HL,(DEST)
DEC DE
LD A,C
SUB $06
LD B,A
LD A,$40
JR Z,L_SINGLE ; naprzód do L-SINGLE
; $134B : 4939
L_CHAR: INC HL ; przesuń wskaźnik znaku
LD A,(HL) ; pobierz znak
AND A ; czy to jest spacja ?
JR Z,L_CHAR ; jeśli tak, wstecz do L-CHAR
INC DE
LD (DE),A
DJNZ L_CHAR ; wstecz w pętli do L-CHAR
OR $80 ; ustaw bit 7 - ostatni znak nazwy
LD (DE),A ; zapisz znak
LD A,$80
; $1359 : 4953
L_SINGLE: LD HL,(DEST)
XOR (HL)
POP HL
CALL L_FIRST
; $1361 : 4961
L_NUMERIC: PUSH HL
RST 28H ; KALKULATOR
.BYTE $02 ; usuń
.BYTE $34 ; koniec-obliczeń
POP HL
LD BC,$0005
AND A
SBC HL,BC
JR L_ENTER ; naprzód do L-ENTER
; $136E : 4974
L_EXISTS: BIT 6,(IY+$01) ; zmienna systemowa FLAGS - wynik liczbowy czy łańcuchowy?
JR Z,L_DELETE ; naprzód do L-DELETE$
LD DE,$0006
ADD HL,DE
JR L_NUMERIC ; wstecz do L-NUMERIC
; $137A : 4986
L_DELETE: LD HL,(DEST)
LD BC,(STRLEN)
BIT 0,(IY+$2D) ; zmienna systemowa FLAGX
JR NZ,L_ADD ; naprzód do L-ADD$
LD A,B
OR C
RET Z
PUSH HL
RST 30H ; BC-MIEJSC
PUSH DE
PUSH BC
LD D,H
LD E,L
INC HL
LD (HL),$00
LDDR
PUSH HL
CALL STK_FETCH
POP HL
EX (SP),HL
AND A
SBC HL,BC
ADD HL,BC
JR NC,L_LENGTH ; naprzód do L-LENGTH
LD B,H
LD C,L
; $13A3 : 5027
L_LENGTH: EX (SP),HL
EX DE,HL
LD A,B
OR C
JR Z,L_IN_W_S ; naprzód do L_IN_W/S
LDIR
; $13AB : 5035
L_IN_W_S: POP BC
POP DE
POP HL
; -------------------
; PROCEDURA 'L-ENTER'
; -------------------
; $13AE : 5038
L_ENTER: EX DE,HL
LD A,B
OR C
RET Z
PUSH DE
LDIR
POP HL
RET
; $13B7 : 5047
L_ADD: DEC HL
DEC HL
DEC HL
LD A,(HL)
PUSH HL
PUSH BC
CALL L_STRING
POP BC
POP HL
INC BC
INC BC
INC BC
JP RECLAIM_2 ; skocz wstecz i wyjdź poprzez RECLAIM-2
; $13C8 : 5064
L_NEW: LD A,$60 ; przygotuj maskę %01100000
LD HL,(DEST) ; załaduj wskaźnik znaków
XOR (HL)
; --------------------
; PROCEDURA 'L-STRING'
; --------------------
;
; $13CE : 5070
L_STRING: PUSH AF
CALL STK_FETCH
EX DE,HL
ADD HL,BC
PUSH HL
INC BC
INC BC
INC BC
RST 30H ; BC-MIEJSC
EX DE,HL
POP HL
DEC BC
DEC BC
PUSH BC
LDDR
EX DE,HL
POP BC
DEC BC
LD (HL),B
DEC HL
LD (HL),C
POP AF
; $13E7 : 5095
L_FIRST: PUSH AF
CALL REC_V80
POP AF
DEC HL
LD (HL),A
LD HL,(STKBOT) ; przenieś TKBOT
LD (E_LINE),HL ; do E_LINE
DEC HL ; przed spodem stosu
LD (HL),$80 ; umieść znacznik końca
RET
; ---------------------
; PROCEDURA 'STK-FETCH'
; ---------------------
; Procedura pobiera 5 bajtową wartość ze stosu kalkulatora, zmniejszając wskaźnik stosu o 5, aby wskazywał na koniec stosu. Przy liczbie
; zmiennoprzecinkowej wykładnik znajduje się w A, a mantysa ma 32 bity i jest umieszczana w EDCB. Przy łańcuchach początek jest w DE, a długość
; w BC. A nie jest używane.
; $13F8 : 5112
STK_FETCH: LD HL,(STKEND) ; adres końca stosu kalkulatora do HL
DEC HL ; Pobierz mantysę do EDBC
LD B,(HL)
DEC HL
LD C,(HL)
DEC HL
LD D,(HL)
DEC HL
LD E,(HL)
DEC HL
LD A,(HL) ; pobierz wykładnik do A
LD (STKEND),HL ; Ustaw nowy adres końca stosu kalkulatora
RET
; -------------------------
; PROCEDURA POLECENIA 'DIM'
; -------------------------
; Tworzona jest tablica i inicjowana na zero, które na ZX-81 jest także spacją.
; $1409 : 5129
DIM: CALL LOOK_VARS
; $140C : 5132
D_RPORT_C: JP NZ,REPORT_C
CALL SYNTAX_Z ; wyjdź przy sprawdzaniu składni
JR NZ,D_RUN ; jeśli nie, to naprzód do D-RUN
RES 6,C
CALL STK_VAR
CALL CHECK_END
; $141C : 5148
D_RUN: JR C,D_LETTER ; naprzód do D-LETTER
PUSH BC
CALL NEXT_ONE
CALL RECLAIM_2
POP BC
; $1426 : 5158
D_LETTER: SET 7,C
LD B,$00
PUSH BC
LD HL,$0001
BIT 6,C
JR NZ,D_SIZE ; naprzód do D-SIZE
LD L,$05
; $1434 : 5172
D_SIZE: EX DE,HL
; $1435 : 5173
D_NO_LOOP: RST 20H ; NASTĘPNY-ZNAK
LD H,$40
CALL INT_EXP1
JP C,REPORT_3 ; skocz wstecz do REPORT-3
POP HL
PUSH BC
INC H
PUSH HL
LD H,B
LD L,C
CALL GET_HL_DE
EX DE,HL
RST 18H ; POBIERZ-ZNAK
CP $1A
JR Z,D_NO_LOOP ; wstecz do D-NO-LOOP
CP $11 ; czy jest to ')' ?
JR NZ,D_RPORT_C ; jeśli nie, wstecz do D-RPORT-C
RST 20H ; NASTĘPNY-ZNAK
POP BC
LD A,C
LD L,B
LD H,$00
INC HL
INC HL
ADD HL,HL
ADD HL,DE
JP C,REPORT_4
PUSH DE
PUSH BC
PUSH HL
LD B,H
LD C,L
LD HL,(E_LINE)
DEC HL
CALL MAKE_ROOM
INC HL
LD (HL),A
POP BC
DEC BC
DEC BC
DEC BC
INC HL
LD (HL),C
INC HL
LD (HL),B
POP AF
INC HL
LD (HL),A
LD H,D
LD L,E
DEC DE
LD (HL),$00
POP BC
LDDR
; $147F : 5247
DIM_SIZES: POP BC
LD (HL),B
DEC HL
LD (HL),C
DEC HL
DEC A
JR NZ,DIM_SIZES ; wstecz do DIM-SIZES
RET
; -------------------
; PROCEDURA 'RESERVE'
; -------------------
; $1488 : 5256
RESERVE: LD HL,(STKBOT) ; pobierz do HL adres spodu stosu kalkulatora
DEC HL ; HL wskazuje teraz ostatni bajt w obszarze roboczym
CALL MAKE_ROOM
INC HL
INC HL
POP BC
LD (E_LINE),BC
POP BC
EX DE,HL
INC HL
RET
; ---------------------------
; PROCEDURA POLECENIA 'CLEAR'
; ---------------------------
; $149A : 5274
CLEAR: LD HL,(VARS)
LD (HL),$80
INC HL
LD (E_LINE),HL
; ------------------
; PROCEDURA 'X-TEMP'
; ------------------
; $14A3 : 5283
X_TEMP: LD HL,(E_LINE)
; ----------------------
; PROCEDURY 'USTAW-STOS'
; ----------------------
; $14A6 : 5286
SET_STK_B: LD (STKBOT),HL
; $14A9 : 5289
SET_STK_E: LD (STKEND),HL
RET
; ------------------------
; PROCEDURA 'TYLKO KURSOR'
; ------------------------
; Ta procedura jest wywoływana w celu stworzenia minimalnego wiersza z kursorem i NEWLINE oraz do ustawienia STKEND na początek wolnej przestrzeni
; na następnej pozycji.
; $14AD : 5293
CURSOR_IN: LD HL,(E_LINE) ; pobierz adres początku wiersza edycyjnego z E_LINE
LD (HL),$7F ; wstaw znak kursora
INC HL ; przejdź na następną pozycję
LD (HL),$76 ; wstaw znak NEWLINE
INC HL ; przejdź na następną wolną komórkę.
LD (IY+$22),$02 ; ustaw wielkość spodu ekranu w DF_SZ
JR SET_STK_B ; wyjdź poprzez SET-STK-B
; ---------------------
; PROCEDURA 'USTAW-MIN'
; ---------------------
; $14BC : 5308
SET_MIN: LD HL,MEMBOT ; zwykła pozycja obszaru pamięci kalkulatora
LD (MEM),HL ; uaktualnij zmienną systemową MEM
LD HL,(STKBOT) ; pobierz STKBOT
JR SET_STK_E ; wstecz do SET-STK-E
; ------------------------------------
; PROCEDURA 'ODZYSKAJ ZNACZNIK KOŃCA'
; ------------------------------------
; $14C7 : 5319
REC_V80: LD DE,(E_LINE)
JP RECLAIM_1
; -----------------
; PROCEDURA 'ALPHA'
; -----------------
; $14CE : 5326
ALPHA: CP $26
JR ALPHA_2 ; przeskocz naprzód do ALPHA-2
; --------------------
; PROCEDURA 'ALPHANUM'
; --------------------
; $14D2 : 5330
ALPHANUM: CP $1C
; $14D4 : 5332
ALPHA_2: CCF
RET NC
CP $40
RET
; -----------------------------------------------------
; PROCEDURA 'LICZBA DZIESIĘTNA DO ZMIENNO-PRZECINKOWEJ'
; -----------------------------------------------------
; $14D9 : 5337
DEC_TO_FP: CALL INT_TO_FP ; procedura INT-TO-FP pobiera pierwszą część
CP $1B ; czy znak jest '.' ?
JR NZ,E_FORMAT ; jeśli nie, naprzód do E-FORMAT
RST 28H ; KALKULATOR
.BYTE $A1 ; jeden-na-stos
.BYTE $C0 ; stos-do-pamięci-0
.BYTE $02 ; usuń
.BYTE $34 ; koniec-obliczeń
; $14E5 : 5349
NXT_DGT_1: RST 20H ; NASTĘPNY-ZNAK
CALL STK_DIGIT
JR C,E_FORMAT ; naprzód do E-FORMAT
RST 28H ; KALKULATOR
.BYTE $E0 ; na-stos-paimięć-0
.BYTE $A4 ; na-stos-dziesięć
.BYTE $05 ; dziel
.BYTE $C0 ; stos-do-pamięci-0
.BYTE $04 ; mnóż
.BYTE $0F ; dodaj
.BYTE $34 ; koniec-obliczeń
JR NXT_DGT_1 ; wróć w pętli do NXT-DGT-1 aż do wyczerpania cyfr
; $14F5 : 5365
E_FORMAT: CP $2A ; czy znakiem jest 'E' ?
RET NZ ; wróć, jeśli nie
LD (IY+$5D),$FF ; inicjuj zmienną systemową MEM-0-1st na $FF TRUE
RST 20H ; NASTĘPNY-ZNAK
CP $15 ; czy znakiem jest '+' ?
JR Z,SIGN_DONE ; jeśli tak, naprzód do SIGN-DONE
CP $16 ; czy jest to '-' ?
JR NZ,ST_E_PART ; jeśli nie, naprzód do ST-E-PART
INC (IY+$5D) ; zmień MEM-0-1st na FALSE
; $1508 : 5384
SIGN_DONE: RST 20H ; NASTĘPNY-ZNAK
; $1509 : 5385
ST_E_PART: CALL INT_TO_FP
RST 28H ; KALKULATOR m, e.
.BYTE $E0 ; na-stos-paimięć-0 m, e, (1/0) TRUE/FALSE
.BYTE $00 ; skocz-przy-prawdzie
.BYTE $02 ; do L1511, E-POSTVE
.BYTE $18 ; zmień-znak m, -e
; $1511 : 5393
E_POSTVE:.BYTE $38 ; e-do-potęgi x.
.BYTE $34 ; koniec-obliczeń x.
RET
; -------------------------
; PROCEDURA 'CYFRA NA STOS'
; -------------------------
; $1514 : 5396
STK_DIGIT: CP $1C
RET C
CP $26
CCF
RET C
SUB $1C
; ---------------------
; PROCEDURA 'A NA STOS'
; ---------------------
; $151D : 5405
STACK_A: LD C,A ; przenieś A do BC
LD B,$00 ; zerując starszy bajt
; ----------------------
; PROCEDURA 'BC NA STOS'
; ----------------------
; ZX81 nie posiada wydzielonego formatu dla liczb całkowitych, zatem zawartość rejestru BC musi być zamieniona na pełną postać zmiennoprzecinkową.
; $1520 : 5408
STACK_BC: LD IY,ERR_NR ; ponownie ustaw wskaźnik zmiennych systemowych.
PUSH BC ; zachowaj wartość całkowitą.
; teraz zapisz na stosie zero - pięć zerowych bajtów jako punkt startowy.
RST 28H ; KALKULATOR
.BYTE $A0 ; na-stos-zero 0.
.BYTE $34 ; koniec-obliczeń
POP BC ; odtwórz wartość całkowitą
LD (HL),$91 ; umieść $91 w wykładniku 65536.
; jest to największa dozwolona wartość
LD A,B ; pobierz starszy bajt
AND A ; sprawdź, czy jest równy zero.
JR NZ,STK_BC_2 ; jeśli nie, to naprzód do STK-BC-2
LD (HL),A ; inaczej wyzeruj z powrotem wykładnik
OR C ; sprawdź młodszy bajt
RET Z ; jeśli BC było równe 0, wróć - koniec roboty.
; inaczej ma być ustawiony bit, jeśli wartością jest tylko jeden.
LD B,C ; zachowaj C w B.
LD C,(HL) ; pobierz zero do C
LD (HL),$89 ; ustaw wykładnik $89 256.
; $1536 : 5430
STK_BC_2: DEC (HL) ; zmniejsz wykładnik - dzieląc liczbę przez 2
SLA C ; C<-76543210<-0
RL B ; C<-76543210<-C
JR NC,STK_BC_2 ; jeśli brak przeniesienia, wróć w pętli do STK-BC-2
SRL B ; 0->76543210->C
RR C ; C->76543210->C
INC HL ; zaadresuj pierwszy bajt mantysy
LD (HL),B ; wstaw B
INC HL ; zaadresuj drugi bajt mantysy
LD (HL),C ; wstaw C
DEC HL ; wskaż ponownie
DEC HL ; na wykładnik
RET
; --------------------------------------------------
; PROCEDURA 'LICZBA CAŁKOWITA NA ZMIENNOPRZECINKOWĄ'
; --------------------------------------------------
; $1548 : 5448
INT_TO_FP: PUSH AF
RST 28H ; KALKULATOR
.BYTE $A0 ; na-stos-zero
.BYTE $34 ; koniec-obliczeń
POP AF
; $154D : 5453
NXT_DGT_2: CALL STK_DIGIT
RET C
RST 28H ; KALKULATOR
.BYTE $01 ; zamień
.BYTE $A4 ; na-stos-dziesięć
.BYTE $04 ; mnóż
.BYTE $0F ; dodaj
.BYTE $34 ; koniec-obliczeń
RST 20H ; NASTĘPNY-ZNAK
JR NXT_DGT_2
; ------------------------------------------
; PROCEDURA 'E-FORMAT NA ZMIENNOPRZECINKOWĄ'
; ------------------------------------------
; (przesunięcie $38: 'e-to-fp')
; wywoływana z DEC-TO-FP i PRINT-FP.
; np. 2.3E4 to 23000.
; Ta procedura oblicza wartość xEm, gdzie m jest dodatnią lub ujemną liczbą całkowitą. Na prostym poziomie x jest mnożone przez 10 dla każdej
; jednostki w m. Jeśli wykładnik dziesiętny jest ujemny, to x jest dzielone przez dziesięć dla każdej jednostki w m. Skrót powstaje, jeśli wykładnik
; jest większy od siedmiu i w takim przypadku jest on zmniejszany o 7, a x zostaje pomnożone lub podzielone przez dziesięć milionów.
; Uwaga, w ZX-Spectrum zastosowano nawet sprytniejszą metodę wykorzystującą przesunięcia bitów poza wykładnik, w której wynik był osiągany w co
; najwyżej sześciu przesunięciach. Poniższa procedura musiała zostać całkowicie przepisana, w większości w kodzie maszynowym Z80. Chociaż już nie
; używany, literał kalkulatora pozostał ze względów kompatybilności wstecznej, natomiast nowa procedura była wywoływana bezpośrednio z kodu
; maszynowego.
; Na wejściu w ZX-81 wykładnik m jest na szczycie stosu, a zmiennoprzecinkowa mantysa leży na stosie poniżej wykładnika.
; $155A : 5466
E_TO_FP: RST 28H ; KALKULATOR x, m.
.BYTE $2D ; duplikuj x, m, m.
.BYTE $32 ; mniej-niż-0 x, m, (1/0).
.BYTE $C0 ; stos-do-pamięci-0 x, m, (1/0).
.BYTE $02 ; usuń x, m.
.BYTE $27 ; wartość-bezwzględna x, +m.
; $1560 : 5472
E_LOOP: .BYTE $A1 ; jeden-na-stos x, m,1.
.BYTE $03 ; odejmij x, m-1.
.BYTE $2D ; duplikuj x, m-1,m-1.
.BYTE $32 ; mniej-niż-0 x, m-1, (1/0).
.BYTE $00 ; skocz-przy-prawdzie x, m-1.
.BYTE $22 ; do L1587, E-END x, m-1.
.BYTE $2D ; duplikuj x, m-1, m-1.
.BYTE $30 ; dane-na-stos
.BYTE $33 ; Wykładnik: $83, Bajty: 1
.BYTE $40 ;(+00,+00,+00) x, m-1, m-1, 6.
.BYTE $03 ; odejmij x, m-1, m-7.
.BYTE $2D ; duplikuj x, m-1, m-7, m-7.
.BYTE $32 ; mniej-niż-0 x, m-1, m-7, (1/0).
.BYTE $00 ; skocz-przy-prawdzie x, m-1, m-7.
.BYTE $0C ; do L157A, E-LOW
; lecz jeśli wykładnik jest większy od 7, to zastosuj większe mnożenie lub dzielenie (przy ujemnym m) przez 10 milionów - 1e7.
.BYTE $01 ; zamień x, m-7, m-1.
.BYTE $02 ; usuń x, m-7.
.BYTE $01 ; zamień m-7, x.
.BYTE $30 ; dane-na-stos
.BYTE $80 ; Bajty: 3
.BYTE $48 ; Wykładnik $98
.BYTE $18,$96,$80 ;(+00) m-7, x, 10,000,000 (=f)
.BYTE $2F ; skocz
.BYTE $04 ; do L157D, E-CHUNK
; $157A : 5498
E_LOW: .BYTE $02 ; usuń x, m-1.
.BYTE $01 ; zamień m-1, x.
.BYTE $A4 ; na-stos-dziesięć m-1, x, 10 (=f).
; $157D : 5501
E_CHUNK: .BYTE $E0 ; na-stos-paimięć-0 m-1, x, f, (1/0)
.BYTE $00 ; skocz-przy-prawdzie m-1, x, f
.BYTE $04 ; do L1583, E-DIVSN
.BYTE $04 ; mnóż m-1, x*f.
.BYTE $2F ; skocz
.BYTE $02 ; do L1584, E-SWAP
; $1583 : 5507
E_DIVSN: .BYTE $05 ; dziel m-1, x/f (= new x).
; $1584 : 5508
E_SWAP: .BYTE $01 ; zamień x, m-1 (= new m).
.BYTE $2F ; skocz x, m.
.BYTE $DA ; do L1560, E-LOOP
; $1587 : 5511
E_END: .BYTE $02 ; usuń x. (-1)
.BYTE $34 ; koniec-obliczeń x.
RET
; ------------------------------------
; PROCEDURA 'ZMIENNOPRZECINKOWA DO BC'
; ------------------------------------
; Liczba zmiennoprzecinkowa zostaje pobrana ze stosu i umieszczona w parze rejestrów BC, w razie konieczności z zaokrągleniem.
; Dopuszczalny zakres od 0 do 65535.4999
; $158A : 5514
FP_TO_BC: CALL STK_FETCH ; procedura STK-FETCH - wykładnik do A, mantysa do EDCB.
AND A ; testuj zero
JR NZ,FPBC_NZRO ; jeśli nie zero, skocz naprzód do FPBC-NZRO
; inaczej wartość jest zero
LD B,A ; zeruj B
LD C,A ; i także C
PUSH AF ; zachowaj znaczniki na stosie maszynowym
JR FPBC_END ; naprzód do FPBC-END
; EDCB => BCE
; $1595 : 5525
FPBC_NZRO: LD B,E ; przenieś mantysę z EDCB
LD E,C ; do BCE. Bit 7 rejestru E jest 17-tym bitem, który będzie
LD C,D ; znaczący przy zaokrąglaniu, jeśli liczba już jest znormalizowana.
SUB $91 ; odejmij 65536
CCF ; wymień bit przeniesienia na przeciwny
BIT 7,B ; testuj bit znaku
PUSH AF ; wynik na stos
SET 7,B ; ustaw implikowany bit
JR C,FPBC_END ; naprzód z przeniesieniem z SUB/CCF do FPBC-END, liczba jest za duża.
INC A ; zwiększ wykładnik i
NEG ; zaneguj, aby otrzymać zakres $00 - $0F
CP $08 ; testuj, czy jest jeden lub dwa bajty
JR C,BIG_INT ; z dwoma bajtami naprzód do BIG-INT
LD E,C ; przesuń mantysę
LD C,B ; 8 bitów w prawo
LD B,$00 ; do B wstaw zero
SUB $08 ; zmniejsz wykładnik o 8
; $15AF : 5551
BIG_INT: AND A ; testuj wykładnik
LD D,A ; zachowaj wykładnik w D.
LD A,E ; bity ułamkowe do A
RLCA ; obróć najbardziej znaczący bit do przeniesienia, aby zaokrąglić już znormalizowaną liczbę.
JR Z,EXP_ZERO ; jeśli wykładnik jest zerem, naprzód do EXP-ZERO
; liczba jest znormalizowana
; $15B5 : 5557
FPBC_NORM: SRL B ; 0->76543210->C
RR C ; C->76543210->C
DEC D ; zmniejsz wykładnik
JR NZ,FPBC_NORM ; wstecz w pętli do FPBC-NORM, aż będzie zerowy wykładnik
; $15BC : 5564
EXP_ZERO: JR NC,FPBC_END ; bez przeniesienia naprzód do NO-ROUND
INC BC ; zaokrąglij w górę.
LD A,B ; testuj wynik
OR C ; na zero
JR NZ,FPBC_END ; jeśli różny od zera, naprzód do GRE-ZERO
POP AF ; odtwórz znacznik znaku
SCF ; ustaw przeniesienie, aby zaznaczyć przepełnienie
PUSH AF ; ponownie zachowaj połączone znaczniki
; $15C6 : 5574
FPBC_END: PUSH BC ; zachowaj wartość BC
; ustaw HL i DE na stos kalkulatora.
RST 28H ; KALKULATOR
.BYTE $34 ; koniec-obliczeń
POP BC ; odtwórz wartość BC
POP AF ; odtwórz znaczniki
LD A,C ; również skopiuj młodszy bajt do A.
RET
; -----------------------------------
; PROCEDURA 'ZMIENNOPRZECINKOWA DO A'
; -----------------------------------
; $15CD : 5581
FP_TO_A: CALL FP_TO_BC ; pobierz liczbę do BC i A
RET C ; jeśli za duża, wróć z ustawionym przeniesieniem
PUSH AF ; zachowaj wynik
DEC B ; sprawdź, czy starszy bajt jest równy zero
INC B
JR Z,FP_A_END ; jeśli tak, to naprzód do FP-A-END
POP AF ; odtwórz wynik
SCF ; ustaw znacznik przeniesienia, sygnalizując przepełnienie
RET
; $15D9 : 5593
FP_A_END: POP AF ; odtwórz wynik
RET
; ------------------------------------------
; PROCEDURA 'PISZ LICZBĘ ZMIENNOPRZECINKOWĄ'
; ------------------------------------------
; wypisuje 'ostatnią wartość' x na stosie kalkulatora.
; Istnieje wiele różnych formatów:
; PI wypisuje jako 3.1415927
; .123 wypisuje jako 0.123
; .0123 wypisuje jako .0123
; 999999999999 wypisuje jako 1000000000000
; 9876543210123 wypisuje jako 9876543200000
; Zaczyna od wyizolowania zera i wypisania dla tego przypadku znaku '0'. Liczby ujemne wypisują wiodący znak '-', a następnie wartość bezwzględną z x.
; $15DB : 5595
PRINT_FP: RST 28H ; KALKULATOR x.
.BYTE $2D ; duplikuj x, x.
.BYTE $32 ; mniej-niż-0 x, (1/0).
.BYTE $00 ; skocz-przy-prawdzie
.BYTE $0B ; do L15EA, PF-NGTVE x.
.BYTE $2D ; duplikuj x, x
.BYTE $33 ; większe-niż-0 x, (1/0).
.BYTE $00 ; skocz-przy-prawdzie
.BYTE $0D ; do L15F0, PF-POSTVE x.
.BYTE $02 ; usuń .
.BYTE $34 ; koniec-obliczeń .
LD A,$1C ; załaduj akumulator znakiem '0'
RST 10H ; PISZ-AKUMULATOR
RET
; $15EA : 5610
PF_NEGTVE:.BYTE $27 ; wartość-bezwzględna +x.
.BYTE $34 ; koniec-obliczeń +x.
LD A,$16 ; załaduj akumulator znakiem '-'
RST 10H ; PISZ-AKUMULATOR
RST 28H ; KALKULATOR x.
; $15F0 : 5616
PF_POSTVE: .BYTE $34 ; koniec-obliczeń x.
; rejestr HL adresuje wykładnik liczby zmiennoprzecinkowej. Jeśli jest dodatnia i przecinek znajduje się na lewo, to bit 7 ma wartość 1.
LD A,(HL) ; pobierz bajt wykładnika
CALL STACK_A ; i umieść go na stosie kalkulatora
; teraz zgrubnie wyznacz ilość cyfr n przed przecinkiem, odejmując połowę od właściwego wykładnika i mnożąc przez logarytm dziesiętny z 2.
; Właściwa ilość może być o jeden wyższa jako wynik całkowity.
RST 28H ; KALKULATOR x, e.
.BYTE $30 ; dane-na-stos
.BYTE $78 ; Wykładnik: $88, Bajty: 2
.BYTE $00,$80 ;(+00,+00) x, e, 128.5.
.BYTE $03 ; odejmij x, e -.5.
.BYTE $30 ; dane-na-stos
.BYTE $EF ; Wykładnik: $7F, Bajty: 4
.BYTE $1A,$20,$9A,$85 ; 0.30103 (log10 2)
.BYTE $04 ; mnóż x,
.BYTE $24 ; na-liczbę-całkowitą
.BYTE $C1 ; stos-do-pamięci-1 x, n.
.BYTE $30 ; dane-na-stos
.BYTE $34 ; Wykładnik: $84, Bajty: 1
.BYTE $00 ;(+00,+00,+00) x, n, 8.
.BYTE $03 ; odejmij x, n-8.
.BYTE $18 ; zmień-znak x, 8-n.
.BYTE $38 ; e-do-potęgi x * (10^n)
; ostatecznie ósma lub dziewiąta cyfra dziesiętna jest zaokrąglana. Dziesięciocyfrowa liczba całkowita może wzrosnąć w tym przypadku, powiedzmy,
; od 999999999.5 do 1000000000.
.BYTE $A2 ; na-stos-1/2
.BYTE $0F ; dodaj
.BYTE $24 ; na-liczbę-całkowitą i.
.BYTE $34 ; koniec-obliczeń
; Jeśli było tylko 8 cyfr, to ostateczne zaokrąglenie będzie miało miejsce na stosie kalkulatora powyżej, a następne dwie instrukcje wprowadzą
; zamaskowane zero, aby już nie było dalszych zaokrągleń. Jeśli wynik jest 9 cyfrową liczbą całkowitą, to zaokrąglenie odbywa się wewnątrz bufora.
LD HL,$406B ; zaadresuj zmienną systemową MEM-2-5th,
; która może być 'dziewiątą' cyfrą.
LD (HL),$90 ; wstaw wartość $90 10010000
; teraz, rozpoczynając od najniższej cyfry, wprowadź 8, 9 lub 10 cyfrową liczbę całkowitą, która reprezentuje znaczącą część liczby.
; Np. PI będzie liczbą 9 cyfrową 314159265
LD B,$0A ; licznik na 10 cyfr.
; $1615 : 5653
PF_LOOP: INC HL ; zwiększ wskaźnik
PUSH HL ; zachowaj adres bufora
PUSH BC ; zachowaj licznik
RST 28H ; KALKULATOR i.
.BYTE $A4 ; na-stos-dziesięć i, 10.
.BYTE $2E ; n-modulo-m i mod 10, i/10
.BYTE $01 ; zamień i/10, reszta.
.BYTE $34 ; koniec-obliczeń
CALL FP_TO_A ; pobierz do wynik do A $00-$09
OR $90 ; ustaw lewe 4 bity na 9
POP BC ; odtwórz licznik
POP HL ; odtwórz adres bufora
LD (HL),A ; wstaw wymaskowaną cyfrę do bufora
DJNZ PF_LOOP ; wróć w pętli do PF-LOOP dla wszystkich 10 cyfr
; najbardziej znacząca cyfra będzie ostatnia, lecz gdy liczba została wyczerpana, to jedna lub dwie ostatnie pozycje mogą zawierać zera ($90).
; Np. dla 'jeden' mamy zero jako oszacowanie wiodących cyfr.
; 1*10^8 100000000 jako wartość całkowita
; 90 90 90 90 90 90 90 90 91 90 jako zawartość bufora mem3/mem4
INC HL ; zwiększ wskaźnik o 1 poza bufor
LD BC,$0008 ; ustaw C na 8 (B i tak jest 0)
PUSH HL ; zachowaj wskaźnik
; $162C : 5676
PF_NULL: DEC HL ; zmniejsz wskaźnik
LD A,(HL) ; pobierz maskowaną cyfrę
CP $90 ; czy jest to wiodące zero ?
JR Z,PF_NULL ; jeśli tak, skocz w pętli wstecz do PF-NULL
; w tym punkcie znaleziono znaczącą cyfrę. Przeniesienie jest wyzerowane.
SBC HL,BC ; odejmij osiem od adresu.
PUSH HL ; ** również ten wskaźnik zachowaj
LD A,(HL) ; pobierz adresowany bajt
ADD A,$6B ; dodaj $6B - co zaokrągli cyfrę rozpływowo, jeśli ma wartość $95 lub więcej
PUSH AF ; zachowaj wynik przeniesienia.
; teraz wejdź do pętli zaokrąglającej liczbę. Po wykonaniu zaokrąglenia zero powstałe z niego lub będące wcześniej na tej pozycji zostaje zmienione
; z $90 na $80.
; $1639 : 5689
PF_RND_LP: POP AF ; odzyskaj przeniesienie ze stosu maszynowego.
INC HL ; zwiększ adres
LD A,(HL) ; pobierz nowy bajt
ADC A,$00 ; dodaj do niego przeniesienie
DAA ; wyrównaj dziesiętnie akumulator przeniesienie rozpłynie się po cyfrach '9'
PUSH AF ; zachowaj przeniesienie na stosie maszynowym.
AND $0F ; wydobądź cyfry 0 - 9 i ustaw znacznik zera, jeśli zero
LD (HL),A ; wstaw z powrotem do bufora
SET 7,(HL) ; ustaw bit 7, aby uczynić znak drukowalnym. lecz nie w przypadku końcowego zera za przecinkiem dziesiętnym
JR Z,PF_RND_LP ; jeśli zero, wstecz do PF-RND-LP aby rozważyć dalsze zaokrąglanie i identyfikację końcowych zer.
POP AF ; usuń zbędne dane ze stosu
POP HL ; odzyskaj dolny wskaźnik
; teraz wstaw 6 końcowych zer, które są drukowane, jeśli znajdują się przed przecinkiem dziesiętnym, lecz zaznacz koniec wydruku, jeśli są po
; przecinku dziesiętnym. Np.:
; 9876543210123 jest drukowane jako 9876543200000
; 123.456001 jest drukowane jako 123.456
LD B,$06 ; licznik na sześć.
; $164B : 5707
PF_ZERO_6: LD (HL),$80 ; wstaw maskowane zero
DEC HL ; zmniejsz wskaźnik
DJNZ PF_ZERO_6 ; dla wszystkich sześciu zer cofnij się w pętli do PF-ZERO-6
; n-mod-m zredukowało liczbę do zera i zostaje ono obecnie usunięte ze stosu kalkulatora przed pobraniem pierwotnego oszacowania wiodących cyfr.
RST 28H ; KALKULATOR 0.
.BYTE $02 ; usuń .
.BYTE $E1 ; na-stos-paimięć-1 n.
.BYTE $34 ; koniec-obliczeń n.
CALL FP_TO_A ; pobierz liczbę do A
JR Z,PF_POS ; jeśli dodatnia, przeskocz naprzód do PF-POS
NEG ; z ujemnych robimy dodatnie
; $165B : 5723
PF_POS: LD E,A ; przenieś licznik cyfr do E
INC E ; zwiększ dwukrotnie
INC E
POP HL ; odzyskaj wskaźnik pozycji o jeden poza buforem
; $165F : 5727
GET_FIRST: DEC HL ; zmniejsz adres
DEC E ; zmniejsz licznik cyfr
LD A,(HL) ; pobierz wymaskowany bajt
AND $0F ; wyizoluj prawe cztery bity.
JR Z,GET_FIRST ; jeśli jest to zero wiodące, wstecz do GET-FIRST
; teraz ustal, czy będzie potrzebny format E do wydruku
LD A,E ; przenieś teraz właściwą liczbę cyfr do A.
SUB $05 ; odejmij pięć
CP $08 ; porównaj z 8, skoro maksymalna liczba cyfr wynosi 13.
JP P,PF_E_FMT ; jeśli dodatnie, naprzód do PF-E-FMT
CP $F6 ; testuj na więcej niż cztery zera za przecinkiem.
JP M,PF_E_FMT ; jeśli są, naprzód do PF-E-FMT
ADD A,$06 ; testuj na zero wiodących cyfr, np. 0.5
JR Z,PF_ZERO_1 ; jeśli tak, naprzód do PF-ZERO-1
JP M,PF_ZEROS ; jeśli jest więcej niż jedno zero, skocz do PF-ZEROS
; w przeciwnym razie cyfry przed przecinkiem mają zostać wydrukowane
LD B,A ; liczba wiodących znaków do B.
; $167B : 5755
PF_NIB_LP: CALL PF_NIBBLE
DJNZ PF_NIB_LP ; wstecz w pętli dla kolejnych cyfr do PF-NIB-LP
JR PF_DC_OUT ; naprzód do PF-DC-OUT w celu przetworzenia części ułamkowej
; $1682 : 5762
PF_E_FMT: LD B,E ; licznik do B
CALL PF_NIBBLE ; procedura PF-NIBBLE drukuje jedną cyfrę.
CALL PF_DC_OUT ; procedura PF-DC-OUT obsługuje część ułamkową.
LD A,$2A ; przygotuj znak 'E'
RST 10H ; PISZ-AKUMULATOR
LD A,B ; przenieś wykładnik do A
AND A ; sprawdź znak.
JP P,PF_E_POS ; jeśli dodatnie, skocz naprzód do PF-E-POS
NEG ; zmień ujemny wykładnik na dodatni.
LD B,A ; zachowaj dodatni wykładnik w B.
LD A,$16 ; przygotuj znak '-'
JR PF_E_SIGN ; przeskocz naprzód do PF-E-SIGN
; $1698 : 5784
PF_E_POS: LD A,$15 ; przygotuj znak '+'
; $169A : 5786
PF_E_SIGN: RST 10H ; PISZ-AKUMULATOR
; teraz zamień całkowity wykładnik w B na dwie cyfry. Będzie miał wartość mniejszą od 99.
LD A,B ; pobierz dodatni wykładnik.
LD B,$FF ; rozpocznij od lewej cyfry równej minus jeden.
; $169E : 5790
PF_E_TENS: INC B ; zwiększ licznik dziesiątek
SUB $0A ; odejmij dziesięć od wykładnika
JR NC,PF_E_TENS ; skocz wstecz w pętli do PF-E-TENS, jeśli większe od dziesięć
ADD A,$0A ; anuluj efekt ostatniego odejmowania
LD C,A ; przenieś resztę do C
LD A,B ; wartość dziesiątek przenieś do A.
AND A ; testuj zero.
JR Z,PF_E_LOW ; przeskocz naprzód do PF-E-LOW, jeśli tak,
CALL OUT_CODE ; procedura OUT-CODE drukuje jako cyfry '1' - '9'
; $16AD : 5805
PF_E_LOW: LD A,C ; młodszy bajt do A
CALL OUT_CODE ; procedura OUT-CODE drukuje ostatnią cyfrę wykładnika.
RET
; ta odnoga zajmuje się zerami po przecinku dziesiętnym. Np. .01 lub .0000999
; $16B2 : 5810
PF_ZEROS: NEG ; zmiana znaku czyni liczbę dodatnią od 1 do 4.
LD B,A ; licznik zer do B.
LD A,$1B ; przygotuj znak '.'
RST 10H ; PISZ-AKUMULATOR
LD A,$1C ; przygotuj '0'
; $16BA : 5818
PF_ZRO_LP: RST 10H ; PISZ-AKUMULATOR
DJNZ PF_ZRO_LP ; w pętli skocz wstecz do PF-ZRO-LP
JR PF_FRAC_LP ; naprzód do PF-FRAC-LP
; istnieje potrzeba wyświetlenia zera wiodącego, np. przy 0.1, lecz nie przy .01
; $16BF : 5823
PF_ZERO_1: LD A,$1C ; przygotuj znak '0'.
RST 10H ; PISZ-AKUMULATOR
; ten podprogram obsługuje przecinek dziesiętny oraz cyfry końcowe. Jeśli kolejnym znakiem jest znaczone zero, $80, to nic więcej nie ma do wydrukowania.
; $16C2 : 5826
PF_DC_OUT: DEC (HL) ; zmniejsz adresowany znak
INC (HL) ; zwiększ go z powrotem
RET PE ; wróć z przepełnieniem (miał wartość 128), ponieważ brak części ułamkowej
; inaczej część ułamkowa występuje, zatem wydrukuj kropkę dziesiętną.
LD A,$1B ; przygotuj znak '.'
RST 10H ; PISZ-AKUMULATOR
; teraz wejdź do pętli, aby wydrukować końcowe cyfry
; $16C8 : 5832
PF_FRAC_LP: DEC (HL) ; test na znaczone zero.
INC (HL)
RET PE ; wróć po wyczerpaniu cyfr
CALL PF_NIBBLE ; drukuj cyfrę
JR PF_FRAC_LP ; wstecz do PF-FRAC-LP dla wszystkich cyfr ułamkowych.
; procedura do wydruku prawych czterech bitów
; $16D0 : 5840
PF_NIBBLE: LD A,(HL) ; pobierz adresowany bajt
AND $0F ; wymaskuj 4 najmłodsze bity
CALL OUT_CODE ; wydrukuj je jako cyfrę
DEC HL ; zmniejsz wskaźnik
RET
; --------------------------------------
; PROCEDURA 'PRZYGOTOWANIA DO DODAWANIA'
; --------------------------------------
; Ta procedura jest wywoływana dwukrotnie do przygotowania każdej z liczb zmiennoprzecinkowych do dodawania w miejscu na stosie kalkulatora.
; Wykładnik zostaje pobrany z pierwszego bajtu, który następnie jest czyszczony w celu odgrywania roli bajtu znaku i znacznika przepełnienia.
; Jeśli wykładnik ma wartość zero, to liczba również ma wartość 0 i wykonany zostaje wczesny powrót. Teraz zostaje ustawiony nadmiarowy bit znaku
; mantysy i w przypadku liczby ujemnej wszystkie pięć bajtów zostają zanegowane w kodzie U2 w celu przygotowania liczby do dodawania. Przy drugim
; wywołaniu wykładnik pierwszej liczby jest w rejestrze B.
; $16D8 : 5848
PREP_ADD: LD A,(HL) ; pobierz wykładnik.
LD (HL),$00 ; ustaw ten bajt na zero, aby przyjął wszelkie przepełnienia i określał liczbę standardowo jako dodatnią.
AND A ; testuj pobrany wykładnik na zero.
RET Z ; wróć z ustawionym znacznikiem Z, jeśli liczba jest zerem.
INC HL ; wskaż pierwszy bajt mantysy.
BIT 7,(HL) ; testuj bit znaku.
SET 7,(HL) ; ustaw go na jego wynikową wartość.
DEC HL ; ustaw wskaźnik ponownie na pierwszy bajt.
RET Z ; wróć, jeśli bit oznaczał liczbę dodatnią.
; jeśli jest ujemna, to wszystkie pięć bajtów zostanie zanegowane w U2, poczynając od najmłodszego.
PUSH BC ; zachowaj zawartość rejestru B.
LD BC,$0005 ; ustaw BC na pięć.
ADD HL,BC ; wskaż na komórkę poza piątym bajtem.
LD B,C ; ustaw licznik B na pięć.
LD C,A ; umieść oryginalny wykładnik w C.
SCF ; ustaw znacznik przeniesienia, aby była dodawana jedynka.
; teraz wejdź do pętli zmieniającej znak liczby na ujemny. Pierwszy z pięciu bajtów otrzyma wartość $FF oznaczającą liczbę ujemną.
; $16EC : 5868
NEG_BYTE: DEC HL ; wskaż na pierwszy, lub bardziej znaczący bajt.
LD A,(HL) ; pobierz go do akumulatora.
CPL ; zaneguj.
ADC A,$00 ; dodaj początkowe przeniesienie lub dalsze przeniesienia.
LD (HL),A ; wstaw liczbę z powrotem na swoje miejsce.
DJNZ NEG_BYTE ; w pętli skocz wstecz pięć razy do NEG-BYTE
LD A,C ; odtwórz wykładnik w akumulatorze.
POP BC ; odtwórz zawartość rejestru B.
RET
; --------------------------------
; PROCEDURA 'POBRANIA DWÓCH LICZB'
; --------------------------------
; Procedurę tę wykorzystują operacje dodawania, mnożenia i dzielenia do pobierania dwóch pięciobajtowych liczb adresowanych przez HL i DE
; ze stosu kalkulatora do rejestrów Z80. Rejestr HL nie może już wskazywać na pierwszą z tych dwóch liczb. Ponieważ 32-bitowe dodawanie
; wykonywane jest przy pomocy 16 bitowych instrukcji Z80, istotne jest, aby młodsze dwa bajty każdej mantysy znajdowały się w jednym zestawie
; rejestrów, a pozostałe bajty w zestawie alternatywnym.
; We: HL = najwyższa liczba, DE= najniższa liczba
; : alt': :
; Wy: :H,B-C:C,B: num1
; :L,D-E:D-E: num2
; $16F7 : 5879
FETCH_TWO: PUSH HL ; zachowaj HL
PUSH AF ; zachowaj A - wynikowy znak, gdy używane przy dzieleniu.
LD C,(HL)
INC HL
LD B,(HL)
LD (HL),A ; wstaw znak, gdy używane przy mnożeniu.
INC HL
LD A,C ; m1
LD C,(HL)
PUSH BC ; na stos m2 m3
INC HL
LD C,(HL) ; m4
INC HL
LD B,(HL) ; m5 BC zawiera m5 m4
EX DE,HL ; ustaw HL na początek drugiej liczby.
LD D,A ; m1
LD E,(HL)
PUSH DE ; na stos m1 n1
INC HL
LD D,(HL)
INC HL
LD E,(HL)
PUSH DE ; na stos n2 n3
EXX
POP DE ; ze stosu n2 n3
POP HL ; ze stosu m1 n1
POP BC ; ze stosu m2 m3
EXX
INC HL
LD D,(HL)
INC HL
LD E,(HL) ; DE zawiera n4 n5
POP AF ; odtwórz zachowane rejestry
POP HL
RET
; --------------------------------
; PROCEDURA 'PRZESUWANIA SKŁADNIKA'
; --------------------------------
; Akumulator zawiera różnicę pomiędzy dwoma wykładnikami. Dodana ma być najmniejsza liczba.
; $171A : 5914
SHIFT_FP: AND A ; testuj różnicę pomiędzy dwoma wykładnikami.
RET Z ; wróć, gdy zero - oba są znormalizowane.
CP $21 ; porównaj z 33 bitami.
JR NC,ADDEND_0 ; jeśli większe niż 32, skocz naprzód do ADDEND-0
PUSH BC ; zachowaj część w BC
LD B,A ; licznik przesunięć do B.
; Teraz wykonaj B przesunięć na składniku L'D'E'D E w celu zrównania go z dodajną H'B'C'C B
; $1722 : 5922
ONE_SHIFT: EXX
SRA L ; 76543210->C bit 7 bez zmian.
RR D ; C->76543210->C
RR E ; C->76543210->C
EXX
RR D ; C->76543210->C
RR E ; C->76543210->C
DJNZ ONE_SHIFT ; w pętli wykonuj B razy od ONE-SHIFT
POP BC ; odzyskaj BC
RET NC ; wróć, jeśli w ostatnim przesuwie nie było przeniesienia.
; jeśli ustawiony został znacznik przeniesienia, to nastąpiła utrata dokładności, zatem zaokrąglij składnik.
CALL ADD_BACK
RET NZ ; wróć, jeśli nie FF 00 00 00 00
; to odgałęzienie ustawia wszystkie pięć bajtów składnika na zero i wykonywane jest, podczas dodawania, gdy wykładniki są zbyt oddalone od siebie,
; aby bity składnika mogły wpłynąć na wynik dodawania.
; $1736 : 5942
ADDEND_0: EXX ; wybierz alternatywny zestaw rejestrów dla bardziej znaczących bajtów.
XOR A ; wyzeruj akumulator.
; ten punkt wejścia (z mnożenia) ustawia wszystkie cztery bajty na zero lub przy kontynuowaniu od poprzedniego adresu, podczas dodawania,
; ustawia wszystkie pięć bajtów na zero.
; $1738 : 5944
ZEROS_4_5: LD L,$00 ; ustaw bajt 1 na zero.
LD D,A ; ustaw bajt 2 na A.
LD E,L ; ustaw bajt 3 na zero.
EXX ; wybierz główny zestaw rejestrów
LD DE,$0000 ; ustaw niższe bajty 4 i 5 na zero.
RET
; ----------------------------
; PROCEDURA 'DODAJ-Z-POWROTEM'
; ----------------------------
; Wywoływana z SHIFT-FP powyżej podczas dodawania i po normalizacji z mnożenia. W rzeczywistości jest to 32 bitowa procedura zwiększania o 1,
; która ustawia znacznik zera zgodnie z 32 bitowym wynikiem. Podczas dodawania jedynie liczby ujemne, typu FF FF FF FF FF, wersje dopełnieniowe
; liczb powiedzmy xx 80 00 00 01 dadzą w wyniku pełną kaskadową zamianę na FF 00 00 00 00. Liczba FF FF FF FF FF przy przesuwaniu w prawo nie jest
; zmieniana przez SHIFT-FP, lecz ustawia przeniesienie, co powoduje wywołanie tej procedury.
; $1741 : 5953
ADD_BACK: INC E
RET NZ
INC D
RET NZ
EXX
INC E
JR NZ,ALL_ADDED ; jeśli brak przepełnienia, skocz naprzód do ALL-ADDED
INC D
; $174A : 5962
ALL_ADDED: EXX
RET ; wróć z ustawionym znacznikiem zera przy zerowej mantysie.
; ----------------------
; OPERACJA 'ODEJMOWANIA'
; ----------------------
; po prostu zmień znak odjemnej i wykonaj dodawanie.
; $174C : 5964
SUBTRACT: LD A,(DE) ; pobierz bajt wykładnika drugiej liczby - odjemnej.
AND A ; testuj zero
RET Z ; jeśli zero, wróć - pierwsza liczba jest wynikiem.
INC DE ; adresuj pierwszy bajt mantysy.
LD A,(DE) ; pobierz go do akumulatora.
XOR $80 ; zmień na przeciwny bit znaku.
LD (DE),A ; umieść z powrotem na stosie kalkulatora.
DEC DE ; wskaż bajt wykładnika. Przejdź do procedury dodawania.
; --------------------
; OPERACJA 'DODAWANIA'
; --------------------
; Operacja dodawania używa większość rejestrów Z80 do dodania dwóch liczb zmiennoprzecinkowych. Jest to operacja dwuargumentowa, a na wejściu HL
; wskazuje pierwszą liczbę i DE drugą.
; $1755 : 5973
ADDITION: EXX
PUSH HL ; zachowaj wskaźnik do następnego literału.
EXX
PUSH DE ; zachowaj wskaźnik do drugiej liczby
PUSH HL ; zachowaj wskaźnik do pierwszej liczby - stanie się
; wskaźnikiem wyniku na stosie kalkulatora.
CALL PREP_ADD
LD B,A ; zachowaj pierwszy wykładnik w B.
EX DE,HL ; zamień ze sobą wskaźniki liczb.
CALL PREP_ADD
LD C,A ; zachowaj drugi wykładnik w C.
CP B ; porównaj bajty wykładników.
JR NC,SHIFT_LEN ; jeśli drugi większy, skocz naprzód do SHIFT-LEN
LD A,B ; inaczej większy wykładnik do A
LD B,C ; mniejszy do B
EX DE,HL ; zamień ze sobą wskaźniki liczb.
; $1769 : 5993
SHIFT_LEN: PUSH AF ; zachowaj starszy wykładnik
SUB B ; odejmij mniejszy wykładnik
CALL FETCH_TWO
CALL SHIFT_FP
POP AF ; odtwórz większy wykładnik.
POP HL ; odtwórz wskaźnik wyniku.
LD (HL),A ; wstaw bajt wykładnika.
PUSH HL ; ponownie zachowaj wskaźnik wyniku.
; teraz wykonaj 32-bitowe dodawanie, wykorzystując 16-bitowe instrukcje dodawania Z80
LD L,B ; przenieś indywidualnie młodsze bajty mantysy do
LD H,C ; rejestru HL
ADD HL,DE ; operacja dodawania młodszych bajtów
; teraz dwie starsze pary bajtów, które są przechowywane w zapasowym zestawie rejestrów.
EXX ; przełącz się na zapasowe rejestry
EX DE,HL ; przenieś starsze bajty mantysy do rejestru HL.
ADC HL,BC ; dodawanie starszych bajtów z przeniesieniem z poprzedniej operacji.
EX DE,HL ; wynik w DE, bajty znaku ($FF lub $00) do HL
; teraz zajmiemy się dwoma bajtami znaków
LD A,H ; pobierz bajt znaku z num1
ADC A,L ; dodaj z przeniesieniem z dodawania mantys 00 lub 01 lub FE lub FF
LD L,A ; wynik w L.
; możliwe wyniki znaków i nadmiaru z mantysy
;
; H + L + carry = L RRA XOR L RRA
; ------------------------------------------------------------
; 00 + 00 = 00 00 00
; 00 + 00 + carry = 01 00 01 carry
; FF + FF = FE C FF 01 carry
; FF + FF + carry = FF C FF 00
; FF + 00 = FF FF 00
; FF + 00 + carry = 00 C 80 80
RRA ; C->76543210->C
XOR L ; ustaw bit 0, jeśli wymagane jest przesunięcie.
EXX ; przełącz na główny zestaw rejestrów
EX DE,HL ; pełny wynik mantysy teraz w rejestrach D'E'D E.
POP HL ; odtwórz wskaźnik do wynikowego wykładnika na stosie kalkulatora.
RRA ; czy wystąpił nadmiar ?
JR NC,TEST_NEG ; jeśli nie, przeskocz naprzód do TEST-NEG
; jeśli dodawanie dwóch dodatnich mantys wywołało nadmiar lub jeśli dodawanie dwóch ujemnych mantys nie wywołało nadmiaru, to wynikowy wykładnik
; musi być zwiększony o 1, a mantysa przesunięta jedną pozycję w prawo.
LD A,$01 ; wymagane jedno przesunięcie.
CALL SHIFT_FP ; procedura SHIFT-FP wykonuje pojedyncze przesunięcie zaokrąglając stracone bity
INC (HL) ; zwiększ wykładnik.
JR Z,ADD_REP_6 ; naprzód do ADD-REP-6, jeśli wykładnik przewija się z FF na zero, ponieważ liczba jest wtedy zbyt duża dla systemu.
; na tym etapie wykładnik na stosie kalkulatora jest poprawny.
; $1790 : 6032
TEST_NEG: EXX ; przełącz się na alternatywny zestaw rejestrów.
LD A,L ; załaduj znak wyniku do akumulatora.
AND $80 ; izoluj bit 7 z bajtu znaku, ustawiając znacznik zera, jeśli liczba jest dodatnia.
EXX ; wróć do głównego zestawu rejestrów.
INC HL ; wskaż pierwszy bajt mantysy
LD (HL),A ; wstaw $00 dla liczby dodatniej lub $80 dla liczby ujemnej na tej pozycji na stosie kalkulatora
DEC HL ; wskaż ponownie wykładnik.
JR Z,GO_NC_MLT ; jeśli liczba jest dodatnia, skocz naprzód do GO-NC-MLT
; liczba ujemna musi być uzupełniona do podstawy 2, zanim zostanie umieszczona na stosie.
LD A,E ; pobierz najniższy (pierwszy z prawej) bajt mantysy.
NEG ; neguj
CCF ; zmień przeniesienie na przeciwne
LD E,A ; umieść z powrotem w rejestrze
LD A,D
CPL
ADC A,$00
LD D,A
EXX ; przełącz się do wyższych (pierwszych z lewej) 16 bitów.
LD A,E
CPL
ADC A,$00
LD E,A
LD A,D
CPL
ADC A,$00
JR NC,END_COMPL ; bez przepełnienia, naprzód do END-COMPL
; inaczej cała mantysa jest zerem. 00 00 00 00
RRA ; ustaw mantysę na 80 00 00 00
EXX ; przełącz.
INC (HL) ; zwiększ wykładnik.
; $17B3 : 6067
ADD_REP_6: JP Z,REPORT_6 ; jeśli wykładnik się wyzerował, skocz naprzód do REPORT-6 'Liczba zbyt duża'
EXX ; przełącz się z powrotem na alternatywny zestaw rejestrów.
; $17B7 : 6071
END_COMPL: LD D,A ; wstaw pierwszy bajt mantysy z powrotem do DE.
EXX ; przełącz się na główny zestaw rejestrów.
; $17B9 : 6073
GO_NC_MLT: XOR A ; wyzeruj przeniesienie i akumulator, aby nie zostały przeniesione naprzód żadne dodatkowe bity, co zdarza się w mnożeniu.
JR TEST_NORM ; naprzód do wspólnego kodu pod adresem TEST-NORM
; ---------------------------------------------------
; PROCEDURA 'PRZYGOTOWANIA DO MNOŻENIA LUB DZIELENIA'
; ---------------------------------------------------
; ta procedura jest wywoływana dwukrotnie z mnożenia i z dzielenia, aby przygotować każdą z dwóch liczb do tych operacji. Początkowo akumulator
; przechowuje zero, a po drugim wywołaniu bit 7 akumulatora stanie się bitem znaku wyniku.
; $17BC : 6076
PREP_M_D: SCF ; ustaw znacznik przeniesienia, aby zasygnalizować, że liczba jest zerem.
DEC (HL) ; testuj wykładnik
INC (HL) ; na wartość zero.
RET Z ; jeśli jest zero, wróć z ustawionym znacznikiem przeniesienia.
INC HL ; adresuj pierwszy bajt mantysy.
XOR (HL) ; suma modulo 2 bitów znaku.
SET 7,(HL) ; ustaw wynikowy bit.
DEC HL ; wskaż bajt wykładnika.
RET
; -------------------
; OPERACJA 'MNOŻENIA'
; -------------------
; $17C6 : 6086
MULTIPLY: XOR A ; zeruj bit 7 - bieżącego znacznika znaku.
CALL PREP_M_D
RET C ; wróć, jeśli liczba jest zerem. zero * cokolwiek = zero.
EXX
PUSH HL ; zachowaj wskaźnik do 'następnego literału'
EXX
PUSH DE ; zachowaj wskaźnik do drugiej liczby
EX DE,HL ; ustaw HL na adres drugiej liczby.
CALL PREP_M_D
EX DE,HL ; HL pierwsza liczba, DE - druga liczba
JR C,ZERO_RSLT ; z przeniesieniem naprzód do ZERO-RSLT cokolwiek * zero = zero.
PUSH HL ; zachowaj wskaźnik do pierwszej liczby.
CALL FETCH_TWO ; procedura FETCH-TWO pobiera dwie mantysy ze stosu kalkulatora do B'C'C,B D'E'D E
; (HL będzie nadpisane, lecz wynikowy znak w A zostaje wstawiony na stos kalkulatora)
LD A,B ; przenieś pierwszy bajt mantysy pierwszej liczby
AND A ; wyzeruj przeniesienie.
SBC HL,HL ; skrót dla LD HL,$0000 w celu ustawienia młodszych dwóch bajtów wyniku. (2 bajty programu)
EXX ; przełącz na zapasowe rejestry
PUSH HL ; zachowaj HL
SBC HL,HL ; ustaw HL na zero również w celu ustawienia starszych dwóch bajtów wyniku i wyzeruj przeniesienie.
EXX ; przełącz na zwykłe rejestry.
LD B,$21 ; rejestr B może teraz zliczać trzydzieści trzy przesunięcia
JR STRT_MLT ; naprzód do punktu wejścia pętli STRT-MLT
; Wejście do pętli mnożenia znajduje się pod adresem STRT-LOOP.
; $17E7 : 6119
MLT_LOOP: JR NC,NO_ADD ; jeśli brak przeniesienia, naprzód do NO-ADD inaczej dodaj mnożnik.
ADD HL,DE ; dodaj dwa najmłodsze bajty do wyniku
EXX ; przełącz się na starsze bajty.
ADC HL,DE ; dodaj starsze bajty mnożnika oraz przeniesienie.
EXX ; przełącz na główny zestaw rejestrów.
; w każdym przypadku przesuń wynik w prawo do B'C'C A
; $17EE : 6126
NO_ADD: EXX ; przełącz na rejestry zapasowe
RR H ; C > 76543210 > C
RR L ; C > 76543210 > C
EXX
RR H ; C > 76543210 > C
RR L ; C > 76543210 > C
; $17F8 : 6136
STRT_MLT: EXX ; przełącz na rejestry zapasowe.
RR B ; C > 76543210 > C
RR C ; C > 76543210 > C
EXX ; teraz główny zestaw rejestrów
RR C ; C > 76543210 > C
RRA ; C > 76543210 > C
DJNZ MLT_LOOP ; wykonuj 33 razy w pętli od MLT-LOOP
EX DE,HL
EXX
EX DE,HL
EXX
POP BC
POP HL
LD A,B
ADD A,C
JR NZ,MAKE_EXPT
AND A
; $180E : 6158
MAKE_EXPT: DEC A
CCF ; Zaneguj znacznik przeniesienia
; $1810 : 6160
DIVN_EXPT: RLA
CCF ; Zaneguj znacznik przeniesienia
RRA
JP P,OFLW1_CLR
JR NC,REPORT_6
AND A
; $1819 : 6169
OFLW1_CLR: INC A
JR NZ,OFLW2_CLR
JR C,OFLW2_CLR
EXX
BIT 7,D
EXX
JR NZ,REPORT_6
; $1824 : 6180
OFLW2_CLR: LD (HL),A
EXX
LD A,B
EXX
; dodawanie łączy się tutaj z wyzerowanym znacznikiem przeniesienia.
; $1828 : 6184
TEST_NORM: JR NC,NORMALIZE
LD A,(HL)
AND A
; $182C : 6188
NEAR_ZERO: LD A,$80 ; przygotuj się do ratowania najbardziej znaczącego bitu mantysy, jeśli jest ustawiony.
JR Z,SKIP_ZERO ; skocz naprzód do SKIP-ZERO
; $1830 : 6192
ZERO_RSLT: XOR A ; stwórz maskę zerową bajtu, aby zasygnalizować ustawienie pięciu bajtów na zero.
; $1831 : 6193
SKIP_ZERO: EXX ; przełącz na rejestry zapasowe
AND D ; wyizoluj najbardziej znaczący bit, jeśli A ma wartość $80.
CALL ZEROS_4_5 ; procedura ZEROS-4/5 ustawia mantysę bez zmiany znaczników.
RLCA ; testuj ustawienie MSB. Bit 7 idzie do bitu 0 albo $00 -> $00, albo $80 -> $01
LD (HL),A ; utwórz wykładnik $01 (najmniejszy) lub $00 dla zero
JR C,OFLOW_CLR ; w pierwszym przypadku skocz naprzód do OFLOW-CLR
INC HL ; adresuj pierwszy bajt mantysy na stosie kalkulatora.
LD (HL),A ; wstaw zero dla bitu znaku.
DEC HL ; wskaż zerowy wykładnik
JR OFLOW_CLR ; naprzód do OFLOW-CLR
; ta gałąź jest wspólna dla dodawania i mnożenia, gdy wynik mantysy jest wciąż w rejestrach D'E'D E .
; $183F : 6207
NORMALIZE: LD B,$20 ; będzie potrzeba co najmniej 32 przesuwy w lewo.
; $1841 : 6209
SHIFT_ONE: EXX ; adresuj starsze 16 bitów.
BIT 7,D ; testuj pierwszy bit od lewej
EXX ; adresuj młodsze 16 bitów.
JR NZ,NORML_NOW ; jeśli pierwszy od lewej bit był ustawiony, skocz naprzód do NORML-NOW
RLCA ; to przechowuje zero z dodawania, 33-ci bit dla mnożenia
RL E ; C < 76543210 < C
RL D ; C < 76543210 < C
EXX ; adresuj starsze 16 bitów.
RL E ; C < 76543210 < C
RL D ; C < 76543210 < C
EXX ; przełącz na główne rejestry.
DEC (HL) ; zmniejsz bajt wykładnika na stosie kalkulatora.
JR Z,NEAR_ZERO ; jeśli wykładnik osiąga zero, wróć do NEAR-ZERO, jest możliwe, że ostatnia rotacja ustawiła bit 7 rejestru D.
; Sprawdzimy to.
DJNZ SHIFT_ONE ; wróć wstecz w pętli do SHIFT-ONE
; Jeśli wykonano trzydzieści dwa przesuwy w lewo bez ustawienia najbardziej znaczącego bitu, to wynik jest zerem.
JR ZERO_RSLT ; wstecz do ZERO-RSLT
; $1859 : 6233
NORML_NOW: RLA ; dla ścieżki dodawania A ma zawsze wartość zero. Dla ścieżki mnożenia ...
JR NC,OFLOW_CLR ; naprzód do OFLOW-CLR
; ta gałąź jest wykonywana tylko przy mnożeniu.
CALL ADD_BACK
JR NZ,OFLOW_CLR ; naprzód do OFLOW-CLR
EXX
LD D,$80
EXX
INC (HL)
JR Z,REPORT_6 ; naprzód do REPORT-6
; teraz przenosimy mantysę ze zestawu rejestrów na stos kalkulatora dołączając do niej bit znaku, który już się tam znajduje.
; $1868 : 6248
OFLOW_CLR: PUSH HL ; zachowaj wskaźnik wykładnika na stosie.
INC HL ; adresuj pierwszy bajt mantysy, który został poprzednio załadowany bitem znaku $00 lub $80.
EXX
PUSH DE ; umieść na stosie dwa najbardziej znaczące bajty.
EXX
POP BC ; pobierz ze stosu - właściwa mantysa jest teraz w BCDE.
; teraz bit znaku.
LD A,B ; pierwszy bajt mantysy do A
RLA ; wyprowadź bit 7, który jest ustawiony na 1
RL (HL) ; wprowadź bit znaku ze stosu do znacznika przeniesienia.
RRA ; wprowadź bit znaku do 7 bitu bajtu mantysy.
; przenosimy mantysę z głównych rejestrów na stos kalkulatora.
LD (HL),A
INC HL
LD (HL),C
INC HL
LD (HL),D
INC HL
LD (HL),E
POP HL ; odtwórz wskaźnik do num1, które jest teraz wynikiem.
POP DE ; odtwórz wskaźnik do num2, który jest teraz STKEND.
EXX
POP HL ; odtwórz wskaźnik do następnego literału kalkulatora.
EXX
RET
; $1880 : 6272
REPORT_6: RST 08H ; ERROR-1
.BYTE $05 ; Raport Błędu: Przepełnienie arytmetyczne.
; --------------------
; OPERACJA 'DZIELENIE'
; --------------------
; "Ze wszystkich operacji arytmetycznych dzielenie jest najbardziej skomplikowane oraz najmniej zrozumiałe. Szczególnie interesujące jest to,
; że programista w firmie Sinclair sam popełnił błąd w swoim kodzie (lub skopiował czyjś błąd!), ponieważ PRINT PEEK 6352 [ $18D0 ]
; ('niepoprawiony' ROM, 6351 [ $18CF ] ) powinno dawać 218 a nie 225." - Dr Ian Logan, czasopismo Syntax sierpień/wrzesień 1982.
; [ tj. skok powinien być wykonywany do div-34th ]
; Najpierw sprawdź dzielenie przez zero
; $1882 : 6274
DIVISION: EX DE,HL ; zajmij się najpierw drugą liczbą
XOR A ; ustaw znacznik znaku
CALL PREP_M_D
JR C,REPORT_6 ; wstecz do REPORT-6, jeśli zero 'Przepełnienie arytmetyczne'
EX DE,HL ; teraz przygotuj drugą liczbę i sprawdź ją na zero.
CALL PREP_M_D
RET C ; jeśli zero, wróć
EXX
PUSH HL ; zachowaj wskaźnik do następnego literału kalkulatora.
EXX
PUSH DE ; zachowaj wskaźnik do dzielnika - będzie to STKEND.
PUSH HL ; zachowaj wskaźnik do dzielnej - będzie to wynik.
CALL FETCH_TWO ; procedura FETCH-TWO pobiera te dwie liczby
; do rejestrów H'B'C'C B
; L'D'E'D E
EXX
PUSH HL ; zachowaj oba wykładniki.
LD H,B ; przenieś dzielną do H'L'H L
LD L,C
EXX
LD H,C
LD L,B
XOR A ; wyzeruj przeniesienie oraz akumulator.
LD B,$DF ; licz wprzód od -33 dziesiętnie
JR DIV_START ; naprzód do punktu wewnątrz pętli pod adres DIV-START
; $18A2 : 6306
DIV_LOOP: RLA ; wymnóż częściowy iloraz przez dwa,
RL C ; ustawiając bit wynikowy z przeniesienia.
EXX
RL C
RL B
EXX
; $18AB : 6315
DIV_34TH: ADD HL,HL
EXX
ADC HL,HL
EXX
JR C,SUBN_ONLY ; naprzód do SUBN-ONLY
; $18B2 : 6322
DIV_START: SBC HL,DE ; odejmij część dzielnika.
EXX
SBC HL,DE
EXX
JR NC,NO_RSTORE ; jeśli da się odejmować, naprzód do NO-RSTORE
ADD HL,DE ; inaczej odtwórz
EXX
ADC HL,DE
EXX
AND A ; wyczyść przeniesienie
JR COUNT_ONE ; naprzód do COUNT-ONE
; $18C2 : 6338
SUBN_ONLY: AND A
SBC HL,DE
EXX
SBC HL,DE
EXX
; $18C9 : 6345
NO_RSTORE: SCF ; ustaw znacznik przeniesienia
; $18CA : 6346
COUNT_ONE: INC B ; zwiększ licznik
JP M,DIV_LOOP ; jeśli wciąż minus, wróć do DIV-LOOP
PUSH AF
JR Z,DIV_START ; wstecz do DIV-START
; "Ten skok jest wykonywany w złe miejsce. 34-ty bit nigdy nie zostanie osiągnięty bez wcześniejszego przesunięcia dzielnej. Dlatego ważne wyniki,
; takie jak 1/10 i 1/1000, nie są zaokrąglane tak jak powinny być. Zaokrąglenie nigdy nie zachodzi, jeśli zależy od 34-tego bitu. Skok powinien być
; wykonywany do DIV-34th powyżej." - Dr Frank O'Hara, "The Complete Spectrum ROM Disassembly", 1983, wydane przez Melbourne House.
; Jednakże, gdy dokona się tej zmiany, to (1/2=.5) będzie teraz dawało wynik true, natomiast (.25=1/4), które dawało true, już nie będzie tak działać.
LD E,A
LD D,C
EXX
LD E,C
LD D,B
POP AF
RR B
POP AF
RR B
EXX
POP BC
POP HL
LD A,B
SUB C
JP DIVN_EXPT ; skocz wstecz do DIVN-EXPT
; -------------------------------------------------------
; PROCEDURA 'OBCINANIA LICZBY CAŁKOWITEJ W KIERUNKU ZERA'
; -------------------------------------------------------
; $18E4 : 6372
TRUNCATE: LD A,(HL) ; pobierz wykładnik
CP $81 ; porównaj z +1
JR NC,T_GR_ZERO ; jeśli 1 lub więcej, skocz naprzód do T-GR-ZERO
; inaczej ta liczba jest mniejsza niż plus lub minus jeden i można uczynić ją zerem
LD (HL),$00 ; zrób wykładnik zero.
LD A,$20 ; przygotuj się na wyzerowanie 32 bitów mantysy.
JR NIL_BYTES ; naprzód do NIL-BYTES
; $18EF : 6383
T_GR_ZERO: SUB $A0 ; odejmij +32 od wykładnika
RET P ; wróć, jeśli wynik dodatni, ponieważ wszystkie 32 bity mantysy odnoszą się do części całkowitej.
; Pływający przecinek jest gdzieś na prawo od mantysy
NEG ; inaczej zaneguj, aby utworzyć liczbę, której prawe bity będą wyczyszczone.
; przykładowo, zaniedbując bit znaku, liczba 3.5 jest przechowywana jako wykładnik $82 mantysa .11100000 00000000 00000000 00000000
; Potrzebujemy ustawić $82 - $A0 = $E2 NEG = $1E (trzydzieści) bitów na zero, aby utworzyć liczbę całkowitą.
; Znak liczby nigdy nie jest rozważany, ponieważ pierwszy bit mantysy musi być częścią liczby całkowitej.
; $18F4 : 6388
NIL_BYTES: PUSH DE ; zachowaj wskaźnik do STKEND
EX DE,HL ; HL wskazuje na STKEND
DEC HL ; teraz na ostatni bajt mantysy.
LD B,A ; Przenieś licznik bitów do rejestru B.
SRL B ; podziel
SRL B ; przez
SRL B ; osiem
JR Z,BITS_ZERO ; jeśli zero, naprzód do BITS-ZERO
; inaczej pierwotny licznik zawierał osiem lub więcej i całe bajty można wyczyścić.
; $1900 : 6400
BYTE_ZERO: LD (HL),$00 ; ustaw osiem bitów na zero.
DEC HL ; wskaż bardziej znaczący bajt mantysy.
DJNZ BYTE_ZERO ; skocz w pętli wstecz do BYTE-ZERO
; teraz rozważ bity resztowe.
; $1905 : 6405
BITS_ZERO: AND $07 ; wyizoluj pozostałe bity
JR Z,IX_END ; jeśli ich brak, skocz naprzód do IX-END
LD B,A ; przenieś licznik bitów do rejestru B.
LD A,$FF ; utwórz maskę 11111111
; $190C : 6412
LESS_MASK: SLA A ; 1 <- 76543210 <- o przesuń maskę w lewo.
DJNZ LESS_MASK ; w pętli powtarzaj aż do wyzerowania licznika
AND (HL) ; usuń niechciane prawe bity
LD (HL),A ; i umieść w bajcie mantysy.
; $1912 : 6418
IX_END: EX DE,HL ; odtwórz wskaźnik wyniku z DE.
POP DE ; odtwórz STKEND ze stosu.
RET
;************************************
;** KALKULATOR ZMIENNOPRZECINKOWY **
;************************************
; Ogólnie kalkulator unika używania rejestru IY.
; Wyjątkami są val i str$.
; Więc programista w asemblerze, który wyłączył przerwania w celu używania IY do innych celów, może wciąż korzystać z kalkulatora do celów matematycznych.
; -----------------
; 'TABLICA STAŁYCH'
; -----------------
; ZX81 posiada tylko liczby zmiennoprzecinkowe. Zarówno ZX80 jak i ZX Spectrum posiadają również liczby całkowite w pewnej postaci.
; $1915 : 6421
STK_ZERO: .BYTE $00 ; Bajty: 1
.BYTE $B0 ; Wykładnik $00
.BYTE $00 ;(+00,+00,+00)
; $1918 : 6424
STK_ONE: .BYTE $31 ; Wykładnik $81, Bajty: 1
.BYTE $00 ;(+00,+00,+00)
; $191A : 6426
STK_HALF: .BYTE $30 ; Wykładnik: $80, Bajty: 1
.BYTE $00 ;(+00,+00,+00)
; $191C : 6428
STK_PI_2: .BYTE $F1 ; Wykładnik: $81, Bajty: 4
.BYTE $49,$0F,$DA,$A2
; $1921 : 6433
STK_TEN: .BYTE $34 ; Wykładnik: $84, Bajty: 1
.BYTE $20 ;(+00,+00,+00)
; -----------------
; 'TABLICA ADRESÓW'
; -----------------
; rozpoczyna się od operacji dwuargumentowych, które posiadają dwa argumenty i jeden wynik. Na początku trzy pseudooperacje dwuargumentowe.
; $1923 : 6435
TBL_ADDRS: .WORD JUMP_TRUE ; $00 Adres: $1C2F - skocz-true
.WORD EXCHANGE ; $01 Adres: $1A72 - wymień
.WORD DELETE ; $02 Adres: $19E3 - usuń
; prawdziwe operacje dwuargumentowe
.WORD SUBTRACT ; $03 Adres: $174C - odejmij
.WORD MULTIPLY ; $04 Adres: $176C - mnóż
.WORD DIVISION ; $05 Adres: $1882 - dziel
.WORD TO_POWER ; $06 Adres: $1DE2 - potęguj
.WORD OR_LOGIC ; $07 Adres: $1AED - logiczne-lub
.WORD NO_AND_NO ; $08 Adres: $1B03 - no-&-no
.WORD NO_L_EQL ; $09 Adres: $1B03 - no-l-eql
.WORD NO_L_EQL ; $0A Adres: $1B03 - no-gr-eql
.WORD NO_L_EQL ; $0B Adres: $1B03 - nos-neql
.WORD NO_L_EQL ; $0C Adres: $1B03 - no-grtr
.WORD NO_L_EQL ; $0D Adres: $1B03 - no-less
.WORD NO_L_EQL ; $0E Adres: $1B03 - nos-eql
.WORD ADDITION ; $0F Adres: $1755 - dodaj
.WORD STR_AND_NO ; $10 Adres: $1AF8 - str-&-no
.WORD NO_L_EQL ; $11 Adres: $1B03 - str-l-eql
.WORD NO_L_EQL ; $12 Adres: $1B03 - str-gr-eql
.WORD NO_L_EQL ; $13 Adres: $1B03 - strs-neql
.WORD NO_L_EQL ; $14 Adres: $1B03 - str-grtr
.WORD NO_L_EQL ; $15 Adres: $1B03 - str-less
.WORD NO_L_EQL ; $16 Adres: $1B03 - strs-eql
.WORD STRS_ADD ; $17 Adres: $1B62 - strs-add
; operacje jednoargumentowe
.WORD NEGATE ; $18 Adres: $1AA0 - neguj
.WORD CODE ; $19 Adres: $1C06 - code
.WORD VAL ; $1A Adres: $1BA4 - val
.WORD LEN ; $1B Adres: $1C11 - len
.WORD SIN ; $1C Adres: $1D49 - sin
.WORD COS ; $1D Adres: $1D3E - cos
.WORD TAN ; $1E Adres: $1D6E - tan
.WORD ASN ; $1F Adres: $1DC4 - asn
.WORD ACS ; $20 Adres: $1DD4 - acs
.WORD ATN ; $21 Adres: $1D76 - atn
.WORD LN ; $22 Adres: $1CA9 - ln
.WORD EXP ; $23 Adres: $1C5B - exp
.WORD INT ; $24 Adres: $1C46 - int
.WORD SQR ; $25 Adres: $1DDB - sqr
.WORD SGN ; $26 Adres: $1AAF - sgn
.WORD ABS ; $27 Adres: $1AAA - abs
.WORD PEEK ; $28 Adres: $1A1B - peek
.WORD USR_NO ; $29 Adres: $1AC5 - usr-no
.WORD STR ; $2A Adres: $1BD5 - str$
.WORD CHRS ; $2B Adres: $1B8F - chrs
.WORD NOT_LOGIC ; $2C Adres: $1AD5 - logiczne-nie
; koniec operacji jednoargumentowych
.WORD MOVE_FP ; $2D Adres: $19F6 - duplikuj
.WORD N_MOD_M ; $2E Adres: $1C37 - n-modulo-m
.WORD JUMP ; $2F Adres: $1C23 - skocz
.WORD STK_DATA ; $30 Adres: $19FC - dane-na-stos
.WORD DEC_JR_NZ ; $31 Adres: $1C17 - dec-jr-nz
.WORD LESS_0 ; $32 Adres: $1ADB - mniej-niż-0
.WORD GREATER_0 ; $33 Adres: $1ACE - więcej-niż-0
.WORD END_CALC ; $34 Adres: $002B - koniec-liczenia
.WORD GET_ARGT ; $35 Adres: $1D18 - pobierz-argt
.WORD TRUNCATE ; $36 Adres: $18E4 - obetnij
.WORD FP_CALC_2 ; $37 Adres: $19E4 - fp-calc-2
.WORD E_TO_FP ; $38 Adres: $155A - e-do-fp
; kolejne adresy są po prostu następnymi dostępnymi slotami dla złożonych literałów o zakresie od $80 do $FF.
.WORD SERIES_XX ; $39 Adres: $1A7F - series-xx $80 - $9F.
.WORD STK_CONST_XX ; $3A Adres: $1A51 - stk-const-xx $A0 - $BF.
.WORD ST_MEM_XX ; $3B Adres: $1A63 - st-mem-xx $C0 - $DF.
.WORD GET_MEM_XX ; $3C Adres: $1A45 - get-mem-xx $E0 - $FF.
; Na boku: 3D - 7F są stąd nieużywanymi literałami kalkulatora.
; 39 - 7B byłyby dostępne dla rozszerzeń.
; -----------------------------
; KALKULATOR ZMIENNOPRZECINKOWY
; -----------------------------
; $199D : 6557
CALCULATE: CALL STK_PNTRS ; procedura ta jest wywoływana w celu ustawienia wskaźników kalkulatora dla standardowej operacji jednoargumentowej.
; HL wskazuje ostatnią wartość na stosie, DE wskazuje pierwszą pozycję po stosie
; w tym punkcie procedura kalkulatora jest wywoływana przez generator ciągów funkcyjnych ...
; $19A0 : 6560
GEN_ENT_1: LD A,B ; przechowaj zawartość rejestru B mikroprocesora Z80
LD (BREG),A ; w zmiennej systemowej BREG, będzie on służył jako licznik dla rozkazu djnz lub za instrukcję dla kalkulatora
; ... a później od tego miejsca
; $19A4 : 6564
GEN_ENT_2: EXX ; przełącz rejestry
EX (SP),HL ; i zachowaj adres następnej instrukcji, adres powrotny w H'L'.
; Jeśli jest to wywołanie rekurencyjne, to H'L' z poprzedniego wywołania idzie na stos c.f. koniec obliczeń.
EXX ; przełącz z powrotem na główny zestaw rejestrów.
; to jest punkt wejścia pętli przy obsłudze literałów łańcuchowych.
; $19A7 : 6567
RE_ENTRY: LD (STKEND),DE ; zachowaj koniec stosu w zmiennej systemowej STKEND
EXX ; przełącz na zapasowe rejestry
LD A,(HL) ; pobierz kolejny literał
INC HL ; zwiększ wskaźnik w HL'
; przy pojedynczej operacji wykonywany jest skok wstecz do tego miejsca
; $19AE : 6574
SCAN_ENT: PUSH HL ; zachowaj wskaźnik na stosie
AND A ; teraz sprawdź, czy literał
JP P,FIRST_3D ; jeśli w zakresie $00 - $3D, skocz naprzód do FIRST-3D wszystko z ustawionym bitem 7 będzie jednym ze złożonych literałów.
; Literały złożone posiadają następujący format.
; bit 7 ustawiony - oznacza literał złożony.
; bity 6-5 są podgrupą 0-3
; bity 4-0 są osadzonym parametrem $00 - $1F.
; Podgrupa 0-3 musi być obsłużona w celu utworzenia dostępnych dalej czterech miejsc adresowych po prostych literałach w tablicy adresów.
LD D,A ; zachowaj literał w rejestrze D
AND $60 ; dokonaj koniunkcji z 01100000, aby wyizolować podgrupę
RRCA ; obróć bity
RRCA ; 4 pozycje w prawo
RRCA ; nie pięć, ponieważ potrzebne nam jest przesunięcie * 2
RRCA ; 00000xx0
ADD A,$72 ; dodaj ($39 * 2), aby otrzymać poprawne przesunięcie.
LD L,A ; umieść w L późniejsze indeksowanie.
LD A,D ; odtwórz literał złożony
AND $1F ; użyj maski do wyizolowania bitów parametru
JR ENT_TABLE ; naprzód do ENT-TABLE
; tutaj następuje skok przy literałach prostych.
; $19C2 : 6594
FIRST_3D: CP $18 ; porównaj z pierwszymi operacjami jednoargumentowymi.
JR NC,DOUBLE_A ; przy operacjach dwuargumentowych skocz do DOUBLE-A
; operacja jednoargumentowa, więc ustaw wskaźniki.
EXX
LD BC,$FFFB ; wartość -5
LD D,H ; przenieś HL, ostatnią wartość, do DE.
LD E,L
ADD HL,BC ; odejmij 5, aby HL wskazywało na drugą wartość.
EXX
; $19CE : 6606
DOUBLE_A: RLCA ; podwój literał
LD L,A ; i umieść w L do indeksowania
; $19D0 : 6608
ENT_TABLE: LD DE,TBL_ADDRS ; Adres: tbl-addrs
LD H,$00 ; przygotuj indeks
ADD HL,DE ; dodaj, aby otrzymać adres procedury
LD E,(HL) ; młodszy bajt do E
INC HL
LD D,(HL) ; starszy bajt do D
LD HL,RE_ENTRY ; Adres: RE-ENTRY
EX (SP),HL ; idzie na stos maszynowy adres następnego literału idzie do HL.
PUSH DE ; teraz na stos trafia adres procedury.
EXX ; przełącz na główne rejestry unikaj używania rejestru IY.
LD BC,($401D) ; STKEND_hi do C nic nie trafia, ale do B idzie BREG i przejdź do następnej instrukcji RET,
; która posiada podwójną tożsamość.
; ------------------
; PROCEDURA 'USUŃ'
; ------------------
; offset $02: 'delete'
; Prosty powrót, lecz przy użyciu jako literał kalkulatora usuwa on ostatnią wartość ze stosu. Na wejściu, jak zwykle z operacjami dwuargumentowymi,
; HL=pierwsza liczba, DE=druga liczba
; Na wyjściu, HL=wynik, DE=STKEND.
; Więc nic do roboty
; $19E3 : 6627
DELETE: RET ; powróć - skocz pośrednio, jeśli kontynuuje się powyższą procedurę.
; --------------------------------
; PROCEDURA 'POJEDYNCZEJ OPERACJI'
; --------------------------------
; offset $37: 'fp-calc-2'
; Ta pojedyncza operacja jest używana do obliczania wartości większości funkcji matematycznych i łańcuchowych, które znajdują się w wyrażeniach
; języka BASIC.
; $19E4 : 6628
FP_CALC_2: POP AF ; porzuć adres powrotny.
LD A,(BREG) ; załaduj akumulator ze zmiennej systemowej BREG wartością będzie literał, np. 'tan'
EXX ; przełącz na rejestry zapasowe
JR SCAN_ENT ; wstecz do SCAN-ENT, następnym literałem będzie end-calc
; ----------------------------
; PROCEDURA "TEST NA 5 MIEJSC'
; ----------------------------
; Procedura ta jest wywoływana z MOVE-FP, STK-CONST i STK-STORE w celu sprawdzenia, czy jest wystarczająco miejsca pomiędzy stosem kalkulatora a
; stosem maszynowym na wartość 5-cio bajtową. Wraca z parą BC zawierającą wartość 5, gotową dla ewentualnej instrukcji LDIR.
; $19EB : 6635
TEST_5_SP: PUSH DE ; zachowaj
PUSH HL ; rejestry na stosie maszynowym
LD BC,$0005 ; potrzeba 5 bajtów
CALL TEST_ROOM ; procedura TEST-ROOM testuje wolną pamięć RAM, generując, jeśli jej brak.
POP HL ; inaczej odtwórz
POP DE ; rejestry
RET ; wróć z parą BC ustawioną na 5.
; ---------------------------------------------
; PROCEDURA 'PRZESUŃ LICZBĘ ZMIENNOPRZECINKOWĄ'
; ---------------------------------------------
; offset $2D: 'duplicate'
; Ta prosta procedura jest instrukcją LDIR przesuwającą 5 bajtów, która wykorzystuje testy pamięci. Gdy używa się jej jako literał kalkulatora,
; duplikuje ostatnią wartość na stosie kalkulatora. Jednoargumentowa, więc na wejściu HL wskazuje na ostatnią wartość, DE na STKEND.
; $19F6 : 6646
MOVE_FP: CALL TEST_5_SP ; procedura TEST-5-SP sprawdza wolną pamięć i ustawia BC na 5
LDIR ; kopiuj te pięć bajtów.
RET ; wróć z parą DE adresującą nowe STKEND i HL adresującą nową ostatnią wartość.
; ---------------------------------
; PROCEDURA 'UMIEŚĆ DANE NA STOSIE'
; ---------------------------------
; offset $30: 'stk-data'
; Gdy jakiś podprogram kalkulatora potrzebuje umieścić jakąś wartość na jego stosie, a nie jest ona typową stałą, to zostaje wywołana ta
; procedura ze zmienną liczbą następujących za nią bajtów danych, które przenoszą do tej procedury pożądaną wartość zmiennoprzecinkową
; przedstawioną w sposób najbardziej treściwy
; $19FC : 6652
STK_DATA: LD H,D ; prześlij STKEND
LD L,E ; do HL dla wyniku.
; $19FE : 6654
STK_CONST: CALL TEST_5_SP ; procedura TEST-5-SP sprawdza, czy jest miejsce i ustawia BC na $05.
EXX ; przełącz na zapasowe rejestry
PUSH HL ; zachowaj na stosie wskaźnik do następnego literału
EXX ; przełącz na główne rejestry
EX (SP),HL ; wskaźnik do HL, adres przeznaczenia na stos.
PUSH BC ; zachowaj BC - wartość 5 z testowania miejsca??.
LD A,(HL) ; pobierz bajt za kodem 'stk-data'
AND $C0 ; wyizoluj bity 7 i 6
RLCA ; obróć
RLCA ; na bity 1 i 0 zakres $00 - $03.
LD C,A ; przenieś do C
INC C ; i zwiększ o 1, aby otrzymać liczbę bajtów do odczytania $01 - $04
LD A,(HL) ; ponownie załaduj pierwszy bajt
AND $3F ; wymaskuj, aby otrzymać możliwy wykładnik.
JR NZ,FORM_EXP ; jeśli dało się dołączyć wykładnik, skocz naprzód do FORM-EXP
; inaczej ten bajt podaje jedynie liczbę bitów, a wykładnik jest w następnym bajcie.
INC HL ; adresuj następny bajt
LD A,(HL) ; i pobierz wykładnik ( - $50).
; $1A14 : 6676
FORM_EXP: ADD A,$50 ; teraz dodaj $50, aby utworzyć właściwy wykładnik
LD (DE),A ; i załaduj go do pierwszego bajtu docelowego.
LD A,$05 ; umieść w akumulatorze $05 i
SUB C ; odejmij C, aby otrzymać liczbę końcowych zer plus jeden.
INC HL ; zwiększ adres źródła
INC DE ; zwiększ adres przeznaczenia
LD B,$00 ; przygotuj się do kopiowania
LDIR ; przekopiuj C bajtów
POP BC ; odtwórz licznik 5 w BC ??.
EX (SP),HL ; umieść HL na stosie jako wskaźnik następnego literału, a wartość ze stosu - wskaźnik wyniku - pobierz do HL.
EXX ; przełącz na rejestry zapasowe.
POP HL ; odtwórz ze stosu w H'L' wskaźnik następnego literału
EXX ; przełącz z powrotem na rejestry główne
LD B,A ; licznik zer do B
XOR A ; wyczyść akumulator
; $1A27 : 6695
STK_ZEROS: DEC B ; zmniejsz o 1 licznik w B
RET Z ; wróć przy zerze, DE wskazuje na nowe STKEND, HL wskazuje na nową liczbę.
LD (DE),A ; inaczej umieść zero w miejscu docelowym
INC DE ; zwiększ wskaźnik przeznaczenia
JR STK_ZEROS ; cofnij się w pętli wstecz do STK-ZEROS, aż zadanie zostanie wykonane.
; -------------------------
; PROCEDURA 'POMIJAJ STAŁE'
; -------------------------
; Procedura ta przechodzi przez elementy tablicy stałych o zmiennej długości, umieszczając pośrednie, niechciane stałe na fikcyjnym stosie kalkulatora,
; który znajduje się w pierwszych pięciu bajtach pamięci ROM w ZX81.
; $1A2D : 6701
SKIP_CONS: AND A ; testuj, czy początkowo jest zero.
; $1A2E : 6702
SKIP_NEXT: RET Z ; Jeśli jest zero, powróć.
PUSH AF ; zachowaj licznik.
PUSH DE ; i normalny adres STKEND
LD DE,$0000 ; fikcyjna wartość dla STKEND na początku ROM. Uwaga, nie jest to błąd, ale ten adres trzeba przenieść gdzieś indziej,
; jeśli program działa w RAM.
CALL STK_CONST ; procedura STK-CONST przechodzi przez rekordy o zmiennej długości.
POP DE ; odtwórz prawdziwy STKEND
POP AF ; odtwórz licznik
DEC A ; zmniejsz go o 1
JR SKIP_NEXT ; skocz wstecz w pętli do SKIP-NEXT
; -----------------------------
; PROCEDURA 'POZYCJA W PAMIĘCI'
; -----------------------------
; Procedura ta po otrzymaniu adresu bazowego w HL i indeksu w A oblicza adres A-tego elementu, gdzie każdy z tych elementów zajmuje w pamięci
; pięć bajtów. Używana jest do adresowania liczb zmiennoprzecinkowych w obszarze pamięci kalkulatora.
; $1A3C : 6716
LOC_MEM: LD C,A ; zachowaj oryginalny numer $00-$1F.
RLCA ; A x 2
RLCA ; A x 4
ADD A,C ; dodaj oryginalny numer, otrzymując pomnożenie przez pięć.
LD C,A ; umieść wynik w C.
LD B,$00 ; ustaw B na 0.
ADD HL,BC ; dodaj w celu otrzymania w HL adresu startu elementu.
RET
; -------------------------------------
; PROCEDURA 'POBIERZ Z OBSZARU PAMIĘCI'
; -------------------------------------
; offsets $E0 to $FF: 'get-mem-0', 'get-mem-1' itd.
; A zawiera przesunięcie $00-$1F. Stos kalkulatora rośnie o 5 bajtów.
; $1A45 : 6725
GET_MEM_XX: PUSH DE ; zachowaj STKEND
LD HL,(MEM) ; MEM jest adresem bazowym komórek pamięci.
CALL LOC_MEM ; ustaw w HL adres pierwszego z 5 bajtów
CALL MOVE_FP ; przenieś 5 bajtów ze sprawdzeniem dostępności pamięci, DE teraz wskazuje na nowy STKEND.
POP HL ; oryginalny STKEND jest teraz wskaźnikiem wyniku.
RET
; -------------------------
; PROCEDURA 'STAŁA NA STOS'
; -------------------------
; offset $A0: 'stk-zero' - 0 na stos
; offset $A1: 'stk-one' - 1 na stos
; offset $A2: 'stk-half' - 1/2 na stos
; offset $A3: 'stk-pi/2' - pi/2 na stos
; offset $A4: 'stk-ten' - 10 na stos
; Procedura ta pozwala jednobajtowej instrukcji umieścić na stosie do 32 stałych przechowywanych w skróconej postaci w tablicy stałych.
; W rzeczywistości potrzebne jest tylko 5 stałych. Na wejściu rejestr A zawiera literał poddany operacji AND z $1F. Nie jest to specjalnie
; efektywne i byłoby lepiej przechowywać te liczby w pełnej, 5 bajtowej postaci i umieszczać je na stosie w podobny sposób do tego, który
; później będzie używany dla tablicy wartości półtonów.
; $1A51 : 6737
STK_CONST_XX: LD H,D ; zachowaj STKEND - potrzebne dla wyniku
LD L,E
EXX ; przełącz na zapasowe rejestry
PUSH HL ; zachowaj wskaźnik do kolejnego literału
LD HL,STK_ZERO ; początek tablicy stałych do H'L'
EXX ; przełącz na główne rejestry
CALL SKIP_CONS ; przejdź do pożądanej stałej
CALL STK_CONST ; umieść stałą na stosie
EXX
POP HL ; odtwórz wskaźnik do następnego literału.
EXX
RET
; -------------------------------------
; PROCEDURA 'ZAPISZ W OBSZARZE PAMIĘCI'
; -------------------------------------
; Offsets $C0 to $DF: 'st-mem-0', 'st-mem-1' etc.
; Chociaż mogą być adresowane 32 lokacje pamięci, to dla ROM potrzebne jest tylko sześć od $C0 do $C5 i dla nich przeznaczono trzydzieści
; bajtów (6*5). Programiści ZX81, którzy chcą używać procedur zmiennoprzecinkowych z poziomu asemblera, mogą zmienić zmienną systemową
; MEM, aby wskazywała na obszar 160 bajtów RAM w celu pełnego wykorzystania całego zakresu. A zawiera wyprowadzone przesunięcie $00-$1F.
; Jednoargumentowa, zatem na wejściu HL wskazuje na ostatnią wartość, DE na STKEND.
; $1A63 : 6755
ST_MEM_XX: PUSH HL ; zachowaj wskaźnik wyniku.
EX DE,HL ; przenieś do DE.
LD HL,(MEM) ; pobierz z MEM adres bazowy obszaru pamięci.
CALL LOC_MEM ; procedura LOC-MEM ustawia HL na adres przeznaczenia.
EX DE,HL ; zamień - HL jest początkiem, DE jest przeznaczeniem.
CALL MOVE_FP ; Uwaga: krótkie LD BC,5; LDIR
; test pamięci nie jest potrzebny, zatem te instrukcje byłyby szybsze!
EX DE,HL ; DE = STKEND
POP HL ; odtwórz oryginalny wskaźnik wyniku
RET
; ------------------
; PROCEDURA 'ZAMIEŃ'
; ------------------
; offset $01: 'exchange'
; Procedura ta zamienia dwie ostatnie wartości na stosie kalkulatora. Na wejściu jak zawsze przy operacjach dwuargumentowych,
; HL=pierwsza liczba, DE=druga liczba
; Na wyjściu: HL=wynik, DE=STKEND.
; $1A72 : 6770
EXCHANGE: LD B,$05 ; należy zamienić pięć bajtów
; początek pętli.
; $1A74 : 6772
SWAP_BYTE: LD A,(DE) ; każdy bajt drugiej liczby
LD C,(HL) ; każdy bajt pierwszej liczby
EX DE,HL ; zamień wskaźniki
LD (DE),A ; zapisz każdy bajt pierwszej liczby
LD (HL),C ; zapisz każdy bajt drugiej liczby
INC HL ; przesuń oba wskaźniki
INC DE
DJNZ SWAP_BYTE ; skocz wstecz w pętli do SWAP-BYTE, aż wszystkie 5 bajtów będzie przesłane.
EX DE,HL ; parzysta liczba zamian przywraca stan wskaźników, aby DE adresowało STKEND.
RET
; -----------------------------
; PROCEDURA 'GENERATORA CIĄGÓW'
; -----------------------------
; offset $86: 'series-06'
; offset $88: 'series-08'
; offset $8C: 'series-0C'
; ZX81 wykorzystuje wielomiany Czybyszewa do tworzenia przybliżeń dla SIN, ATN, LN i EXP. Ich nazwa pochodzi od nazwiska rosyjskiego matematyka,
; Pafunty Czybyszewa, urodzonego w 1821, który dokonał wielu prac pionierskich w dziedzinie ciągów liczbowych. w przypadku kalkulatorów
; wielomiany Czybyszewa posiadają taką zaletę nad innymi ciągami, przykładowo nad ciągami Taylora, iż szybciej osiągają przybliżenia funkcji:
; SIN w 6 iteracjach, EXP w 8, a LN i ATN w 12. Sposób pracy tej procedury jest interesujący, lecz pełne jej wyjaśnienie wraz z demonstracją
; w BASIC'u ZX81 znajduje się "The Complete Spectrum ROM Disassembly" autorstwa dr Iana Logana i dr Franka O'Hary, opublikowanym w 1983 przez
; Melbourne House.
; $1A7F : 6783
SERIES_XX: LD B,A ; parametr $00 - $1F do licznika B
CALL GEN_ENT_1 ; Rekurencyjne wywołanie specjalnego punktu wejścia w kalkulatorze, który umieszcza rejestr B w zmiennej systemowej BREG.
; Adres powrotny jest następną pozycją, gdzie kalkulator oczekuje swojej pierwszej instrukcji - obecnie wskazywanej
; przez H'L'. Poprzedni wskaźnik ciągów pięciobajtowych liczb zostaje umieszczony na stosie maszynowym.
; Faza wstępna.
.BYTE $2D ; duplikuj x,x.
.BYTE $0F ; dodaj x+x.
.BYTE $C0 ; stos-do-pamięci-0 2x.
.BYTE $02 ; usuń .
.BYTE $A0 ; na-stos-zero 0.
.BYTE $C2 ; stos-do-pamięci-2 0.
; teraz następuje wejście do pętli w celu wykonania obliczeń algebraicznych dla każdej z liczb w ciągu
; $1A89 : 6793
G_LOOP: .BYTE $2D ; duplikuj v,v.
.BYTE $E0 ; na-stos-paimięć-0 v,v,2x
.BYTE $04 ; mnóż v,v*2x
.BYTE $E2 ; na-stos-paimięć-2 v,v*2x,v'
.BYTE $C1 ; stos-do-pamięci-1
.BYTE $03 ; odejmij v,v*2x-v'
.BYTE $34 ; koniec-obliczeń
; na stosie kalkulatora umieszczona zostaje kolejna stała
CALL STK_DATA ; procedura STK-DATA jest wywoływana, aby bezpośrednio wstawić tą stałą na stos i zwiększyć wskaźnik H'L'.
CALL GEN_ENT_2 ; procedura GEN-ENT-2 rekurencyjnie wchodzi do kalkulatora bez zmian w zmiennej systemowej BREG
; zawartość H'L' idzie na stos maszynowy i zostaje załadowana jak zwykle następnym adresem.
.BYTE $0F ; dodaj v,2*v*x-v'+v''
.BYTE $01 ; zamień 2*v*x-v'+v'',v
.BYTE $C2 ; stos-do-pamięci-2
.BYTE $02 ; usuń 2*v*x-v'+v''
.BYTE $31 ; zmniejsz-skocz-jeśli-nie-zero
.BYTE $EE ; wstecz do $1A89, G-LOOP
; gdy wyliczana pętla zostanie zakończona, ostatnie odejmowanie da nam wynik, np. SIN X.
.BYTE $E1 ; na-stos-paimięć-1
.BYTE $03 ; odejmij
.BYTE $34 ; koniec-obliczeń
RET ; wróć z H'L' wskazującym na pozycję za ostatnią liczbą w ciągu.
; --------------------------------------
; Obsługa jednoargumentowego minusa ($18)
; --------------------------------------
; Procedura zmienia znak liczby na szczycie stosu kalkulatora. Operacja jednoargumentowa, zatem na wejściu HL wskazuje ostatnią wartość,
; DE wskazuje STKEND.
; $1AA0 : 6816
NEGATE: LD A,(HL) ; pobierz wykładnik ostatniej wartości na stosie kalkulatora.
AND A ; testuj go.
RET Z ; wróć, jeśli zero - liczba jest zerem.
INC HL ; adres bajtu z bitem znaku.
LD A,(HL) ; pobierz go do akumulatora.
XOR $80 ; zmień bit znaku na przeciwny.
LD (HL),A ; włóż z powrotem pobrany bajt.
DEC HL ; ponownie wskaż ostatnią wartość.
RET
; -----------------------
; Wartość bezwzględna ($27)
; -----------------------
; Ta procedura oblicza wartość bezwzględną liczby na szczycie stosu kalkulatora.
; $1AAA : 6826
ABS: INC HL ; wskaż bajt z bitem znaku.
RES 7,(HL) ; ustaw znak na plus.
DEC HL ; wskaż z powrotem ostatnią wartość.
RET
; -----------
; Signum ($26)
; -----------
; Ta procedura zastępuje ostatnią wartość na stosie kalkulatora jedną z liczb:
; -1, jeśli liczba jest ujemna
; 1, jeśli jest dodatnia
; 0, jeśli jest równa 0 - wtedy liczba zostaje pozostawiona bez zmian
; $1AAF : 6831
SGN: INC HL ; wskaż na pierwszy bajt 4-bajtowej mantysy.
LD A,(HL) ; pobierz bajt z bitem znaku.
DEC HL ; wskaż na wykładnik.
DEC (HL) ; testuj wykładnik na wartość zero
INC (HL)
SCF ; Ustaw znacznik przeniesienia.
CALL NZ,FP_0_1 ; procedura FP-0/1 zastępuje ostatnią wartość liczbą 1, jeśli wykładnik wskazuje, iż wartość jest różna od zera.
; W każdym przypadku mantysa składa się teraz z czterech zer.
INC HL ; wskaż pierwszy bajt mantysy.
RLCA ; wstaw oryginalny bit znaku do przeniesienia.
RR (HL) ; wstaw przeniesienie do bitu znaku wyniku.
DEC HL ; wskaż na ostatnią wartość.
RET
; -------------------------
; obsługa funkcji PEEK ($28)
; -------------------------
; Ta funkcja zwraca zawartość komórki pamięci o podanym adresie. Można pobrać komórkę z całego obszaru adresowego, łącznie z ROM.
; $1ABE : 6846
PEEK: CALL FIND_INT ; procedura FIND-INT umieszcza adres w BC.
LD A,(BC) ; pobierz zawartość do rejestru A.
; $1AC2 : 6850
IN_PK_STK: JP STACK_A ; wyjdź poprzez STACK-A, aby umieścić A na stosie kalkulatora.
; ---------------
; USR liczba ($29)
; ---------------
; Funkcja USR, za którą następuje liczba od 0 do 65535, pozwala wywołać komputerowi ZX81 program w kodzie maszynowym. Funkcja ta zwraca jako swoją
; wartość parę rejestrów BC.
; Uwaga: procedura STACK-BC odtwarza stan rejestru IY na $4000, jeśli program użytkownika go zmienił.
; $1AC5 : 6853
USR_NO: CALL FIND_INT ; procedura FIND-INT umieszcza dostarczony adres w BC.
LD HL,STACK_BC ; na stosie maszynowym zostaje umieszczony adres STACK-BC
PUSH HL
PUSH BC ; a następnie adres procedury w kodzie maszynowym.
RET ; wykonany zostanie skok pośredni do tej procedury a przy powrocie z niej również do STACK-BC.
; -----------------------
; Większe od zera ($33)
; -----------------------
; Test, czy ostatnia wartość na stosie kalkulatora jest większa od zera. Ta procedura jest również wywoływana z końcowych testów procedury porównania.
; $1ACE : 6862
GREATER_0: LD A,(HL) ; pobierz wykładnik.
AND A ; test na zero.
RET Z ; jeśli tak, wróć.
LD A,$FF ; przygotuj maskę XOR na bit znaku
JR SIGN_TO_C ; naprzód do SIGN-TO-C, aby wstawić znak do przeniesienia (przeniesienie zostanie ustawione, jeśli znak jest dodatni)
; a następnie zapisz tę lokację 1 lub 0 zgodnie z wynikiem
; ---------------------------
; Obsługa operatora NOT ($2C)
; ---------------------------
; Powoduje zapisanie ostatniej wartości 1, jeśli była równa 0, lub 0 dla każdej innej wartości.
; tj. NOT 0 zwraca 1, NOT 1 zwraca 0, NOT -3 zwraca 0.
; Ta procedura jest również bezpośrednio wywoływana z końcowych testów operatora porównania.
; $1AD5 : 6869
NOT_LOGIC: LD A,(HL) ; pobierz bajt wykładnika.
NEG ; zaneguj - ustawia przeniesienie, jeśli był różny od zera.
CCF ; zaneguj przeniesienie, aby było ustawione, gdy liczba była zerem, inaczej wyzerowane
JR FP_0_1 ; naprzód do FP-0/1.
; ----------------------
; Mniejsze od zera ($32)
; ----------------------
; Niszcząco sprawdź, czy ostatnia wartość na stosie kalkulatora jest mniejsza od 0. Jeśli tak, bit nr 7 drugiego bajtu będzie ustawiony na 1.
; $1ADB : 6875
LESS_0: XOR A ; zeruj maskę XOR (przeniesienie zostanie ustawione, jeśli znak był ujemny).
; przenieś znak mantysy do znacznika przeniesienia.
; $1ADC : 6876
SIGN_TO_C: INC HL ; zaadresuj drugi bajt.
XOR (HL) ; jeśli liczba jest ujemna, bit 7 rejestru A będzie ustawiony na 1.
DEC HL ; adresuj ponownie pierwszy bajt.
RLCA ; obróć bit 7 rejestru A do przeniesienia.
; --------------
; Zero lub jeden
; --------------
; Ta procedura umieszcza liczbę całkowitą o wartości 0 lub 1 na adresowanej lokacji stosu kalkulatora lub obszaru MEM. Wartość 1 jest wpisywana,
; jeśli ustawione jest przeniesienie, inaczej wpisywane jest zero.
; $1AE0 : 6880
FP_0_1: PUSH HL ; zachowaj wskaźnik pierwszego bajtu
LD B,$05 ; pięć bajów do roboty.
; $1AE3 : 6883
FP_LOOP: LD (HL),$00 ; w pętli wstaw 5 zer.
INC HL ; adres kolejnego bajtu
DJNZ FP_LOOP ; powtarzaj
POP HL ; odzyskaj adres pierwszego bajtu
RET NC ; jeśli brak przeniesienia, zero jest gotowe
LD (HL),$81 ; inaczej wpisz 1
RET
; --------------------------
; Obsługa operatora OR ($07)
; --------------------------
; Operator logiczny OR, np. X OR Y
; Wynikiem jest zero, jeśli obie wartości są zerami, inaczej wynik jest niezerowy.
; np. 0 OR 0 zwraca 0.
; -3 OR 0 zwraca -3.
; 0 OR -3 zwraca 1.
; -3 OR 2 zwraca 1.
; Operacja dwuargumentowa.
; Na wejściu HL wskazuje na pierwszy argument (X) a DE na drugi (Y).
; $1AED : 6893
OR_LOGIC: LD A,(DE) ; pobierz wykładnik drugiej liczby
AND A ; testuj go.
RET Z ; jeśli zero, wróć.
SCF ; ustaw znacznik przeniesienia
JR FP_0_1 ; wstecz do FP-0/1 w celu nadpisania pierwszego argumentu wartością 1
; -----------------------------------------
; Obsługa operatora liczba AND liczba ($08)
; -----------------------------------------
; Operator logiczny AND. Zwraca zero, jeśli jeden z argumentów jest zerem.
; inaczej zwraca pierwszy argument.
; np. -3 AND 2 zwraca -3.
; -3 AND 0 zwraca 0.
; 0 AND -2 zwraca 0.
; 0 AND 0 zwraca 0.
; Porównaj z procedurą OR powyżej
; $1AF3 : 6899
NO_AND_NO: LD A,(DE) ; pobierz wykładnik drugiej liczby
AND A ; testuj go.
RET NZ ; wróć, jeśli różne od zera
JR FP_0_1 ; wstecz do FP-0/1 w celu nadpisania pierwszego argumentu zerem.
; ------------------------------------------
; Obsługa operatora łańcuch AND liczba ($10)
; ------------------------------------------
; np. "WYGRYWASZ" AND WYNIK>99 zwróci ten łańcuch, jeśli warunek będzie prawdziwy lub pusty łańcuch, jeśli warunek będzie fałszywy.
; $1AF8 : 6904
STR_AND_NO: LD A,(DE) ; pobierz wykładnik drugiej liczby.
AND A ; testuj go.
RET NZ ; wróć, jeśli liczba była różna od zero - wynikiem będzie łańcuch.
; jeśli liczba była równa zero (fałsz), to musi być zwrócony pusty łańcuch przez zmianę długości łańcucha na stosie kalkulatora na zero.
PUSH DE ; zachowaj wskaźnik do już zbędnej liczby (który stanie się nowym STKEND)
DEC DE ; wskaż piąty bajt deskryptora łańcucha.
XOR A ; wyczyść akumulator.
LD (DE),A ; umieść zero w starszym bajcie długości.
DEC DE ; zaadresuj młodszy bajt długości.
LD (DE),A ; umieść tam zero - teraz łańcuch będzie pusty.
POP DE ; odtwórz wskaźnik - nowe STKEND.
RET
; -------------------------------------
; Wykonaj porównanie ($09-$0E, $11-$16)
; -------------------------------------
; Operacje dwuargumentowe.
; Ta procedura używana jest do wykonania dwunastu możliwych operacji porównań. Na wejściu rejestr B zawiera literał operacji porównania.
; $1B03 : 6915
NO_L_EQL: LD A,B ; przenieś literał do akumulatora
SUB $08 ; teraz zakres jest od 01-06 lub 09-0E
BIT 2,A ; wyodrębnij ">', '<', '='.
JR NZ,EX_OR_NOT ; z tymi operatorami przeskocz do EX-OR-NOT.
DEC A ; inaczej zmień na $00-$02, $08-$0A, 0C-0E, aby zgadzały się bity 0-2.
; $1B0B : 6923
EX_OR_NOT: RRCA ; pierwsze RRCA ustawia przeniesienie dla wymiany.
JR NC,NU_OR_STR ; naprzód do NU-OR-STR w ośmiu pozostałych przypadkach
; dla 4 innych przypadków dwie wartości na stosie kalkulatora są zamieniane miejscami.
PUSH AF ; zachowaj A i przeniesienie.
PUSH HL ; zachowaj HL - wskaźnik do pierwszego argumentu. (DE wskazuje na drugi argument).
CALL EXCHANGE ; procedura zamienia te dwie wartości. (HL = drugi argument, DE = STKEND)
POP DE ; DE = pierwszy argument
EX DE,HL ; jak było wcześniej.
POP AF ; odtwórz A i przeniesienie.
; Uwaga, byłoby lepiej, jeśli drugie RRCA poprzedzałoby test łańcuchowy. Zachowałoby to dwa zdublowane bajty i jeślibyśmy również pozbyli się
; tego SUB 8 na początku, nie musielibyśmy zamieniać miejscami testowanych bitów.
; $1B16 : 6934
NU_OR_STR: BIT 2,A ; test na porównanie łańcuchowe.
JR NZ,STRINGS ; jeśli tak, naprzód do STRINGS.
; kontynuuj z porównaniami liczbowymi.
RRCA ; drugie RRCA powoduje ustawienie przeniesienia przez eql/neql.
PUSH AF ; zachowaj A i przeniesienie.
CALL SUBTRACT ; procedura odejmowania pozostawia wynik na stosie.
JR END_TESTS ; naprzód do END-TESTS
; $1B21 : 6945
STRINGS: RRCA ; drugie RRCA powoduje ustawienie przeniesienia przez eql/neql.
PUSH AF ; zachowaj A i przeniesienie.
CALL STK_FETCH ; procedura STK-FETCH pobiera parametry drugiego łańcucha
PUSH DE ; zachowaj początek drugiego łańcucha.
PUSH BC ; oraz jego długość.
CALL STK_FETCH ; procedura STK-FETCH pobiera parametry pierwszego łańcucha początek w DE, długość w BC.
POP HL ; odtwórz długość drugiego łańcucha w HL.
; Teraz następuje wejście do pętli w celu porównania przez odejmowanie kolejnych znaków w obu łańcuchach. Jeśli znaki są zgodne, wskaźniki
; zostają zwiększone o 1, a długość zmniejsza się o 1 i wykonywany jest skok względny do tego miejsca. Jeśli oba łańcuchy zostaną przeglądnięte
; i będą one zgodne, to są równe.
; $1B2C : 6956
BYTE_COMP: LD A,H ; testuj, czy drugi łańcuch jest pusty
OR L
EX (SP),HL ; umieść długość 2 na stosie, a początek 2 w HL.
LD A,B ; starszy bajt długości do A
JR NZ,SEC_PLUS ; jeśli drugi łańcuch nie jest pusty, naprzód do SEC-PLUS.
OR C ; testuj długość pierwszego łańcucha.
; $1B33 : 6963
SECND_LOW: POP BC ; pobierz długość drugiego ze stosu.
JR Z,BOTH_NULL ; naprzód do BOTH-NULL, jeśli pierwszy łańcuch jest również pusty.
; warunek prawdy - pierwszy łańcuch jest dłuższy od drugiego (SECND-LESS)
POP AF ; odtwórz przeniesienie (ustawione, gdy eql/neql)
CCF ; Zaneguj znacznik przeniesienia.
; Uwaga. Równość staje się fałszywa. Nierówność jest prawdziwa. Poprzez zamianę lub zastosowanie końcowego NOT,
; wszystkie porównania zostały przetworzone, jest to zatem ścieżka sukcesu.
JR STR_TEST ; wyjdź poprzez STR-TEST
; Skok następuje tutaj przy zgodności
; $1B3A : 6970
BOTH_NULL: POP AF ; odtwórz przeniesienie - ustawione dla eql/neql
JR STR_TEST ; naprzód do STR-TEST
; skok tutaj jest wtedy, gdy drugi łańcuch nie jest pusty, a młodszy bajt pierwszego musi zostać przetestowany.
; $1B3D : 6973
SEC_PLUS: OR C ; sprawdź długość pierwszego łańcucha.
JR Z,FRST_LESS ; jeśli długość wynosi zero, skocz naprzód do FRST-LESS.
; w obu łańcuchach pozostało przynajmniej po jednym znaku.
LD A,(DE) ; pobierz znak z pierwszego łańcucha.
SUB (HL) ; odejmij od niego znak z drugiego łańcucha.
JR C,FRST_LESS ; jeśli ustawione przeniesienie, naprzód do FRST-LESS
JR NZ,SECND_LOW ; wstecz do SECND-LOW a następnie do STR-TEST, jeśli brak zgodności
DEC BC ; zmniejsz długość pierwszego łańcucha.
INC DE ; zwiększ wskaźnik pierwszego łańcucha.
INC HL ; zwiększ wskaźnik drugiego łańcucha.
EX (SP),HL ; zamień go z długością na stosie
DEC HL ; zmniejsz długość drugiego łańcucha
JR BYTE_COMP ; wstecz do BYTE-COMP
; fałszywy warunek.
; $1B4D : 6989
FRST_LESS: POP BC ; usuń długość
POP AF ; pobierz A
AND A ; wyzeruj przeniesienie dla wyniku false.
; zgodność oraz x$>y$ spotykają się tutaj
; $1B50 : 6992
STR_TEST: PUSH AF ; dla testów łańcuchowych na stosie kalkulatora umieszczane jest zero
RST 28H ; KALKULATOR
.BYTE $A0 ; zero-na-stos
.BYTE $34 ; koniec-obliczeń
; ścieżka numeryczna i łańcuchowa schodzą się tutaj.
; $1B54 : 6996
END_TESTS: POP AF ; te trzy testy, wywoływane wg konieczności
PUSH AF ; dają poprawny wynik dla wszystkich dwunastu porównań. Wstępne przeniesienie jest ustawiane dla '<>' i '=',
CALL C,NOT_LOGIC ; a końcowe przeniesienie jest ustawiane dla '>', '<' i '='.
CALL GREATER_0
POP AF ; odtwórz literał
RRCA ; trzecie RRCA - test na '<=', ">=' lub '<>'.
CALL NC,NOT_LOGIC ; jeśli tak, zastosuj końcowe NOT.
RET
; -----------------------
; Łączenie łańcuchów($17)
; -----------------------
; Ten literał łączy dwa łańcuchy w jeden, np. LET A$ = B$ + C$. Parametry dwóch łańcuchów do złączenia są na stosie.
; $1B62 : 7010
STRS_ADD: CALL STK_FETCH ; procedura STK-FETCH pobiera parametry pierwszego łańcucha i usuwa je ze stosu kalkulatora.
PUSH DE ; zachowaj adres startu.
PUSH BC ; i długość.
CALL STK_FETCH ; procedura STK-FETCH dla drugiego łańcucha
POP HL ; ponownie pobierz pierwszą długość
PUSH HL ; i zachowaj ponownie na stosie
PUSH DE ; zachowaj początek drugiego łańcucha
PUSH BC ; i jego długość.
ADD HL,BC ; dodaj obie długości.
LD B,H ; przenieś do BC
LD C,L ; i stwórz
RST 30H ; BC-miejsc w obszarze roboczym. DE wskazuje na start tego obszaru.
CALL STK_STO ; procedura STK-STO-$ umieszcza parametry nowego łańcucha na stosie, uaktualniając STKEND.
POP BC ; długość pierwszego
POP HL ; adres początku
LD A,B ; test na zerową długość
OR C
JR Z,OTHER_STR ; jeśli zerowa długość, idź do OTHER-STR
LDIR ; skopiuj łańcuch do przestrzeni roboczej.
; $1B7D : 7037
OTHER_STR: POP BC ; teraz druga długość
POP HL ; i początek łańcucha
LD A,B ; test na zerową długość
OR C
JR Z,STK_PNTRS ; przeskocz naprzód do STK-PNTRS, jeśli długość zerowa.
LDIR ; inaczej kopiuj bajty i kontynuuj w następnej procedurze, która ustawia wskaźniki stosu kalkulatora.
; ---------------------------
; Wskaźniki stosu kalkulatora
; ---------------------------
; Rejestr DE jest ustawiony na STKEND a HL, wskaźnik wyniku, jest ustawiony na 5 komórek poniżej. Procedura jest używana, gdy zapis tych
; wartości jest niewygodny w czasie używania stosu kalkulatora podczas innych operacji na stosie maszynowym. Procedura jest również wykorzystywana
; do zakończenia procedury VAL z tego samego powodu i do inicjowania stosu kalkulatora na początku procedury CALCULATE.
; $1B85 : 7045
STK_PNTRS: LD HL,(STKEND) ; pobierz wartość STKEND ze zmiennej systemowej.
LD DE,$FFFB ; wartość -5
PUSH HL ; zachowaj wartość STKEND.
ADD HL,DE ; odejmij 5 od HL.
POP DE ; pobierz STKEND do DE.
RET
; -----------------
; Obsługa CHR$ (2B)
; -----------------
; Funkcja zwraca jednoznakowy łańcuch, który jest wynikiem zamiany liczby z zakresu 0-255 na łańcuch, np. CHR$ 38 = "A".
; Uwaga. ZX81 nie posiada zestawu znaków ASCII.
; $1B8F : 7055
CHRS: CALL FP_TO_A ; procedura FP-TO-A umieszcza tę liczbę w A.
JR C,REPORT_BD ; jeśli nadmiar, naprzód do REPORT-BD
JR NZ,REPORT_BD ; jeśli ujemna, naprzód do REPORT-BD
PUSH AF ; zachowaj argument.
LD BC,$0001 ; potrzebne jedno miejsce.
RST 30H ; BC-MIEJSC ustawia DE na początek obszaru
POP AF ; odtwórz liczbę ze stosu.
LD (DE),A ; i umieść ją w obszarze roboczym.
CALL STK_STO ; procedura STK-STO-$ umieszcza na stosie deskryptor.
EX DE,HL ; ustaw HL na wskazywanie wyniku a DE na STKEND.
RET
; $1BA2 : 7074
REPORT_BD: RST 08H ; ERROR-1
.BYTE $0A ; Raport Błędu: Liczba całkowita poza zakresem
; -----------------
; Obsługa VAL ($1A)
; -----------------
; VAL traktuje znaki w łańcuchu jak wyrażenie liczbowe.
; np. VAL "2.3" = 2.3; VAL "2+4" = 6, VAL ("2" + "4") = 24.
; $1BA4 : 7076
VAL: LD HL,(CH_ADD) ; pobierz wartość zmiennej systemowej CH_ADD
PUSH HL ; i zapisz ją na stosie maszynowym.
CALL STK_FETCH ; procedura STK-FETCH pobiera argument łańcuchowy ze stosu kalkulatora.
PUSH DE ; zachowaj adres początku łańcucha.
INC BC ; zwiększ długość na znak nowego wiersza.
RST 30H ; BC-MIEJSC tworzy miejsce w obszarze roboczym.
POP HL ; odtwórz początek łańcucha w HL.
LD (CH_ADD),DE ; załaduj CH_ADD początkiem DE w obszarze roboczym.
PUSH DE ; zachowaj początek w obszarze roboczym
LDIR ; skopiuj łańcuch z programu, zmiennych lub obszaru roboczego do zarezerwowanego miejsca.
EX DE,HL ; do HL koniec łańcucha + 1
DEC HL ; zmniejsz HL, aby wskazywało na koniec nowego obszaru.
LD (HL),$76 ; wstaw na końcu znak końca wiersza - ZX81 nie używa kodu ASCII.
RES 7,(IY+$01) ; uaktualnij FLAGS - sygnalizuj sprawdzanie składni.
CALL CLASS_6 ; procedura CLASS-06 - sprawdza składnię wyrażenia.
CALL CHECK_2 ; procedura CHECK-2 sprawdza osiągnięcie końca wiersza.
POP HL ; odtwórz początek łańcucha w obszarze roboczym.
LD (CH_ADD),HL ; ustaw ponownie CH_ADD na początek łańcucha.
SET 7,(IY+$01) ; uaktualnij FLAGS - sygnalizuj wykonywanie programu.
CALL SCANNING ; procedura SCANNING oblicza w całości wyrażenie w łańcuchu, pozostawiając wynik na stosie kalkulatora.
POP HL ; odtwórz oryginalną wartość CH_ADD.
LD (CH_ADD),HL
JR STK_PNTRS ; wyjdź poprzez STK-PNTRS, co resetuje wskaźniki stosu kalkulatora.
; -----------------
; Obsługa STR$ (2A)
; -----------------
; Ta funkcja zwraca reprezentację łańcuchową argumentu liczbowego. Użyta metoda polega na oszukaniu procedury PRINT-FP, tak aby sądziła
; ona, że zapisuje do ściśniętego bufora wideo, gdy w rzeczywistości zapisuje do obszaru roboczego łańcucha.
; Jeśli na pozycji druku znajduje się znak newline a liczba kolumn nie została zredukowana do zera, to procedura wydruku zakłada, że w systemie
; jest tylko 1KB pamięci RAM i pamięć ekranu, jak i reszta pamięci dynamicznej, rozszerza się według potrzeb za pomocą wywołań do procedury
; ONE-SPACE. Ekran jest odzwierciedlony znakowo, nie bitowo.
; $1BD5 : 7125
STR: LD BC,$0001 ; utwórz początkowy bajt w obszarze roboczym
RST 30H ; wykorzystując procedurę BC-SPACES.
LD (HL),$76 ; umieść tam znak końca wiersza.
LD HL,(S_POSN) ; pobierz wartość S_POSN - kolumna i wiersz
PUSH HL ; zachowaj je na stosie.
LD L,$FF ; ustaw wartość kolumny na 255, co da wymyślony bufor o długości 254 znaków
LD (S_POSN),HL ; i zapisz go w zmiennej systemowej S_POSN.
LD HL,(DF_CC) ; pobierz wartość DF_CC
PUSH HL ; i również zachowaj na stosie.
LD (DF_CC),DE ; teraz wstaw adres początku obszaru roboczego do DF_CC, które zwykle wskazuje adres gdzieś wewnątrz bufora ekranu.
PUSH DE ; zachowaj adres nowego łańcucha.
CALL PRINT_FP ; wydrukuj wartość argumentu w obszarze roboczym.
POP DE ; odtwórz początek łańcucha.
LD HL,(DF_CC) ; pobierz adres końca łańcucha z DF_CC.
AND A ; przygotuj się do odejmowania.
SBC HL,DE ; odejmij, aby otrzymać długość.
LD B,H ; i przenieś ją do BC
LD C,L ; register.
POP HL ; odtwórz pierwotną wartość DF_CC
LD (DF_CC),HL
POP HL ; odtwórz pierwotne wartości S_POSN
LD (S_POSN),HL
CALL STK_STO ; procedura STK-STO-$ umieszcza deskryptor łańcucha na stosie kalkulatora.
EX DE,HL ; HL = ostatnia wartość, DE = STKEND.
RET
; --------------
; FUNKCJA 'CODE'
; --------------
; (offset $19: 'code')
; Zwraca kod znaku lub kod pierwszego znaku w łańcuchu. Np. CODE "AARDVARK" = 38 (nie 65, ponieważ ZX81 nie stosuje kodu ASCII).
; $1C06 : 7174
CODE: CALL STK_FETCH ; procedura STK-FETCH pobiera parametry łańcucha i usuwa je ze stosu kalkulatora.
; DE wskazuje na początek, BC zawiera długość.
LD A,B ; test na długość łańcucha
OR C
JR Z,STK_CODE ; jeśli pusty łańcuch, przeskocz do STK-CODE z zerem.
LD A,(DE) ; inaczej pobierz pierwszy znak.
; $1C0E : 7182
STK_CODE: JP STACK_A ; skocz wstecz do STACK-A (z testem pamięci)
; ----------------
; PODPROGRAM 'LEN'
; ----------------
; (offset $1b: 'len')
; Zwraca długość łańcucha. W BASICu firmy Sinclair łańcuchy mogą posiadać długość przekraczającą 20.000 znaków, więc do przechowania długości
; wymagany jest rejestr szesnastobitowy.
; $1C11 : 7185
LEN: CALL STK_FETCH ; procedura STK-FETCH pobiera parametry łańcucha i usuwa je ze stosu kalkulatora.
; para rejestrów BC zawiera teraz długość łańcucha.
JP STACK_BC ; skocz wstecz do STACK-BC, aby umieścić wynik na stosie kalkulatora (z testem pamięci).
; -----------------------------
; PODPROGRAM 'ZMNIEJSZ LICZNIK'
; -----------------------------
; (offset $31: 'dec-jr-nz')
; Kalkulator posiada instrukcję, która zmniejsza jednobajtowy pseudorejestr i wykonuje wynikłe z tego skoki względne w identyczny sposób,
; jak instrukcja DJNZ mikroprocesora Z80.
; $1C17 : 7191
DEC_JR_NZ: EXX ; przełącz na zestaw adresujący kod
PUSH HL ; zachowaj wskaźnik do bajtu z przesunięciem
LD HL,BREG ; zaadresuj BREG w zmiennych systemowych
DEC (HL) ; zmniejsz go
POP HL ; odtwórz wskaźnik
JR NZ,JUMP_2 ; jeśli nie było zera, skocz do JUMP-2
INC HL ; idź poza bajt z długością skoku.
EXX ; wróć do głównego zestawu rejestrów
RET
; -----------------
; PODPROGRAM 'SKOCZ'
; -----------------
; (Offset $2F; 'jump')
; Procedura umożliwia kalkulatorowi wykonywanie skoków względnych, podobnych do instrukcji JR obecnej w zestawie poleceń mikroprocesora Z80.
; $1C23 : 7203
JUMP: EXX ; przełącz na zestaw wskazujący
; $1C24 : 7204
JUMP_2: LD E,(HL) ; bajt skoku 0-127 naprzód, 128-255 wstecz.
XOR A ; wyczyść akumulator.
BIT 7,E ; sprawdź, czy skok wstecz
JR Z,JUMP_3 ; jeśli dodatni, przeskocz do JUMP-3.
CPL ; inaczej zmień na przeciwny.
; $1C2B : 7211
JUMP_3: LD D,A ; przenieś do starszego bajtu.
ADD HL,DE ; przesuń wskaźnik kalkulatora naprzód lub wstecz.
EXX ; wyjdź z zestawu wskaźnikowego.
RET
; --------------------------------
; PODPROGRAM 'SKOCZ PRZY PRAWDZIE'
; --------------------------------
; (Offset $00; 'jump-true')
; Procedura pozwala kalkulatorowi wykonywać warunkowe skoki względne zależne od wyniku ostatniego testu. W ZX81 wykładnik będzie równy 0 dla zera
; lub $81 dla wyniku jeden.
; $1C2F : 7215
JUMP_TRUE: LD A,(DE) ; pobierz bajt wykładnika
AND A ; wynik 0 czy 1 ?
JR NZ,JUMP ; jeśli 1, wstecz do JUMP.
EXX ; inaczej przełącz na zestaw wskaźnikowy.
INC HL ; przejdź ponad bajtem z długością skoku.
EXX ; wróć do głównego zestawu rejestrów.
RET
; -------------------
; PODPROGRAM 'MODULO'
; -------------------
; ( Offset $2E: 'n-mod-m' )
; ( i1, i2 -- i3, i4 )
; Ten podprogram oblicza wartość N mod M, gdzie M jest liczbą naturalną, ostatnią wartością na stosie kalkulatora, a N jest liczbą naturalną
; leżącą poniżej. Procedura zwraca wynik dzielenia jako ostatnią wartość oraz leżącą pod nią resztę z dzielenia.
; Np. 17 MOD 3 = 5 i reszta 2
; Podprogram jest wywoływany podczas obliczania liczby pseudolosowej oraz również przez procedurę PRINT-FP.
; $1C37 : 7223
N_MOD_M: RST 28H ; KALKULATOR 17, 3.
.BYTE $C0 ; stos-do-pamięci-0 17, 3.
.BYTE $02 ; usuń 17.
.BYTE $2D ; duplikuj 17, 17.
.BYTE $E0 ; pobierz-pamięć-0 17, 17, 3.
.BYTE $05 ; dziel 17, 17/3.
.BYTE $24 ; zamień-na-całkowitą 17, 5.
.BYTE $E0 ; pobierz-pamięć-0 17, 5, 3.
.BYTE $01 ; zamień 17, 3, 5.
.BYTE $C0 ; stos-do-pamięci-0 17, 3, 5.
.BYTE $04 ; mnóż 17, 15.
.BYTE $03 ; odejmij 2.
.BYTE $E0 ; pobierz-pamięć-0 2, 5.
.BYTE $34 ; koniec-obliczeń 2, 5.
RET
; --------------------------
; FUNKCJA 'LICZBA CAŁKOWITA'
; --------------------------
; (offset $24: 'int')
; Funkcja ta zwraca część całkowitą z x, która dla liczb dodatnich jest taka sama jak dla zaokrąglania. Literał zaokrąglij
; obcina liczby ujemne w górę, zatem -3.4 daje wynik -3, natomiast funkcja INT w języku BASIC musi zaokrąglać liczby ujemne w dół, aby otrzymać
; INT -3.4 równe -4. Najlepiej przejść przez polecenia, używając jako przykładowych wartości liczb 3.4 i -3.4
; $1C46 : 7238
INT: RST 28H ; KALKULATOR x. (= 3.4 lub -3.4).
.BYTE $2D ; duplikuj x, x.
.BYTE $32 ; mniej-niż-0 x, (1/0)
.BYTE $00 ; skocz-przy-prawdzie x, (1/0)
.BYTE $04 ; do L1C46, X-NEG
.BYTE $36 ; zaokrąglij trunc 3.4 = 3.
.BYTE $34 ; koniec-obliczeń 3.
RET ; wróć z wynikiem INT x na stosie kalkulatora.
; $1C4E : 7246
X_NEG: .BYTE $2D ; duplikuj -3.4, -3.4.
.BYTE $36 ; zaokrąglij -3.4, -3.
.BYTE $C0 ; stos-do-pamięci-0 -3.4, -3.
.BYTE $03 ; odejmij -.4
.BYTE $E0 ; na-stos-paimięć-0 -.4, -3.
.BYTE $01 ; zamień -3, -.4.
.BYTE $2C ; neguj-logicznie -3, (0).
.BYTE $00 ; skocz-przy-prawdzie -3.
.BYTE $03 ; do $1C59, EXIT -3.
.BYTE $A1 ; jeden-na-stos -3, 1.
.BYTE $03 ; odejmij -4.
; $1C59 : 7257
EXIT: .BYTE $34 ; koniec-obliczeń -4.
RET
; ----------------
; Potęgowanie (23)
; ----------------
; $1C5B : 7259
EXP: RST 28H ; KALKULATOR
.BYTE $30 ; dane-na-stos
.BYTE $F1 ; Wykładnik: $81, Bajty: 4
.BYTE $38,$AA,$3B,$29
.BYTE $04 ; mnóż
.BYTE $2D ; duplikuj
.BYTE $24 ; na-liczbę-całkowitą
.BYTE $C3 ; stos-do-pamięci-3
.BYTE $03 ; odejmij
.BYTE $2D ; duplikuj
.BYTE $0F ; dodaj
.BYTE $A1 ; jeden-na-stos
.BYTE $03 ; odejmij
.BYTE $88 ; serie-08
.BYTE $13 ; Wykładnik: $63, Bajty: 1
.BYTE $36 ;(+00,+00,+00)
.BYTE $58 ; Wykładnik: $68, Bajty: 2
.BYTE $65,$66 ;(+00,+00)
.BYTE $9D ; Wykładnik: $6D, Bajty: 3
.BYTE $78,$65,$40 ;(+00)
.BYTE $A2 ; Wykładnik: $72, Bajty: 3
.BYTE $60,$32,$C9 ;(+00)
.BYTE $E7 ; Wykładnik: $77, Bajty: 4
.BYTE $21,$F7,$AF,$24
.BYTE $EB ; Wykładnik: $7B, Bajty: 4
.BYTE $2F,$B0,$B0,$14
.BYTE $EE ; Wykładnik: $7E, Bajty: 4
.BYTE $7E,$BB,$94,$58
.BYTE $F1 ; Wykładnik: $81, Bajty: 4
.BYTE $3A,$7E,$F8,$CF
.BYTE $E3 ; na-stos-paimięć-3
.BYTE $34 ; koniec-obliczeń
CALL FP_TO_A
JR NZ,N_NEGTV
JR C,REPORT_6B
ADD A,(HL)
JR NC,RESULT_OK
; $1C99 : 7321
REPORT_6B: RST 08H ; ERROR-1
.BYTE $05 ; Raport Błędu: Liczba zbyt duża
; $1C9B : 7323
N_NEGTV: JR C,RSLT_ZERO
SUB (HL)
JR NC,RSLT_ZERO
NEG
; $1CA2 : 7330
RESULT_OK: LD (HL),A
RET
; $1CA4 : 7332
RSLT_ZERO: RST 28H ; KALKULATOR
.BYTE $02 ; usuń
.BYTE $A0 ; na-stos-zero
.BYTE $34 ; koniec-obliczeń
RET
; ----------------------------
; FUNKCJA 'LOGARYTM NATURALNY'
; ----------------------------
; (offset $22: 'ln')
; Jak sam ZX81, logarytmy 'naturalne' pochodzą ze Szkocji. Zostały wymyślone w 1614 roku przez obytego w podróżach Szkota o nazwisku John Napier,
; który zapisał "Nic nie jest bardziej dokuczliwe i utrudniające w obliczeniach od mnożeń, dzieleń, kwadratów i sześcianów dużych liczb".
; Logarytmy Napiera umożliwiły wykonywanie powyższych operacji przez proste dodawanie i odejmowanie, ułatwiając obliczenia nawigacyjne i astronomiczne,
; które dręczyły jego erę. Logarytmy naturalne zostały szybko zastąpione logarytmami przy podstawie 10, które wraz z Napierem wynalazł Henry Briggs,
; wyuczony w Cambridge profesor geometrii na Uniwersytecie Oksfordzkim. Uprościły one układanie tablic, które pozwalały ludziom łatwo skalować
; obliczenia.
; Dopiero niedawno, wraz z pojawieniem się kalkulatorów kieszonkowych i komputerów takich jak ZX81, logarytmy naturalne odzyskały swoją pozycję,
; chociaż niektóre komputery wciąż zachowują logarytmy dziesiętne. Logarytmy naturalne są potęgami podstawy 'e', która, podobnie jak liczba pi,
; w sposób naturalny pojawia się w różnych dziedzinach matematyki. Również jak 'pi' liczba 'e' jest niewymierna i zaczyna się jako 2.718281828...
; Tabelaryczne używanie logarytmów polegało na tym, iż aby pomnożyć przez siebie dwie liczby a i b, należało znaleźć ich logarytmy, następnie
; znalezione logarytmy dodać i otrzymaną sumę odszukać w tablicy antylogarytmów, w której dostawaliśmy pożądany iloczyn.
; Funkcja EXP jest odpowiednikiem antylogarytmu w języku BASIC. Biorąc dwie dowolne liczby, powiedzmy 1.72 i 6.89, uruchom poniższy program:
; 10 PRINT EXP ( LN 1.72 + LN 6.89 )
; dostaniesz taki sam wynik jak:
; 20 PRINT 1.72 * 6.89.
; Dzielenie wykonuje się za pomocą odejmowania dwóch logarytmów.
; Napier wspominał również o rachunkach związanych z kwadratowymi i sześciennymi potęgami oraz pierwiastkami. Aby podnieść pewną liczbę do 3 potęgi,
; znajdź jej logarytm naturalny, pomnóż go przez 3 i znajdź antylogarytm wyniku, np.
; PRINT EXP( LN 4 * 3 )
; daje 64.
; Podobnie, aby znaleźć n-ty pierwiastek podziel logarytm przez 'n'. ROM komputera ZX81 wykorzystuje następujący wzór do wyszukiwania wartości
; pierwiastków kwadratowych (tutaj pierwiastka z liczby 9):
; PRINT EXP ( LN 9 / 2 )
; Funkcja Napiera dotycząca pierwiastka kwadratowego jest jedynie specjalnym przypadkiem funkcji 'potęgowej'. Pierwiastek sześcienny lub
; dowolny inny liczy się równie prosto.
; Najpierw sprawdzane jest, czy argument LN jest liczbą większą od zera.
; $1CA9 : 7337
LN: RST 28H ; KALKULATOR x.
.BYTE $2D ; duplikuj x,x.
.BYTE $33 ; większe-niż-0 x,(0/1).
.BYTE $00 ; skocz-przy-prawdzie x.
.BYTE $04 ; do L1CB1, VALID
.BYTE $34 ; koniec-obliczeń
; $1CAF : 7343
REPORT_AB: RST 08H ; ERROR-1
.BYTE $09 ; Raport Błędu: zły argument
; $1CB1 : 7345
VALID: .BYTE $A0 ; na-stos-zero x,0. - usunięte ze stosu 1 zostaje nadpisane zerem
.BYTE $02 ; usuń x. - operacja jest zbędna
.BYTE $34 ; koniec-obliczeń
LD A,(HL) ; pobierz wykładnik e
LD (HL),$80 ; zredukuj x do mantysy - x'
CALL STACK_A ; stos zawiera teraz x',e.
RST 28H ; KALKULATOR x',e.
.BYTE $30 ; dane-na-stos x',e,128.
.BYTE $38 ; Wykładnik: $88, Bajty: 1
.BYTE $00 ;(+00,+00,+00)
.BYTE $03 ; odejmij x',e-128. - zredukuj do prawdziwego wykładnika
.BYTE $01 ; zamień e',x'.
.BYTE $2D ; duplikuj e',x',x'.
.BYTE $30 ; dane-na-stos e',x',x',0.8.
.BYTE $F0 ; Wykładnik: $80, Bajty: 4
.BYTE $4C,$CC,$CC,$CD ;
.BYTE $03 ; odejmij e',x',x'-0.8.
.BYTE $33 ; większe-niż-0 e',x'(0/1).
.BYTE $00 ; skocz-przy-prawdzie e',x'.
.BYTE $08 ; do L1CD2, GRE_8
.BYTE $01 ; zamień x',e'.
.BYTE $A1 ; jeden-na-stos x',e',1.
.BYTE $03 ; odejmij x',e'-1.
.BYTE $01 ; zamień e'-1,x'.
.BYTE $34 ; koniec-obliczeń
INC (HL) ; powiększ x' dwa razy e'-1,2x'.
RST 28H ; KALKULATOR e'-1,2x'.
; $1CD2 : 7378 (x' <= 0.8) (x' > 0.8) - tutaj zbiegają się obie wersje
GRE_8: .BYTE $01 ; zamień 2x',e'-1. | x',e'.
.BYTE $30 ; dane-na-stos 2x',e'-1,LN 2. | x',e',LN 2.
.BYTE $F0 ; Wykładnik: $80, Bajty: 4
.BYTE $31,$72,$17,$F8 ;
.BYTE $04 ; mnóż 2x',(e'-1)LN 2. | x',e'LN 2.
.BYTE $01 ; zamień (e'-1)LN 2,2x'. | e'LN 2,x'.
.BYTE $A2 ; na-stos-1/2 (e'-1)LN 2,2x',0.5. | e'LN 2,x',0.5.
.BYTE $03 ; odejmij-1/2 (e'-1)LN 2,2x'-0.5. | e'LN 2,x'-0.5.
.BYTE $A2 ; na-stos-1/2 (e'-1)LN 2,2x'-0.5,0.5. | e'LN 2,x'-0.5,0.5.
.BYTE $03 ; odejmij (e'-1)LN 2,2x'-1. | e'LN 2,x'-1.
.BYTE $2D ; duplikuj (e'-1)LN 2,2x'-1,2x'-1. | e'LN 2,x'-1,x'-1.
.BYTE $30 ; dane-na-stos (e'-1)LN 2,2x'-1,2x'-1,2.5. | e'LN 2,x'-1,x'-1,2.5.
.BYTE $32 ; Wykładnik: $82, Bajty: 1
.BYTE $20 ;(+00,+00,+00)
.BYTE $04 ; mnóż (e'-1)LN 2,2x'-1,5x'-2.5. | e'LN 2,x'-1,2.5x'-2.5.
.BYTE $A2 ; na-stos-1/2 (e'-1)LN 2,2x'-1,5x'-2.5,0.5. | e'LN 2,x'-1,2.5x'-2.5,0.5.
.BYTE $03 ; odejmij (e'-1)LN 2,2x'-1,5x'-3. | e'LN 2,x'-1,2.5x'-3.
.BYTE $8C ; serie-0C uruchom generator szeregów
.BYTE $11 ; Wykładnik: $61, Bajty: 1
.BYTE $AC ;(+00,+00,+00)
.BYTE $14 ; Wykładnik: $64, Bajty: 1
.BYTE $09 ;(+00,+00,+00)
.BYTE $56 ; Wykładnik: $66, Bajty: 2
.BYTE $DA,$A5 ;(+00,+00)
.BYTE $59 ; Wykładnik: $69, Bajty: 2
.BYTE $30,$C5 ;(+00,+00)
.BYTE $5C ; Wykładnik: $6C, Bajty: 2
.BYTE $90,$AA ;(+00,+00)
.BYTE $9E ; Wykładnik: $6E, Bajty: 3
.BYTE $70,$6F,$61 ;(+00)
.BYTE $A1 ; Wykładnik: $71, Bajty: 3
.BYTE $CB,$DA,$96 ;(+00)
.BYTE $A4 ; Wykładnik: $74, Bajty: 3
.BYTE $31,$9F,$B4 ;(+00)
.BYTE $E7 ; Wykładnik: $77, Bajty: 4
.BYTE $A0,$FE,$5C,$FC ;
.BYTE $EA ; Wykładnik: $7A, Bajty: 4
.BYTE $1B,$43,$CA,$36 ;
.BYTE $ED ; Wykładnik: $7D, Bajty: 4
.BYTE $A7,$9C,$7E,$5E ;
.BYTE $F0 ; Wykładnik: $80, Bajty: 4
.BYTE $6E,$23,$80,$93 ;
.BYTE $04 ; mnóż
.BYTE $0F ; dodaj
.BYTE $34 ; koniec-obliczeń
RET
; --------------------------
; FUNKCJE 'TRYGONOMETRYCZNE'
; --------------------------
; Trygonometria to technika kosmiczna. Używali jej również stolarze i budowniczowie piramid. Niektóre zastosowania trygonometrii mogą być
; zupełnie abstrakcyjne, lecz jej zasady można zaobserwować w prostych trójkątach prostokątnych. Trójkąty posiadają kilka specjalnych własności:
; 1) Suma wszystkich trzech kątów wynosi zawsze PI radianów (180 stopni). Bardzo pomocne, gdy znasz dwa kąty i chcesz znaleźć trzeci.
; 2) W każdym trójkącie prostokątnym suma kwadratów przyprostokątnych jest równa kwadratowi przeciwprostokątnej. Bardzo pomocne, gdy znasz
; długości dwóch boków trójkąta prostokątnego i chcesz znaleźć długość boku trzeciego.
; 3) Funkcje sinus, cosinus i tangens umożliwiają wyliczenie nieznanej długości boku, jeśli znamy długość innego boku oraz kąt pomiędzy
; przyprostokątną a przeciwprostokątną.
; 4) Funkcje arcus sinus, arcus cosinus i arcus tangens pozwalają wyliczyć nieznany kąt, jeśli są znane długości dwóch boków.
; -----------------------------
; PROCEDURA 'ZREDUKUJ ARGUMENT'
; -----------------------------
; (offset $35: 'get-argt')
; Ta funkcja wykonuje dwie funkcje na kącie wyrażonym w radianach, które przygotowują go jako argument dla funkcji sinus i cosinus.
; Najpierw upewnia się, czy kąt redukuje się do normalnego zakresu. Np. jeśli statek zawraca o kąt, powiedzmy, 3PI radianów (540 stopni),
; to efektywny obrót wyniesie PI radianów (180 stopni).
; Następnie zamienia kąt w radianach na ułamek kąta prostego w zależności od tego, w której ćwiartce układu współrzędnych leży ten kąt.
; wynikiem jest liczba z zakresu od -1 do 1.
; 90 stopni
; (pi/2)
; II +1 I
; |
; sin+ |\ | /| sin+
; cos- | \ | / | cos+
; tan- | \ | / | tan+
; | \|/) |
; 180 stopni (pi) 0 -|----+----|-- 0 (0) 0 stopni
; | /|\ |
; sin- | / | \ | sin-
; cos- | / | \ | cos+
; tan+ |/ | \| tan-
; |
; III -1 IV
; (3pi/2)
; 270 stopni
; $1D18 : 7448
GET_ARGT: RST 28H ; KALKULATOR x.
.BYTE $30 ; dane-na-stos
.BYTE $EE ; Wykładnik: $7E, Bajty: 4
.BYTE $22,$F9,$83,$6E ; x, 1/(2*PI).
.BYTE $04 ; mnóż x/(2*PI).
.BYTE $2D ; duplikuj x/(2*PI),x/(2*PI).
.BYTE $A2 ; na-stos-1/2 x/(2*PI),x/(2*PI),0.5.
.BYTE $0F ; dodaj x/(2*PI),x/(2*PI)+0.5.
.BYTE $24 ; na-liczbę-całkowitą x/(2*PI),x/INT((2*PI)+0.5).
.BYTE $03 ; odejmij x/(2*PI)-x/INT((2*PI)+0.5) = y
.BYTE $2D ; duplikuj y,y.
.BYTE $0F ; dodaj 2y.
.BYTE $2D ; duplikuj 2y,2y.
.BYTE $0F ; dodaj 4y.
.BYTE $2D ; duplikuj 4y,4y.
.BYTE $27 ; wartość-bezwzględna 4y,ABS(4y).
.BYTE $A1 ; jeden-na-stos 4y,ABS(4y),1.
.BYTE $03 ; odejmij 4y,ABS(4y)-1 = z
.BYTE $2D ; duplikuj 4y,z,z.
.BYTE $33 ; większe-niż-0 4y,z,(0/1).
.BYTE $C0 ; stos-do-pamięci-0 mem-0 zawiera wynik testu
.BYTE $00 ; skocz-przy-prawdzie 4y,z
.BYTE $04 ; do L1D35, ZPLUS
.BYTE $02 ; usuń 4y.
.BYTE $34 ; koniec-obliczeń 4y.
RET
; Jeśli skok nastąpił do tego miejsca, kontynuuj.
; $1D35 : 7477
ZPLUS: .BYTE $A1 ; jeden-na-stos 4y,z,1.
.BYTE $03 ; odejmij 4y,z-1.
.BYTE $01 ; zamień z-1,4y.
.BYTE $32 ; mniej-niż-0 z-1,(1/0).
.BYTE $00 ; skocz-przy-prawdzie z-1.
.BYTE $02 ; do $1D3C, YNEG
.BYTE $18 ; zmień-znak 1-z.
; $1D3C : 7484
YNEG: .BYTE $34 ; koniec-obliczeń
RET
; -----------------
; FUNKCJA 'COSINUS'
; -----------------
; (offset $1D: 'cos')
; Cosinus jest obliczany jako sinus przeciwnego kąta w trójkącie prostokątnym z poprawą znaku w zależności od numeru ćwiartki, w której
; ten kąt się znajduje.
; /|
; h /y|
; / |o
; /x |
; /----|
; a
; Cosinus kąta x jest równy długości boku a podzielonej przez długość przeciwprostokątnej h. Jednakże, gdy rozważymy kąt y, to okaże się, że
; a/h jest równe jego sinusowi. Ponieważ kąty x i y sumują się do kata prostego, kąt y możemy znaleźć przez odjęcie kąta x od pi/2. Jednakże
; równie łatwe jest wstępne zredukowanie argumentu, a następnie odjęcie go od wartości 1 (zredukowany kąt prosty). Jest jeszcze prościej odjąć
; 1 od tego kąta i skorygować znak. W rzeczywistości po zredukowaniu argumentu wykorzystywana jest jego wartość bezwzględna, a następnie sprawdzany
; jest wynik redukcji, zachowany w obszarze pamięci kalkulatora w mem-0. Na podstawie tego wyniku dokonuje się korekcji znaku argumentu przed
; obliczeniem sinusa.
; $1D3E : 7486
COS: RST 28H ; KALKULATOR x. - kąt w radianach.
.BYTE $35 ; get-argt x. - zredukuj do -1 to +1
.BYTE $27 ; wartość-bezwzględna ABS(x)
.BYTE $A1 ; jeden-na-stos ABS(x),1.
.BYTE $03 ; odejmij ABS(x)-1. - teraz kąt przeciwny
.BYTE $E0 ; na-stos-paimięć-0 ABS(x)-1,(0/1). - pobierz wskaźnik znaku.
.BYTE $00 ; skocz-przy-prawdzie ABS(x)-1. - jeśli zgodny, skocz do obliczeń sinusa
.BYTE $06 ; naprzód do $1D4B, C-ENT
.BYTE $18 ; zmień-znak 1-ABS(x). - inaczej zmień znak na przeciwny
.BYTE $2F ; skocz 1-ABS(x). - i teraz skocz do obliczania sinusa
.BYTE $03 ; naprzód do $1D4B, C-ENT
; ---------------
; FUNKCJA 'SINUS'
; ---------------
; (offset $1C: 'sin')
; Jest to podstawowa funkcja, z której są wyliczane bezpośrednio lub pośrednio pozostałe funkcje trygonometryczne - cos i tg.
; Wykorzystuje ona generator szeregów do wyznaczenia wartości wielomianów Czybyszewa.
; /|
; 1 / |
; / |x
; /a |
; /----|
; y
; $1D49 : 7497
SIN: RST 28H ; KALKULATOR x. - kąt w radianach
.BYTE $35 ; get-argt x. - zredukuj do -1 to +1
; $1D4B : 7499
C_ENT: .BYTE $2D ; duplikuj x,x.
.BYTE $2D ; duplikuj x,x,x.
.BYTE $04 ; mnóż x,x^2.
.BYTE $2D ; duplikuj x,x^2,x^2.
.BYTE $0F ; dodaj x,2x^2.
.BYTE $A1 ; jeden-na-stos x,2x^2,1.
.BYTE $03 ; odejmij x,2x^2-1.
.BYTE $86 ; serie-06 uruchom generator szeregów
.BYTE $14 ; Wykładnik: $64, Bajty: 1
.BYTE $E6 ;(+00,+00,+00)
.BYTE $5C ; Wykładnik: $6C, Bajty: 2
.BYTE $1F,$0B ;(+00,+00)
.BYTE $A3 ; Wykładnik: $73, Bajty: 3
.BYTE $8F,$38,$EE ;(+00)
.BYTE $E9 ; Wykładnik: $79, Bajty: 4
.BYTE $15,$63,$BB,$23 ;
.BYTE $EE ; Wykładnik: $7E, Bajty: 4
.BYTE $92,$0D,$CD,$ED ;
.BYTE $F1 ; Wykładnik: $81, Bajty: 4
.BYTE $23,$5D,$1B,$EA ;
.BYTE $04 ; mnóż
.BYTE $34 ; koniec-obliczeń
RET
; -----------------
; FUNKCJA 'TANGENS'
; -----------------
; (offset $1E: 'tan')
; Oblicza tangens z x jako sin(x) / cos(x).
; /|
; h / |
; / |o
; /x |
; /----|
; a
; Tangens kąta x jest ilorazem długości naprzeciwległego boku o przez długość boku przyległego a. Ponieważ długość naprzeciwległego boku można
; obliczyć wykorzystując sin(x), a długość boku przyległego wykorzystując cos(x), to tangens można zdefiniować jak powyżej.
; Błąd nr 6 powstaje, jeśli argument w radianach jest zbyt bliski +/-pi/2 - dla takiego kata tangens osiąga wartość nieskończoną.
; Np. PRINT TAN (PI/2) jest obliczane jak 1/0.
; Podobnie PRINT TAN (3*PI/2), TAN (5*PI/2) itd.
; $1D6E : 7534
TAN: RST 28H ; KALKULATOR x.
.BYTE $2D ; duplikuj x,x.
.BYTE $1C ; sinus x,SIN(x).
.BYTE $01 ; zamień SIN(x),x.
.BYTE $1D ; cosinus SIN(x),COS(x).
.BYTE $05 ; dziel SIN(x)/COS(x) (= TAN(x)).
.BYTE $34 ; koniec-obliczeń TAN(x).
RET
; -----------------------
; FUNKCJA 'ARCUS TANGENS'
; -----------------------
; (Offset $21: 'atn')
; Funkcja odwrotna do tangensa, której wynik jest w radianach.
; Jest to podstawowa funkcja, z której wyprowadzane są pośrednio lub bezpośrednio pozostałe funkcje, takie jak arcus sinus i arcus cosinus. Zwracany
; wynik leży w zakresie od -pi/2 do pi/2.
; Jest ostatnią z czterech procedur, które wykorzystują generator szeregów do utworzenia wielomianów Chybyszewa.
; $1D76 : 7542
ATN: LD A,(HL) ; pobierz wykładnik
CP $81 ; porównaj go z wykładnikiem jedynki
JR C,SMALL ; jeśli mniejszy, naprzód do SMALL
RST 28H ; KALKULATOR x.
.BYTE $A1 ; jeden-na-stos x,1.
.BYTE $18 ; zmień-znak x,-1.
.BYTE $01 ; zamień -1,x.
.BYTE $05 ; dziel -1/x.
.BYTE $2D ; duplikuj -1/x,-1/x.
.BYTE $32 ; mniej-niż-0 -1/x,(-1/x<0?).
.BYTE $A3 ; na-stos-pi/2 -1/x,(0/1),pi/2.
.BYTE $01 ; zamień -1/x,pi/2,(0,1).
.BYTE $00 ; skocz-przy-prawdzie -1/x,pi/2.
.BYTE $06 ; do L1D8B, CASES
.BYTE $18 ; zmień-znak -1/x,-pi/2.
.BYTE $2F ; skocz -1/x,-pi/2.
.BYTE $03 ; do L1D8B, CASES
; $1D89 : 7561 (x > 0) (x <= 0)
SMALL: RST 28H ; KALKULATOR -1/x,pi/2. | -1/x,-pi/2.
.BYTE $A0 ; na-stos-zero -1/x,pi/2,0. | -1/x,-pi/2,0.
; $1D8B : 7563
CASES: .BYTE $01 ; zamień -1/x,0,pi/2. | -1/x,0,-pi/2.
.BYTE $2D ; duplikuj -1/x,0,pi/2,pi/2. | -1/x,0,-pi/2,-pi/2.
.BYTE $2D ; duplikuj -1/x,0,pi/2,pi/2,pi/2. | -1/x,0,-pi/2,-pi/2,-pi/2.
.BYTE $04 ; mnóż -1/x,0,pi/2,pi^2/4. | -1/x,0,-pi/2,pi^2/4.
.BYTE $2D ; duplikuj -1/x,0,pi/2,pi^2/4,pi^2/4. | -1/x,0,-pi/2,pi^2/4,pi^2/4.
.BYTE $0F ; dodaj -1/x,0,pi/2,pi^2/2. | -1/x,0,-pi/2,pi^2/2.
.BYTE $A1 ; jeden-na-stos -1/x,0,pi/2,pi^2/2,1. | -1/x,0,-pi/2,pi^2/2,1.
.BYTE $03 ; odejmij -1/x,0,pi/2,pi^2/2-1. | -1/x,0,-pi/2,pi^2/2-1.
.BYTE $8C ; serie-0C uruchom generator szeregów
.BYTE $10 ; Wykładnik: $60, Bajty: 1
.BYTE $B2 ;(+00,+00,+00)
.BYTE $13 ; Wykładnik: $63, Bajty: 1
.BYTE $0E ;(+00,+00,+00)
.BYTE $55 ; Wykładnik: $65, Bajty: 2
.BYTE $E4,$8D ;(+00,+00)
.BYTE $58 ; Wykładnik: $68, Bajty: 2
.BYTE $39,$BC ;(+00,+00)
.BYTE $5B ; Wykładnik: $6B, Bajty: 2
.BYTE $98,$FD ;(+00,+00)
.BYTE $9E ; Wykładnik: $6E, Bajty: 3
.BYTE $00,$36,$75 ;(+00)
.BYTE $A0 ; Wykładnik: $70, Bajty: 3
.BYTE $DB,$E8,$B4 ;(+00)
.BYTE $63 ; Wykładnik: $73, Bajty: 2
.BYTE $42,$C4 ;(+00,+00)
.BYTE $E6 ; Wykładnik: $76, Bajty: 4
.BYTE $B5,$09,$36,$BE ;
.BYTE $E9 ; Wykładnik: $79, Bajty: 4
.BYTE $36,$73,$1B,$5D ;
.BYTE $EC ; Wykładnik: $7C, Bajty: 4
.BYTE $D8,$DE,$63,$BE ;
.BYTE $F0 ; Wykładnik: $80, Bajty: 4
.BYTE $61,$A1,$B3,$0C ;
.BYTE $04 ; mnóż
.BYTE $0F ; dodaj
.BYTE $34 ; koniec-obliczeń
RET
; ---------------------
; FUNKCJA 'ARCUS SINUS'
; ---------------------
; (Offset $1F: 'asn')
; Funkcja odwrotna do sinus z wynikiem w radianach. Obliczana z wykorzystaniem powyższej funkcji arcus tangens. Błąd A jest generowany, jeśli
; argument wykracza poza przedział <-1,1>. Do obliczeń wykorzystana została tożsamość trygonometryczna:
; TAN(x/2) = SIN(x/(1+COS x))
; Z TAN(x/2) wyliczany jest kat x/2 za pomocą wywołania ATN i na koniec samo x przez dodanie do siebie połówki kąta - x/2.
; $1DC4 : 7620
ASN: RST 28H ; KALKULATOR x.
.BYTE $2D ; duplikuj x,x.
.BYTE $2D ; duplikuj x,x,x.
.BYTE $04 ; mnóż x,x^2.
.BYTE $A1 ; jeden-na-stos x,x^2,1.
.BYTE $03 ; odejmij x,x^2-1.
.BYTE $18 ; zmień-znak x,1-x^2.
.BYTE $25 ; pierwiastek x,sqr(1-x^2) = y.
.BYTE $A1 ; jeden-na-stos x,y,1.
.BYTE $0F ; dodaj x,y+1.
.BYTE $05 ; dziel x/(y+1).
.BYTE $21 ; arcus tangens a/2 - połówka kąta
.BYTE $2D ; duplikuj a/2,a/2.
.BYTE $0F ; dodaj a.
.BYTE $34 ; koniec-obliczeń a.
RET
; -----------------------
; FUNKCJA 'ARCUS COSINUS'
; -----------------------
; (Offset $20: 'acs')
; Funkcja odwrotna do cosinus z wynikiem w radianach.
; Błąd A, jeśli argument nie jest w zakresie od -1 do 1.
; Wynik w zakresie od 0 do pi.
; Wyprowadzona z powyższego asn, które z kolei jest wyprowadzone z poprzedzającego atn. Mogłoby być wyliczone bezpośrednio z atn wg wzoru
; acs(x) = atn(sqr(1-x*x)/x).
; Jednakże, ponieważ sinus i cosinus są pionowymi translacjami jednego w drugi, użyto
; acs(x) = pi/2 - asn(x)
; tj. arcus cosinus znanej wartości x da poszukiwany kąt b w radianach. Z powyższego wiemy, jak policzyć kąt a używając asn(x). Ponieważ trzy
; kąty dowolnego trójkata dodają się do pi radianów, a największym kątem w tym przypadku jest kąt prosty (pi/2 radianów), to możemy obliczyć
; kąt b jako pi/2 (oba katy) minus asn(x) (kąt a).
;
; /|
; 1 /b|
; / |x
; /a |
; /----|
; y
; $1DD4 : 7636
ACS: RST 28H ; KALKULATOR x.
.BYTE $1F ; arcus-sinus asn(x).
.BYTE $A3 ; na-stos-pi/2 asn(x), pi/2.
.BYTE $03 ; odejmij asn(x) - pi/2.
.BYTE $18 ; zmień-znak pi/2 - asn(x) = acs(x).
.BYTE $34 ; koniec-obliczeń acs(x)
RET
; ---------------------------------
; OPERACJA 'PIERWIASTEK KWADRATOWY'
; ---------------------------------
; (Offset $25: 'sqr')
; Błąd A, jeśli argument jest ujemny. Ta procedura jest godna uwagi ze względu na jej rozmiar - 7 bajtów. Pierwotnie kod ZX81 zajmował 9KB
; i musiano zastosować różne techniki, aby wcisnąć go w pamięć 8K.
; $1DDB : 7643
SQR: RST 28H ; KALKULATOR x.
.BYTE $2D ; duplikuj x, x.
.BYTE $2C ; neguj-logicznie x, 1/0
.BYTE $00 ; skocz-przy-prawdzie x, (1/0).
.BYTE $1E ; do L1DFD, LAST, wyjdź przy argumencie równym zero - wynik zero.
; inaczej kontynuuj obliczanie jako x ** .5
.BYTE $A2 ; na-stos-1/2 x, .5.
.BYTE $34 ; koniec-obliczeń x, .5.
; ----------------------
; OPERACJA 'POTĘGOWANIE'
; ----------------------
; (Offset $06: 'to-power')
; Podnosi pierwszą liczbę x do potęgi drugiej liczby y.
; 0 ** +n = 0
; 0 ** -n = nadmiar arytmetyczny.
; $1DE2 : 7650
TO_POWER: RST 28H ; KALKULATOR x, y.
.BYTE $01 ; zamień y, x.
.BYTE $2D ; duplikuj y, x, x.
.BYTE $2C ; neguj-logicznie y, x, (1/0).
.BYTE $00 ; skocz-przy-prawdzie
.BYTE $07 ; naprzód do $1DEE, XISO, jeśli x jest równe zero.
; inaczej X jest niezerowe. Funkcja 'ln' przechwyci ujemne wartości x.
.BYTE $22 ; logarytm-naturalny y, LN y.
.BYTE $04 ; mnóż y * LN x.
.BYTE $34 ; koniec-obliczeń
JP EXP ; skocz wstecz do procedury EXP
; Te procedury tworzą trzy proste wyniki, gdy liczba ma wartość zero. Rozpocznij od usunięcia znanego zera, aby pozostawić y do potęgi czynnika.
; $1DEE : 7662
XISO: .BYTE $02 ; usuń y.
.BYTE $2D ; duplikuj y, y.
.BYTE $2C ; neguj-logicznie y, (1/0).
.BYTE $00 ; skocz-przy-prawdzie
.BYTE $09 ; naprzód do $1DFB, ONE jeśli y jest równe zero.
; czynnik potęgowy nie jest zerem. Jeśli jest ujemny, powstanie błąd.
.BYTE $A0 ; na-stos-zero y, 0.
.BYTE $01 ; zamień 0, y.
.BYTE $33 ; większe-niż-0 0, (1/0).
.BYTE $00 ; skocz-przy-prawdzie 0.
.BYTE $06 ; do $1DFD, LAST jeśli y był liczbą dodatnią.
; inaczej wymuś dzielenie przez zero, co spowoduje powstanie błędu nadmiaru arytmetycznego. Istnieją pewne jedno i dwubajtowe zamienniki,
; lecz najbardziej formalnym zakończeniem powinno być end-calc; RST 08 .BYTE 05.
.BYTE $A1 ; jeden-na-stos 0, 1.
.BYTE $01 ; zamień 1, 0.
.BYTE $05 ; dziel 1/0 >> błąd
; $1DFB : 7675
ONE: .BYTE $02 ; usuń .
.BYTE $A1 ; jeden-na-stos 1.
; $1DFD : 7677
LAST: .BYTE $34 ; koniec-obliczeń ostatnia wartość 1 lub 0.
RET
; ------------------
; 'ZAPASOWE KOMÓRKI'
; ------------------
; $1DFF : 7679
SPARE: .BYTE $FF ; to już wszystko
; --------------------
; 'ZESTAW ZNAKÓW ZX81'
; --------------------
; $1E00 : 7680
CHAR_SET:
; $00 - Znak: ' ' CHR$(0)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
; $01 - Znak: mozaika CHR$(1)
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
; $02 - Znak: mozaika CHR$(2)
.BYTE %00001111
.BYTE %00001111
.BYTE %00001111
.BYTE %00001111
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
; $03 - Znak: mozaika CHR$(3)
.BYTE %11111111
.BYTE %11111111
.BYTE %11111111
.BYTE %11111111
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
; $04 - Znak: mozaika CHR$(4)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
; $05 - Znak: mozaika CHR$(5)
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
; $06 - Znak: mozaika CHR$(6)
.BYTE %00001111
.BYTE %00001111
.BYTE %00001111
.BYTE %00001111
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
; $07 - Znak: mozaika CHR$(7)
.BYTE %11111111
.BYTE %11111111
.BYTE %11111111
.BYTE %11111111
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
.BYTE %11110000
; $08 - Znak: mozaika CHR$(8)
.BYTE %10101010
.BYTE %01010101
.BYTE %10101010
.BYTE %01010101
.BYTE %10101010
.BYTE %01010101
.BYTE %10101010
.BYTE %01010101
; $09 - Znak: mozaika CHR$(9)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %10101010
.BYTE %01010101
.BYTE %10101010
.BYTE %01010101
; $0A - Znak: mozaika CHR$(10)
.BYTE %10101010
.BYTE %01010101
.BYTE %10101010
.BYTE %01010101
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
; $0B - Znak: '"' CHR$(11)
.BYTE %00000000
.BYTE %00100100
.BYTE %00100100
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
; $0C - Znak: L CHR$(12)
.BYTE %00000000
.BYTE %00011100
.BYTE %00100010
.BYTE %01111000
.BYTE %00100000
.BYTE %00100000
.BYTE %01111110
.BYTE %00000000
; $0D - Znak: '$' CHR$(13)
.BYTE %00000000
.BYTE %00001000
.BYTE %00111110
.BYTE %00101000
.BYTE %00111110
.BYTE %00001010
.BYTE %00111110
.BYTE %00001000
; $0E - Znak: ':' CHR$(14)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00010000
.BYTE %00000000
.BYTE %00000000
.BYTE %00010000
.BYTE %00000000
; $0F - Znak: '?' CHR$(15)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %00000100
.BYTE %00001000
.BYTE %00000000
.BYTE %00001000
.BYTE %00000000
; $10 - Znak: '(' CHR$(16)
.BYTE %00000000
.BYTE %00000100
.BYTE %00001000
.BYTE %00001000
.BYTE %00001000
.BYTE %00001000
.BYTE %00000100
.BYTE %00000000
; $11 - Znak: ')' CHR$(17)
.BYTE %00000000
.BYTE %00100000
.BYTE %00010000
.BYTE %00010000
.BYTE %00010000
.BYTE %00010000
.BYTE %00100000
.BYTE %00000000
; $12 - Znak: ">' CHR$(18)
.BYTE %00000000
.BYTE %00000000
.BYTE %00010000
.BYTE %00001000
.BYTE %00000100
.BYTE %00001000
.BYTE %00010000
.BYTE %00000000
; $13 - Znak: '<' CHR$(19)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000100
.BYTE %00001000
.BYTE %00010000
.BYTE %00001000
.BYTE %00000100
.BYTE %00000000
; $14 - Znak: '=' CHR$(20)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00111110
.BYTE %00000000
.BYTE %00111110
.BYTE %00000000
.BYTE %00000000
; $15 - Znak: '+' CHR$(21)
.BYTE %00000000
.BYTE %00000000
.BYTE %00001000
.BYTE %00001000
.BYTE %00111110
.BYTE %00001000
.BYTE %00001000
.BYTE %00000000
; $16 - Znak: '-' CHR$(22)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00111110
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
; $17 - Znak: '*' CHR$(23)
.BYTE %00000000
.BYTE %00000000
.BYTE %00010100
.BYTE %00001000
.BYTE %00111110
.BYTE %00001000
.BYTE %00010100
.BYTE %00000000
; $18 - Znak: '/' CHR$(24)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000010
.BYTE %00000100
.BYTE %00001000
.BYTE %00010000
.BYTE %00100000
.BYTE %00000000
; $19 - Znak: ';' CHR$(25)
.BYTE %00000000
.BYTE %00000000
.BYTE %00010000
.BYTE %00000000
.BYTE %00000000
.BYTE %00010000
.BYTE %00010000
.BYTE %00100000
; $1A - Znak: ',' CHR$(26)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00001000
.BYTE %00001000
.BYTE %00010000
; $1B - Znak: '"' CHR$(27)
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00000000
.BYTE %00011000
.BYTE %00011000
.BYTE %00000000
; $1C - Znak: '0' CHR$(28)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000110
.BYTE %01001010
.BYTE %01010010
.BYTE %01100010
.BYTE %00111100
.BYTE %00000000
; $1D - Znak: '1' CHR$(29)
.BYTE %00000000
.BYTE %00011000
.BYTE %00101000
.BYTE %00001000
.BYTE %00001000
.BYTE %00001000
.BYTE %00111110
.BYTE %00000000
; $1E - Znak: '2' CHR$(30)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %00000010
.BYTE %00111100
.BYTE %01000000
.BYTE %01111110
.BYTE %00000000
; $1F - Znak: '3' CHR$(31)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %00001100
.BYTE %00000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $20 - Znak: '4' CHR$(32)
.BYTE %00000000
.BYTE %00001000
.BYTE %00011000
.BYTE %00101000
.BYTE %01001000
.BYTE %01111110
.BYTE %00001000
.BYTE %00000000
; $21 - Znak: '5' CHR$(33)
.BYTE %00000000
.BYTE %01111110
.BYTE %01000000
.BYTE %01111100
.BYTE %00000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $22 - Znak: '6' CHR$(34)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000000
.BYTE %01111100
.BYTE %01000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $23 - Znak: '7' CHR$(35)
.BYTE %00000000
.BYTE %01111110
.BYTE %00000010
.BYTE %00000100
.BYTE %00001000
.BYTE %00010000
.BYTE %00010000
.BYTE %00000000
; $24 - Znak: '8' CHR$(36)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %00111100
.BYTE %01000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $25 - Znak: '9' CHR$(37)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %01000010
.BYTE %00111110
.BYTE %00000010
.BYTE %00111100
.BYTE %00000000
; $26 - Znak: 'A' CHR$(38)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %01000010
.BYTE %01111110
.BYTE %01000010
.BYTE %01000010
.BYTE %00000000
; $27 - Znak: 'B' CHR$(39)
.BYTE %00000000
.BYTE %01111100
.BYTE %01000010
.BYTE %01111100
.BYTE %01000010
.BYTE %01000010
.BYTE %01111100
.BYTE %00000000
; $28 - Znak: 'C' CHR$(40)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %01000000
.BYTE %01000000
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $29 - Znak: 'D' CHR$(41)
.BYTE %00000000
.BYTE %01111000
.BYTE %01000100
.BYTE %01000010
.BYTE %01000010
.BYTE %01000100
.BYTE %01111000
.BYTE %00000000
; $2A - Znak: 'E' CHR$(42)
.BYTE %00000000
.BYTE %01111110
.BYTE %01000000
.BYTE %01111100
.BYTE %01000000
.BYTE %01000000
.BYTE %01111110
.BYTE %00000000
; $2B - Znak: 'F' CHR$(43)
.BYTE %00000000
.BYTE %01111110
.BYTE %01000000
.BYTE %01111100
.BYTE %01000000
.BYTE %01000000
.BYTE %01000000
.BYTE %00000000
; $2C - Znak: 'G' CHR$(44)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %01000000
.BYTE %01001110
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $2D - Znak: 'H' CHR$(45)
.BYTE %00000000
.BYTE %01000010
.BYTE %01000010
.BYTE %01111110
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %00000000
; $2E - Znak: 'I' CHR$(46)
.BYTE %00000000
.BYTE %00111110
.BYTE %00001000
.BYTE %00001000
.BYTE %00001000
.BYTE %00001000
.BYTE %00111110
.BYTE %00000000
; $2F - Znak: 'J' CHR$(47)
.BYTE %00000000
.BYTE %00000010
.BYTE %00000010
.BYTE %00000010
.BYTE %01000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $30 - Znak: 'K' CHR$(48)
.BYTE %00000000
.BYTE %01000100
.BYTE %01001000
.BYTE %01110000
.BYTE %01001000
.BYTE %01000100
.BYTE %01000010
.BYTE %00000000
; $31 - Znak: 'L' CHR$(49)
.BYTE %00000000
.BYTE %01000000
.BYTE %01000000
.BYTE %01000000
.BYTE %01000000
.BYTE %01000000
.BYTE %01111110
.BYTE %00000000
; $32 - Znak: 'M' CHR$(50)
.BYTE %00000000
.BYTE %01000010
.BYTE %01100110
.BYTE %01011010
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %00000000
; $33 - Znak: 'N' CHR$(51)
.BYTE %00000000
.BYTE %01000010
.BYTE %01100010
.BYTE %01010010
.BYTE %01001010
.BYTE %01000110
.BYTE %01000010
.BYTE %00000000
; $34 - Znak: 'O' CHR$(52)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $35 - Znak: 'P' CHR$(53)
.BYTE %00000000
.BYTE %01111100
.BYTE %01000010
.BYTE %01000010
.BYTE %01111100
.BYTE %01000000
.BYTE %01000000
.BYTE %00000000
; $36 - Znak: 'Q' CHR$(54)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000010
.BYTE %01000010
.BYTE %01010010
.BYTE %01001010
.BYTE %00111100
.BYTE %00000000
; $37 - Znak: 'R' CHR$(55)
.BYTE %00000000
.BYTE %01111100
.BYTE %01000010
.BYTE %01000010
.BYTE %01111100
.BYTE %01000100
.BYTE %01000010
.BYTE %00000000
; $38 - Znak: 'S' CHR$(56)
.BYTE %00000000
.BYTE %00111100
.BYTE %01000000
.BYTE %00111100
.BYTE %00000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $39 - Znak: 'T' CHR$(57)
.BYTE %00000000
.BYTE %11111110
.BYTE %00010000
.BYTE %00010000
.BYTE %00010000
.BYTE %00010000
.BYTE %00010000
.BYTE %00000000
; $3A - Znak: 'U' CHR$(58)
.BYTE %00000000
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %00111100
.BYTE %00000000
; $3B - Znak: 'V' CHR$(59)
.BYTE %00000000
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %00100100
.BYTE %00011000
.BYTE %00000000
; $3C - Znak: 'W' CHR$(60)
.BYTE %00000000
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %01000010
.BYTE %01011010
.BYTE %00100100
.BYTE %00000000
; $3D - Znak: 'X' CHR$(61)
.BYTE %00000000
.BYTE %01000010
.BYTE %00100100
.BYTE %00011000
.BYTE %00011000
.BYTE %00100100
.BYTE %01000010
.BYTE %00000000
; $3E - Znak: 'Y' CHR$(62)
.BYTE %00000000
.BYTE %10000010
.BYTE %01000100
.BYTE %00101000
.BYTE %00010000
.BYTE %00010000
.BYTE %00010000
.BYTE %00000000
; $3F - Znak: 'Z' CHR$(63)
.BYTE %00000000
.BYTE %01111110
.BYTE %00000100
.BYTE %00001000
.BYTE %00010000
.BYTE %00100000
.BYTE %01111110
.BYTE %00000000
.END ;dyrektywa asemblera TASM
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe