mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
argument type casts for builtin functions, added memset, tweaked memcopy/memset assembly a bit
This commit is contained in:
parent
dd02d97db4
commit
d37c9d1680
@ -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 {{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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,8 +1757,33 @@ 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) {
|
||||
// 60hz IRQ handling
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user