B I A S ======= BASIC INTERACTIVE ASSEMBLER SYSTEM  By I.M. Collier 1986 Disclaimer: This was written in 1986. My views on typography, grammar, and so on may have changed since then... This software is offered free of charge and, while I hope it is useful and free from defects, I cannot make any guarantees regarding its fitness for any purpose. Claimer: Permission is granted to use this software free of charge for any purpose for which it was designed. Copyright of any code assembled with this program remains with the author of the source code. This assembler is nevertheless copyright, and must not be distributed in any form except that in which it was supplied without my written permission. imc@prg.ox.ac.uk. User Instructions BIAS is an assembler which interacts with the BASIC system of the ZX Spectrum in the same way as the BBC Micro's built-in assembler. This gives BIAS a number of unique facilities, in addition to the numerous non-standard features of the assembler. BIAS is a block of code 8984 bytes long which can be loaded into RAM at any address, which is achieved by designating any address 'start' and typing CLEAR start-1 (or any convenient lower address) followed by LOAD "BIAS" CODE start. To initialise the code, type RANDOMIZE USR start which will locate the code and set up the machine stack and input routine. Once this has been done, the assembler only occupies the 8256 bytes following (start+42) and subsequent initialisation is carried out by RANDOMIZE USR (start+42). The assembler intercepts syntax errors by means of the machine stack. Since certain actions clear the stack, it is sometimes necessary to re-initialise. This often occurs after CLEAR, RUN, NEW and also, if an Interface I is connected, when one of the Interface's own errors is generated. Therefore, you must put RANDOMIZE USR (start+42) at the beginning of any program using this assembler. If, at any time, a syntax error is generated by a correct BIAS instruction, this indicates that the machine stack needs to be re-initialised as above. In order to 'switch off' BIAS, enter the command +O . This is necessary before loading code which overwrites the assembler; doing so without using "+O" will almost certainly crash the machine unless a NEW has been carried out. (even after CLEAR, this is still the case). In order to make entry of programs easier, BASIC utilities are provided by BIAS. These are: AUTO: To use the auto numbering facility, type +A followed by the start line number, a comma and the step, if required. Numbers which you do not specify will default to 10. A program listing will be displayed and the first auto number will appear at the bottom of the screen. As program lines are entered, listings are displayed as normal, and auto numbers continue to appear at the bottom of the screen. However the current line cursor will not be on the last line entered, but instead it will be on the line with the same line number as the line currently being entered, if it already exists in the program. If the auto number is deleted and replaced by a different number, auto numbering will continue from the new line number. To stop the auto numbers, delete the auto number and press ENTER. RENUMBER: To use the renumbering facility, type +R followed by the required parameters. There are two options; renumber the whole program to start with line number X and line step Y, or renumber the program from line X onwards to start with line number Z and line step Y. The second option allows gaps to be placed in programs for extra lines of code. If the second option is required, type a comma after +R. Then, in either case, type X and Y separated by a comma. If either number is not specified, it will default to 10. If Z is required, it is placed after a comma following Y (otherwise, where necessary, Z defaults to the same as X). On entering this command, there will be a short delay, and the program will then have been renumbered, unless either the step given was zero, or the highest line number would have been too high (more than 16383:- although only lines up to 9999 may be typed in, lines up to 16383 are correctly handled by the interpreter, although line numbers over 9999 are displayed as a non-numeric character and three digits), in which case a parameter error will result before the program is changed. The line numbers will have changed to the values required, and also all GO TOs, GO SUBs, RUNs, RESTOREs, SAVE... LINEs, LISTs and LLISTs will have been renumbered. RUN and RESTORE may occur without a line number, in which case they are ignored. Otherwise, all these cases are treated the same. A large number of contingencies are catered for in this extensive renumber routine, and they follow (GO TO is used as an example): In a straightforward "GO TO number", the number will have been changed without any problems. Line numbers which do not exist are renumbered to the next existing line number (just as the Spectrum's BASIC interpreter treats them). If any GO TO points past the end of the program, then it is renumbered to one line after the last new line number. If the "GO TO number" is followed by more characters which are part of the same BASIC statement and not "*(", then the first number is renumbered and "PLEASE CHECK LINE X" is displayed, where X is the new number of the line in question. In a "GO TO number*(" case, then the line is assumed to be of the form "GO TO A*(condition 1)+B*(condition 2)+..." and each of A, B etc. are renumbered. The conditions are passed over by the renumber routine and can contain any expression, including sub-parenthesised expressions. If the GO TO line varies from the above format, then renumbering of this GO TO is terminated at the first variation and "PLEASE CHECK LINE X" is displayed. If GO TO is followed by a bracket, a number and "AND", then it is assumed to be of the form "GO TO (A AND condition 1)+(B AND condition 2)+..." and each of A, B etc are renumbered. As with the last case, the conditions are passed over by the renumber routine, and variations from the above format cause termination of renumbering for this GO TO and the message as before. If GO TO is followed by a letter then it is assumed to be a variable and if there is a LET statement at the start of this line whose assignment variable starts with the same letter then the expression after the "=" is treated as if it followed the GO TO and is renumbered according to the above rules. This allows for lines such as LET A=40*(A$="Y")+70*(A$="N" ):IF A THEN GO TO A , which is a conditional GO TO defaulting to the next line. In this case, the LET statement is renumbered as if it were a GO TO according to the above rules. However if the name of a variable occurs in the LET statement in place of a line number, the message "UNABLE TO RENUMBER LINE X" or "TWO VARIABLES IN LINE X" is displayed, depending on its position. In all other cases the message "UNABLE TO RENUMBER LINE X" is displayed. GO TOs in REM statements are also renumbered, thus allowing for the possibility of, for example, "REM display screen:GO SUB 230" to serve as reminders or pointers for the programmer. Although this routine cannot renumber a straightforward "GO TO A", where A depends on a different part of the program, this can be remedied by placing all assignments to "A" at the beginnings of lines, followed by "REM GO TO A" because the routine will renumber the "REM GO TO A" by changing the LET statement. This renumber routine, although its advanced features are unlikely to be required by a pure machine code program, is included for the mixed BASIC and machine code programmer. CALL: You may test machine code routines by typing +C followed by the address of the routine (you may use hex in this command - see Extended Expression Evaluator). This will execute the code just as RANDOMIZE USR. However if you define BASIC variables with the same name as register pairs (e.g. HL or IX, or AF1 with "1" to represent "'") - PC and SP are excluded - the appropriate registers will be given these values before executing the code. If the variables are not defined, zero is used, except IY (normally 23610). Also, any of these variables which are in memory will be changed to the values of the registers at the end of the routine after the code has been executed. The Call command therefore gives a much better test facility than RANDOMIZE USR. PRINT: this is the other BASIC command. To print any hex number or numbers - or any expression(s) using the Extended Expression Evaluator (see later) - use +P followed by the expression or expressions separated by commas. The numbers will be displayed with spaces between. The use of this command is to allow the programmer to calculate numbers using the Extended Expression Evaluator, which is used in all cases where the assembler expects a number, but unfortunately cannot be used in normal BASIC. The most frequent use of +P will probably to display hex numbers in decimal. EXTENDED EXPRESSION EVALUATOR: Because BASIC calculations are limited, features have been added. However, because Spectrum BASIC was not designed for extension, the new features may only be used in assembler commands (excluding +A and +R). Also, the new operators, which have the lowest priority, may not be used inside brackets. The new operators are &, |, !, % and ?. &, | and ! perform logical AND, OR and XOR operations respectively. % is for integer division and ? is a MOD operation. The evaluator will allow single character strings to be used, as long as they start the expression (For example CP "," and DEFB "?"+$80 generate code FE2C and BF respectively but in e.g. LD A,CODE a$ and DEFW 256*CODE "R" the token CODE is needed). Probably the most useful addition to the evaluator is the "$" symbol which is used to denote hexadecimal numbers. Hex numbers may be placed anywhere instead of decimal numbers, and are obtained by typing "$" followed by the hex (without spaces until after the last hex digit, where spaces are optional). In the above BASIC commands, and in hex, as also in assembly language, upper and lower case letters may be interchanged without any effect. USING THE ASSEMBLER To change from BASIC into assembly language, the "[" symbol is used. It may be on its own or at the start, end, or middle of a line (in which case a colon is needed before, but not after, the bracket). This indicates that all further statements are in assembly language. As with BASIC, the assembler mnemonics may be placed one to a line, or more than one to a line, separated by colons. When the "]" symbol is reached, control is passed back to the BASIC interpreter. If BASIC statements are placed after this in the same line, then a colon is required after it, however it may follow most assembler mnemonics without a colon. When the "[" symbol is reached, the BASIC variable P must have been defined as this is used to govern where to assemble the machine code. If it is not defined, then the report "Variable not found" is created, regardless of any other conditions. P is then updated every time anything is assembled, so it always points to the start of the instruction currently being assembled (or next to be assembled if BASIC is in operation at the time). The assembler is capable of two pass assembly (even three or more pass if this is useful) by the use of a FOR - NEXT statement placed around the code to be assembled. The "OPT" assembly directive is usually placed at the start of each pass, where it selects the following options by means of a numeric expression placed after OPT. The bits, when set, of this number have the following meanings: Bit 0: enable assembler errors (signifies 'Pass 0 or 1') Bit 1: enable listing Bit 2: use 'remote assembly' Bit 3: enable printer Bit 4: assembler errors, when enabled, halt the program. Bit 5: do not produce any code The OPTion number should therefore be in the range 0 to 63. An error is produced if this is not the case. Explanations of the above follow: Assembler errors are errors such as "Variable not found" and "Integer out of range" which will inevitably occur on the first pass of assembly and may be eliminated before the second pass. Note that a machine code routine must never be run until it has been assembled with the errors enabled, because false code may have been produced which will cause the system to crash. Usually a FOR-NEXT control variable will be used to assemble the code twice; pass 0 with bit 0 reset, and pass 1 with bit 0 set. The listing consists of the address of each instruction in hex, the first four (or less) bytes of the code produced by it in hex, and the instruction itself. If remote assembly is implemented, the code will be assembled to run at P, but will be stored at O, which is another BASIC variable. If this is not defined when the remote assembly option is selected, then the "Variable not found" error will result. If the printer is selected, all output (if any) will be directed to stream 3, which is usually the ZX Printer but may be changed according to the printer interface instructions (for example OPEN # with the Interface I). The listing option uses TAB control characters and also monitors the print position via the system variables in order to detect when a new line is required. Therefore the program is compatible with the ZX printer. If a serial printer is used with the Interface I, the program will work providing the Interface is not Version 1.0. A way of deciding whether this is the case is to print PEEK 23729 after opening the "T" channel. The answer should be 80 (printing width - which can be change if desired). If this is not the case then the listing will be displayed without spaces and may be unreadable. If a printer with support software is being used, it is probable that TABs will be used but the print position is not stored in 23728 with the width in 23729 (the relevant instructions may give details). If TABs are supported by the software, then the listing should print out correctly, however if the print position is not stored as above, set up a false position by poking 70 and 80 into 23728 and 23729. This means that if an instruction overflows on to the next line, it will continue at the beginning of the line instead of being indented. If bit 4 of the option is set during pass 1 then any assembler errors which occur will be reported as with normal BASIC errors, and execution of the program will halt. If this bit is reset however, then the error message will be printed on the 'main' screen and execution will continue. If the listing is being displayed on the screen then assembly will pause for one second after the message is displayed. If bit 5 of the option is set, then although all actions will be performed as normal, the code will not be stored in memory (and therefore no "Overwriting xxx" messages will occur). The variable P (and also O if bit 2 of the option is set) is still required, however. An OPT instruction usually follows each "[". If no OPT instruction follows, then OPT 3 is always assumed (enable listing and assembler errors). Having entered assembly mode and selected the correct options, you may type in the Z80 assembler mnemonics which are to be assembled, in upper or lower case letters (if a listing is produced, then the instruction is always displayed in capitals, but the operands etc. are displayed exactly as they are in the program). The input routine set up by BIAS should make entry easier by causing an L cursor rather than a K cursor to appear after the "[" has been typed; and the K cursor reappears after the "]" has been typed. Occasionally the cursor may be in the wrong mode. To get into "L" or "C" mode, type "[" and press ENTER and to get into "K" mode, type "[]" and press ENTER. This should work whether or not it makes correct syntax. When lines of assembly language are entered, they are checked for syntax and any errors are reported by the flashing "?" as in BASIC. If the error marker is at the beginning of a statement, it is usually the instruction which cannot be recognised; if it is between the instruction and the operands, it is the operands which are at fault. Extremely occasionally it is possible that the computer locks up after a syntax error but pressing CAPS SHIFT and SPACE together remedies the situation. Assembler directives available are as follows: Comments are inserted into the program by placing a semicolon either at the start of the statement, or, in most cases, directly after a mnemonic. The comment ends at the next carriage return, colon, or "]", and will be displayed in the listing in the 'operands' field. Labels are defined by typing a full stop at the beginning of a statement, followed by the name of the label and then a space, a colon or a carriage return. Labels are just BASIC variables and so the same rules apply - that is a label can be any number of characters long but may only consist of alphanumeric characters, and may only start with a letter. Also, since a space is used to mark the end of a label, spaces are not allowed within the name. After a label has been defined, it may be referred to by any BASIC or assembler command. Therefore the value of any label may be found individually by using PRINT label - a facility which is rarely found elsewhere. Note that if you define a label which has the same name as a register or condition (e.g. HL or NZ), you will experience difficulty in entering instructions using these labels (e.g. JP NZ usually requires an address after it). However, these labels may be used simply by preceeding them with a + sign (e.g. JP +NZ or LD HL,+DE). The variables P and O should not be defined as labels, since they have special uses (but O may be used as a label if remote assembly is not being used). The assembler will not usually report an error if O or P is used in this way. The instruction DEFB allows a specified byte or bytes to be placed in memory at P. The byte(s) must follow the DEFB, separated by commas. A maximum of 127 bytes may follow the DEFB. The instruction DEFW allows a specified word or words (2 bytes each) to be placed in memory. This works like DEFB except that each number specified will be stored as two bytes, least significant byte first. A maximum of 126 bytes may be inserted using one DEFW - 63 words. The instruction DEFM allows a specified message or string to be placed in memory. Only one string may follow, which must be less than 128 bytes long. Each character of the string is inserted in memory as its normal Spectrum code; no end marker etc. is inserted. The instruction DEFS allows a space to be left in the machine code. Any number may follow; this will simply be added to P and the spare bytes in the space will be left unaltered. In order to make entry easier, some of the Z80 assemler mnemonics have been given alternative formats as follows. EX HL,DE; EX HL,(SP); EX IX,(SP) and EX IY,(SP) are available as well as the normal instructions EX DE,HL; EX (SP),HL etc. and mean exactly the same. ADD s; ADC s and SBC s have been added; they mean the same as ADD A,s; ADC A,s and SBC A,s. When using relative jumps, the destination may be specified as usual. However, the displacement may be specified instead, for example JR 1 will produce the code 1801, which is the same as JR label:INC A:.label . Destinations and displacements will usually be easily distinguishable, as the former will usually be two bytes and the latter 7 bits. However when writing programs for ROMS, or for the memory from 65408 upwards, the destination may also be a 7-bit signed integer. In this case, the possibility of the number being a destination is always considered first. In addition to the above extensions, this assembler has allowed for the use of IX and IY as pairs of one-byte registers. The high bytes if IX and IY are referred to as XH and YH respectively, and the low bytes as XL and YL. The Z80 allows these extra registers to be used in place of H and L in most of the instructions involving H and L. However IX, IY and HL may never be mixed in these instructions, and these extra registers may not be shifted, rotated, or dealt with in BIT, SET or RES instructions. All the assembler mnemonics and directives may be used with or without spaces, except in cases where a space must be used to separate parts of the instruction, for example RRC C and RRC, or LDD and LD DE,5. In most cases, if a space is necessary and missing, a syntax marker will be displayed at the end of the instruction which was found by the assembler, for example in LDD?E,5 the LDD was found by the assembler and the syntax marker is after this. However beware of using CPL instead of CP L, RRD instead of RR D or RLD insead of RL D - these errors can not be detected. A feature of BIAS which is extremely rare is the use of calculator instructions. The floating point calculator is described in "The Complete Spectrum ROM disassembly" on page 192 and is probably of little use to programmers without this excellent aid. However, a list of the calculator instructions can be obtained by typing: LET S=start+6889: LET B=PEEK S: POKE S,128: FOR A=0 TO 65: PRINT : LET AF=A*256: LET DE=S:+C$C0A: NEXT A: POKE S,B (where start is the address at which BIAS was loaded in, not the modified re-initialisation address). If you do this and you also have the Disassembly (page 191), then you will find that the instructions used are exactly how they appear in the Disassembly, without spaces or hyphens, except that dec-jr-nz has been shortened to dejrnz. If you type in a calculator instruction in full, it will be recognised and assembled. However most of these instructions are long, so the following abbreviations may be used: Firstly, typing the first three or more letters followed by a space, colon or carriage return will do. If the characters you type may refer to more than one instruction, the instruction with the lowest code will be assumed (therefore "jump" must always be typed in full followed by the destination or displacement, without a space, otherwise "jumptrue" will be obtained). Also, "stk" and "stkdata", although they are actually different instructions, may be interchanged. Therefore "stk" does not need a space after it. Secondly, for the functions which are also BASIC functions (such as "+" or "*", or "STR$" or "EXP" etc. - also "?" is used to mean "nmodm", though the actual function of this routine is to place a quotient and a remainder on the stack, rather than a modulo value), the single character or keyword may be inserted. In the case of string comparison and addition, a "$" is placed before the symbol required, so ">" means "no-grtr" but "$>" means "str-grtr". For the USR function, USR means usr-no and USR $ means usr-$. For greater0 and less0, use ">0" and "<0". Seven of the calculator instructions have names of which the first letters are a Z80 instruction (EXchange, ADDition, SUBtract, OR, NEGate, REStack, DIvision) so a small amount of care is needed with these instructions. If they are typed as correct Z80 instructions, they will be accepted as such unless the instruction name is followed immediately (no space) by a string of letters, otherwise they are checked against calculator instructions (so ADDA, DI and ADD ition are acceptable Z80 instructions; EXHL, DI VIDE and ADDtotal are unacceptable, and EXC, addition and SUB are calculator instructions. Note that "negate" must have at least four letters to distinguish from NEG). If an error is produced in one of these instructions it may be possible for the syntax marker to appear at the start of the statement, rather than after the instruction name. In any case, if a listing is produced, the full name of the calculator instruction is displayed in lower case letters. Note that, due to the way the ROM works, the "val" and "val$" operations and all comparisons except >0 and <0 do not work unless the B register holds the code for the instruction when the preceeding RST 40 instruction is encountered (this could be done with DEFB 6:val$). If any of the "jump" type instructions are used, they must be followed with a number, which, as in the case of "JR", may be either a destination or a displacement. However, in this case, when calculating displacements, the base address is not the start of the next instruction, as with JR, but the address of the displacement byte. Hence "jumptrue 2" will miss out one calculator instruction. If "stk" is used, it must be followed by a number which will be placed onto the calculator stack when the machine code is executed. This number may either be one of the constants zero, one, half, pi/2 or ten (typed in as shown in full in upper or lower case letters), or it may be a different number. In the first case, the instruction "stk" will be used and is one byte long. In the second case, the instruction "stkdata" is used and is three to six bytes long. The last four instructions in the list are "multi-purpose" instructions. This means that the same instruction name is used with a variety of parameters, but the operation code is only one byte. "stk" is one of these. Also there is "getmem","stmem" and "series". "getmem" and "stmem" must be followed by a number between 0 and 5, but "series" must be followed by, firstly, an integer less than 16, and secondly, a number of floating point parameters; the number of these is given by the first integer. These must all follow the "series" instruction, separated by commas. When using the calculator, the instructions should be preceded by "RST 40", unless they are jumped to by another calculator routine. Therefore if any calculator instructions are found without an RST 40, the message "No RST 40" with the line and statement numbers is displayed on the screenas a warning. This error does not stop assembly, but if a listing is currently being displayed on the screen, then assembly will pause for one second before continuing. Similarly, at the end of a list of calculator instructions, there must be an "endcalc" before returning to Z80 mnemonics. If this is not present, the message "No endcalc" with the line and statement numbers is displayed on the screen. This error does not stop assembly but may pause it as in the last case. The two messages mentioned above serve as information only; they are not always errors, and the program need not be changed if it is correct. Assembler Errors: There follows a list of the errors created by this assembler. The errors which are followed by (*) may be controlled by the OPT statement (that is, they may be suppressed altogether or made to be displayed without halting assembly): Instruction too long: This is used in DEFB, DEFW and DEFM to indicate that too many bytes are being dealt with in one instruction. Integer out of range (*): This indicates that an integer is not in the correct range. It may occur when O or P do not point to valid addresses (in this case assembly halts regardless of the option number). It is particularly common with relative jumps. Also, if reported during an RST instruction, it may signify that the operand does not divide by 8. Invalid Operand: This indicates that an instruction has operands which do not form a correct Z80 mnemonic. The error will be very rare, since it is reported by a syntax marker on entry. Label defined twice (*): This indicates that a label has been defined at two different positions in memory. It may occur if the two passes of assembly start at different addresses, or if the label occurs more than once in a program. The line and statement numbers given are of the first occurrence if the error occurs during the second (or subsequent) pass of assembly. No RST 40 and No endcalc : These are reported when calculator instructions are not preceeded by "RST 40" or followed by "endcalc". They do not stop assembly, and may only occur when assembler errors have been enabled. Not enough parameters: This error is created when there are not enough parameters in a "series" instruction. Overwriting assembler, Overwriting BASIC and Overwriting stack : These errors occur when the instruction currently being assembled is trying to occupy the same space as BIAS, or the BASIC system, or the machine stack, respectively. Parameter error: This occurs on entering the +R renumber command, if the step given is zero, or if the highest line number would be more than 16383. The program remains unchanged. Variable not found (*): This error occurs when either of O and P is not defined when required (O is only required for remote assembly); in this case, the error halts assembly, regardless of options. The error is more often created when an undefined label is referred to and assembler errors have been enabled. General Use: As with BASIC, the BREAK key is enabled, so assembly can be halted at any time using it. However CONTINUE may not be used because the BASIC interpreter will be in operation. Therefore, assembly needs to be re-started by running the program again, or by using GO TO to start the current pass again. If a listing is being produced on the screen, it will be scrolled without halting, unless a key is pressed. In this case, the listing will be halted until another key is pressed, when it will continue. Because the assembler interacts with BASIC, loop assembly and conditional assembly is possible, for example INPUT A$: IF A$="Y" THEN [OPT 1:LD HL,150:LD DE,10:CALL &3B5] will place a short beep into the program if the user requires it. An example of loop assembly is INPUT A$: FOR X=1 TO LEN A$: [OPT 1:LD A,CODE A$(X):RST 16]: NEXT X which is a loop to print each character of the required string. Also, in fairly short machine code programs with BASIC support, the assembly language may be included in the program, and as well as the ability to adjust the machine code according to the user's needs, the program has the advantage that all the execution addresses are stored in variables and do not have to be remembered. For very short and simple routines, the instructions do not have to be stored in a program. Because BIAS interacts with BASIC, the instructions may be typed in as direct commands and will be assembled immediately. However every line of direct-entry instructions needs a separate "[" to preceed it. This method is unsuitable for defining labels, since the creation of a variable moves the edit line containing the instructions. This facility may also be used as a hex-printing facility, which otherwise is not provided. As long as P is not being used, you may simply type LET P=value:[OPT3] and the hexadecimal equivalent of P will be displayed on the screen. Every care has been taken to ensure the accuracy of this document and the accompanying program. If any errors should come to light, I would be pleased to hear of them.