From 63aa3cae8cd0bec7ab13f09b87c8006873dbf2c5 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 24 Dec 2017 00:58:55 +0100 Subject: [PATCH] subroutines --- il65/codegen.py | 21 ++++++- il65/parse.py | 44 ++++++++++++--- il65/symbols.py | 9 ++- lib/c64lib.ill | 122 +++++++++++++++++++++++------------------ reference.txt | 10 ++-- testsource/source1.ill | 25 +++++++++ testsource/source3.ill | 3 +- testsource/source4.ill | 44 +++++---------- 8 files changed, 175 insertions(+), 103 deletions(-) diff --git a/il65/codegen.py b/il65/codegen.py index caf190050..a7c847103 100644 --- a/il65/codegen.py +++ b/il65/codegen.py @@ -229,14 +229,33 @@ class CodeGenerator: self.p("* = ${:04x}".format(block.address)) self.p("{:s}\t.proc\n".format(block.label)) self.generate_block_vars(block) - subroutines = list(block.symbols.iter_subroutines()) + subroutines = list(sub for sub in block.symbols.iter_subroutines() if sub.address is not None) if subroutines: self.p("\n; external subroutines") for subdef in subroutines: + assert subdef.sub_block is None self.p("\t\t{:s} = {:s}".format(subdef.name, Parser.to_hex(subdef.address))) self.p("; end external subroutines") for stmt in block.statements: self.generate_statement(stmt) + subroutines = list(sub for sub in block.symbols.iter_subroutines() if sub.address is None) + if subroutines: + self.p("\n; block subroutines") + for subdef in subroutines: + assert subdef.sub_block is not None + self.p("{:s}\t\t; src l. {:d}".format(subdef.name, subdef.sourceref.line)) + params = ", ".join("{:s} -> {:s}".format(p[0] or "", p[1]) for p in subdef.parameters) + returns = ",".join(sorted(subdef.return_registers)) + clobbers = ",".join(sorted(subdef.clobbered_registers)) + self.p("\t\t; params: {}\n\t\t; returns: {} clobbers: {}" + .format(params or "-", returns or "-", clobbers or "-")) + cur_block = self.cur_block + self.cur_block = subdef.sub_block + for stmt in subdef.sub_block.statements: + self.generate_statement(stmt) + self.cur_block = cur_block + self.p("") + self.p("; end external subroutines") self.p("\t.pend\n") def generate_block_vars(self, block: ParseResult.Block) -> None: diff --git a/il65/parse.py b/il65/parse.py index 8ef6a8f2d..ead2d4cdc 100644 --- a/il65/parse.py +++ b/il65/parse.py @@ -805,12 +805,10 @@ class Parser: raise self.PError("ZP block cannot contain code statements") self.prev_line() self.cur_block.statements.append(self.parse_asm()) - continue elif unstripped_line.startswith((" ", "\t")): if is_zp_block: raise self.PError("ZP block cannot contain code statements") self.cur_block.statements.append(self.parse_statement(line)) - continue elif line: if is_zp_block: raise self.PError("ZP block cannot contain code labels") @@ -861,9 +859,10 @@ class Parser: r"\((?P[\w\s:,]*)\)" r"\s*->\s*" r"\((?P[\w\s?,]*)\)\s*" - r"\s+=\s+(?P
\S*)\s*$", line) + r"(?P\s+=\s+(?P
\S*)|{)\s*$", line) if not match: raise self.PError("invalid subx declaration") + code_decl = match.group("decltype") == "{" name, parameterlist, resultlist, address_str = \ match.group("name"), match.group("parameters"), match.group("results"), match.group("address") parameters = [(match.group("name"), match.group("target")) @@ -875,12 +874,41 @@ class Parser: if len(all_paramnames) != len(set(all_paramnames)): raise self.PError("duplicates in parameter names") results = {match.group("name") for match in re.finditer(r"\s*(?P(?:\w+)\??)\s*(?:,|$)", resultlist)} + subroutine_block = None + if code_decl: + address = None + # parse the subroutine code lines (until the closing '}') + subroutine_block = ParseResult.Block(name, self.sourceref, self.cur_block.symbols) + current_block = self.cur_block + self.cur_block = subroutine_block + while True: + self._parse_comments() + line = self.next_line() + unstripped_line = line + line = line.strip() + if line == "}": + # subroutine end + break + if line.startswith(("subx ", "subx\t")): + raise self.PError("cannot nest subroutines") + elif line.startswith(("asm ", "asm\t")): + self.prev_line() + subroutine_block.statements.append(self.parse_asm()) + elif unstripped_line.startswith((" ", "\t")): + subroutine_block.statements.append(self.parse_statement(line)) + elif line: + self.parse_label(line) + else: + raise self.PError("missing } to close subroutine from line " + str(subroutine_block.sourceref.line)) + self.cur_block = current_block + self.cur_block.sourceref = subroutine_block.sourceref + else: + try: + address = parse_expr_as_int(address_str, self.cur_block.symbols, self.ppsymbols, self.sourceref) + except ParseError: + raise self.PError("invalid subroutine address") try: - address = parse_expr_as_int(address_str, self.cur_block.symbols, self.ppsymbols, self.sourceref) - except ParseError: - raise self.PError("invalid subroutine address") - try: - self.cur_block.symbols.define_sub(name, self.sourceref, parameters, results, address) + self.cur_block.symbols.define_sub(name, self.sourceref, parameters, results, address, subroutine_block) except SymbolError as x: raise self.PError(str(x)) from x diff --git a/il65/symbols.py b/il65/symbols.py index a378c76aa..0fa0b6c8f 100644 --- a/il65/symbols.py +++ b/il65/symbols.py @@ -167,9 +167,11 @@ class ConstantDef(SymbolDefinition): class SubroutineDef(SymbolDefinition): def __init__(self, blockname: str, name: str, sourceref: SourceRef, - parameters: Sequence[Tuple[str, str]], returnvalues: Set[str], address: Optional[int]=None) -> None: + parameters: Sequence[Tuple[str, str]], returnvalues: Set[str], + address: Optional[int]=None, sub_block: Any=None) -> None: super().__init__(blockname, name, sourceref, False) self.address = address + self.sub_block = sub_block self.parameters = parameters self.input_registers = set() # type: Set[str] self.return_registers = set() # type: Set[str] @@ -372,9 +374,10 @@ class SymbolTable: self.eval_dict = None def define_sub(self, name: str, sourceref: SourceRef, - parameters: Sequence[Tuple[str, str]], returnvalues: Set[str], address: Optional[int]) -> None: + parameters: Sequence[Tuple[str, str]], returnvalues: Set[str], + address: Optional[int], sub_block: Any) -> None: self.check_identifier_valid(name, sourceref) - self.symbols[name] = SubroutineDef(self.name, name, sourceref, parameters, returnvalues, address) + self.symbols[name] = SubroutineDef(self.name, name, sourceref, parameters, returnvalues, address, sub_block) def define_label(self, name: str, sourceref: SourceRef) -> None: self.check_identifier_valid(name, sourceref) diff --git a/lib/c64lib.ill b/lib/c64lib.ill index 0cdf64506..47cd1bf2f 100644 --- a/lib/c64lib.ill +++ b/lib/c64lib.ill @@ -215,8 +215,8 @@ subx IOBASE () -> (X, Y) = $FFF3 ; read base addres ; @todo use user-defined subroutines here to have param definitions -; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST) -FREADS32 ; () -> (A?, X?, Y?) +subx FREADS32 () -> (A?, X?, Y?) { + ; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST) asm { lda $62 eor #$ff @@ -225,19 +225,21 @@ FREADS32 ; () -> (A?, X?, Y?) ldx #$a0 jmp $bc4f } +} -; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST) -FREADUS32 ; () -> (A?, X?, Y?) +subx FREADUS32 () -> (A?, X?, Y?) { + ; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST) asm { sec lda #0 ldx #$a0 jmp $bc4f } +} -; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes) -; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead. -FREADS24AXY ; (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) +subx FREADS24AXY (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) { + ; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes) + ; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead. asm { sty $62 stx $63 @@ -250,10 +252,10 @@ FREADS24AXY ; (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) ldx #$98 jmp $bc4f } - +} -; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1 -GIVUAYF ; (uword: AY) -> (A?, X?, Y?) +subx GIVUAYF (uword: AY) -> (A?, X?, Y?) { + ; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1 asm { sty $62 sta $63 @@ -261,18 +263,20 @@ GIVUAYF ; (uword: AY) -> (A?, X?, Y?) sec jmp $bc49 } +} -; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1 -GIVAYFAY ; (sword: AY) -> (A?, X?, Y?) +subx GIVAYFAY (sword: AY) -> (A?, X?, Y?) { + ; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1 asm { sta c64.SCRATCH_ZP1 tya ldy c64.SCRATCH_ZP1 jmp c64.GIVAYF ; this uses the inverse order, Y/A } - -; ---- fac1 to signed word in A/Y -FTOSWRDAY ; () -> (A, Y, X?) +} + +subx FTOSWRDAY () -> (A, Y, X?) { + ; ---- fac1 to signed word in A/Y asm { jsr c64.FTOSWORDYA ; note the inverse Y/A order sta c64.SCRATCH_ZP1 @@ -280,9 +284,10 @@ FTOSWRDAY ; () -> (A, Y, X?) ldy c64.SCRATCH_ZP1 rts } +} -; ---- fac1 to unsigned word in A/Y -GETADRAY ; () -> (A, Y, X?) +subx GETADRAY () -> (A, Y, X?) { + ; ---- fac1 to unsigned word in A/Y asm { jsr c64.GETADR ; this uses the inverse order, Y/A sta c64.SCRATCH_ZP1 @@ -290,10 +295,10 @@ GETADRAY ; () -> (A, Y, X?) ldy c64.SCRATCH_ZP1 rts } - +} -; ---- print null terminated string from X/Y -print_string ; (address: XY) -> (A?, Y?) +subx print_string (address: XY) -> (A?, Y?) { + ; ---- print null terminated string from X/Y asm { stx c64.SCRATCH_ZP1 sty c64.SCRATCH_ZP2 @@ -305,9 +310,10 @@ print_string ; (address: XY) -> (A?, Y?) bne - + rts } +} -; ---- print pstring (length as first byte) from X/Y, returns str len in Y -print_pstring ; (address: XY) -> (A?, X?, Y) +subx 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 @@ -322,10 +328,11 @@ print_pstring ; (address: XY) -> (A?, X?, Y) bne - + rts ; output string length is in Y } +} - -; ---- print pstring in memory immediately following the fcall instruction (don't use call!) -print_pimmediate +subx 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 @@ -349,10 +356,10 @@ print_pimmediate inc $102,x ; increment the high byte of the return addr too. + rts } +} - -; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A) -byte2decimal ; (ubyte: A) -> (Y, X, A) +subx byte2decimal (ubyte: A) -> (Y, X, A) { + ; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A) asm { ldy #$2f ldx #$3a @@ -366,9 +373,10 @@ byte2decimal ; (ubyte: A) -> (Y, X, A) adc #$2f rts } +} -; ---- A to hex string in XY (first hex char in X, second hex char in Y) -byte2hex ; (ubyte: A) -> (X, Y, A?) +subx byte2hex (ubyte: A) -> (X, Y, A?) { + ; ---- A to hex string in XY (first hex char in X, second hex char in Y) asm { pha and #$0f @@ -386,20 +394,19 @@ byte2hex ; (ubyte: A) -> (X, Y, A?) hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well } +} -; Convert an 16 bit binary value to BCD -; -; This function converts a 16 bit binary value in X/Y into a 24 bit BCD. It -; works by transferring one bit a time from the source and adding it -; into a BCD value that is being doubled on each iteration. As all the -; arithmetic is being done in BCD the result is a binary to decimal -; conversion. - var .array(3) word2bcd_bcdbuff - -word2bcd ; (address: XY) -> (A?, X?) +subx word2bcd (address: XY) -> (A?, X?) { + ; Convert an 16 bit binary value to BCD + ; + ; This function converts a 16 bit binary value in X/Y into a 24 bit BCD. It + ; works by transferring one bit a time from the source and adding it + ; into a BCD value that is being doubled on each iteration. As all the + ; arithmetic is being done in BCD the result is a binary to decimal + ; conversion. asm { stx c64.SCRATCH_ZP1 sty c64.SCRATCH_ZP2 @@ -426,12 +433,12 @@ word2bcd ; (address: XY) -> (A?, X?) cld ; back to binary rts } +} -; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output' var .array(5) word2decimal_output - -word2decimal ; (address: XY) -> (A?, X?, Y?) +subx word2decimal (address: XY) -> (A?, X?, Y?) { + ; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output' asm { jsr word2bcd lda word2bcd_bcdbuff+2 @@ -459,9 +466,10 @@ word2decimal ; (address: XY) -> (A?, X?, Y?) iny rts } +} -; ---- print the byte in A in decimal form, with left padding 0s (3 positions total) -print_byte_decimal0 ; (ubyte: A) -> (A?, X?, Y?) +subx print_byte_decimal0 (ubyte: A) -> (A?, X?, Y?) { + ; ---- print the byte in A in decimal form, with left padding 0s (3 positions total) asm { jsr byte2decimal pha @@ -472,9 +480,10 @@ print_byte_decimal0 ; (ubyte: A) -> (A?, X?, Y?) pla jmp c64.CHROUT } +} -; ---- print the byte in A in decimal form, without left padding 0s -print_byte_decimal ; (ubyte: A) -> (A?, X?, Y?) +subx print_byte_decimal (ubyte: A) -> (A?, X?, Y?) { + ; ---- print the byte in A in decimal form, without left padding 0s asm { jsr byte2decimal pha @@ -489,9 +498,10 @@ print_byte_decimal ; (ubyte: A) -> (A?, X?, Y?) + pla jmp c64.CHROUT } +} -; ---- print the byte in A in hex form -print_byte_hex ; (ubyte: A) -> (A?, X?, Y?) +subx print_byte_hex (ubyte: A) -> (A?, X?, Y?) { + ; ---- print the byte in A in hex form asm { jsr byte2hex txa @@ -499,9 +509,11 @@ print_byte_hex ; (ubyte: A) -> (A?, X?, Y?) tya jmp c64.CHROUT } +} -; ---- print the word in X/Y in decimal form, with left padding 0s (5 positions total) -print_word_decimal0 ; (address: XY) -> (A?, X?, Y?) + +subx print_word_decimal0 (address: XY) -> (A?, X?, Y?) { + ; ---- print the word in X/Y in decimal form, with left padding 0s (5 positions total) asm { jsr word2decimal lda word2decimal_output @@ -515,9 +527,11 @@ print_word_decimal0 ; (address: XY) -> (A?, X?, Y?) lda word2decimal_output+4 jmp c64.CHROUT } +} -; ---- print the word in X/Y in decimal form, without left padding 0s -print_word_decimal ; (address: XY) -> (A?, X? Y?) + +subx print_word_decimal (address: XY) -> (A?, X? Y?) { + ; ---- print the word in X/Y in decimal form, without left padding 0s asm { jsr word2decimal ldy #0 @@ -547,3 +561,5 @@ _pr_decimal rts } } + +} diff --git a/reference.txt b/reference.txt index 47ade7078..927406a16 100644 --- a/reference.txt +++ b/reference.txt @@ -336,15 +336,13 @@ proc_results = sequence of names that specify in which register(s) th example: "subx CLOSE (logical: A) -> (A?, X?, Y?) $FFC3" -ISOLATION (register preservation when calling subroutines): @todo isolation +REGISTER PRESERVATION BLOCK: @todo (no)preserve - isolate [regs] { .... } that adds register preservation around the containing code default = all 3 regs, or specify which. - fcall -> fastcall, doesn't do register preservations - call -> as before, alsways does it, even when already in isolate block + preserve [regs] { .... } adds register preservation around the containing code default = all 3 regs, or specify which. + nopreserve [regs] { .... } removes register preservation on all statements in the block that would otherwise have it. - -@todo user defined subroutines +@todo document user defined subroutines SUBROUTINE CALLS diff --git a/testsource/source1.ill b/testsource/source1.ill index 8b621fd2c..f3bdf29df 100644 --- a/testsource/source1.ill +++ b/testsource/source1.ill @@ -79,8 +79,33 @@ _loop block2.zpw1 ++ return 155,2,%00000101 ; will end up in A, X, Y } + + ~ block2 { + + return + +subx memsub () -> () = $fff2 + +subx customsub (Y)->() { + + asm { + nop + nop + lda #99 + nop + nop + } + + A=2 + X=33 + goto fidget.subroutine() + +} + + somelabel1222 + customsub(2) return var zp1 diff --git a/testsource/source3.ill b/testsource/source3.ill index 21d3c302e..1f1c7781f 100644 --- a/testsource/source3.ill +++ b/testsource/source3.ill @@ -60,8 +60,7 @@ start c64.CHROUT('0') c64.CHROUT('1') c64.CHROUT('2') - XY = hello - c64util.print_string() + c64util.print_string(hello) goto c64.CHROUT('!') diff --git a/testsource/source4.ill b/testsource/source4.ill index abb7c9703..3b6b2d65a 100644 --- a/testsource/source4.ill +++ b/testsource/source4.ill @@ -14,52 +14,36 @@ start .ptext "hello-pimmediate!{cr}" } - A = 19 - c64util.print_byte_decimal0 ! () + c64util.print_byte_decimal0 ! (19) c64.CHROUT ! (13) - A = 19 - c64util.print_byte_decimal ! () + c64util.print_byte_decimal ! (19) c64.CHROUT ! (13) - X = $01 - Y = $02 - c64util.print_word_decimal0 ! () + c64util.print_word_decimal0 ! ($0102) c64.CHROUT ! (13) - X = $01 - Y = $02 - c64util.print_word_decimal ! () + c64util.print_word_decimal ! ($0102) c64.CHROUT ! (13) return start2 global2.make_screen_black() c64.CLEARSCR() - XY = greeting - c64util.print_string() - XY = p_greeting - c64util.print_pstring() - A = 0 - c64util.print_byte_decimal() - A = 0 - c64util.print_byte_hex() + c64util.print_string(greeting) + c64util.print_pstring(p_greeting) + c64util.print_byte_decimal(0) + c64util.print_byte_hex(0) c64.CHROUT(13) - c64util.print_byte_decimal() - A = 13 - c64util.print_byte_hex() + c64util.print_byte_decimal(13) + c64util.print_byte_hex(13) c64.CHROUT(13) - A = 255 - c64util.print_byte_decimal() - A = 254 - c64util.print_byte_hex() - A = 129 - c64util.print_byte_hex() + c64util.print_byte_decimal(255) + c64util.print_byte_hex(254) + c64util.print_byte_hex(129) c64.CHROUT(13) c64.CHROUT(13) - X = 1 - Y = 0 - c64util.print_word_decimal() + c64util.print_word_decimal($0100) c64.CHROUT(13) return