push,pushw,pop and popw are no longer built-in functions but regular routines in `sys`

This commit is contained in:
Irmen de Jong 2023-12-26 14:47:31 +01:00
parent 38a22fbc99
commit 0d44492086
23 changed files with 243 additions and 146 deletions

View File

@ -125,10 +125,6 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
"rsave" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),

View File

@ -3059,30 +3059,16 @@ $repeatLabel""")
}
}
internal fun popCpuStack(dt: DataType, target: IPtVariable, scope: IPtSubroutine?) {
// note: because A is pushed first so popped last, saving A is often not required here.
val targetAsmSub = (target as PtNode).definingAsmSub()
if(targetAsmSub != null) {
val parameter = targetAsmSub.parameters.first { it.second.name==target.name }
popCpuStack(targetAsmSub, parameter.second, parameter.first)
return
}
val scopedName = when(target) {
is PtConstant -> target.scopedName
is PtMemMapped -> target.scopedName
is PtVariable -> target.scopedName
else -> throw AssemblyError("weird target var")
}
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, this, target.type, scope, target.position, variableAsmName = asmVariableName(scopedName))
internal fun popCpuStack(dt: DataType) {
if (dt in ByteDatatypes) {
out(" pla")
assignRegister(RegisterOrPair.A, tgt)
} else {
} else if (dt in WordDatatypes) {
if (isTargetCpu(CpuType.CPU65c02))
out(" ply | pla")
else
out(" pla | tay | pla")
assignRegister(RegisterOrPair.AY, tgt)
} else {
throw AssemblyError("can't pop type $dt")
}
}

View File

@ -63,24 +63,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" pla")
}
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
"pop" -> {
require(fcall.args[0] is PtIdentifier) {
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
}
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub())
}
"popw" -> {
require(fcall.args[0] is PtIdentifier) {
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
}
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
}
"rsave" -> funcRsave()
"rrestore" -> funcRrestore()
"cmp" -> funcCmp(fcall)

View File

@ -20,10 +20,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"pop" -> funcPop(call)
"popw" -> funcPopw(call)
"push" -> funcPush(call)
"pushw" -> funcPushw(call)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> funcCallfar(call)
"call" -> funcCall(call)
@ -278,44 +274,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val code = IRCodeChunk(null, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return ExpressionCodeResult(result, IRDataType.BYTE, reg, -1)
}
private fun funcPopw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val code = IRCodeChunk(null, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return ExpressionCodeResult(result, IRDataType.WORD, reg, -1)
}
private fun funcPush(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcPushw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)

View File

@ -269,6 +269,34 @@ save_SCRATCH_ZPWORD2 .word 0
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {

View File

@ -774,6 +774,34 @@ _longcopy
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {

View File

@ -772,6 +772,33 @@ _longcopy
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {

View File

@ -114,10 +114,10 @@ psg {
; cx16.r0 = the volume word (volume scaled by 256)
; cx16.r1L = the voice number
; cx16.r2L = attack value
pushw(cx16.r0)
push(cx16.r1L)
push(cx16.r2L)
pushw(cx16.r9)
sys.pushw(cx16.r0)
sys.push(cx16.r1L)
sys.push(cx16.r2L)
sys.pushw(cx16.r9)
; calculate new volumes
for cx16.r1L in 0 to 15 {
when envelope_states[cx16.r1L] {
@ -166,10 +166,10 @@ psg {
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
}
cx16.restore_vera_context()
popw(cx16.r9)
pop(cx16.r2L)
pop(cx16.r1L)
popw(cx16.r0)
cx16.r9 = sys.popw()
cx16.r2L = sys.pop()
cx16.r1L = sys.pop()
cx16.r0 = sys.popw()
return true ; run the system IRQ handler afterwards
}

View File

@ -1615,4 +1615,31 @@ save_SCRATCH_ZPWORD2 .word 0
ldy #>prog8_program_end
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
phy
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
ply
pla
}}
}
}

View File

@ -361,6 +361,34 @@ save_SCRATCH_ZPWORD2 .word 0
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {

View File

@ -126,6 +126,38 @@ sys {
returnr.b r0
}}
}
sub push(ubyte b) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.b r65535,sys.push.b
push.b r65535
}}
}
sub pushw(uword w) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.w r65535,sys.pushw.w
push.w r65535
}}
}
sub pop() -> ubyte {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.b r65535
returnr.b r65535
}}
}
sub popw() -> uword {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.w r65535
returnr.w r65535
}}
}
}
cx16 {

View File

@ -1216,19 +1216,6 @@ internal class AstChecker(private val program: Program,
errors.err("sorting a floating point array is not supported", functionCallStatement.args.first().position)
}
}
else if(funcName[0] in arrayOf("pop", "popw")) {
// can only pop into a variable, that has to have the correct type
val idref = functionCallStatement.args[0]
if(idref !is IdentifierReference) {
if(idref is TypecastExpression) {
val passByRef = idref.expression.inferType(program).isPassByReference
if(idref.type!=DataType.UWORD || !passByRef)
errors.err("invalid argument to pop, must be a variable with the correct type: ${functionCallStatement.args.first()}", functionCallStatement.args.first().position)
} else {
errors.err("invalid argument to pop, must be a variable with the correct type: ${functionCallStatement.args.first()}", functionCallStatement.args.first().position)
}
}
}
else if(funcName[0].startsWith("divmod")) {
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression) {
errors.err("arguments must be all ubyte or all uword", functionCallStatement.position)

View File

@ -2,7 +2,6 @@ package prog8tests
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Assignment
import prog8.ast.statements.FunctionCallStatement
@ -69,19 +68,6 @@ class TestBuiltinFunctions: FunSpec({
conv.returns.reg shouldBe null
}
test("push pop") {
val src="""
main {
sub start () {
pushw(cx16.r0)
push(cx16.r1L)
pop(cx16.r1L)
popw(cx16.r0)
}
}"""
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
}
test("certain builtin functions should be compile time evaluated") {
val src="""
main {

View File

@ -693,9 +693,9 @@ main {
sub start() {
uword variable
pushw(variable)
pushw(handler)
pushw(&handler)
sys.pushw(variable)
sys.pushw(handler)
sys.pushw(&handler)
handler(variable)
handler(handler)
handler(&handler)

View File

@ -16,6 +16,7 @@ import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
import kotlin.io.path.readText
class TestVariousCodeGen: FunSpec({
test("bool to byte cast in expression is correct") {
@ -358,4 +359,24 @@ main {
compileText(VMTarget(), true, src, writeAssembly = false) shouldNotBe null
compileText(Cx16Target(), true, src, writeAssembly = false) shouldNotBe null
}
test("push pop are inlined also with noopt") {
val text = """
main {
sub start() {
sys.push(11)
sys.pushw(2222)
cx16.r2++
cx16.r1 = sys.popw()
cx16.r0L = sys.pop()
}
}"""
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
val assemblyFile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".asm")
val assembly = assemblyFile.readText()
assembly shouldContain "inlined routine follows: push"
assembly shouldContain "inlined routine follows: pushw"
assembly shouldContain "inlined routine follows: pop"
assembly shouldContain "inlined routine follows: popw"
}
})

View File

@ -140,6 +140,20 @@ sys (part of syslib)
It stores and restores the values of the internal prog8 variables.
This allows other code to run that might clobber these values temporarily.
``push (value)``
pushes a byte value on the CPU hardware stack. Low-level function that should normally not be used.
``pushw (value)``
pushes a 16-bit word value on the CPU hardware stack. Low-level function that should normally not be used.
``pop ()``
pops a byte value off the CPU hardware stack and returns it.
Low-level function that should normally not be used.
``popw ()``
pops a 16-bit word value off the CPU hardware stack and returns it.
Low-level function that should normally not be used.
conv
----

View File

@ -898,20 +898,6 @@ pokemon (address, value)
Like poke(), but also returns the previous value in the given address.
Also doesn't have anything to do with a certain video game.
push (value)
pushes a byte value on the CPU hardware stack. Low-level function that should normally not be used.
pushw (value)
pushes a 16-bit word value on the CPU hardware stack. Low-level function that should normally not be used.
pop (variable)
pops a byte value off the CPU hardware stack into the given variable. Only variables can be used.
Low-level function that should normally not be used.
popw (value)
pops a 16-bit word value off the CPU hardware stack into the given variable. Only variables can be used.
Low-level function that should normally not be used.
rol (x)
Rotate the bits in x (byte or word) one position to the left.
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,

View File

@ -1,14 +1,25 @@
%import textio
%zeropage dontuse
;;%import floats
%zeropage basicsafe
main {
sub start() {
uword empty
uword block = memory("block", 500, 0)
sys.push(11)
sys.pushw(2222)
;;floats.pushf(floats.π)
cx16.r2++
txt.print_uwhex(&empty, true)
;;float pi = floats.popf()
;;floats.print_f(pi)
;;txt.nl()
cx16.r1 = sys.popw()
cx16.r0L = sys.pop()
txt.print_ub(cx16.r0L)
txt.nl()
txt.print_uwhex(block, true)
txt.print_uw(cx16.r1)
txt.nl()
}
}

View File

@ -1,4 +1,4 @@
<filetype binary="false" default_extension="prog8" description="Prog8 source file" name="Prog8">
<filetype binary="false" default_extension="p8" description="Prog8 source file" name="Prog8">
<highlighting>
<options>
<option name="LINE_COMMENT" value=";" />
@ -14,10 +14,10 @@
<keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%option;%output;%zeropage;%zpallowed;%zpreserved;atascii:;default:;iso:;petscii:;sc:" />
<keywords3 keywords="@requirezp;@shared;@split;@zp;bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;all;any;call;callfar;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;pop;popw;push;pushw;reverse;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sort;sqrt;swap;|&gt;" />
<keywords4 keywords="abs;all;any;call;callfar;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;reverse;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sort;sqrt;swap;|&gt;" />
</highlighting>
<extensionMap>
<mapping ext="prog8" />
<mapping ext="p8" />
<mapping ext="prog8" />
</extensionMap>
</filetype>

View File

@ -27,7 +27,7 @@
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split requirezp</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">abs all any call callfar clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef push pushw pop popw rsave rsavex rrestore rrestorex reverse rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sort sqrtw swap</Keywords>
<Keywords name="Keywords4">abs all any call callfar clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex reverse rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sort sqrtw swap</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>

View File

@ -13,7 +13,7 @@ syn keyword prog8BuiltInFunc sgn sqrtw
syn keyword prog8BuiltInFunc any all len reverse sort
" Miscellaneous functions
syn keyword prog8BuiltInFunc cmp divmod lsb msb mkword min max peek peekw peekf poke pokew pokef push pushw pop popw rsave rsavex rrestore rrestorex
syn keyword prog8BuiltInFunc cmp divmod lsb msb mkword min max peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex
syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof setlsb setmsb
syn keyword prog8BuiltInFunc swap memory call callfar clamp

View File

@ -43,7 +43,7 @@ class VmProgramLoader {
block.children.forEach { child ->
when(child) {
is IRAsmSubroutine -> throw IRParseException("vm does not support asmsubs (use normal sub): ${child.label}")
is IRAsmSubroutine -> throw IRParseException("vm does not support non-inlined asmsubs (use normal sub): ${child.label}")
is IRCodeChunk -> programChunks += child
is IRInlineAsmChunk -> throw IRParseException("encountered unconverted inline assembly chunk")
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM")

View File

@ -85,7 +85,7 @@ class TestVm: FunSpec( {
)
block += startSub
program.addBlock(block)
shouldThrowWithMessage<IRParseException>("vm does not support asmsubs (use normal sub): main.asmstart") {
shouldThrowWithMessage<IRParseException>("vm does not support non-inlined asmsubs (use normal sub): main.asmstart") {
VirtualMachine(program)
}
}