mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2024-12-21 12:29:46 +00:00
1235 lines
28 KiB
Plaintext
1235 lines
28 KiB
Plaintext
<appendix>
|
|
<title>Example Programs</title>
|
|
<para>
|
|
This Appendix collects all the programs referred to in the course
|
|
of this manual.
|
|
</para>
|
|
<section id="tutor1-src">
|
|
<title id="tutor1-fname"><filename>hello1.oph</filename></title>
|
|
<programlisting>
|
|
.word $0801
|
|
.org $0801
|
|
.outfile "hello.prg"
|
|
|
|
.word next, 10 ; Next line and current line number
|
|
.byte $9e," 2064",0 ; SYS 2064
|
|
next: .word 0 ; End of program
|
|
|
|
.advance 2064
|
|
|
|
ldx #0
|
|
loop: lda hello, x
|
|
beq done
|
|
jsr $ffd2
|
|
inx
|
|
bne loop
|
|
done: rts
|
|
|
|
hello: .byte "HELLO, WORLD!", 0
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor2-src">
|
|
<title id="tutor2-fname"><filename>hello2.oph</filename></title>
|
|
<programlisting>
|
|
.word $0801
|
|
.org $0801
|
|
.outfile "hello.prg"
|
|
|
|
.scope
|
|
.word _next, 10 ; Next line and current line number
|
|
.byte $9e," 2064",0 ; SYS 2064
|
|
_next: .word 0 ; End of program
|
|
.scend
|
|
|
|
.advance 2064
|
|
|
|
.alias chrout $ffd2
|
|
|
|
ldx #0
|
|
* lda hello, x
|
|
beq +
|
|
jsr chrout
|
|
inx
|
|
bne -
|
|
* rts
|
|
|
|
hello: .byte "HELLO, WORLD!", 0
|
|
</programlisting>
|
|
</section>
|
|
<section id="c64-1-src">
|
|
<title id="c64-1-fname"><filename>c64-1.oph</filename></title>
|
|
<programlisting>
|
|
.word $0801
|
|
.org $0801
|
|
|
|
.scope
|
|
.word _next, 10 ; Next line and current line number
|
|
.byte $9e," 2064",0 ; SYS 2064
|
|
_next: .word 0 ; End of program
|
|
.scend
|
|
|
|
.advance 2064
|
|
|
|
.require "../platform/c64kernal.oph"
|
|
</programlisting>
|
|
</section>
|
|
<section id="kernal-src">
|
|
<title id="kernal-fname"><filename>c64kernal.oph</filename></title>
|
|
<programlisting>
|
|
; KERNAL routine aliases (C64)
|
|
|
|
.alias acptr $ffa5
|
|
.alias chkin $ffc6
|
|
.alias chkout $ffc9
|
|
.alias chrin $ffcf
|
|
.alias chrout $ffd2
|
|
.alias ciout $ffa8
|
|
.alias cint $ff81
|
|
.alias clall $ffe7
|
|
.alias close $ffc3
|
|
.alias clrchn $ffcc
|
|
.alias getin $ffe4
|
|
.alias iobase $fff3
|
|
.alias ioinit $ff84
|
|
.alias listen $ffb1
|
|
.alias load $ffd5
|
|
.alias membot $ff9c
|
|
.alias memtop $ff99
|
|
.alias open $ffc0
|
|
.alias plot $fff0
|
|
.alias ramtas $ff87
|
|
.alias rdtim $ffde
|
|
.alias readst $ffb7
|
|
.alias restor $ff8a
|
|
.alias save $ffd8
|
|
.alias scnkey $ff9f
|
|
.alias screen $ffed
|
|
.alias second $ff93
|
|
.alias setlfs $ffba
|
|
.alias setmsg $ff90
|
|
.alias setnam $ffbd
|
|
.alias settim $ffdb
|
|
.alias settmo $ffa2
|
|
.alias stop $ffe1
|
|
.alias talk $ffb4
|
|
.alias tksa $ff96
|
|
.alias udtim $ffea
|
|
.alias unlsn $ffae
|
|
.alias untlk $ffab
|
|
.alias vector $ff8d
|
|
|
|
; Character codes for the colors.
|
|
.alias color'0 144
|
|
.alias color'1 5
|
|
.alias color'2 28
|
|
.alias color'3 159
|
|
.alias color'4 156
|
|
.alias color'5 30
|
|
.alias color'6 31
|
|
.alias color'7 158
|
|
.alias color'8 129
|
|
.alias color'9 149
|
|
.alias color'10 150
|
|
.alias color'11 151
|
|
.alias color'12 152
|
|
.alias color'13 153
|
|
.alias color'14 154
|
|
.alias color'15 155
|
|
|
|
; ...and reverse video
|
|
.alias reverse'on 18
|
|
.alias reverse'off 146
|
|
|
|
; ...and character set
|
|
.alias upper'case 142
|
|
.alias lower'case 14
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor3-src">
|
|
<title id="tutor3-fname"><filename>hello3.oph</filename></title>
|
|
<programlisting>
|
|
.include "c64-1.oph"
|
|
.outfile "hello.prg"
|
|
|
|
.macro print
|
|
ldx #0
|
|
_loop: lda _1, x
|
|
beq _done
|
|
jsr chrout
|
|
inx
|
|
bne _loop
|
|
_done:
|
|
.macend
|
|
|
|
.macro greet
|
|
`print hello1
|
|
`print _1
|
|
`print hello2
|
|
.macend
|
|
|
|
lda #147
|
|
jsr chrout
|
|
`greet target1
|
|
`greet target2
|
|
`greet target3
|
|
`greet target4
|
|
`greet target5
|
|
`greet target6
|
|
`greet target7
|
|
`greet target8
|
|
`greet target9
|
|
`greet target10
|
|
rts
|
|
|
|
hello1: .byte "HELLO, ",0
|
|
hello2: .byte "!", 13, 0
|
|
|
|
target1: .byte "PROGRAMMER", 0
|
|
target2: .byte "ROOM", 0
|
|
target3: .byte "BUILDING", 0
|
|
target4: .byte "NEIGHBORHOOD", 0
|
|
target5: .byte "CITY", 0
|
|
target6: .byte "NATION", 0
|
|
target7: .byte "WORLD", 0
|
|
target8: .byte "SOLAR SYSTEM", 0
|
|
target9: .byte "GALAXY", 0
|
|
target10: .byte "UNIVERSE", 0
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor4a-src">
|
|
<title id="tutor4a-fname"><filename>hello4a.oph</filename></title>
|
|
<programlisting>
|
|
.include "c64-1.oph"
|
|
.outfile "hello.prg"
|
|
|
|
.macro print
|
|
ldx #0
|
|
_loop: lda _1, x
|
|
beq _done
|
|
jsr chrout
|
|
inx
|
|
bne _loop
|
|
_done:
|
|
.macend
|
|
|
|
.macro greet
|
|
lda #30
|
|
jsr delay
|
|
`print hello1
|
|
`print _1
|
|
`print hello2
|
|
.macend
|
|
|
|
lda #147
|
|
jsr chrout
|
|
`greet target1
|
|
`greet target2
|
|
`greet target3
|
|
`greet target4
|
|
`greet target5
|
|
`greet target6
|
|
`greet target7
|
|
`greet target8
|
|
`greet target9
|
|
`greet target10
|
|
rts
|
|
|
|
hello1: .byte "HELLO, ",0
|
|
hello2: .byte "!", 13, 0
|
|
|
|
target1: .byte "PROGRAMMER", 0
|
|
target2: .byte "ROOM", 0
|
|
target3: .byte "BUILDING", 0
|
|
target4: .byte "NEIGHBORHOOD", 0
|
|
target5: .byte "CITY", 0
|
|
target6: .byte "NATION", 0
|
|
target7: .byte "WORLD", 0
|
|
target8: .byte "SOLAR SYSTEM", 0
|
|
target9: .byte "GALAXY", 0
|
|
target10: .byte "UNIVERSE", 0
|
|
|
|
; DELAY routine. Executes 2,560*(A) NOP statements.
|
|
delay: tax
|
|
ldy #00
|
|
* nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
iny
|
|
bne -
|
|
dex
|
|
bne -
|
|
rts
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor4b-src">
|
|
<title id="tutor4b-fname"><filename>hello4b.oph</filename></title>
|
|
<programlisting>
|
|
.include "c64-1.oph"
|
|
.outfile "hello.prg"
|
|
|
|
.macro print
|
|
ldx #0
|
|
_loop: lda _1, x
|
|
beq _done
|
|
jsr chrout
|
|
inx
|
|
bne _loop
|
|
_done:
|
|
.macend
|
|
|
|
.macro greet
|
|
lda #30
|
|
jsr delay
|
|
`print hello1
|
|
`print _1
|
|
`print hello2
|
|
.macend
|
|
|
|
lda #147
|
|
jsr chrout
|
|
lda #lower'case
|
|
jsr chrout
|
|
`greet target1
|
|
`greet target2
|
|
`greet target3
|
|
`greet target4
|
|
`greet target5
|
|
`greet target6
|
|
`greet target7
|
|
`greet target8
|
|
`greet target9
|
|
`greet target10
|
|
rts
|
|
|
|
hello1: .byte "Hello, ",0
|
|
hello2: .byte "!", 13, 0
|
|
|
|
target1: .byte "programmer", 0
|
|
target2: .byte "room", 0
|
|
target3: .byte "building", 0
|
|
target4: .byte "neighborhood", 0
|
|
target5: .byte "city", 0
|
|
target6: .byte "nation", 0
|
|
target7: .byte "world", 0
|
|
target8: .byte "Solar System", 0
|
|
target9: .byte "Galaxy", 0
|
|
target10: .byte "Universe", 0
|
|
|
|
; DELAY routine. Executes 2,560*(A) NOP statements.
|
|
delay: tax
|
|
ldy #00
|
|
* nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
iny
|
|
bne -
|
|
dex
|
|
bne -
|
|
rts
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor4c-src">
|
|
<title id="tutor4c-fname"><filename>hello4c.oph</filename></title>
|
|
<programlisting>
|
|
.include "c64-1.oph"
|
|
.outfile "hello.prg"
|
|
|
|
.macro print
|
|
ldx #0
|
|
_loop: lda _1, x
|
|
beq _done
|
|
jsr chrout
|
|
inx
|
|
bne _loop
|
|
_done:
|
|
.macend
|
|
|
|
.macro greet
|
|
lda #30
|
|
jsr delay
|
|
`print hello1
|
|
`print _1
|
|
`print hello2
|
|
.macend
|
|
|
|
lda #147
|
|
jsr chrout
|
|
lda #lower'case
|
|
jsr chrout
|
|
`greet target1
|
|
`greet target2
|
|
`greet target3
|
|
`greet target4
|
|
`greet target5
|
|
`greet target6
|
|
`greet target7
|
|
`greet target8
|
|
`greet target9
|
|
`greet target10
|
|
rts
|
|
|
|
.charmap 'A, "abcdefghijklmnopqrstuvwxyz"
|
|
.charmap 'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
hello1: .byte "Hello, ",0
|
|
hello2: .byte "!", 13, 0
|
|
|
|
target1: .byte "programmer", 0
|
|
target2: .byte "room", 0
|
|
target3: .byte "building", 0
|
|
target4: .byte "neighborhood", 0
|
|
target5: .byte "city", 0
|
|
target6: .byte "nation", 0
|
|
target7: .byte "world", 0
|
|
target8: .byte "Solar System", 0
|
|
target9: .byte "Galaxy", 0
|
|
target10: .byte "Universe", 0
|
|
|
|
; DELAY routine. Executes 2,560*(A) NOP statements.
|
|
delay: tax
|
|
ldy #00
|
|
* nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
iny
|
|
bne -
|
|
dex
|
|
bne -
|
|
rts
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor5-src">
|
|
<title id="tutor5-fname"><filename>hello5.oph</filename></title>
|
|
<programlisting>
|
|
.include "c64-1.oph"
|
|
.outfile "hello.prg"
|
|
|
|
.data
|
|
.org $C000
|
|
.text
|
|
|
|
.macro print
|
|
ldx #0
|
|
_loop: lda _1, x
|
|
beq _done
|
|
jsr chrout
|
|
inx
|
|
bne _loop
|
|
_done:
|
|
.macend
|
|
|
|
.macro greet
|
|
lda #30
|
|
jsr delay
|
|
`print hello1
|
|
`print _1
|
|
`print hello2
|
|
.macend
|
|
|
|
lda #147
|
|
jsr chrout
|
|
`greet target1
|
|
`greet target2
|
|
`greet target3
|
|
`greet target4
|
|
`greet target5
|
|
`greet target6
|
|
`greet target7
|
|
`greet target8
|
|
`greet target9
|
|
`greet target10
|
|
rts
|
|
|
|
hello1: .byte "HELLO, ",0
|
|
hello2: .byte "!", 13, 0
|
|
|
|
target1: .byte "PROGRAMMER", 0
|
|
target2: .byte "ROOM", 0
|
|
target3: .byte "BUILDING", 0
|
|
target4: .byte "NEIGHBORHOOD", 0
|
|
target5: .byte "CITY", 0
|
|
target6: .byte "NATION", 0
|
|
target7: .byte "WORLD", 0
|
|
target8: .byte "SOLAR SYSTEM", 0
|
|
target9: .byte "GALAXY", 0
|
|
target10: .byte "UNIVERSE", 0
|
|
|
|
; DELAY routine. Takes values from the Accumulator and pauses
|
|
; for that many jiffies (1/60th of a second).
|
|
.scope
|
|
.data
|
|
.space _tmp 1
|
|
.space _target 1
|
|
|
|
.text
|
|
|
|
delay: sta _tmp ; save argument (rdtim destroys it)
|
|
jsr rdtim
|
|
clc
|
|
adc _tmp ; add current time to get target
|
|
sta _target
|
|
* jsr rdtim
|
|
cmp _target
|
|
bmi - ; Buzz until target reached
|
|
rts
|
|
.scend
|
|
|
|
.checkpc $A000
|
|
.data
|
|
.checkpc $D000
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor6-src">
|
|
<title id="tutor6-fname"><filename>hello6.oph</filename></title>
|
|
<programlisting>
|
|
.include "c64-1.oph"
|
|
.outfile "hello.prg"
|
|
|
|
.data
|
|
.org $C000
|
|
.space cache 2
|
|
.text
|
|
|
|
.macro print
|
|
lda #<_1
|
|
ldx #>_1
|
|
jsr printstr
|
|
.macend
|
|
|
|
.macro greet
|
|
lda #30
|
|
jsr delay
|
|
`print hello1
|
|
`print _1
|
|
`print hello2
|
|
.macend
|
|
|
|
; Save the zero page locations that PRINTSTR uses.
|
|
lda $10
|
|
sta cache
|
|
lda $11
|
|
sta cache+1
|
|
|
|
lda #147
|
|
jsr chrout
|
|
`greet target1
|
|
`greet target2
|
|
`greet target3
|
|
`greet target4
|
|
`greet target5
|
|
`greet target6
|
|
`greet target7
|
|
`greet target8
|
|
`greet target9
|
|
`greet target10
|
|
|
|
; Restore the zero page values printstr uses.
|
|
lda cache
|
|
sta $10
|
|
lda cache+1
|
|
sta $11
|
|
|
|
rts
|
|
|
|
hello1: .byte "HELLO, ",0
|
|
hello2: .byte "!", 13, 0
|
|
|
|
target1: .byte "PROGRAMMER", 0
|
|
target2: .byte "ROOM", 0
|
|
target3: .byte "BUILDING", 0
|
|
target4: .byte "NEIGHBORHOOD", 0
|
|
target5: .byte "CITY", 0
|
|
target6: .byte "NATION", 0
|
|
target7: .byte "WORLD", 0
|
|
target8: .byte "SOLAR SYSTEM", 0
|
|
target9: .byte "GALAXY", 0
|
|
target10: .byte "UNIVERSE", 0
|
|
|
|
; DELAY routine. Takes values from the Accumulator and pauses
|
|
; for that many jiffies (1/60th of a second).
|
|
.scope
|
|
.data
|
|
.space _tmp 1
|
|
.space _target 1
|
|
|
|
.text
|
|
|
|
delay: sta _tmp ; save argument (rdtim destroys it)
|
|
jsr rdtim
|
|
clc
|
|
adc _tmp ; add current time to get target
|
|
sta _target
|
|
* jsr rdtim
|
|
cmp _target
|
|
bmi - ; Buzz until target reached
|
|
rts
|
|
.scend
|
|
|
|
; PRINTSTR routine. Accumulator stores the low byte of the address,
|
|
; X register stores the high byte. Destroys the values of $10 and
|
|
; $11.
|
|
|
|
.scope
|
|
printstr:
|
|
sta $10
|
|
stx $11
|
|
ldy #$00
|
|
_lp: lda ($10),y
|
|
beq _done
|
|
jsr chrout
|
|
iny
|
|
bne _lp
|
|
_done: rts
|
|
.scend
|
|
|
|
.checkpc $A000
|
|
.data
|
|
.checkpc $D000
|
|
</programlisting>
|
|
</section>
|
|
<section id="c64-2-src">
|
|
<title id="c64-2-fname"><filename>c64_0.oph</filename></title>
|
|
<programlisting>
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; Commodore 64 Basic Runtime File
|
|
;;
|
|
;; Include this at the TOP of your C64 program, and it will handle
|
|
;; hiding away the BASIC ROM and data and restoring it at the end.
|
|
;;
|
|
;; You will have a contiguous block of RAM from $0800 to $CF81, and
|
|
;; Zero Page access from $02 to $7F in the segment "zp".
|
|
|
|
.word $0801
|
|
.org $0801
|
|
|
|
; BASIC program that just calls our machine language code
|
|
.scope
|
|
.word _next, 10 ; Next line and current line number
|
|
.byte $9e," 2062",0 ; SYS 2062
|
|
_next: .word 0 ; End of program
|
|
.scend
|
|
|
|
.data zp ; Zero Page memory segment.
|
|
.org $0002
|
|
|
|
.text
|
|
|
|
.scope
|
|
; Cache BASIC zero page at top of available RAM
|
|
ldx #$7E
|
|
* lda $01, x
|
|
sta $CF81, x
|
|
dex
|
|
bne -
|
|
|
|
; Swap out the BASIC ROM for RAM
|
|
lda $01
|
|
and #$fe
|
|
ora #$06
|
|
sta $01
|
|
|
|
; Run the real program
|
|
jsr _main
|
|
|
|
; Restore BASIC ROM
|
|
lda $01
|
|
ora #$07
|
|
sta $01
|
|
|
|
; Restore BASIC zero page
|
|
ldx #$7E
|
|
* lda $CF81, x
|
|
sta $01, x
|
|
dex
|
|
bne -
|
|
|
|
; Back to BASIC
|
|
rts
|
|
|
|
_main:
|
|
; Program follows...
|
|
.scend
|
|
</programlisting>
|
|
</section>
|
|
<section id="tutor7-src">
|
|
<title id="tutor7-fname"><filename>hello7.oph</filename></title>
|
|
<programlisting>
|
|
.include "../platform/c64_0.oph"
|
|
.require "../platform/c64kernal.oph"
|
|
.outfile "hello.prg"
|
|
|
|
.data
|
|
.org $C000
|
|
.text
|
|
|
|
.macro print
|
|
lda #<_1
|
|
ldx #>_1
|
|
jsr printstr
|
|
.macend
|
|
|
|
.macro greet
|
|
lda #30
|
|
jsr delay
|
|
`print hello1
|
|
`print _1
|
|
`print hello2
|
|
.macend
|
|
|
|
lda #147
|
|
jsr chrout
|
|
`greet target1
|
|
`greet target2
|
|
`greet target3
|
|
`greet target4
|
|
`greet target5
|
|
`greet target6
|
|
`greet target7
|
|
`greet target8
|
|
`greet target9
|
|
`greet target10
|
|
|
|
rts
|
|
|
|
hello1: .byte "HELLO, ",0
|
|
hello2: .byte "!", 13, 0
|
|
|
|
target1: .byte "PROGRAMMER", 0
|
|
target2: .byte "ROOM", 0
|
|
target3: .byte "BUILDING", 0
|
|
target4: .byte "NEIGHBORHOOD", 0
|
|
target5: .byte "CITY", 0
|
|
target6: .byte "NATION", 0
|
|
target7: .byte "WORLD", 0
|
|
target8: .byte "SOLAR SYSTEM", 0
|
|
target9: .byte "GALAXY", 0
|
|
target10: .byte "UNIVERSE", 0
|
|
|
|
; DELAY routine. Takes values from the Accumulator and pauses
|
|
; for that many jiffies (1/60th of a second).
|
|
.scope
|
|
.data
|
|
.space _tmp 1
|
|
.space _target 1
|
|
|
|
.text
|
|
|
|
delay: sta _tmp ; save argument (rdtim destroys it)
|
|
jsr rdtim
|
|
clc
|
|
adc _tmp ; add current time to get target
|
|
sta _target
|
|
* jsr rdtim
|
|
cmp _target
|
|
bmi - ; Buzz until target reached
|
|
rts
|
|
.scend
|
|
|
|
; PRINTSTR routine. Accumulator stores the low byte of the address,
|
|
; X register stores the high byte. Destroys the values of $10 and
|
|
; $11.
|
|
|
|
.scope
|
|
.data zp
|
|
.space _ptr 2
|
|
.text
|
|
printstr:
|
|
sta _ptr
|
|
stx _ptr+1
|
|
ldy #$00
|
|
_lp: lda (_ptr),y
|
|
beq _done
|
|
jsr chrout
|
|
iny
|
|
bne _lp
|
|
_done: rts
|
|
.scend
|
|
|
|
.checkpc $A000
|
|
|
|
.data
|
|
.checkpc $D000
|
|
|
|
.data zp
|
|
.checkpc $80
|
|
</programlisting>
|
|
</section>
|
|
<section id="structure-src">
|
|
<title id="structure-fname"><filename>structuredemo.oph</filename></title>
|
|
<programlisting>
|
|
.include "../platform/c64_0.oph"
|
|
.require "../platform/c64kernal.oph"
|
|
.outfile "structuredemo.prg"
|
|
|
|
jsr print'unsorted
|
|
jsr insertion'sort
|
|
jsr print'list
|
|
rts
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Linked list data: head, next, lb, hb.
|
|
; lb/hb: Low/high bytes of the data array. These are immutable and
|
|
; kept with the program text.
|
|
; head: Array index of the first element in the list, or #$FF if the
|
|
; list is empty
|
|
; next: Array of successor indices. If you've just read element X,
|
|
; the value of memory location next+X is the index of the
|
|
; next element. If next is #$FF, you've reached the end of
|
|
; the list.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
.data
|
|
.org $C000
|
|
.space head 1
|
|
.space next 16
|
|
|
|
.text
|
|
lb: .byte <$838,<$618,<$205,<$984,<$724,<$301,<$249,<$946
|
|
.byte <$925,<$043,<$114,<$697,<$985,<$633,<$312,<$086
|
|
hb: .byte >$838,>$618,>$205,>$984,>$724,>$301,>$249,>$946
|
|
.byte >$925,>$043,>$114,>$697,>$985,>$633,>$312,>$086
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; insertion'sort: Sorts the list defined by head, next, hb, lb.
|
|
; Arguments: None.
|
|
; Modifies: All registers destroyed, head and next array sorted.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
insertion'sort:
|
|
lda #$FF ; Clear list by storing the terminator in 'head'
|
|
sta head
|
|
ldx #$0 ; Loop through the lb/hb array, adding each
|
|
insertion'sort'loop: ; element one at a time
|
|
txa
|
|
pha
|
|
jsr insert_elt
|
|
pla
|
|
tax
|
|
inx
|
|
cpx #$10
|
|
bne insertion'sort'loop
|
|
rts
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; insert_elt: Insert an element into the linked list. Maintains the
|
|
; list in sorted, ascending order. Used by
|
|
; insertion'sort.
|
|
; Arguments: X register holds the index of the element to add.
|
|
; Modifies: All registers destroyed; head and next arrays updated
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
.data
|
|
.space lbtoinsert 1
|
|
.space hbtoinsert 1
|
|
.space indextoinsert 1
|
|
|
|
.text
|
|
|
|
insert_elt:
|
|
ldy head ; If the list is empty, make
|
|
cpy #$FF ; head point at it, and return.
|
|
bne insert_elt'list'not'empty
|
|
stx head
|
|
tya
|
|
sta next,x
|
|
rts
|
|
insert_elt'list'not'empty:
|
|
lda lb,x ; Cache the data we're inserting
|
|
sta lbtoinsert
|
|
lda hb,x
|
|
sta hbtoinsert
|
|
stx indextoinsert
|
|
ldy head ; Compare the first value with
|
|
sec ; the data. If the data must
|
|
lda lb,y ; be inserted at the front...
|
|
sbc lbtoinsert
|
|
lda hb,y
|
|
sbc hbtoinsert
|
|
bmi insert_elt'not'smallest
|
|
tya ; Set its next pointer to the
|
|
sta next,x ; old head, update the head
|
|
stx head ; pointer, and return.
|
|
rts
|
|
insert_elt'not'smallest:
|
|
ldx head
|
|
insert_elt'loop: ; At this point, we know that
|
|
lda next,x ; argument > data[X].
|
|
tay
|
|
cpy #$FF ; if next[X] = #$FF, insert arg at end.
|
|
beq insert_elt'insert'after'current
|
|
lda lb,y ; Otherwise, compare arg to
|
|
sec ; data[next[X]]. If we insert
|
|
sbc lbtoinsert ; before that...
|
|
lda hb,y
|
|
sbc hbtoinsert
|
|
bmi insert_elt'goto'next
|
|
insert_elt'insert'after'current: ; Fix up all the next links
|
|
tya
|
|
ldy indextoinsert
|
|
sta next,y
|
|
tya
|
|
sta next,x
|
|
rts ; and return.
|
|
insert_elt'goto'next: ; Otherwise, let X = next[X]
|
|
tya ; and go looping again.
|
|
tax
|
|
jmp insert_elt'loop
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; print'unsorted: Steps through the data array and prints each value.
|
|
; Standalone procedure.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
print'unsorted:
|
|
lda #<unsorted'hdr
|
|
ldx #>unsorted'hdr
|
|
jsr put'string
|
|
ldy #$00
|
|
print'unsorted'loop:
|
|
lda hb, Y
|
|
jsr print'hex
|
|
lda lb, y
|
|
jsr print'hex
|
|
lda #$20
|
|
jsr chrout
|
|
iny
|
|
cpy #$10
|
|
bne print'unsorted'loop
|
|
lda #$0D
|
|
jsr chrout
|
|
rts
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; print'list: Starts at head, and prints out every value in the
|
|
; linked list.
|
|
; Standalone procedure.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
print'list:
|
|
lda #<sorted'hdr
|
|
ldx #>sorted'hdr
|
|
jsr put'string
|
|
ldy head
|
|
print'list'loop:
|
|
cpy #$FF
|
|
beq print'list'done
|
|
lda hb, y
|
|
jsr print'hex
|
|
lda lb, y
|
|
jsr print'hex
|
|
lda #$20
|
|
jsr chrout
|
|
lda next, Y
|
|
tay
|
|
jmp print'list'loop
|
|
print'list'done:
|
|
lda #$0d
|
|
jsr chrout
|
|
rts
|
|
|
|
;; String data for the above routines.
|
|
|
|
unsorted'hdr:
|
|
.byte 147 ; Clear screen first!
|
|
.byte "UNSORTED DATA:",13,0
|
|
|
|
sorted'hdr:
|
|
.byte "SORTED DATA:",13,0
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; print'hex: outputs a two-character hex representation of a one-
|
|
; byte value.
|
|
; Arguments: Byte to print in accumulator
|
|
; Modifies: .A and .X
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
print'hex:
|
|
pha
|
|
clc
|
|
lsr
|
|
lsr
|
|
lsr
|
|
lsr
|
|
tax
|
|
lda hexstr,x
|
|
jsr chrout
|
|
pla
|
|
and #$0F
|
|
tax
|
|
lda hexstr,X
|
|
jsr chrout
|
|
rts
|
|
|
|
; Character data array for print'hex.
|
|
hexstr: .byte "0123456789ABCDEF"
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; put'string: outputs a C-style null terminated string with length
|
|
; less than 256 to the screen. If 256 bytes are written
|
|
; without finding a terminator, the routine ends quietly.
|
|
; Arguments: Low byte of string address in .A, high byte in .X
|
|
; Modifies: .A and .Y
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
.data zp
|
|
.space put'string'addr 2
|
|
|
|
.text
|
|
put'string:
|
|
sta put'string'addr
|
|
stx put'string'addr+1
|
|
ldy #$00
|
|
put'string'loop:
|
|
lda (put'string'addr),y
|
|
beq put'string'done
|
|
jsr chrout
|
|
iny
|
|
bne put'string'loop
|
|
put'string'done:
|
|
rts
|
|
|
|
</programlisting>
|
|
</section>
|
|
<section id="fib-src">
|
|
<title id="fib-fname"><filename>fibonacci.oph</filename></title>
|
|
<programlisting>
|
|
.include "../platform/c64_0.oph"
|
|
.require "../platform/c64kernal.oph"
|
|
.outfile "fibonacci.prg"
|
|
|
|
lda #<opening ; Print opening text
|
|
sta fun'args
|
|
lda #>opening
|
|
sta fun'args+1
|
|
jsr print'string
|
|
|
|
lda #$00
|
|
sta fun'vars ; Count num from 0 to 19
|
|
* lda fun'vars ; Main loop: print num, with leading space if <10
|
|
cmp #$09
|
|
bcs +
|
|
lda #$20
|
|
jsr chrout
|
|
lda fun'vars
|
|
* sta fun'args ; Copy num to args, print it, plus ": "
|
|
inc fun'args
|
|
lda #$00
|
|
sta fun'args+1
|
|
jsr print'dec
|
|
lda #$3A
|
|
jsr chrout
|
|
lda #$20
|
|
jsr chrout
|
|
lda fun'vars ; Copy num to args, call fib, print result
|
|
sta fun'args
|
|
jsr fib
|
|
jsr print'dec
|
|
lda #$0D ; Newline
|
|
jsr chrout
|
|
inc fun'vars ; Increment num; if it's 20, we're done.
|
|
lda fun'vars
|
|
cmp #20
|
|
bne -- ; Otherwise, loop.
|
|
rts
|
|
|
|
opening:
|
|
.byte 147, " FIBONACCI SEQUENCE",13,13,0
|
|
|
|
.scope
|
|
; Uint16 fib (Uint8 x): compute Xth fibonnaci number.
|
|
; fib(0) = fib(1) = 1.
|
|
; Stack usage: 3.
|
|
|
|
fib: lda #$03
|
|
jsr save'stack
|
|
|
|
lda fun'vars ; If x < 2, goto _base.
|
|
cmp #$02
|
|
bcc _base
|
|
|
|
dec fun'args ; Otherwise, call fib(x-1)...
|
|
jsr fib
|
|
lda fun'args ; Copy the result to local variable...
|
|
sta fun'vars+1
|
|
lda fun'args+1
|
|
sta fun'vars+2
|
|
lda fun'vars ; Call fib(x-2)...
|
|
sec
|
|
sbc #$02
|
|
sta fun'args
|
|
jsr fib
|
|
clc ; And add the old result to it, leaving it
|
|
lda fun'args ; in the 'result' location.
|
|
adc fun'vars+1
|
|
sta fun'args
|
|
lda fun'args+1
|
|
adc fun'vars+2
|
|
sta fun'args+1
|
|
jmp _done ; and then we're done.
|
|
|
|
_base: ldy #$01 ; In the base case, just copy 1 to the
|
|
sty fun'args ; result.
|
|
dey
|
|
sty fun'args+1
|
|
|
|
_done: lda #$03
|
|
jsr restore'stack
|
|
rts
|
|
.scend
|
|
|
|
.scope
|
|
; Stack routines: init'stack, save'stack, restore'stack
|
|
.data zp
|
|
.space _sp $02
|
|
.space _counter $01
|
|
.space fun'args $10
|
|
.space fun'vars $40
|
|
|
|
.text
|
|
init'stack:
|
|
lda #$00
|
|
sta _sp
|
|
lda #$A0
|
|
sta _sp+1
|
|
rts
|
|
|
|
save'stack:
|
|
sta _counter
|
|
sec
|
|
lda _sp
|
|
sbc _counter
|
|
sta _sp
|
|
lda _sp+1
|
|
sbc #$00
|
|
sta _sp+1
|
|
ldy #$00
|
|
* lda fun'vars, y
|
|
sta (_sp), y
|
|
lda fun'args, y
|
|
sta fun'vars, y
|
|
iny
|
|
dec _counter
|
|
bne -
|
|
rts
|
|
|
|
restore'stack:
|
|
pha
|
|
sta _counter
|
|
ldy #$00
|
|
* lda (_sp), y
|
|
sta fun'vars, y
|
|
iny
|
|
dec _counter
|
|
bne -
|
|
pla
|
|
clc
|
|
adc _sp
|
|
sta _sp
|
|
lda _sp+1
|
|
adc #$00
|
|
sta _sp+1
|
|
rts
|
|
.scend
|
|
|
|
|
|
; Utility functions. print'dec prints an unsigned 16-bit integer.
|
|
; It's ugly and long, mainly because we don't bother with niceties
|
|
; like "division". print'string prints a zero-terminated string.
|
|
|
|
.scope
|
|
.data
|
|
.org fun'args
|
|
.space _val 2
|
|
.space _step 2
|
|
.space _res 1
|
|
.space _allowzero 1
|
|
.text
|
|
print'dec:
|
|
lda #$00
|
|
sta _allowzero
|
|
lda #<10000
|
|
sta _step
|
|
lda #>10000
|
|
sta _step+1
|
|
jsr repsub'16
|
|
lda #<1000
|
|
sta _step
|
|
lda #>1000
|
|
sta _step+1
|
|
jsr repsub'16
|
|
lda #0
|
|
sta _step+1
|
|
lda #100
|
|
sta _step
|
|
jsr repsub'16
|
|
lda #10
|
|
sta _step
|
|
jsr repsub'16
|
|
lda _val
|
|
jsr _print
|
|
rts
|
|
|
|
repsub'16:
|
|
lda #$00
|
|
sta _res
|
|
* lda _val
|
|
sec
|
|
sbc _step
|
|
lda _val+1
|
|
sbc _step+1
|
|
bcc _done
|
|
lda _val
|
|
sec
|
|
sbc _step
|
|
sta _val
|
|
lda _val+1
|
|
sbc _step+1
|
|
sta _val+1
|
|
inc _res
|
|
jmp -
|
|
_done: lda _res
|
|
ora _allowzero
|
|
beq _ret
|
|
sta _allowzero
|
|
lda _res
|
|
_print: clc
|
|
adc #'0
|
|
jsr chrout
|
|
_ret: rts
|
|
.scend
|
|
|
|
print'string:
|
|
ldy #$00
|
|
* lda (fun'args), y
|
|
beq +
|
|
jsr chrout
|
|
iny
|
|
jmp -
|
|
* rts
|
|
</programlisting>
|
|
</section>
|
|
</appendix>
|