Tworzenie programów w kodzie maszynowym dla ZX81 na PC-cie


Pisanie programów na ZX81 zawsze było męką. ZX81 BASIC jest potwornie wolny, z kolei asembler posiada niezbyt wygodny edytor. Jeśli tworzysz kompilowane programy, to w pamięci musisz przechowywać tekst źródłowy. W efekcie masz mniej pamięci dla kodu wynikowego. Przy standardowych 16KB może to być dosyć bolesne.

Dlatego w tym rozdziale przedstawiam tworzenie systemu programowania w asemblerze dla ZX81 na komputerze IBM-PC. Dostaniesz coś, o czym w latach 80-tych mogłem jedynie pomarzyć. Narzędzia te są przeznaczone dla zaawansowanych użytkowników, którzy potrafią (lub potrafili) programować ZX81 w języku maszynowym.

Zacznijmy od skompletowania niezbędnych narzędzi, które są zupełnie DARMOWE i LEGALNE. Będziesz potrzebował cztery rzeczy:

  1. Dowolny emulator ZX81, który potrafi odczytywać pliki taśmy o rozszerzeniu P. Proponuję VB81, jest wręcz idealny:
    ściągnij plik instalacyjny VB81
    przeczytaj instrukcję instalacji i używania VB81
  2. Asembler mikroprocesora Z80, który działa w środowisku Windows. Proponuję darmowy tniasm (istnieje również dobry asembler tasm dla mikroprocesora Z80, jednakże ten ma licencję na 30 dni, po upływie których powinieneś zapłacić autorom 40$, aby nie stać się piratem, zatem sam rozumiesz...):
    ściągnij plik z asemblerem tniasm
    przeczytaj instrukcję dla asemblera tniasm
  3. Wygodny edytor dla tworzonych programów w asemblerze Z80. Ostatecznie można stosować Notatnik z Windows, ale po co się męczyć:
    ściągnij plik instalacyjny edytora MicroAsm
  4. Zestaw plików szablonowych asm dla asemblera tniasm, które utworzą odpowiedni projekt pliku taśmy P dla ZX81.
    pobierz plik z szablonami asm

Poniżej wyjaśniam szczegółowo pracę w tym systemie.

 

Pliki taśmy P w ZX81

Komputer ZX81 posiada dwa rozkazy współpracujące z pamięcią zewnętrzną:

LOAD "nazwa programu" - służy do odczytu programu w języku BASIC z taśmy

SAVE "nazwa programu" - zapisuje bieżący program na taśmie

Jeśli pracujesz z emulatorem VB81, to rozkazy te powodują odczyt lub zapis plików taśmy o rozszerzeniu P w bieżącym katalogu taśmy, który ustawiamy odpowiednią opcją menu. Plik taśmy P zawiera dokładnie to samo, co plik odczytywany na rzeczywistym ZX81 z magnetofonu kasetowego. Naszym celem będzie utworzenie takiego właśnie pliku, w którym umieścimy nasz program w języku maszynowym.

Plik taśmy P zawiera ciągły obszar pamięci zawierający zmienne systemowe, program w języku ZX81 BASIC, bufor wyświetlania DFILE oraz obszar zmiennych języka BASIC. Zapis do pliku rozpoczyna się od adresu szesnastkowego 4009H (16393), a kończy się na końcu obszaru zmiennych języka BASIC. W miejscu tym jest umieszczany bajt o kodzie 80H (128), a jego adres jest równy (E_LINE)-1. Zatem całe zadanie sprowadza się do wygenerowania pliku binarnego, który zawiera obszar pamięci ZX81 od 4009H do (E_LINE)-1.

 

Wiersze programu w ZX81 BASIC

Kolejnym problemem jest miejsce umieszczania kodu maszynowego. Konstrukcja ZX81 uniemożliwia wykonywanie kodu maszynowego ponad adresem 7FFFH (32767). Jest to spowodowane ingerencją ULA w kody wykonywanych instrukcji. Wyświetlanie obrazu w ZX81 jest już tak skonstruowane, iż dla ULA kod maszynowy wykonywany ponad adresem 7FFFH oznacza tworzenie obrazu i wszystkie instrukcje mikroprocesora Z80, które mają wyzerowany bit nr 6 w swoim kodzie, zostaną przekształcone na instrukcję NOP przez ULA. Zatem kod maszynowy musi się znaleźć w obszarze od 4000H do 7FFFH. Tutaj mamy właściwie dwie możliwości:

  1. Wewnątrz instrukcji REM - instrukcje REM są ignorowane przez BASIC i mogą zawierać dowolną treść.
  2. W obszarze zmiennych. Obszar ten jest zapisywany do pliku P. Jednakże zmienne mogą się przemieszczać w pamięci - np. gdy dodasz jakiś wiersz do programu. Rozkaz RUN kasuje wszystkie zmienne i przez nieuwagę może uszkodzić twój kod.

Dla naszych celów najlepszym rozwiązaniem jest umieszczenie całego kodu w pierwszym wierszu programu. Pierwszy wiersz posiada zawsze stały adres 407DH (16509). W ZX81 każdy wiersz programu posiada następującą budowę:

Bajty Wyjaśnienie
2 Numer wiersza w kolejności MSB,LSB - odwrotnie niż normalny zapis liczb 2-bajtowych.
2 Długość wiersza
długość-1 Tekst wiersza programu
1 NEWLINE

Zatem strategia jest następująca:

Cały program w języku maszynowym umieszczamy wewnątrz instrukcji REM w wierszu pierwszym (nadamy mu numer 0, aby nie można go było edytować w edytorze ZX81 BASIC). Długość wiersza wyliczy asembler na podstawie długości programu w kodzie maszynowym. Daje nam to zupełną swobodę co do ilości instrukcji i danych (pod warunkiem nieprzekroczenia dopuszczalnego rozmiaru pamięci). 16KB dla ZX81 to wystarczająco dużo.  Pierwsza instrukcja programu w języku maszynowym rozpocznie się od adresu:

  16509 - początek programu
+     2 - numer wiersza
+     2 - długość wiersza
+     1 - kod instrukcji REM
-------
  16514 - adres pierwszej instrukcji w kodzie maszynowym

Program maszynowy musi być uruchomiony po załadowaniu się programu do pamięci. Zadanie to wypełni drugi wiersz programu (o numerze 1), który będzie zawierał polecenie:

RAND USR VAL "16514"

W zmiennej systemowej NXTLIN umieścimy adres drugiego wiersza programu. Spowoduje to, iż po załadowaniu do pamięci zawartości pliku taśmy P ZX81 automatycznie wykona wiersz drugi i w ten sposób nasz kod maszynowy będzie uruchomiony. A co stanie się dalej, zależy już wyłącznie od twojego kodu.

Podsumowując, program w ZX81 BASIC ma następującą postać:

0 REM kod maszynowy
1 RAND USR VAL "16514"

 

Pliki szablonów asm

Ściągnięte z naszego serwera pliki szablonowe asm zapisz w swoim katalogu projektowym. Plikiem projektu jest plik o nazwie main.asm. Aby otrzymać plik taśmy P musisz skompilować plik main.asm przy pomocy asemblera tniasm (plik ten i reszta plików są przygotowane specjalnie dla tego asemblera). Na szczęście robi się to bardzo prosto. Jeśli umieścisz tniasm.exe w twoim katalogu projektowym z resztą plików asm, to wystarczy wydać polecenie np. z konsoli Windows:

 

tniasm main.asm

 

Jeśli pracujesz w poleconym przez nas edytorze MicroAsm, to wejdź w menu do Options → Program Options i w oknie dialogowym zdefiniuj pola Command (adres tniasm.exe na twoim dysku)  i Arguments (plik main.asm):

 

Teraz możesz kompilować cały projekt klawiszem F7 - pamiętaj o przejściu do katalogu projektowego!

Pliki szablonowe są następujące:

main.asm  -  główny plik projektowy
chars.asm  - definiuje kody znaków dla ZX81, które są niestandardowe. Kody te używasz do wyświetlania znaków ZX81.
svars.asm  - definiuje obszar zmiennych systemowych, który jest umieszczany na początku pliku taśmy P. Przy okazji zostają tutaj zdefiniowane nazwy i adresy poszczególnych zmiennych systemowych, z których możesz swobodnie korzystać w swoich własnych programach.
line0.asm  - pierwszy wiersz programu ZX81 BASIC zawierający instrukcję REM, za którą będzie umieszczony program w kodzie maszynowym
myprg.asm  - w tym pliku umieszczasz swój kod maszynowy. W pamięci ZX81 będzie się on znajdował począwszy od adresu 16514. Tylko ten plik praktycznie edytujesz w swoim projekcie - pozostałe pliki są opakowaniem twojego programu w kodzie maszynowym i służą do wygenerowania poprawnej zawartości pliku taśmy P. Nie zmieniaj ich treści bez wyraźnej i świadomej potrzeby.
line1.asm  - drugi wiersz programu ZX81 BASIC zawierający polecenie RAND USR VAL "16514", które uruchamia twój kod maszynowy, bufor wyświetlania DFILE oraz pusty obszar zmiennych języka BASIC.

 

Poniżej przedstawiam treść poszczególnych plików:

 

main.asm
; Plik projektu dla ZX81

FNAME "TEST.P"		; nazwa pliku taśmy dla ZX81

CPU	Z80

ORG	$4000,$7FFF

INCLUDE "chars.asm"	; definicje znaków ZX81
INCLUDE "svars.asm"	; zmienne systemowe ZX81 BASIC
INCLUDE "line0.asm"	; wiersz numer 0
INCLUDE "myprg.asm"	; główny program maszynowy
INCLUDE "line1.asm"	; wiersz numer 1, bufor wyświetlania, zmienne ZX81 BASIC

 

chars.asm
; Znaki ZX81
;
;  ____0___1___2___3___4___5___6___7___8___9___A___B___C___D___E___F____
;  00 SPC GRA GRA GRA GRA GRA GRA GRA GRA GRA GRA  "  GBP  $   :   ?  0F
;  10  (   )   >   <   =   +   -   *   /   ;   ,   .   0   1   2   3  1F
;  20  4   5   6   7   8   9   A   B   C   D   E   F   G   H   I   J  2F
;  30  K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z  3F

__:	EQU	$00	;spacja
_QT:	EQU	$0B	;"
_PD:	EQU	$0C	;funt
_SD:	EQU	$0D	;$
_CL:	EQU	$0E	;:
_QM:	EQU	$0F	;?
_OP:	EQU	$10	;(
_CP:	EQU	$11	;)
_GT:	EQU	$12	;>
_LT:	EQU	$13	;<
_EQ:	EQU	$14	;=
_PL:	EQU	$15	;+
_MI:	EQU	$16	;-
_AS:	EQU	$17	;*
_SL:	EQU	$18	;/
_SC:	EQU	$19	;;
_CM:	EQU	$1A	;,
_DT:	EQU	$1B	;.
_NL:	EQU	$76	;NEWLINE
_0:	EQU	$1C
_1:	EQU	$1D
_2:	EQU	$1E
_3:	EQU	$1F
_4:	EQU	$20
_5:	EQU	$21
_6:	EQU	$22
_7:	EQU	$23
_8:	EQU	$24
_9:	EQU	$25
_A:	EQU	$26
_B:	EQU	$27
_C:	EQU	$28
_D:	EQU	$29
_E:	EQU	$2A
_F:	EQU	$2B
_G:	EQU	$2C
_H:	EQU	$2D
_I:	EQU	$2E
_J:	EQU	$2F
_K:	EQU	$30
_L:	EQU	$31
_M:	EQU	$32
_N:	EQU	$33
_O:	EQU	$34
_P:	EQU	$35
_Q:	EQU	$36
_R:	EQU	$37
_S:	EQU	$38
_T:	EQU	$39
_U:	EQU	$3A
_V:	EQU	$3B
_W:	EQU	$3C
_X:	EQU	$3D
_Y:	EQU	$3E
_Z:	EQU	$3F

 

svars.asm
; Plik definiuje wszystkie zmienne systemowe ZX81

; Te zmienne nie są zapisywane do pliku z programem

ERR_NR:	RB	1
FLAGS:	RB	1
ERR_SP:	RW	1
RAMTOP:	RW	1
MODE:	RB	1
PPC:	RW	1

; Te zmienne są zapisywane - start pliku z programem

VERSN:	DB	0
E_PPC:	DW	0
DFILE:	DW	DFILEA
DF_CC:	DW	DFILEA+1
VARS:	DW	VARSA
DEST:	DW	0
E_LINE:	DW	VARSA+1
CH_ADD:	DW	LAST-1
X_PTR:	DW	0
STKBOT:	DW	LAST
STKEND:	DW	LAST
BREG:	DB	0
MEM:	DW	MEMBOT
	DB	0
DF_SZ:	DB	2
S_TOP:	DW	1
LAST_K:	DB	$FF,$FF,$FF
MARGIN:	DB	55
NXTLIN:	DW	LINE1 ;adres wiersza, który zostanie wykonany po załadowaniu programu
OLDPPC:	DW	0
FLAGX:	DB	0
STRLEN:	DW	0
T_ADDR:	DW	$0C8D
SEED:	DW	0
FRAMES:	DW	$FFFF
COORDS:	DB	0,0
PR_CC:	DB	$BC
S_POSN:	DB	33,24
CDFLAG:	DB	01000000B
PRTBUF:	DS	33
MEMBOT:	DS	32

; adresy kilku procedur w ROM ZX81

RESET:		EQU	$0000
SLOWFAST:	EQU	$0207
SETFAST:	EQU	$02E7
NEXTLINE:	EQU	$0676
DECODEKEY:	EQU	$07BD
PRINTAT:	EQU	$08F5
MAKEROOM:	EQU	$099E
CLS:		EQU	$0A2A
STACK2BC:	EQU	$0BF5
STACK2A:	EQU	$0C02
CLASS6:		EQU	$0D92
FINDINT:	EQU	$0EA7
FAST:		EQU	$0F23
SLOW:		EQU	$0F2B
DEBOUNCE:	EQU	$0F4B
SETMIN:		EQU	$14BC

 

line0.asm
; Wiersz nr 0. Zawiera instrukcję REM, w której zostaje umieszczony
; nasz kod maszynowy. Parametry wiersza dla języka BASIC są automatycznie
; generowane przez asembler.

LINE0:	DB	0,0		; numer wiersza
	DW	LINE1-$-2	; długość wiersza
	DB	$EA		; kod rozkazu REM

 

myprg.asm
; Tutaj umieszczasz cały swój program

	LD  HL,(DFILE)
	INC HL
	LD  (HL),_X
	RET

 

line1.asm
; Wiersz nr 1. Zawiera instrukcję RAND USR VAL "16514", która
; uruchamia kod maszynowy

	DB   $76			; koniec wiersza 0

LINE1:	DB   0,1			; numer wiersza
	DW   DFILEA-$-2			; długość wiersza
	DB   $F9,$D4,$C5		; RAND USR VAL
	DB   _QT,_1,_6,_5,_1,_4,_QT	; "16514"
	DB   _NL			; NEWLINE

; Bufor wyświetlania

DFILEA:	DB   _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL
	DS   32 | DB _NL

; Zmienne ZX81 BASIC - nie potrzebujesz ich w kodzie maszynowym

VARSA:	DB   $80

; Koniec obszaru zapisywanego do pliku taśmy P

LAST:

 

Instrukcja dla asemblera tniasm

tniASM v0.44
(C)2000-2005 - The New Image

Programował Patriek Lesparre

http://tniasm.tni.nl/
e-mail: tniasm@tni.nl

Chapter 1: Introduction

Rozdział 1: Wprowadzenie

1.1 What is it?

tniASM is a cross assembler for Z80, R800 and GBZ80. Some of its features are:

  • Multi-pass design
  • Conditional assembly
  • Local label mechanism
  • Extensive 32 bit expression parser with precedence levels
  • Source and binary file inclusion
  • Nestable block comments
  • Multi file output and file patching

1.1 Co to jest?

tniASM jest cross-asemblerem dla mikroprocesorów Z80, R800 i GBZ80. Niektóre z jego cech to:

  • Wykonywanie wielu przebiegów
  • Warunkowa asemblacja
  • Mechanizm etykiet lokalnych
  • Wyrażenia 32 bitowe z poziomami priorytetów
  • Dołączanie plików źródłowych i binarnych
  • Komentarze w komentarzach
  • Możliwość tworzenia wielu plików oraz robienia wstawek

1.2 Why?

Why would anyone write another assembler? Good question! First, I needed to learn C and there's no better way to learn a new language than to write a program in it you need. That brings me to the second reason I wrote tniASM, which is that none of the other cross assemblers I know had the features I wanted/needed.

Because of that, and perhaps the fact that I've never written a assembler/compiler before, tniASM is not like other assemblers. It has many peculiarities and frankly, you might find it a bit weird. Nevertheless, it suits my purposes perfectly and I hope it does yours too.

1.2 Po co?

Po co pisać kolejny asembler? Dobre pytanie! Po pierwsze musiałem się nauczyć języka C, a nie ma lepszego sposobu na naukę nowego języka niż napisanie w nim programu, który jest ci potrzebny. Tutaj dochodzę do drugiego powodu napisania tniASM, który skupia się na tym, iż żaden inny znany mi cross-asembler nie posiadał potrzebnych mi cech.

Dlatego, oraz być może z powodu, iż nigdy wcześniej nie napisałem asemblera/kompilatora, tniASM nie przypomina innych asemblerów. Posiada on wiele dziwactw i szczerze mówiąc może on ci się wydawać nieco pokręcony. Niemniej jednak spełnia on moje wymagania i mam nadzieję, że spełni również twoje.

1.3 tniASM and Passes

Most assemblers have 2 passes, one to gather information on labels and another to handle forward references and generate the output.

tniASM goes about it differently. The output generation is a pass by itself, and before that tniASM will do as many passes as is needed to evaluate all expressions.

Basically it means that tniASM will make 2 passes PLUS an extra pass for every level of forward referencing, up to 5 passes total.

1.3 tniASM i przebiegi

Większość asemblerów działa dwuprzebiegowo, w pierwszym przebiegu zbierają one informacje na temat etykiet a w drugim zajmują się odwołaniami naprzód i tworzą program wynikowy.

tniASM działa tutaj inaczej. Tworzenie programu wynikowego wykonywane jest w osobnym przebiegu, a przed nim tniASM wykona tyle przebiegów, ile będzie mu potrzebne w celu wyliczenia wartości wszystkich wyrażeń.

Oznacza to, iż tniASM wykona 2 przebiegi PLUS dodatkowy przebieg dla każdego poziomu odwołania naprzód, w sumie do 5 przebiegów.

1.4 tniASM Assembler Syntax

tniASM has its own assembler syntax for the processors it supports. Don't be alarmed though, the changes are minimal. See chapters 2.7 and on for differences between standard rules.

1.4 Składnia asemblera tniASM

tniASM ma swoją własną składnię asemblera dla obsługiwanych mikroprocesorów. Nie panikuj jednakże, zmiany są minimalne. Zobacz do rozdziału 2.7 i dalszych, jeśli chcesz poznać odstępstwa od standardowych reguł.

1.5 Using tniASM

tniASM is very easy to use. Just type:

tniasm filename

tniASM will then try to assemble 'filename' and output the generated machine code to 'tniasm.out'.

If a filename without extension is given and there's an error, tniASM will add a ".asm" extension and try again.

If you want to use a different output filename than 'tniasm.out' and you don't want to use the FNAME pseudo instruction, you can add a second filename to the command line and tniASM will use that as output.

1.5 Używanie tniASM

tniASM jest bardzo prosty w użyciu. Wystarczy wpisać

tniasm nazwa_pliku

tniASM następnie spróbuje dokonać asemblacji podanego pliku i umieścić wygenerowany kod maszynowy w pliku "tniasm.out".

Jeśli podany zostanie plik bez rozszerzenia i powstanie błąd, to tniASM doda do nazwy rozszerzenie ".asm" i spróbuje ponownie.

Jeśli chcesz używać pliku wyjściowego o innej nazwie niż "tniasm.out", a nie chcesz stosować dyrektywy FNAME, to możesz dodać drugą nazwę pliku w wierszu polecenia, a tniASM użyje go jako pliku wyjściowego.

1.5.1 tniASM Output

As said in chapter 1.5, tniASM will normally output machine code to a file called 'tniasm.out'. You can change this by using the FNAME instruction (see chapter 2.5.5).

tniASM will also output a symbol table file, called 'tniasm.sym', which contains the value of all labels in your program. It's in the form:

'label: EQU value'

so it can be INCLUDEd in external files directly.

Besides these files, tniASM also creates a temporary file called 'tniasm.tmp' strictly for internal use.

1.5.1  Wyjście tniASM

Jak napisano w rozdziale 1.5, tniASM zwykle umieszcza kod maszynowy w pliku o nazwie "tniasm.out". Możesz to zmienić stosując dyrektywę FNAME (zobacz do rozdziału 2.5.5).

tniASM tworzy również plik z tablicą symboli o nazwie "tniasm.sym", który zawiera wartości wszystkich etykiet w twoim programie. Wpisy posiadają postać:

"etykieta: EQU wartość"

zatem można ten plik dołączać bezpośrednio do zewnętrznych plików.

Oprócz tych plików, tniASM tworzy również plik tymczasowy o nazwie "tniasm.tmp", który przeznaczony jest ściśle do wewnętrznego użytku.

Chapter 2: The tniASM Language

Rozdział 2: Język tniASM

2.1 Case-sensitivity

In short, there is none. 'LabelA' and 'labela' are exactly the same to tniASM, as are 'ldir' and 'LdiR'.

2.1 Rozróżnianie liter dużych i małych

Krótko, nie istnieje. Dla tniASM "LabelA" i "labela" oznaczają dokładnie to samo, jak również "ldir" i "LdiR".

2.2 Layout of a tniASM Source Line

This is a point where tniASM differs a lot from most assemblers. Usually a source line is laid out like this:

[label:] [instruction [operands]] [;comment]

Which means it allows any combination of the 3 fields 'label', 'instruction' and 'comment'.

In a way tniASM uses the same layout, but it allows any number of label definitions and instructions on a single line. Only the comment field can exist only once. The layout is best described:

[ [label:] | [instruction [operands]] ]* [;comment]

This means that tniASM is perfectly happy to assemble a line like:

start: JP begin exit: LD BC,0042h CALL dos begin: LD DE,text

In addition to this freedom, tniASM places no restrictions on white space within a line. For example, labels may have white space before them and instructions may have no space before them, which also means instructions can directly follow a label definition. The same counts for comments.

2.2 Układ wiersza źródłowego tniASM

W tym punkcie tniASM różni się znacznie od większości asemblerów. Zwykle wiersz źródłowy posiada układ jak poniżej:

[etykieta:] [instrukcja [operandy]] [;komentarz]

Co oznacza, iż dozwolona jest każda kombinacja tych trzech pól "etykieta", "instrukcja" oraz "komentarz".

W pewnym sensie tniASM stosuje ten sam układ, lecz pozwala na dowolną liczbę definicji etykiet i instrukcji w pojedynczym wierszu. Jedynie pole komentarza może występować tylko jeden raz. Ten układ najlepiej opisać następująco:

[ [etykieta:] | [instrukcja [operandy]] ]* [;komentarz]

Oznacza to, iż tniASM bez żadnego problemu przyjmie do asemblacji taki wiersz:

start: JP begin exit: LD BC,0042h CALL dos begin: LD DE,text

Dodatkowo do takiej swobody tniASM nie ogranicza stosowania białych znaków wewnątrz wiersza. Na przykład etykiety mogą być poprzedzane spacjami, a instrukcje mogą rozpoczynać się na samym początku wiersza, co oznacza również, iż instrukcja może być umieszczona tuż za definicją etykiety. To samo odnosi się do komentarzy.

2.2.1 More on Labels

A label definition must end in a colon (':'). The length of a label is practically unlimited. Valid characters in labels are letters, numbers, '_', '.', '?', '!', '~', '@', and '#'. The first character of a label may not be a number.

It is allowed to define reserved words (like 'pop', 'ld' or 'hl') as labels, but they must be prefixed with '&' when used. So you can code:

call &pop
{...}
pop: pop hl
pop de
pop bc
jp [hl]

tniASM also supports local labels. A local label is always local to the previous non-local label in the source code. A label is local when the first character is a '.'.

An example will clarify:

main: ld b,8
.loop: call doSomething
djnz .loop

sub: ld b,8
.loop: call doSomething
djnz .loop

In the above code four separate labels are defined: "main", "main.loop", "sub", "sub.loop". Because of this behavior, you can also access local labels outside the scope of the current non-local label. Like so:

main: {...}
.end: ret

sub: {...}
jp main.end
.end: ret

Or the other way around, create labels that are local outside of the scope of the current non-local label:

main: ld a,[.value]
{...}

sub: {...}

main.value: db 0

2.2.1 Więcej na temat etykiet

Definicja etykiety musi kończyć się dwukropkiem (:). Długość etykiety jest praktycznie nieograniczona. Poprawnymi znakami w etykiecie są litery, cyfry, '_', '.', '?', '!', '~', '@' i '#'. Pierwszym znakiem etykiety nie może być cyfra.

Można jako etykiety zdefiniować zarezerwowane słowa (takie jak 'pop', 'ld' czy 'hl'), lecz przy zastosowaniu muszą one być poprzedzane znakiem '&'. Możesz zatem zakodować:

call &pop
{...}
pop: pop hl
pop de
pop bc
jp [hl]

tniASM również wspiera etykiety lokalne. Etykieta lokalna jest zawsze lokalna dla poprzedniej nie-lokalnej etykiety w kodzie źródłowym. Etykieta staje się lokalną, jeśli jej pierwszym znakiem jest '.'.

Ten przykład to wyjaśni:

main: ld b,8
.loop: call doSomething
djnz .loop

sub: ld b,8
.loop: call doSomething
djnz .loop

W powyższym kodzie są zdefiniowane cztery oddzielne etykiety: 'main', 'main.loop', 'sub' i 'sub.loop'. Z uwagi na to zachowanie możesz również uzyskać dostęp do lokalnych etykiet poza zasięgiem bieżącej nie-lokalnej etykiety. Na przykład tak:

main: {...}
.end: ret

sub: {...}
jp main.end
.end: ret

Albo inny okrężny sposób - utwórz etykiety lokalne leżące poza zasięgiem bieżącej ni-lokalnej etykiety:

main: ld a,[.value]
{...}

sub: {...}

main.value: db 0

2.2.2 The Line-separator

The '|' character is used as a line separator. You can use it to have more than one instruction on a line. In fact the line-separator is not needed most of the time, since tniASM can figure out by itself that the line "add a,a call jump ret" are actually 3 different instructions. Separating the instructions with '|' is just nicer to look at and perhaps better to understand. However, in a line like "add a,34 and 3 xor 255" tniASM assumes you mean "add a,253" and not 3 separate instructions. To make sure tniASM generates the code you want, use the line-separator.

2.2.2 Separator wierszowy

Znak "|" jest używany jako separator wierszy. Można go również zastosować do uzyskania więcej niż jednej instrukcji w wierszu. W zasadzie separator ten nie jest potrzebny w większości przypadków, ponieważ tniASM sam rozpoznaje, iż wiersz "add a,a call jump ret" to właściwie trzy różne instrukcje. Rozdzielanie instrukcji za pomocą "|" sprawia, że całość lepiej wygląda i łatwiej ją zrozumieć. Jednakże w wierszu takim jak "add a,34 and 3 xor 255" tniASM zakłada, iż miałeś na myśli "add a,253" a nie trzy oddzielne instrukcje. Aby mieć pewność, że tniASM wygeneruje dokładnie taki kod, jaki chcesz, stosuj separator wierszy.

2.2.3 More on Comments

As usual the semi-colon (';') is used as the comment-character. It can be placed anywhere on a line and everything behind it is ignored until a new line.

Besides the normal comments, tniASM also supports comment blocks. The '{' and '}' characters respectively mark the beginning and the end of a comment-block. They can be placed anywhere in a file, and everything between them is ignored. Comment-blocks are nestable with a practically infinite nesting depth.

2.2.3 Więcej na temat komentarzy

Jako znak komentarza stosowany jest jak zwykle średnik (;). Można go umieścić w dowolnym miejscu wiersza, a wszystko za średnikiem jest ignorowane aż do następnego wiersza.

Oprócz zwykłych komentarzy tniASM również wspiera bloki komentarzy. Początek i koniec bloku komentarza oznaczane są odpowiednio znakami "{" i "}". Można je umieścić w dowolnym miejscu pliku, a wszystko pomiędzy nimi zostanie zignorowane. Bloki komentarzy mogą być zagnieżdżane praktycznie w nieskończoność.

2.3 Constants

There are 3 different kinds of constants in tniASM: numeric, character and string. Since tniASM is a 32 bit assembler, constants are 32 bit signed integer values, ranging from -2147483648 (80000000h) to 2147483647 (7FFFFFFFh).

2.3. Stałe

W tniASM mamy trzy różne rodzaje stałych: liczbowe, znakowe i łańcuchowe. Ponieważ tniASM jest asemblerem 32-bitowym, stałe są 32-bitowymi liczbami całkowitymi ze znakiem o zakresie od -2147483648 (80000000h) to 2147483647 (7FFFFFFFh).

2.3.1 Numeric Constants

Numeric constants can be represented in decimal, hexadecimal, binary or octal. The supported forms are as follows:

Decimal:
123, 123d

Hexadecimal (cannot start with a letter, use '0ABCDh' etc.):
1234h,  $1234,  0x1234

Binary (may contain white space, ie. '1110 0100 b') :
11100100b, %11100100 ( " " " " " '% 1110 0100')

Octal:
123o,  123q

2.3.1 Stałe liczbowe

Stałe liczbowe można zapisywać jako dziesiętne, szesnastkowe, dwójkowe lub ósemkowe. Wspierane formaty są następujące:

Dziesiętne:
123,  123d

Szesnastkowe (nie może rozpoczynać literą, stosuj 0ABCDh, itp.):
1234h,  $1234,  0x1234

Dwójkowe (może zawierać białe znaki, tj. 1110 0100 b):
11100100b,  %11100100  ( " " " " " '% 1110 0100')

Ósemkowe:
123o,  123q

2.3.2 String Constants

A string constant is anything between single or double quotes larger than 4 characters. They're used in commands like DB/DW, INCLUDE and FNAME. String constants can not be used in regular expressions.

Example:

DB "a 'double-qouted' string can contain single qoutes"
DB 'and a "single-qouted" string can contain double qoutes'

2.3.2 Stałe łańcuchowe

Stałą łańcuchową jest wszystko pomiędzy apostrofami lub cudzysłowami, co zawiera więcej niż 4 znaki. Stosuje się je w rozkazach takich jak DB/DW, INCLUDE i FNAME. Stałych łańcuchowych nie można stosować w zwykłych wyrażeniach.

Przykład:

DB "łańcuch w 'cudzysłowach' może zawierać apostrofy"
DB 'a łańcuch w "apostrofach" może zawierać cudzysłowy'

2.3.3 Character Constants

Character constants follow the same rules as strings, except that they can be used in expressions. They can be up to 4 characters in size, and are stored low-byte first. The constant 'A' is thus handled as 41h, 'AB' as 4241h (or 41h,42h), 'ABC' as 434241h (41h,42h,43h) and 'ABCD' as 44434241h (41h,42h,43h,44h). In DB/DW, character constants are always considered to be a string constant, except inside an expression.

Example:

DB 'abcd' ; is 'a','b','c','d'.
DB +"abcd",1+'a' ; is 'a','b'. Since both strings are inside
; of an expression, they're treated as a
; character constant.
DB 'a'+1 ; is an error, because 'a' is not considered
; to be inside the expression - see above
DB ('abcd' >> 8) ; is 'b'

2.3.3 Stałe znakowe

Stałe znakowe podlegają takim samym regułom jak stałe łańcuchowe, lecz można je stosować w wyrażeniach. Mogą mieć długość do 4 znaków i są przechowywane w kolejności od najmłodszego bajtu. Zatem stała 'A' jest traktowana jako 41h, 'AB' jako 4241h (lub 41h, 42h), 'ABC' jako 434241h (41h,42h,43h) i 'ABCD' jako 44434241h. W DB/DW stałe znakowe są zawsze traktowane jako stałe łańcuchowe, chyba że znajdują się wewnątrz wyrażenia.

Przykład:

DB 'abcd' ; daje 'a','b','c','d'.
DB +"abcd",1+'a' ; daje 'a','b'. Ponieważ oba łańcuchy są
; wewnątrz wyrażenia, traktuje się je jako stałe znakowe.
DB 'a'+1 ; daje błąd, ponieważ 'a' nie jest uważane za część
; wyrażenia - patrz powyżej
DB ('abcd' >> 8) ; daje 'b'

2.4 Expressions

Expressions are evaluated in 32 bit signed integer arithmetic. An expression consists of one or more constants, combined with zero or more operators. Two special tokens can be used in expressions: '$' and '$$'. They are the assembly position (program counter) and file position respectively, at the beginning of the current instruction.

One could code:

nop
nop
jr $-2 ; jump to the first nop

All supported operators are listed below, starting with the lowest precedence-level. Operators with equal precedence are evaluated from left to right.

Of course any precedence can be overridden with the parenthesis '(' and ')'.

2.4 Wyrażenia

Do obliczania wartości wyrażeń wykorzystywana jest arytmetyka 32-bitowych liczb całkowitych ze znakiem. Wyrażenie składa się z jednej lub więcej stałych w kombinacji z operatorami. W wyrażeniach można stosować daw specjalne symbole: $ i $$. Odnoszą się one kolejno do pozycji asemblacji (licznika programu) oraz pozycji w pliku na początku bieżącej instrukcji.

Można zakodować:

nop
nop
jr $-2 ; skocz do pierwszej instrukcji nop

Wszystkie wspierane operatory zostały wymienione poniżej, począwszy od operatorów o najniższym priorytecie. Operatory posiadające ten sam priorytet są wyliczane od strony lewej do prawej.

Priorytet operatorów można zmienić stosując nawiasy "(" i ")".

2.4.1 Precedence Level 0 - Relational Operators

The relational operators are:
x = y equals
x <> y
x >< y
x != y
not equals
x < y less than
x > y greater than
x <= y
x =< y
less than or equals
x >= y
x => y
greater than or equals

Unlike in the C-language, the relational operators use 0 for 'false' and -1 for 'true' (instead of 0 and 1). This way there's no need for boolean versions of the AND, OR and XOR operators, since they work in exactly the same way as the bitwise ones.

The relational operators allow for complex expressions such as:

x*(1+2*(x<0))

which gives the absolute value of 'x'.

2.4.1 Operatory porównań o priorytecie zero

Operatory porównań są następujące:

x = y równe
x <> y
x >< y
x != y
różne
x < y mniejsze niż
x > y większe niż
x <= y
x =< y
mniejsze lub równe
x >= y
x => y
większe lub równe

W przeciwieństwie do języka C operatory porównań dają 0 dla fałszu i -1 dla prawdy (zamiast 0 i 1). Dzięki temu nie ma potrzeby stosowania logicznych wersji operatorów AND, OR i XOR, ponieważ działają one w identyczny sposób jak operatory bitowe.

Operatory porównań pozwalają tworzyć złożone wyrażenia, takie jak:

x*(1+2*(x<0))

co daje w wyniku wartość bezwzględną z x.

2.4.2 Precedence Level 1 - Additive and (X)OR Operators

These should speak for themselves. tniASM chooses to use 'OR' and 'XOR' keywords (as in BASIC) instead of '|' and '^' characters (as in C).
x + y Addition
x - y Subtraction
x OR y Bitwise OR
x XOR y Bitwise XOR

2.4.2 Operatory addytywne i logiczne (X)OR o priorytecie 1

Są same przez się zrozumiałe. Zamiast znaków "|" i "^" (jak w C) tniASM stosuje słowa kluczowe "OR" i "XOR" (jak w języku BASIC).

x + y dodawanie
x - y odejmowanie
x OR y bitowa suma logiczna
x XOR y bitowa różnica symetryczna
2.4.3 Precedence Level 2 - Multiplicative and AND Operators
x ^ y Exponentiation
x * y Multiplication
x / y Division
x MOD y Modulo
x << y Shift Left
x >> y Shift Right (unsigned)
x AND y Bitwise AND
2.4.3 Operatory multiplikatywne i logiczne AND o priorytecie 2
x ^ y potęgowanie
x * y mnożenie
x / y dzielenie
x MOD y reszta z dzielenia
x << y przesunięcie bitowe w lewo
x >> y przesunięcie bitowe w prawo (bez znaku)
x AND y bitowy iloczyn logiczny
2.4.4 Precedence Level 3 - Unary Operators
+ x unary plus
- x unary minus
NOT x one's complement
2.4.4 Operatory jednoargumentowe o priorytecie 3
+ x plus jednoargumentowy
- x minus jednoargumentowy
NOT x negacja bitów

2.5 Pseudo Instructions

2.5 Pseudo Instrukcje

2.5.1 DB/DW

Define a (string of) byte(s)/word(s).

DB 255
DB "bla",13,10
DW 'AB',4000h
DW "string may be odd" ; odd strings are 0-padded

2.5.1 DB/DW

Definiuje (ciąg) bajt(ów) / słowo (słów).

DB 255
DB "bla",13,10
DW 'AB',4000h
DW "łańcuch nieparzysty" ; nieparzyste łańcuchy są uzupełniane 0

2.5.2 DC

Defines a string terminated by bit 7 being set.

DC "TOKEN" ; same as DB "TOKE","N" OR 128
DC "LIST","STOP" ; defines 2 strings, both bit 7 terminated.

2.5.2 DC

Definiuje łańcuch, w którym ostatni znak ma ustawiony na 1 7-my bit.

DC "TOKEN" ; to samo co DB "TOKE","N" OR 128
DC "LIST","STOP" ;definiuje 2 łańcuchy, oba zakończone
;z ustawionym 7 bitem

2.5.3 DS

Define space (in bytes).

DS 10 ; defines 10 bytes of 0
DS 10,255 ; defines 10 bytes of 255
DS 4000h-$ ; pad with 0 until address 4000h
DS 0 ; doesn't do anything
DS -1 ; the same goes for negative values

2.5.3 SD

Definiuje miejsce (w bajtach).

DS 10 ; definiuje 10 bajtów o wartości 0
DS 10,255 ; definuje10 bajtów o wartości 255
DS 4000h-$ ; wypełnia zerami aż do adresu 4000h
DS 0 ; nic nie robi
DS -1 ;to samo odnosi się dla wartości ujemnych

2.5.4 EQU

Assign a value to a label. An EQU must follow a label on the same line.

bankstart: EQU 4000h
ORG bankstart ; same as org 4000h

2.5.4. EQU

Przypisuje wartość etykiecie. EQU musi występować za etykietą w tym samym wierszu.

bankstart: EQU 4000h
ORG bankstart ; to samo co org 4000h

2.5.5 FNAME

Specify output file. Use FNAME to make tniASM output to a file other than 'tniasm.out'. You can use FNAME as much as you like throughout your source code in order to output different parts to different files. FNAME also sets FORG to 0.

FNAME "output.bin" ; output file is now 'output.bin'
{...}

Instead of creating a new file, you can also instruct tniASM to output to an existing file by specifying a second parameter to FNAME. This second parameter is the file position where tniASM will output to, and FORG is automatically set with this value.

FNAME "output.bin",1024 ; output starts at position 1024 in the
; existing file 'output.bin'
FNAME "output.bin",0 ; output starts at position 0, like normal,
; but in an existing 'output.bin'

2.5.5 FNAME

Określa plik wyjściowy. Używaj FNAME w celu skierowania wyjścia tniASM do pliku innego niż "tniasm.out". W pliku źródłowym można wielokrotnie używać FNAME, aby umieścić różne części kodu wynikowego w różnych plikach. FNAME również ustawia FORG na 0.

FNAME "output.bin" ; teraz plikiem wyjściowym jest "output.bin"
{...}

Zamiast tworzyć nowy plik, można również poinstruować tniASM, aby wyprowadzał dane do istniejącego pliku podając drugi parametr dla FNAME. Ten drugi parametr jest pozycją w pliku, gdzie tniASM ma wstawić kod wyjściowy, a FORG zostanie automatycznie ustawione na tę wartość.

FNAME "output.bin",1024 ; wyjście rozpoczyna się od pozycji 1024
; w istniejącym pliku "output.bin"
FNAME "output.bin",0 ; wyjście rozpoczyna się od pozycji 0, jak
; zwykle, lecz w istniejącym już pliku "output.bin"

2.5.6 FORG

Set output file position. You can use FORG to cause tniASM to output at a certain file position. If the position is larger than the file it will be padded with 0's. When no FORG is given, the starting file position will be 0.

2.5.6 FORG

Ustawia pozycję wyprowadzania kodu w pliku wyjściowym. Używaj FORG do ustalenia określonej pozycji wstawiania w pliku przez tniASM. Jeśli pozycja ta jest większa niż rozmiar pliku, to plik zostanie uzupełniony zerami. Jeśli nie poda się żadnego FORG, to startowa pozycja w pliku wynosi 0.

2.5.7 INCBIN

Include binary file. The INCBIN instruction includes a binary file in the current machine code output. It's particularly useful for embedding graphics or large tables that you wouldn't want to have in huge DB lists.

music1: INCBIN "music1.bin"

INCBIN optionally takes 1 or 2 more parameters. The first is the offset in the file to be included. The second is the total length of data to be included.

INCBIN "basic.bin",7 ; include from offset 7
INCBIN "cutout.bin",1024,512 ; include 512 bytes from
; offset 1024

2.5.7 INCBIN

Dołącza plik binarny. Instrukcja INCBIN wstawia zawartość pliku binarnego w bieżącym miejscu tworzonego kodu maszynowego. Jest szczególnie użyteczna przy osadzaniu grafiki lub dużych tablic, których nie chcesz mieć na olbrzymich listach DB.

music1: INCBIN "music1.bin"

INCBIN opcjonalnie przyjmuje dodatkowe 1 lub 2 parametry. Pierwszy jest przesunięciem w pliku, który zostanie wstawiony. Drugi określa całkowitą liczbę bajtów do wstawienia z tego pliku.

INCBIN "basic.bin",7 ; dołączamy od bajtu nr 7
INCBIN "cutout.bin",1024,512 ; dołączamy 512 bajtów od pozycji 1024

2.5.8 INCLUDE

The INCLUDE instruction includes another file in the current source file, in nesting levels as deep as memory permits.

{...}
INCLUDE "incthis.asm"
{...}

2.5.8 INCLUDE

Instrukcja INCLUDE wstawia inny plik na bieżącej pozycji pliku źródłowego. Liczba zagnieżdżeń ograniczona jedynie pojemnością pamięci.

{...}
INCLUDE "incthis.asm"
{...}

2.5.9 ORG

ORG allows one or two arguments. The first sets the assembly position (program counter) to an address, while the second argument gives the maximum allowable address for this 'section'. If the address is exceeded, tniASM will issue a warning. This warning ignores any PHASE'ing. When no ORG is given, the starting assembly position will be 0.

ORG 0c000h ; following code starts as if from 0c000h
ORG 0c000h,0 ; same as above
ORG 4000h,7FFFh ; start from 4000h, warn if exceeding 7FFFh

2.5.9 ORG

ORG zezwala na jeden lub dwa argumenty. Pierwszy ustawia pozycję asemblacji (licznik programu) na wybrany adres, natomiast drugi argument określa maksymalny adres dla tej "sekcji". Jeśli adres ten zostanie przekroczony, tniASM wygeneruje ostrzeżenie. Ignorowane są przy tym instrukcje PHASE. Jeśli nie poda się żadnego ORG, to asemblacja rozpocznie się od 0.

ORG 0c000h ; dalszy kod rozpocznie się od 0c000h
ORG 0c000h,0 ; tak samo jak powyżej
ORG 4000h,7FFFh ; zaczyna się od 4000h, ostrzega przy
; przekroczeniu 7FFFh

2.5.10 PHASE/DEPHASE

PHASE 'phases' the assembly position to the address specified. This is particularly useful for code that gets relocated later. DEPHASE phases back to the normal assembly position. A new PHASE or ORG command DEPHASE's any previous PHASE'ing.

; this example relocates the routine SetS#0 from its current
; address to 0C000h. Because of the PHASE/DEPHASE its label
; 'SetS#0' already points to 0C000h.

ORG 8000h

ld hl,start
ld de,SetS#0
ld bc,SetS#0.end-SetS#0.start
ldir

{...}

SetS#0.start:
PHASE 0C000h
SetS#0: xor a ; set V9938 S#0
out [99h],a
ld a,15+128
out [99h],a
ret
DEPHASE

2.5.10 PHASE/DEPHASE

PHASE "przesuwa" pozycję asemblacji do określonego adresu. Jest to szczególnie użyteczne dla kodu, który zostanie później przeniesiony w inne miejsce pamięci. DEPHASE przywraca normalną pozycję asemblacji. Nowe PHASE lub ORG kasuje efekt każdego poprzedniego PHASE.

; w tym przykładzie relokujemy procedurę SetS#0 z jej bieżącego
; addresu do 0C000h. Z powodu PHASE/DEPHASE jej etykieta
;"'SetS#0" już wskazuje na 0C000h.

ORG 8000h

ld hl,start
ld de,SetS#0
ld bc,SetS#0.end-SetS#0.start
ldir

{...}

SetS#0.start:
PHASE 0C000h
SetS#0: xor a ; set V9938 S#0
out [99h],a
ld a,15+128
out [99h],a
ret
DEPHASE

2.5.11 RB/RW

Reserve a (number of) byte(s)/word(s) as uninitialized data. This is basically the same as DS, but does not update the file position, neither does it output anything. It merely updates the assembly position. RB and RW are useful when declaring variables in RAM.

ORG 0C000h
Var1: RB 2 ; Var1 = 0C000h
Var2: RW 1 ; Var2 = 0C002h
Var3: RB 0 ; Var3 = 0C004h
Var4: RW -1 ; Var4 = 0C004h because zero and
; negative values are ignored

2.5.11 RB/RW

Rezerwuje (podaną liczbę) bajt(ów)/słowo(słów) jako dane niezainicjowane. W zasadzie jest to to samo co DS, lecz nie zmienia pozycji w pliku, ani nie wyprowadza niczego. Jedynie uaktualnia pozycję asemblacji. RB i RW są użyteczne przy definiowaniu zmiennych w RAM.

ORG 0C000h
Var1: RB 2 ; Var1 = 0C000h
Var2: RW 1 ; Var2 = 0C002h
Var3: RB 0 ; Var3 = 0C004h
Var4: RW -1 ; Var4 = 0C004h ponieważ 0 i wartości ujemne
; są ignorowane

2.6 Conditional Assembly

Sometimes it's useful to have a certain piece of code assemble only when certain conditions are met. For instance when writing code for multiple platforms at the same time (Z80 and R800 for example), or for including/excluding debug code.

tniASM provides this functionality through the IF-construct. Its basic form is:

IF {operand} [{...}] [ELSE [{...}]] ENDIF

Note that due to the multi-pass nature of tniASM, it's allowed to use forward references in IF-constructs. They may also be used across source file boundaries. Of course IF's can be nested with a practically infinite depth.

2.6 Asemblacja Warunkowa

Czasami dobrze jest mieć pewien fragment kodu, który kompiluje się przy spełnieniu określonych warunków. Na przykład przy pisaniu kodu przeznaczonego jednocześnie dla różnych platform sprzętowych (na przykład Z80 i R800), lub do wstawiania/usuwania kodu uruchomieniowego.

tniASM zapewnia asemblację warunkową poprzez konstrukcję IF. Jej podstawowa postać jest następująca:

IF {operand} [{...}] [ELSE [{...}]] ENDIF

Uwaga: z uwagi na wieloprzebiegową naturę tniASM, w konstrukcjach IF wolno stosować odwołania naprzód. Można je również stosować poprzez granice plików źródłowych. Oczywiście polecenia IF można zagnieżdżać praktycznie dowolnie głęboko.

2.6.1 IF {expression}

The expression is evaluated and is considered 'false' when zero, while any non-zero result is considered 'true'.

loop: {...}

IF $-loop < 128
djnz loop
ELSE
dec b
jp nz,loop
ENDIF

2.6.1 IF {wyrażenie}

Wyrażenie zostaje obliczone i jest traktowane jako fałszywe, gdy ma wartość zero, natomiast każda wartość różna od zera jest traktowana jako prawda.

loop: {...}

IF $-loop < 128
djnz loop
ELSE
dec b
jp nz,loop
ENDIF

2.6.2 IFDEF {label}

Check if a label was previously declared this pass.

R800: ; comment away for Z80 version

IFDEF R800 mulub a,b ELSE call mulub_a_b ENDIF

IFDEF R800 ELSE
mulub_a_b: {...}
ret
ENDIF

2.6.2 IFDEF {etykieta}

Sprawdza, czy w bieżącym przebiegu etykieta została poprzednio zadeklarowana.

R800: ; zakomentuj dla wersji Z80

IFDEF R800 mulub a,b ELSE call mulub_a_b ENDIF

IFDEF R800 ELSE
mulub_a_b: {...}
ret
ENDIF

2.6.3 IFEXIST {string}

Check if a file exists. Look at the second example for a nice trick, which works with any IF-instruction.

IFEXIST "test" {...} ENDIF ; do {...} if "test" exists
IFEXIST "test" ELSE {...} ENDIF ; do {...} if "test" does not exist

2.6.3 IFEXIST {łańcuch}

Sprawdza, czy istnieje plik. Zobacz na drugi przykład, gdzie mamy zgrabną sztuczkę, która działa z każą instrukcją IF.

IFEXIST "test" {...} ENDIF ; wykonaj {...}. jeśli "test" istnieje
IFEXIST "test" ELSE {...} ENDIF ; wykonaj {...}, jeśli "test" nie istnieje

2.6.4 IFEXIST {label}

Similar to IFDEF, but checks if a label exists regardless of where or when it is declared. You can use this to check if a label is declared further on in the source code.

2.6.4 IFEXIST {etykieta}

Podobne do IFDEF, lecz sprawdza, czy etykieta istnieje bez względu gdzie i kiedy jest deklarowana. Możesz użyć tej instrukcji do sprawdzenia, czy etykieta jest deklarowana dalej w kodzie źródłowym.

2.7 Multi CPU support

tniASM can assembly code for several CPU's, namely Z80, R800 and the processor commonly known as GBZ80 (GB stands for Game Boy). By default, tniASM assumes it is working in R800/MSX mode. Using the "CPU" instruction, one can switch between the following modes. This can be done anywhere in your code and as often as you wish.

The modes are called "Z80", "R800" (plus the alias "MSX") and "GBZ80".

CPU Z80 ; switch to Z80 mode
CPU R800 ; switch to R800 mode
CPU MSX ; equivalent to R800 mode
CPU GBZ80 ; switch to GBZ80 mode

2.7 Wspieranie wielu mikroprocesorów

tniASM może tworzyć kod maszynowy dla kilku mikroprocesorów, mianowicie Z80, R800 i procesora powszechnie znanego jako GBZ80 (GB to konsola Game Boy). Standardowo tniASM pracuje w trybie R800/MSX. Używając instrukcji CPU można przełączać się pomiędzy poniższymi trybami. Można to robić w dowolnym miejscu kodu i dowolnie często.

Tryby noszą nazwy "Z80", "R800" (plus alias "MSX") i "GBZ80".

CPU Z80 ; przełącza na tryb Z80
CPU R800 ; przełącza na tryb R800
CPU MSX ; ekwiwalent trybu R800
CPU GBZ80 ; przełącza na tryb GBZ80

2.7.1 Z80 mode

This mode does not accept the R800 MULUB/MULUW opcodes, but otherwise is the same as R800/MSX mode.

Differences with standard Z80 syntax rules are:

  •  [ and ] is supported for indirection, in addition to ( and ). So if you want to read a word from memory address 4000h, you can code LD HL,[4000h] as equivalent of LD HL,(4000h).
  • For ADD, ADC, SUB, SBC, AND, XOR, OR and CP, the accumulator is optional.
    So CP A,B and CP B are equivalent.
  • IN [C] or IN F,[C] can be used. (Z80 undocumented)
  • IX and IY can be split up in IXH, IXL, IYH, IYL respectively. (Z80 undocumented)
  • SLL (including alias SLI) is supported. (Z80 undocumented)
  • PUSH and POP take a register list, evaluated from left to right.

PUSH AF,BC,DE,HL ; pushes AF, BC, DE and HL in that order.
POP HL,BC ; pops HL and then BC.

2.7.1 Tryb Z80

Ten tryb nie akceptuje instrukcji MULUB/MULUW dla R800, lecz poza tym jest taki sam jak tryb R800/MSX.

Różnice w stosunku do zasad standardowej składni Z80 są następujące:

  • Klamry [ i ] są wspierane przy adresowaniu pośrednim jako dodatek do nawiasów ( i ). Zatem jeśli chcesz odczytać słowo sod adresu 4000h, możesz zakodować LD HL,[4000h] jako równoważnik LD HL,(4000h).
  • Dla ADD, SUB, SBC, AND, XOR, OR i CP akumulator można pominąć.
    Zatem CP A,B i CP B są równoważne.
  • Można stosować IN [C] i IN F,[C} (nieudokumentowane dla Z80).
  • IX i IY można rozdzielać na IXH, IXL, IYH i IYL (nieudokumentowane dla Z80).
  • SLL (łącznie z aliasem SLI) jest wspierane (nieudokumentowane dla Z80).
  • PUSH i POP przyjmuje listę rejestrów, przetwarzaną z lewa na prawo

PUSH AF,BC,DE,HL ; umieszcza na stosie kolejno AF, BC, DE i HL
POP HL,BC ; pobiera najpierw HL a później BC

2.7.2 R800 or MSX mode

All Z80 and R800 opcodes are accepted.

Differences with standard R800 syntax rules are:

  • Z80 opcode and register names.
  • tniASM Z80 rules. (Chapter 2.7.1)
  • Note that SLL has a different (undocumented) function on R800.
2.7.2 Tryb R800 i MSX

Akceptowane są wszystkie instrukcje Z80 i R800.

Różnice w stosunku do standardowej składni R800 są następujące:

  • Instrukcje Z80 i nazwy rejestrów.
  • Reguły tniASM dla Z80 (rozdział 2.7.1).
  • Uwaga: SLL posiada inną (nieudokumentowaną) funkcję na R800.
2.7.3 GBZ80 mode

Only GBZ80 opcodes and extensions are accepted.

Differences with standard GBZ80 syntax rules are:

  • LD A,(HLI) and LD A,(HLD) (and vice versa) are written LDI A,[HL] and LDD A,[HL].
  • LD A,(n) and LD A,(C) (and vice versa) are written LDH A,[n] and LDH A,[C]. Furthermore they have aliases IN A,[x] and OUT [x],A. The LDH A,[n] and LDH [n],A can take values between 0-FF and FF00-FFFF hex.
  • ADD SP,d is written LD SP,SP+d.
  • LDHL SP,d is written LD HL,SP+d.
  • tniASM Z80 rules. (Chapter 2.7.1)
2.7.3 Tryb GBZ80

Akceptowane są jedynie instrukcje GBZ80 i ich rozszerzenia.

Różnice ze standardową składnią GBZ80 są następujące:

  • LD A,(HLI) i LD A,(HLD) (i na odwrót) są zapisywane jako LDI A,[HL] i LDD A,[HL].
  • LD A,(n) i LD A,(C) (i na odwrót) są zapisywane LDH A,[n] i LDH A,[C}. Co więcej posiadają one aliasy IN A,[x] i OUT [x],A. LDS A,[n] i LDH[n],A mogą przyjmować wartości pomiędzy 0-FF i FF00-FFFF szesnastkowo.
  • ADD SP,d zapisuje się jako LD SP,SP+d.
  • LDHL SP,d zapisuje się jako LD HL,SP+d.
  • Reguły tniASM dla Z80 (rozdział 2.7.1).

Chapter 3: Other Stuff

Rozdział 3: To co pozostało

3.1 Tips and Tricks

The best tip I can give you when working with tniASM is to keep one file in which you include every other file. Sort of like a makefile without dependencies and object stuff.

; "example.asm"
; this is an example 'makefile'

fname "example.com"
org 100h
include "frontend.asm"
include "main.asm"
include "backend.asm"

fname "example.dat"
org 0
incbin "stuff.dat"
include "somesubs.asm"

3.1 Porady

Przy pracy z tniASM utrzymuj jeden plik, do którego dołączasz wszystkie inne pliki. Coś w stylu makefile, lecz bez współzależności i plików pośrednich.

; "example.asm"
; to przykładowe 'makefile'

fname "example.com"
org 100h
include "frontend.asm"
include "main.asm"
include "backend.asm"

fname "example.dat"
org 0
incbin "stuff.dat"
include "somesubs.asm"

3.2 Disclaimer

  • All trademarks are property of their respective owners.
  • tniASM is freeware and can be distributed freely as long as:
    • it is not modified
    • this file is included in the archive
    • it is not part of a commercial product
  • Use it at your own risk. The author is not responsible for any loss or damage resulting from the use or misuse of this software.

3.2 Oświadczenie

  • Wszystkie znaki handlowe należą do swoich właścicieli.
  • tniASM jest oprogramowaniem bezpłatnym i może być rozpowszechniane pod warunkiem, że:
    • nie jest modyfikowany
    • ten plik jest dołączany do archiwum
    • nie jest częścią komercyjnego produktu
  • Używaj na swoje własne ryzyko. Autor nie bierze odpowiedzialności za jakiekolwiek straty czy zniszczenia powstałe w skutek używania lub niewłaściwego używania tego oprogramowania.

 

 


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2018 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

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