ZX-IDE Tutorial for programming assembler and ZX BASIC

Any discussions related to the creation of new hardware or software for the ZX80 or ZX81
Post Reply
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

ZX81 BASIC, VAR definition in memory I

Post by PokeMon »

As previously announced you can define labels direct in memory in the VAR section instead of putting LET statements into the program. This saves time and used memory because the variables are defined already for use and has some more advantages. The disadvantage is, that the program must not be started with RUN but with GOTO instead (example GOTO 1 for the beginning). An alternative is to define the label AUTORUN: in the source in front of the desired BASIC line:

Code: Select all

    format zx81
    ;labelusenumeric
    ;LISTOFF

            // hardware options to be set and change defaults in ZX81DEF.INC
            MEMAVL     =       MEM_16K         // can be MEM_1K, MEM_2K, MEM_4K, MEM_8K, MEM_16K, MEM_32K, MEM_48K
                                               // default value is MEM_16K
            STARTMODE  EQU     SLOW_MODE       // SLOW or FAST
            DFILETYPE  EQU     AUTO            // COLLAPSED or EXPANDED or AUTO
            STARTUPMSG EQU    'CREATED WITH ZX81-IDE' // any message will be shown on screen after loading, max. 32 chars

            include 'SINCL-ZX\ZX81.INC'        // definitions of constants
    ;LISTON
            AUTOLINE 10

            REM 'VAR DEFINITION'
            LET A=99
    AUTORUN:
            PRINT A
    ;LISTOFF
            include 'SINCL-ZX\ZX81DISP.INC'          ; include D_FILE and needed memory areas
    ;LISTON
    VARS_ADDR:
            VAR A=100
            db 80h
    WORKSPACE:

    assert ($-MEMST)<MEMAVL
    // end of program
The above code shows a complete ZX81 BASIC program. There is no change for the startup section but at the end the include of ZX81POST.INC is replaced by ZX81DISP.INC which will define only a valid DFILE but no empty VAR section like the ZX81POST.INC does. Important is the following label VARS_ADDR: followed by the variables needed and the end marker $80 (or 80h) at the end followed by the label WORKSPACE:. The labels are important as they set the appropriate system variables of the ZX81 program.

Code: Select all

    009C: [40A5] 00 1E 0C 00 F1 26 14 25            LET A=99
                 25 7E 87 46 00 00 00 76   
                                            AUTORUN:
    00AC: [40B5] 00 28 03 00 F5 26 76               PRINT A
                                            ;LISTON
                                            VARS_ADDR:
    03CC: [43D5] 66 87 48 00 00 00                  VAR A=100
    03D2: [43DB] 80                                 db 80h
                                            WORKSPACE:
A numeric variable can be defined using the directive VAR, followed by the name and an (optional) initialization with "=" and the desired value. You can see a big difference in the code above between the variable definition in memory with the name just followed by the 5 byte floating point value (6 bytes only) and the definition in a LET statement in the program with 16 bytes in this example (3 times space required) through LET statement, ASCII value, line number, line length - so quite a lot overhead. When the program runs, it will create the variable additionally in the memory (var section). So regarding this the direct definition takes only a quarter of memory.

In the example above the AUTORUN: label is set in front of the PRINT A statement. This will print 100 after loading or after repeat start with GOTO 40 but the LET value 99 after RUN.

Some random definition of numeric variables here:

Code: Select all

                                            VARS_ADDR:
    03B8: [43C1] 66 87 48 00 00 00                  VAR A=100
    03BE: [43C7] 67 87 76 00 00 00                  VAR B=123
    03C4: [43CD] 68 87 B0 00 00 00                  VAR C=-88
    03CA: [43D3] 69 87 30 00 00 00                  VAR D=+88
    03D0: [43D9] 6A 00 00 00 00 00                  VAR E
    03D6: [43DF] A6 27 A8 00 00 00 00 00            VAR ABC=0
    03DE: [43E7] A7 31 26 27 31 A6 9F 0F            VAR BLABLA=12E8
                 0D 18 00                   
    03E9: [43F2] EE 84 20 00 00 00 85 20            VAR I=10,20,2,41
                 00 00 00 82 00 00 00 00   
                 29 00                     
    03FB: [4404] 80                                 db 80h
                                            WORKSPACE:
Numeric variables can consist of one or more letters, followed by a 5 byte floating point value. Several bits in the first and last letter define the type of variable (numeric, string, array). The initialization is optional, see definition of variable E which is initialized automatically with zero as the is no value given. This feature is not very useful for a single numeric variable but could save a lot of work when creating arrays.

The last definition of F is a loop variable like used in a FOR-NEXT loop. There are 4 values given, the current value, the limit, the step width and the line to jump to, usually set to the line number of FOR statement+1. If not existing BASIC jumps automatically to the next existing line number. The first 3 values are defined as floating point values and the last one as word. Take note that the line number as word is here defined as little endian as it is defined as big endian in the program listing.

The NEXT mechanism functions without a FOR statement if the variable is defined as loop variable in the var section in memory. The whole loop variable takes 18 byte in memory as there is additional 25 or 33 byte taken in the definition statement FOR depending of STEP is used or not. When the variable is created during program running you have to calculate all in all with about 50 bytes, so quite more. Additionally it is easy to manipulate this variable in the memory as well to modify the line number to jump to, the current value or similar.

Here is a funny program using NEXT without FOR: :mrgreen:

Code: Select all

            AUTOLINE 10

            REM 'FUNNY PROGRAM'
    AUTORUN:
            PRINT I
            NEXT I
    ;LISTOFF
            include 'SINCL-ZX\ZX81DISP.INC'          ; include D_FILE and needed memory areas
    ;LISTON
    VARS_ADDR:
            VAR I=10,20,2,11
            db 80h
    WORKSPACE:
Numeric arrays are defined similar. Here it is quite convenient that arrays are automatically initialized with zero but you can give other initial values as well easily.

Code: Select all

                                            VARS_ADDR:
    03AE: [43B7] 8C 17 00 01 04 00 81 00            VAR G(4)=1,2,3,4
                 00 00 00 82 00 00 00 00   
                 82 40 00 00 00 83 00 00   
                 00 00                     
    03C8: [43D1] 8D 1C 00 01 05 00 81 00            VAR H(5)=1,2
                 00 00 00 82 00 00 00 00   
                 00 00 00 00 00 00 00 00   
                 00 00 00 00 00 00 00       
    03E7: [43F0] 90 35 00 01 0A 00 00 00            VAR K(10)
                 00 00 00 00 00 00 00 00   
                 00 00 00 00 00 00 00 00   
                 00 00 00 00 00 00 00 00   
                 00 00 00 00 00 00 00 00   
                 00 00 00 00 00 00 00 00   
                 00 00 00 00 00 00 00 00   
    041F: [4428] 80                                 db 80h
                                            WORKSPACE:
Arrays can be initialized complete with G(4), partly with H(5) or not at all with K(10).

Code: Select all

    VARS_ADDR:
            VAR L(3,5)=11,12,13,14,15,\
            21,22,23,24,25,\
            31,32,33,34,35

            VAR M(1000)

            db 80h
    WORKSPACE:
The above definition shows the init of a 2-dimensional array and definition of all values. If there are many values it is helpful to break the line with "\" (backslash) and continue in the next line like shown. This is not allowed inside a string but maybe used in the same way when initializing string values. The second definition shows an easy way of occupying a lot of memory (about 5000 bytes here for M, 5 bytes per floating point).
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

ZX81 BASIC, VAR definition in memory II

Post by PokeMon »

String variables can be defined in the same way but may consist of one letter only followed by the $ sign. The same one-letter restriction is for numeric arrays and loop variabales (FOR-NEXT). Here are some typical use cases:

Code: Select all

            REM 'STRING VARS'
    AUTORUN:
            PRINT A$
            PRINT B$
            PRINT C$
            PRINT D$
            PRINT E$
            PRINT F$
            PRINT G$
    ;LISTOFF
            include 'SINCL-ZX\ZX81DISP.INC'          ; include D_FILE and needed memory areas
    ;LISTON
    VARS_ADDR:
            VAR A$
            VAR B$='SHORT TEXT'
            VAR C$='LONGER TEXT WITH SOME MORE WORDS'
            VAR D$='' // empty text
            VAR E$="TEXT WITH AUTOMATIC QUOTES"
            VAR F$='QUOTE IN TEXT '' SOMEWHERE'
            VAR G$='%'
            db 80h
    WORKSPACE:
and the listing with the output (only var section shown)

Code: Select all

                                            VARS_ADDR:
    03D6: [43DF] 46 00 00                           VAR A$
    03D9: [43E2] 47 0A 00 38 2D 34 37 39            VAR B$='SHORT TEXT'
                 00 39 2A 3D 39             
    03E6: [43EF] 48 20 00 31 34 33 2C 2A            VAR C$='LONGER TEXT WITH SOME MORE WORDS'
                 37 00 39 2A 3D 39 00 3C   
                 2E 39 2D 00 38 34 32 2A   
                 00 32 34 37 2A 00 3C 34   
                 37 29 38                   
    0409: [4412] 49 00 00                           VAR D$='' // empty text
    040C: [4415] 4A 1C 00 0B 39 2A 3D 39            VAR E$="TEXT WITH AUTOMATIC QUOTES"
                 00 3C 2E 39 2D 00 26 3A   
                 39 34 32 26 39 2E 28 00   
                 36 3A 34 39 2A 38 0B       
    042B: [4434] 4B 19 00 36 3A 34 39 2A            VAR F$='QUOTE IN TEXT '' SOMEWHERE'
                 00 2E 33 00 39 2A 3D 39   
                 00 0B 00 38 34 32 2A 3C   
                 2D 2A 37 2A               
    0447: [4450] 4C 01 00 0C                        VAR G$='%'
    044B: [4454] 80                                 db 80h
                                            WORKSPACE:
Variable A$ is an empty string variable (same like D$). Usually definition of string variables are without the quotes and placed in single quotes inside the ZX-IDE. If using double quotes the quotes will be added in the initialization which is normally unwanted. 2 following single quotes can put a single double quote sign into the string when needed. The POUND character is not available in normal ASCII code and can be used with the % character instead (G$).

In the same way as shown for numeric arrays you can define string arrays in memory:

Code: Select all

                                            VARS_ADDR:
    03D6: [43DF] D0 0B 00 01 08 00 1D 1E            VAR K$(8)='12345678'
                 1F 20 21 22 23 24         
    03E4: [43ED] D1 13 00 01 10 00 38 2D            VAR L$(16)='SHORT'
                 34 37 39 00 00 00 00 00   
                 00 00 00 00 00 00         
    03FA: [4403] D2 0B 00 01 08 00 00 00            VAR M$(8)
                 00 00 00 00 00 00         
    0408: [4411] D3 2D 00 02 0A 00 04 00            VAR N$(10,4)='ABC','DEF','GHI',\
                 26 27 28 00 29 2A 2B 00            'JKL','MNO'
                 2C 2D 2E 00 2F 30 31 00   
                 32 33 34 00 00 00 00 00   
                 00 00 00 00 00 00 00 00   
                 00 00 00 00 00 00 00 00   
    0438: [4441] 80                                 db 80h
                                            WORKSPACE:
K$ is initialized complete, L$ is shorter initialized than defined, M$ is initialized with zero (blanks) and N$ is partly initialized with 5 strings, all one byte shorter than defined. The rest is initialized with blanks (space char).

An interesting part is the use of string variables for code or data as shown here:

Code: Select all

    0074: [407D] 00 0A 0B 00 EA 28 34 29            REM 'CODE VARS'
                 2A 00 3B 26 37 38 76       
                                            AUTORUN:
    0083: [408C] 00 14 0E 00 F9 D4 1D 23            RAND USR #labels
                 1F 1F 24 7E 8F 07 74 00   
                 00 76                     
                                           
                                            ;LISTON
                                            VARS_ADDR:
    03AE: [43B7] 5F 07 00                           VAR Z$ _asm
                                            labels:
    03B1: [43BA] 2A 0C 40                           LD HL,(D_FILE)
    03B4: [43BD] 23                                 INC HL
    03B5: [43BE] CB FE                              SET 7,(HL)
    03B7: [43C0] C9                                 RET
                                                    END _asm
                                           
    03B8: [43C1] 80                                 db 80h
                                            WORKSPACE:
The above listing shows a short program in variable Z$ which is automatically executed after loading through the statement RAND USR #labels. The definition of code is slightly shorter than in a REM statement but the main advantage is to get rid off after usage by simply entering LET Z$="" if you want to keep all other variables or CLEAR or RUN if they don't needed anymore.

As long as there is no change at the program (no included/deleted/changed program lines) and as long as there is more memory than 4k present (expanded DFILE) the program doesn't have to be relocatable at all because it is not moved at all in the memory. Variables in the var section are not sorted by type or value so all code variables at the beginning are not moved when changing/adding/deleting variables in memory (var section).

There is one exception of the rule, the SCROLL statement. This does change the size of DFILE when used and will move the code, maybe temporarily. The original position may be restored by using the PRINT AT 0,0; statement or using CLS in the program. So this depends a bit on the program used with the code variables. Anyway relocatable code can be achieved easily when avoiding CALL and JP statements and using only relative jumps JR instead. In these cases the address of the first variable can be achieved easily with this BASIC expression:

PEEK 16400+256* PEEK 16401+3
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

ZX81 p-file converter

Post by PokeMon »

The latest version of ZX-IDE contains a p-file converter which can read .p or .81 files and translate them back to source (BASIC and assembly). So it is easy to analyze, change and recompile some binary programs. The disassembly feature has some remarkable issues as it can handle code only and could deliver wrong results for mixed code/data areas. So in this version and regarding disassembly I would announce this feature as experimental.

You will find a .zip file with tested programs in the next post, mainly plain BASIC but a few with assembly parts as well. After testing they will run at least in the emulator EightyOne.

Questions and feedback please not here as this is the manual to the ZX-IDE.
So please write your comments in the following thread:
viewtopic.php?f=6&t=795&start=40

For reading binaries there is an extra menu entry in the file menu (Open ZX binary):
p2a0.jpg
p2a0.jpg (15.78 KiB) Viewed 10775 times
p2a1.jpg
p2a1.jpg (52.15 KiB) Viewed 10775 times

At the beginning you can find the system variables section, followed from the BASIC program and finally the DFILE and maybe some BASIC variables:
p2a2.jpg
p2a2.jpg (57.44 KiB) Viewed 10775 times
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

ZX81 p-file converter

Post by PokeMon »

Assembly parts will be translated partly correct but only when the structure of the program follows a special scheme (first CODE in a REM line and possible DATA after the code). The mixed usage of CODE and DATA has to be interpreted with care and can deliver wrong results. I hope the assembly part will be more reliable in a future version.
p2a3.jpg
p2a3.jpg (53.63 KiB) Viewed 10776 times
Here is a zip file with a dozen tested programs, mainly consisting of plain BASIC but a few with assembly parts as well:
P2A-TEST.zip
(38.29 KiB) Downloaded 420 times
IanB
Posts: 60
Joined: Mon Jul 27, 2015 5:40 am
Location: Northampton UK

Re: ZX-IDE Tutorial for programming assembler and ZX BASIC

Post by IanB »

PokeMon, a question.

Is there any way to comment out blocks of code simply (that is, multiple lines) rather than one line at a time? I'd like to do it while testing alternate subroutines, etc, but I can't seem to find a way :(
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

Re: ZX-IDE Tutorial for programming assembler and ZX BASIC

Post by PokeMon »

Well - not really.
I didn't write the editor myself - this is only used as available.

There would be a chance to use a call to your routine which may easily to just outcomment the call but this would affect the timing.

Another way would be to put your code into a macro and include just the macro - only one line to outcomment and no different timing / stack usage because the macro is simply replaced with its code during assembly.

Code: Select all

macro routine1 {
        LD      A,A
        LD      B,B
}

macro routine2 {
        LD      C,C
        LD      D,D
}

        REM _asm
        INC     HL
;        routine1
        routine2
        RET
        END _asm

Another way would be to use conditional assembly:

Code: Select all

IF 0
        routine2
END IF
If you replace / change 0 with 1 the code will be inserted, otherwise not.
Well depends on your programming style which variant is more convenient to use for you.
Last edited by PokeMon on Sat Aug 22, 2015 4:14 pm, edited 1 time in total.
IanB
Posts: 60
Joined: Mon Jul 27, 2015 5:40 am
Location: Northampton UK

Re: ZX-IDE Tutorial for programming assembler and ZX BASIC

Post by IanB »

Okay thanks. It's not a big deal really, it would just be nice to be able to comment out a block of code so I can see more easily how many bytes I've used :)
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

Re: ZX-IDE Tutorial for programming assembler and ZX BASIC

Post by PokeMon »

Well my last proposal with IF/END IF has another advantage, you can see better what code is produced by your lines while the macro just inserts a block of (resulting) binary code.
User avatar
blittled
Posts: 229
Joined: Fri Dec 19, 2008 3:04 am
Location: Northwestern Pennsylvania, USA

Re: ZX-IDE Tutorial for programming assembler and ZX BASIC

Post by blittled »

Is INKEY$ as part of a mathematical expression supported?

I have the following line
190 LET B=B-(INKEY$="7"AND B)+(INKEY$="6" AND B<A)
and I get an error with instruction:
190 LET B=B-(INKEY$='€7' AND B]+(INKEY$='€6' AND B<A)
2X Timex Sinclair 1000, ZX81, ZX80Core, 5X 16K Ram Pack, ZXBlast, ZX P file to Ear Input Signal Converter, Elf II
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

Re: ZX-IDE Tutorial for programming assembler and ZX BASIC

Post by PokeMon »

Do you have the latest version 1.71.01p.Z80 ?
Version is displayed in the window title.
So it seems to work when I test your line.
prog.jpg
prog.jpg (62.11 KiB) Viewed 10529 times
Post Reply