mirror of
https://github.com/irmen/prog8.git
synced 2025-03-15 03:29:42 +00:00
added %jmptable
This commit is contained in:
parent
8d2410622c
commit
efd41260f2
@ -171,6 +171,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
}
|
||||
is PtDefer -> "<defer>"
|
||||
is PtIfExpression -> "<ifexpr>"
|
||||
is PtJmpTable -> "<jmptable>"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,3 +193,6 @@ class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
|
||||
|
||||
|
||||
class PtDefer(position: Position): PtNode(position), IPtStatementContainer
|
||||
|
||||
|
||||
class PtJmpTable(position: Position) : PtNode(position) // contains only PtIdentifier nodes
|
||||
|
@ -606,6 +606,7 @@ class AsmGen6502Internal (
|
||||
is PtBlock -> throw AssemblyError("block should have been handled elsewhere")
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtNodeGroup -> stmt.children.forEach { translate(it) }
|
||||
is PtJmpTable -> translate(stmt)
|
||||
is PtNop -> {}
|
||||
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||
}
|
||||
@ -982,6 +983,14 @@ $repeatLabel""")
|
||||
out(stmt.name)
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtJmpTable) {
|
||||
out(" ; jumptable")
|
||||
for(name in stmt.children) {
|
||||
out(" jmp ${asmSymbolName((name as PtIdentifier).name)}")
|
||||
}
|
||||
out(" ; end jumptable")
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtConditionalBranch) {
|
||||
if(stmt.trueScope.children.isEmpty() && stmt.falseScope.children.isNotEmpty())
|
||||
throw AssemblyError("only else part contains code, shoud have been switched already")
|
||||
|
@ -1854,6 +1854,14 @@ class IRCodeGen(
|
||||
is PtLabel -> {
|
||||
irBlock += IRCodeChunk(child.name, null)
|
||||
}
|
||||
is PtJmpTable -> {
|
||||
irBlock += IRCodeChunk(null, null).also {
|
||||
for(addr in child.children) {
|
||||
addr as PtIdentifier
|
||||
it += IRInstruction(Opcode.JUMP, labelSymbol = addr.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
}
|
||||
if (callgraph.unused(block)) {
|
||||
if (block.statements.any { it !is VarDecl || it.type == VarDeclType.VAR } && "ignore_unused" !in block.options()) {
|
||||
if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
if (!block.statements.any { it is Subroutine && !it.hasBeenInlined })
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
if (!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
|
||||
|
@ -1052,6 +1052,15 @@ internal class AstChecker(private val program: Program,
|
||||
if(directive.args.size!=1 || directive.args[0].string !in allowedEncodings)
|
||||
err("invalid encoding directive, expected one of $allowedEncodings")
|
||||
}
|
||||
"%jmptable" -> {
|
||||
for(arg in directive.args) {
|
||||
val target = directive.definingScope.lookup(arg.string!!.split('.'))
|
||||
if(target==null)
|
||||
errors.err("undefined symbol: ${arg.string}", arg.position)
|
||||
else if (target !is Subroutine)
|
||||
errors.err("jmptable entry can only be a subroutine: ${arg.string}", arg.position)
|
||||
}
|
||||
}
|
||||
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
|
||||
}
|
||||
super.visit(directive)
|
||||
|
@ -211,11 +211,22 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val (vardecls, statements) = srcBlock.statements.partition { it is VarDecl }
|
||||
val src = srcBlock.definingModule.source
|
||||
val block = PtBlock(srcBlock.name, srcBlock.isInLibrary, src,
|
||||
PtBlock.Options(srcBlock.address, forceOutput, noSymbolPrefixing, veraFxMuls, ignoreUnused),
|
||||
srcBlock.position)
|
||||
|
||||
for(directive in directives.filter { it.directive == "%jmptable" }) {
|
||||
val table = PtJmpTable(directive.position)
|
||||
directive.args.forEach {
|
||||
table.add(PtIdentifier(it.string!!, DataType.forDt(BaseDataType.UNDEFINED), it.position))
|
||||
}
|
||||
block.add(table)
|
||||
}
|
||||
|
||||
makeScopeVarsDecls(vardecls).forEach { block.add(it) }
|
||||
for (stmt in statements)
|
||||
block.add(transformStatement(stmt))
|
||||
|
@ -440,8 +440,14 @@ private fun DatatypeContext.toAst(): BaseDataType {
|
||||
private fun ArrayindexContext.toAst() : ArrayIndex =
|
||||
ArrayIndex(expression().toAst(), toPosition())
|
||||
|
||||
internal fun DirectiveContext.toAst() : Directive =
|
||||
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
||||
internal fun DirectiveContext.toAst() : Directive {
|
||||
if(directivenamelist() != null) {
|
||||
val identifiers = directivenamelist().scoped_identifier().map { DirectiveArg(it.text, null, it.toPosition()) }
|
||||
return Directive(directivename.text, identifiers, toPosition())
|
||||
}
|
||||
else
|
||||
return Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
||||
}
|
||||
|
||||
private fun DirectiveargContext.toAst() : DirectiveArg {
|
||||
val str = stringliteral()
|
||||
|
@ -111,6 +111,7 @@ data class Directive(val directive: String, val args: List<DirectiveArg>, overri
|
||||
// "%breakpoint",
|
||||
// "%encoding",
|
||||
// "%import",
|
||||
// "%jmptable",
|
||||
// "%launcher",
|
||||
// "%option",
|
||||
// "%output",
|
||||
|
@ -62,6 +62,17 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
importedBy[importedModule] = importedBy.getValue(importedModule) + thisModule
|
||||
}
|
||||
}
|
||||
else if (directive.directive == "%jmptable") {
|
||||
for(arg in directive.args) {
|
||||
val scopedName = arg.string!!.split('.')
|
||||
val target = directive.definingScope.lookup(scopedName)
|
||||
if(target is Subroutine) {
|
||||
val identifier = IdentifierReference(scopedName, arg.position)
|
||||
identifier.linkParents(directive)
|
||||
visit(identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(directive)
|
||||
}
|
||||
@ -107,8 +118,11 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
val target = identifier.targetStatement(program)
|
||||
if(target!=null)
|
||||
if(target!=null) {
|
||||
allIdentifiersAndTargets.add(identifier to target)
|
||||
if(target is Subroutine)
|
||||
notCalledButReferenced += target
|
||||
}
|
||||
|
||||
// if it's a scoped identifier, the subroutines in the name are also referenced!
|
||||
val scope = identifier.definingScope
|
||||
|
@ -362,6 +362,18 @@ Directives
|
||||
You can import modules one at a time, and importing a module more than once has no effect.
|
||||
|
||||
|
||||
.. data:: %jmptable ( lib.routine1, lib.routine2, ... )
|
||||
|
||||
Level: block.
|
||||
This builds a compact "jump table" meant to be used in libraries.
|
||||
You can put the elements of the table on different lines if you wish.
|
||||
It outputs a sequence of JMP machine code instructions jumping to each
|
||||
of the given subroutines in the jmptable list::
|
||||
|
||||
jmp lib.routine1
|
||||
jmp lib.routine2
|
||||
...
|
||||
|
||||
.. data:: %launcher <type>
|
||||
|
||||
Level: module.
|
||||
|
@ -1,11 +1,6 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- diskio has a problem when running certain code when a not-connected drive number is selected, and when that is run in a hiram bank
|
||||
(this is not a prog8 problem, see https://discord.com/channels/547559626024157184/629863245934755860/1336805196634001438)
|
||||
|
||||
- for creating libraries, something like %jmptable( block.func1, block.func2, ... ) could be useful to create a compact jump table, and possibly generating extsub definitions as well. Problem: directives cannot span multiple lines atm.
|
||||
|
||||
- Make neo and atari targets external via configs? They are very bare bones atm so easier to contribute to if they're configurable externally? What about the pet32 target
|
||||
|
||||
- add paypal donation button as well?
|
||||
|
@ -19,17 +19,17 @@
|
||||
|
||||
main {
|
||||
; Create a jump table as first thing in the library.
|
||||
uword[] @shared @nosplit jumptable = [
|
||||
; NOTE: the compiler has inserted a single JMP instruction at the start
|
||||
; of the 'main' block, that jumps to the start() routine.
|
||||
; This is convenient because the rest of the jump table simply follows it,
|
||||
; making the first jump neatly be the required initialization routine
|
||||
; for the library (initializing variables and BSS region).
|
||||
; Btw, $4c = opcode for JMP.
|
||||
$4c00, &fileselector.configure,
|
||||
$4c00, &fileselector.configure_appearance,
|
||||
$4c00, &fileselector.select,
|
||||
]
|
||||
; NOTE: the compiler has inserted a single JMP instruction at the start
|
||||
; of the 'main' block, that jumps to the start() routine.
|
||||
; This is convenient because the rest of the jump table simply follows it,
|
||||
; making the first jump neatly be the required initialization routine
|
||||
; for the library (initializing variables and BSS region).
|
||||
; Think of it as the implicit first entry of the jump table.
|
||||
%jmptable (
|
||||
fileselector.configure,
|
||||
fileselector.configure_appearance,
|
||||
fileselector.select
|
||||
)
|
||||
|
||||
sub start() {
|
||||
; has to remain here for initialization
|
||||
|
@ -33,11 +33,11 @@ fselector {
|
||||
extsub $a000 = init() clobbers(A)
|
||||
|
||||
; what entry types should be displayed (default=all)
|
||||
extsub $a004 = config(ubyte drivenumber @A, ubyte types @Y) clobbers(A)
|
||||
extsub $a003 = config(ubyte drivenumber @A, ubyte types @Y) clobbers(A)
|
||||
|
||||
; configure the position and appearance of the dialog
|
||||
extsub $a008 = config_appearance(ubyte column @R0, ubyte row @R1, ubyte max_entries @R2, ubyte normalcolors @R3, ubyte selectedcolors @R4) clobbers(A)
|
||||
extsub $a006 = config_appearance(ubyte column @R0, ubyte row @R1, ubyte max_entries @R2, ubyte normalcolors @R3, ubyte selectedcolors @R4) clobbers(A)
|
||||
|
||||
; show the file selector dialog. Normal pattern would be "*" to include everything. Returns the selected entry name, or 0 if error or nothing selected.
|
||||
extsub $a00c = select(str pattern @AY) clobbers(X) -> uword @AY
|
||||
extsub $a009 = select(str pattern @AY) clobbers(X) -> uword @AY
|
||||
}
|
||||
|
@ -7,14 +7,19 @@
|
||||
|
||||
main {
|
||||
; Create a jump table as first thing in the library.
|
||||
uword[] @shared @nosplit jumptable = [
|
||||
; NOTE: the compiler has inserted a single JMP instruction at the start of the 'main' block, that jumps to the start() routine.
|
||||
; This is convenient because the rest of the jump table simply follows it,
|
||||
; making the first jump neatly be the required initialization routine for the library (initializing variables and BSS region).
|
||||
; btw, $4c = opcode for JMP.
|
||||
$4c00, &library.func1,
|
||||
$4c00, &library.func2,
|
||||
]
|
||||
; uword[] @shared @nosplit jumptable = [
|
||||
; ; NOTE: the compiler has inserted a single JMP instruction at the start of the 'main' block, that jumps to the start() routine.
|
||||
; ; This is convenient because the rest of the jump table simply follows it,
|
||||
; ; making the first jump neatly be the required initialization routine for the library (initializing variables and BSS region).
|
||||
; ; btw, $4c = opcode for JMP.
|
||||
; $4c00, &library.func1,
|
||||
; $4c00, &library.func2,
|
||||
; ]
|
||||
|
||||
%jmptable (
|
||||
library.func1,
|
||||
library.func2,
|
||||
)
|
||||
|
||||
sub start() {
|
||||
; has to be here for initialization
|
||||
|
@ -406,11 +406,7 @@ class IRBlock(
|
||||
operator fun plusAssign(sub: IRAsmSubroutine) { children += sub }
|
||||
operator fun plusAssign(asm: IRInlineAsmChunk) { children += asm }
|
||||
operator fun plusAssign(binary: IRInlineBinaryChunk) { children += binary }
|
||||
operator fun plusAssign(irCodeChunk: IRCodeChunk) {
|
||||
// this is for a separate label in the block scope. (random code statements are not allowed)
|
||||
require(irCodeChunk.isEmpty() && irCodeChunk.label!=null)
|
||||
children += irCodeChunk
|
||||
}
|
||||
operator fun plusAssign(irCodeChunk: IRCodeChunk) { children += irCodeChunk }
|
||||
|
||||
fun isEmpty(): Boolean = children.isEmpty() || children.all { it.isEmpty() }
|
||||
fun isNotEmpty(): Boolean = !isEmpty()
|
||||
|
@ -137,10 +137,12 @@ unconditionaljump : 'goto' expression ;
|
||||
|
||||
directive :
|
||||
directivename=('%output' | '%launcher' | '%zeropage' | '%zpreserved' | '%zpallowed' | '%address' | '%memtop' | '%import' |
|
||||
'%breakpoint' | '%asminclude' | '%asmbinary' | '%option' | '%encoding' | '%align' )
|
||||
(directivearg? | directivearg (',' directivearg)*)
|
||||
'%breakpoint' | '%asminclude' | '%asmbinary' | '%option' | '%encoding' | '%align' | '%jmptable' )
|
||||
(directivenamelist | (directivearg? | directivearg (',' directivearg)*))
|
||||
;
|
||||
|
||||
directivenamelist: '(' EOL? scoped_identifier (',' EOL? scoped_identifier)* ','? EOL?')' ;
|
||||
|
||||
directivearg : stringliteral | identifier | integerliteral ;
|
||||
|
||||
vardecl: datatype (arrayindex | ARRAYSIG)? TAG* identifier (',' identifier)* ;
|
||||
|
@ -12,7 +12,7 @@
|
||||
<option name="HAS_STRING_ESCAPES" value="true" />
|
||||
</options>
|
||||
<keywords keywords="&;&<;&>;->;@;alias;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;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;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
|
||||
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nosplit;@nozp;@requirezp;@shared;@split;@zp;atascii:;c64os:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
|
||||
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%jmptable;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nosplit;@nozp;@requirezp;@shared;@split;@zp;atascii:;c64os:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
|
||||
<keywords3 keywords="bool;byte;const;float;long;str;ubyte;uword;void;word" />
|
||||
<keywords4 keywords="abs;bmx;call;callfar;callfar2;cbm;clamp;cmp;conv;cx16;defer;divmod;floats;len;lsb;lsw;math;max;memory;min;mkword;msb;msw;peek;peekf;peekw;poke;pokef;pokew;psg;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt;strings;sys;txt;verafx" />
|
||||
</highlighting>
|
||||
|
@ -25,7 +25,7 @@
|
||||
<Keywords name="Folders in comment, middle"></Keywords>
|
||||
<Keywords name="Folders in comment, close"></Keywords>
|
||||
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp</Keywords>
|
||||
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
||||
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%jmptable
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
||||
<Keywords name="Keywords3">inline sub asmsub extsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat unroll
break continue return goto</Keywords>
|
||||
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
|
||||
<Keywords name="Keywords5">true false
not and or xor
as to downto |></Keywords>
|
||||
|
Loading…
x
Reference in New Issue
Block a user