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("{: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 "<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")
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")
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<parameters>[\w\s:,]*)\)"
r"\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:
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<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:
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

View File

@ -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)

View File

@ -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
}
}
}

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"
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

View File

@ -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

View File

@ -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('!')

View File

@ -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