1
0
mirror of https://github.com/cc65/cc65.git synced 2024-12-23 04:30:10 +00:00

Merge pull request #424 from clbr/trampoline

Trampoline support
This commit is contained in:
Oliver Schmidt 2017-05-18 16:29:34 +02:00 committed by GitHub
commit 11b01b908d
12 changed files with 616 additions and 1 deletions

View File

@ -1229,6 +1229,30 @@ parameter with the <tt/#pragma/.
</verb></tscreen>
<sect1><tt>#pragma wrapped-call (&lt;push&rt;, &lt;name&gt;, &lt;identifier&gt;)</tt><label id="pragma-wrapped-call"><p>
This pragma sets a wrapper for functions, often used for trampolines.
The name is a function returning void and taking no parameters.
The identifier is an 8-bit number that's set to tmp4.
The address of the function is passed in ptr4.
This is useful for example with banked memory, to automatically
switch banks to where this function resides, and then restore
the bank when it returns.
The <tt/#pragma/ requires the push and pop parameters as explained above.
Example:
<tscreen><verb>
void mytrampoline(void);
#pragma wrapped-call (push, mytrampoline, 0)
void somefunc(void);
#pragma wrapped-call (pop)
</verb></tscreen>
<sect1><tt>#pragma writable-strings ([push,] on|off)</tt><label id="pragma-writable-strings"><p>
Changes the storage location of string literals. For historical reasons,

10
libsrc/runtime/callptr4.s Normal file
View File

@ -0,0 +1,10 @@
;
; CC65 runtime: call function via pointer in ptr4
;
.export callptr4
.importzp ptr4
callptr4:
jmp (ptr4) ; jump there

View File

@ -58,6 +58,7 @@
#include "scanner.h"
#include "standard.h"
#include "symtab.h"
#include "wrappedcall.h"
#include "typeconv.h"
@ -1315,6 +1316,8 @@ static FuncDesc* ParseFuncDecl (void)
{
unsigned Offs;
SymEntry* Sym;
SymEntry* WrappedCall;
unsigned char WrappedCallData;
/* Create a new function descriptor */
FuncDesc* F = NewFuncDesc ();
@ -1380,6 +1383,13 @@ static FuncDesc* ParseFuncDecl (void)
/* Leave the lexical level remembering the symbol tables */
RememberFunctionLevel (F);
/* Did we have a WrappedCall for this function? */
GetWrappedCall((void **) &WrappedCall, &WrappedCallData);
if (WrappedCall) {
F->WrappedCall = WrappedCall;
F->WrappedCallData = WrappedCallData;
}
/* Return the function descriptor */
return F;
}
@ -1447,6 +1457,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
/* Function declaration */
FuncDesc* F;
SymEntry* PrevEntry;
/* Skip the opening paren */
NextToken ();
@ -1460,6 +1471,16 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
Qualifiers &= ~T_QUAL_FASTCALL;
}
/* Was there a previous entry? If so, copy WrappedCall info from it */
PrevEntry = FindGlobalSym (D->Ident);
if (PrevEntry && PrevEntry->Flags & SC_FUNC) {
FuncDesc* D = PrevEntry->V.F.Func;
if (D->WrappedCall && !F->WrappedCall) {
F->WrappedCall = D->WrappedCall;
F->WrappedCallData = D->WrappedCallData;
}
}
/* Add the function type. Be sure to bounds check the type buffer */
NeedTypeSpace (D, 1);
D->Type[D->Index].C = T_FUNC | Qualifiers;

View File

@ -536,6 +536,10 @@ static void FunctionCall (ExprDesc* Expr)
/* Special handling for function pointers */
if (IsFuncPtr) {
if (Func->WrappedCall) {
Warning("Calling a wrapped function via a pointer, wrapped-call will not be used");
}
/* If the function is not a fastcall function, load the pointer to
** the function into the primary.
*/
@ -584,7 +588,47 @@ static void FunctionCall (ExprDesc* Expr)
} else {
/* Normal function */
g_call (TypeOf (Expr->Type), (const char*) Expr->Name, ParamSize);
if (Func->WrappedCall) {
char tmp[64];
StrBuf S = AUTO_STRBUF_INITIALIZER;
/* Store the WrappedCall data in tmp4 */
sprintf(tmp, "ldy #%u", Func->WrappedCallData);
SB_AppendStr (&S, tmp);
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "sty tmp4");
g_asmcode (&S);
SB_Clear(&S);
/* Store the original function address in ptr4 */
SB_AppendStr (&S, "ldy #<(_");
SB_AppendStr (&S, (const char*) Expr->Name);
SB_AppendChar (&S, ')');
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "sty ptr4");
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "ldy #>(_");
SB_AppendStr (&S, (const char*) Expr->Name);
SB_AppendChar (&S, ')');
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "sty ptr4+1");
g_asmcode (&S);
SB_Clear(&S);
SB_Done (&S);
g_call (TypeOf (Expr->Type), Func->WrappedCall->Name, ParamSize);
} else {
g_call (TypeOf (Expr->Type), (const char*) Expr->Name, ParamSize);
}
}

View File

@ -60,6 +60,8 @@ FuncDesc* NewFuncDesc (void)
F->ParamCount = 0;
F->ParamSize = 0;
F->LastParam = 0;
F->WrappedCall = 0;
F->WrappedCallData = 0;
/* Return the new struct */
return F;

View File

@ -67,6 +67,8 @@ struct FuncDesc {
unsigned ParamCount; /* Number of parameters */
unsigned ParamSize; /* Size of the parameters */
struct SymEntry* LastParam; /* Pointer to last parameter */
struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */
unsigned char WrappedCallData;/* The WrappedCall's user data */
};

View File

@ -51,6 +51,7 @@
#include "scanstrbuf.h"
#include "symtab.h"
#include "pragma.h"
#include "wrappedcall.h"
@ -88,6 +89,7 @@ typedef enum {
PRAGMA_STATIC_LOCALS,
PRAGMA_STATICLOCALS, /* obsolete */
PRAGMA_WARN,
PRAGMA_WRAPPED_CALL,
PRAGMA_WRITABLE_STRINGS,
PRAGMA_ZPSYM,
PRAGMA_COUNT
@ -123,6 +125,7 @@ static const struct Pragma {
{ "static-locals", PRAGMA_STATIC_LOCALS },
{ "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */
{ "warn", PRAGMA_WARN },
{ "wrapped-call", PRAGMA_WRAPPED_CALL },
{ "writable-strings", PRAGMA_WRITABLE_STRINGS },
{ "zpsym", PRAGMA_ZPSYM },
};
@ -446,6 +449,84 @@ ExitPoint:
}
static void WrappedCallPragma (StrBuf* B)
/* Handle the wrapped-call pragma */
{
StrBuf S = AUTO_STRBUF_INITIALIZER;
const char *Name;
long Val;
SymEntry *Entry;
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
case PP_NONE:
Error ("Push or pop required");
break;
case PP_PUSH:
break;
case PP_POP:
PopWrappedCall();
/* Done */
goto ExitPoint;
case PP_ERROR:
/* Bail out */
goto ExitPoint;
default:
Internal ("Invalid result from ParsePushPop");
}
/* A symbol argument must follow */
if (!SB_GetSym (B, &S, NULL)) {
goto ExitPoint;
}
/* Skip the following comma */
if (!GetComma (B)) {
/* Error already flagged by GetComma */
Error ("Value required for wrapped-call identifier");
goto ExitPoint;
}
if (!GetNumber (B, &Val)) {
Error ("Value required for wrapped-call identifier");
goto ExitPoint;
}
if (Val < 0 || Val > 255) {
Error ("Identifier must be between 0-255");
goto ExitPoint;
}
/* Get the string */
Name = SB_GetConstBuf (&S);
Entry = FindSym(Name);
/* Check if the name is valid */
if (Entry && Entry->Flags & SC_FUNC) {
PushWrappedCall(Entry, Val);
Entry->Flags |= SC_REF;
} else {
/* Segment name is invalid */
Error ("Wrapped-call target does not exist or is not a function");
}
ExitPoint:
/* Call the string buf destructor */
SB_Done (&S);
}
static void CharMapPragma (StrBuf* B)
/* Change the character map */
@ -791,6 +872,10 @@ static void ParsePragma (void)
FlagPragma (&B, &StaticLocals);
break;
case PRAGMA_WRAPPED_CALL:
WrappedCallPragma(&B);
break;
case PRAGMA_WARN:
WarnPragma (&B);
break;

102
src/cc65/wrappedcall.c Normal file
View File

@ -0,0 +1,102 @@
/*****************************************************************************/
/* */
/* wrappedcall.c */
/* */
/* WrappedCall management */
/* */
/* */
/* */
/* (C) 2017, Mega Cat Studios */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdarg.h>
#include <string.h>
/* common */
#include "chartype.h"
#include "check.h"
#include "coll.h"
#include "scanner.h"
#include "intptrstack.h"
#include "xmalloc.h"
/* cc65 */
#include "codeent.h"
#include "error.h"
#include "wrappedcall.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* WrappedCalls */
static IntPtrStack WrappedCalls;
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void PushWrappedCall (void *Ptr, unsigned char Val)
/* Push the current WrappedCall */
{
if (IPS_IsFull (&WrappedCalls)) {
Error ("WrappedCall stack overflow");
} else {
IPS_Push (&WrappedCalls, Val, Ptr);
}
}
void PopWrappedCall (void)
/* Remove the current WrappedCall */
{
if (IPS_GetCount (&WrappedCalls) < 1) {
Error ("WrappedCall stack is empty");
} else {
IPS_Drop (&WrappedCalls);
}
}
void GetWrappedCall (void **Ptr, unsigned char *Val)
/* Get the current WrappedCall */
{
if (IPS_GetCount (&WrappedCalls) < 1) {
*Ptr = NULL;
*Val = 0;
} else {
long Temp;
IPS_Get (&WrappedCalls, &Temp, Ptr);
*Val = Temp;
}
}

65
src/cc65/wrappedcall.h Normal file
View File

@ -0,0 +1,65 @@
/*****************************************************************************/
/* */
/* wrappedcall.h */
/* */
/* Wrapped-call management */
/* */
/* */
/* */
/* (C) 2017, Mega Cat Studios */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#ifndef WRAPPEDCALL_H
#define WRAPPEDCALL_H
#include <stdio.h>
/* common */
#include "attrib.h"
/* cc65 */
#include "opcodes.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void PushWrappedCall (void *Ptr, unsigned char Val);
/* Push the current WrappedCall */
void PopWrappedCall (void);
/* Pop the current WrappedCall */
void GetWrappedCall (void **Ptr, unsigned char *Val);
/* Get the current WrappedCall, if any */
/* End of wrappedcall.h */
#endif

90
src/common/intptrstack.c Normal file
View File

@ -0,0 +1,90 @@
/*****************************************************************************/
/* */
/* intptrstack.c */
/* */
/* Integer+ptr stack used for program settings */
/* */
/* */
/* */
/* (C) 2017, Mega Cat Studios */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
/* common */
#include "check.h"
#include "intptrstack.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void IPS_Get (const IntPtrStack* S, long *Val, void **Ptr)
/* Get the value on top of an int stack */
{
PRECONDITION (S->Count > 0);
if (Val) *Val = S->Stack[S->Count-1].val;
if (Ptr) *Ptr = S->Stack[S->Count-1].ptr;
}
void IPS_Set (IntPtrStack* S, long Val, void *Ptr)
/* Set the value on top of an int stack */
{
PRECONDITION (S->Count > 0);
S->Stack[S->Count-1].val = Val;
S->Stack[S->Count-1].ptr = Ptr;
}
void IPS_Drop (IntPtrStack* S)
/* Drop a value from an int stack */
{
PRECONDITION (S->Count > 0);
--S->Count;
}
void IPS_Push (IntPtrStack* S, long Val, void *Ptr)
/* Push a value onto an int stack */
{
PRECONDITION (S->Count < sizeof (S->Stack) / sizeof (S->Stack[0]));
S->Stack[S->Count].val = Val;
S->Stack[S->Count++].ptr = Ptr;
}
void IPS_Pop (IntPtrStack* S, long *Val, void **Ptr)
/* Pop a value from an int stack */
{
PRECONDITION (S->Count > 0);
if (Val) *Val = S->Stack[--S->Count].val;
if (Ptr) *Ptr = S->Stack[S->Count].ptr;
}

121
src/common/intptrstack.h Normal file
View File

@ -0,0 +1,121 @@
/*****************************************************************************/
/* */
/* intptrstack.h */
/* */
/* Integer+ptr stack used for program settings */
/* */
/* */
/* */
/* (C) 2017, Mega Cat Studios */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#ifndef INTPTRSTACK_H
#define INTPTRSTACK_H
#include "inline.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
typedef struct IntPtrStack IntPtrStack;
struct IntPtrInner {
long val;
void *ptr;
};
struct IntPtrStack {
unsigned Count;
struct IntPtrInner Stack[8];
};
/* An initializer for an empty int stack */
#define STATIC_INTPTRSTACK_INITIALIZER { 0, { 0, 0 }, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} } }
/* Declare an int stack with the given value as first element */
#define INTPTRSTACK(Val, Ptr) { 1, { {Val, Ptr}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} } }
/*****************************************************************************/
/* Code */
/*****************************************************************************/
#if defined(HAVE_INLINE)
INLINE int IPS_IsFull (const IntPtrStack* S)
/* Return true if there is no space left on the given int stack */
{
return (S->Count >= sizeof (S->Stack) / sizeof (S->Stack[0]));
}
#else
# define IPS_IsFull(S) ((S)->Count >= sizeof ((S)->Stack) / sizeof ((S)->Stack[0]))
#endif
#if defined(HAVE_INLINE)
INLINE int IPS_IsEmpty (const IntPtrStack* S)
/* Return true if there are no values on the given int stack */
{
return (S->Count == 0);
}
#else
# define IPS_IsEmpty(S) ((S)->Count == 0)
#endif
#if defined(HAVE_INLINE)
INLINE unsigned IPS_GetCount (const IntPtrStack* S)
/* Return the number of elements on the given int stack */
{
return S->Count;
}
#else
# define IPS_GetCount(S) (S)->Count
#endif
void IPS_Get (const IntPtrStack* S, long *Val, void **Ptr);
/* Get the value on top of an int stack */
void IPS_Set (IntPtrStack* S, long Val, void *Ptr);
/* Set the value on top of an int stack */
void IPS_Drop (IntPtrStack* S);
/* Drop a value from an int stack */
void IPS_Push (IntPtrStack* S, long Val, void *Ptr);
/* Push a value onto an int stack */
void IPS_Pop (IntPtrStack* S, long *Val, void **Ptr);
/* Pop a value from an int stack */
/* End of IntPtrStack.h */
#endif

49
test/val/trampoline.c Normal file
View File

@ -0,0 +1,49 @@
/*
!!DESCRIPTION!! wrapped-call pragma used for trampolines
!!ORIGIN!! cc65 regression tests
!!LICENCE!! Public Domain
!!AUTHOR!! Lauri Kasanen
*/
static unsigned char flag;
static void trampoline_set() {
asm("ldy tmp4");
asm("sty %v", flag);
asm("jsr callptr4");
}
void trampoline_inc() {
asm("inc %v", flag);
asm("jsr callptr4");
}
void func3() {
}
#pragma wrapped-call(push, trampoline_inc, 0)
void func2() {
func3();
}
#pragma wrapped-call(push, trampoline_set, 4)
void func1(void);
#pragma wrapped-call(pop)
#pragma wrapped-call(pop)
void func1() {
func2();
}
int main(void)
{
flag = 0;
func1();
return flag == 5 ? 0 : 1;
}