From e807ac752b4653487ec5bdf516205e24a1c657eb Mon Sep 17 00:00:00 2001
From: notaz <notasas@gmail.com>
Date: Wed, 20 Feb 2008 21:35:19 +0000
Subject: [PATCH] svp compiler direct calls

git-svn-id: file:///home/notaz/opt/svn/PicoDrive@360 be3aeb3a-fb24-0410-a615-afba39da0efa
---
 Pico/Cart.c                |   9 ++-
 Pico/Pico.c                |   1 +
 Pico/PicoInt.h             |   1 +
 Pico/carthw/carthw.h       |   1 +
 Pico/carthw/svp/compiler.c | 113 +++++++++++++++----------------------
 Pico/carthw/svp/compiler.h |  11 ++++
 Pico/carthw/svp/gen_arm.c  |  70 ++++++++++++++---------
 Pico/carthw/svp/ssp16.c    |   5 +-
 Pico/carthw/svp/ssp16.h    |   4 --
 Pico/carthw/svp/stub_arm.S |  81 ++++++++++++++++++++++++++
 Pico/carthw/svp/stub_arm.s |  23 --------
 Pico/carthw/svp/svp.c      |  36 +++++++++++-
 platform/gp2x/Makefile     |   3 +
 13 files changed, 235 insertions(+), 123 deletions(-)
 create mode 100644 Pico/carthw/svp/compiler.h
 create mode 100644 Pico/carthw/svp/stub_arm.S
 delete mode 100644 Pico/carthw/svp/stub_arm.s

diff --git a/Pico/Cart.c b/Pico/Cart.c
index e616622a..5e01e9ac 100644
--- a/Pico/Cart.c
+++ b/Pico/Cart.c
@@ -15,6 +15,8 @@
 
 static char *rom_exts[] = { "bin", "gen", "smd", "iso" };
 
+void (*PicoCartUnloadHook)(void) = NULL;
+
 void (*PicoCartLoadProgressCB)(int percent) = NULL;
 void (*PicoCDLoadProgressCB)(int percent) = NULL; // handled in Pico/cd/cd_file.c
 
@@ -493,6 +495,11 @@ int PicoCartInsert(unsigned char *rom,unsigned int romsize)
   Pico.rom=rom;
   Pico.romsize=romsize;
 
+  if (PicoCartUnloadHook != NULL) {
+    PicoCartUnloadHook();
+    PicoCartUnloadHook = NULL;
+  }
+
   PicoMemResetHooks();
   PicoDmaHook = NULL;
   PicoResetHook = NULL;
@@ -646,7 +653,7 @@ void PicoCartDetect(void)
   if (name_cmp("Virtua Racing") == 0 ||
       name_cmp("VIRTUA RACING") == 0)
   {
-    PicoSVPInit();
+    PicoSVPStartup();
   }
 }
 
diff --git a/Pico/Pico.c b/Pico/Pico.c
index 0e9ee6c2..b07af08d 100644
--- a/Pico/Pico.c
+++ b/Pico/Pico.c
@@ -38,6 +38,7 @@ int PicoInit(void)
   z80_init(); // init even if we aren't going to use it
 
   PicoInitMCD();
+  PicoSVPInit();
 
   SRam.data=0;
 
diff --git a/Pico/PicoInt.h b/Pico/PicoInt.h
index b503330d..f29c20ba 100644
--- a/Pico/PicoInt.h
+++ b/Pico/PicoInt.h
@@ -377,6 +377,7 @@ extern carthw_state_chunk *carthw_chunks;
 
 // Cart.c
 PICO_INTERNAL void PicoCartDetect(void);
+extern void (*PicoCartUnloadHook)(void);
 
 // Debug.c
 int CM_compareRun(int cyc, int is_sub);
diff --git a/Pico/carthw/carthw.h b/Pico/carthw/carthw.h
index 397dc283..e940d580 100644
--- a/Pico/carthw/carthw.h
+++ b/Pico/carthw/carthw.h
@@ -11,6 +11,7 @@ typedef struct {
 extern svp_t *svp;
 
 void PicoSVPInit(void);
+void PicoSVPStartup(void);
 
 unsigned int PicoSVPRead16(unsigned int a, int realsize);
 void PicoSVPWrite8 (unsigned int a, unsigned int d, int realsize);
diff --git a/Pico/carthw/svp/compiler.c b/Pico/carthw/svp/compiler.c
index dac52851..7f10a5bd 100644
--- a/Pico/carthw/svp/compiler.c
+++ b/Pico/carthw/svp/compiler.c
@@ -2,11 +2,10 @@
 // 14 IRAM blocks
 
 #include "../../PicoInt.h"
+#include "compiler.h"
 
-#define TCACHE_SIZE (1024*1024)
 static unsigned int *block_table[0x5090/2];
 static unsigned int *block_table_iram[15][0x800/2];
-static unsigned int *tcache = NULL;
 static unsigned int *tcache_ptr = NULL;
 
 static int had_jump = 0;
@@ -517,44 +516,64 @@ static int get_iram_context(void)
 
 #define PROGRAM(x) ((unsigned short *)svp->iram_rom)[x]
 
+static int translate_op(unsigned int op, int *pc)
+{
+	switch (op >> 9)
+	{
+		// ld d, s
+		case 0x00: break;
+	}
+
+	return -1;
+}
+
 static void *translate_block(int pc)
 {
-	unsigned int op, op1, icount = 0;
+	unsigned int op, op1, imm, ccount = 0;
 	unsigned int *block_start;
+	int ret;
 
 	// create .pool
-	*tcache_ptr++ = (u32) &g_cycles;		// -3 g_cycles
-	*tcache_ptr++ = (u32) &ssp->gr[SSP_PC].v;	// -2 ptr to rPC
-	*tcache_ptr++ = (u32) in_funcs;			// -1 func pool
+	//*tcache_ptr++ = (u32) in_funcs;			// -1 func pool
 
 	printf("translate %04x -> %04x\n", pc<<1, (tcache_ptr-tcache)<<2);
 	block_start = tcache_ptr;
 
 	emit_block_prologue();
 
-	for (; icount < 100;)
+	for (; ccount < 100;)
 	{
-		icount++;
 		//printf("  insn #%i\n", icount);
 		op = PROGRAM(pc++);
 		op1 = op >> 9;
+		imm = (u32)-1;
 
-		emit_mov_const(0, op);
+		if ((op1 & 0xf) == 4 || (op1 & 0xf) == 6)
+			imm = PROGRAM(pc++); // immediate
 
-		// need immediate?
-		if ((op1 & 0xf) == 4 || (op1 & 0xf) == 6) {
-			emit_mov_const(1, PROGRAM(pc++)); // immediate
-		}
+		ret = translate_op(op, &pc);
+		if (ret <= 0)
+		{
+			emit_mov_const(0, op);
 
-		// dump PC
-		emit_pc_inc(block_start, pc);
+			// need immediate?
+			if (imm != (u32)-1)
+				emit_mov_const(1, imm);
 
-		emit_call(block_start, op1);
+			// dump PC
+			emit_pc_dump(pc);
 
-		if (in_funcs[op1] == NULL) {
-			printf("NULL func! op=%08x (%02x)\n", op, op1);
-			exit(1);
+			emit_interpreter_call(in_funcs[op1]);
+
+			if (in_funcs[op1] == NULL) {
+				printf("NULL func! op=%08x (%02x)\n", op, op1);
+				exit(1);
+			}
+			ccount++;
 		}
+		else
+			ccount += ret;
+
 		if (op1 == 0x24 || op1 == 0x26 || // call, bra
 			((op1 == 0 || op1 == 1 || op1 == 4 || op1 == 5 || op1 == 9 || op1 == 0x25) &&
 				(op & 0xf0) == 0x60)) { // ld PC
@@ -562,7 +581,7 @@ static void *translate_block(int pc)
 		}
 	}
 
-	emit_block_epilogue(block_start, icount + 1);
+	emit_block_epilogue(ccount + 1);
 	*tcache_ptr++ = 0xffffffff; // end of block
 	//printf("  %i inst\n", icount);
 
@@ -596,16 +615,12 @@ static void *translate_block(int pc)
 
 // -----------------------------------------------------
 
-int ssp1601_dyn_init(void)
+int ssp1601_dyn_startup(void)
 {
-	tcache = tcache_ptr = malloc(TCACHE_SIZE);
-	if (tcache == NULL) {
-		printf("oom\n");
-		exit(1);
-	}
-	memset(tcache, 0, sizeof(TCACHE_SIZE));
+	memset(tcache, 0, TCACHE_SIZE);
 	memset(block_table, 0, sizeof(block_table));
 	memset(block_table_iram, 0, sizeof(block_table_iram));
+	tcache_ptr = tcache;
 	*tcache_ptr++ = 0xffffffff;
 
 	return 0;
@@ -621,7 +636,7 @@ void ssp1601_dyn_run(int cycles)
 {
 	while (cycles > 0)
 	{
-		void (*trans_entry)(void);
+		int (*trans_entry)(void);
 		if (rPC < 0x800/2)
 		{
 			if (iram_dirty) {
@@ -641,45 +656,9 @@ void ssp1601_dyn_run(int cycles)
 
 		had_jump = 0;
 
-		//printf("enter @ %04x, PC=%04x\n", (PC - tcache)<<1, rPC<<1);
-		g_cycles = 0;
-		//printf("enter %04x\n", rPC);
-		trans_entry();
-		//printf("leave %04x\n", rPC);
-		cycles -= g_cycles;
-/*
-		if (!had_jump) {
-			// no jumps
-			if (pc_old < 0x800/2)
-				rPC += (PC - block_table_iram[iram_context][pc_old]) - 1;
-			else
-				rPC += (PC - block_table[pc_old]) - 1;
-		}
-*/
-		//printf("end   @ %04x, PC=%04x\n", (PC - tcache)<<1, rPC<<1);
-/*
-		if (pc_old < 0x400) {
-			// flush IRAM cache
-			tcache_ptr = block_table[pc_old];
-			block_table[pc_old] = NULL;
-			nblocks--;
-		}
-		if (pc_old >= 0x400 && rPC < 0x400)
-		{
-			int i, crc = chksum_crc32(svp->iram_rom, 0x800);
-			for (i = 0; i < 32; i++)
-				if (iram_crcs[i] == crc) break;
-			if (i == 32) {
-				char name[32];
-				for (i = 0; i < 32 && iram_crcs[i]; i++);
-				iram_crcs[i] = crc;
-				printf("%i IRAMs\n", i+1);
-				sprintf(name, "ir%08x.bin", crc);
-				debug_dump2file(name, svp->iram_rom, 0x800);
-			}
-			printf("CRC %08x %08x\n", crc, iram_id);
-		}
-*/
+		//printf("enter %04x\n", rPC<<1);
+		cycles -= trans_entry();
+		//printf("leave %04x\n", rPC<<1);
 	}
 //	debug_dump2file("tcache.bin", tcache, (tcache_ptr - tcache) << 1);
 //	exit(1);
diff --git a/Pico/carthw/svp/compiler.h b/Pico/carthw/svp/compiler.h
new file mode 100644
index 00000000..1d8dc6cd
--- /dev/null
+++ b/Pico/carthw/svp/compiler.h
@@ -0,0 +1,11 @@
+#define TCACHE_SIZE (1024*1024)
+
+extern unsigned int tcache[];
+
+void regfile_load(void);
+void regfile_store(void);
+
+int  ssp1601_dyn_startup(void);
+void ssp1601_dyn_reset(ssp1601_t *ssp);
+void ssp1601_dyn_run(int cycles);
+
diff --git a/Pico/carthw/svp/gen_arm.c b/Pico/carthw/svp/gen_arm.c
index 30364136..6e661726 100644
--- a/Pico/carthw/svp/gen_arm.c
+++ b/Pico/carthw/svp/gen_arm.c
@@ -1,5 +1,13 @@
 #define EMIT(x) *tcache_ptr++ = x
 
+#define A_R4M  (1 << 4)
+#define A_R5M  (1 << 5)
+#define A_R6M  (1 << 6)
+#define A_R7M  (1 << 7)
+#define A_R8M  (1 << 8)
+#define A_R9M  (1 << 9)
+#define A_R10M (1 << 10)
+#define A_R11M (1 << 11)
 #define A_R14M (1 << 14)
 
 #define A_COND_AL 0xe
@@ -37,6 +45,7 @@
 #define EOP_LDR_IMM(   rd,rn,offset_12) EOP_C_XXR_IMM(A_COND_AL,1,0,1,rn,rd,offset_12)
 #define EOP_LDR_NEGIMM(rd,rn,offset_12) EOP_C_XXR_IMM(A_COND_AL,0,0,1,rn,rd,offset_12)
 #define EOP_LDR_SIMPLE(rd,rn)           EOP_C_XXR_IMM(A_COND_AL,1,0,1,rn,rd,0)
+#define EOP_STR_IMM(   rd,rn,offset_12) EOP_C_XXR_IMM(A_COND_AL,1,0,0,rn,rd,offset_12)
 #define EOP_STR_SIMPLE(rd,rn)           EOP_C_XXR_IMM(A_COND_AL,1,0,0,rn,rd,0)
 
 /* ldm and stm */
@@ -52,6 +61,12 @@
 
 #define EOP_BX(rm) EOP_C_BX(A_COND_AL,rm)
 
+#define EOP_C_B(cond,l,signed_immed_24) \
+	EMIT(((cond)<<28) | 0x0a000000 | ((l)<<24) | (signed_immed_24))
+
+#define EOP_B( signed_immed_24) EOP_C_B(A_COND_AL,0,signed_immed_24)
+#define EOP_BL(signed_immed_24) EOP_C_B(A_COND_AL,1,signed_immed_24)
+
 
 static void emit_mov_const(int d, unsigned int val)
 {
@@ -72,53 +87,56 @@ static void emit_mov_const(int d, unsigned int val)
 		EOP_C_DOP_IMM(A_COND_AL,need_or ? A_OP_ORR : A_OP_MOV, 0, need_or ? d : 0, d, 0, val&0xff);
 }
 
+/*
 static void check_offset_12(unsigned int val)
 {
 	if (!(val & ~0xfff)) return;
 	printf("offset_12 overflow %04x\n", val);
 	exit(1);
 }
+*/
 
-static void emit_block_prologue(void)
+static void check_offset_24(int val)
 {
-	// stack LR
-	EOP_STMFD_ST(A_R14M);			// stmfd r13!, {r14}
+	if (val >= (int)0xff000000 && val <= 0x00ffffff) return;
+	printf("offset_24 overflow %08x\n", val);
+	exit(1);
 }
 
-static void emit_block_epilogue(unsigned int *block_start, int icount)
+static void emit_call(void *target)
 {
-	int back = (tcache_ptr - block_start) + 2;
-	back += 3; // g_cycles
-	check_offset_12(back<<2);
+	int val = (unsigned int *)target - tcache_ptr - 2;
+	check_offset_24(val);
 
-	EOP_LDR_NEGIMM(2,15,back<<2);		// ldr r2,[pc,#back]
-	emit_mov_const(3, icount);
-	EOP_STR_SIMPLE(3,2);			// str r3,[r2]
+	EOP_BL(val & 0xffffff);			// bl target
+}
 
-	EOP_LDMFD_ST(A_R14M);			// ldmfd r13!, {r14}
-	EOP_BX(14);				// bx r14
+static void emit_block_prologue(void)
+{
+	// stack regs
+	EOP_STMFD_ST(A_R4M|A_R5M|A_R6M|A_R7M|A_R8M|A_R9M|A_R10M|A_R11M|A_R14M);	// stmfd r13!, {r4-r11,lr}
+	emit_call(regfile_load);
 }
 
-static void emit_pc_inc(unsigned int *block_start, int pc)
+static void emit_block_epilogue(int icount)
 {
-	int back = (tcache_ptr - block_start) + 2;
-	back += 2; // rPC ptr
-	check_offset_12(back<<2);
+	emit_call(regfile_store);
+	EOP_LDMFD_ST(A_R4M|A_R5M|A_R6M|A_R7M|A_R8M|A_R9M|A_R10M|A_R11M|A_R14M);	// ldmfd r13!, {r4-r11,lr}
+	emit_mov_const(0, icount);
+	EOP_BX(14);				// bx r14
+}
 
-	EOP_LDR_NEGIMM(2,15,back<<2);		// ldr r2,[pc,#back]
+static void emit_pc_dump(int pc)
+{
 	emit_mov_const(3, pc<<16);
-	EOP_STR_SIMPLE(3,2);			// str r3,[r2]
+	EOP_STR_IMM(3,7,0x400+6*4);		// str r3, [r7, #(0x400+6*8)]
 }
 
-static void emit_call(unsigned int *block_start, unsigned int op1)
+static void emit_interpreter_call(void *target)
 {
-	int back = (tcache_ptr - block_start) + 2;
-	back += 1; // func table
-	check_offset_12(back<<2);
-
-	EOP_LDR_NEGIMM(2,15,back<<2);		// ldr r2,[pc,#back]
-	EOP_MOV_REG_SIMPLE(14,15);		// mov lr,pc
-	EOP_LDR_IMM(15,2,op1<<2);		// ldr pc,[r2,#op1]
+	emit_call(regfile_store);
+	emit_call(target);
+	emit_call(regfile_load);
 }
 
 static void handle_caches()
diff --git a/Pico/carthw/svp/ssp16.c b/Pico/carthw/svp/ssp16.c
index b2de4596..ea0529f1 100644
--- a/Pico/carthw/svp/ssp16.c
+++ b/Pico/carthw/svp/ssp16.c
@@ -332,7 +332,10 @@
 }
 
 
-static ssp1601_t *ssp = NULL;
+#ifndef EMBED_INTERPRETER
+static
+#endif
+ssp1601_t *ssp = NULL;
 static unsigned short *PC;
 static int g_cycles;
 
diff --git a/Pico/carthw/svp/ssp16.h b/Pico/carthw/svp/ssp16.h
index c7db4de9..3d8a764d 100644
--- a/Pico/carthw/svp/ssp16.h
+++ b/Pico/carthw/svp/ssp16.h
@@ -58,7 +58,3 @@ typedef struct
 void ssp1601_reset(ssp1601_t *ssp);
 void ssp1601_run(int cycles);
 
-int  ssp1601_dyn_init(void);
-void ssp1601_dyn_reset(ssp1601_t *ssp);
-void ssp1601_dyn_run(int cycles);
-
diff --git a/Pico/carthw/svp/stub_arm.S b/Pico/carthw/svp/stub_arm.S
new file mode 100644
index 00000000..d87d4415
--- /dev/null
+++ b/Pico/carthw/svp/stub_arm.S
@@ -0,0 +1,81 @@
+@ vim:filetype=armasm
+
+.if 0
+#include "compiler.h"
+.endif
+
+.global tcache
+
+.global flush_inval_caches
+.global regfile_load
+.global regfile_store
+
+@ translation cache buffer
+.text
+.align 12 @ 4096
+.size tcache, TCACHE_SIZE
+tcache:
+ .space TCACHE_SIZE
+
+
+.text
+.align 2
+
+
+flush_inval_caches:
+    mov     r2, #0x0  @ must be 0
+    swi     0x9f0002
+    bx      lr
+
+
+@       SSP_GR0, SSP_X,     SSP_Y,   SSP_A,
+@       SSP_ST,  SSP_STACK, SSP_PC,  SSP_P,
+@       SSP_PM0, SSP_PM1,   SSP_PM2, SSP_XST,
+@       SSP_PM4, SSP_gr13,  SSP_PMC, SSP_AL
+
+@ register map:
+@ r4:  XXYY
+@ r5:  A
+@ r6:  STACK and emu flags
+@ r7:  SSP context
+@ r8:  r0-r2
+@ r9:  r4-r6
+@ r10: P
+@ r11: cycles
+
+@ trashes r2,r3
+
+regfile_load:
+    ldr     r7, =ssp
+    ldr     r7, [r7]
+    add     r2, r7, #0x400
+    add     r2, r2, #4
+    ldmia   r2, {r3,r4,r5,r6,r8}
+    mov     r3, r3, lsr #16
+    mov     r3, r3, lsl #16
+    orr     r4, r3, r4, lsr #16         @ XXYY
+    bic     r6, r6, #0xff
+    orr     r6, r6, r8, lsr #16         @ flags + STACK
+    ldr     r8, [r7, #0x440]            @ r0-r2
+    ldr     r9, [r7, #0x444]            @ r4-r6
+    ldr     r10,[r7, #(0x400+7*4)]      @ P
+    bx      lr
+
+
+regfile_store:
+    str     r10,[r7, #(0x400+7*4)]      @ P
+    str     r8, [r7, #0x440]            @ r0-r2
+    str     r9, [r7, #0x444]            @ r4-r6
+    mov     r9, r6, lsl #16
+    and     r9, r9, #(7<<16)            @ STACK
+    bic     r6, r6, #0xff               @ ST
+    mov     r3, r4, lsl #16             @ Y
+    mov     r2, r4, lsr #16
+    mov     r2, r2, lsl #16             @ X
+    add     r8, r7, #0x400
+    add     r8, r8, #4
+    stmia   r8, {r2,r3,r5,r6,r9}
+    bx      lr
+
+
+
diff --git a/Pico/carthw/svp/stub_arm.s b/Pico/carthw/svp/stub_arm.s
deleted file mode 100644
index 18a202c3..00000000
--- a/Pico/carthw/svp/stub_arm.s
+++ /dev/null
@@ -1,23 +0,0 @@
-@ vim:filetype=armasm
-
-
-@ register map:
-@ r4:  XXYY
-@ r5:  A
-@ r6:  STACK and emu flags
-@ r7:  SSP context
-@ r8:  r0-r2
-@ r9:  r4-r6
-@ r10: P
-
-.global flush_inval_caches
-
-.text
-.align 4
-
-flush_inval_caches:
-  mov r2, #0x0  @ must be 0
-  swi 0x9f0002
-  bx lr
-
-
diff --git a/Pico/carthw/svp/svp.c b/Pico/carthw/svp/svp.c
index 3dbc269c..e76c7ce8 100644
--- a/Pico/carthw/svp/svp.c
+++ b/Pico/carthw/svp/svp.c
@@ -7,6 +7,10 @@
 
 
 #include "../../PicoInt.h"
+#include "compiler.h"
+#ifdef __GP2X__
+#include <sys/mman.h>
+#endif
 
 svp_t *svp = NULL;
 int PicoSVPCycles = 1000; // cycles/line
@@ -40,6 +44,10 @@ static void PicoSVPReset(void)
 
 static void PicoSVPLine(int count)
 {
+	static int inited = 0;
+	if (!(svp->ssp1601.gr[SSP_PM0].h & 2) && !inited) return;
+	inited = 1;
+
 	// ???
 	if (PicoOpt&0x20000)
 		ssp1601_run(PicoSVPCycles * count);
@@ -77,6 +85,25 @@ static int PicoSVPDma(unsigned int source, int len, unsigned short **srcp, unsig
 
 
 void PicoSVPInit(void)
+{
+#ifdef __GP2X__
+	int ret;
+	ret = munmap(tcache, TCACHE_SIZE);
+	printf("munmap tcache: %i\n", ret);
+#endif
+}
+
+
+static void PicoSVPShutdown(void)
+{
+#ifdef __GP2X__
+	// also unmap tcache
+	PicoSVPInit();
+#endif
+}
+
+
+void PicoSVPStartup(void)
 {
 	void *tmp;
 
@@ -93,9 +120,14 @@ void PicoSVPInit(void)
 	svp = (void *) ((char *)tmp + 0x200000);
 	memset(svp, 0, sizeof(*svp));
 
+#ifdef __GP2X__
+	tmp = mmap(tcache, TCACHE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+	printf("mmap tcache: %p, asked %p\n", tmp, tcache);
+#endif
+
 	// init SVP compiler
 	if (!(PicoOpt&0x20000)) {
-		if (ssp1601_dyn_init()) return;
+		if (ssp1601_dyn_startup()) return;
 	}
 
 	// init ok, setup hooks..
@@ -105,6 +137,7 @@ void PicoSVPInit(void)
 	PicoDmaHook = PicoSVPDma;
 	PicoResetHook = PicoSVPReset;
 	PicoLineHook = PicoSVPLine;
+	PicoCartUnloadHook = PicoSVPShutdown;
 
 	// save state stuff
 	svp_states[0].ptr = svp->iram_rom;
@@ -113,3 +146,4 @@ void PicoSVPInit(void)
 	carthw_chunks = svp_states;
 }
 
+
diff --git a/platform/gp2x/Makefile b/platform/gp2x/Makefile
index 1bb9204b..8145ecd0 100644
--- a/platform/gp2x/Makefile
+++ b/platform/gp2x/Makefile
@@ -159,6 +159,9 @@ up: PicoDrive.gpe
 .s.o:
 	@echo ">>>" $<
 	$(GCC) $(COPT) $(DEFINC) -c $< -o $@
+.S.o:
+	@echo ">>>" $<
+	$(GCC) $(COPT) $(DEFINC) -c $< -o $@
 
 ../../Pico/carthw/svp/compiler.o : ../../Pico/carthw/svp/ssp16.o ../../Pico/carthw/svp/gen_arm.c
 
-- 
2.39.5