Jim Butterfield, Associate Editor
Bagel
Break, Part 2
Last month we outlined the logic of a simple machine language program to play "Bagels," a well-known guessing game. Let's pause and look at the various ways we can change our planned program into a real machine language program.
You may have a tiny assembler that is built into your monitor system. This type of simple assembler is often called a nonsymbolic assembler for reasons we'll discuss in a moment. If so, you'll work out all the addresses yourself and write them in as you jot down the program coding. The type of outline you write will be similar to that in Program 2. You'll need to guess at some of the "forward" branches; at the time you write the branch instruction, you won't know what the exact destination address will be. No matter, as long as you remember to put the correct addresses in later.
You may have purchased a full-scale assembler, in which case you'll write the program as shown in Program 1. It's still the same logic flow, but now we can give a name (or "symbolic address") to the various parts of the program. We'll let the assembler figure out where these locations are and compute the correct branch for us. This type of assembler, where we can name locations with symbolic names, or "labels," is often called a "symbolic assembler" to distinguish it from the simple assemblers mentioned previously.
Symbolic names, or labels, seem like a convenience feature at first: not too important, but handy. In fact, they change the nature of the work in a couple of ways. First, we now have the freedom to give meaningful names to our program and work locations. The program is easier to read. Second - and this can be very powerful - we can move the logic to an entirely new part of memory with very little work; the assembler will figure everything out for us. Perhaps most important of all: if we wish to change or correct the program, we can do so without needing to type everything in again; the "source" coding will be saved on a file and may be recalled and corrected as desired.
In whatever fashion we write our program outline, we'll still need to change it into machine language. We may use an assembler - symbolic or nonsymbolic - or we might do the job by hand. Program 3 shows the output from a typical assembly. It's full of information, but the only data that really count are the two-digit hexadecimal numbers found to the left of the printout. (The four-digit hex numbers at the extreme left are addresses, to help you know where the code is located.)
An assembly listing is a rich source of information, especially if it's well commented. But the business end - the two-digit hex numbers - is all that is needed to do the job. Those numbers are all that we need to put into the computer. Program 4 shows a hexadecimal dump of memory with the program in place. All the pretty trimmings from the assembly listing are gone. All that we have are the instructions, ready to go to work. That's probably the way we would type them in, using the screen-editing feature of the machine language monitor to change memory until it looked like Program 4.
But our game isn't completed yet. We need to generate the mystery numbers from BASIC, and tie all the pieces together. Next time....
Program 1:
Code As Prepared For A Full Assembler
NGUESS =$0240 ;number of guesses
EXACT =$0241 ;exact count
MATCH =$0242 ;other match count
INCHAR =$0243 ;input character count
SECRET =$0244 ;secret code
SCOPY =$0248 ;copy of secret code
UGUESS =$024C ;user's guess
*= $033C ;start program here
; start game
START LDA #$00 ; guesses to zero
STA NGUESS
; accept next guess
GUESS INC NGUESS ; count the guess
LDA NGUESS ; look at it
CMP #10 ; over nine?
BEQ QUIT ; yup, quit
JSR PLAY ; take guess
BNE GUESS ; not finished? back
QUIT RTS ; end of game
; get guess & play
PLAY ORA #$30 ;ascii numeric
JSR $FFD2 ; .. print
LDA #$20 ;ascii space
JSR $FFD2 ; print it
; set counts to zero
LDX #0
STX EXACT
STX MATCH
STX INCHAR
; get 4 character guess
INLOOP JSR $FFE4 ; get char
CMP #$41 ; less than A
BCC INLOOP
CMP #$47 ; over F
BCS INLOOP ; reject it
JSR $FFD2 ; OK, print it
LDX INCHAR ; get position
INC INCHAR ; bump position
STA UGUESS,X ; store character
LDA SECRET,X ; copy secret char
STA SCOPY,X ; .. to copy area
CPX #3 ; four chars?
BNE INLOOP ; nope, go back
; check guess for exact matches
COMPAR LDA SCOPY,X ; test character
CMP UGUESS,X ; against guess
BNE SKIP ; nope, try next
INC EXACT ; yes, count it
LDA #0 ; and wipe out..
STA SCOPY,X ; .. matching ..
STA UGUESS,X ; .. characters
SKIP DEX
BPL COMPAR
; check for out-of-place matches
LDY #$00 ; first secret char
RETRY LDX #$00 ; first guessed char
CHECK LDA SCOPY,Y ; is character wiped?
BEQ PASS ; yes, ignore next bit
CMP UGUESS,X ; compare it to guess
BNE PASS ; nope, move on
INC MATCH ; yup, count it
LDA #$00 ; and wipe out..
STA SCOPY,Y ; .. matching ..
STA UGUESS,X ; .. characters
PASS INX ; next guessed character
CPX #4 ; tried them all?
BCC CHECK ; no, try next one
INY ; next secret character
CPY #4 ; tried them all?
BCC RETRY ; no, keep going
; print results
LDX #0 ; start at 'exact'
PLOOP LDA #$20 ; print a space
JSR $FFD2
LDA EXACT,X ; get the number
ORA #$30 ; to ascii numeric
JSR $FFD2 ; and print
INX ; move to 'match'
CPX #$02 ; too far?
BCC PLOOP ; nope, keep printing
LDA #$0D ; print 'return'
JSR $FFD2
LDA EXACT ; four exact?
CMP #4 ; if so, set z flag
RTS
Program 2:
Code As Prepared For
A Tiny Assembler
(033C) LDA #$00
STA $0240
(0341) INC $0240
LDA $0240
CMP #10
BEQ $0350
JSR $0351
BNE $0341
(0350) RTS
(0351) ORA #$30
JSR $FFD2
LDA #$20
JSR $FFD2
LDX #$00
STX $0241
STX $0242
STX $0243
(0366) JSR $FFE4
CMP #$41
BCC $0366
CMP #$47
BCS $0366
JSR $FFD2
LDX $0243
INC $0243
STA $024C,X
LDA $0244,X
STA $0248,X
CPX #$03
BNE $0366
(0387) LDA $0248,X
CMP $024C,X
BNE $0394
INC $0241
LDA #$00
STA $0248,X
STA $024C,X
(039A) DEX
BPL $0381
LDY #$00
(039F) LDX #$00
(03A1) LDA $0248,Y
BEQ $03B0
CMP $024C,X
BNE $03B0
INC $0242
LDA #$00
STA $0248,Y
STA $024C,X
(03B6) INX
CPX #$04
BCC $039B
INY
CPY #$04
BCC $0399
LDX #$00
(03C2) LDA #$20
JSR $FFD2
LDA $0241,X
ORA #$30
JSR $FFD2
INX
CPX #$02
BCC $03BC
LDA #$0D
JSR $FFD2
LDA $0241
CMP #$04
RTS
Program 3: Code As Assembled By A Full Assembler
NGUESS =$0240 ;number of guesses
EXACT =$0241 ;exact count
MATCH =$0242 ;other match count
INCHAR =$0243 ;input char count
SECRET =$0244 ;secret code
SCOPY =$0248 ;copy secret code
UGUESS =$024C ;user's guess
*= $033C ;start program here
; start game
033C A9 00 START LDA #$00 ; guesses, zero
033E 8D 40 02 STA NGUESS
;accept next guess
0341 EE 40 02 GUESS INC NGUESS ; count guess
0344 AD 40 02 LDA NGUESS ; get it
0347 C9 0A CMP #10 ; over nine?
0349 F0 05 BEQ QUIT ; yup, quit
034B 20 51 03 JSR PLAY ; take guess
034E D0 Fl BNE GUESS ; not finished?
0350 60 QUIT RTS ; end of game
;get guess & play
0351 09 30 PLAY ORA #$30 ;ascii numeric
0353 20 D2 FF JSR $FFD2 ; .. print
0356 A9 20 LDA #$20 ;ascii space
0358 20 D2 FF JSR $FFD2 ; print it
;set counts to zero
035B A2 00 LDX #0
035D 8E 41 02 STX EXACT
0360 8E 42 02 STX MATCH
0363 8E 43 02 STX INCHAR ; get 4 character
guess
0366 20 E4 FF INLOOP JSR $FFE4 ; get char
0369 C9 41 CMP #$41 ; less than A
036B 90 F9 BCC INLOOP
036D C9 47 CMP #$47 ; over F
036F B0 F5 BCS INLOOP ; reject it
0371 20 D2 FF JSR $FFD2 ; OK, print it
0374 AE 43 02 LDX INCHAR ; get position
0377 EE 43 02 INC INCHAR ; bump position
037A 9D 4C 02 STA UGUESS,X ; store
character
037D BD 44 02 LDA SECRET,X ; copy secret
ch
0380 9D 48 02 STA SCOPY,X ; .. to copy
area
0383 E0 03 CPX #3 ; four chars?
0385 D0 DF BNE INLOOP ; nope, go back
; check guess for
exact matches
0387 BD 48 02 COMPAR LDA SCOPY,X ; test
character
038A DD 4C 02 CMP UGUESS,X ; against guess
038D D0 0B BNE SKIP ; nope, try
next
038F EE 41 02 INC EXACT ; yes, count it
0392 A9 00 LDA #0 ; and wipe
out..
0394 9D 48 02 STA SCOPY,X ; .. matching
..
0397 9D 4C 02 STA UGUESS,X ; .. characters
039A CA SKIP DEX
039B 10 EA BPL COMPAR
; check for matches
039D A0 00 LDY #$00 ; first secret
039F A2 00 RETRY LDX #$00 ; first guessed
03A1 B9 48 02 CHECK LDA SCOPY,Y ; char wiped?
03A4 F0 10 BEQ PASS ; yes, ignor
03A6 DD 4C 02 CMP UGUESS,X ; compare
03A9 D0 0B BNE PASS ; nope, move on
03AB EE 42 02 INC MATCH ; yup, count it
03AE A9 00 LDA #$00 ; and wipe
out..
03B0 99 48 02 STA SCOPY,Y ; .. matching
..
03B3 9D 4C 02 STA UGUESS,X ; .. characters
03B6 E8 PASS INX ; next guess
03B7 E0 04 CPX #4 ; tried all?
03B9 90 E6 BCC CHECK ; no, try next
03BB C8 INY ; next char
03BC C0 04 CPY #4 ; tried all?
03BE 90 DF BCC RETRY ; no, keep on
; print results
03C0 A2 00 LDX #0 ; first numbr
03C2 A9 20 PLOOP LDA #$20 ; print space
03C4 20 D2 FF JSR $FFD2
03C7 BD 41 02 LDA EXACT,X ; get number
03CA 09 30 ORA #$30 ; to ascii num.
03CC 20 D2 FF JSR $FFD2 ; and print
03CF E8 INX ; move on
03D0 E0 02 CPX #$02 ; too far?
03D2 90 EE BCC PLOOP ; nope, loop
03D4 A9 0D LDA #$0D ; print return
03D6 20 D2 FF JSR $FFD2
03D9 AD 41 02 LDA EXACT ; four exact?
03DB C9 04 CMP #4 ; z flag
03DD 60 RTS
Program 4:
Hexadecimal Dump Of Memory
C*
PC IRQ SR AC XR YR SP
.; B780 E455 2C 34 3A 9D F8
.
.: 033C A9 00 8D 40 02 EE 40 02
.: 0344 AD 40 02 C9 0A F0 05 20
.: 034C 51 03 D0 F1 60 09 30 20
.: 0354 D2 FF A9 20 20 D2 FF A2
.: 035C 00 8E 41 02 8E 42 02 8E
.: 0364 43 02 20 E4 FF C9 41 90
.: 036C F9 C9 47 B0 F5 20 D2 FF
.: 0374 AE 43 02 EE 43 02 9D 4C
.: 037C 02 BD 44 02 9D 48 02 E0
.: 0384 03 D0 DF BD 48 02 DD 4C
.: 038C 02 D0 0B EE 41 02 A9 00
.: 0394 9D 48 02 9D 4C 02 CA 10
.: 039C EA A0 00 A2 00 B9 48 02
.: 03A4 F0 10 DD 4C 02 D0 0B EE
.: 03AC 42 02 A9 00 99 48 02 9D
.: 03B4 4C 02 E8 E0 04 90 E6 C8
.: 03BC C0 04 90 DF A2 00 A9 20
.: 03C4 20 D2 FF BD 41 02 09 30
.: 03CC 20 D2 FF E8 E0 02 90 EE
.: 03D4 A9 0D 20 D2 FF AD 41 02
.: 03DC C9 04 60
.