; ===================================================================== ; 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