This commit is contained in:
Irmen de Jong 2017-12-31 03:19:06 +01:00
parent a5283bfc7b
commit 2f6ef28c80
15 changed files with 284 additions and 218 deletions

View File

@ -1514,7 +1514,7 @@ class CodeGenerator:
# assigning a register to a float requires c64 ROM routines # assigning a register to a float requires c64 ROM routines
if r_register in REGISTER_WORDS: if r_register in REGISTER_WORDS:
def do_rom_calls(): def do_rom_calls():
self.p("\t\tjsr c64util.GIVUAYF") # uword AY -> fac1 self.p("\t\tjsr c64.GIVUAYF") # uword AY -> fac1
self.p("\t\tldx #<" + lv_string) self.p("\t\tldx #<" + lv_string)
self.p("\t\tldy #>" + lv_string) self.p("\t\tldy #>" + lv_string)
self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY
@ -1750,7 +1750,7 @@ class CodeGenerator:
self.p("\t\tsta c64.SCRATCH_ZPWORD1+1") self.p("\t\tsta c64.SCRATCH_ZPWORD1+1")
self.p("\t\tldx #<" + l_str) self.p("\t\tldx #<" + l_str)
self.p("\t\tldy #>" + l_str) self.p("\t\tldy #>" + l_str)
self.p("\t\tjsr c64_lib.copy_mflt") self.p("\t\tjsr il65_lib.copy_mflt")
elif rvalue.datatype == DataType.BYTE: elif rvalue.datatype == DataType.BYTE:
with self.preserving_registers({'A', 'X', 'Y'}): with self.preserving_registers({'A', 'X', 'Y'}):
self.p("\t\tldy " + r_str) self.p("\t\tldy " + r_str)
@ -1762,7 +1762,7 @@ class CodeGenerator:
with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True): with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True):
self.p("\t\tlda " + r_str) self.p("\t\tlda " + r_str)
self.p("\t\tldy {:s}+1".format(r_str)) self.p("\t\tldy {:s}+1".format(r_str))
self.p("\t\tjsr c64util.GIVUAYF") # uword AY -> fac1 self.p("\t\tjsr c64.GIVUAYF") # uword AY -> fac1
self.p("\t\tldx #<" + l_str) self.p("\t\tldx #<" + l_str)
self.p("\t\tldy #>" + l_str) self.p("\t\tldy #>" + l_str)
self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY

View File

@ -1308,7 +1308,7 @@ class Parser:
raise self.PError("all call arguments should have a name or be matched on a named parameter") raise self.PError("all call arguments should have a name or be matched on a named parameter")
if isinstance(target, (type(None), ParseResult.Value)): if isinstance(target, (type(None), ParseResult.Value)):
# special case for the C-64 lib's print function, to be able to use it with a single character argument # special case for the C-64 lib's print function, to be able to use it with a single character argument
if target.name == "c64util.print_string" and len(arguments) == 1 and isinstance(arguments[0], str): if target.name == "c64scr.print_string" and len(arguments) == 1 and isinstance(arguments[0], str):
if arguments[0][1].startswith("'") and arguments[0][1].endswith("'"): if arguments[0][1].startswith("'") and arguments[0][1].endswith("'"):
target = self.parse_expression("c64.CHROUT") target = self.parse_expression("c64.CHROUT")
address = target.address address = target.address
@ -1797,7 +1797,7 @@ class Optimizer:
def remove_unused_subroutines(self, block: ParseResult.Block) -> None: def remove_unused_subroutines(self, block: ParseResult.Block) -> None:
# some symbols are used by the emitted assembly code from the code generator, # some symbols are used by the emitted assembly code from the code generator,
# and should never be removed or the assembler will fail # and should never be removed or the assembler will fail
never_remove = {"c64util.GIVUAYF", "c64.FREADUY", "c64.FTOMEMXY"} never_remove = {"c64.GIVUAYF", "c64.FREADUY", "c64.FTOMEMXY"}
discarded = [] discarded = []
for sub in list(block.symbols.iter_subroutines()): for sub in list(block.symbols.iter_subroutines()):
usages = self.parsed.subroutine_usage[(sub.blockname, sub.name)] usages = self.parsed.subroutine_usage[(sub.blockname, sub.name)]

View File

@ -221,6 +221,8 @@ class Zeropage:
raise SymbolError("cannot configure the ZP multiple times") raise SymbolError("cannot configure the ZP multiple times")
if clobber_zp: if clobber_zp:
self.free = list(range(0x04, 0xfb)) + [0xff] self.free = list(range(0x04, 0xfb)) + [0xff]
for updated_by_irq in [0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6]:
self.free.remove(updated_by_irq)
else: else:
# these are valid for the C-64 (when no RS232 I/O is performed): # these are valid for the C-64 (when no RS232 I/O is performed):
# ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines) # ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines)

View File

@ -7,14 +7,19 @@
; indent format: TABS, size=8 ; indent format: TABS, size=8
output raw
~ c64 { ~ c64 {
memory .byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP memory .byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
memory .byte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP memory .byte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
memory .word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc) memory .word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
memory .word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe) memory .word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
memory .byte TIME_HI = $a0 ; software jiffy clock, hi byte
memory .byte TIME_MID = $a1 ; .. mid byte
memory .byte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
memory .byte STKEY = $91 ; various keyboard statuses (updated by IRQ)
memory .byte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
memory .byte COLOR = $0286 ; cursor color memory .byte COLOR = $0286 ; cursor color
memory .word CINV = $0314 ; IRQ vector memory .word CINV = $0314 ; IRQ vector
@ -107,17 +112,17 @@ sub MOVAF () -> (A?, X?) = $bc0c ; copy fac1 to fac2 (rounded)
sub MOVEF () -> (A?, X?) = $bc0f ; copy fac1 to fac2 sub MOVEF () -> (A?, X?) = $bc0f ; copy fac1 to fac2
sub FTOMEMXY (mflpt: XY) -> (A?, Y?) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt sub FTOMEMXY (mflpt: XY) -> (A?, Y?) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
sub FTOSWORDYA () -> (Y, A, X?) = $b1aa ; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY) sub FTOSWORDYA () -> (Y, A, X?) = $b1aa ; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
; use c64util.FTOSWRDAY to get A/Y output (lo/hi switched to normal order) ; use c64.FTOSWRDAY to get A/Y output (lo/hi switched to normal order)
sub GETADR () -> (Y, A, X?) = $b7f7 ; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) sub GETADR () -> (Y, A, X?) = $b7f7 ; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY)
; (result also in $14/15) use c64util.GETADRAY to get A/Y output (lo/hi switched to normal order) ; (result also in $14/15) use c64.GETADRAY to get A/Y output (lo/hi switched to normal order)
sub QINT () -> (?) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST. sub QINT () -> (?) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
sub AYINT () -> (?) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY) sub AYINT () -> (?) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
sub GIVAYF (lo: Y, hi: A) -> (?) = $b391 ; signed word in Y/A -> float in fac1 sub GIVAYF (lo: Y, hi: A) -> (?) = $b391 ; signed word in Y/A -> float in fac1
; use c64util.GIVAYFAY to use A/Y input (lo/hi switched to normal order) ; use c64.GIVAYFAY to use A/Y input (lo/hi switched to normal order)
; there is also c64util.GIVUAYF - unsigned word in A/Y (lo/hi) to fac1 ; there is also c64.GIVUAYF - unsigned word in A/Y (lo/hi) to fac1
; there is also c64util.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST ; there is also c64.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
; there is also c64util.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST ; there is also c64.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
; there is also c64util.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes) ; there is also c64.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
sub FREADUY (ubyte: Y) -> (?) = $b3a2 ; 8 bit unsigned Y -> float in fac1 sub FREADUY (ubyte: Y) -> (?) = $b3a2 ; 8 bit unsigned Y -> float in fac1
sub FREADSA (sbyte: A) -> (?) = $bc3c ; 8 bit signed A -> float in fac1 sub FREADSA (sbyte: A) -> (?) = $bc3c ; 8 bit signed A -> float in fac1
sub FREADSTR (len: A) -> (?) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length sub FREADSTR (len: A) -> (?) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
@ -217,10 +222,9 @@ sub IOBASE () -> (X, Y) = $FFF3 ; read base address of I/O devices
memory .word RESET_VEC = $FFFC ; reset vector, set by the kernal if banked in memory .word RESET_VEC = $FFFC ; reset vector, set by the kernal if banked in
memory .word IRQ_VEC = $FFFE ; interrupt vector, set by the kernal if banked in memory .word IRQ_VEC = $FFFE ; interrupt vector, set by the kernal if banked in
}
~ c64util { ; ----- utility functions ----
sub init_system () -> (?) { sub init_system () -> (?) {
; ---- initializes the machine to a sane starting state ; ---- initializes the machine to a sane starting state
@ -252,7 +256,6 @@ sub init_system () -> (?) {
} }
} }
sub FREADS32 () -> (?) { sub FREADS32 () -> (?) {
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST) ; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
asm { asm {
@ -335,70 +338,55 @@ sub GETADRAY () -> (AY, X?) {
} }
} }
sub print_string (address: XY) -> (A?, Y?) {
; ---- print null terminated string from X/Y
; note: the IL65 compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char,
; by just one call to c64.CHROUT of that single char.
asm {
stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZP2
ldy #0
- lda (c64.SCRATCH_ZP1),y
beq +
jsr c64.CHROUT
iny
bne -
+ rts
}
}
sub print_pstring (address: XY) -> (A?, X?, Y) { } ; ------ end of block c64
; ---- print pstring (length as first byte) from X/Y, returns str len in Y
asm {
stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZP2 ~ c64scr {
ldy #0 ; ---- this block contains (character) Screen and text I/O related functions ----
lda (c64.SCRATCH_ZP1),y
beq +
tax sub clear_screen (char:A, color: Y, screenaddr_hi: X) -> () {
- iny ; ---- clear the character screen with the given fill character and character color.
lda (c64.SCRATCH_ZP1),y ; X must be set to the high byte of the current screen display memory address (usually $04, for $0400).
jsr c64.CHROUT
dex
bne -
+ rts ; output string length is in Y
}
}
sub print_pimmediate () -> () {
; ---- print pstring in memory immediately following the subroutine fast call instruction
; note that the clobbered registers (A,X,Y) are not listed ON PURPOSE
asm { asm {
tsx ;ldx #$04 ; high byte of screen addres
lda $102,x ;lda #1 ; fill char
tay ; put high byte in y ;ldy #3 ; fill color
lda $101,x sta _loop +1 ; self-modify
tax ; and low byte in x. sty _mod_col +1 ; self-modify
stx _loop + 4 ; self-modify
inx inx
bne + stx _loop + 7 ; self-modify
iny inx
+ jsr print_pstring ; print string in XY, returns string length in y. stx _loop + 10 ; self-modify
tya stx _loop + 13 ; self-modify
tsx
clc
adc $101,x ; add content of 1st (length) byte to return addr. ldx #0
bcc + ; if that made the low byte roll over to 00, _loop lda #0
inc $102,x ; then increment the high byte too. sta $8400,x ; screen address will be modified
+ clc sta $8500,x
adc #1 ; now add 1 for the length byte itself. sta $8600,x
sta $101,x sta $86e8,x
bne + ; if that made it (the low byte) roll over to 00, _mod_col lda #0
inc $102,x ; increment the high byte of the return addr too. sta $d800,x ; color address is fixed
+ rts sta $d900,x
sta $da00,x
sta $dae8,x
inx
bne _loop
lda _loop+1 ; restore A and X
ldx _loop+4
rts
} }
} }
sub byte2decimal (ubyte: A) -> (Y, X, A) { sub byte2decimal (ubyte: A) -> (Y, X, A) {
; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A) ; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A)
asm { asm {
@ -437,10 +425,10 @@ hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as
} }
} }
; var .array(4) word2hex_output_array @todo support to use array/matrix type by address
var .text word2hex_output = "123" ; 0-terminated, 4 bytes total @todo remove once array works var .text word2hex_output = "1234" ; 0-terminated, to make printing easier
sub word2hex (word: XY) -> (?) { sub word2hex (word: XY) -> (?) {
; ---- convert 16 bit word in X/Y into hexadecimal string into memory 'word2hex_output' ; ---- convert 16 bit word in X/Y into 4-character hexadecimal string into memory 'word2hex_output'
asm { asm {
stx c64.SCRATCH_ZP2 stx c64.SCRATCH_ZP2
tya tya
@ -526,6 +514,77 @@ sub word2decimal (word: XY) -> (?) {
} }
} }
; @todo string to 32 bit unsigned integer http://www.6502.org/source/strings/ascii-to-32bit.html
sub print_string (address: XY) -> (A?, Y?) {
; ---- print null terminated string from X/Y
; note: the IL65 compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char,
; by just one call to c64.CHROUT of that single char.
asm {
stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZP2
ldy #0
- lda (c64.SCRATCH_ZP1),y
beq +
jsr c64.CHROUT
iny
bne -
+ rts
}
}
sub print_pstring (address: XY) -> (A?, X?, Y) {
; ---- print pstring (length as first byte) from X/Y, returns str len in Y
asm {
stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZP2
ldy #0
lda (c64.SCRATCH_ZP1),y
beq +
tax
- iny
lda (c64.SCRATCH_ZP1),y
jsr c64.CHROUT
dex
bne -
+ rts ; output string length is in Y
}
}
sub print_pimmediate () -> () {
; ---- print pstring in memory immediately following the subroutine fast call instruction
; note that the clobbered registers (A,X,Y) are not listed ON PURPOSE
asm {
tsx
lda $102,x
tay ; put high byte in y
lda $101,x
tax ; and low byte in x.
inx
bne +
iny
+ jsr print_pstring ; print string in XY, returns string length in y.
tya
tsx
clc
adc $101,x ; add content of 1st (length) byte to return addr.
bcc + ; if that made the low byte roll over to 00,
inc $102,x ; then increment the high byte too.
+ clc
adc #1 ; now add 1 for the length byte itself.
sta $101,x
bne + ; if that made it (the low byte) roll over to 00,
inc $102,x ; increment the high byte of the return addr too.
+ rts
}
}
sub print_byte_decimal0 (ubyte: A) -> (?) { sub print_byte_decimal0 (ubyte: A) -> (?) {
; ---- print the byte in A in decimal form, with left padding 0s (3 positions total) ; ---- print the byte in A in decimal form, with left padding 0s (3 positions total)
asm { asm {
@ -540,6 +599,7 @@ sub print_byte_decimal0 (ubyte: A) -> (?) {
} }
} }
sub print_byte_decimal (ubyte: A) -> (?) { sub print_byte_decimal (ubyte: A) -> (?) {
; ---- print the byte in A in decimal form, without left padding 0s ; ---- print the byte in A in decimal form, without left padding 0s
asm { asm {
@ -560,6 +620,7 @@ _print_tens txa
} }
} }
sub print_byte_hex (prefix: SC, ubyte: A) -> (?) { sub print_byte_hex (prefix: SC, ubyte: A) -> (?) {
; ---- print the byte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the byte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
asm { asm {
@ -576,6 +637,7 @@ sub print_byte_hex (prefix: SC, ubyte: A) -> (?) {
} }
} }
sub print_word_hex (prefix: SC, word: XY) -> (?) { sub print_word_hex (prefix: SC, word: XY) -> (?) {
; ---- print the (unsigned) word in X/Y in hexadecimal form (4 digits) ; ---- print the (unsigned) word in X/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
@ -641,9 +703,6 @@ _pr_decimal
} }
; @todo string to 32 bit unsigned integer http://www.6502.org/source/strings/ascii-to-32bit.html
sub input_chars (buffer: AX) -> (A?, Y) { sub input_chars (buffer: AX) -> (A?, Y) {
; ---- Input a string (max. 80 chars) from the keyboard. ; ---- Input a string (max. 80 chars) from the keyboard.
; It assumes the keyboard is selected as I/O channel!! ; It assumes the keyboard is selected as I/O channel!!
@ -665,6 +724,11 @@ sub input_chars (buffer: AX) -> (A?, Y) {
} }
} }
} ; ---- end block c64scr
;sub memcopy_basic () -> (?) { ;sub memcopy_basic () -> (?) {
; ; ---- copy a memory block by using a BASIC ROM routine @todo fix code ; ; ---- copy a memory block by using a BASIC ROM routine @todo fix code
; ; it calls a function from the basic interpreter, so: ; ; it calls a function from the basic interpreter, so:
@ -708,36 +772,3 @@ sub input_chars (buffer: AX) -> (A?, Y) {
; stx $59 ; stx $59
; jsr $a3bf ; jsr $a3bf
}
~ c64_lib {
asm {
; ---- copy a 5 byte MFLT floating point variable to another place
; input: X/Y = source address, SCRATCH_ZPWORD1 = destination address
copy_mflt stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZPWORD1+1
ldy #0
lda (c64.SCRATCH_ZP1),y
sta (c64.SCRATCH_ZPWORD1),y
iny
lda (c64.SCRATCH_ZP1),y
sta (c64.SCRATCH_ZPWORD1),y
iny
lda (c64.SCRATCH_ZP1),y
sta (c64.SCRATCH_ZPWORD1),y
iny
lda (c64.SCRATCH_ZP1),y
sta (c64.SCRATCH_ZPWORD1),y
iny
lda (c64.SCRATCH_ZP1),y
sta (c64.SCRATCH_ZPWORD1),y
ldy c64.SCRATCH_ZPWORD1+1
rts
}
}

View File

@ -5,7 +5,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
output raw
~ il65_lib_zp { ~ il65_lib_zp {
; note: separate block so the 64tass assembler can remove this when no zp restore is required ; note: separate block so the 64tass assembler can remove this when no zp restore is required
@ -17,9 +16,10 @@ save_zeropage
sei sei
ldx #2 ldx #2
- lda $00,x - lda $00,x
sta zp_backup-2,x sta zp_backup,x
inx inx
bne - bne -
cli
rts rts
restore_zeropage restore_zeropage
@ -28,6 +28,14 @@ restore_zeropage
txa txa
pha pha
sei sei
lda $a0 ; save the current jiffy clock
sta zp_backup+$a0
lda $a1
sta zp_backup+$a1
lda $a2
sta zp_backup+$a2
ldx #2 ldx #2
- lda zp_backup-2,x - lda zp_backup-2,x
sta $00,x sta $00,x
@ -40,7 +48,7 @@ restore_zeropage
plp plp
rts rts
zp_backup .fill 254, 0 zp_backup .fill 256, 0
} }
} }
@ -87,4 +95,30 @@ jsr_indirect_XY
jmp (SCRATCH_ZP1) jmp (SCRATCH_ZP1)
} }
asm {
; ---- copy a 5 byte MFLT floating point variable to another place
; input: X/Y = source address, SCRATCH_ZPWORD1 = destination address
copy_mflt stx SCRATCH_ZP1
sty SCRATCH_ZPWORD1+1
ldy #0
lda (SCRATCH_ZP1),y
sta (SCRATCH_ZPWORD1),y
iny
lda (SCRATCH_ZP1),y
sta (SCRATCH_ZPWORD1),y
iny
lda (SCRATCH_ZP1),y
sta (SCRATCH_ZPWORD1),y
iny
lda (SCRATCH_ZP1),y
sta (SCRATCH_ZPWORD1),y
iny
lda (SCRATCH_ZP1),y
sta (SCRATCH_ZPWORD1),y
ldy SCRATCH_ZPWORD1+1
rts
}
} }

View File

@ -1,7 +1,9 @@
; IL65 integer math library for 6502 ; IL65 integer math library for 6502
; (floating point math is done via the C-64's BASIC ROM routines) ; (floating point math is done via the C-64's BASIC ROM routines)
; ;
; some more interesting routines can be found here http://6502org.wikidot.com/software-math ; some more interesting routines can be found here:
; http://6502org.wikidot.com/software-math
; http://codebase64.org/doku.php?id=base:6502_6510_maths
; ;
; Written by Irmen de Jong (irmen@razorvine.net) ; Written by Irmen de Jong (irmen@razorvine.net)
; License: GNU GPL 3.0, see LICENSE ; License: GNU GPL 3.0, see LICENSE
@ -9,8 +11,6 @@
; indent format: TABS, size=8 ; indent format: TABS, size=8
output raw
~ math { ~ math {
; note: the following ZP scratch registers must be the same as in c64lib ; note: the following ZP scratch registers must be the same as in c64lib
memory .byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP memory .byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP

View File

@ -102,11 +102,22 @@ The four reserved locations mentioned above are subtracted from this set, leavin
five 1-byte and two 2-byte usable zero page registers. five 1-byte and two 2-byte usable zero page registers.
IL65 knows about all this: it will use the above zero page locations to place its ZP variables in, IL65 knows about all this: it will use the above zero page locations to place its ZP variables in,
until they're all used up. You can instruct it to treat your program as taking over the entire until they're all used up. You can instruct it to treat your program as taking over the entire
machine, in which case all of the zero page locations are suddenly available for variables. machine, in which case (almost) all of the zero page locations are suddenly available for variables.
IL65 can generate a special routine that saves and restores the zero page to let your program run IL65 can generate a special routine that saves and restores the zero page to let your program run
and return safely back to the system afterwards - you don't have to take care of that yourself. and return safely back to the system afterwards - you don't have to take care of that yourself.
@todo some global way (in ZP block) to promote certian other blocks/variables from that block or even **IRQ and the Zero page:**
The normal IRQ routine in the C-64's kernal will read and write several locations in the zero page:
``$a0 - $a2``; ``$91``; ``$c0``; ``$c5``; ``$cb``; ``$f5 - $f6``
These locations will not be used by the compiler for zero page variables, so your variables will
not interfere with the IRQ routine and vice versa. This is true for the normal zp mode but also
for the mode where the whole zp has been taken over.
@todo: some global way (in ZP block) to promote certian other blocks/variables from that block or even
subroutine to the zeropage. Don't do this in the block itself because it's a global optimization subroutine to the zeropage. Don't do this in the block itself because it's a global optimization
and if blocks require it themselves you can't combine various modules anymore once ZP runs out. and if blocks require it themselves you can't combine various modules anymore once ZP runs out.
@ -198,18 +209,15 @@ However you can specify some options globally in your program to change this beh
be stored in the ZP, which is more efficient. be stored in the ZP, which is more efficient.
- ``zp clobber, restore`` - ``zp clobber, restore``
Use the whole zeropage, but make a backup copy of the original values at program start. Use the whole zeropage, but make a backup copy of the original values at program start.
When your program exits, the original ZP is restored and you drop back to the BASIC prompt. When your program exits, the original ZP is restored (except for the software jiffy clock
Not that the interrupts are *disabled* when your program is entered! in ``$a0 - $a2``) and you drop back to the BASIC prompt.
(you want/have to set your own IRQ routine because the default one will write to Not that the default IRQ routine is *still enabled* when your program is entered!
various locations in the zeropage) See the paragraph on the zero page for more info about this.
If you use ``zp clobber``, you can no longer use BASIC or KERNAL routines, If you use ``zp clobber``, you can no longer use most BASIC or KERNAL routines,
because these depend on most of the locations in the ZP. This includes most of the floating-point because these depend on most of the locations in the ZP. This includes the floating-point
logic and several utility routines that do I/O, such as ``print_string``. logic and several utility routines that do I/O, such as ``print_string``.
@todo default IRQ handling will still change certain values in ZP...
### Program Entry Point ### Program Entry Point
@ -376,6 +384,12 @@ if-statuses can be used when such a *comparison expression* is used. An example
``if_not A > 55 goto more_iterations`` ``if_not A > 55 goto more_iterations``
Conditional jumps are compiled into 6502's branching instructions (such as ``bne`` and ``bcc``) so
the rather strict limit on how *far* it can jump applies. The compiler itself can't figure this
out unfortunately, so it is entirely possible to create code that cannot be assembled successfully.
You'll have to restructure your gotos in the code (place target labels closer to the branch)
if you run into this type of assembler error.
Debugging (with Vice) Debugging (with Vice)
--------------------- ---------------------

View File

@ -38,14 +38,20 @@ def test_zp_noclobber_allocation():
def test_zp_clobber_allocation(): def test_zp_clobber_allocation():
zp = Zeropage() zp = Zeropage()
zp.configure(True) zp.configure(True)
assert zp.available() == 248 assert zp.available() == 239
loc = zp.allocate("", DataType.FLOAT) loc = zp.allocate("", DataType.FLOAT)
assert loc > 3 and loc not in zp.free assert loc > 3 and loc not in zp.free
num, rest = divmod(zp.available(), 5) num, rest = divmod(zp.available(), 5)
for _ in range(num): for _ in range(num-3):
zp.allocate("", DataType.FLOAT) zp.allocate("", DataType.FLOAT)
assert zp.available() == rest assert zp.available() == 19
for _ in range(rest // 2): with pytest.raises(LookupError):
zp.allocate("", DataType.FLOAT) # can't allocate because no more sequential bytes, only fragmented
for _ in range(14):
zp.allocate("", DataType.BYTE)
zp.allocate("", DataType.WORD)
zp.allocate("", DataType.WORD)
with pytest.raises(LookupError):
zp.allocate("", DataType.WORD) zp.allocate("", DataType.WORD)
assert zp.available() == 1 assert zp.available() == 1
zp.allocate("last", DataType.BYTE) zp.allocate("last", DataType.BYTE)

View File

@ -88,11 +88,11 @@ label4
start start
c64util.init_system() c64.init_system()
A = 0 A = 0
printloop printloop
c64util.print_byte_decimal(A) c64scr.print_byte_decimal(A)
c64.CHROUT('\n') c64.CHROUT('\n')
A++ A++
if A <20 goto printloop if A <20 goto printloop

View File

@ -47,7 +47,7 @@ sub printflt (float: AY) -> (?) {
c64.MOVFM!(AY) c64.MOVFM!(AY)
goto c64.FPRINTLN goto c64.FPRINTLN
; c64.FOUT!() ; c64.FOUT!()
; c64util.print_string!(AY) ; c64scr.print_string!(AY)
;goto c64.CHROUT('\n') ;goto c64.CHROUT('\n')
} }

View File

@ -6,7 +6,7 @@ import "c64lib"
var .text name = "?"*80 var .text name = "?"*80
start start
c64util.init_system() c64.init_system()
XY = c64.CINV XY = c64.CINV
SI = 1 SI = 1
@ -14,15 +14,15 @@ start
SI = 0 SI = 0
c64util.print_string("enter your name: ") c64scr.print_string("enter your name: ")
c64util.input_chars(name) c64scr.input_chars(name)
c64.CHROUT('\n') c64.CHROUT('\n')
blop blop
breakpoint ; yeah! breakpoint ; yeah!
c64util.print_string("thank you, mr or mrs: ") c64scr.print_string("thank you, mr or mrs: ")
c64util.print_string(name) c64scr.print_string(name)
c64.CHROUT('\n') c64.CHROUT('\n')
SI = 1 SI = 1

View File

@ -13,19 +13,19 @@ import "c64lib"
start start
c64util.init_system() c64.init_system()
A = c64.VMCSB A = c64.VMCSB
A |= 2 ; @todo c64.VMCSB |= 2 A |= 2 ; @todo c64.VMCSB |= 2
c64.VMCSB = A c64.VMCSB = A
; greeting ; greeting
c64util.print_string("Enter your name: ") c64scr.print_string("Enter your name: ")
Y = c64util.input_chars(name) Y = c64scr.input_chars(name)
c64.CHROUT('\n') c64.CHROUT('\n')
c64.CHROUT('\n') c64.CHROUT('\n')
c64util.print_string("Hello, ") c64scr.print_string("Hello, ")
c64util.print_string(name) c64scr.print_string(name)
c64.CHROUT('.') c64.CHROUT('.')
c64.CHROUT('\n') c64.CHROUT('\n')
@ -35,43 +35,43 @@ start
c64.MUL10() c64.MUL10()
c64.FADDH() c64.FADDH()
c64.FADDH() c64.FADDH()
AY = c64util.GETADRAY() AY = c64.GETADRAY()
secretnumber = A secretnumber = A
c64util.print_string("I am thinking of a number from 1 to 100!You'll have to guess it!\n") c64scr.print_string("I am thinking of a number from 1 to 100!You'll have to guess it!\n")
printloop printloop
c64util.print_string("\nYou have ") c64scr.print_string("\nYou have ")
c64util.print_byte_decimal(attempts_left) c64scr.print_byte_decimal(attempts_left)
c64util.print_string(" guess") c64scr.print_string(" guess")
; @todo comparison expression so we can do if attempts_left>0 ... ; @todo comparison expression so we can do if attempts_left>0 ...
A = attempts_left A = attempts_left
A-- A--
if_zero A goto ask_guess if_zero A goto ask_guess
c64util.print_string("es") c64scr.print_string("es")
ask_guess ask_guess
c64util.print_string(" left.\nWhat is your next guess? ") c64scr.print_string(" left.\nWhat is your next guess? ")
Y = c64util.input_chars(guess) Y = c64scr.input_chars(guess)
c64.CHROUT('\n') c64.CHROUT('\n')
[$22.word] = guess [$22.word] = guess
c64.FREADSTR(A) c64.FREADSTR(A)
AY = c64util.GETADRAY() AY = c64.GETADRAY()
A -= secretnumber ; @todo condition so we can do if guess > secretnumber.... A -= secretnumber ; @todo condition so we can do if guess > secretnumber....
if_zero goto correct_guess if_zero goto correct_guess
if_gt goto too_high if_gt goto too_high
c64util.print_string("That is too ") c64scr.print_string("That is too ")
c64util.print_string("low!\n") c64scr.print_string("low!\n")
goto continue goto continue
correct_guess correct_guess
c64util.print_string("\nThat's my number, impressive!\n") c64scr.print_string("\nThat's my number, impressive!\n")
goodbye() goodbye()
return return
too_high too_high
c64util.print_string("That is too ") c64scr.print_string("That is too ")
c64util.print_string("high!\n") c64scr.print_string("high!\n")
continue continue
attempts_left-- attempts_left--
@ -79,8 +79,8 @@ continue
goto printloop goto printloop
game_over game_over
c64util.print_string("\nToo bad! It was: ") c64scr.print_string("\nToo bad! It was: ")
c64util.print_byte_decimal(secretnumber) c64scr.print_byte_decimal(secretnumber)
c64.CHROUT('\n') c64.CHROUT('\n')
goodbye() goodbye()
return return
@ -90,7 +90,7 @@ sub goodbye ()->() {
;memory y = $c000 ; @todo vars in sub ;memory y = $c000 ; @todo vars in sub
;const q = 22 ; @todo const in sub ;const q = 22 ; @todo const in sub
c64util.print_string("\nThanks for playing. Bye!\n") c64scr.print_string("\nThanks for playing. Bye!\n")
return return
} }

View File

@ -57,7 +57,7 @@ start
c64.CHROUT('0') c64.CHROUT('0')
c64.CHROUT('1') c64.CHROUT('1')
c64.CHROUT('2') c64.CHROUT('2')
c64util.print_string(hello) c64scr.print_string(hello)
goto c64.CHROUT('!') goto c64.CHROUT('!')

View File

@ -9,41 +9,41 @@ output prg,basic ; create a c-64 program with basic SYS to() launch it
const .word BORDER = $d020 const .word BORDER = $d020
start start
c64util.print_pimmediate ! () ; this prints the pstring immediately following it c64scr.print_pimmediate ! () ; this prints the pstring immediately following it
asm { asm {
.ptext "hello-pimmediate!{cr}" .ptext "hello-pimmediate!{cr}"
} }
c64util.print_byte_decimal0 ! (19) c64scr.print_byte_decimal0 ! (19)
c64.CHROUT ! (13) c64.CHROUT ! (13)
c64util.print_byte_decimal ! (19) c64scr.print_byte_decimal ! (19)
c64.CHROUT ! (13) c64.CHROUT ! (13)
c64util.print_word_decimal0 ! ($0102) c64scr.print_word_decimal0 ! ($0102)
c64.CHROUT ! (13) c64.CHROUT ! (13)
c64util.print_word_decimal ! ($0102) c64scr.print_word_decimal ! ($0102)
c64.CHROUT ! (13) c64.CHROUT ! (13)
return return
start2 start2
global2.make_screen_black() global2.make_screen_black()
c64.CLEARSCR() c64.CLEARSCR()
c64util.print_string(greeting) c64scr.print_string(greeting)
c64util.print_pstring(p_greeting) c64scr.print_pstring(p_greeting)
c64util.print_byte_decimal(0) c64scr.print_byte_decimal(0)
c64util.print_byte_hex(0, 0) c64scr.print_byte_hex(0, 0)
c64.CHROUT(13) c64.CHROUT(13)
c64util.print_byte_decimal(13) c64scr.print_byte_decimal(13)
c64util.print_byte_hex(0, 13) c64scr.print_byte_hex(0, 13)
c64.CHROUT(13) c64.CHROUT(13)
c64util.print_byte_decimal(255) c64scr.print_byte_decimal(255)
c64util.print_byte_hex(0, 254) c64scr.print_byte_hex(0, 254)
c64util.print_byte_hex(0, 129) c64scr.print_byte_hex(0, 129)
c64.CHROUT(13) c64.CHROUT(13)
c64.CHROUT(13) c64.CHROUT(13)
c64util.print_word_decimal($0100) c64scr.print_word_decimal($0100)
c64.CHROUT(13) c64.CHROUT(13)
return return

View File

@ -1,48 +1,27 @@
output prg,basic output prg,basic
zp clobber, restore
;reg_preserve off ; @todo global option off/on default off? ;reg_preserve off ; @todo global option off/on default off?
import "c64lib" import "c64lib"
~ ZP {
var .float fl1 = 3.1415927
var .float fl2 = 99.999999
var .float fl3 = 100000
}
~ main { ~ main {
var .float fl1 = 3.1415927
var .float fl2 = 99.999999
var .float fl3 = 10
start start
fl1 = 111111.22222 A = $11
fl2 = 0 X = $22
fl3 = 1 Y = $33
fl3 = -1
fl3 = 0.5
fl3 = -0.5
X=6 c64scr.clear_screen !(81, 5, $04)
loop1 ;c64scr.clear_screen !A (81, 5, $04)
A=X ;c64scr.clear_screen !AX (81, 5, $04)
c64util.print_byte_hex(0, A) ;c64scr.clear_screen !AXY (81, 5, $04)
c64.CHROUT!(" ")
X--
if_pos goto loop1
Y=6 c64scr.print_byte_hex(1,A)
loop2 c64.CHROUT(' ')
A=Y c64scr.print_byte_hex(1,X)
c64util.print_byte_hex(0, A) c64.CHROUT(' ')
c64.CHROUT!(" ") c64scr.print_byte_hex(1,Y)
Y-- c64scr.print_word_decimal(1222)
if_neg goto stop c64.CHROUT('\n')
goto loop2
stop
return return