0% found this document useful (0 votes)
667 views164 pages

Commodore 64 Assembler Workshop

Commodore 64 Assembler Workshop

Uploaded by

Dave Snyder
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
667 views164 pages

Commodore 64 Assembler Workshop

Commodore 64 Assembler Workshop

Uploaded by

Dave Snyder
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 164

Commodore 64

Assembler
Workshop
Commodore 64
Assembler
Workshop

Bruce Smith

O h i v a Publishing Limited
SHIVA PUBLISHING LIMITED
64 Welsh Row, Nantwich, Cheshire CW5 5ES, England

Bruce Smith, 1984

ISBN 1 85014004 9

All rights reserved. No part of this publication may be reproduced,


stored in a retrieval system, or transmitted in any form or by any
means, electronic, mechanical, photocopying, recording and/or
otherwise, without the prior written permission of the Publishers.

This book is sold subject to the Standard Conditions of Sale of Net


Books and may not be resold in the UK below the net price given by
the Publishers in their current price list.

An interface was used to produce this book from a microcomputer


disc, which ensures direct reproduction of error-free program
listings.

Typeset and printed by Devon Print Group, Exeter


Contents

Introduction 1
1 Opening the Tool Box 3
Writing Machine Code 4
Debugging 5
2 Commodore Command 7
CHRGET 7
The Wedge Operating System 9
The New Commands 15
Using the WOS 17
3 ASCII to Binary Conversions 20
ASCII Hex to Binary Conversion 20
Four ASCII Digits to Hex 26
Convert Decimal ASCII String to Binary 30
4 Binary to Hex ASCII 38
Print Accumulator 38
Print a Hexadecimal Address 41
Binary Signed Number to Signed ASCII Decimal
String 42
5 String Manipulation 53
Comparing Strings 53
Strings Unite 58
CopyCat 64
Insertion 71
6 Printing Print! 78
7 A Bubble of Sorts 84
8 Software Stack 91
Binary Ins and Outs 98
Come In 100
9 Move, Fill and Dump 104
Move it! 104
Fill 111
A Memory Dump 113
10 Hi-res Graphics 120
A BASIC Move 121
Selecting Hi-res 123
A Clear View 124

Appendix 1: 6510 Complete Instrurtion Set 129


Appendix 2: 6510 Opcodes 145
Appendix 3: Commodore 64 Memory Map 149
Appendix 4: Branch Calculators 150
Index 151
Introduction

The Commodore 64 Assembler Workshop is aimed at those of you


who have been delving into the delights of programming at
machine code level. It is a natural progression from Commodore 64
Assembly Language, but will be invaluable even if you learned
assembler and machine code using any of the other relevant books
available. It provides a bench full of useful assembly language
routines and utilities programs and examines the techniques
involved.
Extensive use of vectored addresses is made throughout the
Commodore's operation, allowing modifications to be made to the
manner in which the micro operates. Chapter 2 demonstrates how
the CHRGET subroutine can be used to allow new RAM-based
commands to be added to the already extensive facilities provided
by the machine. A short 'wedge' interpreter is provided and the
techniques for adding your own commands examined, and to get
you going, three commands come supplied with the wedge inter-
preter: @CLS, @UPand @LOW.
Conversion between ASCII based numerical character strings
and their two-byte binary equivalents and vice versa is not straight-
forward. Such conversions are fully described in Chapters 3 and 4,
and working routines are listed.
Any program which handles strings of data must be able to
manipulate the strings, whether it is an adventure game or the
latest stock control reports. Routines for comparing, copying,
deleting and inserting strings are included, and Chapter 6 goes on
to show the various ways in which text can be printed to the screen.
Sorting data lists into order is a task which it is often necessary to
perform within a program, so the technique of bubble sorting is
investigated.
Many other processors provide operations that would be useful
to have available when using the 6510. A software stack imple-
mentation similar to that found on the 6809 preocessor is produced
in Chapter 8, allowing up to eight selected registers to be pushed on
to a memory-based stack.
Routines to move, fill and produce a hex and ASCII dump of
memory are then examined and the final chapter provides a few
hi-resolution graphics utilities to speed you along the way.
Many of the chapters suggest projects for you to undertake at
your leisure, while every program has a detailed line-by-line
description of its operation. Program listings are provided using
BASIC loaders so that they can be used directly as they are.
Included in each line is a REM statement giving the mnemonic
representation of the instruction should you be using an assembler.
In fact. all the tools for using the Assembler Workshop are
supplied-assuming of course you have the workbench!

Highbury, November 1984 Bruce Smith

2
1 Opening the Tool Box

The routines included in this book are designed to make your life
that much easier when writing machine code. Quite often, after
mastering the delights of the Commodore 64's microprocessor,
programmers become frustrated because the techniques involved
in, say, converting between ASCII characters and their equivalent
binary values are not known. Nor are they readily available in a
published form, so the painful process of sitting down armed with
pencil and paper and working out the conversion through trial and
error begins.
This is just one example of the type of assembler program you
will find within these pages. Wherever possible, they are supplied
in a form that will make them relocatable, the only addresses
requiring alteration being those specified by JSR or JMP.
Each listing is in the form of a BASIC loader program, using a
loop to READ and POKE decimal machine code data into
memory. This will allow those of you who have not yet splashed out
your hard earned cash on a suitable assembler program to get
underway. For those lucky ones among you who do have an as-
sembler, each data statement has been followed by a REM line
containing the standard mnemonic representation of the instruc-
tion (see Appendix 1 for a summary). This can be entered directly
and assembled as required.
Although the programs are typeset they have been spooled
direct as ASCII files and loaded into my word processor so all
should run as they are.
BASIC is used freely to demonstrate the machine code's oper-
ation-rather than repeating sections of assembler code, BASIC is
often used to shorten the overall listing, and it is left to you to add
further sections of assembler from other programs within the book
or from your own resources. For example, many programs require
you to input a decimal address. In the demonstrations, this is
indicated by means of a one-line INPUT statement. In Chapter 3,
however, there is a routine for inputting a string of five ASCII

3
decimal characters and converting It mto a two-byte binary
number. This can be inserted into the assembler text of the pro-
gram, to go some way to making it a full machine code program
available for use as a completely self-contained section of machine
code.

WRITING MACHINE CODE

You have an idea that you wish to convert into machine code-so
what's the best way to go about it? Firstly, make some brief notes
about its operation. Will it use the screen? If so, what mode? Will it
require the user to input values from the keyboard? If so, what keys
do you use? What will the screen presentation look like? Will you
want to use sound? ... and so on. Once you have decided on the
effects you want, put them down in flowchart form. This need not
be the normal flowchart convention of boxes and diamonds-I find
it just as easy to write each operation I want the program to
perform in a list and then join the flow of these up afterwards.
Quite often, the next step is to write the program in BASIC! This
may sound crazy, but it allows you to examine various aspects of
the program's operation in more detail. An obvious example ofthis
is obtaining the correct screen layout-you might find after run-
ning the routine that the layout does not look particularly good.
Finding this out at an early stage will save you a lot of time later,
avoiding the need to rewrite the screen layout portion of your
machine code-rewriting BASIC is much easier! If you write the
BASIC tester as a series of subroutines, it will greatly simplify the
process of conversion to machine code. Consider the main loop of
such a BASIC tester, which takes the form:

l GOSUB 2 REM SET UP VARIABLES


2 GOSUB 3 REM SET UP SCREEN
3 REM LOOP
4 GOSUB 4 REM INPUT VALUES
5 GOSUB 5 REM CONVERT AS NEEDED
6 GOSUB 6 REM DISPLAY VALUES
7 GOSUB 7 REM DO UPDATE
8 IF TEST=NOTDONE THEN GOTO 3
9 END

Each module can be taken in tum, converted into assembler and


tested. Once performing correctly the next procedure can be
examined. Debugging is made easier because the results of each
module are known having used the BASIC tester. The final main
loop of the assembler might then look something like this:

4
JSR $C2 REM SET UP VARIABLES
JSR $C3 REM SET UP SCREEN
REM LOOP
JSR $C4 REM INPUT VALUES
JSR $C5 REM CONVERT AS NEEDED
JSR $C6 REM DISPLAY VALUES
JSR $:;7 REM DO UPDATE
BNE LOOP

You might be surprised to learn that this ,technique of testing


machine code programs by first using BASIC is employed by many
software houses the world over,

DEBUGGING

A word or two about debugging machine code programs that will


not perform as you had hoped: if this happens to you, before
pulling your hair out and throwing the latest copy of Machine Code
Nuclear Astrophysics Weekly in the rubbish bin, a check of the
following points may reveal the bug!

1. If you are using a commercial assembler, check that your labels


have all been declared and correctly assigned. If you are
assembling 'by hand', double-check all your branch displace-
ments and JMP and JSR destination addresses. You can
normally ascertain exactly where the problem is by examining
how much of the program works before the error occurs,
rather than checking it all.
2. If your program uses immediate addressing, ensure you have
prefixed the mnemonic with a hash (#) to inform the assembler
or, if compiling by hand, check that you have used the correct
opcode. It is all too easy to assemble the coding for LOA $41
when you really want the coding for LO A #$41.
3. Check that you have set or cleared the Carry flag before
subtraction or addition.
4. My favourite now-ensure that you save the result of a sub-
traction or an addition. The sequence:

CLC
LOA $FB
AOC #1
BCC OVER
INC $FC
OVER
RTS
5
is not much good if you don't save the result of the addition
with:

STA $FB

before the RTS!


5. Does the screen clear to the READY prompt whenever you
perform a SYS call, seemingly without executing any of the
machine code? The bug that often causes this is due to an extra
comma being inserted into a series of DATA statements. For
example the DATA line:

DATA 169,, ,162,255

with an extra comma between the 0 and 162, would assemble


the following:

LDA #$
BRK
LDX #$FF

as the machine has interpreted',,' as ',0,' and assembled the


command which has zero as its opcode-BRK!
6. Does the program 'hang up' every time you run it, when you
are quite certain that the data statements are correct? This is
often caused by a full stop instead of a comma being used
between DATA statements, e.g.

DATA 169,6,162.5,96

Here, if a full stop has been used instead of a comma between


the 162 and the 5, the READ command interprets this as a
single number, 162.5, rounds it down to 162, and assembles
this ignoring the 5 and using the 96 (RTS) as the operand, as
follows:

LDA #$6
LDX #$6
XXX

When executed, the garbage after the last executable instruc-


tion results in the system hanging up. This error should not
occur if you calculate your loop count correctly, so always
double-check this value before running your program.

If none of these errors is the cause of the problem, then I'm afraid
you must put your thinking cap on. Well-commented assembler
will make debugging very much easier.
6
2 Commodore Command

One of the disadvantages of using random access memory-based


machine code routines as utilities within a BASIC program is that it
is left to you, the programmer, to remember just where they are
stored, and to use the appropriate SYS call to implement them.
This doesn't usually pose any problems if only one or two machine
code utilities are present; the problems occur when several are
being used. Normally you would need to keep a written list of these
next to you, looking up the address of each routine as you need it.
Great care must be taken to ensure that the SYS call is made to the
correct address, as a mis-typed or wrongly called address can send
the machine into an infinite intemalloop, for which the only cure is
a hard reset, which would destroy all your hard work.
The program offered here provides a useful and exciting solution
to the problem, enabling you to add new commands to your
Commodore 64's vocabulary. Each of your routines can be given a
command name, and the machine code comprising any command
will be executed by simply entering its command name. The
routine is written so that these new commands can be used either
directly from the keyboard or from within programs.
The trick in 'teaching' the Commodore 64 new commands is to
get the machine to recognize them. If an unrecognized command is
entered at the keyboard, the almost immediate response from the
64 is '?SYNTAX ERROR'. If you have any expansion cartridges
you'll know that it is possible to expand the command set, and the
Programmer's Reference Guide gives a few hints on how to do this,
on pages 307 and 308-the method pursued here is by resetting the
system CHRGET subroutine.

CHRGET

The CHRGET routine is, in fact, a subroutine which is called by


the main BASIC Interpreter. You can think of it as a loop of code,
protruding from the machine, into which we can wedge our own
7
bits of code, thereby allowing fundamental changes to be made to
the manner in which the Commodore operates. Let's have a look at
how the normal CHRGET subroutine (which is located in zero
page from $73) operates:

Table 2.1

Address Machine code Assembler

$73 E6 7A INC $7A


$75 O 2 BNE $79
$77 E6 7B INC $7B
$79 AD xx xx LOA $xxxx
$7C C9 3A CMP #$3A
$7E B A BCS $8A
$8 C9 2 CMP #$2
$82 F EF BEQ $73
$84 38 SEC
$85 E9 3 SBC #$3
$87 38 SEC
$88 E9 O SBC #$O
$8A 6 RTS

The subroutine begins by incrementing the byte located at $7A.


This address forms a vector which holds the address of the inter~
preter within the BASIC program that is currently beil1g run. If
there is no carry over into the high byte, which must therefore itself
be incremented, a branch occurs to location $0079. You will notice
that the bytes which have just been incremented lie within the
subroutine itself. These are signified in the above listing by 'xx xx',
because they are being updated continually by the routine. The
reason for this should be fairly self-evident: looking at the opcode,
we can see that it is LDA, therefore each byte is, in turn, being
extracted from the program.
The next two bytes at $OO7C perform a compare, CMP #$3A.
The operand here, $3A, is the ASCII code for a colon, so
CHRGET is checking for a command delimiter. The BCS $008A
will occur if the accumulator contents are greater than $3A, effec
tively returning control back to within the BASIC Interpreter
ROM. The next line, CMP #$20, checks whether a space has been
encountered within the program. If it has, the branch is executed
back to $0073 and the code rerun.
The rest of the coding is checking that the byte is a legitimate
8
one-it should be an ASCII character code in the range $30 to $39,
that is, a numeric code. If it is, the coding will return to the main
interpreter with the Carry flag clear. If the accumulator contains
less than $30 (it could, of course, have ASCII $20 in it, as we have
already checked for this) then the Carry flag is set.
It is important to understand what is happening here, as we will
need to overwrite part of this code to point it in the direction of our
own 'wedge' interpreter. This has to perform the 'deleted' tasks
before returning to the main interpreter to ensure the smooth and
correct running of the Commodore 64.

THE WEDGE OPERATING SYSTEM

To distinguish the Wedge Operating System (WOS) commands


from normal commands (and illegal ones!), we must prefix them
with a special character-one which is not used by the Commodore
64. The Programmer's Reference Guide suggests the use of the
the '@' sign, so that's what we will use.
Program 1a lists the coding for the WOS. I have chosen to place it
well out of the way, in the free RAM area from 49666 (3C202)
onwards. As we shall see the memory below (bis to 49152 ($C000)
is also used by the WOS.
Program la

10 REM *** WEDGE OPERATING SYSTEM - WOS ***


20 REM *** WOS INTERPRETER FOR COMMODORE 64 ***
30
4 CODIi:=49666
5 FOR LOOP= TO 188
6 READ BYTE
7 POKE CODE+LOOP,BYTE
8 NEXT LOOP
9
1 REM ** M/C DATA **
ll DATA 169, REM LDA #$
12 DATA 16,192 REM LDY #$C
13 DATA 32,3,171 REM JSR $AB1E
14 DATA 169,76 REM LDA #$4C
15 DATA 133,124 REM STA $7C
16 DATA 169,24 REM LDA #$18
17 DATA 133,125 REM STA $7D
18 DATA 169,194 REM LDA #$C2
19 DATA 133,126 REM STA $7E
9
2~~ DATA 1~8,2,3 REM JMP ($JO~JO~)
2~5 REM was STARTS HERE
21~ DATA 2~1 , 64 REM CMP #$4~
22~ DATA 2~8 , 68 REM BNE $44
23~ DATA 165,157 REM LDA $9D
24~ DATA 24~ , 4~ REM BEQ $28
25~ DATA 173 , ~ , 2 REM LDA $~2~~
26~ DATA 2~1,64 REM CMP #$4~
27~ DATA 2~8 , 28 REM BNE $IC
28~ DATA 32,114,194 REM JSR $C272
29~ DATA 16~ , ~ REM LDY #$~~
3~~ DATA 177 , 122 REM LDA ($7A), Y
31~ DATA 2~1, 32 REM CMP #$2~
32~ DATA 24~,9 REM BEQ $~9
33~ DATA 23~,122 REft. INC $7A
34~ DATA 2~8 , 246 REM BNE $F6
35~ DATA 23~,123 REM INC $7B
36~ DATA 56 REM SEC
37~ DATA 176 , 241 REM BCS $Fl
38~ DATA 32,116,164 REM JSR $A474
39~ DATA 169,~ REM LDA #$~~
4~~ DATA 56 REM SEC
41~ DATA 176 , 29 REM BCS $ID
42~ DATA 169,64 REM LDA #$4~
43~ DATA 56 REM SEC
44~ DATA 176,24 REM BCS $18
445 REM PROGRAM-MODE
45~ DATA 32,114,194 REM JSR $C272
46~ DATA 16~,~ REM LDY #$~~
47~ DATA 177 , 122 REM LDA ($7A), Y
48~ DATA 2~1,~ REM CMP #$~~
49~ DATA 24~, 13 REM BEQ $~D
5~~ DATA 2~1, 58 REM CMP #$3A
51~ DATA 24~,9 REM BEQ $~9
52~ DATA 23~,122 REM INC $7A
53~ DATA 2~8,242 REM BNE $F2
54~ DATA 23~,123 REM INC $7B
55~ DATA 56 REM SEC
56~ DATA 176 , 237 REM BCS $ED

10
GETBYfE
FROM
COMMAND
TABLE

Yes

Figure 2.1 The wedge operating system flowchart

57 DATA 21.58 REM CMP #$3A


58 DATA 176.1 REM SCS $A
59 DATA 2161.32 REM CMP #$216
6 DATA 2416.7 REM SEQ $167
61 DATA 56 REM SEC
62 DATA 233.48 REM SSC #$316
63 DATA 56 REM SEC
64 DATA 233.2168 REM SSC #$016
65 DATA 96 REM RTS
66 DATA 76.115.16 REM JMP $161673
665 REM FIND-EXECUTE

11
67fd DATA 169,fd REM LDA #$fdfd
68fd DATA 133,127 REM STA $7F
69fd DATA 169 , 193 REM LDA #$Cl
7fdfd DATA 133,128 REM STA $8fd
71fd DATA 23fd,122 REM INC $7A
72fd DATA 2fd8,2 REM BNE $fd2
73fd DATA 23fd, 133 REM INC $7B
74fd DATA 16fd,fd REM LDY #$fdfd
75fd DATA 162,fd REM LDX #$fdfd
76fd DATA 177 , 127 REM LDA ($7F) , Y
77fd DATA 24fd,36 REM BEQ $24
78fd DATA 2fd9, 122 REM CMP ($7A), Y
79fd DATA 2fd8,4 REM BNE $fd4
8fdfd DATA 2fdfd REM INY
81fd DATA 56 REM SEC
82fd DATA 176 , 244 REM BCS $F4
83fd DATA 177,127 REM LDA ($7F) , Y
84fd DATA 24fd,4 REM BEQ $fd4
8Sfd DATA 2fdfd REM INY
86fd DATA 56 REM SEC
87fd DATA 176 , 248 REM BCS $F8
88fd DATA 2fdfd REM INY
89fd DATA 152 REM TYA
9fdfd DATA 24 REM CLC
91fd DATA Ifdl , 127 REM ADC $7F
92fd DATA 133,127 REM STA $7F
93fd DATA 169,fd REM LDA #$fdfd
94fd DATA Ifdl,128 REM ADC $8fd
95fd DATA 133,128 REM STA $8fd
96fd DATA 16fd,fd REM LDY #$fdfd
97fd DATA 232 REM INX
98fd DATA 232 REM INX
99fd DATA 56 REM SEC
Ifdfdfd DATA 176,216 REM BCS $D8
Ifdlfd DATA 189,8fd,192 REM LDA $Cfd5fd,x
Ifd2fd DATA 133,128 REM STA $8fd
Ifd3fd DATA 232 REM INX
Ifd4fd DATA 189,8fd,192 REM LDA $Cfd5fd,x
Ifdsfd DATA 133,129 REM STA $81
Ifd6fd DATA Ifd8, 128, fd REM JMP ($fdfd8fd)
12
165 REM ILLEGAL
17 DATA 162,11 REM LOX #$B
18 DATA 18,,3 REM JMP ($3)
19
11 REM ** SET UP COMMAND TABLE **
111 TABLE=4948
112 FOR LOOP= TO 1
113 READ BYTE
114 POKE TABLE+LOOP,BYTE
115 NEXT LOOP
116
117 REM ** ASCII COMMAND DATA **
118 DATA 67,76,83, REM CLS
119 DATA 76,79,87, REM LOW
12 DATA 85,8, REM UP

To enable the WOS to identify a wedge command, it needs a


complete list to which it can compare the one it is interpreting in the
program-this is done with the aid of a command table, which is
formed by the program lines from 1100 to 1200. This ASCII table is
based at 49408 ($CI00) and, as you can see from the listing, three
commands are provided: @CLS, @ LOW and @ UP. Note that
the @ is omitted from the front of each command in the table-it is
unnecessary at the comparison stage, as by this time it has already
been established that it is a WOS command-and that each com-
mand is terminated by a zero. A table listing the execution address
of each command must also be constructed, but more of this later.
The main program consists of two parts, an initialization routine
and the interpreter proper.
The initialization routine is embodied in lines 110 to 200. Its
function is to reset the CHRGET subroutine investigated earlier.
Lines 110 to 130 issue a heading on the screen indicating that the
49408

+
l"c"I"l"I"s"I_I"l" 1"0"1 "w"I_I"u"I"p"II_1
The Command Table

49232

~
EXEcunON I_
ADDRESS OF:
~~~
C411J5 C41"A C41
CLS lOW UP

The Address Table

Figure 2.2 The Command and Address Tables.


13
was has been initialized. The subroutine at $AB1E, called by line
130, prints out an ASCII string located at the address given by the
index registers. In this instance it is located at $C~ (49152), and is
assembled into memory by the second part of the listing. Lines 140
to 190 poke three bytes into the CHRGET subroutine which effec-
tively assembles the code:

JMP $C218

The address $C218 is the address of the start of the WOS inter-
preter at line 210. Finally, line 200 does an indirect jump through
the IMAN vector at $0302 to perform a warm BASIC start. The
CHRGET subroutine, complete with wedge jump, now looks like
this:

Table 2.2

Address Machine code Assembler

$73 E6 7A INC $7A


$75 O 2 BNE $79
$77 E6 7B INC $7B
$79 AD xx xx LOA $xxxx
$7C 4C 18 C2 JMP $C218

When the was is entered, the byte in the accumulator is checked


to see if it is an @ (line 210), signifying a wedge command. If it is
not, then a branch to line 570 is performed. As you can see, the
code from line 570 to 650 performs the normal function of the
CHRGET routine, with control returning to the BASIC Inter-
preter.
If the byte is an @ , the interpretation continues. The byte at $9D
is located, to detect whether the command is within a program or
has been issued in direct mode. A zero indicates that the command
has been called from within a program and the branch of line 240 to
line 450 is performed. In both instances the interpretation follows
similar lines-for descriptive purposes, we will assume program
mode and resume the commentary from line 450.
The subroutine at $C272 is the interpreter proper. Starting at
line 665 it locates the command and executes' it. The first eight
bytes (lines 670 to 700) set up a zero page vector to point to the
command table at $Cl00. Lines 710 to 730 update the zero page
bytes at $7A and $7B, which hold the address of the current point
within the program. After initializing both index registers, the first

14
byte within the command table is located (lines 740 to 7(0), and
compared to the byte within the program, immediately after the @
(line 780). If the comparison fails, the branch to line 830 is per-
formed, locating the zero and therefore the next command in the
command table. When a comparison is successful (the command is
identified) and the terminating zero located by line 770, the branch
to line 1010 is performed. Lines 1010 to 1060 locate the execution
address of the command from the address table located at $C050.
The X register is used as an offset into this, being incremented by
two each time a command table comparison fails (lines 970 and
980). The two address bytes are loaded to form a zero page vector
and the machine code is executed via an indirect jump.
On completion of the routine, its terminating RTS returns
control to line 460, and the next byte after the command is sought
out. When a zero is found, the branch of line 490 is performed and
the CHRGET routine is completed, control being returned to the
BASIC Interpreter.

THE NEW COMMANDS

Program 1b provides the assembly routines to construct the initial-


ization prompts, the machine code for the new commands and the
address table:
Program Ib

121 REM ** TITLE MESSAGE DISPLAYED ON SYS


49666 **
122 HEAD=49152
123 FOR LOOP= TO 4
124 READ BYTE
125 POKE HEAD+LOOP,BYTE
126 NEXT LOOP
127
128 REM ** ASCII CHARACTER DATA **
129 DATA 147,13,32,32,42,42,32,67,54,52,32
13 DATA 69,88,84,69,78,68,69,68,32,83,85
12 DATA 8,69,82,32,66,65,83,73,67,32,86,49
131 DATA 46,48,32,42,42,13,
132
136 REM ** SET UP MIC FOR COMMANDS **
137 MC=5176
138 FOR LOOP= TO 14
139 READ BYTE

15
14 POKE MC+LOOP,BYTE
141ta NEXT LOOP
142~
143 REM ** COMMAND M/C **
144 REM CLS
1450 DATA 169,147 REM LDA 11$93
146 DATA 76,210,255 REM JMP $FFD2
147 REM LOW
148 DATA 169,14 REM LDA #$E
149 DATA 76,21,255 REM JMF $FFD2
15 REM UP
151 DATA 169,142 REM LDA #$8D
152 DATA 76,21,255 REM JMP $FFD2
153
154 REM ** SET UP ADDRESS TABLE **
155 ADDR=49232
156 FOR LOOP= TO 5
157 READ BYTE
158 POKE ADDR+LOOP,BYTE
159 NEXT LOOP
16
161 REM ** ADDRESS DATA **
162 DATA ,196 REM CLS $C4
163 DATA 5,196 REM LOW $C405
164 DATA 1,196 REM UP $C4A

Each command's machine code is located from 50176 ($C400). The


three new commands and their functions are:

CLS clear screen and home cursor


LOW select lower case character set
UP select upper case character set

Nothing to set the house alight, admittedly, but the techniques


involved are more important at present. These are simple to imple-
ment and, once understood, enable more useful and complex
commands to be added. The code associated with each command is
responsible simply for printing its ASCII code. The final section of
listing (lines 1540 to 1650) pokes the execution address of each
command into memory. The final address points to the code at line
170, and the program jumps to this position if the command is not
found within the command table. This code performs an indirect
jump to the BASIC Interpreter's error handler.
16
USING THE WOS

Using the Wedge Operating System is easy: enter the program as


shown, run it to assemble the code into memory, and if all goes
well, save the program. To initialize the was enter:

SYS 49666

The screen will clear, and the following message be printed across
the top of the screen:

** C64 EXTENDED SUPER BASIC Vl.0 **


The wedge commands are now available for immediate use. Re-
member that pressing RUN/STOP and RESTORE together will
reset the CHRGET routine to its default value making the WOS
invisible. To relink it, simply execute the SYS 49666 call again.

Line-by-Iine

A line-by-line description of the was now follows, to enable you


to examine its operation in more detail:

line 110 load accumuator with low byte message address


line 120 load accumulator with high byte message address
line 130 print start up message
line 140 reset CHRGET subroutine
line 200 do a BASIC warm start
line 205 main entry for WOS
line 210 is it an '@' and therefore a WOS command?
line 220 no, so branch to line 570 to update
line 230 yes, check for direct or program mode
line 240 if zero, then WOS command is within program,
so branch to line 450
line 250 else direct mode so get byte from buffer
line 260 recheck that it is a WOS command
line 270 if error, branch to line 410
line 280 find and execute the command else issue
appropriate error message
line 290 initialize index
line 300 get byte from buffer
line 310 is it a space?
line 320 yes, so branch to line 380

17
line 330 increment low byte of address
line 340 branch back to line 300 if high byte does not need
to be updated
line 350 else increment high byte of address
line 360 set Carry flag and do a forced branch back to
line 300
line 380 print 'READY' prompt
line 390 clear accumulator
line 400 set Carry flag and force a branch to line 500 to
update and return
line 420 get '@' into accumulator
line 430 set Carry flag and force a branch to line 570
line 445 entry point for PROGRAM-MODE
line 450 locate and execute command or print appropriate
error message
line 460 clear indexing register
line 470 get byte -from program
line 480 is it a 0 and therefore end of line?
line 490 yes, branch to line 500
line 500 no, is it the command delimiter ':'?
line 510 yes, branch to line 570
line 520 no, increment low byte of address
line 530 if not zero, branch back to line 470 to redo loop
line 540 increment high byte of address
line 550 set Carry flag and force a branch back to line 470.
line 570 is it a command delimiter ':'?
line 580 if greater than or equal to ':' then branch to line 650
line 590 is it a space?
line 600 yes, so branch to line 650
line 610 set Carry flag
line 620 subtract ASCII base code
line 630 set C&rry flag
line 640 subtract token and ASCII set bits
line 650 return to BASIC Interpreter
line 660 jump to CHRGET
line 665 entry for FIND-EXECUTE subroutine
line 670 seed address of command table ($Cl00) into vector
at $7F
line 710 increment low byte of command address
line 720 branch over if no carry into high byte
18
line 73~ else increment high byte of address
line 74~ back together, initialize Y register
line 75~ and X register
line 76~ get byte from the command table
line 77~ if zero byte, then command is identified, branch to
line 1010
line 78~ is it the same as the byte pointed to in the command
table?
line 79~ no, branch to line 830
line 8~~ increment index
line 82~ set Carry flag and force a branch back to line 760
line 83~ command not identified-seek out zero byte. Get
byte from command table
line 84~ if zero, branch to line 880
line 85~ increment index
line 86~ set Carry flag and force a branch to line 830
line 88~ increment index
line 89~ transfer into accumulator
line 9~~ clear Carry flag
line 9l~ add to low byte of vector address
line 92~ save result
line 93~ clear accumulator
line 94~ add carry to high byte of vectored address
line 95~ and save the result
line 96~ initialize index
line 97~ add two to X to move onto next address in the
line 98~ ., command address table
line 99~ set Carry flag and force a branch to line 760
line l~l~ get low byte of command execution address
line l~2~ save it in a vector
line l~3~ increment index
line l~4~ get high byte of command execution address
line l~5~ save it in vector
line l~6~ jump to vectored address to execute machine code
of identified command
line l~65 entry for ILLEGAL-unrecognized WOS command
line l~7~ get error code into X register
line l~8~ and jump to error handling routine

19
3 ASCII to Binary
Conversions

An important aspect of interactive machine code is the ability to


convert strings of ASCII characters into their hexadecimal equi-
valents, so that they may be manipulated by the processor. In this
chapter we shall examine, with program examples, how this is
performed. The routines provide the following conversions:

1. Single ASCII hex characters into binary.


2. Four ASCII hex digits into two hex bytes.
3. Signed ASCII decimal string into two signed hex bytes.

ASCII HEX TO BINARY CONVERSION

This routine converts a hexadecimal ASCII character in the accum-


ulator into its four-bit binary equivalent. For example, if the
accumulator contains $37 (that is, ASC"7"), the routine will result
in the accumulator holding $7, or 00000111 binary. Similarly, if the
accumulator holds $46 (ASC"F") the routine will return $F, or
~1111 , in the accumulator.
Conversion is quite simple, and Table 3.1 gives some indication
of what is required.

Table 3.1

Hex Binary value ASCII value ASCII binary

$3 11
1 1 $31 ll1
2 1 $32 1l1
3 11 $33 1111
4 1 $34 111
20
Table 3.1 (contd.)
5 (lj(lj(lj(lj(lj 1(lj1 $35 (lj(ljll(lj1(lj1
6 (lj(lj(lj(lj(lj11(lj $36 (lj(ljll(ljll(lj
7 (lj(lj(lj(lj(ljll1 $37 (lj(ljll(ljll1
8 (lj(lj(lj(lj1(lj(lj(lj $38 (lj(lj111(lj(lj(lj
9 (lj(lj(lj(lj1(lj(lj1 $39 (lj(lj1ll(lj(lj1
A (lj(lj(lj(lj1(lj1(lj $41 (lj1(lj(lj(lj(lj(lj1
B (lj(lj(lj(lj 1(lj1 1 $42 (lj1(lj(lj(lj(lj1(lj
C (lj(lj(lj(lj11(lj(lj $43 (lj1(lj(lj(lj(lj11
D (lj(lj(lj(lj ll(lj1 $44 (lj1(lj(lj(lj1(lj(lj
E (lj(lj(lj(ljll1(lj $45 (lj1(lj(lj(lj 1(lj1
F (lj(lj(lj(ljllll $46 (lj1(lj(lj1l(lj

The conversion of ASCII characters 0 to 9 is straightforward. All


we need to do is mask off the high nibble of the character's ASCII
code. For example ASC "1" is $31 or 00110001 binary-masking
the high nibble with AND $OF results in 00000001. Converting
ASCII characters A and F is a little less obvious, however. If the
high nibble of the code is masked off, then the remaining bits are 9
less than the hex required. For example, the ASCII for the letter
'D' is $44 or 01000100. Masking the high nibble with AND $OF
gives 4, or 00000100, and adding 9 to this gives:

(lj(lj(lj(lj1(lj(lj
+ (lj(lj(lj(lj1(lj(lj1

the binary value for $D.

Program 2

1(lj REM ** CONVERT ASCII CHARACTER IN **


2(lj REM ** ACCUMULATOR TO BINARY **
3(lj REM ** REQUIRES 2(lj BYTES OF MEMORY **
4(lj
5(lj CODE=49152
6(lj FOR LOOP=(lj TO 2(lj
7(lj READ BYTE
8(lj POKE CODE+LOOP,BYTE
9(lj NEXT LOOP
1(lj(lj

21
11.0 REM ** M/C DATA **
12.0 DATA 2.01,48 REM CMP #$3.0
13.0 DATA 144,15 REM BCC $F
14.0 DATA 2.01,58 REM CMP #$3A
15.0 DATA 144,8 REM BCC $.08
16.0 DATA 233,7 REM SBC #$.07
17.0 DATA 144,7 REM BCC $.07
18.0 DATA 2.01,64 REM CMP #$4.0
19.0 DATA 176,2 REM BCS $.02
2.0.0 REM ZERO-NINE
21.0 DATA 41,15 REM AND #$F
22.0 REM RETURN
23.0 DATA 96 REM RTS
24.0 REM ILLEGAL
25.0 DATA 56 REM SEC
26.0 DATA 96 REM RTS
27.0
28.0
29.0 REM ** TESTING ROUTINE **
3.0.0 TEST=49184
31.0 FOR LOOP= TO 14
32.0 READ BYTE
33.0 POKE TEST+LOOP,BYTE
34.0 NEXT LOOP
35.0
36.0 REM ** M/C TEST DATA **
37.0 REM TEST
38.0 DATA 32,228,255 REM JSR $FFE4
39.0 DATA 24.0,251 REM BEQ $FB
4.0.0 DATA 32,.0,192 REM JSR $C
41.0 DATA 144,2 REM BCC $.02
42.0 DATA 169,255 REM LDA #$FF
43.0 REM OVER
44.0 DATA 133,251 REM STA $FB
45.0 DATA 96 REM RTS
46.0
47.0 PRINT CHR$(147)
48.0 PRINT"HIT A HEX CHARACTER KEY, AND ITS
BINARY"
49.0 PRINT"EQUIVALENT VALUE WILL BE PRINTED"
22
Yes

Yes

No

SUBTRACT7TO
MOVE TO
A-F

Yes

SET CARRY
TO DENOTE
ERROR
MASK HIGH
NIBBLE

Figure 3.1 Conversion flowchart

5
51 SYS TEST
52
53 PRINT "RESULT = "PEEK(251)

Program 2 contains a short demonstration, prompting for a hexa-


decimal value key to be pressed (i.e. 0 to F) and returning its
hexadecimal code. Thus, pressing the 'A' key will produce a result
of 41. 23
The ASCII-BINARY routine begins by checking for 'the legality
of the character, by comparing it with 48 ($30) . If the value in the
accumulator is less than ASC"0", the Carry flag will be cleared,
signalling an error. If the character is legal, the contents are then
compared with 58 ($3A), which is one greater than the ASCII code
for 9. This part of the routine ascertains whether the accumulator's
contents are in the range $30 to $39. If they are. the Carry flag will
be cleared and the branch to ZERO-NINE (lines 150 and 120)
performed. The high nibble is then masked off to complete the
conversion .
If the branch of line 150 fails. a legality check for the hex
characters A to F is performed. This is done by subtracting 7 from
the accumulator's contents. which should bring the value it holds
down below 64 ($40), or one less than the ASCII code for the letter
'A'. At this point the Carry flag is set (it was previously set as the
branch of the previous line was not performed). and the CMP#$40
of line 180 clears it if the contents are hight:;r than 64. The routine
then masks off the high nibble. leaving the correct binary.
The following example shows how the conversion of ASC'F" to
$F ~'orks:

Mnemonic Accumulator Carry flag

$46 (ASC"F")
CMP #$3.0 $46 1
BCC ILLEGAL
CMP 1I$3A $46 1
BCe ZERO-NINE
SBC #7 $3F 1
BCC ILLEGAL
CMP 11$4.0 $3F .0
BCS RETURN
AND $.0F $.0F .0
RTS

Note that this routine indicates an error by returning with the Carry
flag set. so any calls to the conversion routine should always check
for this on return. The short test routine does this. and loads the
accumulator with $FF to signal the fact.
Using two calls to this routine would allow two-byte hex values to
be input and converted into a full eight-byte value. On completion
of the first call. the accumulator's contents would need to be shifted
into the high nibble.

24
The coding might look like this:

REM WAIT
JSR GETIN REM GET FIRST CHARACTER
BEQ WAITl
JSR ASCII-BINARY REM CONVERT TO BINARY
BCS REPORT-ERROR REM NON-HEX IF C=l
ASL A REM MOVE INTO HIGHER
NIBBLE
ASL A
ASL A
ASL A
STA HIGH-NIBBLE REM SAVE RESULT
REM WAIT2
JSR GETIN REM GET SECOND CHARACTER
BEQ WAIT2
JSR ASCII-BINARY REM CONVERT TO BINARY
BCS REPORT-ERROR REM NON-HEX IF C=l
ORA HIGH-NIBBLE REM ADD HIGH NIBBLE
REM ALL BINARY NOW IN
ACCUMULATOR
Using this routine and entering, say, $FE will return 11111110 in
the accumulator.

Line-by-Iine

A line-by-line description of Program 2 follows:

line 12.0 is it > = than ASC"0"?


line 13.0 no, branch to ILLEGAL
line 14.0 is it in range 0-9?
line 15.0 yes, branch to ZERO-NINE to skip A-F
translation.
line 16.0 move onto ASCII codes for A - F
line 17.0 branch to ILLEGAL if Carry flag clear
line 18.0 is it higher than ASC" @"?
line 19.0 no, branch to ILLEGAL
line 2.0.0 entry for ZERO-NINE
line 21.0 clear high nibble
line 22.0 entry for RETURN
25
line 23~ return with binary In accumulator
line 24~ entry for ILLEGAL
line 25~ set Carry flag to denote an error
line 26~ return to BASIC
line 37~ entry for TEST
line 38~ read keyboard
line 39~ if null string, branch to TEST
line 4~~ call conversion at $C000
line 41~ if no errors, branch OVER
line 42~ else error, place 255 in accumulator
line 43~ entry for OVER
line 44~ save accumulator in $FB
line 45~ and return to BASIC

FOUR ASCII DIGITS TO HEX

We can use the ASCII-BINARY routine as the main subroutine in


a piece of coding which will convert four ASCII digits into a
two-byte hexadecimal number, making the routine most useful for
inputting two-byte hexadecimal addresses. For example, the
routine would convert the ASCII string "CAFE" into a two-byte
binary number 11001010 11111110 or $CAFE. Program 3 lists the
entire coding:

Program 3

1~ REM ** CONVERT FOUR ASCII DIGITS INTO **


2~ REM ** A TWO-BYTE HEXADECIMAL NUMBER **
3~ CODE=49152
4~ FOR LOOP=O TO 62
5~ READ BYTE
6~ POKE CODE+LOOP,BYTE
7~ NEXT LOOP
8~
9~ REM ** MIC DATA **
l~~ DATA 16~,~ REM LDY I~
ll~ DATA 162,251 REM LDX I$FB
12~ DATA 148,~ REM STY $0~,X
13~ DATA 148,1 REM STY $~l,X
14~ DATA 148,2 REM STY $~2,X
15~ REM NEXT-CHARACTER
26
160 DATA 185,60,3 REM LDA $33C,Y
170 DATA 32,42 , 192 REM JSR $C02A
180 DATA 176 , 21 REM BCS $15
190 DATA 10,10 REM ASL A : ASLA
200 DATA 10,10 REM ASL A : ASLA
210 DATA 148,2 REM STY $02,X
220 DATA 160 , 4 REM LDY l' $04
225 REM AGAIN
230 DATA 10 REM ASL A
240 DATA 54,0 REM ROL $00,X
250 DATA 54,1 REM ROL $01,X
260 DATA 136 REM DEY
270 DATA 208,248 REM BNE $F8
280 DATA 180,2 REM LDY $02,Y
290 DATA 200 REM INY
300 DATA 208,227 REM BNE $E3
310 :: REM ERROR
320 DATA 181 , 2 REM LDA $02,X
330 DATA 96 REM RTS
340
350 REM *** ASCII-BINARY CONVERSION ***
360 DATA 201,48 REM CMP #$30
370 DATA 144 , 15 REM BCC $0F
380 DATA 201,58 REM CMP #$3A
390 DATA 144,8 REM BCC $08
400 DATA 233,7 REM SBC $07
410 DATA 144,7 REM BCC $07
420 DATA 201,64 REM CMP /1$40
430 DATA 176,2 REM BCS $02
440 REM ZERO-NINE
450 DATA 41,15 REM AND $0F
460 REM RETURN
470 DATA 96 REM RTS
480 REM ILLEGAL
490 DATA 56 REM SEC
500 DATA 96 REM RTS
510
520 REM *** SET UP A TEST PROCEDURE ***
530 TEST =49232
540 FOR LOOP =0 TO 34
27
55 READ BYTE
56 POKE TEST+LOOP,BYTE
57 NEXT LOOP
58
59 REM ** TEST M/C DATA **
6 DATA 16, REM LDY #$
61 DATA 162,4 REM LDX #$4
62 REM OVER
63 DATA 142,52,3 REM STX $334
64 DATA 14,53,3 REM STY $335
65 REM INNER
66 DATA 32,228,255 REM JSR $FFE4
67 DATA 24,251 REM BEQ $FB
68 DATA 174,52,3 REM LDX $334
69 DATA 172,53,3 REM LDY $335
7 DATA 153,6,3 REM STA $33C,Y
71 DATA 32,21,255 REM JSR $FFD2
72 DATA 2 REM INY
73 DATA 22 REM DEX
74 DATA 28,229 REM BNE $E5
75 DATA 32,,192 REM JSR $C
76 DATA 96 REM RTS
77
78 PRINT CHR$(147)
79 PRINT "INPUT A FOUR DIGIT HEX NUMBER $";
8 SYS TEST
81 PRINT
82 PRINT "THE FIRST BYTE WAS :";PEEK(251)
83 PRINT "THE SECOND BYTE WAS :";PEEK(252)

The machine code begins by clearing three bytes of zero page RAM
pointed to by the contents of the X register (lines 100 to 140). The
ASCII characters are accessed one by one from a buffer which may
be resident anywhere in memory (line 1(0), though in this case it is
the four bytes at the start of the cassette buffer. Conversion and
error-detection are performed (lines 170 and 180) and the four
returned bits shifted into the high four bits of the accumulator. The
buffer index, which keeps track of the character position in the
buffer, is saved in the third of the three bytes cleared.
The loop between lines 250 and 300 is responsible for moving the
four bits through the two zero page bytes which hold the final
result. In fact, with the accumulator, the whole process of the loop
is to perform the operation of a 24-hit "hift rpaister. Figure 3.2
28
Accumulator
ASLA

Carry n---1111111 1- 0

~----.-.---------------,
ROL0. X

Carry

ROL I. X

carryD

Figure 3.2 Movement of bits through a 3-byte shift register

illustrates the procedure.


The ASL A instruction shuffles the bits in the accumulator one
bit to the left, with the dislodged bit 7 moving across into the Carry
flag bit. This carry bit is then rotated into bit 0 of the result address
low byte, which in turn rotates its bit 7 into the Carry flag. The next
ROL instruction repeats this movement on the high byte. The net
effect of all this is that as the process is executed four times, the
returned conversions are shifted through the result address to
reside in the correct place, as Figure 3.3 illustrates.

I,X 0,X Accumulator

Entry ~ f/1IJ11/1/1/1II 1111~

1st l"ass ~ ~1111 f/WI/111/1/1/J


2nd pass f/1I1lI1JlJI1/1 1111~ f/1I1lI1JlJI1/1
3rd pass ~1111 f/111!II11IIIl1 f/WI/111/1/1/J
4th pass 1111~ f/1IJ11/1/1/1II f/WI/111/1/1/J

Figure 3.3 A 24-bit shift register, showing passage of the bits in the
number$F~

Error-checking is provided for, the routine aborting when it


encounters an illegal hex character, leaving the accumulator con-
taining the index into the buffer, pointing to the illicit value. In fact.
this method is used to complete the execution of the conversion-
rotate loop, using a RETURN character placed at the end of the
29
ASCII hex string.
The test routine (lines 590 to 800) prompts for four hex-based
characters to be input. These are placed in the buffer (line 610) and
printed to the VDU. On completion of the input, the address-
binary routine is called, and the result placed in the first two bytes
of the user area, for printing or manipulation purposes.

Line-by-line

A line-by-line decription of Program 3 follows:

line 100 clear indexing register


line 110 get byte destination
line 120 clear three bytes
line 150 entry for NEXT-CHARACTER
line 160 get character from buffer
line 170 call ASCII-BINARY to convert
line 180 branch to ERROR if Carry flag is set
line 190 move low nibble into high nibble
line 210 save index into buffer
line 220 moving four bits
line 225 entry for AGAIN
line 230 move bit 7 into Carry flag
line 240 move carry into bit 0 and bit 7 into Carry flag
line 250 move carry into bit 0 and bit 7 into Carry flag
line 260 decrement bit count
line 270 and do until four bits done
line 280 restore index into buffer
line 290 increment it to point to next character
line 300 do branch to NEXT-CHARACTER
line 310 entry for ERROR
line 320 get illegal character
line 330 return to calling routine

CONVERT DECIMAL ASCII STRING TO BINARY

This routine takes a signed decimal string of ASCII characters and


transfonns it into a two-byte hexadecimal number. For example,
entering -32,678 will return the value $8000, where $8000 is its
signed binary equivalent. Entry requirements to the conversion
routine are obtained by the BASIC text in lines 880 to 940. Note

30
that in addition to obtaining the characters for insertion into the
string buffer, the number of characters for conversion is required,
this being placed in the first byte of the buffer.

Pmgram4

10 REM ** DECIMAL ASCII TO BINARY **


20 REM ** READ & POKE M/C DATA **
30 CODE=49152
40 FOR LOOP=0 TO 155
50 READ BYTE
60 POKE CODE+LOOP,BYTE
70 NEXT LOOP
80
90 REM ** M/C DATA **
100
110 DATA 174,60,3 REM LDX $33C
120 DATA 208,3 REM BEQ $03
125 DATA 76,154,192 REM JMP $C09A
130 DATA 160,0 REM LDY #0
140 DATA 140,55,3 REM STY $337
150 DATA 140,53,3 REM STY $335
160 DATA 150,54,3 REM STY $336
170 DATA 200 REM INY
180 DATA 140,52,3 REM STY $334
190 DATA 185,60,3 REM LDA $33C,Y
200 DATA 201,45 REM CMP #$2D
210 DATA 208,14 REM BNE $0E
220 DATA 169,255 REM LDA #&FF
230 DATA 141,55,3 REM STA $337
24~ DATA 238,52,3 REM INC $334
250 DATA 202 REM DEX
260 DATA 240,113 REM BEQ $71
270 DATA 76,54,192 REM JMP $C036
280 REM POSITIVE
290 DATA 201,43 REM CMP #$2B
300 DATA 208,12 REM BNE $06
310 DATA 238,52,3 REM INC $334
320 DATA 202 REM DEX
330 DATA 240,100 REM BEQ $64

31
340 REM CONVERT-CHARACTER
350 DATA 172,52,3 REM LDY $334
360 DATA 185,60,3 REM LDA $33C,Y
370 REM CHECK-LEGALITY
380 DATA 201,58 REM CMP 1I$3A
390 DATA 16,90 REM BPL $5A
400 DATA 201,48 REM CMP 11$30
410 DATA 48,86 REM BMI $56
420 DATA 72 REM PHA
430 DATA 14,53,3 REM ASL $335
440 DATA 46,54,3 REM ROL $336
450 DATA 173,53,3 REM LDA $335
460 DATA 172,53,3 REM LDY $336
470 DATA 14,53,3 REM ASL $335
480 DATA 46,54,3 REM ROL $336
490 DATA 14,53,3 REM ASL $335
500 DATA 46,54,3 REM ROL $336
510 DATA 24 REM CLC
520 DATA 109,53,3 REM ADC $335
530 DATA 141,53,3 REM STA $335
540 DATA 152 REM TYA
550 DATA 109,54,3 REM ADC $336
560 DATA 141,54,3 REM STA $336
570 DATA 56 REM SEC
580 DATA 104 REM PLA
590 DATA 233,48 REM SBC 11$30
600 DATA 24 REM CLC
610 DATA 109,53,3 REM ADC $335
620 DATA 141,53,3 REM STA $335
630 DATA 144,3 REM BCC $03
640 DATA 238,54,3 REM INC $336
650 REM NO-CARRY
660 DATA 238,52,3 REM INC $334
670 DATA 202 REM DEX
680 DATA 208,181 REM BNE $B5
690 DATA 173,55,3 REM LDA $337
700 DATA 16,17 REM BPL $11
710 DATA 56 REM SEC
720 DATA 169,0 REM LDA 110
730 DATA 237,53,3 REM SBC $335
32
GET
FIRST
CHARACTER

SET SIGN CALCULATE


FLAG ABSOLUTE
ADDRESS

SET
ERROR END
FLAG

CONVERT
TO
BINARY

ROTATE
SHIff
REGISTER~

No

Figure 3.4 A SCI/ string to binary conversion flowchart

33
74 DATA 141,53,3 REM STA $335
75 DATA 169, REM LDA #
76 DATA 237,54,3 REM SBC $336
77 DATA 141,54,3 REM STA $336
78 REM NO-COMPLEMENT
79 DATA 24 REM CLC
8 DATA 144,1 REM BCC $1
81 REM ERROR
82 DATA 56 REM SEC
83 REM FINISH
84 DATA 96 REM RTS
85
86 REM ** SET UP SCREEN AND GET NUMBER **
87 PRINT CHR$(147)
88 INPUT"NUMBER FOR CONVERSION";A$
89 FOR LOOP=l TO LEN(A$)
9 TEMP$=MID$(A$,LOOP,1)
91 B=ASC(TEMP$)
92 POKE 828+LOOP,B
93 NEXT LOOP
94 POKE 828,LEN(A$)
95
96 SYS CODE
97
98 PRINT"THE TWO BYTES ARE AS FOLLOWS"
99 PRINT"LOW BYTE ";PEEK(821)
1 PRINT"HIGH BYTE ";PEEK(822)

Bytes are designated as follows:

829' ($334) string index


821 ($335) current count
823 ($336) sign flag
828 ($33C) length of string
829 ($330) start of character string

The machine code begins by obtaining the character count from the
X register. An error is signalled if this count is zero, otherwise the

34
program progresses, clearing the sign flag (used to signal positive or
negative values) and result destination bytes at 'current' (lines 130
to 160). Location $70 is used to hold the string index, pointing to
the next character for conversion. This byte is initially loaded with
1 so that it skips over the count byte in the buffer.
The first byte of the string is tested for a '+' or '-' sign, the
former being an optional item in the string, and the sign flag is set
accordingly (lines 190 to 230). The CONVERT-CHARACTER
loop starts by testing the character about to be manipulated to
ensure it is a decimal value, i.e. 0 to 9 inclusive. Converting the byte
into binary form is achieved by multiplying the byte by 10. This
multiplication is readily available using four arithmetic shifts and
an addition: 2 * 2 * 2 + 2 = 10.
Because we are dealing with a two-byte result, the arithmetic
shift must be performed on the two bytes, allowing bits to be
transferred from one byte to the other. This is performed by using
an ASL followed by a ROL. As figure 3.5 illustrates, this acts
exactly like a 16-bit ASL. The first pass through this character-
conversion loop has little effect, as it is operating on characters
already converted, of which there are none first time round!
Lines 570 to 620 carry out the conversion of ASCII to binary and
store the result. This is performed, as we know from earlier
examples, by masking off the high nibble. Another technique for
doing this is simply to subtract the ASCII code for '0': $30.

ASL high byte

-0

Carry ROL low byte

Figure 3.5 A16-bit arithmetic shift

Once all the characters have been processed, the sign flag at $334
(820) is checked for a negative value. If this is indicated (lines 690
and 700), the value of current is subtra~ted from zero, thereby
converting the absolute value into a signed negative byte (lines 710
to 770). The Carry flag is used to indicate any error condit~ons-if
it is set an error occurred, and the string index at $334 points to the
illegal character.

35
Line-by-Iine

A line-by-line description of Program 4 now follows:

line ll get length of string


line 12 branch if not zero
line 125 else jump to ERROR
line 13 clear Y register
line 14 sign flag
line 15 and store bytes
line 17 increment Y
line 18 set index to first ASCII character
line 19 get first character
line 2 is it a minus sign?
line 21 no, branch to POSITIVE
line 22 yes, get negative byte
line 23 and set the sign flag
line 24 move to next character
line 25 decrement length counter
line 26 branch to ERROR if zero
line 27 else jump to CONVERT-CHARACTER
line 28 entry for POSITIVE
line 29 is first character a +?
line 3 no, branch to CHECK-LEGALITY
line 31 yes, move to next character
line 32 decrement length counter
line 33 branch to ERROR if zero
line 34 entry for CONVERT-CHARACTER
line 35 restore index
line 36 get character from buffer
line 37 entry for CHECK-LEGALITY
line 38 is it <= ASC"9"?
line 39 no, it's bigger, branch to ERROR
line 4 is it > = ASC"0"?
line 41 no, branch to ERROR
line 42 save code on stack
line 43 multiply both bytes by two
line 45 save low byte

36
line 46 save high byte
line 47 multiply by two again (now *4)
line 49 and again (now *8)
line 5l clear Carry flag
line 52 add low byte *2
line 53 and save result
line 54 transfer high byte *2
line 55 and add to *8 high byte
line 56 save it. Now *10
line 57 set Carry flag
line 58 restore ASCII code from stack
line 59 convert ASCII to binary
line 6 clear Carry flag
line 61 add it to low byte current
line 62 save result
line 63 branch if NO-CARRY
line 64 else increment high byte
line 65 entry for NO-CARRY
line 66 move index on to next byte
line 67 decrement length counter
line 68 branch to CONVERT-CHARACTER if not finished
line 69 completed so get sign flag
line 7 if clear branch to NO-COMPLEMENT
line 7l else set Carry flag
line 72 clear accumulator
line 73 and obtain two's complement
line 74 save low byte result
line 75 clear accumulator
line 76 subtract high byte from 0
line 77 and save result
line 78 entry for NO-COMPLEMENT
line 79 clear Carry flag
line 8 and force branch to FINISH
line 81 entry for ERROR
line 82 set Carry flag to denote error
line 83 entry for FINISH
line 84 return to BASIC

37
4 Binary to Hex ASCII

This chapter complements the previous one and illustrates how


memory-based hex values can be converted into their ASCII repre-
sentation. The routines provide the following conversions:

1. Print accumulator as two ASCII hex characters.


2. Print two hex bytes as four ASCII hex characters.
3. Print two-byte signed binary number as signed decimal
number.

PRINT ACCUMULATOR

To convert an eight-bit binary number into its ASCII hex equi-


valent characters, the procedure described in Chapter 3 must be
reversed. However, because text is printed on the screen from left
to right, we must deal with the high nibble ofthe byte first. Program
5 uses the hexprint routine to print the hexadecimal value of any
key pressed at the keyboard.

ProgramS

1 REM ** PRINT ACCUMULATOR AS A HEX NUMBER **


2
3 CODE=49152
4 FOR LOOP= TO 21
5 READ BYTE
6 POKE CODE+LOOP,BYTE
7 NEXT LOOP
8
9 REM ** MIC DATA **
1
38
ll0 DATA 72 REM PHA
120 DATA 74, 74 REM ASL A : ASLA
130 DATA 74, 74 REM ASL A : ASLA
140 DATA 32,9,192 REM JSR $C009
150 DATA 104 REM PLA
160 REM FIRST $C009
170 DATA 41,15 REM AND #$0F
180 DATA 201,10 REM CMP #$0A
190 DATA 144,02 REM BCC $02
200 DATA 105,6 REM ADC #$06
210 REM OVER
220 DATA 105,48 REM ADC #$30
230 DATA 76,210,255 REM JMP $FFD2
240
250 REM ** SET UP DEMO AT 828 **
260 REM LDA $FB JMP $C000
270 POKE 828,165 : POKE 829,251
280 POKE 830,76 : POKE 831,0 : POKE 832,192
290'PRINT CHR$(147)
300 PRINT "HIT ANY KEY AND ITS HEX VALUE IN"
310 PRINT "ASCII WILL BE DISPLAYED"
320 GET A$:- IF A$","" THEN GOTO 320
330 A=ASC(A$)
340 POKE 251,A
350
360 SYS 828
370 REM CALL 'SYS CODE' TO USE DIRECTLY

The hexprint routine is embedded between lines 110 and 230. The
accumulator's contents are first pushed on to the hardware stack.
This procedure is necessary as it will have to be restored before the
second pass, which calculates the ASCII code for the second char-
acter. The first pass through the routine sets about moving the
upper nibble of the accumulator byte into the lower nibble (lines
120 and 130). The FIRST subroutine ensures that the high nibble is
cleared by logically ANDing it with $0F. This is, of course, surplus
to requirement on the first pass, but is needed on the second pass to
isolate the low nibble. Comparing the accumulator's contents with
10 will ascertain whether the value is in the range 0 to 9 or A to F. If
the Carry flag is clear, it falls in the lower range (0 to 9) and simply
setting bits 4 and 5, by adding $30, will give the required ASCII
code. A further 7 must be added to skip non-hex ASCII codes to
arrive at the ASCII codes for A to F ($41 to $46). You may have
39
noticed that line 200 does not add 7 but in fact adds one less, 6. ThIs
is because. for this section of coding to be executed, the carry must
have been set, and the 6510 addition opcode references the Carry
flag in addition. Therefore, the addition performed is: accumulator
+ 6 + 1.
The JMP of line 230 will return the program back to line 150.
Remember, FIRST was called with a JSR, so the RTS from com-
pletion of the CHROUT call returns control here. The accumu-
lator is restored and the process repeated for the second ASCII
digit.
A short test routine is established in lines 250 to 340. This
requests you to hit a key, the value of which is placed in a free zero
page byte. The 'hand-POKEd' routine at 828 is called by line 360,
and puts the key's value into the accumulator before performing a
jump to the main routine.
The following example illustrates the program's operation,
assuming the accumulator holds the value 01001111, $4F:

Mnemonic Accumulator Carry flag

$4F
LSR A $27 1
LSR A $13 1
LSR A $9 1
LSR A $4 1
JSR FIRST
AND #$F $4 1
CMP #$A $4
BCC OVER
OVER
ADC #$3 $34 (ASC"4")
JMP CHROUT
PLA $4F
AND #$F $F
CMP #$A

Line-by-line

A line-by-line description of Program 5 follows:

line ll save accumulator on stack


line l2 move high nibble into low nibble
40
line 14 call FIRST subroutine
line 15 restore accumulator
line 16 entry for FIRST
line 17 ensure only low nibble set
line 18 is it < 10?
line 19 yes, branch to OVER
line 2 no, add 7, value $A to $F
line 21 entry for OVER
line 22 add 48 to convert to ASCII code
line 23 and print, returning to line 140 or BASIC

PRINT A HEXADECIMAL ADDRESS

The hexprint routine can be extended to enable two zero page


bytes to be printed out in hexadecimal form. This is an especially
important procedure when writing machine based utilities, such as
a hex dump or disassembler. The revamped program is listed
below:

Program 6

l REM ** PRINT TWO HEX BYTES AS **


2 REM ** A TWO-BYTE ADDRESS **
3 CODE=49152
4 FOR LOOP= TO 34
5 READ BYTE
6 POKE CODE+LOOP,BYTE
7 NEXT LOOP
8
9 REM ** M/C DATA **
l REM ** CALL WITH $FB,$FC HOLDING BYTES **
ll REM ADDRESS-PRINT
12 DATA 162,251 REM LOX #$FB
13 DATA 181,1 REM LOA $l,X
14 DATA 32,13,192 REM JSR $CD
15 DATA 181, REM LOA $,X
16 DATA 32,13,192 REM JSR $CD
17 DATA 96 REM RTS
18 REM HEXPRINT
19 DATA 72 REM PHA
2 DATA 74,74 REM LSR A LSR A
41
210 DATA 74,74 REM LSR A : L::>K A
220 DATA 32,22,192 REM JSR $C016
230 DATA 104 REM PLA
240 REM FIRST
250 DATA 41,15 REM AND #$0F
260 DATA 201,10 REM CMP #$0A
270 DATA 144,2 REM BCC $02
280 DATA 105,6 REM ADC #$06
290 REM OVER
300 DATA 105,48 REM ADC #$30
310 DATA 76,210,255 REM JMP $FFD2

Zero paged indexed addressing is used to access the two bytes, the
crucial location being given in the X, register, which acts as the
index for the high byte, LDA $01,X (Iine130), and the low byte,
LDA $OO,X (line 150). The all-important address in this instance is
$FB (line 130), so the bytes accessed by ADDRESS-PRINT are
$FB ($FB+0) and $FC ($FB+1). Using this method, various
addresses can be housed within zero page and anyone reached
simply by seeding the X register with the location value.

Project

Adapt Program 6 to accept a five character decimal number from


the keyboard, printing its hexadecimal value on the screen.
Remember-no BASIC, and the input routine must be able to
accept numbers in the range 0 to 65!

BINARY SIGNED NUMBER TO SIGNED ASCII


DECIMAL STRING
This conversion utility takes a two-byte hexadecimal number and
converts it into its equivalent decimal based ASCII character
string. For example, if the two-byte value is $7FFF, the decimal
string is 32,767, $7FFF being 32,767 in decimal. The coding uses
signed binary values so that if the most significant bit is set, a
negative value is interpreted. This is relayed in the string with a
minus sign. This means that the routine can handle values in the
range 32,767 to -32,768. When using the routine, remember that
the two's complement representation is used, so that a hex value of
$FFFF is converted to the string -1, and $8000 returns the char-
acter string -32,767.
The two address bytes are located at $334 and $335 and the string
buffer from $FB onwards. The length of the string buffer will vary,
but its maximum length will not exceed six digits, so this number of
bytes should be reserved.

42
SHIITHIGH
NIBBLE INTO
LOWER
NIBBLE

CONVERT
FIRST
DIGIT

Yes

ADD6TO
CONVERT TO
A-F

CONVERT
DIGIT TO
ASCII

END

Figure 4. J Hex to ASCll conversion flowchart

43
Program 7

10 REM ** BINARY SIGNED NUMBER CONVERSION **


20 REM ** INTO SIGNED DECIMAL ASCII STRING **
30 CODE=49152 : OUTPUT =49301
40 FOR LOOP =0 TO 163
50 READ BYTE
60 POKE CODE +LOOP . BYTE
70 NEXT LOOP
80
90 REM ** M/C DATA **
100 DATA 160.0 REM LDY #$00
110 DATA 152 REM TYA
120 DATA 133 . 251 REM STA $FB
130 DATA 133 . 252 REM STA $FC
140 DATA 133 . 253 REM STA $FD
150 DATA 133 . 254 REM STA $FE
160 DATA 133 . 255 REM STA $FF
170 DATA 173.53 . 3 REM LDA $335
180 DATA 141 . 56 . 3 REM STA $338
190 DATA 16 . 15 REM BPL $0F
200 DATA 56 REM SEC
210 DATA 152 REM TYA
220 DATA 237.52.3 REM SBC $334
230 DATA 141.52.3 REM STA $334
240 DATA 152 REM TYA
250 DATA 237 . 53.3 REM SBC $335
260 DATA 141 . 53 . 3 REM STA $335
270 REM CONVERSION
280 DATA 169.0 REM LDA #$00
290 DATA 141.54.3 REM STA $336
300 DATA 141.55 . 3 REM STA $337
310 DATA 24 REM CLC
320 DATA 162 . 16 REM LDX /l i $10
330 REM LOOP
340 DATA 46 . 52.3 REM ROL $334
350 DATA 46 . 53 . 3 REM ROL $335
360 DATA 46 . 54 . 3 REM ROL $336
370 DATA 46 . 55 . 3 REM ROL $337
380 DATA 56 REM SEC
44
SAVE
SIGN
BIT

CALCULATE
ABSOLUTE
VALUE

No

CONCATENATE
CHARACTER
TO STRING

No

No

Figure 4.2 Binary to ASCIl string conversion flowchart

45
399.1 DATA 173 , 54,3 REM LDA $336
49.19.1 DATA 233 , 19.1 REM SBC #$A
419.1 DATA 168 REM TAY
429.1 DATA 173,55,3 REM LDA $337
439.1 DATA 233,9.1 REM SBC #$9.19.1
449.1 DATA 144,6 REM BCC $9.16
459.1 DATA 149.1,54,3 REM STY $336
469.1 DATA 141,55 , 3 REM STA $337
479.1 REM LESS-THAN
489.1 DATA 29.12 REM DEX
499.1 DATA 29.18,221 REM BNE $DD
59.19.1 DATA 46,52,3 REM ROL $334
519.1 DATA 46,53,3 REM ROL $335
529.1 REM ADD-ASCII
539.1 DATA 24 REM CLC
549.1 DATA 173,54,3 REM LDA $336
559.1 DATA 19.15,48 REM ADC #$39.1
569.1 DATA 32,116,192 REM JSR $C74
579.1 DATA 173,52 , 3 REM LDA $334
589.1 DATA 13,53,3 REM ORA $335
599.1 DATA 29.18 , 187 REM BNE $BB
69.19.1 REM FINISHED
619.1 DATA 173 , 56 , 3 REM LDA $338
629.1 DATA 16,5 REM BPL $9.15
639.1 DATA 169,45 REM LDA #$2D
649.1 DATA 32,116 , 192 REM JSR $C74
659.1 REM POSITIVE
669.1 DATA 96 REM RTS
670 REM SUBROUTINE TO FORM ASCII CHARACTER
STRING IN $FB
689.1 :: REM CONCATENATE
699.1 DATA 72 REM PHA
79.19.1 DATA 169.1,9.1 REM LDY #$9.19.1
719.1 DATA 185,251,9.1 REM LDA $FB,Y
729.1 DATA 168 REM TAY
739.1 DATA 249.1,11 REM BEQ $B
749.1 REM SHUFFLE-ALONG
759.1 DATA 185,251,9.1 REM LDA $FB,Y
769.1 DATA 29.19.1 REM INY
779.1 DATA 153,251,9.1 REM STA $FB,Y
46
78 DATA 136,136 REM DEY : DEY
79 DATA 28,245 REM BNE $F5
8 REM ZERO-FINISH
81 DATA 14 REM PLA
82 DATA 16,1 REM LDY #$1
83 DATA 153,251, REM STA $FB,Y
84 DATA 136 REM DEY
85 DATA 182,251 REM LDX $FB,Y
86 DATA 232 REM INX
87 DATA 15,251 REM STX $FB,Y
88 DATA 96 REM RTS
890 REM STRING PRINTING ROUTINE
9 REM STRING-PRINT
91 DATA 166,251 REM LDX $FB
92 DATA 16,1 REM LDY #$1
93 REM PRINT-LOOP
94 DATA 185,251, REM LDA $FB,Y
95 DATA 32,21,255 REM JSR $FFD2
96 DATA 2 REM INY
97 DATA 22 REM DEX
98 DATA 28,246 REM BNE $F6
99 DATA 96 REM RTS
1
11 REM ** GET IN A HEX NUMBER **
12 PRINT CHR$(147) : PRINT
13 PRINT"INPUT A HEX NUMBER :$";
14 GOSUB 2
15 POKE 82,LOW : REM LOW BYTE HEX
NUMBER
16 GOSUB 20
17 POKE 821,HIGH REM HIGH BYTE HEX
NUMBER
18
19 SYS CODE : REM CALL CONVERSION
ll
111 PRINT"ITS DECIMAL EQUIVALENT IS :";
112 SYS OUTPUT
113 END
114
1999 REM ** HEX INPUT CONTROL **
47
2000 GOSUB 2500
2010 F=NUM : PRINT Z$;
2020 GOSUB 2500
2030 S=NUM PRINT Z$;
2040 HIGH=F*16+S
2050 GOSUB 2500
2060 F=NUM : PRINT Z$;
2070 GOSUB 2500
2080 S=NUM : PRINT Z$
2090 LOW=Fd6+S
2100 RETURN
2200
2499 REM ** GET HEX ROUTINE **
2500 GET Z$
2510 IF Z$="" THEN GO TO 2500
2520 IF Z$>"F" THEN GOTO 2500
2530 IF Z$="A" THEN NUM=10: RETURN
2540 IF Z$="B" THEN NUM=ll: RETURN
2550 IF Z$="C" THEN NUM=12: RETURN
2560 IF Z$="D" THEN NUM=13: RETURN
2570 IF Z$="E" THEN NUM=14: RETURN
2580 IF Z$="F" THEN NUM=15: RETURN
2590 NUM=VAL(Z$): RETURN

Functional bytes:

251-255 ($FB-$FF) ASCII string buffer


820-821 ($334-$335) binary address for
conversion
822-823 ($336-$337) temporary storage
824 ($338) sign flag

To demonstrate the routine's workings, the program first prompts


for a hexadecimal number using the BASIC hex loader subroutine
at line 2({1/1/). This is evaluated and placed at BINARY-ADDRESS
by lines 1050 and 1070.
The program proper begins by clearing the string buffer area
(lines 100 to 160), an important procedure which ensures no illicit
characters find their way into the ASCII string. The sign of the
number is tested by loading the high byte of the address byte into
the accumulator and saving its value in the sign flag byte. This
process will condition the Negative flag. If it is set, a negative
number is interpreted and the phiS branch to CONVERSION (line
48
190) fails. The next seven operations obtain the absolute value of
the two-byte number by subtracting it from itself and the set carry
bit. Thus $FFFF will result in an absolute value of 1 and $8~ an
absolute value of 32,678.
The two flows of the program rejoin at line 280, where the two
temporary bytes are cleared. These bytes are used in conjunction
with the binary address bytes to form a 32-bit shift register, allow-
ing bits to flow from the low byte address to the high byte of
temporary.
The loop of lines 340 to 510 performs the conversion, by succes-
sively dividing through by ten until the quotient has a value ofzero.
By this time the binary equivalent of this ASCII character being
processed will have been pla~ed in the temporary byte. To produce
this, the loop needs sixteen iterations so the X register is used to
count these out. Converting the binary to hex involves simply
adding $30 or ASC"0" to it (lines 530 to 550).
Because it may not be immediately clear what is happening,
Table 4.1 shows the values of the accumulator and four associated
bytes after each of the 16 passes of the loop, when converting
$FFFF into its absolute ASCII value of 1. It should be clear from
this how the bits shuffle their way through the four byte 'register'.

Table 4.1

Iteration Accumulator $334 $335 $336 $337

1 1
2 FF 2
3 FF 4
4 FF 8
5 FF 1
6 FF 2
7 FF 4
8 FF 8
9 FF 1
1 FF 1
11 FF 1
12 FF 1
13 FF 1
14 FF 1
15 FF 1
16 FF 1

49
All that is now required is for this character to be added to the
string buffer. This concatenation is completed by the code of lines
690 to 880. This began by obtaining the buffer index, which con-
tains the current number of characters already concatenated. This
is stored in the first byte of the buffer, $FB in this instance. It is then
moved across into the accumulator. Next, lines 750to 790 move any
characters present in the buffer up memory one byte, thereby
opening up a gap of one byte into which the newly formed char-
acter can be placed (lines 810 to 870). The buffer index is also
incremented and restored at this point, before an RTS is made
back to the main body of the program.
End of program operation is tested for by logically ~Ring the
contents of the high and low bytes of the address. If the result is
zero, all bits have been rotated and dealt with, in which case the
sign flag byte is tested to ascertain whether a minus sign need be
placed at the start ofthe ASCII string (lines 600 to 660).

Line-by-line

A line-by-line description of Program 7 follows:

line 100 clear Y register


line 110 and accumulator
line 120 and then the five buffer bytes
line 170 get high byte for conversion
line 180 save in sign flag
line 190 if positive branch to CONVERSION
line 200 else set Carry flag
line 210 clear accumulator
line 220 obtain absolute value of low byte
line 2391 and save
line 240 clear accumulator
line 250 obtain absolute value of high byte
line 260 and save
line 270 entry for CONVERSION
line 280 clear accumulator
line 290 clear temporary storage bytes
line 310 clear Carry flag
line 320 sixteen bits to process
line 330 entry for LOOP
line 340 move bit 7 into Carry flag
line 350 and on into bit 0
line 360 move bit 7 into Carry flag
50
line 37~ and on into bit 0
line 38~ set Carry flag
line 39~ get low byte of temp
line 4~~ subtract 10
line 4l~ save result in Y
line 42~ get high byte of temporary
line 43~ subtract carry bit
line 44~ branch to LESS-THAN if divisor> dividend
line 45~ else save result of operation in temporary
line 47~ entry for LESS-THAN
line 48~ decrement bit count
line 49~ branch to LOOP until 16 bits done
line 5~~ rotate bit 7 into Carry flag
line 5l~ and on into bit 0
line 52~ entry for ADD-ASCII
line 53~ clear Carry flag
line 54~ get low byte from temporary
line 55~ convert into ASCII character
line 56~ concatenate on to string in buffer
line 57~ get low byte of binary number
line 58~ OR with high byte. If 0 then all done
line 59~ if not finished branch to CONVERSION
line 6~~ entry for FINISHED
line 6l~ get sign
line 62~ if N = 0 branch to POSITIVE
line 63~ otherwise get ASC" - "
line 64~ and add it to final string
line 65~ entry for POSITIVE
line 66~ back to BASIC
line 68~ entry for CONCATENATE, $C074
line 69~ save accumulator
line 7~~ initialize index
line 7l~ and get buffer length
Hne 72~ move it into Y for indexing
line 73~ if 0 branch to ZERO-LENGTH
line 74~ entry for SHUFFLE-ALONG
line 75~ get character from buffer
line 76~ increment index
line 77~ save character one byte along
line 78~ restore original address minus one
51
line 790 branch to SHUFFLE-ALONG until completed
line 800 entry for ZERO-FINISH
line 810 restore accumulator
line 820 index past length byte
line 830 add character to buffer
fine 840 decrement index
line 850 get length byte
line 860 increment it
line 870 save it
line 880 back to calling routine
line 900 entry for OUTPUT
line 910 get length of string as counter
line 920 set index to first character
line 930 entry for PRINT-LOOP
line 940 get character
line 950 print it
line 960 increment index
line 970 decrement count
line 980 branch to PRINT-LOOP until all done
line 990 back to BASIC

52
110 INPUT"WHICH DIRECTION

5 String Manipulation

In this chapter we will look at how ASCII character strings can be


manipulated using machine code routines to perform the following
operations:

1. Compare two strings.


2. Concatenate one string onto another.
3. Copy a substring from within a main string.
4. Insert a substring into a main string.

These types of routines are essential if you intend to write any


programs that manipulate data and information. Adventure games
are a typical example of this kind of program.

COMPARING STRINGS

String comparison is normally performed after the computer user


has input some information from the keyboard. In BASIC this
might be written as:

1 A$="MOVE LEFT"
11 INPUT"WHICH DIRECTION?"; B$
12 IF A$=B$ THEN PRINT "CORRECT!"

We do not always wish to test for equality, however. In BASIC, we


are able to test for unlike items using the NOT operators '< >'.
Thus, line 120 could have been written as:

120 IF A$ <> B$ PRINT "WRONG!"

At other times, we may wish to test which of two strings has a


greater length, and this is possible in BASIC using the LEN
statement:

5~
21.0 IF LEN(A$) > LEN(B$) THEN PRINT "FIRST"

Program 8 gives the assembler and BASIC listing for the string
comparison routine, which puts all the functions described above
at your disposal whenever the program is used. The Status register
holds these answers in the Zero and Carry flags. The Zero flag is
used to signal equality: if it is set (Z= 1), the two strings compared
were identical; if it is cleared (Z=0) they were dissimilar.
The Carry flag returns information as to which of the two strings
was the longer: if it is set (C= 1), they were identical in length or the
first string was the larger. The actual indication required here is
evaluated in conjunction with the Zero flag. If Z=0 and C= 1, then
a longer string rather than an equal-length string is indicated, but if
the Carry flag is returned clear (C=0), then the second string was
longer than the first.

ProgramS

1.0 REM ** STRING COMPARISON ROUTINE **


2.0 CODE=49152
3.0 TEST=49184
4.0 FOR LOOP= TO 41
5.0 READ BYTE
6.0 POKE CODE+LOOP,BYTE
7.0 NEXT LOOP
8.0
9.0 REM ** M/C DATA **
1.0.0 DATA 173,52,3 REM LDA $334
ll DATA 2.05,53,3 REM CMP $335
12.0 DATA 144,3 REM BCC $.03
13.0 DATA 174,53,3 REM LDX $335
14.0 REM COMPARE-STRING
15.0 DATA 24.0,12 REM BEQ $C
16.0 DATA 16.0,.0 REM LDY 11$.0.0
17.0 REM COMPARE-BYTES
18.0 DATA 177,251 REM LDA ($FB), Y
19.0 DATA 2.09,253 REM CMP ($FD), Y
2.0.0 DATA 2.08,1.0 REM BNE $A
21.0 DATA 2.0.0 REM INY
22.0 DATA 2.02 REM DEX
23.0 DATA 2.08,246 REM BNE $F6
24.0 REM CONDITION-FLAGS
25.0 DATA 173,52,3 REM LDA $334
54
GET BYTE
FROM
STRING 1

COMPARE
WITH BYTE
IN STRING 2

SET ERROR END


FLAGS

INCREMENT
POINTERS

No

SET STRING
FLAGS

FigureS.} Compare strings flowchart

26 DATA 25,53,3 REM CMP $335


27 REM FINISH
28 DATA 96 REM RTS
29
3 REM TEST ROUTINE
31 DATA 32,,192 REM JSR $C
55
32.0 DATA 8 REM PHP
33.0 DATA 1.04 REM PLA
34.0 DATA 41,3 REM AND 11$.03
35.0 DATA 133,251 REM STA $FB
36.0 DATA 96 REM RTS
37.0:
38.0 REM ** SET UP STRINGS FOR COMPARISON **
39.0 PRINT CHR$(147)
4.0.0 INPUT "FIRST STRING :";A$
41.0 FOR LOOP=l TO LEN(A$)
42.0 TEMP$=MID$(A$,LOOP,1)
43.0 A=ASC(TEMP$)
44.0 POKE 5.0432+LOOP-1,A
45.0 NEXT LOOP
46.0
47.0 INPUT "SECOND STRING :";B$
48.0 FOR LOOP=l TO LEN(B$)
49.0 TEMP$=MID$(B$,LOOP,l)
5.0.0 B=ASC(TEMP$)
51.0 POKE 5.0688+LOOP-1,B
52.0 NEXT LOOP
53.0
54.0 POKE 251,.0 : POKE 252,197
55.0 POKE 253,.0 : POKE 254,198
56.0 POKE 82.0,LEN(A$) POKE 821,LEN(B$)
57.0
58.0 SYS TEST
59.0
6.0.0 PRINT "RESULT IS ";PEEK(251)

Bytes reserved:

251-252 ($FB-$FC) address of first string


253-254 ($FD-$FE) address of second string
82.0 ($334) length of first string
821 ($335) length of second string

Once run, the BASIC text of lines 380 to 520 calls for two strings to
be input. These are stored in memory from $C500 and $C600. Note
that the routine cannot handle strings greater than 256 characters in
length (though it could of course be expanded to do so). The length

56
of each string is also required by the routine, so this is ascertained
and stored in the appropriate zero page bytes at $334 and $335 (line
560).
To allow the string buffers to be fully relocatable, the string
addresses are held in two zero page vectors (lines 540 and 550).
String comparison proper starts by evaluating the length bytes to
find out if they are the same length. If they are not equal, then the
strings cannot be identical. However, as the routine returns infor-
mation about the lengths of the strings it is still completed-in this
case the program compares bytes through the length of the smaller
of the two strings.
Byte comparison is performed by lines 170 to 190, using post-
indexed indirect addressing. On the first non-equal characters the
main loop is exited to FINISH. Assu.ming the entire comparison
works, and the X register, which holds the working string length,
has been decremented to zero, the length bytes (lines 250 and 260)
are compared to condition the Zero and Carry flags before the
routine completes.
The short test routine returns the Zero and Carry flag values and
prints them out. indicating the following results:

Returned z C Result

o o o Strings <> and string 1 larger


1 o 1 Strings <> and string 2 larger
3 1 1 Strings =

Line-by-Iine

A line-by-line description of Program 8 follows:

line 100 get length of first string


line 110 is it the same length as the second string?
line 120 no. it's longer. so branch to COMPARE-STRING
line 130 yes. so get length of second string
line 140 entry for COMPARE-STRING
line 150 if zero. branch to CONDITION-FLAGS
line 160 initialize indexing register
line 170 entry for COMPARE-BYTES
line 180 get character from first string
line 190 compare to same character in second string
line 200 if dissimilar. branch to FINISH
line 210 increment index
57
line 22 decrement string counter
line 23 branch back to COMPARE-BYTES until zero
line 24 entry for CONDITION-FLAG
line 25 get length of first string
line 26 compare with length of the second string
line 27 entry for finish
line 28 back to calling routine
line 3 entry for TEST routine
line 31 push status onto stack
line 32 pull into accumulator
line 33 save Z and C
line 34 save at location $FB
line 35 back to BASIC

STRINGS UNITE

Strings may be joined together by a process called 'concatenation'.


In BASIC the addition operator '+' performs this function. Thus
the program:

l A$="REM"
ll B$="ARK"
12 C$=A$+B$

assigns the string 'REMARK' to the string C$. If line 120 were
rewritten as:

12 C$=B$+A$

the resultant value assigned to C$ would be 'ARKREM'. We can


see from this that one string is simply tagged on to the end of the
other. overwriting the former's RETURN character, but preserv-
ing the latter's.
This process of concatenation can be performed quite readily as
Program 9 illustrates. However. the actual BASIC equivalent of
the operation we are performing here is:

A$=A$+B$

In other words, we are adding the second string on to the first


string, rather than summing the two to give a separate final string,
although this is possible with slight modifications to the assembler
text.

58
Program 9

1~ REM ** STRING CONCATENATION **


2~ CODE =49152
3~ FOR LOOP=~ TO 96
4~ READ BYTE
5~ POKE CODE+LOOP,BYTE
6~ NEXT LOOP
7~
8~ REM ** MIC DATA **
9~ REM STRING-CONCATENATION
1~~ DATA 173,52,3 REM LDA $334
ll~ DATA 141,54,3 REM STA $336
12~ DATA 169,~ REM LDA II$
13~ DATA 141 , 55,3 REM STA $337
14~ DATA 24 REM CLC
15~ DATA 173,53,3 REM LDA $335
16~ DATA 19 , 52,3 REM ADC $334
17~ DATA 176 , 3 REM BCS $3
18~ DATA 76,45,192 REM JMP $C2D
19~ REM TOO-LONG
2~~ DATA 169 , 255 REM LDA II$FF
21~ DATA 141,57 , 3 REM STA $339
22~ DATA 56 REM SEC
23~ DATA 237,52,3 REM SBC $334
24~ DATA 144,51 REM BCC $33
25~ DATA 141,56 , 3 REM STA $338
26~ DATA 169,255 REM LOA II$FF
27~ DATA 141,52,3 REM STA $334
28~ DATA 76,59,192 REM JMP $C3B
29~ REM GOOD-LENGTH
3~~ DATA 141,52,3 REM STA $334
31~ OATA 169,~ REM LOA II$~
32~ DATA 141,57,3 REM STA $339
33~ DATA 173,53,3 REM LOA $335
34~ DATA 141 , 56,3 REM STA $338
35~ REM CONCATENATION
36~ DATA 173,56,3 REM LOA $338
37~ DATA 24 , 21 REM BEQ $15
38~ REM LOOP
59
39 DATA 172,55,3 REM LOY $337
4 DATA 177,253 REM LOA ($FD), Y
41 DATA 172,54,3 REM LOY $336
42 DATA 145,251 REM STA ($FB) , Y
43 DATA 238,54,3 REM INC $336
44 DATA 238,55,3 REM INC $337
45 DATA 26,56,3 REM DEC $338
46 DATA 28,235 REM BNE $EB
47 REM FINISHED
48 DATA 172,52,3 REM LDY $334
49 DATA 169,13 REM LOA II$D
5 DATA 145,251 REM STA ($FB), Y
51 DATA 173,57,3 REM LOA $339
52 DATA 16 REM ROR A
53 DATA 96 REM RTS
54
6 PRINT CHR$( 147)
61 INPUT "FIRST STRING ";A$
62 INPUT "SECOND STRING ";B$
63
64 F=49664 REM $C2
65 S=4992 REM $C3
66
67 FOR LOOP=l TO LEN(A$)
68 TEMP$=MID$(A$,LOOP,1)
69 A=ASC(TEMP$)
7 POKE F+LOOP-1,A
71 NEXT LOOP
72
73 FOR LOOP=l TO LEN(B$)
74 TEMP$=MID$(B$,LOOP,l)
75 B=ASC(TEMP$)
76 POKE S+LOOP-1,B
77 NEXT LOOP
78
79 POKE 251, POKE 252,194
8 POKE 253, POKE 254,195
81 POKE 82,LEN(A$)

60
GET TOTAL
STRING
LENGTH

CALCULATE
TRUNCATION
INDEX

GET BYTE RESEED


FROM VALUES
STRING 2

PLACE AT
END OF
STRING 1

ADJUST
INDEXES
& COUNTERS

SIGNAL
ANY END
OVERFLOW

Figure 5.2 Concatenate strings flowchart

820 POKE 821,LEN(B$)


830
840 SYS CODE
850

61
86 REM *** PRINT OUT FINAL STRING ***
87 PRINT "FINAL STRING IS :";
88 LOOP=
89 REM ** REPEAT **
9 BYTE=PEEK(F+LOOP)
91 PRINT CHR$(BYTE);
92 LOOP=LOOP+l
93 IF BYTE=13 THEN END
94 GOTO 9

This program allows a final string of 256 characters in length to be


manipulated. Therefore, as the program stands, the combined
lengths of the two strings should not exceed this length. If they do,
then only as many characters as space allows will be concatenated
on to the first string, leaving the second string truncated. The Carry
flag is used to signal whether any truncation has taken place, being
set if it has and cleared otherwise. As with the string comparison
routine, the string buffers are accessed via two zero page vectors
(lines 790 and 800) and two bytes are reserved to hold the length of
each string. A further two bytes are used to save index values.
The first nine machine code operations (lines 100 to 180) deter-
mine the final length of the string, by adding the length of the first
string to that of the second string. A sum greater than 256 is
signalled in the Carry flag and the branch of line 170 is performed,
in which case the number of characters which can be inserted into
the first string buffer is ascertained. The overflow indicator is
loaded with $FF if a truncation occurs; otherwise it is cleared with
$00.
The concatenating loop is held between lines 350 and 460. This
simply moves a byte from the vectored address plus the index of the
second string and places it at the end ofthe first string, as pointed to
by the first string index byte. This process is reiterated until the
value of 'count' has reached zero. Lines 480 and 500 place a
RETURN character at the end of the string to facilitate printing
from BASIC or machine code. The Overflow flag is loaded into the
accumulator and bit 7 rotated across into the Carry flag, thereby
signalling whether truncation has occurred. Lines 610 to 770 hold
the BASIC test routine that reads in and then pokes the character
strings into memory at $C200 and $C300. After the SYS call (line
840), the final BASIC routine prints the concatenated string from
memory.

Project

Adapt the program to perform the BASIC equivalent of


C$= A$+ B$ or C$= B$+ A$ on request.

62
Line-by-Iine

A line-by-line description of Program 9 now follows:

line 100 get first string's length


line 110 string one's index
line 120 clear accumulator
line 130 set string two's index to zero
line 140 clear Carry flag
line 150 get second string's length
line 160 and add to length of first string
line 170 branch to TOO-LONG if total greater than 256 bytes
line 180 otherwise jump to GOOD-LENGTH
line 190 entry for TOO-LONG
line 200 load accumulator with 255
line 210 and store to indicate overflow
line 220 set Carry flag and subtract
line 230 string one's length from maximum length
line 240 branch to FINISH if first string is greater than
256 bytes in length
line 250 save current count
line 260 restore maximum length
line 270 store in string one's length
line 280 jump to concatenation routine
line 290 entry for GOOD-LENGTH
line 300 save accumulator in string one's length
line 310 load with 0 to clear
line 320 overflow indicator
line 330 get string two's length
line 340 save in count
line 35.0 entry for CONCATENATION
line 360 get count value
line 370 if zero, then finish
line 380 entry for LOOP
line 390 get index for string two
line 400 and get character from second string
line 410 get string one's index
line 420 and place character into first string
line 430 increment first string's index
line 440 increment second string's index
line 450 decrement count
63
line 460 branch to LOOP until count=0
line 470 entry for FINISHED
line 480 get final length of first string
line 490 load accumulator with ASCII return
line 500 place at end of string
line 510 get overflow indicator
line 520 and move it into Carry flag
line 530 back to calling routine

COPYCAT

String manipulation routines must include a method of copying


substrings of characters from anywhere within a string of char-
acters. In BASIC, three such commands are provided. They are
MID$, LEFT$ and RIGHT$, although with the first of these, any
point in a string can be accessed. The following shows the sort of
thing possible in BASIC:

100 A$="CONCATENATE"
110 B$=MID$(A$,0,3)
120 PRINT B$

Running this will output the string 'CON'. What the code has done
is to take the three characters from the first character in the Main$.
Program 10 produces the same type of operation from machine
code.

Program 10

10 REM ** COpy A SUBSTRING FROM WITHIN **


20 REM ** A MAIN ASCII STRING **
30 CODE=49152
40 MAIN=50432 : REM $C500
50 SUB=50688 : REM $C600
60 REM ** READ AND POKE M/C DATA **
70 FOR LOOP=0 TO 123
80 READ BYTE
90 POKE CODE+LOOP,BYTE
100 NEXT LOOP
110
120 REM ** M/Q DATA **
130 DATA 160,0 REM LDY #$00
140 DATA 140,52,3 : REM STY $334
64
CALCULATE
LENGTH
SOURCE STRING

GET INDEX
AND COUNT

LOCATE
BYTE IN
STRING

PLACE IN
SUBSTRING

UPDATE
INDEXES

No

PLACE <RETURN>
IN
SUBSTRING

Figure 5.3 Copy string flowchart

15.0 DATA 14.0,56,3 REM STY $338


16.0 DATA 173,54,3 REM LOA $336
17.0 DATA 24.0,98 REM BEQ $62
65
18 DATA 173,53,3 REM LDA $335
19 DATA 25,55,3 REM CMP $337
2 DATA 144,93 REM BCC $5D
21 DATA 24 REM CLC
22 DATA 173,55,3 REM LDA $337
23 DATA 19,54,3 REM ADC $336
24 DATA 176,9 REM BCS $9
25 DATA 17 REM TAX
26 DATA 22 REM DEX
27 DATA 236,53,3 REM CPX $335
28 DATA 144,2 REM BCC $14
29 DATA 24,18 REM BEQ $12
3 REM TRUNCATION
31 DATA 56 REM SEC
32 DATA 173,53,3 REM LDA $335
33 DATA 237,55,3 REM SBC $337
34 DATA 141,54,3 REM STA $336
35 DATA 238,54,3 REM INC $336
36 DATA 169,255 REM LDA I/$FF
37 DATA 141,56,3 REM STA $338
38 REM GREATER-EQUAL
39 DATA 173,54,3 REM LDA $336
4 DATA 21,255 REM CMP I/$FF
41 DATA 144,1 REM BCC $A
42 DATA 24,8 REM BEQ $8
43 DATA 169,255 REM LDA I/$FF
44 DATA 141,54,3 REM STA $336
45 DATA 141,56,3 REM STA $338
46 REM COPY-SUBSTRING
47 DATA 174,54,3 REM LDX $336
48 DATA 24,35 REM BEQ $23
49 DATA 169, REM LDA I/$
5 DATA 141,52,3 REM STA $334
51 REM LOOP
52 DATA 172,55,3 REM LDY $337
53 DATA 177,251 REM LDA ($FB), Y
54 DATA 172,52,3 REM LDY $334
55 DATA 145,253 REM STA ($FD), Y
56 DATA 238,55,3 REM INC $337
57 DATA 238,52,3 REM INC $334
66
58 DATA 22 REM DEX
59 DATA 28,237 REM BNE $ED
6 DATA 26,52,3 REM DEC $334
61 DATA 173,56,3 REM LOA $338
62 DATA 28,3 REM BNE $3
63 REM FINISH
64 DATA 24 REM CLC
65 DATA 144,1 REM BCC $1
655 REM ERROR
66 DATA 56 REM SEC
67 REM OUT
68 DATA 169,13 REM LOA #$D
69 DATA 172,52,3 REM LOY $334
7 DATA 2 REM INY
71 DATA 145,253 REM STA ($FD), Y
72 DATA 96 REM RTS
73
74 REM ** SET UP MAIN STRING **
75 PRINT CHR$(147)
76 REM ERROR
77 INPUT "MAIN STRING ";B$
78 FOR LOOP=l TO LEN(B$)
79 TEMP$=MID$(B$,LOOP,1)
8 B=ASC(TEMP$)
81 POKE MAIN+LOOP-1,B
82 NEXT LOOP
83
84 INPUT"INDEX INTO STRING ";X
85 INPUT"NUMBER OF BYTES TO COPY ";Y
86~
87 REM ** SET UP BYTES FOR MIC **
88 POKE 251, POKE 252,197
: REM $C5 VECTOR
89 POKE 253, POKE 254,198
REM $C6 VECTOR
9 POKE 821,LEN(B$)
91 POKE 822,Y
92 POKE 823,X
93
94 SYS CODE
67
950
960 REM ** READ COPIED SUBSTRING **
970 FOR LOOP=l TO Y
980 Z=PEEK(SUB+LOOP-1)
990 PRINT CHR$(Z);
1000 NEXT LOOP

Bytes are designated as follows:

251-252 ($FB-$FC) main string vector


253-254 ($FD-$FE) substring vector
820 ($334) length of substring
821 ($335) length of main string
822 ($336) number of bytes to be copied
823 ($337) index into main string
824 ($338) error flag

Once again, a few lines of BASIC demonstrate the operation of the


routine, requesting the source string, starting index and length of
substring, or rather the number of bytes to be copied into the
substring from the starting index. The main string is in a buffer
located at $C5oo and the substring is copied into its own buffer at
$C600. As always, these addresses may be changed to suit user
needs, as they are vectored through zero page (lines 880 and 890).
Error-checking is allowed, as the Carry flag is set on exit if an
error has occurred. Normally, an error will occur only if the starting
index is beyond the length of the source string, or the number of
bytes to be copied from the main string is zero. If the number of
bytes requested in the length exceeds the number left from the
indexed position to the end of the main string, then only the bytes
available will be copied to the substring buffer.
On entry to the routine, error-checking is performed (lines 160
to 240) and if any are found, the program exits. Lines 300 to 370
perform a truncation if the number of bytes to be copied exceeds
those available. The COPY-SUBSTRING loop (lines 460 to 590)
copies each string byte from the vectored address in the main string
to the substring buffer. Each time a character is copied, the sub-
string length byte is incremented. On completion of this loop,
controlled by the X register, the error flag is restored and the Carry
flag conditioned accordingly (lines 610 to 660). Finally (lines 690 to
730), an ASCII RETURN character is placed at the end of the
substring.
The following example shows the resultant substrings produced
from the main string 'CONCATENATE' for different indexes.
Figure 5.4 illustrates the index value for each of the main string's
characters.

68
Index Length Substring

o 3 CON
3 3 CAT
4 3 ATE

String
Index

Figure 5.4 String Index

Line-by-line

A line-by-line description of Program 10 follows:

line 130 initialize Y register


line 140 clear substring length
line 150 and error flag
line 160 get substring length
line 170 if a null string, branch to FINISH
line 180 get main string's length
line 190 compare it with index byte
line 200 branch to ERROR if index is greater
line 210 clear the Carry flag
line 220 get index
line 230 add it to substring length
line 240 branch to TRUNCA nON if result is greater than 255
line
line
.
250
260
move index across into X register
decrement it by one
line 270 compare result with string length
line 280 branch to GREATER-EQUAL if result is
line 290 greater than or equal to stnng length
line 300 entry for TRUNCAnON
line 310 set the Carry flag
line 320 get string length
line 330 subtract the index from it
line 340 save the new length
line 350 and increment it by one

69
line 360 denote an error by
line 370 setting the error flag
line 380 entry for GREATER-EQUAL
line 390 get length into accumultor
line 400 compare with maximum length
line 410 branch if count is
line 420 greater or equal to maximum length
line 430 put maximum length in accumulator
line 440 store in bytes to copy
line 450 and also in error flag
line 460 entry for COPY-SUBSTRING
line 470 get the index position
line 480 branch to ERROR if zero
line 490 clear accumulator
line 500 and substring length
line 510 entry for LOOP
line 520 get main string index into Y register
line 530 get character from main string
line 540 get substring index
line 550 copy character into substring
line 560 increment main string index
line 570 increment substring index
line 580 decrement bytes to move counter
line 590 branch to LOOP if still bytes to be copied
line 600 decrement final substring count
line 610 get error flag into accumulator
line 620 branch to ERROR if not zero
line 630 FINISH entry
line 640 clear Carry flag as nQ error
line 650 branch to OUT
line 655 entry for ERROR
line 660 set Carry flag to indicate error
line 670 entry for OUT
line 680 place RETURN in accumlator
line 690 get substring index into Y
line 700 increment Y
line 710 place RETURN at end of substring
line 720 return to BASIC.

70
INSERTION

This final routine provides the facility for inserting a string within
the body of another string, allowing textual material-for
example, in word processing applications-to be manipulated. If
the main string held 'ELIZABETH OKAY', this routine could be
called to insert the string 'RULES', so that the final string would
read 'ELIZABETH RULES OKAY'. As with the COPY routine,
the position of the insertion is pointed to by an index byte, and the
Carry flag is set if an error is detected-that is, if an index of0 or a
null substring is specified.
The maximum length of the final string is 256 characters. If the
insertion of the substring would cause this length to be exceeded,
the substring is truncated to the length given by (256 minus length
of main string) and only these characters are inserted.
As always, a BASIC primer demonstrates the routine's use. The
string buffers are held at $C5OO and $C600 and in this instance they
are accessed directly, although there is no reason why vectored
addresses could not be used.

Program 11

10 REM ** INSERT ONE ASCII STRING **


20 REM ** INTO ANOTHER ASCII STRING **
30 MAIN=50432 REM $C500
40 SUB=50688 : REM $C600
50 CODE=49152
60 REM ** READ AND POKE DATA **
70 FOR LOOP=0 TO 141
80 READ BYTE
90 POKE LOOP+CODE,BYTE
100 NEXT LOOP
110
120 REM ** M/C DATA **
130 DATA 160,0 REM LDY #0
140 DATA 140,53,3 REM STY $335
150 DATA 165,252 REM LDA $FC
160 DATA 208,3 REM BNE $03
170 DATA 76,137,192 REM JMP $C089
180 REM ZERO-LENGTH
190 DATA 165,253 REM LDA $FD
200 DATA 240,124 REM BEQ $7C
210 DATA REM CHECK

71
22 DATA 24 REM CLC
23 DATA 165.252 REM LOA $FC
24 DATA 11.251 REM AOC $FB
25 DATA 176.6 REM BCS $6
26 DATA 21.255 REM CMP I1$FF
27 DATA 24 .18 REM BEQ $12
28 DATA 144.16 REM BCC $1
29 REM CUT-OFF
3 DATA 169.255 REM LDA #$FF
31 DATA 56 REM SEC
32 DATA 229.251 REM SBC $FB
33 DATA 24 . 14 REM BEQ $68
34 DATA 144.12 REM BCC $66
35 DATA 133.252 REM STA $FC
36 DATA 169,255 REM LDA #.$FF
37 DATA 141,53.3 REM STA $335
38 REM CALC-LENGTH
39 DATA 165,251 REM LOA $FB
4 DATA 197.253 REM CMP $FO
41 DATA 176,2 REM BCS $14
42 DATA 166.251 REM LDX $FB
43 DATA 232 REM INX
44 DATA 134.253 REM STX $FD
45 DATA 169.255 REM LDA #$FF
46 DATA 141.53.3 REM STA $335
47 DATA 24 REM CLC
48 DATA 165,251 REM LDA $FB
49 DATA 11 . 252 REM ADC $FC
5 DATA 133 . 251 REM STA $FB
51 DATA 76.19.192 REM JMP $C6D
52 :: REM NO-PROBLEMS
53 DATA 56 REM SEC
54 DATA 165,251 REM LDA $FB
55 DATA 229.253 REM SBC $FD
56 DATA 17 REM TAX
57 DATA 232 REM INX
58 DATA 165 . 251 REM LDA $FB
59 DATA 133.254 REM STA $FE
6 DATA 24 REM CLC
61 DATA 11.252 REM ADC $FB
72
62 DATA 133 , 251 REM STA $FB
63 DATA 141 , 52,3 REM STA $334
64 REM MAKE-SPACE
65 DATA 164 , 254 REM LDY $FE
66 DATA 185 ,,197 REM LOA $C5 , Y
67 DATA 172 , 52,3 REM LOY $334
68 DATA 153,,197 REM STA $C5,Y
69 DATA 26 , 52.3 REM DEC $334
7 DATA 198,254 REM DEC $FE
71 DATA 22 REM OEX
72 DATA 28 , 237 REM BNE $EO
73 REM INSERT-SUBSTRING
74 DATA 169, REM LOA #$
75 DATA 133 , 254 REM STA $FE
76 DATA 166 , 252 REM LOX $FC
77 REM TRANSFER
78 DATA 164 , 254 REM LOY $FE
79 DATA 185,,198 REM LDA $C6 , Y
8 DATA 164 , 253 REM LDY $FE
81 DATA 153 , , 197 REM STA $C5 , Y
82 DATA 23 , 253 REM INC $FD
83 DATA 23,254 REM INC $FE
84 DATA 22 REM DEX
85 DATA 28 , 239 REM BNE $EF
86 DATA 173 , 53 , 3 REM LOA $335
87 DATA 28,3 REM BNE $3
88 REM GOOD
89 DATA 24 REM CLC
9 DATA 144 , 1 REM BCC $1
91 REM ERROR
92 DATA 56 REM SEC
93 REM FINISH
94 DATA 96 REM RTS
95
96 REM ** GET MAIN STRING AND STORE AT
$C5 **
97 PRINT CHR$(147)
98 INPUT"MAIN STRING";B$
99 FOR LOOP=l TO LEN(B$)
l TEMP$=MID$(B$,LOOP , 1)
73
ll B=ASC(TEMP$)
12 POKE MAIN+LOOP-l,B
13 NEXT LOOP
14
15 REM ** GET SUBSTRING AND STORE AT $C600**
16 INPUT"SUB STRING";C$
17 FOR LOOP=l TO LEN(C$)
18 TEMP$=MID$(C$,LOOP,1)
19 B=ASC(TEMP$)
ll POKE SUB+LOOP-l,B
lll NEXT LOOP
112
113 REM ** GET INSERTION INDEX **
114 INPUT"INSERTION INDEX"; X
115
116 REM ** POKE VALUES INTO ZERO PAGE **
117 POKE 251,LEN(B$)
118 POKE 252,LEN(C$)
119 POKE 253, X
12
121 SYS CODE
122
123 REM ** READ FINAL STRING **
124 COUNT=LEN(B$)+LEN(C$)-l
125 FOR LOOP= TO COUNT
126 Z=PEEK(MAIN+LOOP)
127 PRINT CHR$(Z);
128 NEXT LOOP

The program begins by checking the length bytes to ensure that no


null strings are present (lines 150 to 200) and then sums the two
lengths to obtain the final length. If the addition results in the Carry
flag being set (line 250), the total length will exceed 256 bytes and,
as a result, the inserted substring will be truncated (lines 310 to
390).
If the insertion index is greater than the length of the string, the
substring is actually concatenated on to the end of the main string.
This evaluation is performed through lines 400 to 530. Before
inserting the substring, all characters to the left of the index must be
shuffled up through memory to make space for it. These calcu-
lations are carried out in lines 550 to 650, ready for the shuffling
process (lines 660 to 740). Inserting the substring now involves
simply copying it from its buffer into the space opened up for it
74
(lines 750 to 870), the X register being used as the characters-
moved counter.
Finally, the error flag is restored and the Carry flag conditioned
to signal any errors.

Line-by-line

A line-by-line description of Program 11 follows:

line l3~ clear indexing register


line l4~ clear error flag
line l5~ get substring length
line l6~ branch to ZERO-LENGTH if Z=0
line l7~ otherwise carryon
line l8~ entry for ZERO-LENGTH
line 19~ get offset
line 2~~ branch to ERROR if Z= 1
line 2l~ entry for CHECK
line 22~ clear Carry flag
line 23~ get substring length
line 24~ add it to main string length
line 25~ branch to CUT-OFF if greater than 256
line 26~ is it maximum length?
line 27~ branch to CALC-LENGTH if
line 28~ it is equal to or greater than
line 29~ entry for CUT-OFF
line 3~~ get the maximum length allowed
line 3l~ set Carry flag
line 32~ subtract length of string
line 33~ branch to ERROR if
line 34~ length is equal to or greater than string
line 35~ save characters free
line 36~ set error flag
line 38~ entry for CALC-LENGTH
line 39~ get main string length
line 4~~ is offset within string?
line 4l~ branch to NO-PROBLEMS if it is
line 42~ else place substring
line 43~ at end of main string
line 44~ save X in offset
line 45~ and flag the error
75
line 4691 in error flag byte
line 47.0 clear Carry flag
line 48.0 get length of string
line 49.0 calculate total length
line 5.0.0 and save result
line 51.0 jump to INSERT-SUBSTRING
line 52.0 entry for NO-PROBLEMS
line 53.0 set Carry flag
line 54.0 get length of substring
line 55.0 subtract offset
line 56.0 move index into X
line 57.0 increment index
line 58.0 get length
line 59.0 save in source
line 6.0.0 clear Carry flag
line 61.0 find total length
line 62.0 save result
line 63.0 and for index
line 64.0 entry for MAKE-SPACE
line 65.0 get source index
line 66.0 get byte from main
line 67.0 get offset into string
line 68.0 move byte along
line 69.0 decrement both indexes
line 71.0 decrement counter
line 72.0 branch to MAKE-SPACE until done
line 73.0 entry for INSERT-SUBSTRING
line 74.0 clear accumulator
line 75.0 and source
line 76.0 get counter
line 77.0 entry for TRANSFER
line 78.0 get index
line 79.0 get byte from substring
line 8.0.0 get offset into main string
line 81.0 and place byte in main
line 82.0 increment both indexes
line 84.0 do until substring inserted
line 85.0 branch to TRANSFER
line 86.0 get error flag
line 87.0 branch to ERROR
76
line 8891 entry for GOOD
line 8991 signal no error
line 99191 branch to FINISH
line 9191 entry for ERROR
line 9291 denote error
line 9391 entry for FINISH
line 9491 return to calling routine

77
6 Printing Print!

Every machine code program sooner or later requires text to be


printed on to the screen. In most instances, this is a fairly simple
process and often involves merely indexing into an ASCII string
table and printing the characters, using one of the Operating
System calls, until either a RETURN character or zero byte is
encountered. Program 12 uses this method.

Program 12

1 REM ** PRINT STRING FROM MEMORY **


2 CODE=49152
3 FOR LOOP= TO 13
4 READ BYTE
5 POKE CODE+LOOP,BYTE
6 NEXT LOOP
7
8 REM ** M/C DATA **
9 REM STRING-PRINT
1 DATA 162, REM LDX #$0
11O REM NEXT-CHARACTER
12 DATA 189,O,197 REM LDA $C500,X
13 DATA 32,21,255 REM JSR $FFD2
14 DATA 232 REM INX
15 DATA 21,13 REM CMP #$0D
16 DATA 208,245 REM BNE $F5
17O DATA 96 REM RTS
18
19 REM ** GET STRING TO BE PRINTED **
2 STRING.=5432
78
START

JUMP TO END
RTSADDRESS

Figure 6.1 Printing embedded code flowchart

21 PRINT CHR$(147)
22 INPUT "INPUT STRING :";A$
23 FOR LOOP=l TO LEN(A$)
24 TEMP$=MID$(A$,LOOP,1)
25 B=ASC(TEMP$)
26 POKE STRING+LOOP-l,B
27 NEXT LOOP
28 PRINT:PRINT
29 PRINT"YOUR STRING WAS AS FOLLOWS .".
3 SYS CODE

Here, a string buffer is located at $C500 (50432) and the require-


ment for printing the string is that it must be terminated with an
ASCII RETURN character, $0D. The program begins by initial-
izing an index, the X register (line 100), and loading the byte at
$C500+ X into the accumulator. This is printed using the Kemal's
CHROUT routine, the index is incremented and then the accumu-
lator's contents are compared to see whether the character just
I)utput was a RETURN (line 150). If not, the loop branches back
and the next character is sought.
79
Program 13 shows how several strings may be printed to the
screen using a loop similar to that described above. The number of
strings for printing may be variable, the desired number being
passed into the routine via the Y register. The string data has b~en
entered using the DATA statement. If a large amount of string
data is to be stored, and the amount to be printed at anyone time
varied, a vectored address should be used to access the table.
Positioning of the text on the screen can be performed by embed-
ding the relative number of RETURNs and spaces into the DATA,
or more neatly by using the Kemal's PLOT routine to set the X and
Y tab co-ordinates.

Program 13

1 REM ** PRINT Y NUMBER OF STRINGS **


2 CODE=49152
3 FOR LOOP= TO 18
4 READ BYTE
5 POKE CODE+LO~P,BYTE
6 NEXT LOOP
7
8 REM ** M/C DATA **
9 DATA 162, REM LDX It$
1 DATA 16,4 REM LDY 1t$4
ll REM NEXT-CHARACTER
12 DATA 189,,197 REM LDA $C5,X
13 DATA 32,21,255 REM JSR $FFD2
14 DATA 232 REM INX
15 DATA 21,13 REM CMP 1t$D
16 DATA 28,245 REM BNE $F5
17 DATA 136 REM DEY
18 DATA 28,242 REM BNE $F2
19 DATA 96 REM RTS
2
21 REM ** SET UP FOUR SIMPLE STRINGS **
22 STRING=5432
23 FOR LOOP= TO 31
24 READ BYTE
25 POKE STR1NG+LOOP,BYTE
26 NEXT
27
28 REM ** ASCII DATA **
80
290 DATA 32,65,65,65,65,65,65,13
300 DATA 32,32,66,66,66,66,66,13
310 DATA 32,32,32,67,67,67,67,13
320 DATA 32,32,32,32,68,68,68,13

The final program in this chapter shows the way I find easiest to
store arid print character strings, stowing them directly wjthin the
machine code. The two main advantages of this method are that
the string is inserted directly at the point it is needed, avoiding the
need to calculate indexes into look-up tables, and that because it
manipulates its own address it is fully relocatable.

Program 14

10 REM ** ASCII STRING OUTPUT ROUTINE **


20 CODE=49152
30 FOR LOOP=0 TO 26
40 READ BYTE
50 POKE CODE+LOOP,BYTE
60 NEXT LOOP
70
80 REM ** M/C DATA **
90 DATA 104 REM PLA
100 DATA 133,251 REM STA $FB
110 DATA 104 REM PLA
120 DATA 133,252 REM STA $FC
130 REM REPEAT
140 DATA 160,0 REM LOY #$0
150 DATA 230,251 REM INC $FB
160 DATA 208,2 REM BNE $02
170 DATA 230,252 REM INC $FC
180 REM OVER
190 DATA 177,251 REM LOA ($FB), Y
200 DATA 48,6 REM BMI $06
210 DATA 32,210,255 REM JSR $FFD2
220 DATA 76,6,192 REM JMP $C006
230 REM FINISH
240 DATA 108,251,0 REM JMP ($FB)
250
260 REM ** DEMO ROUTINE LOCATED AT $C200 **
270 DEMO =49664

81
28.0 FOR LOOP=.0 TO 38
29.0 READ BYTE
3.0.0 POKE DEMO+LOOP,BYTE
31.0 NEXT LOOP
32.0
33.0 REM ** DEMO M/C DATA **
34.0 DATA 169,147 REM LDA #$93
35.0 DATA 32,21.0,255 :. REM JSR $FFD2
36.0 DATA 32,.0,192 : REM JSR $C.0.0.0
37.0 REM ** NOW STORE ASCII CODES FOR PRINTING **
38.0 DATA 13 : REM CARRIAGE-RETURN
39.0 DATA 83,84,82,73,78,71,83,32
: REM STRINGS<SPACE>
4.0.0 DATA 87,73,84,72,73,78,32
: REM WITHIN<SPACE>
41.0 DATA 77,65,67,72,73,78,69,32
: REM MACHINE<SPACE>
42.0 DATA 67,79,68,69,33
REM CODE!
43.0 DATA 234 REM NOP
44.0 DATA 96 REM RTS
45.0
46.0 SYS DEMO

The ASCII character string is placed in memory by leaving the


machine code assembly (line 3(0) and POKEing the ASCII codes
of the string directly into successive memory locations (lines 380 to
420).
For this routine to work, it is imperative that the first byte
following the string is a negative byte-that is, one with bit 7 set.
The apcode for NOP, $EA, is ideal for this purpose as it has its
most significant bit set ($EA=11101010) and its only effect is to
cause a very short delay.
The ASCII print routine is just 27 bytes in length and it should be
called as a subroutine immediately before the string is encountered
(line 3(0). On entry into the subroutine, the first four operations
pull the return address from the stack and save it in a zero page
vector at $FB and $FC. These bytes are then incremented by one to
pointat the byte following the subroutine call.
Because the string data follows on immediately after the ASCII
print subroutine call, post-indexed indirect addressing can be used
to load the first string character into the accumulator (line 190).
The string terminating negative byte is tested for (line 200), and if
not found the byte is printed with a CHROUT call. A JMP to
82
REPEAT is then performed and the loop reiterated. When the
negative byte is encountered, and the branch of line 200 succeeds,
an indirect jump (line 240) via the current vectored address is
executed, returning control back to the calling machine code at the
end of the ASCII string.

Line-by-Iine

A line-by-line description of Program 14 follows:

line 90 set low byte RTS address


line 100 save in $FB
line 110 get high byte RTS address
line 120 save in $FC
line 130 entry for REPEAT
line 140 initialize index to zero
line 150 increment low byte of vectored address
line 160 branch to OVER if not zero
line 170 else increment page value
line 180 entry for OVER
line 190 get byte from within program
line 200 if negative, branch to FINISH
line 210 else print it
line 220 jump to REPEAT
line 230 entry for FINISH
line 240 jump back into main program
line 340 load accumulator with clear screen code
line 350 and print it
line 360 call string printing routine at $C000
line 380 ASCII code for RETURN
line 390 ASCII string 'STRINGS'
line 400 ASCII string 'WITHIN '
line 410 ASCII string 'MACHINE'
line 420 ASCII string 'CODE!'
line 430 negative byte
line 440 back to BASIC
7 A Bubble of Sorts

Any program written to handle quantities of data will, at some


time, require the data in a data table to be sorted into ascending or
descending order. Several algorithims are available to facilitate this
manipulation of data, of which the bubble sort is perhaps the
simplest to implement in BASIC or machine code.
The technique involves moving through the data list and com-
paring pairs of bytes. If the first byte is smaller than the next byte in
the list, the next pair of bytes is sought. If, on the other hand, the
second byte is less than the first, the two bytes are swapped. This
procedure is repeated until a pass is executed in which no elements
are exchanged, so all are in ascending order. Program 15 is the
BASIC version of such a bubble sort.

Program 15

1~ REM ** BASIC BUBBLE SORT **


2~ TABLE=828
3~ FOR LOOP=~ TO 19
4~ READ BYTE
5~ POKE TABLE+LOOP,BYTE
6~ NEXT LOOP
7~
8~ REM ** BUBBLE-UP ROUTINE **
9~ FOR BUBBLE=~ TO 19
1~~ TEMP=BUBBLE
ll~
12~ IF PEEK(TABLE+TEMPPEEK(TABLE+(TEMP-1))
THEN GOTO 18~
13~ HOLD=PEEK(TABLE+TEMP)
14~ POKE TABLE+TEMP,PEEK(TABLE+(TEMP-l))

84
15.0 POKE TABLE+(TEMP-l) ,HOLD
16.0 TEMP=TEMP-l
17.0 IF TEMP<>.0 THEN GOTO 12.0
18.0 NEXT
19.0
2.0.0 REM ** DATA FOR SORTING **
21.0 DATA 1,255,67,89,12.0
22.0 DATA 6,2.0.0,85,45,199
23.0 DATA .0,123,77,98,231
24.0 DATA 9,234,99,98,1.0.0
25.0
26.0 REM ** PRINT SORTED DATA **
27.0 FOR LOOP=.0 TO 19
28.0 PRINT PEEK(TABLE+LOOP)
29.0 NEXT LOOP

The data bytes for sorting are held within the four data lines from
210 to 240 and these are read into a memory array called TABLE.
The sorting procedure is perfonned through lines 90 to 180. line 120
checking to see if a swap is required. If a swap is unnecessary.
OOTO 180 is executed and the swap routine bypassed. If it is
required. however, the OOTO statement is not encounted. and the
swap is performed in lines 130 to 160. The byte currently being
pointed to is PEEKed into the variable HOLD (line 130) and the
next byte is PEEKed and then POKEd into the location immedi-
ately before it (line 140). The swap is completed by POKEing the
value of HOLD into the now 'vacant' location. The variable TEMP
is used to keep track of the number of passes through the loop.

27 27 0A
CA 0A I 27

0A I 4C 4C
4C I CA 50
F0 50 I CA
50 I F0 F0
a) b) c)

Figure 7.1 Numbers bubbling up


85
Figure 7.1 illustrates how small numbers bubble up through a
data list using this sorting method. In this example, the data list
consists of six numbers 27, CA, 0A, 4C, F0 and 50 (Figure 7.1a).
After the first pass of the bubble sort three swaps have occurred
(Figure 7.1b), thus:

1. 27 < CA therefore no change.


2. CA > 0A therefore swap items.
3. CA > 4C therefore swap items.
4. CA < F0 therefore no change.
5. F0> 50 therefore swap items.

The next pass through the data list produces the ordered list of
Figure 7.1c in which just two swaps occurred, as follows:

1. 27 > 0A therefore swap items.


2. 27 < 4C therefore no change.
3. 4C < 50 therefore no change.
4. CA > 50 therefore swap items.
5. CA < F0 therefore no change.

All the data elements are now in their final order, so the next pass
through the list will have no effect. We can signal this by using an
exchange flag to indicate whether the last pass produced any swaps,
the sort routine exiting when the flag is cleared. This detail is
included in the BASIC loader listed below as Program 16.

Program 16

1 REM *** BUBBLE SORT ***


2 CODE=49152
3 TABLE=5432
4 FOR LOOP= TO 44
5 READ BYTE
6 POKE CODE+LOOP,BYTE
7 NEXT LOOP
8
9 REM ** M/C DATA **
1 DATA 26,52,3 REM DEC $334
ll REM BUBBLE-LOOP
12 DATA 16, REM LOY It$
13 DATA 14,53,3 REM STY $335
14 DATA 174,52,3 REM LOX $334
15 REM LOOP
R6
GETBYfE
FROM
ARRAY

SET
EXCHANGE
FLAG

GET
EXCHANGE
FLAG

L. .______-----------'
Figure 7.2 Bubble sort flowchart

87
16~ DATA 177,253 REM LDA ($FD), Y
17~ DATA 2~9,251 REM CMP ($FB), Y
18~ DATA 176,13 REM BCS $~D
19~ DATA 72 REM PHA
2~~ DATA 177,251 REM LDA ($FB), Y
21~ DATA 145,253 REM STA ($FD), Y
22~ DATA 1~4 REM PLA
23~ DATA 145,251 REM STA ($FB) ,Y
24~ DATA 169,1 REM LDA #$~1
25~ DATA 141,53,3 REM STA $335
26~ REM SECOND-FIRST
27~ DATA 2~~ REM INY
28~ DATA 2~2 REM DEX
29~ DATA 2~8,233 REM BNE $E9
3~~ DATA 173,53,3 REM LDA $335
31~ DATA 24~,5 REM BEQ $~5
32~ DATA 2~6,52,3 REM DEC $334
33~ DATA 2~8,215 REM BNE $D7
335 REM FINISH
34~ DATA 96 REM RTS
35~
36~ REM ** SET UP VECTORS **
37~ REM $FB=$C5~~, $FD=$C5~1
38~ POKE 251,~ POKE 252,197
39~ POKE 253,1 : POKE 253,197
4~~
41~ REM ** SET UP SCREEN AND ARRAY **
42~ PRINT CHR$(147)
43~ PRINT "**** MACHINE CODE BUBBLE SORT ****"
44~ PRINT:PRINT
45~ INPUT"NUMBER OF ELEMENTS IN ARRAY ";N
46~ POKE 82~,N REM LENGTH OF ARRAY
AT $334
47~ FOR LOOP=~ TO N-1
48~ PRINT"INPUT ELEMENT ";LOOP+1;
49~ INPUT A
5~~ POKE TABLE+LOOP,A
51~ NEXT LOOP
52~
53~ REM ** CALL CODE THEN PRINT SORTED TABLE **
88
540 SYS CODE
550 PRINT"SORTED VALUES ARE AS FOLLOWS"
560 FOR LOOP=0 TO N-l
570 PRINT PEEK(TABLE+LOOP)
580 NEXT LOOP

After POKEing the machine code data into memory at $C~, two
zero page vectors are created to hold the address of the TABLE
and TABLE+1 (lines 370 to 390). The program then requests (in
BASIC!) the number of elements in the array, which should be a
series of integer values less than 256. These are then POKEd into
memory (lines 450 to 510). The machine code begins by decrement-
ing the length of array byte by one .(line 100), because the last
element in the array will have no element beyond it to swap with.
The swap flag is then cleared (line 130) and the main loop entered
using the X register to count the iterations.
The LOOP begins by loading the data byte into the accumulator
(line 160) and comparing it with the one immediately preceding it.
If the byte+ 1 is greater than the byte, the Carry flag will be set and
no swap required, in which case the branch to SECOND-FIRST is
executed (line 180).
If a swap is required, the second byte is saved, pushing it on to
the hardware stack. The first byte is then transferred to the second
byte's position (lines 200 and 210) and the accumulator is restored
from the stack and transferred to the position of the first byte (lines
220 to 230). To denote that a swap has occured, the swap flag is set
(lines 240 and 250). The index and counters are then adjusted (lines
270 and 280) and the loop continues until all the array elements
have been compared. Upon completion of a full pass through the
array, the swap flag is checked. If it is clear. no exchanges took
place during the last pass, so the data list is now ordered and the
sort finished (line 300 and 310). If the flag is set. the length of array
byte is decremented and the procedure repeated once more (lines
320 and 330). On return from the SYS call. the now ordered list is
printed out to the screen.

Line-by-line

A line-by-line description of Program 16 now follows:

line 100 subtract one from the length of the array


line 110 entry for BUBBLE-LOOP
line 120 initialize indexing. register
line 130 clear the swap flag
line 140 get the array size into the X register to act as a loop
counter
R9
line l5 entry for LOOP
line l6 get the byte at the byte + 1 position
line 17 compare it with the previous byte
line l8 branch to SECOND-FIRST if the second byte
(byte + 1) is larger than the first (byte)
line 19 save accumulator on hardware stack
line 2 get first byte at 'byte' position
line 2l place in current location (byte + 1)
line 22 restore accumulator
line 23 and complete swap of bytes
line 24 load accumulator with 1
line 25 and set the swap flag to denote that a swap has been
performed
line 26 entry for SECOND-FIRST
line 27 move index on to next byte
line 28 decrement loop counter
line 29 branch to LOOP until done
line 3 get the swap flag into the accumulator
line 3l if clear, branch to FINISH
line 32 decrement outer counter
line 33 branch to BUBBLE-LOOP until all done
line 335 entry to FINISH
line 34 back to calling routine

Projects
Rewrite the BASIC sections of the program to make it a complete
machine code routine.
Adapt the sorting routine to handle 16-bit numbers.

90
8 Software Stack

One of the criticisms of the 6510 processor is that it has a very


limited set of operation instructions-only 56, though addressing
modes extend this to 152 functions. With some thought, however,
it is possible to implement operations present on other processors,
such as the Z80 or 6809, and build up a set of very useful sub-
routines which can ultimately be strung together to perform quite
sophisticated operations, as well as making the conversion of pro-
grams written for other processors much easier.
The routine described below mimics an instruction in the 6809
instruction set which allows the contents of up to eight registers to
be pushed on to a stack in memory. This stack is often known as the
user stack. I said 'up to eight registers', because the ones to be
pushed can be selected, this being determined by the bit pattern of
the byte after the user stack subroutine call. But more of that in a
moment. First, which registers are we going to push? Obviously all
the processor registers: the Program Counter, Status register,
accumulator, and Index registers. The three remaining ones, we
will implement as three two-byte 'psuedo-registers' from the user
area of zero page. These are:

PRl $80 and $81


PR2 $82 and $83
PR3 $84 and $84

This now enables us to save the contents of these locations when


required.
As already stated, the byte after the user stack subroutine call
determines by its bit pattern which registers are to be pushed, as
follows:

bit 0 pseudo-register 1
bit 1 pseudo-register 2
bit 2 pseudo-register 3
91
bit 3 Y register
bit 4 X register
bit 5 accumulator
bit 6 Status register
bit 7 Program Counter

The rule here is that if the bit is set, the related register is pushed.
Thus the instructions:

JSR USER-STACK
.BYTE $FF

would push all registers on to the user stack, the embedded byte
being $FF or 11111111. Alternatively, the coding:

JSR USER-STACK
.BYTE $lE

where $lE = ~11110 would push only the accumulator, Status


and Index registers. Perhaps at this point a question is running
through your mind: 'won't the embedded byte cause my program
to crash?'. That's true on face value, but what we do is get the user
stack coding to move the Program Counter on one byte, to pass
over it, as Program 17 shows:

Program 17

1.0 REM ** USER STACK **


2.0 CODE=49152
3.0 FOR LOOP=.0 TO 116
4.0 READ BYTE
5.0 POKE CODE+LOOP,BYTE
6.0 NEXT LOOP
7.0
8.0 REM ** MIC DATA **
9.0 DATA 8 REM PHP
1.0.0 DATA 72 REM PHA
ll.0 DATA 138,72 REM TXA PHA
12.0 DATA 152,72 REM TYA PHA
13.0 DATA 186 REM TSX
14.0 DATA 16.0,6 REM LDY 11$.06
15.0 REM PUSH-ZERO-PAGE
16.0 DATA 185,138,.0 REM LDA $.0.08A,Y

92
170 DATA 72 REM PHA
180 DATA 136 REM DEY
190 DATA 208,249 REM BNE $F9
200 DATA 254,5,1 REM INC $UI5, X
210 DATA 189,5,1 REM LDA $105,X
220 DATA 133,139 REM STA $8B
230 DATA 208,3 REM BNE $03
240 DATA 254 , 6,1 REM INC $UI6 , X
250 REM PC-LOW
260 DATA 189,6,1 REM LDA $UI6 , X
270 DATA 133,140 REM STA $8C
280 DATA 169,135 REM LDA #$87
290 DATA 133,141 REM STA $8D
300 DATA 177,139 REM LDA ($8B), Y
310 DATA 133 , 142 REM STA $8E
320 DATA 169,8 REM LDA #$08
330 DATA 133,143 REM STA $8F
340 DATA 136 REM DEY
350 DATA 198 , 252 REM DEC $FC
360 REM ROTATE-BYTE
370 DATA 38,142 REM ROL $8E
380 DATA 144,16 REM BCC $10
390 DATA 189,6,1 REM LDA $106 , X
400 DATA 145,251 REM STA ($FB), Y
410 DATA 136 REM DEY
420 DATA 36,141 REM BIT $8D
430 DATA 16 , 6 REM BPL $06
440 DATA 189 , 5,1 REM LDA $105,X
450 DATA 145 , 251 REM STA ($FB), Y
460 DATA 136 REM DEY
470 REM BIT-CLEAR
480 DATA 202 REM DEX
490 DATA 38 , 141 REM ROL $8D
500 DATA 144,1 REM BCC $01
510 DATA 202 REM DEX
520 REM OVER
530 DATA 198,143 REM DEC $8F
540 DATA 208,226 REM BNE $E2
550 DATA 56 REM SEC
560 DATA 152 REM TYA
93
570 DATA 101,251 REM ADC $FB
580 DATA 133,251 REM STA $FB
590 DATA 144,2 REM BCC $02
600 DATA 230,252 REM INC $FC
610 REM CLEAR-STACK
620 DATA 162,0 REM LOX #0
630 REM REPEAT
640 DATA 104 REM PLA
650 DATA 149,139 REM STA $8B,X
660 DATA 232 REM INX
670 DATA 224,6 REM CPX #$06
680 DATA 208,248 REM BNE $F8
690 DATA 104,168 REM PLA TAY
700 DATA 104,170 REM PLA : TAX
710 DATA 104 REM PLA
720 DATA 40 REM PLP
730 DATA 96 REM RTS
740 REM TEST-ROUTINE
750 DATA 169,240 REM LOA #$F0
760 DATA 162,15 REM LOX #$0F
770 DATA 160,255 REM LOY #$FF
780 DATA 32,0,192 REM JSR $C000
790 DATA 255 REM EMBEDDED-BYTE
800 DATA 96 REM RTS
810
820 REM ** SET UP ZERO PAGE AND FREE RAM **
830 PRINT CHR$(147)
840 POKE 251,12 POKE 252,197
850 FOR N=139 TO 144 POKE N,N NEXT
860 FOR N=50432 TO 50440 : POKE N,0 NEXT
870
880 SYS 49258 : REM SYS TEST-ROUTINE
890
900 REM ** READ RESULTS **
910 FOR LOOP=50432 TO 50443
920 READ NAME$
930 PRINT NAME$;
940 PRINT PEEK(LOOP)
950 NEXT LOOP
960
94
970 DATA "ZERO PAGE ","ZERO PAGE+1"
980 DATA "ZERO PAGE+2","ZERO PAGE+3"
990 DATA "ZERO PAGE+4", "ZERO PAGE+5"
1000 DATA fly REGISTER ","X REGISTER"
1010 DATA "ACCUMULATOR", "STATUS "
1020 DATA "PC LOW ","PC HIGH "
The problem to solve next is that of where to place the user stack.
This will depend on your own requirements, so to make the whole
thing flexible, a vectored address in the bytes at $FB and $FC
contains the stack address. In the program listed above, this is
$C512 (line 840). The vectored address is, in fact, the address + 12.
This is because the stack is pushed in reverse (decreasing) order.
When executed, the coding first pushes all the processor
registers on to the hardware stack and moves the stack pointer
across into the X register (lines 90 to 140). Next, the six zero page
pseudo-registers are pushed there (lines 150 to 190). The return
address from the subroutine call is then incremented on the stack,
using the contents of the X register (stack pointer) to access it (lines
200 to 240). The two bytes that form the RTS address are copied
into pseudo-register 1 (now safely on the hardware stack) to form a
vector though which the embedded data byte can be loaded into
the accumulator and then saved for use in zero page (lines 250 to
310).
In line 280, a pre-defined byte was loaded into the accumulator
and saved in zero page. This byte holds a bit code that will inform
the program as to whether the register being pulled from the
hardware stack for transfer to the software stack is one or two bytes
long. The byte value, $87, is 10000111 in binary and the set bits
correspond to the two-byte registers, the Program Counter and the
three pseudo-registers. By rotating this byte left after each pull
operation and using the BIT operation, the Negative flag can be
tested to see if a further pull is needed. All this and the copy
hardware stack/push software stack is handled by lines 320 to 550.
Finally, the registers and pseudo-registers are restored to their
original values (lines 620 to 730). The test routine between lines 750
and 800 shows the way the program is used. When run, the test
procedure produces the following output on the screen:

ZERO PAGE 139


ZERO PAGEd 140
ZERO PAGE+2 141
ZERO PAGE+3 142
ZERO PAGE+4 143
ZERO PAGE+5 144
Y REGISTER 255
X REGISTER 15
95
ACCUMULATOR 24.0
STATUS 176
PC LOW 115
PC HIGH 192

As can be seen, the zero page bytes contain the values POKEd into
them by the FOR ... NEXT loop of line 830 while the accumulator
and Index registers display their seeded values (lines 750 to 770).
The Program Counter holds 192 * 256 + 115, or $C073, which was
the point in the program where its contents where pushed at line
780.
This program could be extended to provide a routine to perform
a pull user stack, to copy the contents of a software stack into the
processor and pseudo-registers.

Line-by-Iine

A line-by-line description of Program 17 follows:

line 9.0 save all processor registers on hardware stack


line 14.0 move stack pointer into X for index
line 15.0 entry for PUSH-ZERO-PAGE
line 16.0 get zero page byte
line 17.0 push on to hardware stack
line 18.0 , decrement index
line 19.0 branch to PUSH-ZERO-PAGE until done
line 2.0.0 increment low byte of RTS address
line 21.0 get it from stack
line 22.0 and save in zero page
line 23.0 if not equal branch to PC-LOW
line 24.0 else increment page byte of RTS address
line 25.0 entry for PC-LOW
line 26.0 get high byte of RTS address
line 27.0 and save it to form vector
line 28.0 get bit code to indicate register size
line 29.0 and save it
line 3.0.0 get embedded code after subroutine call
line 31.0 and save it
line 32.0 eight bits in embedded byte to test
line 33.0 save bit count
line 34.0 decrement index to $FF
line 35.0 decrement high byte of vectored address at $FB

96
line 36.0 entry for ROTATE-BYTE
line 37.0 move next coded bit into Carry flag
line 38.0 if bit clear skip it, branch to BIT-CLEAR
line 39.0 otherwise get byte from stack
line 4.0.0 save it on user stack
line 41.0 decrement index
line 42.0 is it a two byte register?
line 43.0 no, so branch to BIT-CLEAR
line 44.0 yes, so get the second byte from the stack
line 45.0 and save it on the user stack
line 46.0 decrement index
line 47.0 entry for BIT-CLEAR
line 48.0 decrement hardware stack index
line 49.0 move bit of register code into Carry flag
line 5.0.0 if clear, branch to OVER
line 51.0 else decrement hardware stack index
line 52.0 entry for OVER
line 53.0 decrement bit counter
line 54.0 and repeat until all done
line 55.0 set Carry flag
line 56.0 move user stack pointer into accumulator
line 57.0 add to low byte of address
line 58.0 and save
line 59.0 branch to CLEAR-STACK if carry is clear
line 6.0.0 else increment high byte of address
line 61.0 entry for CLEAR-STACK
line 62.0 initialize X register
line 63.0 entry for REPEAT
line 64.0 pull byte from stack
line 65.0 and restore zero page
line 66.0 increment index
line 67.0 all bytes restored?
line 68.0 no, branch to REPEAT
line 69.0 yes, restore all registers
line 73.0 back to calling routine
line 74.0 entry for TEST-ROUTINE
line 75.0 seed registers
line 78.0 call user stack routine
line 79.0 embedded byte
line 8.0.0 back to BASIC
97
BINARY INS AND OUTS

Sometimes when printing the values of registers, it is necessary to


have their binary representation-for example, in the case of the
Status register, because we are concerned with the state of the
particular bits within it, rather than the overall value of the con-
tents. Program 18 provides a short routine which produces such a
binary output from a decimal input. This could easily be adapted
for use within a program such as the software stack given above.

Program 18

1.0 REM ** PRINT ACCUMULATOR AS A **


2.0 REM ** BINARY NUMBER **
3.0 CODE=49152
4.0 FOR LOOP= TO 17
5.0 READ BYTE
6.0 POKE CODE+LOOP,BYTE
7.0 NEXT LOOP
8.0
9.0 REM ** M/C DATA **
1.0.0 DATA 162,.0 REM LOX #$.08
11.0 DATA 72 REM PHA
12.0 REM NEXT-BIT
13.0 DATA 1.04 REM PLA
14.0 DATA 1.0 REM ASL A
15.0 DATA 72 REM PHA
16.0 DATA 169,48 REM LOA #$3.0
17.0 DATA 1.05,.0 REM ADC #$.0.0
18.0 DATA 32,21.0,255 REM JSR $FFD2
19.0 DATA 2.02 REM DEX
2.0.0 DATA 2.08,243 REM BNE $F3
21.0 DATA 1.04 REM PLA
22.0 DATA 96 REM RTS
23.0
24.0 REM ** SET UP DEMO RUN **
25.0 REM LOA $FB : JSR $C : RTS
26.0 POKE 82.0,165 :POKE 821,251
27.0 POKE 822,32 POKE 823,.0
28.0 POKE 824,192 POKE 825,96
29.0 PRINT CHR$(147) PRINT
3.0.0 INPUT "INPUT A NUMBER ";A$
98
310 A=VAL(A$)
320 POKE 251,A
330 PRINT"BINARY VALUE IS . " .
340 SYS 820

Line-by-Iine

The following line-by-line description should make the program's


operation clear. It is simply moving each bit of the accumulator in
turn into the Carry flag position, using the arithmetic shift left
operation (see Figure 8.1) and adding its value to the ASCII code
for 0, i.e.

accumu1ator=48+carry

0.-1 87 1 86 1 85 1 84 1 83 1 82 1 81 I 881.- 8

87,,\

I 861 85 1 84 1 83 1 82 I 81 1 8S 1 8 I

1 +ASq"W')=ASq"l").

o + ASq"0") = ASq"0")

Figure 8.1 Arithmetic shift left

If the Carry flag is clear, the result will be 48+0=48, so the


CHROUT routine will print a 0. On the other hand, if the Carry
flag is set, the result of the addition will be 48+ 1 =49, so a 1 will be
printed by CHROUT.

line 100 eight bits in a byte


line 110 push accumulator on to stack
line 120 entry for NEXT-BIT

99
line l3 restore accumulator
line l4 shift bit 7 into carry
line l5 save shifted accumulator on stack
line l6 get ASCII code for 0
line l7 add carry
line l8 print either 0 or 1
line 19 decrement bit counter
line 2 do NEXT-BIT until complete
line 2l pull stack to balance push
line 22 back to BASIC

COME IN

By reversing this process, it is possible to input a number directly


into the accumulator in binary form as Program 19 shows. The
program scans the keyboard for a pressed 1 or f/J key and the Carry
flag is set or cleared respectively. A copy of the accumulator,
initially cleared, is kept on the hardware stack and restored each
time round to rotate the carry bit into it using the rotate left
operation (see Figure 8.2). The loop is executed eight times, once
for each bit, and on completion, the accumulator holds the
hexadecimal value of the binary number.

1 87 1 86 1 85 1 84 1 83 1 82 1 81 1 8D 1

I 86 1 85 1 84 1 83 1 82 I 81 I 8D I c I

L~~
Figure 8.2 Input a number directly into the accumulator
100
Program 19

10 REM ** INPUT A HEX NUMBER IN BINARY FORM **


20 CODE =49152
30 FOR LOOP;0 TO 41
40 READ BYTE
50 POKE CODE+LOOP,BYTE
60 NEXT LOOP
70
80 REM ** M/C DATA **
90 DATA 162,8 REM LDX 11$08
100 DATA 169,0 REM LDA 11$00
110 DATA 72 REM PHA
120 DATA 24 REM CLC
130 REM MAINLOOP
140 DATA 134,243 REM STX $FD
150 REM LOOP
160 DATA 32,228,255 REM JSR $FFE4
170 DATA 240,251 REM BEQ $FB
180 DATA 201,49 REM CMP 11$31
190 DATA 240,7 REM BEQ $07
200 DATA 201,48 REM CMP 11$30
210 DATA 208,243 REM BNE $F3
220 DATA 24 REM CLC
230 DATA 144,1 REM BCC $01
240 REM SET
250 DATA 56 REM SEC
260 REM OVER
270 DATA 8 REM PHP
280 DATA 32,210,255 REM JSR $FFD2
290 DATA 40 REM PLP
300 DATA 104 REM PLA
310 DATA 42 REM ROL A
320 DATA 72 REM PHA
330 DATA 166,253 REM LDX $FD
340 DATA 202 REM DEX
350 DATA 208,224 REM BNE $E0
360 DATA 104 REM PLA
370 DATA 133,251 REM STA $FB
380 DATA 96 REM RTS
101
39
4 PRINT CHR$(147)
4l PRINT
42 PRINT"INPUT YOUR BINARY NUMBER ." .
43 SYS CODE
44 PRINT PEEK(25l)

Line-by-Iine

A line-by-line explanation of Program 19 now follows:

line 9 eight bits to read


line l clear accumulator-shift register
line ll push it on to stack
line l2 clear the Carry flag
line l3 entry for MAINLOOP
line l4 save X register
line l5 entry for LOOP
line l6 jump to GETIN
line l7 if null, branch to LOOP
line l8 is it ASC"l"?
line 19 yes, branch to SET
line 2 is it ASC"0"?
line 2l no, branch to LOOP
line 22 yes, clear Carry flag
line 23 and force branch to OVER
line 24 entry for SET
line 25 set Carry flag
line 26 entry for OVER
line 27 save Carry flag on stack
line 28 print 0 or 1
line 29 restore Carry flag
line 3 restore accumulator
line 3l move Carry flag into bit 0
line 32 save accumulator
line 33 restore bit count
line 34 decrement it by one
line 35 branch to MAINLOOP until aU done
line 36 restore accumulator
line 37 save in zero page
line 38 back to BASIC
102
Project

Convert the software stack program to print the binary values of


each register upon completion.
Modify it further to allow register values to be seeded into the
software stack test routine, using the binary input routine. Note
that you should only attempt seeding the accumulator and Index
registers. Why?

103
9 Move, Fill and Dump

MOVE IT!
The ability to move blocks of memory around within the bounds of
the memory map is a necessity. When manipulating hi-resolution
graphics, for example, large blocks of memory need to be moved
around quickly and smoothlY.,The program could also be used to
relocate sections of machine code rather than rewriting the assemb-
ler that created them-assuming, of course, that your code has
been designed to make it portable.
At first sight, it may seem that the simplest method of moving a
block of memory is to take the first byte to be moved and store it at
the destination address, take the second byte and place it at the
destination address + 1, and so forth. There would be no problem
here if the destination address was outside the source address, but
consider what would happen if the destination address was within
the bounds to be searched by the source address-that is, the two
regions overlapped, Figure 9.1 illustrates the problem using this
straightforward method to move a block of five bytes forward by
just a single byte, relocating the five bytes from $C500 to $C501.
Using the obvious method, the first character, 'S', is moved from
$C500 to $C501 thereby overwriting the 'A'. The program then
takes the next character at location START + 1 ($C501), the'S' that
has just been written there, and places it at START+2 ($C502)
START $C500 S S S S S S
$C501 A S S S
$C502 R R S
$C503 A A S
$C504 H H S
$C505 S
1st 2nd 3rd 4th 5th
Figure 9.1 The overwriting move sequence
104
overwriting the 'R'. As you can see, the end result is SSSSS-the
whole block is full of 'S's-not the required effect!
To avoid this problem, the MOVE routine acts 'intellegently'
and if it calculates that an overwrite would occur, performs the
movement of bytes in the reverse order, starting at the highest
address and moving down the memory map as Figure 9.2 shows.

START $C500 S S S
$C501 A A S
$C502 R R A
$C503 A R
$C504 H A
$C505 H
1st 2nd 3rd 4th 5th
Figure 9.2 The correct move sequence

Program 20

1~ REM ** MEMORY BLOCK MOVE ROUTINE **


2~ REM ** 109 BYTES LONG WHEN ASSEMBLED **
3~ REM ** PLUS 5 DATA BYTES IN ZERO PAGE **
4~ CODE=49152
5~ FOR LOOP=~ TO 1~8
6~ READ BYTE
7~ POKE CODE+LOOP,BYTE
8~ NEXT LOOP
9~
1~~ REM ** M/C DATA **
ll~ DATA 56 REM SEC
12~ DATA 165,251 REM LDA $FB
13~ DATA 229,253 REM SBC $FD
14~ DATA 17~ REM TAX
15~ DATA 165,252 REM LDA $FC
16~ DATA 229,254 REM SBC $FE
17~ DATA 168 REM TAY
18~ DATA 138 REM TXA
19~ DATA 2~5,52,3 REM CMP $334
2~~ DATA 152 REM TYA
21~ DATA 237,53,3 REM SBC $335
105
220 DATA 176 , 2 REM BCS $02
230 DATA 144,35 REM BCC $23
240 REM MOVE-LEFT
250 DATA 160 ,0 REM LOY #$00
260 DATA 174 , 53,3 REM LOX $335
270 DATA 240,14 REM BEQ $0E
280 REM LEFT-COMPLETE-PAGES
290 DATA 177 , 253 REM LOA ($FD) , Y
300 DATA 145 , 251 REM STA ($FB) , Y
310 DATA 200 REM INY
320 DATA 208,249 REM BNE $F9
330 DATA 230 , 254 REM INC $FE
340 DATA 230,252 REM INC $FC
350 DATA 202 REM DEX
360 DATA 208 , 242 REM BNE $F2
370 REM LEFT-PARTIAL-PAGE
380 DATA 174 , 52,3 REM LOX $334
390 DATA 240,8 REM BEQ $08
400 REM LAST-LEFT
410 DATA 177 , 253 REM LOA ($FD), Y
420 DATA 145 , 251 REM STA ($FB), Y
430 DATA 200 REM INY
440 DATA 202 REM DEX
450 DATA 208 , 248 REM BNE $FB
460 REM EXIT
470 DATA 96 REM RTS
4B0
490 REM MOVE-RIGHT
500 DATA 24 REM CLC
510 DATA 173,53 , 3 REM LOA $335
520 DATA 72 REM PHA
530 DATA 101 , 254 REM ADC $FE
540 DATA 133 , 254 REM STA $FE
550 DATA 24 REM CLC
560 DATA 104 REM PLA
570 DATA 101,252 REM ADC $FC
580 DATA 133 , 252 REM STA $FC
590 DATA 172 , 52,3 REM LOY $334
600 DATA 240,9 REM BEQ $09

106
61 REM TRANSFER
62 DATA 136 REM DEY
63 DATA 177,253 REM LOA ($FD) ,Y
64 DATA 145,251 REM STA ($FB) , Y
65 DATA 192, REM CPY It$
66 DATA 28,247 REM BNE $F7
67 REM RIGHT-COMPLETE-PAGES
68 DATA 174,53,3 REM LOX $335
69 DATA 24,221 REM BEQ $00
7 REM UPDATE
71 DATA 198,254 REM DEC $FE
72 DATA 198,252 REM DEC $FC
73' :: REM PAGE
74 DATA 136 ," REM DEY
75 DATA 177,253 REM LOA ($FD) ,Y
76 DATA 145,251 REM STA ($FB) ,Y
77 DATA 192, REM CPY It$
78 DATA 28,247 REM BNE $F7
79 DATA 22 REM DEX
8 DATA 28,24 REM BNE $F
81 DATA 96 REM RTS
82
83 REM ** SET UP VARIABLES **
84 PRINT CHR$(147)
85 PRINT" *** MEMORY MOVER V1.1 ***"
86 INPUT"START ADDRESS ";S
87 INPUT"DESTINATION ";0
88 INPUT"LENGTH IN BYTES ";L
89
9 Sl=INT(S!256) S2=S-(Sh256)
91 D1=INT(D!256) D2=D-(Dh256)
92 L1=INT(L!256) L2=L-(Lh256)
93
94 POKE 251,02 POKE 252,01
95 POKE 253,S2 POKE 254,Sl
96 POKE 82,L2 POKE 821,L1
97
98 REM ** SET UP DEMO **
99 FOR N= TO 15
1 POKE 828+N,N
107
1.01.0 POKE 9.0.0+N,.0
1.02.0 NEXT N
1.03.0
1.04.0 SYS CODE
1.05.0
1.06.0 REM ** PRINT THE RESULTS! **
1.07.0 FOR N=.0 TO 15
1.08.0 PRINT PEEK(828+N);" II.
,
1.09.0 PRINT PEEK(9.0.0+~)
11.0.0 NEXT N

Bytes reserved:

251-252 ($FB-$FC) Destination vector


253-254 ($FD-$FE) Source vector
82.0-821 ($334-$335) Length of block to be
moved

When run. the BASIC test requests three inputs: the START
address of the memory block to be moved, its DESTINATION
address and its LENGTH in bytes. All values should be entered as
decimal values. Thus. to move a 1K block of memory from 49152 to
56f11/1/J. the values to input are:
START ADDRESS 49152
DESTINATION 56.0.0.0
LENGTH IN BYTES 1.024

For reasons already explained. the coding begins by ascertaining


whether a left-move or a right-move operation is required. It
calculates this (lines 110 to 210) by subtracting the source address
from the destination address. If the result is less than the number of
bytes to be moved, overwriting would occur using the MOVE-
LEFT routine, so the MOVE-RIGHT coding is called (line 230). If
the memory locations do not overlap, the quicker MOVE-LEFT
routine is selected (line 220). For further description purposes we
will examine the MOVE-LEFT routine (lines 240 to 470).
Memory movement is performed in two phases: complete
memory pages are first relocated, and then any remaining bytes in
the final partial page are moved. These details' are held in the
length of block bytes $334 and $335.
The routine begins by loading the number of pages to be moved
into the X register (line 260), branching to LEFT-PARTIAL-
PAGE if it is zero (line 280). Transfer of data bytes is completed
using post-indexed indirect addressing through the zero page
vectors. When all the whole pages have been transferred, any
108
remaining bytes are transferred by the LEFf-PARTIAL-PAGE
loop (lines 370 to 450).
The MOVE-RIGHT routine is similar in operation, except that
it starts at the highest memory location referenced and moves down
through memory, the highest address of the source and destination
being calculated in lines 500 to 650.

Line-by-line

A line-by-line description of Program 20 now follows:

line ll set Carry flag


line 12.0 get low byte destination address
line 13.0 subtract low byte source address
line 14.0 transfer result into X register
line 15.0 get high byte destination address
line 16.0 subtract high byte source address
line 17.0 save result in X register
line 18.0 restore result of low byte subtraction
line 19.0 compare it with low byte of length
line 2.0.0 restore result of high byte subtraction
line 21.0 subtract high byte of length from it
line 22.0 if Carry flag set, branch to MO>'E-LEFT
line 23.0 else branch to MOVE-RIGHT
line 24.0 entry for MOVE-LEFT
line 25.0 initialize index
line 26.0 get number of pages to be moved
line 27.0 if zero, branch to LEFT-PARTIAL-PAGE
line 28.0 entry for LEFT-COMPLETE-PAGES
line 29.0 get source byte
line 3.0.0 store at destination
line 31.0 increment index
line 32.0 branch to LEFT-COMPLETE-PAGES until page
done
line 33.0 increment source page
line 34.0 increment destination page
line 35.0 decrement page counter
line 36.0 branch to LEFT-COMPLETE-PAGES until all moved
line 37.0 entry for LEFf-PARTlAL-PAGE
line 38.0 get number of bytes on page to be moved
line 39.0 if zero, branch to EXIT
109
line 400 entry for LAST-LEFf
line 410 get source byte
line 420 store at destination
line 430 increment index
line 440 decrement byte count
line 450 branch to LAST-LEFf until done
line 460 entry for EXIT
line 470 back to BASIC
line 490 entry for MOVE-RIGHT
line 500 clear Carry flag
line 510 get number of pages to be moved
line 520 save on stack
line 530 add it to source high byte
line 540 and save result
line 550 reclear Carry flag
line 560 get length high byte off stack
line 570 add it to destination high byte
line 580 and save the result
line 590 get low byte of length into Y register
line 600 branch to RIGHT-COMPLETE-PAGES if zero
line 610 entry for TRANSFER
line 620 decrement index
line 630 get source byte
line 640 and copy to destination
line 650 is Y = 0?
line 660 no, branch to TRANSFER
line 670 entry for RIGHT-COMPLETE-PAGES
line 680 get number of pages to be moved
line 690 if zero, branch to EXIT
line 700 entry for UPDATE
line 710 decrement number of pages to do
line 720 and also destination
line 730 entry for PAGE
line 740 decrement index
line 750 get source byte
line 760 copy to destination
line 770 is Y = 0?
line 780 no, branch to PAGE

110
line 7991 decrement page counter
line 89191 if not zero, branch to UPDATE
line 8191 return to BASIC

FILL

Program 21 provides the BASIC loader listing to implement a


memory FILL routine, which is particularly useful for clearing
sections of RAM with a pre-determined value.

Program 21

191 REM ** MEMORY FILL ROUTINE **


291 REM ** 30 BYTES LONG WHEN ASSEMBLED **
391 REM ** PLUS 5 DATA BYTES IN ZERO PAGE **
491 CODE=49152
591 FOR LOOP=91 TO 391
691 READ BYTE
791 POKE CODE+LOOP,BYTE
891 NEXT LOOP
991
19191REM ** M/C DATA **
ll91 DATA 165,255 REM LDA $FF
1291 DATA 166,252 REM LDX $FC
1391 DATA 2491,12 REM BEQ $91C
1491 DATA 1691,91 REM LDY #$9191
1591 REM COMPLETE-PAGE
1691 DATA 145,253 REM STA ($FD), Y
1791 DATA 29191 REM INY
1891 DATA 2918,251 REM BNE $FB
1991 DATA 2391,254 REM INC $FE
29191DATA 2912 REM DEX
2191 DATA 2918,246 REM BNE $F6
2291 REM PARTIAL-PAGE
2391 DATA 166,251 REM LDX $FB
2491 DATA 2491,8 REM BEQ $918
2591 DATA 1691,91 REM LDY #$9191
2691 REM AGAIN
2791 DATA 145,253 REM STA ($FD),Y

111
28~DATA 2~~ REM INY
29~ DATA 2~2 REM DEX
3~~ DATA 2~8,25~ REM BNE $FA
31~ REM FINISH
32~ DATA 96 REM RTS
33~
34~ REM ** GET DETAILS **
35~ PRINT CHR$(147)
36~ INPUT"FILL DATA :";F
37~ INPUT"START ADDRESS :";S
38~ INPUT"NUMBER OF BYTES :";L
39~
4~~ Sl=INT(S/256) S2=S-(Sh256)
41~ L1=INT(L/256) L2=L-(Lh256)
42~
43~ POKE 251,L2 POKE 252,L1
44~ POKE 253,S2 POKE 254,Sl
45~ POKE 255,F
46~
47~ SYS CODE

Bytes reserved:

251-252 ( $FB-$FC ) : number of bytes to be filled


253-254 ( $FD-$FE ) : start of address of bytes to be
filled
255 ( $FF ) : value to fill with

When executed, the machine code expects to find the fill value, the
start address and the amount of memory to be filled, in five zero
page bytes of memory from $FB. Input of each of these is handled
by a few lines of BASIC from line 360. To clear a 1K block of RAM
from $C500with zero, the following information should be entered
in response to the 64's prompt:

FILL DATA ~
START ADDRESS 49152
NUMBER OF BYTES 1~24

The FILL routine works in a similar manner to the MOVE routine


described above, dealing with whole and partial pages separately.
The main fill loop is embodied in lines 150 to 300.

112
Line-by-Iine

A line-by-line description of the program now follows:

line 11.0 get data with which to fill


line 12.0 get number of complete pages to be filled
line 13.0 if zero, branch to PARTIAL-PAGE
line 14.0 initialize index
line 15.0 entry for COMPLETE-PAGE
line 16.0 fill byte
line 17.0 increment index
line 18.0 branch to COMPLETE-PAGE until all of page is
done
line 19.0 increment page
line 2.0.0 decrement page counter
line 21.0 branch to COMPLETE-PAGE until all pages are
filled
line 22.0 entry for PARTIAL-PAGE
line 23.0 get number of bytes left to be filled
line 24.0 if zero, branch to FINISH
line 25.0 else clear index
line 26.0 entry for AGAIN
line 27.0 fill byte
line 28.0 increment index
line 29.0 decrement bytes left to do count
line 3.0.0 branch to AGAIN until all filled
line 31.0 entry for FINISH
line 32.0 back to BASIC

A MEMORY DUMP
A hex and ASCII dump of memory can be extremely useful, not
only within machine code programs, but also when used from a
BASIC program. Most often it provides information about the way
a program is manipulating numeric and string variables and tables.
Figure 9.3 shows the type of dump produced by the routine:
twenty-four lines of eight bytes each. The example shows some text
stored in memory. Each line starts with the current address, fol-
lowed by the eight bytes stored in memory from that point. The far
right of the listing provides the ASCII equivalents of each byte.
Any non-ASCII character (that is, one greater than $7F) or control
code (those less than $20) is represented by a full stop.

113
Cl08 54 68 69 73 20 69 73 20 This is
CllO 61 20 73 69 60 70 6C 65 a simple
C1l8 20 65 78 61 60 70 6C 65 example
C120 20 6F 66 20 68 6F 77 20 of how
C128 74 68 65 20 80 64 75 60 the .dum
C130 70 20 72 6F 75 74 69 6E p routin
C138 65 20 66 6F 72 20 74 68 e for th
C140 65 20 43 6F 60 60 6F 64 e Commod
C148 6F 72 65 20 36 34 20 80 are 64 .
C150 77 6F 72 6B 73 2E 00 54 works T
C158 68 65 20 64 75 60 70 20 he dump
C160 63 61 6E 20 62 65 20 64 can be d
C168 69 76 69 64 65 64 20 69 ivided i
C170 6E 74 6F 20 74 68 72 65 nto thre
C178 65 20 80 73 65 63 74 69 e .secti
C180 6F 6E 73 2E 20 54 68 65 ons. The
C188 20 66 69 72 73 74 20 63 first c
C190 6F 6C 75 60 6E 20 6C 69 olumn 1 i
C198 73 74 73 20 74 68 65 20 sts the
C1AO 80 73 74 61 72 74 20 61 .start a
C1A8 64 64 72 65 73 73 20 6F ddress a
C1BO 66 20 74 68 65 20 62 6C f the bl
C1B8 6F 63 6B 2E 20 54 68 65 ock. The
C1CO 20 73 65 63 6F 6E 64 20 second
C1C8 80 63 6F 6C 75 60 6E 20 .column
Cl00 69 73 20 69 6E 20 66 61 is in fa
Cl08 63 74 20 74 68 65 20 68 ct the h
ClEO 65 78 61 64 65 63 69 60 exadecim
C1E8 61 6C 20 80 76 61 6C 75 .301 .valu
C1FO 65 73 20 6F 66 20 65 69 es of ei
C1F8 67 68 74 20 62 79 74 65 ght byte
C200 73 20 66 72 6F 60 20 74 s from t
C208 68 69 73 20 80 61 64 64 his add
C210 72 65 73 73 2E 20 46 69 ress. Fi
C218 6E 61 6C 6C 79 20 74 68 nally th
C220 65 20 6C 61 73 74 20 63 e last c
C228 6F 6C 75 60 6E 20 80 64 olumn .d
C230 65 70 69 63 74 73 20 74 epicts t
C238 68 65 20 41 53 43 49 49 he ASCII
C240 20 76 61 6C 75 65 73 20 values
C248 6F 66 20 74 68 65 73 65 of these
C250 20 80 62 79 74 65 73 2E .bytes.
C258 20 75 6E 6C 65 73 73 20 unless
C260 74 68 65 20 62 79 74 65 the byte
C268 20 69 73 20 6E 6F 6E 20 is non-
C270 41 53 43 49 49 20 80 77 ASCII .w
C278 68 69 63 68 20 69 73 20 hich is
C280 74 68 65 6E 20 64 69 73 then dis
C288 70 6C 61 79 65 64 20 61 played a
C290 73 20 61 2G 66 75 6C 6C s a full
C298 20 73 74 6F 70 21 00 00 stop ~ ..

. .- . - -..
C2AO 00 4C 00 C9 A9 FF 85 22 L "
C2A8 08 60 00 00 00 00 00 00

Figure 9.3 Memory dump

114
As it stands, the routine requires three zero page data bytes, two
for the start address and one for the number of eight byte lines to be
dumped. The routine also employs the ADDRESS-PRINT and
HEXPRINT routines discussed earlier.

Program 22

1.0 REM ** DUMP LINES OF 8 BYTES OF **


2.0 REM ** MEMORY IN HEX AND ASCII **
3.0 CODE=49152
4.0 FOR LOOP= TO 111
5.0 READ BYTE
6.0 POKE CODE+LOOP,BYTE
7.0 NEXT LOOP
8.0
9.0 REM ** M/C DATA **
1.0.0 DATA 32,71,192 REM JSR $C47
11.0 REM HEX-BYTES
12.0 DATA 162,7 REM LOX #$.07
13.0 DATA 16.0,.0 REM LOY #$.0.0
14.0 REM HEX-LOOP
15.0 DATA 177,251 REM LOA ($FB), Y
16.0 DATA 32,9.0,192 REM JSR $C5A
17.0 DATA 32,66,192 REM JSR $C42
18.0 DATA 2.0.0 REM INY
19.0 DATA 2.02 REM OEX
2.0.0 DATA 16,244 REM BPL $F4
21.0 DATA 32,66,192 REM JSR $C42
22.0 REM ASCII-BYTES
23.0 DATA 162,7 REM LOX #$.07
24.0 DATA 16.0,.0 REM LOY #$.0.0
25.0 REM ASCII-LOOP
26.0 DATA 177,251 REM LOA ($FB),Y
27.0 DATA 2.01,32 REM CMP #$2.0
28.0 DATA 48,4 REM BMI $.04
29.0 DATA 2.01,128 REM CMP #$8.0
3.0.0 DATA 144,2 REM BCC $.02
31.0 REM FULL-STOP
32.0 DATA 169,46 REM LOA #$2E
33.0 REM LEAP-FROG

115
34.0 DATA 32.21.0.255 REM JSR $FFD2
35.0 DATA 2.0.0 REM INY
36.0 DATA 2.02 REM DEX
37.0 DATA 16 . 237 REM BPL $ED
38.0 DATA 169 . 13 REM LDA 1I$.0D
39.0 DATA 32.21.0 . 255 REM JSR $FFD2
4.0.0 DATA 24 REM CLC
41.0 DATA 165.251 REM LOA $FB
42.0 DATA 1.05 . 8 REM ADC 11$.08
43.0 DATA 133 . 251 REM STA $FB
44.0 DATA 144.2 REM BCC $.02'
45.0 DATA 23.0.252 REM INC $FC
46.0 REM NO-CARRY
47.0 DATA 198.254 REM DEC $FE
48.0 DATA 2.08.191 REM BNE $BF
49.0 DATA 96 REM RTS
5.0.0 REM SPACE
51.0 DATA 169 . 32 REM LDA 11$2.0
52.0 DATA 76 . 21.0 . 255 REM JMP $FFD2
53.0 REM ADDRESS-PRINT
54.0 DATA 162 . 251 REM LDX II$FB
55.0 DATA 181.1 REM LDA 1 . X
56.0 DATA 32 . 9.0 . 192 REM JSR $C.05A
57.0 DATA 181 . .0 REM LDA .0 . X
58.0 DATA 32.9.0 . 192 REM JSR $C.05A
59.0 DATA 32.66 . 192 REM JSR $C.042
6.0.0 DATA 32.66 . 192 REM JSR $C.042
61.0 DATA 96 REM RTS
62.0 REM HEXPRINT
63.0 DATA 72 REM PHA
64.0 DATA 74.74 REM LSR A : LSR A
65.0 DATA 74.74 REM LSR A : LSR A
66.0 DATA 32 . 99 . 192 REM JSR $C.063
67.0 DATA 1.04 REM PLA
68.0 REM FIRST
69.0 DATA 41.15 REM AND 1I$.0F
7.0.0 DATA 2.01.1.0 REM CMP 1I$.0A
71.0 DATA 144.2 REM BCC $.02
72.0 DATA 1.05 . 6 REM ADC 11$.06
73.0 :: REM OVER
116
74~ DATA 1~5,48 REM ADC #$3~
75~ DATA 76,2l~,255 REM JMP $FFD2
76~
77~ REM ** INPUT DETAILS FOR DUMP **
78~ PRINT CHR$(147)
79~ INPUT"DUMP START ADDRESS ";A
8~~ HIGH=INT(A/256)
8l~ LOW=A-(HIGH*256)
82~ POKE 25l,LOW : POKE 252,HIGH
83~ INPUT"NUMBER OF LINES (2~/SCREEN) ";8
84~ POKE 254,B
85~ SYS CODE

The program's operation is quite simple, using the X register to


count the bytes as they are printed across the screen using
HEXPRINT (lines 120 to 210). The second section of code (lines
220 to 370) is responsible for printing either the ASCII character
contained in the byte, or a full stop if an unprintable character or a
control code is encountered. The final section of code moves the
cursor down one line and increments the address counter. The
whole loop is repeated until the line count reaches zero.

Line-by-Iine

A line-by-line description of the Program 22 now follows:

line l~~ print start address of current line


line ll~ entry for HEX-BYTES
line l2~ eight bytes to do (0- 7)
line l3~ clear index
line l4~ entry for HEX-LOOP
line l5~ get byte through vectored address
line l6~ print it as two hex digits
line l7~ print a space
line l8~ increment index
line 19~ decrement bit count
line 2~~ branch to HEX-LOOP until all done
line 2l~ print a space
line 22~ entry for ASCII-BYrES
line 23~ eight bytes to redo
line 24~ set index
line 25~ entry for ASCII-LOOP
117
line 26 get byte through vectored address
line 27 is it less than ASC" "?
line 28 yes, branch to FULL-STOP
line 29 is it greater than 128?
line 3 no, branch to LEAP-FROG
line 3l entry for FULL-STOP
line 32 get ASC"." into accumulator
line 33 entry for LEAP-FROG
line 34 print accumulator's contents
line 35 increment index
line 36 decrement bit count
line 37 branch to ASCII-LOOP until all done
line 38 get ASCII code for RETURN
line 39 print new line
line 4 clear Carry flag
line 4l get low byte of address
line 42 add 8 to it
line 43 save result
line 44 if no carry, branch to NO-CARRY
line 45 else increment high byte of address
line 46 entry for NO-CARRY
line 47 decrement line counter
line 48 branch to start at $C000 until all lines done
line 49 return to BASIC
line 5 entry to SPACE
line 5l get ASCII code for space
line 52 print it and return through jump
line 53 entry to ADDRESS-PRINT
line 54 load index into X register
line 55 get high byte of address
line 56 print it as two hex digits
line 57 get low byte of address
line 58 print it as two hex digits
line 59 print a space
line 6~ print a second space
line 6l return to main program
line 62 entry to HEXPRINT
line 63 save accumulator on stack
line 64 move high nibble into low nibble position
line 66 call FIRST subroutine
118
line 67 restore accumulator to do low byte
line 68 entry for FIRST
line 69 mask off high nibble
line 7 is it less than 10?
line 7l yes, so jump OVER
line 72 add 7 to convert to A - F
line 73 entry to OVER
line 74 add 48 to convert to ASCII code
line 75 print it and return

119
10 HI-res Graphics

The Commodore 64 can support hi-resolution graphics. However,


as you are no doubt aware, setting up the hi-res screen prior to
using it can be a rather long-winded process, requiring several lines
of BASIC text. In fact, four routines are normally required:

1. Move start of BASIC user area and set position for hi-res
screen.
2. Clear screen memory.
3. Select screen colour and clear to that colour.
4. Reselect normal character mode.

All of these can be performed quite simply at machine level, and


the routines for each follow. They can be compiled as D ATA at the
end of a graphics program, poked into memory at RUN time and
executed via a SYS call. This does have one of the original dis-
advantages, in that a large chunk of program is required. However,
the main advantage is speed, particularly in clearing the screen.
Alternatively, any of these routines would make an admirable
addition to the Wedge Operating System. allowing it to be called by
name from within your programs. Suitable command names might
be:

@MOVEBAS move BASIC program area to make room for


hi-res screen
@HIRES select hi-res screen
@CLEAR clear hi-res screen
@GCOL clear to graphics colour specified in a dedicated
byte
@MODE select normal character mode

Let us now examine each command in tum.

120
A BASIC MOVE

You may be wondering why we should bother to move the BASIC


program area at all-why not just position the hi-res screen mid-
way in memory? The reason for the careful positioning of the
routine is as a matter of safety-placing the hi-res screen above the
BASIC program area could lead to it being corrupted, especially if
it is being used in conjunction with the program, because adding a
line or two to the program could cause it to extend into the hi-res
screen. Making sure the BASIC program fits in is qo real safeguard
either, as variables, strings and arrays all eat up memory at an
incredible rate, and these could find their way into the screen
memory. All these problems can be avoided by moving the start of
BASIC up enough bytes to allow the bi-res screen to be tucked in
underneath.
To do this requires a machine code program. The Programmer's
Reference Guide lists five vectors associated with BASBAS (that's
my mnemonic for BASIC's base!), as follows:'

$2B-$2C TXTTAB start of BASIC text


$2D-$2E VARTAB start of BASIC variables
$2F-$3 ARYTAB start of BASIC arrays
$31-$32 STREND end of BASIC arrays + 1
$281-$282 MEMSTR bottom of memory

To move BASIC, each ofthese vectors must be reset to point to the


new start area and the first three bytes of the new start area must be
cleared to keep the Kemal happy.
Program 23 performs each of these functions. The address of the
new BASIC area is $~, which allows room for the hi-res screen
plus 32 sprites.

Program 23

1.0 REM ** MOVE BASIC PROGRAM AREA START **


2.0 REM ** UP TO 16348 TO FREE HI-RES SCREEN **
3.0
4.0 CODE=49152
5.0 FOR LOOP= TO 39
6.0 READ BYTE
7.0 POKE CODE+LOOP,BYTE
8.0 NEXT LOOP
9.0
1.0.0 REM ** M/C DATA **
11.0 DATA 169,.0 REM LOA #$00
l~l
120 DATA 141,2,64 REM STA $4002
130 DATA 141,1,64 REM STA $4001
140 DATA 141,0,64 REM STA $4000
150 DATA 141,129,2 REM STA $0281
160 DATA 169,64 REM LDA #$40
170 DATA 133,44 REM STA $2C
180 DATA 133,46 REM STA $2E
190 DATA 133,48 REM STA $30
200 DATA 133,50 REM STA $32
210 DATA 141,130,2 REM STA $0282
220 DATA 169,1 REM LDA #$01
230 DATA 133,43 REM STA $28
240 DATA 169,3 REM LDA #$03
250 DATA 133,45 REM STA $2D
260 DATA 133,47 REM STA $2F
270 DATA 133,49 REM STA $31
280 DATA 96 REM RTS

Line-by-Iine

A line-by-line description of Program 23 follows:

line 110 initialize accumulator


line 120 and clear first four bytes of new program area
line 150 set low byte of MEMSTR (bottom of memory pointer)
line 160 load high byte of new program area address into
accumulator
line 170 set high byte of TXTTAB
line 180 set high byte of V ARTAB
line 190 set high byte of ARYTAB
line 200 set high byte of STREND
line 210 set high byte of MEMSTR
line 220 load accumulator with 1
line 230 store in low byte of TXTTAB
line 240 load accumulator with 3
line 250 set low bytes of all vectored addresses

122
SELECTING HI-RES

Before selecting the hi-resolution screen mode, it is necessary to


point the VIC chip to the start of screen memory. This is done by
writing to the VIC Memory Control register located at $D018
(57272). The actual location is controlled by the condition of bits 3.
2 and 1. Table 10.1 details their settings for various addresses.

Table 10.1

Bit code Value Address selected

xxxx000x 0 0-2047 ($0000-${{J7FF)


xxxx001x 2 2048-4095 ($0800-$0FFF)
xxxx010x 4 4096-6143 ($1000-$17FF)
xxxx011x 6 6144-8191 ($1800-$lFFF~
xxxx100x 8 8192-10239 ($2000-$27FF)
xxxx101x 10 10240-12287 ($2800-$2FFF)
xxxx110x 12 12288-14335 ($3-$37FF)
xxxx111x 14 14336-16383 ($38-$3FFF)

You can see from the table that the screen memory may be moved
around in 2K block steps. An 'x' in each of the other bits denotes
that these bits may be in either state. However, remember that
these bits are controlling other aspects of the VIC's function, so
that any reprogramming of bits 3,2 and 1 must preserve the other
bits. This is best done with the logical OR function. Looking at
Table 10.1 we can see that bit 3 must be set to point the Memory
Control register at location 8192. In BASIC this would simplify to:

100 A=PEEK(53727) REM GET VALUE


110 A=A OR 8 REM SET BIT 3
12.0 POKE 53727,A REM REPROGRAM

which translates to assembler as:

LOA #$08
ORA $0018
STA $0018

Now that the hi-res screen has been defined, it can be switched in
by setting bit 5 of the VIC Control register at $D011 (53265).

123
Again, the other bits in the register must be preserved, so the byte
must be ORed with 32 (OO1~ binary). In BASIC this is:

130 A=PEEK(53265) REM GET VALUE


140 A=A OR 32 REM SET BIT 5
150 POKE 53265,A REM REPROGRAM

and in assembler:

LOA #$20
ORA $0011
STA $0011

A CLEAR VIEW

Once hi-res mode has been selected, it will be filled with junk
(often referred to as garbage). To clear this, each location must in
turn be POKEd with zero. A BASIC program to do this would take
the form:

200 SB=8192
210 FOR L=SB TO SB+7999
220 POKE L,0
230 NEXT L

Previously, in normal character mode, locations 1024 to 2023 were


used to control which character was displayed-for example,
POKEing a 1 into location 1024 would make a letter A appear at
the top left hand corner of the screen. When in hi-res mode, this
area of memory is used to hold the colour information of that byte.
Note that the colour information does not now come from the
colour memory-colour details are taken directly from the hi-res
screen itself. The high nibble of the byte (that is, bits 4 to 7) holds
the colour code of any bit that is set in that 8 by 8 bit matrix, while
the lower nibble (bits 3 to 0) holds the colour of any bits that are
clear in the same area.
To clear the hi-res screen to black ink on green paper in BASIC
we would use:

240 FOR C=1024 TO 2023


250 POKE C,13
260 NEXT C

If all the above BASIC program lines were to be combined and


RUN, the resulting hi-res screen would take around 20 seconds to
construct-a bit slow, you'll agree! Program 24 provides the
124
machine code equivalent. Note that the value assigned to CODE is
49408 and NOT 49152 as we have been using previously. This is to
allow the program to be used in conjunction with the MOVEBAS
program described earlier. After you have entered and RUN
MOVEBAS, try this one for an instant hi-res screen!

Program 24

Hi REM ** HI-RES GRAPHICS SCREEN SET AND


CLEAR **
20 CODE=49408
30 FOR LOOP=0 TO 105
40 READ BYTE
50 POKE CODE+LOOP,BYTE
60 NEXT LOOP
70
80 REM ** M/C DATA **
85 REM SELECT-HI-RES
90 DATA 169,8 REM LDA #$08
100 DATA 13,24,208 REM ORA $D018
110 DATA 141,24,208 REM STA $D018
120 DATA 169,32 REM LDA #$20
130 DATA 13,17,208 REM ORA $D011
140 DATA 141,17,208 REM STA $D011
150 REM CLEAR-SCREEN-MEMORY
160 DATA 169,0 REM LDA #$00
170 DATA 133,251 REM STA $FC
180 DATA 169,32 REM LDA #$20
190 DATA 133,252 REM STA $FC
200 DATA 169,64 REM LDA #$40
210 DATA 133,253 REM STA $FD
220 DATA 169,63 REM LDA #$3F
230 DATA 133,254 REM STA $FE
240 REM IN
250 DATA 165,252 REM LDA $FC
260 DATA 197,254 REM CMP $FE
270 DATA 208,9 REM BNE $09
280 DATA 165,251 REM LDA $FB
290 DATA 197,253 REM CMP $FD
300 DATA 208,3 REM BNE $03

125
31.0 DATA 76,62,192 REM JMP $C3E
32.0 REM CLEAR
33.0 DATA 16.0,.0 REM LOY #$.0.0
34.0 DATA 169,.0 REM LOA #$.0.0
35.0 DATA 145,251 REM STA ($FB), Y
36.0 DATA 23.0,251 REM INC $FB
37.0 DATA 2.08,231 REM BNE $E7
38.0 DATA 23.0,252 REM INC $FC
39.0 DATA 56 REM SEC
4.0.0 DATA 176,226 REM BCS $E2
41.0
42.0 REM COLOUR
43.0 DATA 169,.0 REM LOA #$.0.0
44.0 DATA 133,251 REM STA $FB
45.0 DATA 169,4 REM LOA #$.04
46.0 DATA 133,252 REM STA $FC
47.0 DATA 169,231 REM LOA #$E7
48.0 DATA 133,253 REM STA $FD
49.0 DATA 169,7 REM LOA #$.07
5.0.0 DATA 133,254 REM STA $FE
51.0 REM CIN
52.0 DATA 165,252 REM LOA $FC
53.0 DATA 197,254 REM CMP $FE
54.0 DATA 2.08,7 REM BNE $.07
55.0 DATA 165,251 REM LOA $FB
56.0 DATA 197,253 REM CMP $FD
57.0 DATA 2.08,1 REM BNE $.01
58.0 DATA 96 REM RTS
59.0 REM GREEN
6.0.0 DATA 16.0,.0 REM LOY #$.0.0
61.0 DATA 169,13 REM LDA #$.00
62.0 DATA 145,251 REM STA ($FB), Y
63.0 DATA 23.0,251 REM INC $FB
64.0 DATA 2.08,233 REM BNE $E9
65.0 DATA 23.0,252 REM INC $FC
66.0 DATA 56 REM SEC
67.0 DATA 176,228 REM BCS $E4

126
Line-by-Iine

A line-by-line description of Program 24 follows:

line 90 load accumulator with mask 00001000


line 100 force bit 3 to select 8196 as bit map start address
line 110 and program VIC Memory Control register
line 120 load accumulator with mask f/1/J1f/1/f11J1/J
line 130 force bit 5 to select bit map mode
line 140 and program CIC Control register
line 150 entry for bit map CLEAR-SCREEN-MEMORY
routine
line 160 set up vector to point to screen start address $2000
line 200 set up vector to point to screen end address $403F
line 240 entry for IN
line 250 get high byte current address
line 260 is it same as high byte end address?
line 270 no, so branch to CLEAR
line 280 yes, get low byte current address
line 290 is it same as low byte end address
line 300 no, so branch to CLEAR
line 310 yes, all done jump to COLOUR
line 320 entry for CLEAR
line 330 initialize index
line 340 clear accumulator
line 350 clear byte of screen memory
line 360 increment low byte of current screen address
line 370 branch to IN if no carryover
line 380 increment high byte
line 390 set Carry flag
line 400 force branch to IN
line 420 entry for COLOUR
line 430 set up vector to point to start of colour memory
line 470 set up vector to point to end of colour memory
line 510 entry for CIN
line 520 get high byte of current address
line 530 is it the same as high byte end address?
line 540 no, branch to GREEN

127
line 550 get low byte of current address
line 560 is it the same as the low byte end address?
line 570 no, branch to GREEN
line 580 back to calling routine
line 590 entry for GREEN
line 600 clear indexing register
line 610 get code for green into accumulator
line 620 POKE it into colour memory
line 630 increment low byte of current address
line 640 branch to CIN if no carryover
line 650 increment high byte
line 660 set Carry flag
line 670 and force branch to CIN

128
Appendix 1: 6510 Complete
Instruction Set

ADC Add with carry NZCV

Address mode Op-code Bytes Cycles


Immediate $69 2 2
Zero page $65 2 3
Zeropage,X $75 2 4
Absolute $60 3 4
Absolute,X $70 3 40r5
Absolute,Y $79 3 40r 5
(Indirect,X) $61 2 6
(Indirect), Y $71 2 5

AND ANO with accumulator NZ

Address mode Op-code Bytes Cycles


Immediate $29 2 2
Zero page $25 2 3
Zero page,X $35 2 4
Absolute $20 3 4
Absolute,X $30 3 40r 5
Absolute,Y $39 3 40r5
(Indirect,X) $21 2 6
(Indirect),Y $31 2 5

129
ASL Shift left NZC

Address mode Op-code Bytes Cycles


Accumulator $0A 1 2
Zero page $06 2 5
Zeropage,X $16 2 6
Absolute $0E 3 6
Absolute,X $IE 3 7

Bee Branch if C = 0 Flags unaltered

Address mode Op-code Bytes Cycles


Relative $90 2 30r2

Bes Branch if C = 1 Flags unaltered

Address mode Op-code Bytes Cycles


Relative $B0 2 30r2

BEQ Branch if Z = 1 Flags unaltered

A ddress mode Op-code Bytes Cycles


Relative $F0 2 30r2

130
BIT Z,N,V

Address mode Op-code Bytes Cycles


Zero page $24 2 3
Absolute $2C 3 4

BMI Branch if N = 1 Flags unaltered

Address mode Op-code Bytes Cycles


Relative $30 2 3or2

BNE Branch if Z = 0 Flags unaltered

Address mode Op-code Bytes Cycles


Relative $D0 2 3or2

BPL Branch ifN =0 Flags unaltered

Address mode Op-code Bytes Cycles


Relative $10 2 3or2

131
BRK Break B flag = 1

Address mode Op-code Bytes Cycles


Implied $00 1 7

BVC Branch if V = 0 Flags unaltered

Address mode Op-code Bytes Cycles


Relative $50 2 30r2

BVS Branch if V =1 Flags unaltered

Address mode Op-code Bytes Cycles


Relative $70 2 3or2

CLC Clear Carry flag C flag = 0

Address mode Op-code Bytes Cycles


Implied $18 1 2

132
CLD Clear Decimal flag o flag = 0

Address mode Op-code Bytes Cycles


Implied $08 1 2

CLI Clear Interrupt flag I flag =0

Address mode Op-code Bytes Cycles


Implied $58 1 2

CLV Clear Overflow flag V flag = 0

Address mode Op-code Bytes Cycles


Implied $88 1 2

CMP Compare accumulator NZC

Address mode Op-code Bytes Cycles


Immediate $C9 2 2
Zero page $C5 2 3
Zeropage,X $05 2 4
Absolute $CD 3 4
Absolute,X $00 3 40r5
Absolute,Y $09 3 40r5
(Indirect ,X) $C1 2 6
(Indirect), Y $01 2 50r6

133
CPX Compare X register NZC

Address mode Op-code Bytes Cycles


Immediate $E0 2 2
Zero page $E4 2 3
Absolute $EC 3 4

Cpy Compa~e Y register NZC

Address mode Op-code Bytes Cycles


Immediate $C0 2 2
Zero page $C4 2 3
Absolute $CC 3 4

DEC Decrement memory NZ

Address mode Op-code Bytes Cycles


Zero page $C6 2 5
Zeropage,X $06 2 6
Absolute $CE 3 6
Absolute,X $DE 3 7

DEX Decrement X register NZ

Address mode Op-code Bytes Cycles


Implied $CA 1 2

134
DEY Decrement Y register NZ

Address mode Op-code Bytes Cycles


Implied $88 1 2

EOR Exclusive-OR NZ

Address mode Op-code Bytes Cycles


Immediate $ 49 2 2
Zero page $ 45 2 3
Zeropage,X $ 55 2 4
Absolute $4D 3 4
Absolute,X $5D 3 4or5
Absolute,Y $59 3 4or5
(lndirect,X) $41 2 6
(Indirect), Y $51 2 5

INC Increment memory NZ

Address mode Op-code Bytes Cycles


Zero page $E6 2 5
Zeropage,X $F6 2 6
Absolute $EE 3 6
Absolute,X $FE 3 7

135
INX Increment X register NZ

Address mode Op-code Bytes Cycles


Implied $E8 1 2

INY Increment Y register NZ

Address mode Op-code Bytes Cycles


Implied $C8 1 2

JMP Jump Flags unaltered

Address mode Op-code Bytes Cycles


Absolute $4C 3 3
Indirect $6C 3 5

JSR Jump to subroutine Flags unaltered

Address mode Op-code Bytes Cycles


Absolute $ 20 3 6

136
LOA Load accumulator NZ

Address mode Op-code Bytes Cycles


Immediate $A9 2 2
Zero page $A5 2 3
Zeropage,X $B5 2 4
Absolute $AD 3 4
Absolute,X $BD 3 4or5
Absolute,Y $B9 3 40r 5
(lndirect,X) $Al 2 6
(Indirect), Y $Bl 2 50r6

LOX Load X register NZ

Address mode Op-code Bytes Cycles


Immediate $A2 2 2
Zero page $A6 2 3
Zeropage,Y $B6 2 4
Absolute $AE 3 4
Absolute,Y $BE 3 4or5

LOY Load Y register NZ

Address mode Op-code Bytes Cycles


Immediate $A0 2 2
Zero page $A4 2 3
Zeropage,X $B4 2 4
Absolute $AC 3 4
Absolute,X $BC 3 4or5

137
LSR Logical shift right N = 0,ZC

Address mode Op-code Bytes Cycles


Accumulator $4A 1 2
Zero page $46 2 5
Zeropage,X $56 2 6
Absolute $4E 3 6
Absolute,X $SE 3 7

NOP No operation Flags unaltered

Address mode Op-code Bytes Cycles


Implied $EA 1 2

ORA Inclusive OR NZ

Address mode Op-code Bytes Cycles


Immediate $09 2 2
Zero page $05 2 3
Zeropage,X $15 2 4
Absolute $00 3 4
Absolute,X $10 3 40r5
Absolute,Y $19 3 40r5
(Indirect,X) $ 01 2 6
(Indirect), Y $11 2 5

138
PUA Push accumulator Flags unaltered

Address mode Op-code Bytes Cycles


Implied $48 1 3

PUP Push Status register Flags unaltered

Address mode Op-code Bytes Cycles


Implied $08 1 3

PLA Pull accumulator NZ

Address mode Op-code Bytes Cycles


Implied $68 1 4

PLP Pull Status register Flags as status

Address mode Op-code Bytes Cycles


Implied $28 1 4

139
ROL Rotate left NZC

Address mode Op-code Bytes Cycles


Accumulator $2A 1 2
Zero page $26 2 5
Zeropage,X $36 2 6
Absolute $2E 3 6
Absolute,X $3E 3 7

ROR Rotate right NZC

Address mode Op-code Bytes Cycles


Accumulator $6A 1 2
Zero page $66 2 5
Zeropage,X $76 2 6
Absolute $6E 3 6
Absolute,X $7E 3 7

RTI Return from interrupt Flags as pulled

4ddress mode Op-code Bytes Cycles


Implied $ 40 1 6

140
RTS Return from subroutine Flags unaltered

Address mode Op-code Bytes Cycles


ImpJied $60 1 6

SBC Subtract from accumulator NZCV

Address fHv.ie Op-code Bytes Cycles


Immediate $E9 2 2
Zero page $E5 2 3
Zero page ,X $F5 2 4
Absolute SED 3 4
Absolute ,X $FD 3 4or5
Absolute,Y $F9 3 4or5
(Indirect ,X) $ E1 2 6
(Indirect), Y $ Fl 2 50r6

SEC Set Carry flag C=l

Address mode Op-code Bytes Cycles


Implied $38 1 2

SED Set Decimal flag D= 1

Address mode Oo-code Bytes Cycles


Implied $F8 1 2

141
SEI Set Interrupt flag 1=1

Address mode Op-code Bytes Cycles


Implied $78 1 2

STA Store accumulator Flags unaltered

Address mode Op-code Bytes Cycles


Zero page $85 2 3
Zero page ,X $95 2 4
Absolute $8D 3 4
Absolute ,X $9D 3 5
Absolute,Y $99 3 5
(Indirect,X) $81 2 6
(Indirect), Y $91 2 6

STX Store X register Flags unaltered

Address mode Op-code Bytes Cycles


Zero page $ 86 2 3
Zeropage,Y $ 96 2 4
Absolute $8E 3 4

142
STY Store Y register Flags unaltered

Address mode Op-code Bytes Cycles


Zero page $84 2 3
Zeropage,X $94 2 4
Absolute $8C 3 4

TAX Transfer accumulator to X NZ

Address mode Op-code Bytes Cycles


Implied $AA 1 2

TAY Transfer accumulator to Y NZ

Address mode Op-code Bytes Cycles


Implied $A8 2

TSX Transfer Stack Pointer to X NZ

Address mode Op-code Bytes Cycles


Implied $BA 1 2

143
TXA Transfer X to accumulator NZ

Address mode Op-code Bytes Cycles


Implied $8A 1 2

TXS Transfer X to Stack Pointer Flags unaltered

A ddress mode Op-code Bytes Cycles


Implied $9A 1 2

TVA Transfer Y to accumulator NZ

Address mode Ov-code Bytes Cycles


Implied $98 1 2

144
Appendix 2: 6510 Opcodes

All numbers are hexadecimal.

00 BRK implied IC Future expansion


01 ORA (zero page, X) ID ORA absolute, X
02 Future expansion IE ASL absolute, X
03 Future expansion IF Future expansion
04 Future expansion 20 JSR absolute
05 ORA zero page 21 AND (zero page, X)
06 ASL zero page 22 Future expansion
07 Future expansion 23 Future expansion
08 PHP implied 24 BIT zero page
09 ORA lIimmediate 25 AND zero page
0A ASL accumulator 26 ROL zero page
0B Future expansion 27 Future expansion
0C Future expansion 28 PLP implied
0D ORA absolute 29 AND lIimmediate
0E ASL absolute 2A ROL accumulator
0F Future expansion 2B Future expansion
10 BPL relative 2C BIT absolute
II ORA (zero page), Y 2D AND absolute
12 Future expansion 2E ROL absolute
13 Future expansion 2F Future expansion
14 Future expansion 30 BMI relative
15 ORA zero page, X 31 AND (zero page), Y
16 ASL zero page, X 32 Future expansion
17 Future expansion 33 Future expansion
18 CLC implied 34 Future expansion
19 ORA absolute, Y 35 AND zero page, X
IA Future expansion 36 ROL zero page, X
IB Future expansion 37 Future expansion

145
38 SEC implied 50 EOR absoltlte, X
39 AND absolute, Y 5E LSR absolute, X
3A Future expansion 5F Future expansion
3B Future expansion 60 RTS implied
3C Future expansion 61 ADC (zero page, X)
3D AND absolute, X 62 Future expansion
3E ROL absolute, X 63 Future expansion
3F Future expansion 64 Future expansion
40 RTI implied 65 ADC zero page
41 EOR (zero page, X) 66 ROR zero page
42 Future expansion 67 Future expansion
43 Future expansion 68 PLA implied
44 Future expansion 69 ADC Itimmediate
45 EOR zero page 6A ROR accumulator
46 LSR zero page 6B Future expansion
47 Future expansion 6C JMP (indirect)
48 PHA implied 60 ADC absolute
49 EOR Itimmediate 6E ROR absolute
4A LSR accumulator 6F Future expansion
4B Future expansion 70 BYS relative
4C JMP absolute 71 ADC (zero page), Y
40 EOR absolute 72 Future expansion
4E LSR absolute 73 Future expansion
4F Future expansion 74 Future expansion
50 BYC relative 75 ADC zero page, X
51 EOR (zero page), Y 76 ROR zero page, X
52 Future expansion 77 Future expansion
53 Future expansion 78 SEI implied
54 Future expansion 79 ADC absolute, Y
55 EOR zero page, X 7A Future expansion
56 LSR zero page, X 7B Future expansion
57 Future expansion 7C Future expansion
58 CLI implied 7D ADC absolute, X
59 EOR absolute, Y 7E ROR absolute, X
5A Future expansron 7F Future expansion
5B Future expansion 80 Future expansion
5C Future expansion 81 ST A (zero page, X)

146
82 Future expansion A7 Future expansion
83 Future expansion A8 TAY implied
84 STY zero page A9 LOA lIimmediate
85 ST A zero page AA TAX implied
86 STX zero page AB Future expansion
87 Future expansion AC LOY absolute
88 DEY implied AD LOA absolute
89 Future expansion AE LOX absolute
8A TXA implied AF Future expansion
8B Future expansion B0 BCS relative
8C STY absolute BI LOA (zero page). Y
80 ST A absolute B2 Future expansion
8E STX absolute B3 Future expansion
8F Future expansion B4 LOY zero page. X
90 BCC relative 85 LOA zero page. X
91 ST A (zero page). Y B6 LOX zero page. Y
92 Future expansion B7 Future expansion
93 Future expansion B8 CLY implied
94 STY zero page. X 89 LOA absolute. Y
95 STA zero page, X 8A TSX implied
96 STX zero page, Y BB Future expansion
97 Future expansion BC LOY absolute, X
98 TYA implied BO LOA absolute. X
99 ST A absolute, Y BE LOX absolute. Y
9A TXS implied BF Future expansion
9B Future expansion C0 CPY lIimmediate
9C Future expansion Cl CMP (zero page. X)
90 ST A absolute, X C2 Future expansion
9E Future expansion C3 Future expansion
9F Future expansion C4 CPY zero page
A0 LOY lIimmediate C5 eMP zero page
Al LOA (zero page. X) C6 DEC zero page
A2 LOX lIimmediate C7 Future expansion
A3 Future expansion C8 INY implied
A4 LOY zero page C9 CMP #immediate
A5 LOA zero page CA OEX implied
A6 LOX zero page CB Future expansion

147
CC CPY absolute E6 INC zero page
CD CMP absolute E7 Future expansion
CE DEC absolute E8 INX implied
CF Future expansion E9 SBC lIimmediate
00 BNE relative EA NOP implied
01 CMP (zero page), Y EB Future expansion
D2 Future expansion EC CPX absolute
D3 Future expansion ED SBC absolute
04 Future expansion EE INC absolute
D5 CMP zero page, X EF Future expansion
06 DEC zero page, X F0 BEQrelative
07 Future expansion FI SBC (zero page), Y
08 CLD implied F2 Future expansion
09 CMP absolute, Y F3 Future expansion
DA Future expansion F4 Future expansion
DB Future expansion F5 SBC zero page, X
DC Future expansion F6 INC zero page, X
DO CMP absolute, X F7 Future expansion
DE DEC absolute. X F8 SED implied
OF Future expansion F9 SBC absolute, Y
E0 CPX lIimmediate FA Future expansion
EI SBC (zero page. X) FB Future expansion
E2 Future expansion FC Future expansion
E3 Future expansion FD SBC absolute, X
E4 CPX zero page FE INC absolute, X
E5 SBC zero page FF Future expansion

148
Appendix 3: Commodore 64
Memory Map
FFFF

Kernal Operating System


ROM

DCOO'
Colour RAM
D800'
VIC and SID

'Free'RAM

BASIC interpreter ROM

VSP cartridge ROM


81mB'

Program area

Screen memory

400
Kernal vectors and flags
300
Input buffers

200
Stack
1m
Zero page

149
Appendix 4: Branch Calculators
The branch calculators are used to give branch values in hex. First, count the number of
bytes you need to branch. Then locate this number in the centre of the appropriate table,
and finally, read off the high and low hex nibbles from the side column and top row
respectively.

Example For a backward branch of 16 bytes:

Locate 16 in the centre of Table A4.1 (bottom row), then read off high nibble (lIF) and
low nibble (#0) to give displacement value (#F0).

Table A4.1 Backward branch calculator

:sN 8
9
0

128
112
2

127 126
III 110
3 4 5

125 124 123 122


109 108 107 106
6 7 8 9

121 120 1I9


105 104 103
A

1I8
102
B

117
101
C

116
100
0

115
99
E

114
98
F

II3
97
A 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81
B 80 79 n 77 76 75 74 73 72 71 70 69 68 67 66 65
C 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49
0 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33
E 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17
F 16 15 14 I3 12 II 10 9 8 7 6 5 4 3 2 I

Table A4.2 Forward branch calculator

0 2 3 4 5 6 7 8 9 A B C 0 E F

0 0 I 2 3 4 5 6 7 8 9 10 II 12 13 14 15
I 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
2 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
3 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
4 64 66 66 67 68 69 70 71 72 73 74 75 76 77 78 79
5 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
6 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 III
7 112 113 1I4 115 1I6 117 1I8 119 120 121 122 123 124 125 126 127

150
Index

@CLS, 13, 16 graphics, hi-res, 120


@LOW, 13, 16
hi-res graphics, 120
@UP, 13, 16
selection, 123
ASCII decimal string to binary,
clear screen, 124
30
ASCII hex to binary conversion, memory,
20,26 dump, 113,
fill, 111
BASIC, Extended Super, 17
move, 104
BASIC, move start of, 121
move BASIC area, 121
BASIC tester, 4
print a hex address, 41
binary input, 100
print accumulator as hex, 38
binary output, 98
printing print, 78
binary to hex conversion 38
binary to signed ASCII s~ring, 42
print string from memory, 78
print string in program, 81
bubble sort, 84
shift register,
CHRGET, 7,13,14
24-bit, 29
commands, 7
16-bit,35
conversion,
software stack, 91
ASCII decimal string to binary,
string manipulation, 53
30
copy substring, 64
ASCII hex to binary, 20, 26
insert substring, 71
binary to hex, 38
string comparison, 53
binary to signed ASCII string,
string concatenation, 58
42
toolbox, 3
debugging, 5
wedge operating system, 9
Extended Super BASIC, 17
writing machine code, 4

151
Other titles of interest

Easy Programming for the Commodore 64 6.95


Ian Stewart & Robin Jones
An intoductory guide to BASIC programming.
Commodore 64 Assembly Language 7.95
Bruce Smith
The Commodore 64 Music Book 5.95
James Vogel & Nevin B. Scrimshaw
Commodore 64 Machine Codew 6.95
Ian Stewart & Robin Jones
'An excellent introduction to the subject'-Popular Computing
Weekly
Gateway to Computing with the Commodore 64
Ian Stewart
'Recommended'-Popular Computing Weekly
Book One 4.95(p) 6.95(h)
Book Two 4.95(p) 6.95(h)
Computers in a Nutshell 4.95
Ian Stewart
Microchip Mathematics: Number Theory for
Computor Users 12.95
Keith Devlin
A fascinating book about the interaction of mathematics and
computing.
Brainteasers for BASIC Computers 4.95
Gorden Lee
'A book I would warmly recommend'-Computer & Video Games
ORDER FORM
I should like to order the following Shiva titles:
Qty Title ISBN Price
EASY PROGRAMMING FOR THE COMMODORE 64 090681264 X 6.95
COMMODORE 64 ASSEMBL Y LANGUAGE 0906812968 7.95
THE COMMODORE 64 MUSIC BOOK 1850140197 5.95
COMMODORE 64 MACHINE CODE 185014025 1 6.95

GA TEW A Y TO COMPUTING WITH THE COMMODORE 64

BOOK ONE (pbk) 1850140170 4.95

BOOK ONE (hdbk) 1850140510 6.95


BOOK TWO (pbk) 1850140359 4.95
BOOK TWO (hdbk) 1850140553 6.95

COMPUTERS IN A NUTSHELL 1850140189 4.95

MICROCHIP MA THEMA TICS 1850140472 12.95


BRAINTEASERS FOR BASIC COMPUTERS 0906812364 4.95

Please send me a full catalogue of computer books and software: o


Name ............................................................ .
Address .......................................................... .

This form should be taken to your local bookshop or computer store. Incase of
difficulty, write to Shiva Publishing Ltd, FreeposL 64 Welsh Row. Nantwich.
Cheshire CW5 5BR, enclosing a cheque for .......................... .

For payment by credit card: Access/Barclaycard/Visa/ American Express

Card No .................... . Signature

You might also like