subroutines

This commit is contained in:
Irmen de Jong 2017-12-24 00:58:55 +01:00
parent 37f049ee54
commit 63aa3cae8c
8 changed files with 175 additions and 103 deletions

View File

@ -229,14 +229,33 @@ class CodeGenerator:
self.p("* = ${:04x}".format(block.address)) self.p("* = ${:04x}".format(block.address))
self.p("{:s}\t.proc\n".format(block.label)) self.p("{:s}\t.proc\n".format(block.label))
self.generate_block_vars(block) 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: if subroutines:
self.p("\n; external subroutines") self.p("\n; external subroutines")
for subdef in 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("\t\t{:s} = {:s}".format(subdef.name, Parser.to_hex(subdef.address)))
self.p("; end external subroutines") self.p("; end external subroutines")
for stmt in block.statements: for stmt in block.statements:
self.generate_statement(stmt) 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 "<unnamed>", 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") self.p("\t.pend\n")
def generate_block_vars(self, block: ParseResult.Block) -> None: def generate_block_vars(self, block: ParseResult.Block) -> None:

View File

@ -805,12 +805,10 @@ class Parser:
raise self.PError("ZP block cannot contain code statements") raise self.PError("ZP block cannot contain code statements")
self.prev_line() self.prev_line()
self.cur_block.statements.append(self.parse_asm()) self.cur_block.statements.append(self.parse_asm())
continue
elif unstripped_line.startswith((" ", "\t")): elif unstripped_line.startswith((" ", "\t")):
if is_zp_block: if is_zp_block:
raise self.PError("ZP block cannot contain code statements") raise self.PError("ZP block cannot contain code statements")
self.cur_block.statements.append(self.parse_statement(line)) self.cur_block.statements.append(self.parse_statement(line))
continue
elif line: elif line:
if is_zp_block: if is_zp_block:
raise self.PError("ZP block cannot contain code labels") raise self.PError("ZP block cannot contain code labels")
@ -861,9 +859,10 @@ class Parser:
r"\((?P<parameters>[\w\s:,]*)\)" r"\((?P<parameters>[\w\s:,]*)\)"
r"\s*->\s*" r"\s*->\s*"
r"\((?P<results>[\w\s?,]*)\)\s*" r"\((?P<results>[\w\s?,]*)\)\s*"
r"\s+=\s+(?P<address>\S*)\s*$", line) r"(?P<decltype>\s+=\s+(?P<address>\S*)|{)\s*$", line)
if not match: if not match:
raise self.PError("invalid subx declaration") raise self.PError("invalid subx declaration")
code_decl = match.group("decltype") == "{"
name, parameterlist, resultlist, address_str = \ name, parameterlist, resultlist, address_str = \
match.group("name"), match.group("parameters"), match.group("results"), match.group("address") match.group("name"), match.group("parameters"), match.group("results"), match.group("address")
parameters = [(match.group("name"), match.group("target")) parameters = [(match.group("name"), match.group("target"))
@ -875,12 +874,41 @@ class Parser:
if len(all_paramnames) != len(set(all_paramnames)): if len(all_paramnames) != len(set(all_paramnames)):
raise self.PError("duplicates in parameter names") raise self.PError("duplicates in parameter names")
results = {match.group("name") for match in re.finditer(r"\s*(?P<name>(?:\w+)\??)\s*(?:,|$)", resultlist)} results = {match.group("name") for match in re.finditer(r"\s*(?P<name>(?:\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: try:
address = parse_expr_as_int(address_str, self.cur_block.symbols, self.ppsymbols, self.sourceref) self.cur_block.symbols.define_sub(name, self.sourceref, parameters, results, address, subroutine_block)
except ParseError:
raise self.PError("invalid subroutine address")
try:
self.cur_block.symbols.define_sub(name, self.sourceref, parameters, results, address)
except SymbolError as x: except SymbolError as x:
raise self.PError(str(x)) from x raise self.PError(str(x)) from x

View File

@ -167,9 +167,11 @@ class ConstantDef(SymbolDefinition):
class SubroutineDef(SymbolDefinition): class SubroutineDef(SymbolDefinition):
def __init__(self, blockname: str, name: str, sourceref: SourceRef, 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) super().__init__(blockname, name, sourceref, False)
self.address = address self.address = address
self.sub_block = sub_block
self.parameters = parameters self.parameters = parameters
self.input_registers = set() # type: Set[str] self.input_registers = set() # type: Set[str]
self.return_registers = set() # type: Set[str] self.return_registers = set() # type: Set[str]
@ -372,9 +374,10 @@ class SymbolTable:
self.eval_dict = None self.eval_dict = None
def define_sub(self, name: str, sourceref: SourceRef, 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.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: def define_label(self, name: str, sourceref: SourceRef) -> None:
self.check_identifier_valid(name, sourceref) self.check_identifier_valid(name, sourceref)

View File

@ -215,8 +215,8 @@ subx IOBASE () -> (X, Y) = $FFF3 ; read base addres
; @todo use user-defined subroutines here to have param definitions ; @todo use user-defined subroutines here to have param definitions
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST) subx FREADS32 () -> (A?, X?, Y?) {
FREADS32 ; () -> (A?, X?, Y?) ; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
asm { asm {
lda $62 lda $62
eor #$ff eor #$ff
@ -225,19 +225,21 @@ FREADS32 ; () -> (A?, X?, Y?)
ldx #$a0 ldx #$a0
jmp $bc4f jmp $bc4f
} }
}
; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST) subx FREADUS32 () -> (A?, X?, Y?) {
FREADUS32 ; () -> (A?, X?, Y?) ; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST)
asm { asm {
sec sec
lda #0 lda #0
ldx #$a0 ldx #$a0
jmp $bc4f jmp $bc4f
} }
}
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes) subx FREADS24AXY (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) {
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead. ; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
FREADS24AXY ; (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) ; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
asm { asm {
sty $62 sty $62
stx $63 stx $63
@ -250,10 +252,10 @@ FREADS24AXY ; (lo: A, mid: X, hi: Y) -> (A?, X?, Y?)
ldx #$98 ldx #$98
jmp $bc4f jmp $bc4f
} }
}
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1 subx GIVUAYF (uword: AY) -> (A?, X?, Y?) {
GIVUAYF ; (uword: AY) -> (A?, X?, Y?) ; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
asm { asm {
sty $62 sty $62
sta $63 sta $63
@ -261,18 +263,20 @@ GIVUAYF ; (uword: AY) -> (A?, X?, Y?)
sec sec
jmp $bc49 jmp $bc49
} }
}
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1 subx GIVAYFAY (sword: AY) -> (A?, X?, Y?) {
GIVAYFAY ; (sword: AY) -> (A?, X?, Y?) ; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
asm { asm {
sta c64.SCRATCH_ZP1 sta c64.SCRATCH_ZP1
tya tya
ldy c64.SCRATCH_ZP1 ldy c64.SCRATCH_ZP1
jmp c64.GIVAYF ; this uses the inverse order, Y/A 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 { asm {
jsr c64.FTOSWORDYA ; note the inverse Y/A order jsr c64.FTOSWORDYA ; note the inverse Y/A order
sta c64.SCRATCH_ZP1 sta c64.SCRATCH_ZP1
@ -280,9 +284,10 @@ FTOSWRDAY ; () -> (A, Y, X?)
ldy c64.SCRATCH_ZP1 ldy c64.SCRATCH_ZP1
rts rts
} }
}
; ---- fac1 to unsigned word in A/Y subx GETADRAY () -> (A, Y, X?) {
GETADRAY ; () -> (A, Y, X?) ; ---- fac1 to unsigned word in A/Y
asm { asm {
jsr c64.GETADR ; this uses the inverse order, Y/A jsr c64.GETADR ; this uses the inverse order, Y/A
sta c64.SCRATCH_ZP1 sta c64.SCRATCH_ZP1
@ -290,10 +295,10 @@ GETADRAY ; () -> (A, Y, X?)
ldy c64.SCRATCH_ZP1 ldy c64.SCRATCH_ZP1
rts rts
} }
}
; ---- print null terminated string from X/Y subx print_string (address: XY) -> (A?, Y?) {
print_string ; (address: XY) -> (A?, Y?) ; ---- print null terminated string from X/Y
asm { asm {
stx c64.SCRATCH_ZP1 stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZP2 sty c64.SCRATCH_ZP2
@ -305,9 +310,10 @@ print_string ; (address: XY) -> (A?, Y?)
bne - bne -
+ rts + rts
} }
}
; ---- print pstring (length as first byte) from X/Y, returns str len in Y subx print_pstring (address: XY) -> (A?, X?, Y) {
print_pstring ; (address: XY) -> (A?, X?, Y) ; ---- print pstring (length as first byte) from X/Y, returns str len in Y
asm { asm {
stx c64.SCRATCH_ZP1 stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZP2 sty c64.SCRATCH_ZP2
@ -322,10 +328,11 @@ print_pstring ; (address: XY) -> (A?, X?, Y)
bne - bne -
+ rts ; output string length is in Y + rts ; output string length is in Y
} }
}
subx print_pimmediate () -> () {
; ---- print pstring in memory immediately following the fcall instruction (don't use call!) ; ---- print pstring in memory immediately following the subroutine fast call instruction
print_pimmediate ; note that the clobbered registers (A,X,Y) are not listed ON PURPOSE
asm { asm {
tsx tsx
lda $102,x lda $102,x
@ -349,10 +356,10 @@ print_pimmediate
inc $102,x ; increment the high byte of the return addr too. inc $102,x ; increment the high byte of the return addr too.
+ rts + rts
} }
}
subx 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)
byte2decimal ; (ubyte: A) -> (Y, X, A)
asm { asm {
ldy #$2f ldy #$2f
ldx #$3a ldx #$3a
@ -366,9 +373,10 @@ byte2decimal ; (ubyte: A) -> (Y, X, A)
adc #$2f adc #$2f
rts rts
} }
}
; ---- A to hex string in XY (first hex char in X, second hex char in Y) subx byte2hex (ubyte: A) -> (X, Y, A?) {
byte2hex ; (ubyte: A) -> (X, Y, A?) ; ---- A to hex string in XY (first hex char in X, second hex char in Y)
asm { asm {
pha pha
and #$0f 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 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 var .array(3) word2bcd_bcdbuff
subx word2bcd (address: XY) -> (A?, X?) {
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 { asm {
stx c64.SCRATCH_ZP1 stx c64.SCRATCH_ZP1
sty c64.SCRATCH_ZP2 sty c64.SCRATCH_ZP2
@ -426,12 +433,12 @@ word2bcd ; (address: XY) -> (A?, X?)
cld ; back to binary cld ; back to binary
rts rts
} }
}
; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output'
var .array(5) word2decimal_output var .array(5) word2decimal_output
subx word2decimal (address: XY) -> (A?, X?, Y?) {
word2decimal ; (address: XY) -> (A?, X?, Y?) ; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output'
asm { asm {
jsr word2bcd jsr word2bcd
lda word2bcd_bcdbuff+2 lda word2bcd_bcdbuff+2
@ -459,9 +466,10 @@ word2decimal ; (address: XY) -> (A?, X?, Y?)
iny iny
rts rts
} }
}
; ---- print the byte in A in decimal form, with left padding 0s (3 positions total) subx print_byte_decimal0 (ubyte: A) -> (A?, X?, Y?) {
print_byte_decimal0 ; (ubyte: A) -> (A?, X?, Y?) ; ---- print the byte in A in decimal form, with left padding 0s (3 positions total)
asm { asm {
jsr byte2decimal jsr byte2decimal
pha pha
@ -472,9 +480,10 @@ print_byte_decimal0 ; (ubyte: A) -> (A?, X?, Y?)
pla pla
jmp c64.CHROUT jmp c64.CHROUT
} }
}
; ---- print the byte in A in decimal form, without left padding 0s subx print_byte_decimal (ubyte: A) -> (A?, X?, Y?) {
print_byte_decimal ; (ubyte: A) -> (A?, X?, Y?) ; ---- print the byte in A in decimal form, without left padding 0s
asm { asm {
jsr byte2decimal jsr byte2decimal
pha pha
@ -489,9 +498,10 @@ print_byte_decimal ; (ubyte: A) -> (A?, X?, Y?)
+ pla + pla
jmp c64.CHROUT jmp c64.CHROUT
} }
}
; ---- print the byte in A in hex form subx print_byte_hex (ubyte: A) -> (A?, X?, Y?) {
print_byte_hex ; (ubyte: A) -> (A?, X?, Y?) ; ---- print the byte in A in hex form
asm { asm {
jsr byte2hex jsr byte2hex
txa txa
@ -499,9 +509,11 @@ print_byte_hex ; (ubyte: A) -> (A?, X?, Y?)
tya tya
jmp c64.CHROUT 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 { asm {
jsr word2decimal jsr word2decimal
lda word2decimal_output lda word2decimal_output
@ -515,9 +527,11 @@ print_word_decimal0 ; (address: XY) -> (A?, X?, Y?)
lda word2decimal_output+4 lda word2decimal_output+4
jmp c64.CHROUT 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 { asm {
jsr word2decimal jsr word2decimal
ldy #0 ldy #0
@ -547,3 +561,5 @@ _pr_decimal
rts rts
} }
} }
}

View File

@ -336,15 +336,13 @@ proc_results = sequence of <register> names that specify in which register(s) th
example: "subx CLOSE (logical: A) -> (A?, X?, Y?) $FFC3" 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. preserve [regs] { .... } adds register preservation around the containing code default = all 3 regs, or specify which.
fcall -> fastcall, doesn't do register preservations nopreserve [regs] { .... } removes register preservation on all statements in the block that would otherwise have it.
call -> as before, alsways does it, even when already in isolate block
@todo document user defined subroutines
@todo user defined subroutines
SUBROUTINE CALLS SUBROUTINE CALLS

View File

@ -79,8 +79,33 @@ _loop block2.zpw1 ++
return 155,2,%00000101 ; will end up in A, X, Y return 155,2,%00000101 ; will end up in A, X, Y
} }
~ block2 { ~ block2 {
return
subx memsub () -> () = $fff2
subx customsub (Y)->() {
asm {
nop
nop
lda #99
nop
nop
}
A=2
X=33
goto fidget.subroutine()
}
somelabel1222 somelabel1222
customsub(2)
return return
var zp1 var zp1

View File

@ -60,8 +60,7 @@ start
c64.CHROUT('0') c64.CHROUT('0')
c64.CHROUT('1') c64.CHROUT('1')
c64.CHROUT('2') c64.CHROUT('2')
XY = hello c64util.print_string(hello)
c64util.print_string()
goto c64.CHROUT('!') goto c64.CHROUT('!')

View File

@ -14,52 +14,36 @@ start
.ptext "hello-pimmediate!{cr}" .ptext "hello-pimmediate!{cr}"
} }
A = 19 c64util.print_byte_decimal0 ! (19)
c64util.print_byte_decimal0 ! ()
c64.CHROUT ! (13) c64.CHROUT ! (13)
A = 19 c64util.print_byte_decimal ! (19)
c64util.print_byte_decimal ! ()
c64.CHROUT ! (13) c64.CHROUT ! (13)
X = $01 c64util.print_word_decimal0 ! ($0102)
Y = $02
c64util.print_word_decimal0 ! ()
c64.CHROUT ! (13) c64.CHROUT ! (13)
X = $01 c64util.print_word_decimal ! ($0102)
Y = $02
c64util.print_word_decimal ! ()
c64.CHROUT ! (13) c64.CHROUT ! (13)
return return
start2 start2
global2.make_screen_black() global2.make_screen_black()
c64.CLEARSCR() c64.CLEARSCR()
XY = greeting c64util.print_string(greeting)
c64util.print_string() c64util.print_pstring(p_greeting)
XY = p_greeting c64util.print_byte_decimal(0)
c64util.print_pstring() c64util.print_byte_hex(0)
A = 0
c64util.print_byte_decimal()
A = 0
c64util.print_byte_hex()
c64.CHROUT(13) c64.CHROUT(13)
c64util.print_byte_decimal() c64util.print_byte_decimal(13)
A = 13 c64util.print_byte_hex(13)
c64util.print_byte_hex()
c64.CHROUT(13) c64.CHROUT(13)
A = 255 c64util.print_byte_decimal(255)
c64util.print_byte_decimal() c64util.print_byte_hex(254)
A = 254 c64util.print_byte_hex(129)
c64util.print_byte_hex()
A = 129
c64util.print_byte_hex()
c64.CHROUT(13) c64.CHROUT(13)
c64.CHROUT(13) c64.CHROUT(13)
X = 1 c64util.print_word_decimal($0100)
Y = 0
c64util.print_word_decimal()
c64.CHROUT(13) c64.CHROUT(13)
return return