Improve inline assembler parser
This commit is contained in:
parent
0e59af5122
commit
d493bddf8d
92
README.md
92
README.md
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue