home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
ENTERPRS
/
C128
/
TEXT
/
A-DOC.ARC
/
PA2.TXT
< prev
next >
Wrap
Text File
|
1993-01-07
|
54KB
|
1,345 lines
80 PRINT'X'ROUTINE LDA CHARACTER
90 JMP $FFD2
If you use POWER ASSEMBLER to assemble this, then DLOAD "SIMPLE" and RUN it,
you would see an X printed on your screen (be still my heart).
Basic may even use the symbol table names in expressions. Anywhere the actual
value is needed the quoted symbol may be used. Lines like;
100 FOR N=0 TO PEEK("TABLE'LENGTH")
110 POKE"TABLE"+N,PEEK("DATA"+N)
120 NEXT:REM MOVE DATA TO TABLE
... could be used. If any of the symbol names referenced were not defined in
the assembler source an UNDEFINED SYMBOL error would ensue.
.LINK "0:NEXTSOURCEFILE"
This is one way of chaining a number of source files together. The .LINK
command will appear at the end of each but the last source file in the chain.
It causes POWER ASSEMBLER to LOAD the source file specified into memory before
continuing the assembly. The last program in your chain will end with a .LOOP
"0:FIRSTSOURCEFILE" line. The names used will be of course be the names you
have DSAVE'd your files to disk under.
If you make use of the "+" forward referencing temporary label then the
largest source file should be the first in the chain. The address stack for
these labels builds up from the end of the program in memory when assembly
begins. If a longer source file is .LINKed in it will overwrite these
addresses spoiling everything.
.LOOP "0:FIRST-FILE"
This tells POWER ASSEMBLER that there are no more files in the LINKed chain.
The file name specified by .LOOP will be the first file in the chain. On pass
one this file will be loaded into memory and pass two begun. On pass two the
.LOOP command signals the end. Any output files are closed and control is
returned to Basic. The source program ending with the .LOOP instruction will
be sitting in Basic's program buffer.
For the Commodore 64 only, .LINK....LOOP memory chaining allows you to take
full advantage of FASTDISK utilities which intercept Basic's LOAD vector.
Again though, make sure that the largest source file comes first if you are
using the forward "+" temporary symbols.
.FILE "0:SAVED-SOURCEFILE"
This is a very convenient way of chaining source files together. The .FILE
command tells POWER ASSEMBLER to assemble the specified source file directly
from disk then to return to the next line of the in memory source and
continue. A very short program containing nothing but .FILE statements can be
used to assemble multiple giant source programs as one. It might look like
this:
- Page 25 -
SYS 999 (SYS 4000 for C-128) ;call POWER ASSEMBLER
10 .FILE "0:INITIALIZE"
20 .FILE "0:PROCESS"
30 .FILE "0:THESEROUTINES"
40 .FILE "0:THOSEROUTINES"
50 .FILE "0:MOREROUTINES"
60 .FILE "0:MESSAGES"
With this type of setup the assembly process and file chain can be very
easily modified. To add a source file called "PROTECTION" to the chain would
be as simple as adding a line 70 .FILE "0:PROTECTION" to the rest before
running (assembling). Changing the order in which the files are assembled
would involve merely switching a few line numbers. To save the symbol table
part way through would entail only inserting the line 15 .SST "0:INIT-SYMS"
for example. Alternating display options, I/O device numbers and assembly
modes (e.g. .FAS or .MEM) would also not involve loading, modifying and
resaving large source files.
It is not even necessary to save the changes made to the memory-based file
chaining program before assembly; it will still be there afterwards.
The relative sizes of .FILEd source programs are unimportant because they are
read directly from disk. The temporary label address stack will always be
completely safe. The amount of memory available for .MEM output and symbol
tables is also maximized by this method of source file chaining.
Large source files and even .LINKed source files may contain .FILE statements.
Control will always return to the next line after the specified source has
been assembled in from disk. .FILE assembled source, however, may not contain
its own .FILE or .LINK commands. This type of nesting would lead to great
unhappiness were POWER ASSEMBLER to attempt it. The .LINK and .LOOP commands
are ignored in .FILE assembled source.
.SEQ "0:ASCIISRCFILE"
This works exactly like .FILE except that the source is expected in ASCII
format, not Basic. This makes POWER ASSEMBLER highly compatible with almost
any editor or word processor.
With POWER ASSEMBLER you can combine types to produce a single, ML object
program using (1) in-memory Basic type source created on the C-64 or C-128
Basic editor, (2) .FILE'ing in SAVE'd source programs and (3) .SEQ'ing in
source created on the ASCII editor of your choice.
Source files specified in the .SEQ instruction must have the following
attributes:
1. They will be in pure ASCII form. No screen code or
tokenization.
2. Lines will not be numbered. POWER ASSEMBLER will
attach a sequence number to each line in a file for
display purposes.
3. A carriage return, i.e. CHR$(13), will be the last
character of each line, and at least two of these will
be at the end of each source file.
- Page 26 -
4. Colons may still be used to link statements on a
line, but no line should be longer than 255 characters
A large source program in this format might possibly assemble slightly faster
than if it were in Basic source format. It would not be necessary for POWER
ASSEMBLER to un-crunch tokens or to read in the four bytes of overhead
associated with link and line number.
.TOP
In some situations it may be desirable to save only a portion of the symbols
defined or used in a program. The .TOP command lowers the symbol table top in
so far as any future .SST is concerned, permitting the saving of intermittent
symbols only. Symbols defined prior to .TOP, although accessible to the
program in every other way, will not be saved. Unless one has a penchant for
empty files one should not attempt to .SST immediately following .TOP. Here
is probably the more practical application of .TOP:
100 .LST "0:HUGE-SYMTAB"
120 .TOP ;will not effect coding
130 ;now a whole bunch
. ;of neat stuff using the
. ;loaded symbol table
500 .SST "0:NEW-SYMS" ;saves only the newly defined symbols
Numerous, completely exclusive symbol tables can be saved from within one
assembly list just as numerous separate object files can be created.
With .TOP it is possible for two programs to access each other's symbol tables
without re-definition problems or phase errors caused by late zero page
assignments. If .TOP is not used then every symbol defined prior to the .SST
command will be saved.
.SST "0:SYMBOL-TABLE-NAME"; save symbol table
This can be used to save all or portions of a symbol table. If the above were
the last line of your source program all of its symbols might be saved to a
file under the name you used.
Use .SST to create a file of Kernal routines, important register addresses and
memory locations for use in all your programs. There are clear advantages to
this.
1. You don't have to type them all in every time you
start something new.
2. Your source files will be shorter without the
numerous assignment statements.
3. Certain consistency and uniformity will be lent to
your source programs. The names of key symbols will
not change from one project to the next.
.SST and .LST provide an excellent way of modifying large ML programs without
having to re-assemble the entire system each time changes are to be tested.
- Page 27 -
Imagine that you have developed a sophisticated word processor or game or
assembler or something and you now wish to add to it a fancy new feature. You
know perfectly well you're not going to get it right the first, second, third
or maybe even the twentieth time. We're talking tricky here. The thought of
re-assembling the fifteen of so chained files involved with each new try is
not the most fun thing that you could possibly ever imagine. you'd probably
spend more time waiting than working. Try this:
1. Put a call to the new routine in the main source and
also assign therein an address to it. This will not be
the final destination, just a free, safe place to work
on it. So somewhere in the main source will be a line
like 500 JSR NEW'FEATURE, and a line like 50
NEW'FEATURE = 50000.
2. Now assemble the whole thing. Be sure to create an
object file via an .OBJ "GREAT-BIG-ML-PGM" and to save
its symbol table at the end via .SST "ITS-SYMBOLS".
3. You should have then a BLOAD'able version of your
program and a copy of its symbol table, i.e. the
addresses and values of all of the routines, and
variables contained in or used by it.
4. Write the new routine. You don't have to get it
perfect right off. It should .ORG originate at the
address you told the main program it would. The first
thing this source will do is load in the symbol table
of the main program with .LST "ITS-SYMBOLS" line.
.LST "0:ITS-SYMBOLS"
This will load in the specified symbol table for use by your program. ...
carrying on with our example:
5. BLOAD the main program in then assemble the new
module (routine) right into memory using .MEM. This
new module will have complete access to the main one as
if they had been assembled together. Any routine in
the large one will be made call-able by name from the
new one. Any flags, registers or variables in the main
one are also at the disposal of the new part.
6. So try the whole thing out. Run it. Crash-boom, or
yuk, or whatever. It didn't work but that's okay
because you planned it that way. At worst you'll have
to re-boot POWER ASSEMBLER, LOAD you ML code and the
source for your test program before you can try it
again. At best you won't have to do any of that before
you begin making corrections.
7. Sooner or later you'll get it perfect. Believe. Now
remove the line from the main source which assigned the
test address to the routine and either .FILE or .LINK
assemble them together the way you would have like to do
in the first place if life wasn't so full of mistakes.
If you .LST symbols in before you define any of your own (i.e. first), re-
definitions will trigger error messages when they occur. Duplicates will not
be loaded in. In the case of labels this is usually convenient since it is
the last occurrence of a label that you are probably interested in anyway.
- Page 28 -
.BYTE [onebytevalues,...,...]
This is used to place one byte value(s) into your code. Here are a few
examples of .BYTE:
10 .BYTE 0,2,4,8,16,32,64,128 ;powers of 2
20 .BYTE <1000,2000,3000 ;low bytes only
30 .BYTE >SUB1,SUB2,SUB3 ;high bytes only
40 .BYTE "a","b","c"+128 ;ASCII values
Notice that commas separate the operands and that no spaces are included.
Also notice how the < and > work: they affect the entire string of values
and should not be repeated. This will make setting up high and low byte
address tables more convenient.
.WORD [twobytevalues,...,...]
Use .WORD to set up address tables. All values following will be treated as
two byte values. This means that 10 .WORD $FF,$FF would have the same
effect as 10 .BYTE 0,$FF,0,$FF.
Here are some examples of .WORD:
10 .WORD DESTINATION-1 ;setup rts jmp
20 .WORD 12*4096,$c000+OFFSET ;expressions
It would be pointless to use > or < in conjunction with word data since the
resulting values would never exceed one byte.
.ASC "***ASCII TEXT***"
Use the .ASC command followed by any quote-mode-typeable string of ASCII
characters you wish to be placed in your code.
The opening quote is not optional. Omitting it will result in a "QUOTE
EXPECTED" error message.
A closing quote is optional unless of course you wish to include some banks at
the end of your text entry.
For the Commodore 64, the following is a staple routine for printing messages
in ML. It is almost always used in conjunction with the .ASC pseudo-op.
50 SYS 999 ;again
70 .ORG 820 ;sys 820 after
80 .MEM
90 PTR =251
95 PRINT =$FFD2 ;Kernal ROM
100 JSR WRITE ;print message routine
110 .ASC "***HI MOM***":.BYTE 13,0
120 RTS
130 WRITE =*
140 LDY #0
150 PLA:STA PTR+1 ;message address-1 on stack
160 PLA:STA PTR
170 - INC PTR
- Page 29 -
180 BNE + ;to line 200
190 INC PTR+1
200 + LDA (PTR),Y
210 BEQ + ;to line 240
220 JSR PRINT
230 BNE - ;jmp to line 170
240 + LDA PTR+1:PHA ;restore the rts address past zero
250 LDA PTR:PHA
260 RTS
The preceding WRITE routine works much the way Basic's PRINT command does in
that the following text is printed. A zero marks the end of WRITE text. If
you examine this routine you will see how the 6510 stack works during JSR and
RTS executions.
The C-128 only, has a new Kernal routine to print out strings of text. This
text cannot be longer that 255 characters and must be terminated by a null
(zero).
Here is as example of this routine used in conjunction with the .ASC pseudo-
op:
50 SYS 4000 ;again
70 .ORG $B00 ;sys 2816 after
80 .MEM
90 FOREVER =*
100 JSR $FF7D ;kernal primm routine
120 .ASC "HI MOM":.BYTE 13,0
130 - JSR $FFE4 ;kernal get keystroke]
140 BEQ - ;loop if no key
150 JSR $FF7D ;primm routine again
160 .ASC "HI MOM":.BYTE 13,0
170 JMP FOREVER
NOTE: don't try JMPing to $FF7D
.SCR "***SCREEN CODE VALUES***"
.SCReen works the same as .ASC except the following text is converted to its
screen code equivalent. That is the value you would use to poke the character
directly to the screen.
The line 100 .SCR "A" would code the value 1 whereas the line 100 .ASC
"A" would code the value 65. This should make like a little easier for
programmers who maintain menu lines and display by "poking" character values
directly to the screen.
.FAS
For the Commodore 64, .FASt switches off the screen. This should increase in-
memory assembly speed approximately by about 20 percent.
For the Commodore 128, .FASt switches the micro processor into the 2mhz mode
and turns off the video. This should at least double in-memory assembly
speed.
- Page 30 -
There is no danger of missing any important messages by doing this. If any
errors are encountered the screen is turned back on for you. It would be
pointless, a waste of time to use .FASt and .DISplay together.
.BURST (C-128 only)
the .BURST command is for disk based (i.e. .SEQ and .FILE) assembly using the
1571. When .BURST is used source files, instead of being read via kernal
routines a line at a time from disk, will be burst loaded into memory at the
bottom of bank 1. From here they will be accessed RAM DISK fashion by the
assembler. This more than doubles the speed of disk based operation making
this almost as fast as .LINK/>LOOP load chaining which is always burst driven.
If you are using the .FILE or .SEQ commands have a 1571 and can spare low
memory in bank 1 during assembly then .BURST is highly recommended. It need
only be used once at the beginning of your source. If you are using more than
one drive and only one is a 1571 the others will not be affected.
.PSU
.PSeUdo allows for the use of mnemonics like LAX, DCM, INS, SKB, AXS, .etc. to
code non-standard opcode. The reliability of some of these are somewhat moot.
I would suggest you execute them with interrupts disabled. Some very widely
distributed commercial programs make extensive use of non-standard opcode both
to conserve space and to confuse disassembly.
Like most inherent (operand-less) pseudos it is a toggle command. Using it
for a second time will turn the feature off. You will probably want it on
only for those portions of code which make use of non-standard opcode. as
with standard mnemonics like LDA and INX you will have to avoid giving symbols
in your program the same names as non-standard mnemonics when .PSU is enabled.
See the table appended to this manual for a full listing and brief
descriptions of the pseudo mnemonics which POWER ASSEMBLER recognizes.
.IF [operand]; conditional assembly
When the expression following an .IF is not equal to zero then assembly will
proceed until an .ELSE is encountered, then skip to an .IFE line marking the
end of conditional assembly or another .ELSE.
When the value following .IF equals zero then POWER ASSEMBLER will ignore
everything until an .ELSE or an .IFE is found. Assembly will resume there.
- Page 31 -
.ELSE
This is where assembly will pick up when the value following the previous .IF
was zero. If a second (third, fourth...) .ELSE follows, assembly will
alternate between them.
20 .IF FLAG
30 : LDA "A":JSR $FFD2 ;Kernal print
40 .ELSE
50 : LDA "1":JSR $FFD2
60 .ELSE
70 : LDA "B":JSR $FFD2
80 .ELSE
90 : LDA "2":JSR $FFD2
100 .ELSE
110 : LDA "C":JSR $FFD2
120 .ELSE
130 : LDA "3":JSR $FFD2
140 .IFE ;end of conditional assembly
110 : LDA "!":JMP $FFD2
If flag = 0 in the above then the assembled code would print "123",
otherwise the code would print "ABC!"
Another more useful application of .IFE .ELSE conditional assembly would be to
protect you indirect jumps from accidentally falling on page boundaries.
10 JMP (INDIRECT) ;to destination
.
.
500 .IF <*=1 ;check page boundary
510 INDIRECT =* ;not page boundary
520 .WORD DESTINATION
530 .ELSE
540 NOP ;pass page boundary
550 INDIRECT =*
560 .WORD DESTINATION
570 .IFE ;end of conditional assembly
No re-definition of a symbol error would occur during the above assembly.
Only the .IFE or .ELSE portion of the actual source would be assembled.
This would depend on whether or not <*+1 (the low byte of the program counter
was +1) was zero.
If you are using a number of .ELSEs you might want to take advantage of the
fact that pseudo-ops can be extended and tack some alternating character on
telling you which condition belongs to, i.e.
.ELSE1,...ELSE0,...ELSE1,...Else0, etc.
Note: Don't try JMPing to $FF7D. Never stick a label in front of an .ELSE
or an .IFE. POWER ASSEMBLER will look no further and miss the switch.
- Page 32 -
.IFE
This ends conditional assembly. Everything following is assembled.
MACRO-OPS
Three of the most common activities in machine language involve (1) comparing
pointers, (2) filling, i.e. erasing, ranges of memory, and (3) moving ranges
of memory. POWER ASSEMBLER has provided macro-ops to make short work of these
traditionals while enhancing the readability and reducing the size of your
source.
All require operands which are expected to be in the form of zero page
pointers. While this may seem a trifle inconvenient at first glance it makes
the resultant code much more flexible.
For instance, you do not want to have to use the .MOVE macro every time you
want to relocate some range of memory. It would be much more efficient to use
it once as a subroutine (i.e. preceded by a label and followed by a RTS) and a
JSR to it whit its three pointers set to your specific needs on each
particular occasion. This would not of course be possible if this macro-op
took constraints as operands.
Another advantage to taking pointers is that you can choose precisely what
addresses will be used by the generated code. Only the pointers you specify
and the processor's registers are manipulated. Back in the joyous awareness
that your data and variables will always be safe when macro coding; trip on to
the absolute power you exercise over memory usage when employing POWER
ASSEMBLER's macros.
I have come into contact with a number of very proficient and talented,
professional assembly language programmers over the last several years and not
one has confessed to having ever used macros. I believe this is because by
their very nature ML programmers enjoy the exquisite control they have over
their machines and do not wish to relinquish this to something "standard".
Perfection is in order. Custom subroutines seem to hold more appeal than
built-in, space-wasting, other-people's macros.
However, the few that have been selected for POWER ASSEMBLER are universally
applicable. To overcome your apprehensions about using them I would suggest
that you UNASM to disassemble the code generated by each. You will find it
totally re-locatable and non-self-modifying as well as fast, efficient and
correct.
.TEST ZEROPTR1,ZEROPTR2
In situations where you wish to compare two addresses designated indirectly by
zero page pointers you could use the .TEST macro-op. The carry returns clear
if the first was pointing to a lower address, otherwise it will be set. The Z
flag is set if they both point to the same address.
- Page 33 -
.DUMP BEGINPTR,ENDPTR
This dumps the contents of the accumulator to a range of memory. It might be
used quite effectively to clear buffers or hi-res screen areas. The first
pointer must designate the first address to be filled and the second pointer
the last. Make sure that they are properly set and that the A register has
been loaded with the desired value before you use (or call the subroutine
using) the .DUMP command. In the following exciting demonstration of it the
40 column screen is filled with "B"s
10 SYS 999 (C-64) SYS 4000 (C-128)
20 .ORG 820:.MEM
30 SCREEN =1024 ;in 40 column mode for C-128
40 LDA <SCREEN:STA TOPPTR
50 LDA >SCREEN:STA TOPPTR+1
60 LDA <SCREEN+999:STA BOTPTR
70 LDA >SCREEN+999:STA BOTPTR+1
80 LDA "B" ;screen code for "B"
90 .DUMP TOPPTR+BOTPTR
100 RTS
.MOVE BEGINPTR,ENDPTR,DESTINATIONPTR
This will generate the code to move the range of memory specified by the first
two pointers to begin at the address pointed to by the third pointer. The
range can be moved in either direction any distance without overwriting
itself. In other words, it does not matter whether the destination is above
or below the beginning range to be moved or if the distance is very small.
Memory will still be intact. This macro is used in EDITOR.64 or LABELGUN (C-
128 only) to shift ranges of source up or down when inserting or deleting text
and replacing strings with others that are longer or shorter. Of course the
memory being mover (your source) cannot be corrupted in any way.
Write the following short program to locate in the cassette buffer.
10 SYS 820 SYS4000 for C-128
20 .ORG 820:.MEM FOR C-64 .ORG $B00:.MEM for C-128
30 FROMPTR =251 ;safe Basic zero page
40 TOPTR =253
50 DESTPTR =65
60 .MOVE FROMPTR,TOPTR,DESTPTR
Now use UNASMbler to disassemble and examine it. Notice that only the
pointers you defined and the micro processor's registers are used. Try
moving some memory around. Convince yourself that .MOVE works and is safe.
Almost every ML program ever written uses memory moves. Getting comfortable
with this POWER ASSEMBLER macro can save you time and trouble.
WRITING YOUR OWN COMMANDS
There is space in POWER ASSEMBLER's pseudo-op stack for up to five new
commands. Each one takes up five bytes of memory. The first three, which are
currently spaces will be replaced by your own
- Page 34 -
three-letter command which you will make up all by yourself; the next two will
be the address-1 of the routine you want to execute when the assembler comes
across this command.
A symbol table for each version of your assembler is on the system disk. To
display one use the following technique:
10 SYS 999 (C-64) SYS 4000 (C-128)
20 .DIS ;to display to screen
30 .LST BUDDYSYMS
The symbol you will use to get your commands into the code is called
"PUT'YOUR'CMDS'HERE"; and nothing could be easier that putting your command
there. Let us create a new feature for POWER ASSEMBLER called "fun"; every
time the pseudo-op .FUN is encountered in your source POWER ASSEMBLER will
inform you that fun is being had; what could be nicer?
10 SYS 999 (C-64) SYS 4000 (C-128)
20 .LST BUDDYSYMS ;so you can use them
30 .ORG PUT'YOUR'CMDS'HERE
40 .MEM ;now we put fun on the stack
50 .ASC "FUN" ;no period here
60 .WOR FUNROUTINE-1 ;address of new useful routine less one
70 .ORG 832 ($B00 on C-128) ;we'll put it in the cassette
buffer
80 FUNROUTINE =* ;powerful new command
90 JSR MESSAGE ;POWER ASSEMBLER's print message subroutine
100 .ASC "WHEEEE! THIS IS FUN."
110 BYTE 13,0 ;must end with zero
120 JMP NEWLINE ;POWER ASSEMBLER takes over
after running this, run the following:
10 SYS 999 (C-64) SYS 4000 (C-128)
20 .FUN
Your "fun" message should have been printed twice: once on each pass. If it
wasn't then it's your fault. Fix whatever you did wrong, try again, and be
more careful this time (GRIN).
IMPORTANT ROUTINES AND LOCATIONS
Every source line is de-tokenized into memory at the address of the BUFFER
symbol. A zero byte marks the end of that line.
If you generate output you should call POWER ASSEMBLER's NEWPC routine. First
set BYTES to the appropriate value, not greater than three. Put code
generated at OUTPUT, OUTPUT+1, and OUTPUT+2 as necessary. You may call NEWPC
more than once (i.e. in a loop) When you are done, a JMP NEWLINE; passes
control back to POWER ASSEMBLER.
If your command takes and operand you can immediately JSR the EVALOPERAND
routine. Any valid POWER ASSEMBLER expression will be evaluated and the value
returned in SUM and SUM+1.
- Page 35 -
PASSNUM will be 0 on pass 1 and 255 on pass 2.
Try changing the previous .FUN command so you can use .FUN 100 to print the
"fun" message 100 times, but only on pass 1.
Of course there are many, many more routines and flags and variables that you
will want to become familiar with if you plan to really get intimate with the
inner workings of your assembler. You have symbol tables. You have a
powerful unassembler. You have fun.
TEMPORARY SYMBOLS
TEMPORARY LABELS: - / +
The multiplication, division, addition and subtraction
characters each have two possible uses. In expressions, if
"*" is an arithmetic operator then values on either side
are multiplied (e.g. 12*4096); whereas, if it is used as a
symbol it will represent the program counter (e.g. LABEL =*
or *=*+4). This is standard use of "*" and is mentioned
only to illustrate dual functioning of one special
character.
In POWER ASSEMBLER source the "+", the "-" and the "/" also
serve two purposes. In addition to their standard
application in arithmetic, they may be used as temporary
labels. Many ML programmers don't like having to think up
symbol names for numerous, routine, short branches. This is
especially so in very long programs after all variations of
the labels SKIP and LOOP and BACK and AHEAD and OVER and so
on.... and so on.... have been exhausted. Objections to
using these often random symbols are based on the following:
1. Time and effort are wasted in deciding on their names
and typing them in, each at least twice.
2. They have a tendency to camouflage more meaningful
symbols, making it harder to visualize what is happening.
3. Symbol tables become unnecessarily large, wasting memory
and slowing things down.
Judicious use of POWER ASSEMBLER's three temporary flags
smartly overcome all of these difficulties.
TEMPORARY BACKWARD REFERENCING
When the "-" is used as a symbolic operand, the last
occurrence of it as a label is referred to. The command BNE
- will code a conditional branch back to the last line
flagged with a "-" character. Here is how it might be used
in a simple time delay routine:
100 WAIT =* ;name of subroutine
110 LDX #0 ;initialize x and y
120 LDY #0
- Page 36 -
130 - DEX
140 BNE - ;loop back until x=0
150 DEY
160 BNE - ;same for y
170 RTS
Up to three minus signs may be used together as a symbol (e.g. BCC - - -) to
refer back as far as the third last "-" flagged line; only the last three are
remembered. The minus sign may be used as a label again and again in your
source without re-definition errors. You must be careful that when you use "-
" characters symbolically that the line on which the referenced one has
occurred as a label is the one you will want to access (e.g. branch to). Any
"-" markers prior to the third one are inaccessible.
TEMPORARY FORWARD REFERENCING
The plus sign, as you may have guessed already, works in just the opposite
way. That is, BNE + would code a conditional branch to the very next
occurrence of the "+" flag. Here is how one might use it to increment a
pointer.
10 INC PTR ;the low byte
20 BNE +
30 INC PTR+1 ;the high byte
40 + RTS
A symbol could have been used instead of "+", but what a bother, a mess and a
waste of space.
There is no limit to how far forward the next "+" flag may be or how far back
to the last "-" flagged lines may be. JMP - - or JMP ++ are valid too.
Within there scope of three, these temporary flags may be dealt with just like
any other symbol. Still, all subroutines and data should be given meaningful
labels even if you could get away with a "+" or "-" temp.
The next three "+" flagged lines may be referenced at any point by using 1 to
3 "+"'s (e.g. BEQ +, BEQ ++, or BEQ +++) as a symbol just as any of the last
three "-" flagged lines may be accessed by using 1 to 3 "-"'s.
Don't let temporary labels permit you to become un-imaginative. Restrict
their use to short, redundant branches.
FORWARD OR BACKWARD
When the "/" character is used as a label it serves as both "+" and "-",
either of which can be used to reference it. In effect it is as though the
"/" flagged line had both "+" and "-" as a label on it. The JMP - statement
would actually code a jump back to either the very last "-" or "/" flagged
line. A JMP + would code a jump forward to the very next "/" or "+" label
position. In the next example both conditional branches target the RTS in the
middle.
- Page 37 -
10 BEQ +
20 LDA #0 ;or whatever
30 / RTS ;destination of both
branches
40 DEX ;or whatever
50 BEQ -
TEMPORARY SYMBOL MANAGEMENT
The backward referenced "-" label is handled only on pass two. Only three
addresses need ever be "remembered" by the assembler with regard to it. The
forward referenced "+" can not be dealt with so easily. A table of all of its
occurrences as a flag is created on pass one which is then accessed on pass
two. This table is separate from the normal symbol table and contains only
addresses. It builds up from the end of your source.
If you are using the memory based
.LINK "NEXTFILE". . . .
.LOOP "FIRSTFILE"
system to chain source files together and you have made use of any temporary,
forward "+" references you should make sure that the largest file in the chain
comes first; otherwise, a larger file when loaded into memory will clash with
the "+" address table. Consider disk based .FILE "ANYFILE" chaining as an
excellent alternative to memory based chaining in this situation.
LABELGUN (for C=128 only)
The C=128 screen editor is an excellent one. With it you can redefine keys,
freeze scrolling, delete ranges, renumber, auto line number and much more.
About the only thing missing when it comes to developing a large program is
sophisticated string handling. To be able to seek our occurrences of and
possibly modify a given symbol (e.g. string of characters) instantly
throughout an entire source program is so useful as to be almost essential.
With Bud installed you have this ability. So never strain your eyes scrolling
through screen after screen of source looking for that elusive BUG subroutine.
Just enter the following command:
L,BUG
Every line in your program with the word BUG on it will be listed for you.
Change every occurrence of BUG to CRITTER like this:
C,BUG,CRITTER
In the above case words like DEBUG, BUGEYES and BUGGY would also be changed.
This may not be what you had in mind.
- Page 38 -
To have only whole words considered you would have to use a period in place of
the first comma.
C.X,EXITROUTINE
This would not ruin all your words containing X's. Only if X occurred as a
whole symbol would it be changed to EXITROUTINE. All those LDX, INX, STX and
TXA commands would go unmolested.
Sometimes the string you seek will contain a Basic keyword but not have been
tokenized by the Basic editor. This may be due to its following a DATA or REM
string on a line or because it exists between quotes. In this situation it is
possible that the string you target, even though it looks the same as in your
program, will not be found by Labelgun.
If you have doubts or if you are after a string you know is in quotes, do
this:
L,"ENDING
or
C"STOPTHIS,STOPTHAT
You may put a period at the end of any Labelgun command to add extra spaces to
the end of a string;
L,MODULE .
... would find any subroutines whose names ended in MODULE, but probably not
calls to them.
You will find these string handling commands virtually indispensable. Use
them to update label names that have changed their meaning. Quickly locate
routines by name. If you have source for the C=64 around that you would like
to convert to the C=128, Labelgun can help.
Source written on the C=64 editor can be assembled by BUD, but source written
on the C=128 might not work with a C=64 basic environment assembler because of
the much larger set of tokens used on the C=128.
TWO ENVIRONMENT EDITOR
Buddy-System 64 actually encompasses two machine language development
environments. It is the POWER ASSEMBLER half which has been discussed so far.
Although POWER ASSEMBLER is able to assemble ASCII files from disk such as can
be written on EDITOR.64 (or EDITOR.128) or on most word processors, its memory
based source must be in Basic format. Basic source, unlike pure ASCII text is
actually a linked list: each line starts with a two byte pointer to the next.
Following this pointer are two more bytes representing the line number. Next
comes the actual text with all Basic keywords tokenized (i.e. crunched). At
the end of each line is a zero byte.
While this format does very well for Basic it may not be the most efficient
for assembly language. However, many programmers are comfortable with the
Basic editor and source format, have acquired
- Page 39 -
utilities such as POWER-64 which greatly extend its capabilities, and have no
desire to switch to a different system. If you are one of these people then
stay with POWER ASSEMBLER; it was made for you.
LOADING EBUD
On the disk is another version of the assembler which can be invoked by
entering LOAD "EBUD",8 <RETURN> and then RUN. This will result in the
editor compatible version of your assembler. ED-BUDDY.64 or ED-
BUDDY.128 and the ASCII editor itself. EDITOR.64 or EDITOR.128, being loaded
into memory. You will not return immediately to Basic as in the case when
booting with POWER ASSEMBLER.
EDITOR.64 (or EDITOR.128)
Printed at the top of your screen will be COLUMN:1 LINE:1; a solid cursor
will be in the upper left corner of the now clear text area. Welcome to our
editor!
MEMORY USAGE
EDITOR.64 commandeers the highest 2k of Basic RAM and sets the top of Basic to
a point below itself. Thus it is safe from Basic activities and any utilities
(such as UNASM) which are sensitive to Basic's pointers. Although 2k is quite
small by some standards the editor, as you will soon see, is no weakling.
REPLACES BASIC EDITOR (C=128 only)
EDITOR.128 effectively replaces the Basic editor insofar as the EBUD version
of your assembler is concerned. Basic is still completely at your disposal,
but you will not be using its line number oriented editor to write your source
on or assemble your source from. EDITOR.128 is short, as editors go, and easy
to learn to use. Nonetheless, a number of very useful features have been
built into it.
4-WAY SCROLLING and PAGING
Begin typing. When you come to the right of the screen instead of wrapping to
the next line as you would in Basic the screen window scrolls with you to the
right. Lines may be up to 250 characters long. With text in memory you can
scroll up, down, left and right by using the cursor keys. You may also page
up and down with the f3/f4 key and page left and right with the f5/f6 key.
This allows you to fillip through your source very quickly. The CLR HOME key
can be used to position you immediately to the top or bottom of your source.
- Page 40 -
SIMPLE INSERT and DELETE
The INST DEL key works pretty much the way it does in Basic to add or remove
text one character at a time. The f1/f2 key can be used to delete the
remainder of a line or to insert a new line. This key can also be used to
split and join lines.
CUT and PASTE
To delete an entire range of text position the cursor at one end of the text
you wish to remove, then press <LOGO> S to set range. You will see [RNG]
appear at the left of your status line next to COLUMN: Now move to the other
end of the range of text to cut. It does not matter how far or near this is.
Press <LOGO> D and this text will all disappear. Pressing <LOGO> S twice in
a row takes you out of the Set Range mode.
Once you've cut a range of text you may paste (insert) it back anywhere, as
often as you like and even move blocks of source between files. To insert the
range simply position the cursor to where you would like it to begin and press
<LOGO> T for Text and presto -- there it is again.
You may go back and forth from Basic, clear (new) source and load files
without disturbing cut text so that routines can easily be moved from one file
to another.
FIND and REPLACE
To find occurrences of any word or words in your source, press <LOGO> F for
Find. This will temporarily position you on the status line. Following the
"OLD:" prompt, enter the string of characters you would like to find. When
you are done press <RETURN> to get back to where you where in your text. Move
to where you would like the search to begin (to search all your source press
<SHFT> CLR HOME to go to the top) and then the f7 key. Every time you press
the f7 your cursor will move to the next occurrence of the target string you
entered until you reach the bottom of your source.
If you would like this target string replaced in your source with something
else, press <LOGO> R for Replace. Again you will move to the status line
where following the "NEW:" you will type in whatever you would like to change
the "OLD:" stuff to.
You may proceed in two ways: (1) If you press f7 only the next occurrence of
the old will be replaced with the new. (2) If you press f8 then all
occurrences following will be changed and you will finish at the end of your
source.
LOADING and SAVING TEXT
Text may be kept as either sequential or program files. ASCII Sequential
files can be disk assembled by either version of your assembler via SEQ
FILENAME. Program files can be .LINK/.LOOP load chain assembled (i.e.
assembled directly from memory) by EBUD only. The C=64 can also take
advantage of FAST LOAD/SAVE cartridges for the 1541.
- Page 41 -
PROGRAM FILES
To save your source as a program file simply press RUN STOP to return to
Basic, then enter SAVE "MY-STUFF",8 (C=64) or DSAVE "MYSTUFF" (C=128) just the
way you would any Basic program. To load this file back in tomorrow you would
enter LOAD "MY-STUFF",8 (C=64) or DLOAD "MYSTUFF",8 (C=128).
TO & FROM BASIC
To return to the editor from Basic use the ED command. If you loaded
something new it will be there; otherwise whatever you were working on will
still be waiting, unless of course you gave the NEW command to Basic in which
case your source will have been cleared.
SEQUENTIAL FILES
To save and load sequential files it is not necessary to leave the editor. To
save a file as a SEQ file begin by pressing <LOGO> P. Then, following the
"PUT:" prompt enter the name you would like to give your source on disk.
To load a SEQ file press <LOGO> G and following the "GET:" prompt type in the
file-name and press RETURN. The file will be loaded in, beginning at the
position of the cursor. This can be used to join two files.
ASSEMBLING
To assemble editor source, first press RUN STOP to return to Basic. Then
enter the AS command. The source in the editor will be assembled directly
from memory. It is not necessary to save it first (unless you plan to kill
the machine). You may then issue the appropriate SYS command to test the code
(hopefully) return to your still intact source via the ED command afterwards.
Complete memory based operation is supported. Either EBUD and EDITOR.64 and
EDITOR.128 you can also disk assemble, file chain, load and save symbol
tables, create object files, and indeed do all of the things POWER ASSEMBLER
does with the Basic editor.
SOMETHING TO TRY (C=64 only)
The source for the BUDDY-UNASMBLER is on disk in sequential format. Either
version of Buddy will assemble it from disk to memory at 50000. To create a
program file of ML code from this source you will have to use EBUD. After
loading and running EBUD use <LOGO> G to get UNASM-SOURCE into memory.
Change the .MEM on line 2 to .OBJ UNASM.OBJ then RUN STOP to Basic. Enter the
AS command to assemble UNASM-SOURCE creating UNASM.OBJ which can now be
LOADed...,8,1. If you would like a version to load somewhere else in memory
change the .ORG 50000 line.
Now you've got a really powerful and fast memory based unassembler that will
convert code to true POWER ASSEMBLER source.
- Page 42 -
CONVERTING SOURCE TO ASCII
On the disk is a program called MAKE-ASCII that will create an ASCII file
completely compatible with the EBUD system from any Basic format source file
such as created by UNASM and used by POWER ASSEMBLER.
LOAD and RUN MAKE-ASCII
Enter the name of the Basic file followed by the name of the ASCII file you
would like to create. It will be done. You can get this file into EDITOR.64
or EDITOR.128 using the <LOGO> G command to load a sequential file. You will
probably see that this new ASCII file consumes less space on disk than the
original Basic one did.
EDITOR COMMAND SUMMARY
f1 delete rest of line
f2 insert new line
f3 page up
f4 page down
f5 page right
f6 page left
f7 find/replace next occurrence
f8 replace all occurrences
CLR top of text
HOME bottom of text
<LOGO> S start set range
<LOGO> D delete range
<LOGO> T insert range
<LOGO> F set string to find
<LOGO> R set string to replace
<LOGO> P save (put) seq file
<LOGO> G get (load) seq file
RUN STOP go to Basic
ED go to editor
AS assemble source in editor
ZBUDDY (for Commodore 128 only)
The following is intended to assist the more advanced ML programmer in making
use of the C=128's Z/80 micro processor via the very powerful cross assembler,
ZBUDDY. ZBUDDY lets you use standard Z/80 mnemonics (see "TEST.ZMNE" program
on disk) and BUDDY's expression syntax and rich body of pseudo-ops (see those
sections of this manual) to create ML code for the 128's "other" micro
processor. Symbol tables for these assemblers are fully compatible (i.e.
symbols can be .SST saved on one and .LST loaded by another) so that complex
programs involving both the Z/80 and the 8500 can be written.
- Page 43 -
PROGRAMMING THE Z/80 (C=128 only)
The C=128 is a two processor system. Inside are the 8500 and a Z/80. The
Z/80 is one of the most advanced 8 bit processors alive. It, unlike the 8500
which is memory based, is a register based micro processor. It has two sets
of general purpose registers. Each of these sets contains an accumulator, a
status register and a six, 8 bit, general purpose registers. The second set
can be used for the interrupt flip-flop (IFF) or by the exchange (EXX) command
to remember and restore register contents. Data registers can also be paired
for 16 bit addressing and arithmetic. In addition to these there are four
other 16 bit registers: the PC (program counter), the SP (stack pointer) and
the (IX) and (IY) (index) registers.
8 BIT INTERNAL REGISTERS (C=128 only)
A A' accumulator
B B' general purpose
C C'
D D'
E E'
H H'
L L'
F F' flag status
16 BIT REGISTER PAIRS (C=128 only)
BC B=hi byte C=lo byte
DE D=hi byte E=lo byte
HL H=hi byte L=lo byte
TRUE 16 BIT REGISTERS (C=128 only)
IX index
IY index
SP stack pointer
PC program counter
COMMANDS (C=128 only)
The Z/80 recognizes several times as many instructions as the 8500; some
therefore require more than one byte of opcode. These commands can be
functionally divided into 13 groups.
1. THE EIGHT BIT LOAD GROUP (C=128 ONLY)
The Z/80 assembler load instruction, LD, might more aptly be named MOVE.
There is no store instruction. Every LD will be followed by two operands
delimited by commas. The first operand represents the destination and the
second the source, so that the instruction LD ($C000),A means store the
contents of A at $C000 whereas LD A,($C000) would mean load A from $C000. In
Z/80 mnemonics, parenthesis define memory location; otherwise an immediate
value is assumed.
- Page 44 -
2. THE SIXTEEN BIT LOAD GROUP (C=128 ONLY)
This includes all the commands which move two byte values either between
registers or between registers and addresses. Included here are the PUSH and
POP instructions which is handy since addresses are what stacks are mainly
for.
3. THE EXCHANGE GROUP (C=128 only)
Register contents can be swapped with the secondary set or within the primary
set. There's nothing like this on the 8500 although we often wish there was.
4. THE BLOCK TRANSFER GROUP (C=128 only)
Set a few register pairs and use one of these to move or fill memory a byte at
a time or in a Z/80 controlled loop. The short Z/80 routine which we will
later call from Basic to copy its ROM into 8500 visible RAM uses an LDIR loop.
5. THE BLOCK SEARCH GROUP (C=128 only)
As above, the Z/80 can automatically control looping by counting down the
value contained in the BC pair and incrementing the address pointed to by DE.
Ranges of memory are compared with the A register until a match is found or
the BC pair decrements to zero.
6. THE 8 BIT ARITHMETIC AND LOGICAL GROUP (C=128 only)
These allow for manipulation of one byte values in pretty much the same way
6510 programmers are used to. Addition and subtraction are possible with or
without a carry.
7. THE 16 BIT ARITHMETIC AND LOGICAL GROUP (C=128 only)
Same as above but with two byte values being manipulated. The logical AND, OR
and XOR are not found in this group.
8. THE CPU CONTROL GROUP (C=128 only)
Processor and interrupt modes and status flags are handled.
9. THE ROTATE AND SHIFT GROUP (C=128 only)
Many different types of shifts accessing both one and two byte values via a
variety of addressing modes are available.
- Page 45 -
10. THE BIT SET RESET AND TEST GROUP (C=128 only)
These commands provide for complete bit addressing. Each takes two
parameters. The first will specify which bit (0-7) is to be set, reset, or
tested; the second will designate the register or memory location to be
manipulated. For example SET 3,(IX+0) would set bit 3 in the address pointed
to by the ISX register (i.e. OR it with the number 8).
11. THE JUMP GROUP (C=128 only)
Conditional and unconditional, jumps (direct) and branches (relative) are
supported. Anyone who had ever had to fake a conditional jump in the 6510 via
BNE *+5:JMP FAR or an unconditional branch via SEC:BCS NEAR will appreciate
the versatility of this Z/80 group.
12. THE CALL AND RETURN GROUP (C=128 only)
Subroutines may also be called and returned from conditionally or
unconditionally.
13. INPUT OUTPUT GROUP (C=128 only)
These are specialized load and store instructions. In the C=128, when
accessing I/O memory (D000-DFFF), IN and OUT commands should be used instead
of LD.
PROGRAMMING THE Z/80 IN 128 MODE (C=128 ONLY)
The Z/80 brings a convenience and conciseness to ML programming that is sure
to please and impress 6510 assembly language programmers. I hope the above
has wetted your appetite for doing a little exploring. It will inspire you to
know that this micro processor can be used in conjunction with (not at the
same time as) the 8500 in the C=128, even from Basic; switching between them
is not much more difficult than switching between memory banks once you know
how.
SWITCHING PROCESSORS (C=128 only)
Bit 0 at $D505 (54533) controls the micro processor mode. If it is turned on
then the 8500 becomes active; if it is not then the Z/80 takes over.
You can't just poke it off. A little housekeeping is first in order:
Disable the 8500 interrupts via SEI because you are going to switch to a
memory configuration in which Kernal ROM is not visible.
To do this, store a $3E (62) at $FF00 (the configuration register). This
leaves I/O RAM intact but switches everything else to RAM 0.
- Page 46 -