mirror of
https://github.com/cc65/cc65.git
synced 2024-06-15 17:30:06 +00:00
versionable header for sim65
load and run address now configured from header fix error codes not to conflict with test fix test/misc/endless.c which is supposed to fail if an endless loop does not occur
This commit is contained in:
parent
07ca772932
commit
fb7d4acd5c
|
@ -4,7 +4,7 @@ SYMBOLS {
|
||||||
}
|
}
|
||||||
MEMORY {
|
MEMORY {
|
||||||
ZP: file = "", start = $0000, size = $001B;
|
ZP: file = "", start = $0000, size = $001B;
|
||||||
HEADER: file = %O, start = $0000, size = $0002;
|
HEADER: file = %O, start = $0000, size = $000C;
|
||||||
MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__;
|
MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__;
|
||||||
}
|
}
|
||||||
SEGMENTS {
|
SEGMENTS {
|
||||||
|
|
|
@ -4,7 +4,7 @@ SYMBOLS {
|
||||||
}
|
}
|
||||||
MEMORY {
|
MEMORY {
|
||||||
ZP: file = "", start = $0000, size = $001B;
|
ZP: file = "", start = $0000, size = $001B;
|
||||||
HEADER: file = %O, start = $0000, size = $0002;
|
HEADER: file = %O, start = $0000, size = $000C;
|
||||||
MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__;
|
MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__;
|
||||||
}
|
}
|
||||||
SEGMENTS {
|
SEGMENTS {
|
||||||
|
|
|
@ -140,28 +140,39 @@ Assembly tests may similarly be assembled and linked with
|
||||||
and the sim65 library provides an <tt/exit/ symbol that the program may <tt/JMP/
|
and the sim65 library provides an <tt/exit/ symbol that the program may <tt/JMP/
|
||||||
to terminate with the current A register value as an exit code.
|
to terminate with the current A register value as an exit code.
|
||||||
|
|
||||||
Without using the provided target library, there are some relevant internal details:
|
The binary file has a 12 byte header:
|
||||||
|
|
||||||
<itemize>
|
<itemize>
|
||||||
|
|
||||||
<item>The binary input file has a 2 byte header. The first byte indicates CPU type:
|
<item>5 byte **signature**: <tt/$73, $69, $6D, $36, $35/ or <tt/'sim65'/
|
||||||
0 = 6502, 1 = 65C02. The second byte is the address of the C parameter stack pointer
|
|
||||||
<tt/sp/, used by the paravirtualization functions.
|
|
||||||
|
|
||||||
<item>The rest of the input file, after the header, will be loaded at <tt/$0200/,
|
<item>1 byte **version**: <tt/2/
|
||||||
and execution will begin at <tt/$0200/.
|
|
||||||
|
<item>1 byte **CPU type**: <tt/0/ = 6502, <tt/1/ = 65C02
|
||||||
|
|
||||||
|
<item>1 byte **sp address**: the zero page address of the C parameter stack pointer <tt/sp/ used by the paravirtualization functions
|
||||||
|
|
||||||
|
<item>1 word **load address**: where to load the data from the file into memory (default: <tt/$0200/)
|
||||||
|
|
||||||
|
<item>1 word **reset address**: specifies where to begin execution after loading (default: <tt/$0200/)
|
||||||
|
|
||||||
|
</itemize>
|
||||||
|
|
||||||
|
Other internal details:
|
||||||
|
|
||||||
|
<itemize>
|
||||||
|
|
||||||
<item>The entire 64 kilobyte address space is writeable RAM.
|
<item>The entire 64 kilobyte address space is writeable RAM.
|
||||||
Aside from the loaded binary, the reset vector at <tt/$FFFC/ will be
|
Aside from the loaded binary, the reset vector at <tt/$FFFC/ will be
|
||||||
pre-loaded with <tt/$0200/ as the start address.
|
pre-loaded with the given **reset address**.
|
||||||
|
|
||||||
<item>The <tt/exit/ address is <tt/$FFF1/.
|
<item>The <tt/exit/ address is <tt/$FFF1/.
|
||||||
Jumping to this address will terminate execution with the A register value as an exit code.
|
Jumping to this address will terminate execution with the A register value as an exit code.
|
||||||
|
|
||||||
<item>The built-in functions are provided by 6 paravirtualization hooks present at
|
<item>The built-in functions are provided by 6 paravirtualization hooks present at
|
||||||
<tt/$FFF0-$FFF5/. Except for <tt/exit/, a <tt/JSR/ to one of these
|
<tt/$FFF0-$FFF5/. Except for <tt/exit/, a <tt/JSR/ to one of these
|
||||||
addresses will return immediately after performing a special function,
|
addresses will return immediately after performing a special function.
|
||||||
intended for use with the sim65 target C library.
|
These use cc65 calling conventions, and are intended for use with the sim65 target C library.
|
||||||
|
|
||||||
<item><tt/IRQ/ and <tt/NMI/ events will not be generated, though <tt/BRK/
|
<item><tt/IRQ/ and <tt/NMI/ events will not be generated, though <tt/BRK/
|
||||||
can be used if the IRQ vector at <tt/$FFFE/ is manually prepared by the test code.
|
can be used if the IRQ vector at <tt/$FFFE/ is manually prepared by the test code.
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
;
|
;
|
||||||
|
|
||||||
.export _exit
|
.export _exit
|
||||||
|
.export sim65start
|
||||||
.export __STARTUP__ : absolute = 1 ; Mark as startup
|
.export __STARTUP__ : absolute = 1 ; Mark as startup
|
||||||
.import zerobss, callmain
|
.import zerobss, callmain
|
||||||
.import initlib, donelib
|
.import initlib, donelib
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
|
|
||||||
.segment "STARTUP"
|
.segment "STARTUP"
|
||||||
|
|
||||||
|
sim65start:
|
||||||
cld
|
cld
|
||||||
ldx #$FF
|
ldx #$FF
|
||||||
txs
|
txs
|
||||||
|
|
|
@ -7,8 +7,14 @@
|
||||||
|
|
||||||
.export __EXEHDR__ : absolute = 1 ; Linker referenced
|
.export __EXEHDR__ : absolute = 1 ; Linker referenced
|
||||||
.importzp sp
|
.importzp sp
|
||||||
|
.import __MAIN_START__
|
||||||
|
.import sim65start
|
||||||
|
|
||||||
.segment "EXEHDR"
|
.segment "EXEHDR"
|
||||||
|
|
||||||
.byte .defined(__SIM65C02__)
|
.byte $73, $69, $6D, $36, $35 ; 'sim65'
|
||||||
.byte sp
|
.byte 2 ; header version
|
||||||
|
.byte .defined(__SIM65C02__) ; CPU type
|
||||||
|
.byte sp ; sp address
|
||||||
|
.addr __MAIN_START__ ; load address
|
||||||
|
.addr sim65start ; reset address
|
||||||
|
|
|
@ -69,7 +69,21 @@ void Error (const char* Format, ...)
|
||||||
vfprintf (stderr, Format, ap);
|
vfprintf (stderr, Format, ap);
|
||||||
putc ('\n', stderr);
|
putc ('\n', stderr);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
exit (EXIT_FAILURE);
|
exit (SIM65_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ErrorCode (int Code, const char* Format, ...)
|
||||||
|
/* Print an error message and die with the given exit code */
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start (ap, Format);
|
||||||
|
fprintf (stderr, "Error: ");
|
||||||
|
vfprintf (stderr, Format, ap);
|
||||||
|
putc ('\n', stderr);
|
||||||
|
va_end (ap);
|
||||||
|
exit (Code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,5 +97,5 @@ void Internal (const char* Format, ...)
|
||||||
vfprintf (stderr, Format, ap);
|
vfprintf (stderr, Format, ap);
|
||||||
putc ('\n', stderr);
|
putc ('\n', stderr);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
exit (EXIT_FAILURE);
|
exit (SIM65_ERROR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Data */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define SIM65_ERROR 256
|
||||||
|
/* Does not use EXIT_FAILURE because it may overlap with test results. */
|
||||||
|
|
||||||
|
#define SIM65_ERROR_TIMEOUT 257
|
||||||
|
/* An error result for max CPU instructions exceeded. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Code */
|
/* Code */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -55,6 +69,9 @@ void Warning (const char* Format, ...) attribute((format(printf,1,2)));
|
||||||
void Error (const char* Format, ...) attribute((noreturn, format(printf,1,2)));
|
void Error (const char* Format, ...) attribute((noreturn, format(printf,1,2)));
|
||||||
/* Print an error message and die */
|
/* Print an error message and die */
|
||||||
|
|
||||||
|
void ErrorCode (int Code, const char* Format, ...) attribute((noreturn, format(printf,2,3)));
|
||||||
|
/* Print an error message and die with the given exit code */
|
||||||
|
|
||||||
void Internal (const char* Format, ...) attribute((noreturn, format(printf,1,2)));
|
void Internal (const char* Format, ...) attribute((noreturn, format(printf,1,2)));
|
||||||
/* Print an internal error message and die */
|
/* Print an internal error message and die */
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,16 @@ const char* ProgramFile;
|
||||||
/* exit simulator after MaxCycles Cycles */
|
/* exit simulator after MaxCycles Cycles */
|
||||||
unsigned long MaxCycles;
|
unsigned long MaxCycles;
|
||||||
|
|
||||||
|
/* Header signature 'sim65' */
|
||||||
|
static const unsigned char HeaderSignature[] = {
|
||||||
|
0x73, 0x69, 0x6D, 0x36, 0x35
|
||||||
|
};
|
||||||
|
#define HEADER_SIGNATURE_LENGTH (sizeof(HeaderSignature)/sizeof(HeaderSignature[0]))
|
||||||
|
|
||||||
|
static const unsigned char HeaderVersion = 2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Code */
|
/* Code */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -135,8 +145,11 @@ static void OptQuitXIns (const char* Opt attribute ((unused)),
|
||||||
static unsigned char ReadProgramFile (void)
|
static unsigned char ReadProgramFile (void)
|
||||||
/* Load program into memory */
|
/* Load program into memory */
|
||||||
{
|
{
|
||||||
int Val;
|
unsigned I;
|
||||||
unsigned Addr = 0x0200;
|
int Val, Val2;
|
||||||
|
int Version;
|
||||||
|
unsigned Addr;
|
||||||
|
unsigned Load, Reset;
|
||||||
unsigned char SPAddr = 0x00;
|
unsigned char SPAddr = 0x00;
|
||||||
|
|
||||||
/* Open the file */
|
/* Open the file */
|
||||||
|
@ -145,6 +158,18 @@ static unsigned char ReadProgramFile (void)
|
||||||
Error ("Cannot open '%s': %s", ProgramFile, strerror (errno));
|
Error ("Cannot open '%s': %s", ProgramFile, strerror (errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Verify the header signature */
|
||||||
|
for (I=0; I<HEADER_SIGNATURE_LENGTH; ++I) {
|
||||||
|
if ((Val = fgetc(F)) != HeaderSignature[I]) {
|
||||||
|
Error ("'%s': Invalid header signature.", ProgramFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get header version */
|
||||||
|
if ((Version = fgetc(F)) != HeaderVersion) {
|
||||||
|
Error ("'%s': Invalid header version.", ProgramFile);
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the CPU type from the file header */
|
/* Get the CPU type from the file header */
|
||||||
if ((Val = fgetc(F)) != EOF) {
|
if ((Val = fgetc(F)) != EOF) {
|
||||||
if (Val != CPU_6502 && Val != CPU_65C02) {
|
if (Val != CPU_6502 && Val != CPU_65C02) {
|
||||||
|
@ -158,10 +183,25 @@ static unsigned char ReadProgramFile (void)
|
||||||
SPAddr = Val;
|
SPAddr = Val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get load address */
|
||||||
|
if (((Val = fgetc(F)) == EOF) ||
|
||||||
|
((Val2 = fgetc(F)) == EOF)) {
|
||||||
|
Error ("'%s': Header missing load address", ProgramFile);
|
||||||
|
}
|
||||||
|
Load = Val | (Val2 << 8);
|
||||||
|
|
||||||
|
/* Get reset address */
|
||||||
|
if (((Val = fgetc(F)) == EOF) ||
|
||||||
|
((Val2 = fgetc(F)) == EOF)) {
|
||||||
|
Error ("'%s': Header missing reset address", ProgramFile);
|
||||||
|
}
|
||||||
|
Reset = Val | (Val2 << 8);
|
||||||
|
|
||||||
/* Read the file body into memory */
|
/* Read the file body into memory */
|
||||||
|
Addr = Load;
|
||||||
while ((Val = fgetc(F)) != EOF) {
|
while ((Val = fgetc(F)) != EOF) {
|
||||||
if (Addr == 0xFF00) {
|
if (Addr == 0xFF00) {
|
||||||
Error ("'%s': To large to fit into $0200-$FFF0", ProgramFile);
|
Error ("'%s': To large to fit into $%04X-$FFF0", ProgramFile, Addr);
|
||||||
}
|
}
|
||||||
MemWriteByte (Addr++, (unsigned char) Val);
|
MemWriteByte (Addr++, (unsigned char) Val);
|
||||||
}
|
}
|
||||||
|
@ -174,8 +214,11 @@ static unsigned char ReadProgramFile (void)
|
||||||
/* Close the file */
|
/* Close the file */
|
||||||
fclose (F);
|
fclose (F);
|
||||||
|
|
||||||
Print (stderr, 1, "Loaded '%s' at $0200-$%04X\n", ProgramFile, Addr - 1);
|
Print (stderr, 1, "Loaded '%s' at $%04X-$%04X\n", ProgramFile, Load, Addr - 1);
|
||||||
|
Print (stderr, 1, "File version: %d\n", Version);
|
||||||
|
Print (stderr, 1, "Reset: $%04X\n", Reset);
|
||||||
|
|
||||||
|
MemWriteWord(0xFFFC, Reset);
|
||||||
return SPAddr;
|
return SPAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,9 +306,7 @@ int main (int argc, char* argv[])
|
||||||
while (1) {
|
while (1) {
|
||||||
ExecuteInsn ();
|
ExecuteInsn ();
|
||||||
if (MaxCycles && (GetCycles () >= MaxCycles)) {
|
if (MaxCycles && (GetCycles () >= MaxCycles)) {
|
||||||
Error ("Maximum number of cycles reached.");
|
ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached.");
|
||||||
exit (-99); /* do not use EXIT_FAILURE to avoid conflicts with the
|
|
||||||
same value being used in a test program */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,15 @@ void MemWriteByte (unsigned Addr, unsigned char Val)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void MemWriteWord (unsigned Addr, unsigned Val)
|
||||||
|
/* Write a word to a memory location */
|
||||||
|
{
|
||||||
|
MemWriteByte (Addr, Val & 0xFF);
|
||||||
|
MemWriteByte (Addr + 1, Val >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned char MemReadByte (unsigned Addr)
|
unsigned char MemReadByte (unsigned Addr)
|
||||||
/* Read a byte from a memory location */
|
/* Read a byte from a memory location */
|
||||||
{
|
{
|
||||||
|
@ -82,7 +91,7 @@ unsigned MemReadWord (unsigned Addr)
|
||||||
|
|
||||||
|
|
||||||
unsigned MemReadZPWord (unsigned char Addr)
|
unsigned MemReadZPWord (unsigned char Addr)
|
||||||
/* Read a word from the zero page. This function differs from ReadMemW in that
|
/* Read a word from the zero page. This function differs from MemReadWord in that
|
||||||
** the read will always be in the zero page, even in case of an address
|
** the read will always be in the zero page, even in case of an address
|
||||||
** overflow.
|
** overflow.
|
||||||
*/
|
*/
|
||||||
|
@ -96,10 +105,6 @@ unsigned MemReadZPWord (unsigned char Addr)
|
||||||
void MemInit (void)
|
void MemInit (void)
|
||||||
/* Initialize the memory subsystem */
|
/* Initialize the memory subsystem */
|
||||||
{
|
{
|
||||||
/* Fill momory with illegal opcode */
|
/* Fill memory with illegal opcode */
|
||||||
memset (Mem, 0xFF, sizeof (Mem));
|
memset (Mem, 0xFF, sizeof (Mem));
|
||||||
|
|
||||||
/* Set RESET vector to 0x0200 */
|
|
||||||
Mem[0xFFFC] = 0x00;
|
|
||||||
Mem[0xFFFD] = 0x02;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,9 @@
|
||||||
void MemWriteByte (unsigned Addr, unsigned char Val);
|
void MemWriteByte (unsigned Addr, unsigned char Val);
|
||||||
/* Write a byte to a memory location */
|
/* Write a byte to a memory location */
|
||||||
|
|
||||||
|
void MemWriteWord (unsigned Addr, unsigned Val);
|
||||||
|
/* Write a word to a memory location */
|
||||||
|
|
||||||
unsigned char MemReadByte (unsigned Addr);
|
unsigned char MemReadByte (unsigned Addr);
|
||||||
/* Read a byte from a memory location */
|
/* Read a byte from a memory location */
|
||||||
|
|
||||||
|
@ -54,7 +57,7 @@ unsigned MemReadWord (unsigned Addr);
|
||||||
/* Read a word from a memory location */
|
/* Read a word from a memory location */
|
||||||
|
|
||||||
unsigned MemReadZPWord (unsigned char Addr);
|
unsigned MemReadZPWord (unsigned char Addr);
|
||||||
/* Read a word from the zero page. This function differs from ReadMemW in that
|
/* Read a word from the zero page. This function differs from MemReadWord in that
|
||||||
** the read will always be in the zero page, even in case of an address
|
** the read will always be in the zero page, even in case of an address
|
||||||
** overflow.
|
** overflow.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -103,15 +103,6 @@ static void SetAX (CPURegs* Regs, unsigned Val)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void MemWriteWord (unsigned Addr, unsigned Val)
|
|
||||||
{
|
|
||||||
MemWriteByte (Addr, Val);
|
|
||||||
Val >>= 8;
|
|
||||||
MemWriteByte (Addr + 1, Val);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static unsigned char Pop (CPURegs* Regs)
|
static unsigned char Pop (CPURegs* Regs)
|
||||||
{
|
{
|
||||||
return MemReadByte (0x0100 + ++Regs->SP);
|
return MemReadByte (0x0100 + ++Regs->SP);
|
||||||
|
|
|
@ -9,5 +9,5 @@ int main(void)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
printf("error: should not come here\n");
|
printf("error: should not come here\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_SUCCESS; /* test verifies failure, not success */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user