mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 19:54:26 +00:00
more asm
This commit is contained in:
parent
1e0ce40d1d
commit
067426016d
@ -1,29 +1,5 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
|
%import mathlib
|
||||||
~ block2 $4000 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
~ block3 $3000 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
~ blockNoAddr1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
~ blockNoAddr2 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
~ block4 $6000 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
~ blockNoAddr3 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
@ -33,10 +9,29 @@ sub start() {
|
|||||||
const ubyte border_color = 2
|
const ubyte border_color = 2
|
||||||
const ubyte cursor_color = 7
|
const ubyte cursor_color = 7
|
||||||
|
|
||||||
c64.BGCOL0 = screen_color
|
ubyte ubb
|
||||||
c64.EXTCOL = border_color
|
uword uww
|
||||||
c64.COLOR = cursor_color
|
byte color
|
||||||
|
byte color2
|
||||||
|
|
||||||
|
AX=XY
|
||||||
|
uww=XY
|
||||||
|
AY=uww
|
||||||
|
|
||||||
|
A++
|
||||||
|
X++
|
||||||
|
;AY++
|
||||||
|
|
||||||
|
A = ~X
|
||||||
|
A = not Y
|
||||||
|
ubb = ~ubb
|
||||||
|
uww = ~uww
|
||||||
|
color2 = ~color
|
||||||
|
uww = not uww
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -544,6 +544,16 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
return lv
|
return lv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun process(expr: PrefixExpression): IExpression {
|
||||||
|
if(expr.operator=="-") {
|
||||||
|
val dt = expr.resultingDatatype(namespace, heap)
|
||||||
|
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||||
|
checkResult.add(ExpressionError("can only take negative of a signed number type", expr.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.process(expr)
|
||||||
|
}
|
||||||
|
|
||||||
override fun process(expr: BinaryExpression): IExpression {
|
override fun process(expr: BinaryExpression): IExpression {
|
||||||
when(expr.operator){
|
when(expr.operator){
|
||||||
"/", "//", "%" -> {
|
"/", "//", "%" -> {
|
||||||
|
@ -39,6 +39,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
|||||||
|
|
||||||
fun optimize() {
|
fun optimize() {
|
||||||
println("Optimizing stackVM code...")
|
println("Optimizing stackVM code...")
|
||||||
|
// remove nops (that are not a label)
|
||||||
|
for (blk in blocks) {
|
||||||
|
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
|
||||||
|
}
|
||||||
|
|
||||||
optimizeDataConversionAndUselessDiscards()
|
optimizeDataConversionAndUselessDiscards()
|
||||||
optimizeVariableCopying()
|
optimizeVariableCopying()
|
||||||
optimizeMultipleSequentialLineInstrs()
|
optimizeMultipleSequentialLineInstrs()
|
||||||
|
@ -143,21 +143,11 @@ enum class Opcode {
|
|||||||
NOT_WORD,
|
NOT_WORD,
|
||||||
|
|
||||||
// increment, decrement
|
// increment, decrement
|
||||||
INC_B,
|
|
||||||
INC_UB,
|
|
||||||
INC_W,
|
|
||||||
INC_UW,
|
|
||||||
INC_F,
|
|
||||||
INC_VAR_B,
|
INC_VAR_B,
|
||||||
INC_VAR_UB,
|
INC_VAR_UB,
|
||||||
INC_VAR_W,
|
INC_VAR_W,
|
||||||
INC_VAR_UW,
|
INC_VAR_UW,
|
||||||
INC_VAR_F,
|
INC_VAR_F,
|
||||||
DEC_B,
|
|
||||||
DEC_UB,
|
|
||||||
DEC_W,
|
|
||||||
DEC_UW,
|
|
||||||
DEC_F,
|
|
||||||
DEC_VAR_B,
|
DEC_VAR_B,
|
||||||
DEC_VAR_UB,
|
DEC_VAR_UB,
|
||||||
DEC_VAR_W,
|
DEC_VAR_W,
|
||||||
|
@ -17,16 +17,21 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
private lateinit var output: PrintWriter
|
private lateinit var output: PrintWriter
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// because 64tass understands scoped names via .proc / .block,
|
// Because 64tass understands scoped names via .proc / .block,
|
||||||
// we'll strip the block prefix from all scoped names in the program.
|
// we'll strip the block prefix from all scoped names in the program.
|
||||||
|
// Also, convert invalid label names (such as "<<<anonymous-1>>>") to something that's allowed.
|
||||||
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
|
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
|
||||||
for(block in program.blocks) {
|
for(block in program.blocks) {
|
||||||
val newvars = block.variables.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
val newvars = block.variables.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
||||||
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
|
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
|
||||||
val newinstructions = block.instructions.asSequence().map {
|
val newinstructions = block.instructions.asSequence().map {
|
||||||
it as? LabelInstr ?: Instruction(it.opcode, it.arg,
|
when {
|
||||||
if(it.callLabel!=null) symname(it.callLabel, block) else null,
|
it is LabelInstr -> LabelInstr(symname(it.name, block))
|
||||||
if(it.callLabel2!=null) symname(it.callLabel2, block) else null)
|
it.opcode == Opcode.INLINE_ASSEMBLY -> it
|
||||||
|
else -> Instruction(it.opcode, it.arg,
|
||||||
|
if (it.callLabel != null) symname(it.callLabel, block) else null,
|
||||||
|
if (it.callLabel2 != null) symname(it.callLabel2, block) else null)
|
||||||
|
}
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
val newConstants = block.integerConstants.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
val newConstants = block.integerConstants.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
||||||
newblocks.add(IntermediateProgram.ProgramBlock(
|
newblocks.add(IntermediateProgram.ProgramBlock(
|
||||||
@ -67,8 +72,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
return AssemblyProgram(program.name)
|
return AssemblyProgram(program.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock) =
|
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock): String {
|
||||||
if (scoped.startsWith("${block.shortname}.")) scoped.substring(block.shortname.length+1) else scoped
|
val name = if (scoped.startsWith("${block.shortname}.")) scoped.substring(block.shortname.length+1) else scoped
|
||||||
|
val validName = name.replace("<<<", "prog8_").replace(">>>", "")
|
||||||
|
if(validName=="-")
|
||||||
|
return "-"
|
||||||
|
return validName.replace("-", "").replace(".", "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun copyFloat(source: String, target: String) {
|
private fun copyFloat(source: String, target: String) {
|
||||||
// todo: optimize this to bulk copy all floats in the same loop
|
// todo: optimize this to bulk copy all floats in the same loop
|
||||||
@ -292,6 +303,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
|
|
||||||
private fun pushByte(byte: Int) {
|
private fun pushByte(byte: Int) {
|
||||||
out("\tlda #${byte.toHex()}")
|
out("\tlda #${byte.toHex()}")
|
||||||
|
pushByteA()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pushByteA() {
|
||||||
out("\tsta ${ESTACK_LO.toHex()},x")
|
out("\tsta ${ESTACK_LO.toHex()},x")
|
||||||
out("\tdex")
|
out("\tdex")
|
||||||
}
|
}
|
||||||
@ -311,7 +326,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
private fun pushWord(word: Int) {
|
private fun pushWord(word: Int) {
|
||||||
out("\tlda #<${word.toHex()}")
|
out("\tlda #<${word.toHex()}")
|
||||||
out("\tsta ${ESTACK_LO.toHex()},x")
|
out("\tsta ${ESTACK_LO.toHex()},x")
|
||||||
out("\tlda #>${word.toHex()}")
|
out("\tlda #<${word.toHex()}")
|
||||||
|
out("\tsta ${ESTACK_HI.toHex()},x")
|
||||||
|
out("\tdex")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pushWordAY() {
|
||||||
|
out("\tsta ${ESTACK_LO.toHex()},x")
|
||||||
|
out("\ttya")
|
||||||
out("\tsta ${ESTACK_HI.toHex()},x")
|
out("\tsta ${ESTACK_HI.toHex()},x")
|
||||||
out("\tdex")
|
out("\tdex")
|
||||||
}
|
}
|
||||||
@ -350,6 +372,17 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
out("\tdex")
|
out("\tdex")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun popByteA() {
|
||||||
|
out("\tinx")
|
||||||
|
out("\tlda ${ESTACK_LO.toHex()},x")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun popWordAY() {
|
||||||
|
out("\ninx")
|
||||||
|
out("\tlda ${ESTACK_LO.toHex()},x")
|
||||||
|
out("\tldy ${ESTACK_HI.toHex()},x")
|
||||||
|
}
|
||||||
|
|
||||||
private fun instr2asm(insIdx: Int, ins: Instruction, block: IntermediateProgram.ProgramBlock): Int {
|
private fun instr2asm(insIdx: Int, ins: Instruction, block: IntermediateProgram.ProgramBlock): Int {
|
||||||
if(ins is LabelInstr) {
|
if(ins is LabelInstr) {
|
||||||
if(ins.name==block.shortname)
|
if(ins.name==block.shortname)
|
||||||
@ -369,6 +402,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
Opcode.SEI -> out("\tsei")
|
Opcode.SEI -> out("\tsei")
|
||||||
Opcode.CLI -> out("\tcli")
|
Opcode.CLI -> out("\tcli")
|
||||||
Opcode.RETURN -> out("\trts") // todo is return really this simple?
|
Opcode.RETURN -> out("\trts") // todo is return really this simple?
|
||||||
|
Opcode.JUMP -> out("\tjmp ${ins.callLabel}")
|
||||||
Opcode.B2UB -> {} // is a no-op, just carry on with the byte as-is
|
Opcode.B2UB -> {} // is a no-op, just carry on with the byte as-is
|
||||||
Opcode.UB2B -> {} // is a no-op, just carry on with the byte as-is
|
Opcode.UB2B -> {} // is a no-op, just carry on with the byte as-is
|
||||||
Opcode.RSAVE -> out("\tphp\n\tpha\n\ttxa\n\tpha\n\ttya\n\tpha")
|
Opcode.RSAVE -> out("\tphp\n\tpha\n\ttxa\n\tpha\n\ttya\n\tpha")
|
||||||
@ -376,6 +410,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
Opcode.DISCARD_BYTE -> out("\tinx") // remove 1 (2) bytes from stack
|
Opcode.DISCARD_BYTE -> out("\tinx") // remove 1 (2) bytes from stack
|
||||||
Opcode.DISCARD_WORD -> out("\tinx") // remove 2 bytes from stack
|
Opcode.DISCARD_WORD -> out("\tinx") // remove 2 bytes from stack
|
||||||
Opcode.DISCARD_FLOAT -> out("\tinx\n\tinx\n\tinx") // remove 5 (6) bytes from stack
|
Opcode.DISCARD_FLOAT -> out("\tinx\n\tinx\n\tinx") // remove 5 (6) bytes from stack
|
||||||
|
Opcode.INLINE_ASSEMBLY -> out(ins.callLabel) // All of the inline assembly is stored in the calllabel property.
|
||||||
Opcode.COPY_VAR_BYTE -> {
|
Opcode.COPY_VAR_BYTE -> {
|
||||||
when {
|
when {
|
||||||
ins.callLabel2 in registerStrings -> {
|
ins.callLabel2 in registerStrings -> {
|
||||||
@ -504,6 +539,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
return 1 // skip 1
|
return 1 // skip 1
|
||||||
}
|
}
|
||||||
// byte from variable onto stack
|
// byte from variable onto stack
|
||||||
|
TODO("can be register")
|
||||||
pushVarByte(ins.callLabel!!)
|
pushVarByte(ins.callLabel!!)
|
||||||
}
|
}
|
||||||
Opcode.PUSH_WORD -> {
|
Opcode.PUSH_WORD -> {
|
||||||
@ -579,6 +615,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
return 1 // skip 1
|
return 1 // skip 1
|
||||||
}
|
}
|
||||||
// word from memory onto stack
|
// word from memory onto stack
|
||||||
|
TODO("can be register")
|
||||||
pushVarWord(ins.callLabel!!)
|
pushVarWord(ins.callLabel!!)
|
||||||
}
|
}
|
||||||
Opcode.PUSH_FLOAT -> {
|
Opcode.PUSH_FLOAT -> {
|
||||||
@ -630,44 +667,79 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
}
|
}
|
||||||
pushFloat(ins.arg!!.integerValue().toHex())
|
pushFloat(ins.arg!!.integerValue().toHex())
|
||||||
}
|
}
|
||||||
Opcode.INLINE_ASSEMBLY -> out(ins.callLabel) // All of the inline assembly is stored in the calllabel property.
|
Opcode.INC_VAR_UB, Opcode.INC_VAR_B -> {
|
||||||
|
TODO("can be register")
|
||||||
|
out("\tinc ${ins.callLabel}")
|
||||||
|
}
|
||||||
|
Opcode.DEC_VAR_UB, Opcode.DEC_VAR_B -> {
|
||||||
|
TODO("can be register")
|
||||||
|
out("\tdec ${ins.callLabel}")
|
||||||
|
}
|
||||||
|
Opcode.ADD_UB, Opcode.ADD_B -> {
|
||||||
|
out("\tlda ${(ESTACK_LO+2).toHex()},x")
|
||||||
|
out("\tclc\n\tadc ${(ESTACK_LO+1).toHex()},x")
|
||||||
|
out("\tsta ${(ESTACK_LO+2).toHex()},x")
|
||||||
|
out("\tinx")
|
||||||
|
}
|
||||||
|
Opcode.SUB_UB, Opcode.SUB_B -> {
|
||||||
|
out("\tlda ${(ESTACK_LO+2).toHex()},x")
|
||||||
|
out("\tsec\n\tsbc ${(ESTACK_LO+1).toHex()},x")
|
||||||
|
out("\tsta ${(ESTACK_LO+2).toHex()},x")
|
||||||
|
out("\tinx")
|
||||||
|
}
|
||||||
|
Opcode.POP_MEM_UB, Opcode.POP_MEM_B -> {
|
||||||
|
popByteA()
|
||||||
|
out("\tsta ${ins.arg!!.integerValue().toHex()}")
|
||||||
|
}
|
||||||
|
Opcode.POP_VAR_BYTE -> {
|
||||||
|
popByteA()
|
||||||
|
TODO("can be register")
|
||||||
|
out("\tsta ${ins.callLabel}")
|
||||||
|
}
|
||||||
|
Opcode.POP_VAR_WORD -> {
|
||||||
|
popWordAY()
|
||||||
|
TODO("can be register")
|
||||||
|
out("\tsta ${ins.callLabel}")
|
||||||
|
out("\tsty ${ins.callLabel}+1")
|
||||||
|
}
|
||||||
|
Opcode.NEG_B -> {
|
||||||
|
popByteA()
|
||||||
|
out("\teor #\$ff")
|
||||||
|
out("\tsec\n\tadc #0")
|
||||||
|
pushByteA()
|
||||||
|
}
|
||||||
|
Opcode.INV_BYTE, Opcode.NOT_BYTE -> {
|
||||||
|
popByteA()
|
||||||
|
out("\teor #\$ff")
|
||||||
|
pushByteA()
|
||||||
|
}
|
||||||
|
Opcode.INV_WORD, Opcode.NOT_WORD -> {
|
||||||
|
popWordAY()
|
||||||
|
out("\teor #\$ff")
|
||||||
|
out("\tpha")
|
||||||
|
out("\ttya")
|
||||||
|
out("\teor #\$ff")
|
||||||
|
out("\ttay")
|
||||||
|
out("\tpla")
|
||||||
|
pushWordAY()
|
||||||
|
}
|
||||||
|
|
||||||
else-> TODO("asm for $ins")
|
else-> TODO("asm for $ins")
|
||||||
// Opcode.POP_MEM_B -> TODO()
|
|
||||||
// Opcode.POP_MEM_UB -> TODO()
|
|
||||||
// Opcode.POP_MEM_W -> TODO()
|
// Opcode.POP_MEM_W -> TODO()
|
||||||
// Opcode.POP_MEM_UW -> TODO()
|
// Opcode.POP_MEM_UW -> TODO()
|
||||||
// Opcode.POP_MEM_FLOAT -> TODO()
|
// Opcode.POP_MEM_FLOAT -> TODO()
|
||||||
// Opcode.POP_VAR_BYTE -> TODO()
|
|
||||||
// Opcode.POP_VAR_WORD -> TODO()
|
// Opcode.POP_VAR_WORD -> TODO()
|
||||||
// Opcode.POP_VAR_FLOAT -> TODO()
|
// Opcode.POP_VAR_FLOAT -> TODO()
|
||||||
// Opcode.COPY_VAR_FLOAT -> TODO()
|
// Opcode.COPY_VAR_FLOAT -> TODO()
|
||||||
// Opcode.INC_B -> TODO()
|
|
||||||
// Opcode.INC_UB -> TODO()
|
|
||||||
// Opcode.INC_W -> TODO()
|
|
||||||
// Opcode.INC_UW -> TODO()
|
|
||||||
// Opcode.INC_F -> TODO()
|
|
||||||
// Opcode.INC_VAR_B -> TODO()
|
|
||||||
// Opcode.INC_VAR_UB -> TODO()
|
|
||||||
// Opcode.INC_VAR_W -> TODO()
|
// Opcode.INC_VAR_W -> TODO()
|
||||||
// Opcode.INC_VAR_UW -> TODO()
|
// Opcode.INC_VAR_UW -> TODO()
|
||||||
// Opcode.INC_VAR_F -> TODO()
|
// Opcode.INC_VAR_F -> TODO()
|
||||||
// Opcode.DEC_B -> TODO()
|
|
||||||
// Opcode.DEC_UB -> TODO()
|
|
||||||
// Opcode.DEC_W -> TODO()
|
|
||||||
// Opcode.DEC_UW -> TODO()
|
|
||||||
// Opcode.DEC_F -> TODO()
|
|
||||||
// Opcode.DEC_VAR_B -> TODO()
|
|
||||||
// Opcode.DEC_VAR_UB -> TODO()
|
|
||||||
// Opcode.DEC_VAR_W -> TODO()
|
// Opcode.DEC_VAR_W -> TODO()
|
||||||
// Opcode.DEC_VAR_UW -> TODO()
|
// Opcode.DEC_VAR_UW -> TODO()
|
||||||
// Opcode.DEC_VAR_F -> TODO()
|
// Opcode.DEC_VAR_F -> TODO()
|
||||||
// Opcode.ADD_UB -> TODO()
|
|
||||||
// Opcode.ADD_B -> TODO()
|
|
||||||
// Opcode.ADD_UW -> TODO()
|
// Opcode.ADD_UW -> TODO()
|
||||||
// Opcode.ADD_W -> TODO()
|
// Opcode.ADD_W -> TODO()
|
||||||
// Opcode.ADD_F -> TODO()
|
// Opcode.ADD_F -> TODO()
|
||||||
// Opcode.SUB_UB -> TODO()
|
|
||||||
// Opcode.SUB_B -> TODO()
|
|
||||||
// Opcode.SUB_UW -> TODO()
|
// Opcode.SUB_UW -> TODO()
|
||||||
// Opcode.SUB_W -> TODO()
|
// Opcode.SUB_W -> TODO()
|
||||||
// Opcode.SUB_F -> TODO()
|
// Opcode.SUB_F -> TODO()
|
||||||
@ -696,7 +768,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
// Opcode.POW_UW -> TODO()
|
// Opcode.POW_UW -> TODO()
|
||||||
// Opcode.POW_W -> TODO()
|
// Opcode.POW_W -> TODO()
|
||||||
// Opcode.POW_F -> TODO()
|
// Opcode.POW_F -> TODO()
|
||||||
// Opcode.NEG_B -> TODO()
|
|
||||||
// Opcode.NEG_W -> TODO()
|
// Opcode.NEG_W -> TODO()
|
||||||
// Opcode.NEG_F -> TODO()
|
// Opcode.NEG_F -> TODO()
|
||||||
// Opcode.SHL_BYTE -> TODO()
|
// Opcode.SHL_BYTE -> TODO()
|
||||||
@ -741,8 +812,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
// Opcode.BITOR_WORD -> TODO()
|
// Opcode.BITOR_WORD -> TODO()
|
||||||
// Opcode.BITXOR_BYTE -> TODO()
|
// Opcode.BITXOR_BYTE -> TODO()
|
||||||
// Opcode.BITXOR_WORD -> TODO()
|
// Opcode.BITXOR_WORD -> TODO()
|
||||||
// Opcode.INV_BYTE -> TODO()
|
|
||||||
// Opcode.INV_WORD -> TODO()
|
|
||||||
// Opcode.LSB -> TODO()
|
// Opcode.LSB -> TODO()
|
||||||
// Opcode.MSB -> TODO()
|
// Opcode.MSB -> TODO()
|
||||||
// Opcode.B2WORD -> TODO()
|
// Opcode.B2WORD -> TODO()
|
||||||
@ -758,8 +827,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
// Opcode.OR_WORD -> TODO()
|
// Opcode.OR_WORD -> TODO()
|
||||||
// Opcode.XOR_BYTE -> TODO()
|
// Opcode.XOR_BYTE -> TODO()
|
||||||
// Opcode.XOR_WORD -> TODO()
|
// Opcode.XOR_WORD -> TODO()
|
||||||
// Opcode.NOT_BYTE -> TODO()
|
|
||||||
// Opcode.NOT_WORD -> TODO()
|
|
||||||
// Opcode.LESS_B -> TODO()
|
// Opcode.LESS_B -> TODO()
|
||||||
// Opcode.LESS_UB -> TODO()
|
// Opcode.LESS_UB -> TODO()
|
||||||
// Opcode.LESS_W -> TODO()
|
// Opcode.LESS_W -> TODO()
|
||||||
@ -792,7 +859,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
// Opcode.WRITE_INDEXED_VAR_BYTE -> TODO()
|
// Opcode.WRITE_INDEXED_VAR_BYTE -> TODO()
|
||||||
// Opcode.WRITE_INDEXED_VAR_WORD -> TODO()
|
// Opcode.WRITE_INDEXED_VAR_WORD -> TODO()
|
||||||
// Opcode.WRITE_INDEXED_VAR_FLOAT -> TODO()
|
// Opcode.WRITE_INDEXED_VAR_FLOAT -> TODO()
|
||||||
// Opcode.JUMP -> TODO()
|
|
||||||
// Opcode.BCS -> TODO()
|
// Opcode.BCS -> TODO()
|
||||||
// Opcode.BCC -> TODO()
|
// Opcode.BCC -> TODO()
|
||||||
// Opcode.BZ -> TODO()
|
// Opcode.BZ -> TODO()
|
||||||
|
@ -13,7 +13,7 @@ class AssemblyProgram(val name: String) {
|
|||||||
println("Generating machine code program...")
|
println("Generating machine code program...")
|
||||||
|
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool",
|
||||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor")
|
"-Werror", "--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor")
|
||||||
|
|
||||||
val outFile = when(options.output) {
|
val outFile = when(options.output) {
|
||||||
OutputType.PRG -> {
|
OutputType.PRG -> {
|
||||||
|
@ -13,7 +13,7 @@ import prog8.functions.BuiltinFunctions
|
|||||||
todo remove if statements with empty statement blocks
|
todo remove if statements with empty statement blocks
|
||||||
todo replace if statements with only else block
|
todo replace if statements with only else block
|
||||||
todo statement optimization: create augmented assignment from assignment that only refers to its lvalue (A=A+10, A=4*A, ...)
|
todo statement optimization: create augmented assignment from assignment that only refers to its lvalue (A=A+10, A=4*A, ...)
|
||||||
todo statement optimization: X+=1, X-=1 --> X++/X-- ,
|
todo statement optimization: X+=1, X-=1 --> X++/X-- (to 3? 4? incs/decs in a row after that use arithmetic)
|
||||||
todo remove statements that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
todo remove statements that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
||||||
todo optimize addition with self into shift 1 (A+=A -> A<<=1)
|
todo optimize addition with self into shift 1 (A+=A -> A<<=1)
|
||||||
todo assignment optimization: optimize some simple multiplications and divisions into shifts (A*=2 -> lsl(A), X=X/2 -> lsr(X) )
|
todo assignment optimization: optimize some simple multiplications and divisions into shifts (A*=2 -> lsl(A), X=X/2 -> lsr(X) )
|
||||||
|
@ -633,56 +633,6 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
checkDt(v, DataType.UWORD)
|
checkDt(v, DataType.UWORD)
|
||||||
evalstack.push(v.inv())
|
evalstack.push(v.inv())
|
||||||
}
|
}
|
||||||
Opcode.INC_B -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.BYTE)
|
|
||||||
evalstack.push(v.inc())
|
|
||||||
}
|
|
||||||
Opcode.INC_UB -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.UBYTE)
|
|
||||||
evalstack.push(v.inc())
|
|
||||||
}
|
|
||||||
Opcode.INC_W -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.WORD)
|
|
||||||
evalstack.push(v.inc())
|
|
||||||
}
|
|
||||||
Opcode.INC_UW -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.UWORD)
|
|
||||||
evalstack.push(v.inc())
|
|
||||||
}
|
|
||||||
Opcode.INC_F -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.FLOAT)
|
|
||||||
evalstack.push(v.inc())
|
|
||||||
}
|
|
||||||
Opcode.DEC_UB -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.UBYTE)
|
|
||||||
evalstack.push(v.dec())
|
|
||||||
}
|
|
||||||
Opcode.DEC_B -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.BYTE)
|
|
||||||
evalstack.push(v.dec())
|
|
||||||
}
|
|
||||||
Opcode.DEC_UW -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.UWORD)
|
|
||||||
evalstack.push(v.dec())
|
|
||||||
}
|
|
||||||
Opcode.DEC_W -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.WORD)
|
|
||||||
evalstack.push(v.dec())
|
|
||||||
}
|
|
||||||
Opcode.DEC_F -> {
|
|
||||||
val v = evalstack.pop()
|
|
||||||
checkDt(v, DataType.FLOAT)
|
|
||||||
evalstack.push(v.dec())
|
|
||||||
}
|
|
||||||
Opcode.SYSCALL -> dispatchSyscall(ins)
|
Opcode.SYSCALL -> dispatchSyscall(ins)
|
||||||
Opcode.SEC -> P_carry = true
|
Opcode.SEC -> P_carry = true
|
||||||
Opcode.CLC -> P_carry = false
|
Opcode.CLC -> P_carry = false
|
||||||
|
@ -424,26 +424,6 @@ class TestStackVmOpcodes {
|
|||||||
testUnaryOperator(Value(DataType.UWORD, 5000), Opcode.NOT_WORD, Value(DataType.UBYTE, 0))
|
testUnaryOperator(Value(DataType.UWORD, 5000), Opcode.NOT_WORD, Value(DataType.UBYTE, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testInc() {
|
|
||||||
testUnaryOperator(Value(DataType.UBYTE, 255), Opcode.INC_UB, Value(DataType.UBYTE, 0))
|
|
||||||
testUnaryOperator(Value(DataType.UBYTE, 99), Opcode.INC_UB, Value(DataType.UBYTE, 100))
|
|
||||||
testUnaryOperator(Value(DataType.UWORD, 65535), Opcode.INC_UW, Value(DataType.UWORD, 0))
|
|
||||||
testUnaryOperator(Value(DataType.UWORD, 999), Opcode.INC_UW, Value(DataType.UWORD, 1000))
|
|
||||||
testUnaryOperator(Value(DataType.FLOAT, -1.0), Opcode.INC_F, Value(DataType.FLOAT, 0.0))
|
|
||||||
testUnaryOperator(Value(DataType.FLOAT, 2022.5), Opcode.INC_F, Value(DataType.FLOAT, 2023.5))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDec() {
|
|
||||||
testUnaryOperator(Value(DataType.UBYTE, 100), Opcode.DEC_UB, Value(DataType.UBYTE, 99))
|
|
||||||
testUnaryOperator(Value(DataType.UBYTE, 0), Opcode.DEC_UB, Value(DataType.UBYTE, 255))
|
|
||||||
testUnaryOperator(Value(DataType.UWORD, 1000), Opcode.DEC_UW, Value(DataType.UWORD, 999))
|
|
||||||
testUnaryOperator(Value(DataType.UWORD, 0), Opcode.DEC_UW, Value(DataType.UWORD, 65535))
|
|
||||||
testUnaryOperator(Value(DataType.FLOAT, 0.5), Opcode.DEC_F, Value(DataType.FLOAT, -0.5))
|
|
||||||
testUnaryOperator(Value(DataType.FLOAT, 2022.5), Opcode.DEC_F, Value(DataType.FLOAT, 2021.5))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNeg() {
|
fun testNeg() {
|
||||||
testUnaryOperator(Value(DataType.BYTE, 12), Opcode.NEG_B, Value(DataType.BYTE, -12))
|
testUnaryOperator(Value(DataType.BYTE, 12), Opcode.NEG_B, Value(DataType.BYTE, -12))
|
||||||
|
@ -7,80 +7,80 @@
|
|||||||
|
|
||||||
|
|
||||||
~ c64 {
|
~ c64 {
|
||||||
memory byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
|
memory ubyte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
|
||||||
memory byte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
|
memory ubyte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
|
||||||
memory word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
memory uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
||||||
memory word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
memory uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
||||||
|
|
||||||
|
|
||||||
memory byte TIME_HI = $a0 ; software jiffy clock, hi byte
|
memory ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
memory byte TIME_MID = $a1 ; .. mid byte
|
memory ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
memory byte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
memory ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
memory byte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
memory ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
memory byte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
memory ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||||
|
|
||||||
memory byte COLOR = $0286 ; cursor color
|
memory ubyte COLOR = $0286 ; cursor color
|
||||||
memory byte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
memory ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
||||||
memory word CINV = $0314 ; IRQ vector
|
memory uword CINV = $0314 ; IRQ vector
|
||||||
memory word NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
memory uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||||
memory word RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
memory uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||||
memory word IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
memory uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||||
|
|
||||||
memory byte[40, 25] Screen = $0400 ; default character screen matrix
|
memory ubyte[40, 25] Screen = $0400 ; default character screen matrix
|
||||||
memory byte[40, 25] Colors = $d800 ; character screen colors
|
memory ubyte[40, 25] Colors = $d800 ; character screen colors
|
||||||
|
|
||||||
|
|
||||||
; ---- VIC-II registers ----
|
; ---- VIC-II registers ----
|
||||||
|
|
||||||
memory byte SP0X = $d000
|
memory ubyte SP0X = $d000
|
||||||
memory byte SP0Y = $d001
|
memory ubyte SP0Y = $d001
|
||||||
memory byte SP1X = $d002
|
memory ubyte SP1X = $d002
|
||||||
memory byte SP1Y = $d003
|
memory ubyte SP1Y = $d003
|
||||||
memory byte SP2X = $d004
|
memory ubyte SP2X = $d004
|
||||||
memory byte SP2Y = $d005
|
memory ubyte SP2Y = $d005
|
||||||
memory byte SP3X = $d006
|
memory ubyte SP3X = $d006
|
||||||
memory byte SP3Y = $d007
|
memory ubyte SP3Y = $d007
|
||||||
memory byte SP4X = $d008
|
memory ubyte SP4X = $d008
|
||||||
memory byte SP4Y = $d009
|
memory ubyte SP4Y = $d009
|
||||||
memory byte SP5X = $d00a
|
memory ubyte SP5X = $d00a
|
||||||
memory byte SP5Y = $d00b
|
memory ubyte SP5Y = $d00b
|
||||||
memory byte SP6X = $d00c
|
memory ubyte SP6X = $d00c
|
||||||
memory byte SP6Y = $d00d
|
memory ubyte SP6Y = $d00d
|
||||||
memory byte SP7X = $d00e
|
memory ubyte SP7X = $d00e
|
||||||
memory byte SP7Y = $d00f
|
memory ubyte SP7Y = $d00f
|
||||||
|
|
||||||
memory byte MSIGX = $d010
|
memory ubyte MSIGX = $d010
|
||||||
memory byte SCROLY = $d011
|
memory ubyte SCROLY = $d011
|
||||||
memory byte RASTER = $d012
|
memory ubyte RASTER = $d012
|
||||||
memory byte LPENX = $d013
|
memory ubyte LPENX = $d013
|
||||||
memory byte LPENY = $d014
|
memory ubyte LPENY = $d014
|
||||||
memory byte SPENA = $d015
|
memory ubyte SPENA = $d015
|
||||||
memory byte SCROLX = $d016
|
memory ubyte SCROLX = $d016
|
||||||
memory byte YXPAND = $d017
|
memory ubyte YXPAND = $d017
|
||||||
memory byte VMCSB = $d018
|
memory ubyte VMCSB = $d018
|
||||||
memory byte VICIRQ = $d019
|
memory ubyte VICIRQ = $d019
|
||||||
memory byte IREQMASK = $d01a
|
memory ubyte IREQMASK = $d01a
|
||||||
memory byte SPBGPR = $d01b
|
memory ubyte SPBGPR = $d01b
|
||||||
memory byte SPMC = $d01c
|
memory ubyte SPMC = $d01c
|
||||||
memory byte XXPAND = $d01d
|
memory ubyte XXPAND = $d01d
|
||||||
memory byte SPSPCL = $d01e
|
memory ubyte SPSPCL = $d01e
|
||||||
memory byte SPBGCL = $d01f
|
memory ubyte SPBGCL = $d01f
|
||||||
|
|
||||||
memory byte EXTCOL = $d020 ; border color
|
memory ubyte EXTCOL = $d020 ; border color
|
||||||
memory byte BGCOL0 = $d021 ; screen color
|
memory ubyte BGCOL0 = $d021 ; screen color
|
||||||
memory byte BGCOL1 = $d022
|
memory ubyte BGCOL1 = $d022
|
||||||
memory byte BGCOL2 = $d023
|
memory ubyte BGCOL2 = $d023
|
||||||
memory byte BGCOL4 = $d024
|
memory ubyte BGCOL4 = $d024
|
||||||
memory byte SPMC0 = $d025
|
memory ubyte SPMC0 = $d025
|
||||||
memory byte SPMC1 = $d026
|
memory ubyte SPMC1 = $d026
|
||||||
memory byte SP0COL = $d027
|
memory ubyte SP0COL = $d027
|
||||||
memory byte SP1COL = $d028
|
memory ubyte SP1COL = $d028
|
||||||
memory byte SP2COL = $d029
|
memory ubyte SP2COL = $d029
|
||||||
memory byte SP3COL = $d02a
|
memory ubyte SP3COL = $d02a
|
||||||
memory byte SP4COL = $d02b
|
memory ubyte SP4COL = $d02b
|
||||||
memory byte SP5COL = $d02c
|
memory ubyte SP5COL = $d02c
|
||||||
memory byte SP6COL = $d02d
|
memory ubyte SP6COL = $d02d
|
||||||
memory byte SP7COL = $d02e
|
memory ubyte SP7COL = $d02e
|
||||||
|
|
||||||
; ---- end of VIC-II registers ----
|
; ---- end of VIC-II registers ----
|
||||||
|
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
|
|
||||||
~ math {
|
~ math {
|
||||||
; note: the following ZP scratch registers must be the same as in c64lib
|
; note: the following ZP scratch registers must be the same as in c64lib
|
||||||
memory byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
|
memory ubyte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
|
||||||
memory byte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
|
memory ubyte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
|
||||||
memory word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
memory uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
||||||
memory word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
memory uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sub multiply_bytes (byte1: X, byte2: Y) -> (A, X?) {
|
asmsub multiply_bytes (byte1: ubyte @ X, byte2: ubyte @ Y) -> clobbers(X) -> (ubyte @ A) {
|
||||||
; ---- multiply 2 bytes, result as byte in A (signed or unsigned)
|
; ---- multiply 2 bytes, result as byte in A (signed or unsigned)
|
||||||
%asm {{
|
%asm {{
|
||||||
stx SCRATCH_ZP1
|
stx SCRATCH_ZP1
|
||||||
@ -37,7 +37,7 @@ sub multiply_bytes (byte1: X, byte2: Y) -> (A, X?) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub multiply_bytes_16 (byte1: X, byte2: Y) -> (A?, XY) {
|
asmsub multiply_bytes_16 (byte1: ubyte @ X, byte2: ubyte @ Y) -> clobbers(A) -> (uword @ XY) {
|
||||||
; ---- multiply 2 bytes, result as word in X/Y (unsigned)
|
; ---- multiply 2 bytes, result as word in X/Y (unsigned)
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #0
|
lda #0
|
||||||
@ -58,7 +58,7 @@ _m_with_add stx SCRATCH_ZP1
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub multiply_bytes_addA_16 (byte1: X, byte2: Y, add: A) -> (A?, XY) {
|
asmsub multiply_bytes_addA_16 (byte1: ubyte @ X, byte2: ubyte @ Y, add: ubyte @ A) -> clobbers(A) -> (uword @ XY) {
|
||||||
; ---- multiply 2 bytes and add A, result as word in X/Y (unsigned)
|
; ---- multiply 2 bytes and add A, result as word in X/Y (unsigned)
|
||||||
%asm {{
|
%asm {{
|
||||||
jmp multiply_bytes_16._m_with_add
|
jmp multiply_bytes_16._m_with_add
|
||||||
@ -66,7 +66,7 @@ sub multiply_bytes_addA_16 (byte1: X, byte2: Y, add: A) -> (A?, XY) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
word[2] multiply_words_product = 0
|
word[2] multiply_words_product = 0
|
||||||
sub multiply_words (number: XY) -> (?) {
|
asmsub multiply_words (number: uword @ XY) -> clobbers(A,X) -> () {
|
||||||
; ---- multiply two 16-bit words into a 32-bit result
|
; ---- multiply two 16-bit words into a 32-bit result
|
||||||
; input: X/Y = first 16-bit number, SCRATCH_ZPWORD1 in ZP = second 16-bit number
|
; input: X/Y = first 16-bit number, SCRATCH_ZPWORD1 in ZP = second 16-bit number
|
||||||
; output: multiply_words_product 32-bits product, LSB order (low-to-high)
|
; output: multiply_words_product 32-bits product, LSB order (low-to-high)
|
||||||
@ -100,7 +100,7 @@ mult16 lda #$00
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub divmod_bytes (number: X, divisor: Y) -> (X, A) {
|
asmsub divmod_bytes (number: ubyte @ X, divisor: ubyte @ Y) -> clobbers() -> (ubyte @ X, ubyte @ A) {
|
||||||
; ---- divide X by Y, result quotient in X, remainder in A (unsigned)
|
; ---- divide X by Y, result quotient in X, remainder in A (unsigned)
|
||||||
; division by zero will result in quotient = 255 and remainder = original number
|
; division by zero will result in quotient = 255 and remainder = original number
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -123,7 +123,7 @@ sub divmod_bytes (number: X, divisor: Y) -> (X, A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub divmod_words (divisor: XY) -> (A?, XY) {
|
asmsub divmod_words (divisor: uword @ XY) -> clobbers(A) -> (uword @ XY) {
|
||||||
; ---- divide two words (16 bit each) into 16 bit results
|
; ---- divide two words (16 bit each) into 16 bit results
|
||||||
; input: SCRATCH_ZPWORD1 in ZP: 16 bit number, X/Y: 16 bit divisor
|
; input: SCRATCH_ZPWORD1 in ZP: 16 bit number, X/Y: 16 bit divisor
|
||||||
; output: SCRATCH_ZPWORD1 in ZP: 16 bit result, X/Y: 16 bit remainder
|
; output: SCRATCH_ZPWORD1 in ZP: 16 bit result, X/Y: 16 bit remainder
|
||||||
@ -172,7 +172,7 @@ remainder = SCRATCH_ZP1
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub randbyte () -> (A) {
|
asmsub randbyte () -> clobbers() -> (ubyte @ A) {
|
||||||
; ---- 8-bit pseudo random number generator into A
|
; ---- 8-bit pseudo random number generator into A
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -194,7 +194,7 @@ _magiceors .byte $1d, $2b, $2d, $4d, $5f, $63, $65, $69
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub randword () -> (XY) {
|
asmsub randword () -> clobbers() -> (uword @ XY) {
|
||||||
; ---- 16 bit pseudo random number generator into XY
|
; ---- 16 bit pseudo random number generator into XY
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
|
Loading…
Reference in New Issue
Block a user