argument type casts for builtin functions, added memset, tweaked memcopy/memset assembly a bit

This commit is contained in:
Irmen de Jong 2019-01-23 00:19:29 +01:00
parent dd02d97db4
commit d37c9d1680
9 changed files with 251 additions and 158 deletions

View File

@ -292,134 +292,6 @@ asmsub str2word(str string @ AY) -> clobbers() -> (word @ AY) {
; @todo string to 32 bit unsigned integer http://www.6502.org/source/strings/ascii-to-32bit.html
%asm {{
; copy memory UP from (SCRATCH_ZPWORD1) to (SCRATCH_ZPWORD2) of length X/Y (16-bit, X=lo, Y=hi)
; clobbers register A,X,Y
memcopy16_up .proc
source = SCRATCH_ZPWORD1
dest = SCRATCH_ZPWORD2
length = SCRATCH_ZPB1 ; (and SCRATCH_ZPREG)
stx length
sty length+1
ldx length ; move low byte of length into X
bne + ; jump to start if X > 0
dec length ; subtract 1 from length
+ ldy #0 ; set Y to 0
- lda (source),y ; set A to whatever (source) points to offset by Y
sta (dest),y ; move A to location pointed to by (dest) offset by Y
iny ; increment Y
bne + ; if Y<>0 then (rolled over) then still moving bytes
inc source+1 ; increment hi byte of source
inc dest+1 ; increment hi byte of dest
+ dex ; decrement X (lo byte counter)
bne - ; if X<>0 then move another byte
dec length ; we've moved 255 bytes, dec length
bpl - ; if length is still positive go back and move more
rts ; done
.pend
; copy memory UP from (SCRATCH_ZPWORD1) to (AY) with length X (1 to 256, 0 meaning 256)
; destination must not overlap, or be before start, then overlap is possible.
; clobbers A, X, Y
memcopy .proc
sta c64.SCRATCH_ZPWORD2
sty c64.SCRATCH_ZPWORD2+1
ldy #0
- lda (c64.SCRATCH_ZPWORD1), y
sta (c64.SCRATCH_ZPWORD2), y
iny
dex
bne -
rts
.pend
; fill memory from (SCRATCH_ZPWORD1), length XY, with value in A.
; clobbers X, Y
memset .proc
stx SCRATCH_ZPB1
sty SCRATCH_ZPREG
ldy #0
ldx SCRATCH_ZPREG
beq _lastpage
_fullpage sta (SCRATCH_ZPWORD1),y
iny
bne _fullpage
inc SCRATCH_ZPWORD1+1 ; next page
dex
bne _fullpage
_lastpage ldy SCRATCH_ZPB1
beq +
- dey
sta (SCRATCH_ZPWORD1),y
bne -
+ rts
.pend
; fill memory from (SCRATCH_ZPWORD1) number of words in SCRATCH_ZPWORD2, with word value in AY.
; clobbers A, X, Y
memsetw .proc
sta _mod1+1 ; self-modify
sty _mod1b+1 ; self-modify
sta _mod2+1 ; self-modify
sty _mod2b+1 ; self-modify
ldx SCRATCH_ZPWORD1
stx SCRATCH_ZPB1
ldx SCRATCH_ZPWORD1+1
inx
stx SCRATCH_ZPREG ; second page
ldy #0
ldx SCRATCH_ZPWORD2+1
beq _lastpage
_fullpage
_mod1 lda #0 ; self-modified
sta (SCRATCH_ZPWORD1),y ; first page
sta (SCRATCH_ZPB1),y ; second page
iny
_mod1b lda #0 ; self-modified
sta (SCRATCH_ZPWORD1),y ; first page
sta (SCRATCH_ZPB1),y ; second page
iny
bne _fullpage
inc SCRATCH_ZPWORD1+1 ; next page pair
inc SCRATCH_ZPWORD1+1 ; next page pair
inc SCRATCH_ZPB1+1 ; next page pair
inc SCRATCH_ZPB1+1 ; next page pair
dex
bne _fullpage
_lastpage ldx SCRATCH_ZPWORD2
beq _done
ldy #0
-
_mod2 lda #0 ; self-modified
sta (SCRATCH_ZPWORD1), y
inc SCRATCH_ZPWORD1
bne _mod2b
inc SCRATCH_ZPWORD1+1
_mod2b lda #0 ; self-modified
sta (SCRATCH_ZPWORD1), y
inc SCRATCH_ZPWORD1
bne +
inc SCRATCH_ZPWORD1+1
+ dex
bne -
_done rts
.pend
}}
asmsub set_irqvec_excl() -> clobbers(A) -> () {
%asm {{

View File

@ -1012,14 +1012,172 @@ func_memcopy .proc
lda c64.ESTACK_HI+2,x
sta c64.SCRATCH_ZPWORD1+1
lda c64.ESTACK_LO+1,x
ldy c64.ESTACK_HI+1,x
pha
sta c64.SCRATCH_ZPWORD2
lda c64.ESTACK_HI+1,x
sta c64.SCRATCH_ZPWORD2+1
lda c64.ESTACK_LO,x
tax
pla
jsr c64utils.memcopy
ldy #0
- lda (c64.SCRATCH_ZPWORD1), y
sta (c64.SCRATCH_ZPWORD2), y
iny
dex
bne -
ldx c64.SCRATCH_ZPREGX
inx
inx
rts
.pend
func_memset .proc
; note: clobbers A,Y
inx
stx c64.SCRATCH_ZPREGX
lda c64.ESTACK_LO+2,x
sta c64.SCRATCH_ZPWORD1
lda c64.ESTACK_HI+2,x
sta c64.SCRATCH_ZPWORD1+1
lda c64.ESTACK_LO+1,x
sta c64.SCRATCH_ZPB1
ldy c64.ESTACK_HI+1,x
lda c64.ESTACK_LO,x
ldx c64.SCRATCH_ZPB1
jsr memset
ldx c64.SCRATCH_ZPREGX
inx
inx
rts
.pend
func_memsetw .proc
; note: clobbers A,Y
; -- fill memory from (SCRATCH_ZPWORD1) number of words in SCRATCH_ZPWORD2, with word value in AY.
inx
stx c64.SCRATCH_ZPREGX
lda c64.ESTACK_LO+2,x
sta c64.SCRATCH_ZPWORD1
lda c64.ESTACK_HI+2,x
sta c64.SCRATCH_ZPWORD1+1
lda c64.ESTACK_LO+1,x
sta c64.SCRATCH_ZPWORD2
lda c64.ESTACK_HI+1,x
sta c64.SCRATCH_ZPWORD2+1
lda c64.ESTACK_LO,x
ldy c64.ESTACK_HI,x
jsr memsetw
ldx c64.SCRATCH_ZPREGX
inx
inx
rts
.pend
memcopy16_up .proc
; -- copy memory UP from (SCRATCH_ZPWORD1) to (SCRATCH_ZPWORD2) of length X/Y (16-bit, X=lo, Y=hi)
; clobbers register A,X,Y
source = c64.SCRATCH_ZPWORD1
dest = c64.SCRATCH_ZPWORD2
length = c64.SCRATCH_ZPB1 ; (and SCRATCH_ZPREG)
stx length
sty length+1
ldx length ; move low byte of length into X
bne + ; jump to start if X > 0
dec length ; subtract 1 from length
+ ldy #0 ; set Y to 0
- lda (source),y ; set A to whatever (source) points to offset by Y
sta (dest),y ; move A to location pointed to by (dest) offset by Y
iny ; increment Y
bne + ; if Y<>0 then (rolled over) then still moving bytes
inc source+1 ; increment hi byte of source
inc dest+1 ; increment hi byte of dest
+ dex ; decrement X (lo byte counter)
bne - ; if X<>0 then move another byte
dec length ; we've moved 255 bytes, dec length
bpl - ; if length is still positive go back and move more
rts ; done
.pend
memset .proc
; -- fill memory from (SCRATCH_ZPWORD1), length XY, with value in A.
; clobbers X, Y
stx c64.SCRATCH_ZPB1
sty c64.SCRATCH_ZPREG
ldy #0
ldx c64.SCRATCH_ZPREG
beq _lastpage
_fullpage sta (c64.SCRATCH_ZPWORD1),y
iny
bne _fullpage
inc c64.SCRATCH_ZPWORD1+1 ; next page
dex
bne _fullpage
_lastpage ldy c64.SCRATCH_ZPB1
beq +
- dey
sta (c64.SCRATCH_ZPWORD1),y
bne -
+ rts
.pend
memsetw .proc
; -- fill memory from (SCRATCH_ZPWORD1) number of words in SCRATCH_ZPWORD2, with word value in AY.
; clobbers A, X, Y
sta _mod1+1 ; self-modify
sty _mod1b+1 ; self-modify
sta _mod2+1 ; self-modify
sty _mod2b+1 ; self-modify
ldx c64.SCRATCH_ZPWORD1
stx c64.SCRATCH_ZPB1
ldx c64.SCRATCH_ZPWORD1+1
inx
stx c64.SCRATCH_ZPREG ; second page
ldy #0
ldx c64.SCRATCH_ZPWORD2+1
beq _lastpage
_fullpage
_mod1 lda #0 ; self-modified
sta (c64.SCRATCH_ZPWORD1),y ; first page
sta (c64.SCRATCH_ZPB1),y ; second page
iny
_mod1b lda #0 ; self-modified
sta (c64.SCRATCH_ZPWORD1),y ; first page
sta (c64.SCRATCH_ZPB1),y ; second page
iny
bne _fullpage
inc c64.SCRATCH_ZPWORD1+1 ; next page pair
inc c64.SCRATCH_ZPWORD1+1 ; next page pair
inc c64.SCRATCH_ZPB1+1 ; next page pair
inc c64.SCRATCH_ZPB1+1 ; next page pair
dex
bne _fullpage
_lastpage ldx c64.SCRATCH_ZPWORD2
beq _done
ldy #0
-
_mod2 lda #0 ; self-modified
sta (c64.SCRATCH_ZPWORD1), y
inc c64.SCRATCH_ZPWORD1
bne _mod2b
inc c64.SCRATCH_ZPWORD1+1
_mod2b lda #0 ; self-modified
sta (c64.SCRATCH_ZPWORD1), y
inc c64.SCRATCH_ZPWORD1
bne +
inc c64.SCRATCH_ZPWORD1+1
+ dex
bne -
_done rts
.pend

View File

@ -51,6 +51,10 @@ enum class DataType {
ARRAY_W -> targetType == UWORD
ARRAY_F -> targetType == UWORD
}
fun assignableTo(targetTypes: Set<DataType>) = targetTypes.any { this.assignableTo(it) }
}
enum class Register {

View File

@ -784,8 +784,9 @@ class AstChecker(private val namespace: INameScope,
else {
for (arg in args.withIndex().zip(func.parameters)) {
val argDt=arg.first.value.resultingDatatype(namespace, heap)
if(argDt !in arg.second.possibleDatatypes)
checkResult.add(ExpressionError("builtin function argument ${arg.first.index+1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
if(argDt!=null && !argDt.assignableTo(arg.second.possibleDatatypes)) {
checkResult.add(ExpressionError("builtin function argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
}
}
}
} else if(target is Subroutine) {

View File

@ -6,6 +6,7 @@ import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.compiler.intermediate.branchOpcodes
import prog8.functions.BuiltinFunctions
import prog8.optimizing.same
import prog8.parser.tryGetEmbeddedResource
import prog8.stackvm.Syscall
@ -685,6 +686,17 @@ private class StatementTranslator(private val prog: IntermediateProgram,
}
}
private fun tryConvertType(givenDt: DataType, targetDt: DataType): Boolean {
return try {
convertType(givenDt, targetDt)
true
} catch (x: CompilerException) {
false
}
}
private fun convertType(givenDt: DataType, targetDt: DataType) {
// only WIDENS a type, never NARROWS. To avoid loss of precision.
if(givenDt==targetDt)
@ -784,7 +796,22 @@ private class StatementTranslator(private val prog: IntermediateProgram,
return
}
args.forEach { translate(it) } // place function argument(s) on the stack
val builtinFuncParams = BuiltinFunctions[funcname]?.parameters
args.forEachIndexed { index, arg ->
// place function argument(s) on the stack
translate(arg)
// cast type if needed
if(builtinFuncParams!=null) {
val paramDts = builtinFuncParams[index].possibleDatatypes
val argDt = arg.resultingDatatype(namespace, heap)!!
if(argDt !in paramDts) {
for(paramDt in paramDts.sorted())
if(tryConvertType(argDt, paramDt))
break
}
}
}
when (funcname) {
"len" -> {
// 1 argument, type determines the exact syscall to use

View File

@ -70,9 +70,17 @@ val BuiltinFunctions = mapOf(
"clear_irqd" to FunctionSignature(false, emptyList(), null),
"swap" to FunctionSignature(false, listOf(BuiltinFunctionParam("first", NumericDatatypes), BuiltinFunctionParam("second", NumericDatatypes)), null),
"memcopy" to FunctionSignature(false, listOf(
BuiltinFunctionParam("from", IntegerDatatypes + IterableDatatypes),
BuiltinFunctionParam("to", IntegerDatatypes + IterableDatatypes),
BuiltinFunctionParam("from", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("to", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("numbytes", IntegerDatatypes)), null),
"memset" to FunctionSignature(false, listOf(
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("numbytes", setOf(DataType.UWORD)),
BuiltinFunctionParam("bytevalue", setOf(DataType.UBYTE, DataType.BYTE))), null),
"memsetw" to FunctionSignature(false, listOf(
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
"vm_write_memchr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_memstr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_num" to FunctionSignature(false, listOf(BuiltinFunctionParam("number", NumericDatatypes)), null),

View File

@ -78,7 +78,9 @@ enum class Syscall(val callNr: Short) {
FUNC_SUM_UW(132),
FUNC_SUM_W(133),
FUNC_SUM_F(134),
FUNC_MEMCOPY(138)
FUNC_MEMCOPY(138),
FUNC_MEMSET(139),
FUNC_MEMSETW(140)
// note: not all builtin functions of the Prog8 language are present as functions:
// some of them are straight opcodes (such as MSB, LSB, LSL, LSR, ROL_BYTE, ROR, ROL2, ROR2, and FLT)!
@ -1755,7 +1757,32 @@ class StackVm(private var traceOutputFile: String?) {
val from = evalstack.pop().integerValue()
mem.copy(from, to, numbytes)
}
}
Syscall.FUNC_MEMSET -> {
val value = evalstack.pop()
val address = evalstack.pop().integerValue()
val numbytes = evalstack.pop().integerValue()
val bytevalue = value.integerValue().toShort()
when {
value.type==DataType.UBYTE -> for(addr in address until address+numbytes)
mem.setUByte(addr, bytevalue)
value.type==DataType.BYTE -> for(addr in address until address+numbytes)
mem.setSByte(addr, bytevalue)
else -> throw VmExecutionException("(u)byte value expected")
}
}
Syscall.FUNC_MEMSETW -> {
val value = evalstack.pop()
val address = evalstack.pop().integerValue()
val numwords = evalstack.pop().integerValue()
val wordvalue = value.integerValue()
when {
value.type==DataType.UWORD -> for(addr in address until address+numwords*2 step 2)
mem.setUWord(addr, wordvalue)
value.type==DataType.WORD -> for(addr in address until address+numwords*2 step 2)
mem.setSWord(addr, wordvalue)
else -> throw VmExecutionException("(u)word value expected")
}
} }
}
fun irq(timestamp: Long) {

View File

@ -663,6 +663,17 @@ memcopy(from, to, numbytes)
Because this function imposes some overhead to handle the parameters,
it is only faster if the number of bytes is larger than a certain threshold.
Compare the generated code to see if it was beneficial or not.
The most efficient will always be to write a specialized copy routine in assembly yourself!
memset(address, numbytes, bytevalue)
Efficiently set a part of memory to the given (u)byte value.
But the most efficient will always be to write a specialized fill routine in assembly yourself!
Note that for clearing the character screen, very fast specialized subroutines are
available in the ``c64scr`` block (part of the ``c64utils`` module)
memsetw(address, numwords, wordvalue)
Efficiently set a part of memory to the given (u)word value.
But the most efficient will always be to write a specialized fill routine in assembly yourself!
swap(x, y)
Swap the values of numerical variables (or memory locations) x and y in a fast way.

View File

@ -5,26 +5,11 @@
sub start() {
; memset($0400, $0400+40, 81)
memset($0400+40*3, 40*8, 81)
memsetw($0400+40*12, 8*40/2, $80a0)
memset($0400, 20, 33)
memcopy($0400, $0400+121, 20)
A=99
if(A<99) goto first else goto second
first:
c64scr.print("a<99 !\n")
goto next
second:
c64scr.print("wrong: a>=99 ?!\n")
next:
A=99
if(A<99) goto first2 else {
c64scr.print("wrong: a>=99 ?!\n")
}
return
first2:
c64scr.print("a<99 !\n")
return