Improve inline assembler parser

This commit is contained in:
drmortalwombat 2021-10-10 20:51:21 +02:00
parent 0e59af5122
commit d493bddf8d
3 changed files with 140 additions and 22 deletions

View File

@ -49,7 +49,6 @@ After four weeks, the compiler has now matured significantly. There are still s
* Simple loop opmtimization * Simple loop opmtimization
* Partial block domination analysis * Partial block domination analysis
* No register use for arguments
* Auto variables placed on fixed stack for known call sequence * Auto variables placed on fixed stack for known call sequence
### Intermediate code generation ### Intermediate code generation
@ -77,7 +76,98 @@ The compiler is command line driven, and creates an executable .prg file.
A list of source files can be provided. A list of source files can be provided.
## Inline Assembler
Inline assembler can be embedded inside of any functions, regardles of their compilation target of byte code or native.
### Accessing variables in assembler
Access to local variables and parameters is done with zero page registers, global variables are accessed using absolute addressing.
void putchar(char c)
{
__asm {
lda c
bne w1
lda #13
w1:
jsr 0xffd2
}
}
A function return value can be provided in the zero page addresses ACCU (+0..+3).
char getchar(void)
{
__asm {
jsr 0xffcf
sta accu
lda #0
sta accu + 1
}
}
Labels are defined with a colon after the name. Pure assembler functions can be defined outside of the scope of a function and accessed using their name inside of other assembler function. One can e.g. set up an interrupt
### Interrupt routines
The C compiler will not generate good interrupt code, it is simply too greedy with the zero page registers. Interrupt code should therefore be written in assembler.
#include <math.h>
// Next line for interrupt
char npos;
// Interrupt routine
__asm irq
{
lda $d019 // Check if it is raster IRQ
and #$01
beq w1
inc $d020 // Start colored section
inc $d021
ldx #20 // Wait for 2/3 lines
l1: dex
bne l1
dec $d020 // End colored section
dec $d021
lda npos // Setup next interrupt
sta $d012
w1:
asl $d019 // Ack interrupt
jmp $ea31 // System IRQ routine
}
int main(void)
{
__asm { sei } // Disable interrupt
*(void **)0x0314 = irq; // Install interrupt routine
*(char *)0xd01a = 1; // Enable raster interrupt
*(char *)0xd011 &= 0x7f; // Set raster line for IRQ
*(char *)0xd012 = 100;
npos = 100;
__asm { cli } // Re-enable interrupt
// Move the interrupt raster line up/down
float f = 0;
while (true)
{
npos = 130 + (int)(100 * sin(f));
f += 0.1;
}
return 0;
}
## Implementation Details ## Implementation Details

View File

@ -668,6 +668,23 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration*
} break; } break;
case DT_CONST_ASSEMBLER:
{
if (!dec->mLinkerObject)
TranslateAssembler(proc->mModule, dec->mValue, nullptr);
InterInstruction* ins = new InterInstruction();
ins->mCode = IC_CONSTANT;
ins->mDst.mType = IT_POINTER;
ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType);
ins->mConst.mVarIndex = dec->mVarIndex;
ins->mConst.mLinkerObject = dec->mLinkerObject;
ins->mConst.mMemory = IM_PROCEDURE;
ins->mConst.mIntConst = 0;
block->Append(ins);
return ExValue(TheVoidPointerTypeDeclaration, ins->mDst.mTemp);
}
case DT_CONST_POINTER: case DT_CONST_POINTER:
{ {
vl = TranslateExpression(procType, proc, block, dec->mValue, breakBlock, continueBlock, inlineMapper); vl = TranslateExpression(procType, proc, block, dec->mValue, breakBlock, continueBlock, inlineMapper);

View File

@ -1991,6 +1991,17 @@ Expression* Parser::ParseAssemblerBaseOperand(void)
else else
mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Identifier for qualification expected"); mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Identifier for qualification expected");
} }
if (exp->mDecValue->mType == DT_CONST_ASSEMBLER)
{
Declaration* ndec = new Declaration(mScanner->mLocation, DT_LABEL);
ndec->mIdent = exp->mDecValue->mIdent;
ndec->mBase = exp->mDecValue;
ndec->mInteger = 0;
exp->mDecValue = ndec;
exp->mDecType = TheUnsignedIntTypeDeclaration;
}
break; break;
default: default:
mErrors->Error(mScanner->mLocation, EERR_ASM_INVALD_OPERAND, "Invalid assembler operand"); mErrors->Error(mScanner->mLocation, EERR_ASM_INVALD_OPERAND, "Invalid assembler operand");
@ -2286,7 +2297,7 @@ Expression* Parser::ParseAssembler(void)
{ {
ilast->mAsmInsType = ins; ilast->mAsmInsType = ins;
mScanner->NextToken(); mScanner->NextToken();
if (mScanner->mToken == TK_EOL) if (mScanner->mToken == TK_EOL || mScanner->mToken == TK_CLOSE_BRACE)
ilast->mAsmInsMode = ASMIM_IMPLIED; ilast->mAsmInsMode = ASMIM_IMPLIED;
else if (mScanner->mToken == TK_HASH) else if (mScanner->mToken == TK_HASH)
{ {
@ -2382,12 +2393,12 @@ Expression* Parser::ParseAssembler(void)
if (ilast->mAsmInsType == ASMIT_BYTE) if (ilast->mAsmInsType == ASMIT_BYTE)
ilast->mAsmInsMode = ASMIM_IMMEDIATE; ilast->mAsmInsMode = ASMIM_IMMEDIATE;
if (mScanner->mToken != TK_EOL) if (mScanner->mToken != TK_EOL && mScanner->mToken != TK_CLOSE_BRACE)
{ {
mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "End of line expected"); mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "End of line expected");
} }
while (mScanner->mToken != TK_EOL && mScanner->mToken != TK_EOF) while (mScanner->mToken != TK_EOL && mScanner->mToken != TK_EOF && mScanner->mToken != TK_CLOSE_BRACE)
mScanner->NextToken(); mScanner->NextToken();
offset += AsmInsSize(ilast->mAsmInsType, ilast->mAsmInsMode); offset += AsmInsSize(ilast->mAsmInsType, ilast->mAsmInsMode);