天天看点

利用 Ophis 编写 Commodore 64 programs PRG 程序(四)表达式零页1附录前往下一节

在上一节中,我们理解了字符映射与

.data

段的用法。接下来我们将看看另外两个好用的功能:表达式和零页

本节参考了Expressions、Advanced Memory Segments

表达式

Ophis允许书写表达式,这些表达式将在汇编之前完成计算。全部可用运算符如下

运算符 含义
[ ] 相当于( ),因为圆括号另有涵义
< > 取数的低8位/高8位
* / 乘除
+ - 加减
& | ^ 按位与、或、非

在介绍完零页之后,将在附录介绍一个表达式的用例。我们将用表达式实现打印内存中任意位置的字符串的子程序。

零页1

顾名思义,"零页"是指内存的第

页,即

0x0000

-

0x00FF

。位于零页的内存不仅访问速度更快(和寄存器速度相同),而且只有该部分内存能够使用间接偏移寻址。这种寻址方式对于编程而言十分重要,我们将在附录的用例中进行详细介绍。

然而,正因为零页十分重要,

KERNAL

BASIC

都使用了零页上的大量空间,留给我们的空间变得十分稀少(只有5个,位于02、fb、fc、fd、fe)。但是在机器代码运行时并不需要

BASIC

,因此我们可以另辟蹊径,在程序运行开始时将零页的

BASIC

数据区(到

8f

为止)搬移到内存的其他区域,在程序结束后进行恢复,从而得到该部分的零页空间的使用权。

不过,因为

00

01

位置被映射到寄存器用于进行内存控制,所以实际可用范围为

02

-

8f

Ophis采用如下语句标识零页位置

.data zp
.org $0002
           

然后即可进行零页搬移

./platform/c64_0.cph

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 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 $CFFF, and
;; Zero Page access from $02 to $8F in the segment "zp".

.include "c64header.oph"

.data zp ; Zero Page memory segment.
.org $0002

.text

.scope
	; Cache BASIC zero page underneath the KERNAL, while also
	; making RAM copies of the NMI routines
	ldx	#$00
*	lda	$00, x
	sta	$e000, x
	inx
	bne	-

	; Swap out the BASIC ROM for RAM
	lda	$01
	and	#$fe
	ora	#$06
	sta	$01

	; Run the real program
	jsr	_main

	; Swap out KERNAL to expose cached BASIC ZP values
	; Block IRQs during this period. NMIs cannot be blocked,
	; but we copied enough of the processing code into the
	; RAM under the KERNAL that we can disable NMI processing
	; during this period
	sei				; Disable IRQs
	lda	#$c1		; Defang NMIs
	sta	$318

	lda	$01			; Swap out KERNAL
	and	#$fd
	sta	$01

	; Restore BASIC zero page
	ldx	#$8E
*	lda	$e001, x
	sta	$01, x
	dex
	bne	-

	; Restore BASIC ROM, KERNAL, and interrupts
	lda	$01
	ora	#$07
	sta	$01
	lda	#$47	; Restore NMI vector
	sta	$318
	cli			; Re-enable interrupts

	; Back to BASIC. We do this by clearing the keyboard
	; buffer and then jumping through the warm start
	; vector. This will more cleanly handle case where
	; the program has somehow modified BASIC's state,
	; such as running through PUCRUNCH or a onefiler.
	stx	$c6		; .X is zero from previous loop
	jmp	($a002)

_main:
	; Program follows...
.scend
           

./platform/c64header.oph

.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,"2061",0	; SYS 2061
_next:	.word 0			; End of program
.scend
	; Program follows...
           

在程序的最后,还需要如下语句检查是否越界

.data zp
.checkpc $80
           

附录

接下来我们将实现打印内存中任意位置的字符串的子程序。

由于内存地址为

16

位,因此需要使用两个

8

位的连续零页内存空间,按小端序存放该地址。

; PRINTSTR routine.  Accumulator stores the low byte of the address,
; X register stores the high byte.

.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
           

接下来,因为涉及到超过8位的参数,我们使用宏来调用该子程序。

.macro print
        lda #<_1	; 取参数的低八位
        ldx #>_1	; 取参数的高八位
        jsr printstr
.macend
           

前往下一节

  1. Zeropage - C64-Wiki ↩︎

继续阅读