|
|||||||
Zx81 assembler for beginners. |
|||||||
Assembler (or ASM), is a basic language based a several mnemonics coded in machine codes. The CPU , the Z80, is able to read sequencial instruction, and jumps to the next step... the next order. It's designed to get a byte groupe and run an internal process ... move the memory, set a register, read a port... The assembler code could be type-in using hexadecimals, decimals or characters codes.
Many Zx81 movices had typed a machine code game using POKE 16514,0 ... in the Basic editor. However, the assembler language can be compiled with a dedicated program such « Artic assembler » or « TASM ». Both able to type mnemonics instead of purs machine values. It make easy type-in and all modifications.
|
|||||||
A disassembled machine code : |
|||||||
8 or 16 Bits values: In case of 8 bits value (a byte), the CPU get a number between 0 and 255 ... in hexadecimal, 0 and FF. The hexadecimal format is prefered because of it taking 2 characters on the screen, but most of Zx81 programs, written on the Zx Printer are available in decimals... In fact, the ZX81 charcter table and the bad Zx Printer printed quality force the user to use it. Hexadecimals codes use « 8 » and « B », « D » intead of « 0 » ... it can make any confusions while running the typed in game! In case of 16 bits values (2 bytes), you had to swap both values (Byte2;Byte1) to read the correct hexadecimal value. 00h;40h (Where h is the hexadecimal tag) is read : 4000h (16384 in decimal) In the compiled code : 2A;00;40 ... 2A= « LD HL register » mnemonic and 00;40= 4000h value. But, our ZX81 is running with hexadecimals values and don't use decimals values...
|
|||||||
Mnemonics: |
|||||||
The CPU convert all codes in specifics actions. « 2A » value throw to the CPU an order to set a register or a byte located in the next codes. In machine code, it named « LD » (Load Datas) and « 2A » specify that's the HL register. Regards our example, 2A;00;40 this groupe will be note : LD HL,4000
Note: - A low case « h » could be added to tag an hexadecimal value, to make reading easy, but by default, there's nothing in Artic ASM2 - To tag a decimal value in Artic ASM2, you had to use a « + » before the value. -
All exemple in this page are written in Artic ASM2 language. - If you had to use Turbo-ASM DOS compiler, it alow to use somes of advenced mnemonics and the text chart seem different. ( Have a look to the TASM documentation to retrieve all text specifications ).
|
|||||||
Where assembled codes can be located? |
|||||||
The main Zx81 particularity is his Load and Save Basic ROM function. It able to save the Basic segment, the D_File and the variables memory segment in a single file. If you put your code above the Variables segment, it wont be Saved ! If you plan to Save your ASM codes, you had to store it in the saved memory segment. It can be placed in the Basic segment in a reseved REM line or in the Variables segment. The D_File display memory segment could be used but must by move to another location after a loading... and must be cleaned using the CLS function to retrive a conventional display. You can move your codes to the 32-48k segment, or use the 8-16k ram on a 64k ram pack from Memotech. But, the most popular location is in the Basic segment ... in a Rem line. You had to reserve a REM line to place your code. 1 REM xxxxxxxxxxxxxxx All « x » characters will be replaced with your ASM codes. If you had to choice a line number, prefer using all REM lines in the start of Basic program. The first launched code in your ASM code must be located in the same offset. In case of Basic program update, this offset could be decayed, if you had to add a line behind your code.
|
|||||||
Registers: |
|||||||
The Z80 CPU use several registers to store datas or read internals flags. But, all of them can't be set.
8 bits registers:
AF
register: is used to retrive booleans result and internal
functions. (8bits RW+ 8bits RO) This register could be used to store any temporary datas, but it's used to retrieve some datas set with ADD,SUB, XOR... In case of conditions opperationals codes, the F register will be changed.
|
|||||||
Bit location. |
Flag. |
Internal function. |
Conditions. |
||||
B7 |
fs |
Sign. |
Positive= 0 - Negative = 1 (P and N condition) |
||||
B6 |
fz |
Zero. |
OTHER =0 - Zero = 1 |
||||
B5 |
f5 |
Not used |
Internal flag used by the Z80. |
||||
B4 |
fh |
Half-Carry |
Bit3
(or 11) ->bit4 (or 12) not changed = 0 |
||||
B3 |
f3 |
Not used. |
Internal flag used by the Z80. |
||||
B2 |
fpv |
PARITY/Overflow. |
Parity
odd=0/parity Even=1 or |
||||
B1 |
fn |
Substract. |
(internal) The last instruction was a Substract or an addition ? |
||||
B0 |
fc |
Carry |
Bit7 (or 16) to a fictive Bit8 (or 17) changed =1 else =0 |
||||
NZ
- not zero ( fz=0 ) PO
- parity odd ( fpv = 0 ) P - positive ( fs = 1 )
16 Bits registers: HL:
Data register, you can set H and L separatelly (RW) as 8bits
registers. DE : Data register, you can set D and E separatelly (RW) as 8bits registers.
HL, BC and DE have got an internal buffer for each register. There values can be stored or retrived using the EXX opp. Code.(It also named « Alternat registers pairs »:HL',BC' and DE') Exemple:
LD HL,22 EXX HL=12 EXX HL=22 It avoid to use the stack, PUSH HL and POP HL. It also usefull in case of DJNZ function if BC is allready used in your codes.
IR : Data used to point to interrupts , you can set I and R separatelly (RW).(Point to Characters binary table segment or « UDG ») IX : Display register.(Horizontal-RO) IY : Display register.(Vertical-RO) SP : Internal pointer register to stacks.(RO) PC : Program Pointer program register.(Internal Pointer not available).
|
|||||||
LD function and reading rules. |
|||||||
The « LD » function is the main ASM function ! It used to retrieve, move, set a register or the memory. LD A, 10 mean ... Load Data in A register from/with 10 value. In fact, you can use « LD » and set the first argument with the secound one. In case of indirects values, the CPU get the value located in a 2 bytes address in the memory . Use : LD A,(4000) , Get the byte located in 4000h address.( in case of LD A,4000 ... LD=4000 ) Notes: With 16 bits values ... LD HL,(4000) And 4000h= 12 4001h= 13, HL = 1312h ! To set the memory, you had to use LD (4000),HL ! LD HL, 1312 ; Set HL=1312 LD (4000), HL ; Put 1312 in memory at 4000h offset. If you Peek (16384), you can see 18 (12h) and Peek (16385) = 19 (13h). In a machine code program, this inverted value is masked and don't need to be inverted. But, if you had to retrieve it in Basic, you had to type : Let address = (Peek (16383)*256)+Peek (16384)
LD HL, 1312 ; Set HL=1312 LD (4000), HL ; Put 1312 in memory at 4000h offset. LD HL,(4000) ; Get the memory 2 bytes (16bits) at 4000h offset and store it in HL. HL
= 1312h the stored value is the same in ASM! PUSH
HL store the HL value and POP HL will retrieve the
previous HL value.
|
|||||||
How to use basics mnemonics? |
|||||||
If you had to retrieve a simple data: 100+12 = Data. Note: All datas after the « ; » tag, is a remarque and isn't compiled.
LD A, 64 ; Load A register with (+)100 in decimal (+ is the decimal tag in Artic ASM2). ADD +12 ; Add 12 to A register ... in Artic Assembler you can't use « ADD C » ( but prefere « 0C » (Zero+C)), ; C will be the C register while the ASM2 compilation. You had to write +12 in decimal or 0C !
« A » register will be set to 70h (+112).
LD A, FF ; Load A register with (+)255 in decimal. XOR FF ; Will change A register with the exclusive OR function. 'A' register will be set to 00h (+0).
In those both exemples, the A register will be changed and can't be retrived.
With paire registers HL,BC and DE (2Bytes and 16Bits), you had to choose specifics functions... 1000h +1200h = Data LD HL, 1000 ; HL=1000 LD BC, 1200 ; BC=1200 ADD HL, BC ; ADD HL and BC. HL = 2200h
1200h - 1000h = Data LD HL, 1200 ; HL=1200 LD BC, 1000 ; BC=1000 SBC HL, BC ; ADD HL and BC. HL = 200h In those both exemples, you had to use the HL register and can't be used as a value storage.
|
|||||||
Advanced ASM function: |
|||||||
LDIR exemple. In the mnemonics functions list, many function called, use register as temporary values or throw values in registers. LDIR is a memory function used to copy or move a memory segment (screen copy, scrowlings or datas copy), This function is very fast and take less memory than an ASM counter using LD to move the memory. To move a memory segment located at HL to DE offset... LD HL, 5000 ; HL= 5000h (source) LD DE, 4000 ; DE= 4000h (destination) LD BC, +1024 ; BC= 1024 (lenght of the segment) LDIR ; Copy the memory segment (lenght 1024Bytes), how start at 4000h. RET ; Return to the CALL ASM function or exit to the Basic. This function will get each byte HL + n , move it to DE + n , and will repeat this action untile BC=1 (0 after the LDIR function) Else, n will be increase: n = n +1 and will continue to copy each byte.
LDDR is the same function, but n is decease: n = n -1
|
|||||||
How to use Artic Assembler ? |
|||||||
ZX-Assembler-2 by Artic Computing LTD The text source code is stored in « line 2 », the object code(ASM) in « line 1 ». The main program is located in « line 0 ».
Type LET OC=USR 16516 or RAND USR 16516 to launch the Artic Assembler program. Type « E » to go to the inboad editor (shift+Q to quit) In the main menu, type « Q » (twice) to exit to basic editor. If the LET command is used, it return the OC value of the start address of the object code. If an ORG directive is added using ORG +16516, it able to get proper jumps address in the futur `1 REM' code. It allow to get propers address jump when it moved to another start address. Use COPY (A in the main menu) option to perform the assembly. Erase lines 0 and 2, and save your code. Note: The line 1 can't be start at 16514, There's a line header/footer !. Your code must start at 16516.
Keys:
> Shift+ A ( Label. ) > Shift+ S ( Find. ) > Shift+ T ( Page Up. ) > Shift+ 0 ( Delete char. ) > Shift+ 9 ( Insert char. ) > Shift+ E ( Insert line. ) > Shift+ D ( Delete line. ) > Shift+ G (Printer ouput?) > Shift+ Q ( Undo. )
> J ( Jump to ) > L ( Move Down ) > P,O ( Move Up ) > Q ( Quit viewer ) > R ( Repeat entry )
Most options can be quitted by pressing « Q » or « Shift-Q » exit assembler by pressing « Q » twice at main prompt
Note: - Hexadecimals values are set by default, add a « + » before your value if decimal. - « ; » is used to add a comment.
Constants: @DFILE=+16396 ; Set a Decimal value @START ; is an address label. (@ is the « shift+A » function in the ASM2 editor, used for labels and constants settings)
Direct assembly datas:
1C 1D ; Will be directly assembled. "0" "2";...(add a space) +28 +29 ;...(add a space) "02" ; ...
Click here to download my « Edit+ » Plug-in to parse ASM2 functions.
|
|||||||
How to use inboard « Artic Assembler 2 » in Vb81 XuR: |
|||||||
It was code to wrap windows facilities to this ZX81 program. You can use an external assembler like TASM, or other Dos program, but ASM2 is more suitable for a ZX81's emulator !
|
|||||||
-Open VB81 XuR, the tool-bar if it closed. -Click on then « ASM » button. And « Ok » A new window will be displayed... (...) |
|
||||||
-
Click on the « 3 »
button to create a new line 2. Now, return to the emulator and type R ... « RUN » and « New/Line » to validate. Under ASM2, type « E »... All text box is now in the ASM2!
Press
« shift-Q »(twice) to return to the main
menu.
Return
to the right window and click on « 4 »
caption. You can also type-in « Save ''MyAsm'' » and alls codes and mnemonics will be saved.
Now,
erase the text box, and type the « 7 »
caption. The « 1 » and « 5 » are used to load or save the text box content.
The
« 6 »
caption is used to unlock all REMs line to delete the 0 and 2
lines. |
|
||||||
Include a binary file in a REM Line:
If
you have saved the binary code under a « BIN »
extension, using the « 4 »
caption...
Go
to the « Tools » popup, and select « Memory
Tools »
Select
the proper « BIN » file, saved using the
« 4 »
caption.
Notes: |
|
||||||
|
|||||||
Mains ZX81 variables: |
|||||||
D_File: The floating display memory segment. (after the tokenized Basic program) Vars: The floating variables memory segment. (after the tokenized display memory segment) Keyboard: The keyboard statement.
All address values are placed in a reseved memory segment, used by the Basic editor and the Rom feature. It's a ram memory placed beatween 4000h and 4082h, start of Basic memory.
|
|||||||
Mains Roms routines: |
|||||||
In machine codes, to reduce the memory used in your codes, you can jump to the rom, to execute some existing subroutines. You can use a CALL function, but, if this function isn't stand alone you had to set HL,DE,BC etc... The Rom disassembly allow to retrieve many address entry : Click here to download the Rom's subroutines address list. TASM include file : ZX81.sym (symbol address list)
Just add: #include « ZX81.sym » In your TASM mnemonics file.
Exemple: ; Plot a pixel at 1,1 on the screen. #include « ZX81.sym » ; Load les symbol file. LD
BC, $0101 ; Set location variables : X=1 Y=1 RET ; Return to Basic.
|
|||||||
Z80 INSTRUCTIONS AND ADDRESSING MODES |
|||||||
|
|||||||
SYMBOLIC
DESCRIPTION |
COND =
Condition |
||||||
OPCODE |
OPERAND DESCRIPTION |
||||||
ADC
A,DATA-8 |
Add
immediate with carry to accumulator |
||||||
ADD
A,DATA-8 |
Add
immediate to accumulator |
||||||
AND
DATA-8 |
And
immediate with accumulator |
||||||
BIT
BIT,REG |
Test
BIT in register |
||||||
CALL
ADDR |
Call
the routine at ADDR |
||||||
CCF |
Complement carry flag |
||||||
CP
DATA-8 |
Compare
immediate data with accumulator |
||||||
CPD |
Compare
accumulator with memory and |
||||||
CPDR |
Compare
accumulator with memory and |
||||||
CPI |
Compare
accumulator with memory and |
||||||
CPIR |
Compare
accumulator with memory and |
||||||
CPL |
Complement the accumulator |
||||||
DAA |
Decimal adjust accumulator |
||||||
DEC
REG |
Decrement register contents |
||||||
DI |
Disable interrupts |
||||||
DJNZ DISP |
Decrement reg B and jump relative if zero |
||||||
EI |
Enable interrupts |
||||||
EX
AF,AF' |
Exchange
program status and alt program stat |
||||||
EXX |
Exchange register pairs and alt reg pairs |
||||||
HALT |
Program execution stops |
||||||
IM
0 |
Interrupt
mode 0 |
||||||
IN A,PORT |
Input port to accumulator |
||||||
INC
REG |
Increment
contents of register |
||||||
IND |
Input to memory and decrement pointer |
||||||
INDR |
Input
to memory and decrement pointer until |
||||||
INI |
Input to memory and increment pointer |
||||||
INIR |
Input
to memory and increment pointer until |
||||||
IN REG,(PORT) |
Input to register |
||||||
JP
ADDR |
Jump
to location |
||||||
JR
DISP |
Jump
relative |
||||||
LD
A,I |
Move
interrupt vector contents to accumulator |
||||||
LDD |
Transfer
data between memory and decrement |
||||||
LDDR |
Transfer
data between memory until byte |
||||||
LDI |
Transfer
data between memory and increment |
||||||
LDIR |
Transfer
data between memory until byte |
||||||
NEG |
Negate contents of accumulator |
||||||
NOP |
No operation |
||||||
OR
DATA-8 |
Or
immediate with accumulator |
||||||
OUT (PORT),REG |
Output from register |
||||||
OUTD |
Output from memory, decrement address |
||||||
OTDR |
Output
from memory, decrement address |
||||||
OUTI |
Output from memory, increment address |
||||||
OTIR |
Output
from memory, increment address |
||||||
OUT PORT,A |
Output from accumulator |
||||||
POP
RP |
Load
register pair from top of stack |
||||||
PUSH
RP |
Store
resister pair on top of stack |
||||||
RES
BIT,REG |
Reset
register bit |
||||||
RET |
Return
from subroutine |
||||||
RETI |
Return from interrupt |
||||||
RETN |
Return from non-maskable interrupt |
||||||
RL
REG |
Rotate
left through carry register contents |
||||||
RLA |
Rotate left through carry accumulator |
||||||
RLC
REG |
Rotate
left branch carry register contents |
||||||
RLCA |
Rotate left accumulator |
||||||
RLD |
Rotate
one BCD digit left between the |
||||||
RR
REG |
Rotate
right through carry register contents |
||||||
RRA |
Rotate right through carry accumulator |
||||||
RRC
REG |
Rotate
right branch carry register contents |
||||||
RRCA |
Rotate right branch carry accumulator |
||||||
RRD |
Rotate
one BCD digit right between the |
||||||
RST |
Restart |
||||||
SBC
A,DATA-8 |
Subtract
data from A with borrow |
||||||
SCF |
Set carry flag |
||||||
SET
BIT,REG |
Set
register bit |
||||||
SLA
REG |
Shift
register left arithmetic |
||||||
SRA
REG |
Shift
register right arithmetic |
||||||
SRL
REG |
Shift
register right logical |
||||||
SUB
DATA-8 |
Subtract
immediate from accumulator |
||||||
XOR
DATA-8 |
Exclusive
or immediate with accumulator |
||||||
|
|
||||||
|