added memory() function for memory slab allocations

This commit is contained in:
Irmen de Jong 2020-12-27 02:22:18 +01:00
parent 3b8e18004c
commit b40e1eabb9
9 changed files with 101 additions and 18 deletions

View File

@ -964,11 +964,14 @@ internal class AstChecker(private val program: Program,
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
if(targetStatement!=null)
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
if(!functionCallStatement.void && targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) {
if(targetStatement.returntypes.size==1)
errors.warn("result value of subroutine call is discarded (use void?)", functionCallStatement.position)
else
errors.warn("result values of subroutine call are discarded (use void?)", functionCallStatement.position)
if (!functionCallStatement.void) {
// check for unused return values
if (targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) {
if(targetStatement.returntypes.size==1)
errors.warn("result value of subroutine call is discarded (use void?)", functionCallStatement.position)
else
errors.warn("result values of subroutine call are discarded (use void?)", functionCallStatement.position)
}
}
if(functionCallStatement.target.nameInSource.last() == "sort") {

View File

@ -697,6 +697,7 @@ class AsmGenInfo {
var usedRegsaveY = false
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
val removals = mutableListOf<Statement>()
class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex)
}

View File

@ -50,6 +50,7 @@ internal class AsmGen(private val program: Program,
private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen)
internal val loopEndLabels = ArrayDeque<String>()
private val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
internal val slabs = mutableMapOf<String, Int>()
override fun compileToAssembly(): IAssemblyProgram {
assemblyLines.clear()
@ -63,6 +64,7 @@ internal class AsmGen(private val program: Program,
throw AssemblyError("first block should be 'main'")
for(b in program.allBlocks())
block2asm(b)
slaballocations()
footer()
val outputFile = outputDir.resolve("${program.name}.asm").toFile()
@ -152,6 +154,14 @@ internal class AsmGen(private val program: Program,
out(" jmp main.start ; start program / force start proc to be included")
}
private fun slaballocations() {
out("; memory slabs")
out("prog8_slabs\t.block")
for((name, size) in slabs)
out("$name\t.fill $size")
out("\t.bend")
}
private fun footer() {
// the global list of all floating point constants for the whole program
out("; global float constants")
@ -834,6 +844,12 @@ internal class AsmGen(private val program: Program,
out("; statements")
sub.statements.forEach{ translate(it) }
for(stmt in sub.asmGenInfo.removals) {
sub.remove(stmt)
}
sub.asmGenInfo.removals.clear()
out("; variables")
out("; register saves")
if(sub.asmGenInfo.usedRegsaveA)

View File

@ -108,10 +108,40 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
else
asmgen.out(" lda #<prog8_program_end | ldy #>prog8_program_end")
}
"memory" -> funcMemory(fcall, discardResult, resultToStack)
else -> TODO("missing asmgen for builtin func ${func.name}")
}
}
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean) {
if(discardResult || fcall !is FunctionCall)
throw AssemblyError("should not discard result of memory allocation at $fcall")
val scope = fcall.definingScope()
val nameRef = fcall.args[0] as IdentifierReference
val name = (nameRef.targetVarDecl(program.namespace)!!.value as StringLiteralValue).value
val size = (fcall.args[1] as NumericLiteralValue).number.toInt()
val existingSize = asmgen.slabs[name]
if(existingSize!=null && existingSize!=size)
throw AssemblyError("memory slab '$name' already exists with a different size ($size) at ${fcall.position}")
val slabname = IdentifierReference(listOf("prog8_slabs", name), fcall.position)
slabname.linkParents(fcall)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
val target =
if(resultToStack)
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
else
AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(src, target, false, fcall.position)
asmgen.translateNormalAssignment(assign)
// remove the variable for the name, it's not used as a variable only as a tag for the assembler.
val nameDecl = scope.statements.single { it is VarDecl && it.name==nameRef.nameInSource.single() }
(scope as Subroutine).asmGenInfo.removals.add(nameDecl)
asmgen.slabs[name] = size
}
private fun funcMemSetCopy(fcall: IFunctionCall, func: FSignature, scope: Subroutine) {
if(CompilationTarget.instance is Cx16Target) {
when(func.name) {

View File

@ -143,10 +143,11 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("clear_carry" , false, emptyList(), null),
FSignature("set_irqd" , false, emptyList(), null),
FSignature("clear_irqd" , false, emptyList(), null),
FSignature("read_flags" , false, emptyList(), DataType.UBYTE),
FSignature("read_flags" , true, emptyList(), DataType.UBYTE),
FSignature("progend" , true, emptyList(), DataType.UWORD),
FSignature("target" , true, emptyList(), DataType.UBYTE, ::builtinTarget),
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
FSignature("memory" , false, listOf(FParam("name", setOf(DataType.STR)), FParam("size", setOf(DataType.UWORD))), DataType.UWORD),
FSignature("memcopy" , false, listOf(
FParam("from", IterableDatatypes + DataType.UWORD),
FParam("to", IterableDatatypes + DataType.UWORD),

View File

@ -931,6 +931,14 @@ target()
- 16 = compiled for CommanderX16 with 65C02 CPU
- 64 = compiled for Commodore-64 with 6502/6510 CPU
memory(name, size)
Statically allocates a fixed portion of memory of the given size in bytes, and returns its address.
Slabs are considered identical if their name and size are the same.
This can be used to allocate parts of the memory where a normal byte array would
not suffice for instance if you need more than 256 bytes, and/or don't want to work
with fixed memory addresses for buffers.
The portion of memory cannot be used as an array, you only have the address of the first byte.
progend()
Returns the last address of the program in memory + 1.
Can be used to load dynamic data after the program, instead of hardcoding something.

View File

@ -2,6 +2,7 @@
TODO
====
- detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)

View File

@ -1,19 +1,42 @@
;%import test_stack
;%import textio
%import gfx2
%import test_stack
%import textio
%zeropage basicsafe
%option no_sysinit
main {
sub start () {
; txt.lowercase()
; txt.print_ub(txt.width())
; txt.chrout('\n')
; txt.print_ub(txt.height())
; txt.chrout('\n')
gfx2.text(0,0,2, "sdafsdf")
; test_stack.test()
thing()
thing()
thing()
thing()
thing()
test_stack.test()
sub thing() -> ubyte {
uword buffer = memory("buffer", 512)
uword buffer2 = memory("buffer", 512)
uword buffer3 = memory("cache", 20)
txt.print_uwhex(buffer, true)
txt.chrout('\n')
txt.print_uwhex(buffer2, true)
txt.chrout('\n')
txt.print_uwhex(buffer3, true)
txt.chrout('\n')
buffer+=$1111
buffer2+=$1111
buffer3+=$1111
txt.print_uwhex(buffer, true)
txt.chrout('\n')
txt.print_uwhex(buffer2, true)
txt.chrout('\n')
txt.print_uwhex(buffer3, true)
txt.chrout('\n')
txt.chrout('\n')
return 0
}
}
}

View File

@ -11,10 +11,10 @@
<option name="HAS_PARENS" value="true" />
<option name="HAS_STRING_ESCAPES" value="true" />
</options>
<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;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<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;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memset;memsetw;min;mkword;msb;progend;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strlen;substr;sum;swap;tan" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memory;memset;memsetw;min;mkword;msb;progend;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strcopy;strlen;substr;sum;swap;tan;target" />
</highlighting>
<extensionMap>
<mapping ext="p8" />