This file contains the 3 .DOC files: AZM-COM.DOC PHASE.DOC MAC-AZM.DOC ****************************************************************************** AZM-COM.DOC The Z80MR creates two output files from an assembly language source file with a .AZM extention. The assembly language file can be any combination of Z80 mnemonics or assembler commands and pseudo-ops in the correct form as described in the file Z80MR.DOC. Each line of the source program is seperated by a carriage return linefeed and can be created on any editor. The object file is the executable machine code represented by ascii characters in a form known as Intel Hex Format. The object file will have the same filename as the source file but will have a .HEX extension. This format must be translated into the actual machine executable binary values in order to be run on a computer. This can be done with LOAD.COM or DDT.COM which came with your CP/M. (described later). Many EPROM burners only accept the program to be burned in Hex format. The listing file is the other ouput by the assembler. It is ascii representation of the code actually produced by the assembler. There are many options available for controlling what is included in the listing file as described in Z80MR.DOC. The listing file will have the same filename as the source file but will have a .PRN extension. Invoking the Assembler..................................................... To assemble the file TEST.AZM the following command can be used: Z80MR TEST The assembler will read in the file TEST.AZM from the current drive and create an object file TEST.HEX and a listing file TEST.PRN. Controlling Output......................................................... Either of the output files (or both) may be omitted. In addition, the files may be located on different drives. Since the source file must always have a .AZM extention we may use the extention to control the output. Z80MR TEST.sol The letter in the 's' position tells the assembler where to find the source file and may be any disk drive letter from A to D. The letter in the 'o' position tells the assembler where to locate the object file and may be A-D as above or Z if the file is not desired. The letter in the 'l' position tells the assembler what to do with the listing file. It can be any drive letter from A-D or Z if the file is not desired. In addition the listing file may be sent to the console if an X is in that position. Loading Hex Files.......................................................... The object file must be translated from Hex format to machine executable values by another program. If the program was written to ORG at 100H the program may be loaded with the CP/M utility LOAD.COM. All of the programs on this disk with a .COM extension are ORGed at 100H. This is where CPM loads its transient programs. To load TEST.HEX with LOAD.COM just enter LOAD TEST The extention of .HEX is assumed. If you have a program that does not ORG at 100H you must use DDT to read in the .HEX file which it does by translating the Hex format to binary as it reads it into memory. Once in memory you may manipulate it with DDT even SAVE an image of the program in memory actually creating a .COM file. If you write a program that ORGs where CP/M resides you can wipe out CP/M and have to reset the computer to recover. See the file PHASE.DOC for some more information on ORGing files at High memory locations. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: PHASE.DOC PHASED code One of the limitations of this assembler is the lack of a .PHASE directive. This directive causes the assembler to generate addresses for a different section of memory for labels than the actual place the code is to be loaded. This is important for the Kaypro since when the ROM is called, the lower 32K of memory is no longer available. Simply ORGing at higher location later in the program and jumping there will cause the entire area of memory between 100H and the end point of the program to be saved resulting in a huge .COM file when loaded with LOAD.COM. There are many ways to phase code and still end up with a reasonable sized .COM file. Here I will present two of the most common methods. Label+OFFSET Method........................................................ In order for code to be assembled in one area to run in another, our only concern is how the addresses are calculated by the assembler. Normally, an assembler sets a location counter when it sees an ORG pseudo-op. As it produces each byte of the it increments the location counter to calculate the next address. If it finds a label, it sets the label's address according to this location counter. The programmer has another method of setting the address of labels, with EQU. If every label in the program takes the form of Label+OFFSET where the offset is a constant, then the assembler will produce the code to run in high memory while creating a file that will load in low memory. The following short program which re-loads your monitor ROM from the ROM to RAM illustrates this. ;************************************************************************** ;** Rom Save Program ** ;** Run and then enter SAVE 16 ROM.COM ** ;** ROM.COM will contain object code of your monitor ROM ** ;************************************************************************** ORG 100h TRUE EQU 0FFH FALSE EQU 0 OLD EQU TRUE ; TRUE IF NOT 10 OR 4-84 IF OLD SYSPRT EQU 1CH ; old kaypro 2's and fours ELSE SYSPRT EQU 14H ; tens, 4-84's, 2-84's ENDIF OFFSET EQU 8000h ; offset for calculating high addresses LD DE,HISTRT ; the destination of the High memory code LD HL,LODEND+1 ; the source code is just beyond this loader LD BC,HIEND-HISTRT ; the number of bytes to move (end - start) LDIR ; move it up there JP HISTRT ; jump to it LODEND: NOP ; to calculate end of loader/start of high ; memory code segment HISTRT EQU $+OFFSET ; BEGIN USING OFFSET IN A,(SYSPRT) ; get present sysport data SET 7,A ; bank select bit OUT (SYSPRT),A CALL MOVIT ; move the code routine IN A,(SYSPRT) RES 7,A ; back to ram bank OUT (SYSPRT),A CALL TPAMOV ; now move to 100H for save JP 0 ; back to CP/M MOVIT EQU $+OFFSET LD HL,0 ; Source is at 0 (in ROM ) LD DE,OFFSET+1000H ; Load above us LD BC,1000h ; pick up 4K (2732) LDIR RET TPAMOV EQU $+OFFSET LD HL,OFFSET+1000H ; destination becomes source LD DE,100H ; move to TPA start LD BC,1000H ; 4K bytes to move LDIR RET HIEND EQU $+OFFSET ; end of code to be relocated END Assemble this program (the source is on the disk as phase1.azm so you don't have to type it in). Examine the listing file. Notice that the assembler generated high memory addresses though the program loads low. Using DDT.COM.............................................................. With this method you would split the above program into two parts, the loader and the code that is to be relocated. You can assemble the loader and pick an arbitrary source address for the code to be relocated (say 200h). Then you can assemble the relocatable portion with an high ORG. (8000H say). Now you can join the two HEX files together with DDT.COM reading in the high portion with an OFFSET. To get the offset use DDT's Hex sum and difference command in the form of: H, The second number will be the OFFSET. The program we wrote above would go together like this. ;************************************************************************** ;** Loader.azm Loads relocating code to its destination *** ;************************************************************************** ORG 100H BYTCNT EQU 100H ; we can either supply a value here that we ; know will load enough code or assemble ; the high code first and let the assembler ; give us this value (examine print file) HISTRT EQU 8000H ; where the relocation code goes HILOAD EQU 200H ; where the other file goes LD DE,HISTRT ; the destination of the High memory code LD HL,HILOAD ;the source code is just beyond this loader LD BC,BYTCNT ;the number of bytes to move (end - start) LDIR ; move it up there JP HISTRT ; jump to it ; memory code segment END ;************************************************************************** ;** Phase2.azm *** ;** Relocate with DDT *** ;************************************************************************** TRUE EQU 0FFH FALSE EQU 0 OLD EQU FALSE IF OLD SYSPRT EQU 1CH ; old kaypro 2's and fours ELSE SYSPRT EQU 14H ; tens, 4-84's, 2-84's ENDIF BYTCNT EQU HIEND-HISTRT ORG 8000h HISTRT: IN A,(SYSPRT) ; get present sysport data SET 7,A ; bank select bit OUT (SYSPRT),A CALL MOVIT ; move the code routine IN A,(SYSPRT) RES 7,A ; back to ram bank OUT (SYSPRT),A CALL TPAMOV ; now move to 100H for save JP 0 ; back to CP/M MOVIT: LD HL,0 ; Source is at 0 (in ROM ) LD DE,9000H ; load above us LD BC,1000h ; pick up 4K (2732) LDIR RET TPAMOV: LD HL,9000H ;destination becomes source LD DE,100H ; move to TPA start LD BC,1000H ; 4K bytes to move LDIR RET HIEND EQU $ END Now we can use DDT to join the two files. DDT LOADER.HEX H200,8000 8200 8200 ; in this case both numbers are the same (we want ; second IPHASE2.HEX ; prepare to load file R8200 ; read in with offset ^C ; exit to CPM SAVE 1 PHASE3.COM ; and the com file is created ORGing High ( CP/M modifications )......................................... DDT.COM can be used to load .HEX files anywhere in memory despite where the load point (ORG) was set. It does this by reading the file in with a negative offset with the R command. Usually CPM is modified by saving the SYSGEN image and then overlaying the image with the modified section and re-SYSGENing. Lets say you wrote a new BIOS for your Kaypro. The BIOS for the old Kaypros ORGed at FA00H. You assemble it with the assembler with the ORG at FA00H. The object file is KBIOS.HEX If you want to overlay the present bios with your new bios immediately to see if it works it is done as follows: Enter DDT by entering DDT IKBIOS.HEX ; this sets up DDT for a file read R ; actually reads the file in and overlays ; your old bios with the binary code ^C ; returns you to CPM and if all is well ; with your new bios you will warm boot Now you want a permanent copy of your new bios on your system tracks. Once done you will be able to copy your new system onto any disk with SYSGEN. SYSGEN copies the system into memory but not at the same place the system runs. The BIOS image actually begins at 1F80H in the SYSGEN image. We must read the file in at 1F80H even though it ORGs at FA00H. We can do this with DDT also. First we must save the SYSGEN image to a file. sysgen KAYPRO SYSGEN V2.2 SOURCE DRIVE (OR RETURN TO SKIP)a ; get the system from A: SOURCE ON A THEN RETURN ; enter a carriage return DESTINATION DRIVE (OR RETURN TO REBOOT) ; enter a carriage return save 40 cpm.com ; save it to a file Now we use DDT to work on it ddt cpm.com h1f80,fa00 ; we ask for the sum and difference ; of desired address - load address ; DDT prints this. The second number is the ; offset ikbios.hex r ^C Now when we return to CP/M we run sysgen to save the new image to system tracks. SYSGEN KAYPPRO SYSGEN V2.2 SOURCE DRIVE (OR RETURN TO SKIP) ;enter return (use the memory image) DESTINATION DRIVE (OR RETURN TO REBOOT) ;we will put it on B DESTINATION ON B THEN RETURN ; another return FUNCTION COMPLETE DESTINATION DRIVE (OR RETURN TO REBOOT) ; one more return Now when when we boot with the disk in drive B the new system will be loaded. ****************************************************************************** MAC-AZM.DOC Most of the assembly language programs released on Micro Cornucopuia disks are set up for the M80 assembler. If you've been wondering what the .MAC extension is for, those are M80 source code programs. Many of the people that got this disk did so to assemble the Kaypro Tinkerkit (source of the rom monitor and bios) without spending $150 for M80. It can be done. For the most part .MAC files can easily be translated to .AZM with very few changes. Unfortunately M80 is as extremely versatile assembler which when used to its full advantage contains some pseudo-ops and directives that Z80MR does not understand. I will reveal the most common of these and provide some help on getting around them in this file. First things first......................................................... Rename the file to have a .AZM extension. Edit the file. Near the beginning of the file you will see the pseudo op that tells M80 what mnemonics you will be using. M80 can handle both Z80 and 8080 mnemonics. You can even switch in the middle of the program. Two pseudo-ops .Z80 and .8080 tell the assembler which mnemonics to recognize. If there is a .Z80 there delete it. If there is a .8080 there you will need to run the program through an 8080 to Z80 translator (like XLATE2.COM). At this point what I usually do is run the program through the assembler. The assembler is very good at finding errors, so I let it tell me what it doesn't like. You can run the program through the assembler without generating any output files (see AZM-COM.DOC) and still get the error report on the console. A ^P before running the assembler will dump to the printer as well. Now you can go back into the file and fix the syntax where the assembler generated errors. Dual mnemonic files........................................................ XLATE2 is a great translator on 8080 only files. It also will only translate the 8080 mnemonics in a file that has both. Unfortunately there are a couple of Z80 mnemonics that are the same as 8080 mnemonics and will be dutifully translated by XLATE2. The problem is that the mnemonics are the same but the instructions they represent are not. So the file will assemble OK but will run with unpredictable results. An example. JP is a valid Z80 mnemonic. It is also widely used since it is an unconditional jump (JMP in 8080) JP is also a valid 8080 mnemonic. It is the jump on parity instruction. So a Z80 JP is translated as if it was the 8080 JP which will make it JP P in the output file. Bad news. This in itself is enough to make it too undependable to use on dual mnemonic files. To avoid the grief you can do one of two things: Translate the 8080 sections by hand if they are small enough. Split the file into smaller files where the mnemonics change. Run the 8080 sections through the translator. You can let the assembler rejoin the files using the *INCLUDE statement or use pip or your editor to rejoin and assemble the whole mess. What I'd really like to see is someone rework XLATE2 so that it would stop translating when it saw .Z80 (and also comment the pseudo-op out) and start again when it saw .8080 (and comment it out as well). Also some of the other minor syntax changes could be fixed as well. Yes XLATE2 can be assembled with Z80MR (it is a .MAC file). After removing the .Z80 at the beginning of the file the only error I got was for the MOD operation on the version number (MOD not supported by this assembler). My solution, just enter the version number there. Having the assembler figure out the ascii just wasn't that important. Macros..................................................................... The M80 macros go through Z80MR pretty well for the most part. There are some syntax changes to be made. Precede the parameters with # signs (including locals). Also there are some macro pseudo-ops that are not supported (also not widely used). If some one writes Z80MR macros to substitute for these (and a MOD operator) please send it in and we'll include it in the macro library that we are developing for this assembler. .phase and .dephase........................................................ These are used to make the assembler generate label addresses for some other part of memory than where the file will be loaded. The file PHASE.DOC offers some alternative ways of doing this.