From be40cdd8aa0cd032c7355c0030631e0b0a45263e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 30 Dec 2017 20:03:19 +0100 Subject: [PATCH] math --- il65/codegen.py | 4 +-- il65/symbols.py | 12 ++++--- lib/c64lib.ill | 19 +++++----- lib/il65lib.ill | 8 +++-- lib/mathlib.ill | 94 ++++++++++++++++++++++++++++++++++++++++++++++--- reference.md | 4 +-- 6 files changed, 116 insertions(+), 25 deletions(-) diff --git a/il65/codegen.py b/il65/codegen.py index 07c5e5873..aaf27157a 100644 --- a/il65/codegen.py +++ b/il65/codegen.py @@ -1723,9 +1723,9 @@ class CodeGenerator: if rvalue.datatype == DataType.FLOAT: with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True): self.p("\t\tlda #<" + r_str) - self.p("\t\tsta c64.SCRATCH_ZPWORD") + self.p("\t\tsta c64.SCRATCH_ZPWORD1") self.p("\t\tlda #>" + r_str) - self.p("\t\tsta c64.SCRATCH_ZPWORD+1") + self.p("\t\tsta c64.SCRATCH_ZPWORD1+1") self.p("\t\tldx #<" + l_str) self.p("\t\tldy #>" + l_str) self.p("\t\tjsr c64_lib.copy_mflt") diff --git a/il65/symbols.py b/il65/symbols.py index f3c30d73a..f0244fe37 100644 --- a/il65/symbols.py +++ b/il65/symbols.py @@ -208,7 +208,8 @@ class SubroutineDef(SymbolDefinition): class Zeropage: SCRATCH_B1 = 0x02 SCRATCH_B2 = 0x03 - SCRATCH_W1 = 0xfd # $fd/$fe + SCRATCH_W1 = 0xfb # $fb/$fc + SCRATCH_W2 = 0xfd # $fd/$fe def __init__(self) -> None: self.unused_bytes = [] # type: List[int] @@ -219,13 +220,14 @@ class Zeropage: if self._configured: raise SymbolError("cannot configure the ZP multiple times") if clobber_zp: - self.unused_bytes = list(range(0x04, 0x80)) + [0xfc, 0xff] - self.unused_words = list(range(0x80, 0xfc, 2)) + self.unused_bytes = list(range(0x04, 0x80)) + [0xff] + self.unused_words = list(range(0x80, 0xfb, 2)) else: # these are valid for the C-64 (when no RS232 I/O is performed): - # ($02, $03, $fd-$fe are reserved as scratch addresses for various routines) + # ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines) self.unused_bytes = [0x04, 0x05, 0x06, 0x2a, 0x52] # 5 zp variables (1 byte each) - self.unused_words = [0xf7, 0xf9, 0xfb] # 3 zp word variables (2 bytes each) + self.unused_words = [0xf7, 0xf9] # 2 zp word variables (2 bytes each) + # @todo more clever allocating, don't have fixed bytes and words assert self.SCRATCH_B1 not in self.unused_bytes and self.SCRATCH_B1 not in self.unused_words assert self.SCRATCH_B2 not in self.unused_bytes and self.SCRATCH_B2 not in self.unused_words self._configured = True diff --git a/lib/c64lib.ill b/lib/c64lib.ill index fe027af02..aaf28c256 100644 --- a/lib/c64lib.ill +++ b/lib/c64lib.ill @@ -12,7 +12,8 @@ output raw ~ c64 { memory .byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP memory .byte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP - memory .word SCRATCH_ZPWORD = $fd ; scratch word in ZP ($fd/$fe) + memory .word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc) + memory .word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe) memory .byte COLOR = $0286 ; cursor color memory .word CINV = $0314 ; IRQ vector @@ -717,25 +718,25 @@ sub input_chars (buffer: AX) -> (A?, Y) { asm { ; ---- copy a 5 byte MFLT floating point variable to another place -; input: X/Y = source address, SCRATCH_ZPWORD = destination address +; input: X/Y = source address, SCRATCH_ZPWORD1 = destination address copy_mflt stx c64.SCRATCH_ZP1 - sty c64.SCRATCH_ZPWORD+1 + sty c64.SCRATCH_ZPWORD1+1 ldy #0 lda (c64.SCRATCH_ZP1),y - sta (c64.SCRATCH_ZPWORD),y + sta (c64.SCRATCH_ZPWORD1),y iny lda (c64.SCRATCH_ZP1),y - sta (c64.SCRATCH_ZPWORD),y + sta (c64.SCRATCH_ZPWORD1),y iny lda (c64.SCRATCH_ZP1),y - sta (c64.SCRATCH_ZPWORD),y + sta (c64.SCRATCH_ZPWORD1),y iny lda (c64.SCRATCH_ZP1),y - sta (c64.SCRATCH_ZPWORD),y + sta (c64.SCRATCH_ZPWORD1),y iny lda (c64.SCRATCH_ZP1),y - sta (c64.SCRATCH_ZPWORD),y - ldy c64.SCRATCH_ZPWORD+1 + sta (c64.SCRATCH_ZPWORD1),y + ldy c64.SCRATCH_ZPWORD1+1 rts } diff --git a/lib/il65lib.ill b/lib/il65lib.ill index 634338211..91ae62c63 100644 --- a/lib/il65lib.ill +++ b/lib/il65lib.ill @@ -47,9 +47,11 @@ zp_backup .fill 254, 0 ~ il65_lib { - ; note: the following two ZP scratch registers must be the same as in c64lib - memory SCRATCH_ZP1 = $02 ; scratch register #1 in ZP - memory SCRATCH_ZP2 = $03 ; scratch register #2 in ZP + ; 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_ZP2 = $03 ; scratch register #2 in ZP + memory .word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc) + memory .word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe) asm { diff --git a/lib/mathlib.ill b/lib/mathlib.ill index 9fbd6f90f..364e09f97 100644 --- a/lib/mathlib.ill +++ b/lib/mathlib.ill @@ -12,9 +12,11 @@ output raw ~ math { - ; note: the following two ZP scratch registers must be the same as in c64lib - memory SCRATCH_ZP1 = $02 ; scratch register #1 in ZP - memory SCRATCH_ZP2 = $03 ; scratch register #2 in ZP + ; 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_ZP2 = $03 ; scratch register #2 in ZP + memory .word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc) + memory .word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe) @@ -65,13 +67,48 @@ sub multiply_bytes_addA_16 (byte1: X, byte2: Y, add: A) -> (A?, XY) { } } + var .wordarray(2) multiply_words_product +sub multiply_words (number: XY) -> (A?, X?, Y?) { ; @todo '?' to mean all 3 registers + ; ---- multiply two 16-bit words into a 32-bit result + ; input: X/Y = first 16-bit number, SCRATCH_ZPWORD1 in ZP = second 16-bit number + ; output: multiply_words_product 32-bits product, LSB order (low-to-high) -sub divide_bytes (numerator: X, denominator: Y) -> (X, A) { + asm { + stx SCRATCH_ZPWORD2 + sty SCRATCH_ZPWORD2+1 + +mult16 lda #$00 + sta multiply_words_product+2 ; clear upper bits of product + sta multiply_words_product+3 + ldx #16 ; for all 16 bits... +- lsr SCRATCH_ZPWORD1+1 ; divide multiplier by 2 + ror SCRATCH_ZPWORD1 + bcc + + lda multiply_words_product+2 ; get upper half of product and add multiplicand + clc + adc SCRATCH_ZPWORD2 + sta multiply_words_product+2 + lda multiply_words_product+3 + adc SCRATCH_ZPWORD2+1 ++ ror a ; rotate partial product + sta multiply_words_product+3 + ror multiply_words_product+2 + ror multiply_words_product+1 + ror multiply_words_product + dex + bne - + rts + } +} + + +sub divmod_bytes (number: X, divisor: Y) -> (X, A) { ; ---- divide X by Y, result quotient in X, remainder in A (unsigned) ; division by zero will result in quotient = 255 and remainder = original number asm { stx SCRATCH_ZP1 sty SCRATCH_ZP2 + lda #0 ldx #8 asl SCRATCH_ZP1 @@ -82,9 +119,58 @@ sub divide_bytes (numerator: X, denominator: Y) -> (X, A) { + rol SCRATCH_ZP1 dex bne - + ldx SCRATCH_ZP1 rts } } +sub divmod_words (divisor: XY) -> (A?, XY) { + ; ---- divide two words (16 bit each) into 16 bit results + ; input: SCRATCH_ZPWORD1 in ZP: 16 bit number, X/Y: 16 bit divisor + ; output: SCRATCH_ZPWORD1 in ZP: 16 bit result, X/Y: 16 bit remainder + ; division by zero will result in quotient = 65535 and remainder = divident + + asm { +remainder = SCRATCH_ZP1 + + stx SCRATCH_ZPWORD2 + sty SCRATCH_ZPWORD2+1 + lda #0 ;preset remainder to 0 + sta remainder + sta remainder+1 + ldx #16 ;repeat for each bit: ... + +- asl SCRATCH_ZPWORD1 ;number lb & hb*2, msb -> Carry + rol SCRATCH_ZPWORD1+1 + rol remainder ;remainder lb & hb * 2 + msb from carry + rol remainder+1 + lda remainder + sec + sbc SCRATCH_ZPWORD2 ;substract divisor to see if it fits in + tay ;lb result -> Y, for we may need it later + lda remainder+1 + sbc SCRATCH_ZPWORD2+1 + bcc + ;if carry=0 then divisor didn't fit in yet + + sta remainder+1 ;else save substraction result as new remainder, + sty remainder + inc SCRATCH_ZPWORD1 ;and INCrement result cause divisor fit in 1 times + ++ dex + bne - + + lda remainder ; copy remainder to ZPWORD2 result register + sta SCRATCH_ZPWORD2 + lda remainder+1 + sta SCRATCH_ZPWORD2+1 + + ldx SCRATCH_ZPWORD1 ; load division result in X/Y + ldy SCRATCH_ZPWORD1+1 + + rts + + } +} + } diff --git a/reference.md b/reference.md index 397f20444..5c8471aa0 100644 --- a/reference.md +++ b/reference.md @@ -86,7 +86,7 @@ The following 6502 hardware registers are directly accessible in your code (and The zero page locations ``$02`` - ``$ff`` can be regarded as 254 other registers because they take less clock cycles to access and need fewer instruction bytes than access to other memory locations. Theoretically you can use all of them in your program but there are a few limitations: -- the four locations ``$02``, ``$03``, ``$fd - $fe`` are reserved for internal use as scratch registers by IL65 +- several locations (``$02``, ``$03``, ``$fb - $fc``, ``$fd - $fe``) are reserved for internal use as scratch registers by IL65 - most other addresses often are in use by the machine's operating system or kernal, and overwriting them can crash the machine. Your program must take over the entire system to be able to safely use all zero page locations. @@ -99,7 +99,7 @@ For the Commodore-64 here is a list of free-to-use zero page locations even when ``$f7`` - ``$f8``; ``$f9`` - ``$fa``; ``$fb`` - ``$fc``; ``$fd`` - ``$fe`` The four reserved locations mentioned above are subtracted from this set, leaving you with -five 1-byte and three 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, 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.