improved subroutine param ast checks, added asm for Carry parameter

This commit is contained in:
Irmen de Jong 2019-07-29 00:33:19 +02:00
parent b0dda08e74
commit 8e8c112ff0
6 changed files with 133 additions and 47 deletions

View File

@ -202,12 +202,12 @@ asmsub CINT () clobbers(A,X,Y) = $FF81 ; (alias: SCINIT) initialize scree
asmsub IOINIT () clobbers(A, X) = $FF84 ; initialize I/O devices (CIA, SID, IRQ)
asmsub RAMTAS () clobbers(A,X,Y) = $FF87 ; initialize RAM, tape buffer, screen
asmsub RESTOR () clobbers(A,X,Y) = $FF8A ; restore default I/O vectors
asmsub VECTOR (ubyte dir @ Pc, uword userptr @ XY) clobbers(A,Y) = $FF8D ; read/set I/O vector table
asmsub VECTOR (uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) = $FF8D ; read/set I/O vector table
asmsub SETMSG (ubyte value @ A) = $FF90 ; set Kernal message control flag
asmsub SECOND (ubyte address @ A) clobbers(A) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
asmsub TKSA (ubyte address @ A) clobbers(A) = $FF96 ; (alias: TALKSA) send secondary address after TALK
asmsub MEMTOP (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF99 ; read/set top of memory pointer
asmsub MEMBOT (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF9C ; read/set bottom of memory pointer
asmsub MEMTOP (uword address @ XY, ubyte dir @ Pc) -> uword @ XY = $FF99 ; read/set top of memory pointer
asmsub MEMBOT (uword address @ XY, ubyte dir @ Pc) -> uword @ XY = $FF9C ; read/set bottom of memory pointer
asmsub SCNKEY () clobbers(A,X,Y) = $FF9F ; scan the keyboard
asmsub SETTMO (ubyte timeout @ A) = $FFA2 ; set time-out flag for IEEE bus
asmsub ACPTR () -> ubyte @ A = $FFA5 ; (alias: IECIN) input byte from serial bus
@ -235,7 +235,7 @@ asmsub GETIN () clobbers(X,Y) -> ubyte @ A = $FFE4 ; (via 810 ($32A)) get a
asmsub CLALL () clobbers(A,X) = $FFE7 ; (via 812 ($32C)) close all files
asmsub UDTIM () clobbers(A,X) = $FFEA ; update the software clock
asmsub SCREEN () -> ubyte @ X, ubyte @ Y = $FFED ; read number of screen rows and columns
asmsub PLOT (ubyte dir @ Pc, ubyte col @ Y, ubyte row @ X) -> ubyte @ X, ubyte @ Y = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
asmsub PLOT (ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
asmsub IOBASE () -> uword @ XY = $FFF3 ; read base address of I/O devices
; ---- end of C64 kernal routines ----

View File

@ -821,7 +821,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}
asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
stx c64.SCRATCH_ZPREGX
@ -840,7 +840,7 @@ asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
}
asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
stx c64.SCRATCH_ZPREGX
@ -862,7 +862,7 @@ asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
}
asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
@ -875,7 +875,7 @@ asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
}
asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{

View File

@ -303,6 +303,18 @@ internal class AstChecker(private val program: Program,
if(subroutine.asmClobbers.intersect(regCounts.keys).isNotEmpty())
err("a return register is also in the clobber list")
if(subroutine.statements.any{it !is InlineAssembly})
err("asmsub can only contain inline assembly (%asm)")
val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc
if(statusFlagsNoCarry.isNotEmpty())
err("can only use Carry as status flag parameter")
val carryParameter = subroutine.asmParameterRegisters.singleOrNull { it.statusflag==Statusflag.Pc }
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
err("carry parameter has to come last")
} else {
// TODO: non-asm subroutines can only take numeric arguments for now. (not strings and arrays) Maybe this can be improved now that we have '&' ?
// the way string params are treated is almost okay (their address is passed) but the receiving subroutine treats it as an integer rather than referring back to the original string.

View File

@ -11,7 +11,7 @@ class AssemblyProgram(val name: String) {
companion object {
// reserved by the 64tass assembler (on top of prog8"s own reserved names)
val reservedNames = setOf("bit", "bits", "bool", "bytes", "code", "dict", "gap", "int", "list", "tuple", "type",
val reservedNames = setOf("bits", "bool", "bytes", "code", "dict", "gap", "int", "list", "tuple", "type",
"trunc", " frac", "cbrt", "log10", "log", "exp", "pow", "asin", "sinh", "acos", "cosh", "tanh", "hypot",
"atan2", "sign", "binary", "format", "random", "range", "repr", "size", "sort")

View File

@ -698,18 +698,62 @@ internal class AsmGen2(val program: Program,
when {
stack==true -> TODO("param on stack")
statusflag!=null -> {
if (statusflag == Statusflag.Pc) TODO("carry flag param")
if (statusflag == Statusflag.Pc) {
// TODO this param needs to be set last right before the jsr
when(value) {
is NumericLiteralValue -> {
val carrySet = value.number.toInt() != 0
out(if(carrySet) " sec" else " clc")
}
is IdentifierReference -> {
val sourceName = asmIdentifierName(value)
out("""
lda $sourceName
beq +
sec
bcs ++
+ clc
+
""")
}
is RegisterExpr -> {
when(value.register) {
Register.A -> out(" cmp #0")
Register.X -> out(" txa")
Register.Y -> out(" tya")
}
out("""
beq +
sec
bcs ++
+ clc
+
""")
}
else -> {
translateExpression(value)
out("""
inx
lda $ESTACK_LO_HEX,x
beq +
sec
bcs ++
+ clc
+
""")
}
}
}
else throw AssemblyError("can only use Carry as status flag parameter")
}
register!=null && register.name.length==1 -> {
val literal = value as? NumericLiteralValue
when {
literal!=null -> {
when (value) {
is NumericLiteralValue -> {
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
target.linkParents(value.parent)
assignFromByteConstant(target, literal.number.toShort())
assignFromByteConstant(target, value.number.toShort())
}
value is IdentifierReference -> {
is IdentifierReference -> {
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
target.linkParents(value.parent)
assignFromByteVariable(target, value)
@ -727,30 +771,34 @@ internal class AsmGen2(val program: Program,
}
register!=null && register.name.length==2 -> {
// register pair as a 16-bit value (only possible for subroutine parameters)
val literal = value as? NumericLiteralValue
if(literal!=null) {
// optimize when the argument is a constant literal
val hex = literal.number.toHex()
if (register == RegisterOrPair.AX) out(" lda #<$hex | ldx #>$hex")
else if (register == RegisterOrPair.AY) out(" lda #<$hex | ldy #>$hex")
else if (register == RegisterOrPair.XY) out(" ldx #<$hex | ldy #>$hex")
} else if(value is AddressOf) {
// optimize when the argument is an address of something
val sourceName = asmIdentifierName(value.identifier)
if (register == RegisterOrPair.AX) out(" lda #<$sourceName | ldx #>$sourceName")
else if (register == RegisterOrPair.AY) out(" lda #<$sourceName | ldy #>$sourceName")
else if (register == RegisterOrPair.XY) out(" ldx #<$sourceName | ldy #>$sourceName")
} else if(value is IdentifierReference) {
val sourceName = asmIdentifierName(value)
if (register == RegisterOrPair.AX) out(" lda #<$sourceName | ldx #>$sourceName")
else if (register == RegisterOrPair.AY) out(" lda #<$sourceName | ldy #>$sourceName")
else if (register == RegisterOrPair.XY) out(" ldx #<$sourceName | ldy #>$sourceName")
} else {
translateExpression(value)
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
throw AssemblyError("can't use X register here - use a variable")
else if (register == RegisterOrPair.AY)
out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
when (value) {
is NumericLiteralValue -> {
// optimize when the argument is a constant literal
val hex = value.number.toHex()
if (register == RegisterOrPair.AX) out(" lda #<$hex | ldx #>$hex")
else if (register == RegisterOrPair.AY) out(" lda #<$hex | ldy #>$hex")
else if (register == RegisterOrPair.XY) out(" ldx #<$hex | ldy #>$hex")
}
is AddressOf -> {
// optimize when the argument is an address of something
val sourceName = asmIdentifierName(value.identifier)
if (register == RegisterOrPair.AX) out(" lda #<$sourceName | ldx #>$sourceName")
else if (register == RegisterOrPair.AY) out(" lda #<$sourceName | ldy #>$sourceName")
else if (register == RegisterOrPair.XY) out(" ldx #<$sourceName | ldy #>$sourceName")
}
is IdentifierReference -> {
val sourceName = asmIdentifierName(value)
if (register == RegisterOrPair.AX) out(" lda #<$sourceName | ldx #>$sourceName")
else if (register == RegisterOrPair.AY) out(" lda #<$sourceName | ldy #>$sourceName")
else if (register == RegisterOrPair.XY) out(" ldx #<$sourceName | ldy #>$sourceName")
}
else -> {
translateExpression(value)
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
throw AssemblyError("can't use X register here - use a variable")
else if (register == RegisterOrPair.AY)
out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
}
}
}
}

View File

@ -5,9 +5,30 @@
~ main {
sub start() {
ubyte zz
carry(0, 0)
carry(1, 1)
A=0
carry(2, A)
A=1
carry(3, A)
zz=0
carry(4, zz)
zz=122
carry(5, zz)
carry(6, zz-122)
carry(7, zz+34)
ubyte endX = X
if endX == 255
c64scr.print("\n\nstack x ok!\n")
else
c64scr.print("\n\nerror: stack x != 255 !\n")
carry(1)
carry(0)
; ubyte bb = @($d020)+4
; ubyte bb2 = @($d020+A)+4
;
@ -18,12 +39,17 @@
; subje(bb+43)
}
sub carry(ubyte cc) {
A=cc
if A!=0
c64scr.print("carry set\n")
else
c64scr.print("carry clear\n")
asmsub carry(byte offset @Y, ubyte cc @Pc) {
%asm {{
bcc +
lda #1
sta $0400,y
rts
+
lda #0
sta $0400,y
rts
}}
}
sub subje(ubyte arg) {