adds semihosting skeleton

This commit is contained in:
Eyck-Alexander Jentzsch 2024-01-09 12:50:41 +01:00
parent 207f778ee6
commit 075e04249a
2 changed files with 719 additions and 10 deletions

View File

@ -1,22 +1,680 @@
#include "semihosting.h"
#include <exception>
#include <iss/vm_types.h>
#include <stdexcept>
template <typename T> void semihosting_callback(iss::arch_if* arch_if_ptr, T a0, T a1) {
if(a0 == 0x04 /*WRITE0*/) {
template <typename T> void semihosting_callback(iss::arch_if* arch_if_ptr, T call_number, T parameter) {
switch(call_number) {
case SEMIHOSTING_ENTER_SVC: {
throw std::runtime_error("Semihosting Call not Implemented");
} /* DEPRECATED */
case SEMIHOSTING_SYS_CLOCK: {
/*
* Returns the number of centiseconds (hundredths of a second)
* since the execution started.
*
* Values returned can be of limited use for some benchmarking
* purposes because of communication overhead or other
* agent-specific factors. For example, with a debug hardware
* unit the request is passed back to the host for execution.
* This can lead to unpredictable delays in transmission and
* process scheduling.
*
* Use this function to calculate time intervals, by calculating
* differences between intervals with and without the code
* sequence to be timed.
*
* Entry
* The PARAMETER REGISTER must contain 0. There are no other
* parameters.
*
* Return
* On exit, the RETURN REGISTER contains:
* - The number of centiseconds since some arbitrary start
* point, if the call is successful.
* - 1 if the call is not successful. For example, because
* of a communications error.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_CLOSE: {
/*
* Closes a file on the host system. The handle must reference
* a file that was opened with SYS_OPEN.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* one-field argument block:
* - field 1 Contains a handle for an open file.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 0 if the call is successful
* - 1 if the call is not successful.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_ELAPSED: {
/*
* Returns the number of elapsed target ticks since execution
* started.
* Use SYS_TICKFREQ to determine the tick frequency.
*
* Entry (32-bit)
* On entry, the PARAMETER REGISTER points to a two-field data
* block to be used for returning the number of elapsed ticks:
* - field 1 The least significant field and is at the low address.
* - field 2 The most significant field and is at the high address.
*
* Entry (64-bit)
* On entry the PARAMETER REGISTER points to a one-field data
* block to be used for returning the number of elapsed ticks:
* - field 1 The number of elapsed ticks as a 64-bit value.
*
* Return
* On exit:
* - On success, the RETURN REGISTER contains 0, the PARAMETER
* REGISTER is unchanged, and the data block pointed to by the
* PARAMETER REGISTER is filled in with the number of elapsed
* ticks.
* - On failure, the RETURN REGISTER contains -1, and the
* PARAMETER REGISTER contains -1.
*
* Note: Some semihosting implementations might not support this
* semihosting operation, and they always return -1 in the
* RETURN REGISTER.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_ERRNO: {
/*
* Returns the value of the C library errno variable that is
* associated with the semihosting implementation. The errno
* variable can be set by a number of C library semihosted
* functions, including:
* - SYS_REMOVE
* - SYS_OPEN
* - SYS_CLOSE
* - SYS_READ
* - SYS_WRITE
* - SYS_SEEK.
*
* Whether errno is set or not, and to what value, is entirely
* host-specific, except where the ISO C standard defines the
* behavior.
*
* Entry
* There are no parameters. The PARAMETER REGISTER must be 0.
*
* Return
* On exit, the RETURN REGISTER contains the value of the C
* library errno variable.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_EXIT: {
/*
* Note: SYS_EXIT was called angel_SWIreason_ReportException in
* previous versions of the documentation.
*
* An application calls this operation to report an exception
* to the debugger directly. The most common use is to report
* that execution has completed, using ADP_Stopped_ApplicationExit.
*
* Note: This semihosting operation provides no means for 32-bit
* callers to indicate an application exit with a specified exit
* code. Semihosting callers may prefer to check for the presence
* of the SH_EXT_EXTENDED_REPORT_EXCEPTION extension and use
* the SYS_REPORT_EXCEPTION_EXTENDED operation instead, if it
* is available.
*
* Entry (32-bit)
* On entry, the PARAMETER register is set to a reason code
* describing the cause of the trap. Not all semihosting client
* implementations will necessarily trap every corresponding
* event. Important reason codes are:
*
* - ADP_Stopped_ApplicationExit 0x20026
* - ADP_Stopped_RunTimeErrorUnknown 0x20023
*
* Entry (64-bit)
* On entry, the PARAMETER REGISTER contains a pointer to a
* two-field argument block:
* - field 1 The exception type, which is one of the set of
* reason codes in the above tables.
* - field 2 A subcode, whose meaning depends on the reason
* code in field 1.
* In particular, if field 1 is ADP_Stopped_ApplicationExit
* then field 2 is an exit status code, as passed to the C
* standard library exit() function. A simulator receiving
* this request must notify a connected debugger, if present,
* and then exit with the specified status.
*
* Return
* No return is expected from these calls. However, it is
* possible for the debugger to request that the application
* continues by performing an RDI_Execute request or equivalent.
* In this case, execution continues with the registers as they
* were on entry to the operation, or as subsequently modified
* by the debugger.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_EXIT_EXTENDED: {
/*
* This operation is only supported if the semihosting extension
* SH_EXT_EXIT_EXTENDED is implemented. SH_EXT_EXIT_EXTENDED is
* reported using feature byte 0, bit 0. If this extension is
* supported, then the implementation provides a means to
* report a normal exit with a nonzero exit status in both 32-bit
* and 64-bit semihosting APIs.
*
* The implementation must provide the semihosting call
* SYS_EXIT_EXTENDED for both A64 and A32/T32 semihosting APIs.
*
* SYS_EXIT_EXTENDED is used by an application to report an
* exception or exit to the debugger directly. The most common
* use is to report that execution has completed, using
* ADP_Stopped_ApplicationExit.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* two-field argument block:
* - field 1 The exception type, which should be one of the set
* of reason codes that are documented for the SYS_EXIT
* (0x18) call. For example, ADP_Stopped_ApplicationExit.
* - field 2 A subcode, whose meaning depends on the reason
* code in field 1. In particular, if field 1 is
* ADP_Stopped_ApplicationExit then field 2 is an exit status
* code, as passed to the C standard library exit() function.
* A simulator receiving this request must notify a connected
* debugger, if present, and then exit with the specified status.
*
* Return
* No return is expected from these calls.
*
* For the A64 API, this call is identical to the behavior of
* the mandatory SYS_EXIT (0x18) call. If this extension is
* supported, then both calls must be implemented.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_FLEN: {
/*
* Returns the length of a specified file.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* one-field argument block:
* - field 1 A handle for a previously opened, seekable file
* object.
*
* Return
* On exit, the RETURN REGISTER contains:
* - The current length of the file object, if the call is
* successful.
* - 1 if an error occurs.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_GET_CMDLINE: {
/*
* Returns the command line that is used for the call to the
* executable, that is, argc and argv.
*
* Entry
* On entry, the PARAMETER REGISTER points to a two-field data
* block to be used for returning the command string and its length:
* - field 1 A pointer to a buffer of at least the size that is
* specified in field 2.
* - field 2 The length of the buffer in bytes.
*
* Return
* On exit:
* If the call is successful, then the RETURN REGISTER contains 0,
* the PARAMETER REGISTER is unchanged, and the data block is
* updated as follows:
* - field 1 A pointer to a null-terminated string of the command
* line.
* - field 2 The length of the string in bytes.
* If the call is not successful, then the RETURN REGISTER
* contains -1.
*
* Note: The semihosting implementation might impose limits on
* the maximum length of the string that can be transferred.
* However, the implementation must be able to support a
* command-line length of at least 80 bytes.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_HEAPINFO: {
/*
* Returns the system stack and heap parameters.
*
* Entry
* On entry, the PARAMETER REGISTER contains the address of a
* pointer to a four-field data block. The contents of the data
* block are filled by the function. The following C-like
* pseudocode describes the layout of the block:
* struct block {
* void* heap_base;
* void* heap_limit;
* void* stack_base;
* void* stack_limit;
* };
*
* Return
* On exit, the PARAMETER REGISTER is unchanged and the data
* block has been updated.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_ISERROR: {
/*
* Determines whether the return code from another semihosting
* call is an error status or not.
*
* This call is passed a parameter block containing the error
* code to examine.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* one-field data block:
* - field 1 The required status word to check.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 0 if the status field is not an error indication
* - A nonzero value if the status field is an error indication.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_ISTTY: {
/*
* Checks whether a file is connected to an interactive device.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* one-field argument block:
* field 1 A handle for a previously opened file object.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 1 if the handle identifies an interactive device.
* - 0 if the handle identifies a file.
* - A value other than 1 or 0 if an error occurs.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_OPEN: {
/*
* Opens a file on the host system.
*
* The file path is specified either as relative to the current
* directory of the host process, or absolute, using the path
* conventions of the host operating system.
*
* Semihosting implementations must support opening the special
* path name :semihosting-features as part of the semihosting
* extensions reporting mechanism.
*
* ARM targets interpret the special path name :tt as meaning
* the console input stream, for an open-read or the console
* output stream, for an open-write. Opening these streams is
* performed as part of the standard startup code for those
* applications that reference the C stdio streams. The
* semihosting extension SH_EXT_STDOUT_STDERR allows the
* semihosting caller to open separate output streams
* corresponding to stdout and stderr. This extension is
* reported using feature byte 0, bit 1. Use SYS_OPEN with
* the special path name :semihosting-features to access the
* feature bits.
*
* If this extension is supported, the implementation must
* support the following additional semantics to SYS_OPEN:
* - If the special path name :tt is opened with an fopen
* mode requesting write access (w, wb, w+, or w+b), then
* this is a request to open stdout.
* - If the special path name :tt is opened with a mode
* requesting append access (a, ab, a+, or a+b), then this is
* a request to open stderr.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* three-field argument block:
* - field 1 A pointer to a null-terminated string containing
* a file or device name.
* - field 2 An integer that specifies the file opening mode.
* - field 3 An integer that gives the length of the string
* pointed to by field 1.
*
* The length does not include the terminating null character
* that must be present.
*
* Return
* On exit, the RETURN REGISTER contains:
* - A nonzero handle if the call is successful.
* - 1 if the call is not successful.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_READ: {
/*
* Reads the contents of a file into a buffer. The file position
* is specified either:
* - Explicitly by a SYS_SEEK.
* - Implicitly one byte beyond the previous SYS_READ or
* SYS_WRITE request.
*
* The file position is at the start of the file when it is
* opened, and is lost when the file is closed. Perform the
* file operation as a single action whenever possible. For
* example, do not split a read of 16KB into four 4KB chunks
* unless there is no alternative.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* three-field data block:
* - field 1 Contains a handle for a file previously opened
* with SYS_OPEN.
* - field 2 Points to a buffer.
* - field 3 Contains the number of bytes to read to the buffer
* from the file.
*
* Return
* On exit, the RETURN REGISTER contains the number of bytes not
* filled in the buffer (buffer_length - bytes_read) as follows:
* - If the RETURN REGISTER is 0, the entire buffer was
* successfully filled.
* - If the RETURN REGISTER is the same as field 3, no bytes
* were read (EOF can be assumed).
* - If the RETURN REGISTER contains a value smaller than
* field 3, the read succeeded but the buffer was only partly
* filled. For interactive devices, this is the most common
* return value.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_READC: {
/*
* Reads a byte from the console.
*
* Entry
* The PARAMETER REGISTER must contain 0. There are no other
* parameters or values possible.
*
* Return
* On exit, the RETURN REGISTER contains the byte read from
* the console.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_REMOVE: {
/*
* Deletes a specified file on the host filing system.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* two-field argument block:
* - field 1 Points to a null-terminated string that gives the
* path name of the file to be deleted.
* - field 2 The length of the string.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 0 if the delete is successful
* - A nonzero, host-specific error code if the delete fails.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_RENAME: {
/*
* Renames a specified file.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* four-field data block:
* - field 1 A pointer to the name of the old file.
* - field 2 The length of the old filename.
* - field 3 A pointer to the new filename.
* - field 4 The length of the new filename. Both strings are
* null-terminated.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 0 if the rename is successful.
* - A nonzero, host-specific error code if the rename fails.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_SEEK: {
/*
* Seeks to a specified position in a file using an offset
* specified from the start of the file. The file is assumed
* to be a byte array and the offset is given in bytes.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* two-field data block:
* - field 1 A handle for a seekable file object.
* - field 2 The absolute byte position to seek to.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 0 if the request is successful.
* - A negative value if the request is not successful.
* Use SYS_ERRNO to read the value of the host errno variable
* describing the error.
*
* Note: The effect of seeking outside the current extent of
* the file object is undefined.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_SYSTEM: {
/*
* Passes a command to the host command-line interpreter.
* This enables you to execute a system command such as dir,
* ls, or pwd. The terminal I/O is on the host, and is not
* visible to the target.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* two-field argument block:
* - field 1 Points to a string to be passed to the host
* command-line interpreter.
* - field 2 The length of the string.
*
* Return
* On exit, the RETURN REGISTER contains the return status.
*/
/* Provide SYS_SYSTEM functionality. Uses the
* libc system command, there may be a reason *NOT*
* to use this, but as I can't think of one, I
* implemented it this way.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_TICKFREQ: {
/*
* Returns the tick frequency.
*
* Entry
* The PARAMETER REGISTER must contain 0 on entry to this routine.
*
* Return
* On exit, the RETURN REGISTER contains either:
* - The number of ticks per second.
* - 1 if the target does not know the value of one tick.
*
* Note: Some semihosting implementations might not support
* this semihosting operation, and they always return -1 in the
* RETURN REGISTER.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_TIME: {
/*
* Returns the number of seconds since 00:00 January 1, 1970.
* This value is real-world time, regardless of any debug agent
* configuration.
*
* Entry
* There are no parameters.
*
* Return
* On exit, the RETURN REGISTER contains the number of seconds.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_TMPNAM: {
/*
* Returns a temporary name for a file identified by a system
* file identifier.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* three-word argument block:
* - field 1 A pointer to a buffer.
* - field 2 A target identifier for this filename. Its value
* must be an integer in the range 0-255.
* - field 3 Contains the length of the buffer. The length must
* be at least the value of L_tmpnam on the host system.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 0 if the call is successful.
* - 1 if an error occurs.
*
* The buffer pointed to by the PARAMETER REGISTER contains
* the filename, prefixed with a suitable directory name.
* If you use the same target identifier again, the same
* filename is returned.
*
* Note: The returned string must be null-terminated.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_WRITE: {
/*
* Writes the contents of a buffer to a specified file at the
* current file position. The file position is specified either:
* - Explicitly, by a SYS_SEEK.
* - Implicitly as one byte beyond the previous SYS_READ or
* SYS_WRITE request.
*
* The file position is at the start of the file when the file
* is opened, and is lost when the file is closed.
*
* Perform the file operation as a single action whenever
* possible. For example, do not split a write of 16KB into
* four 4KB chunks unless there is no alternative.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* three-field data block:
* - field 1 Contains a handle for a file previously opened
* with SYS_OPEN.
* - field 2 Points to the memory containing the data to be written.
* - field 3 Contains the number of bytes to be written from
* the buffer to the file.
*
* Return
* On exit, the RETURN REGISTER contains:
* - 0 if the call is successful.
* - The number of bytes that are not written, if there is an error.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_WRITEC: {
/*
* Writes a character byte, pointed to by the PARAMETER REGISTER,
* to the debug channel. When executed under a semihosting
* debugger, the character appears on the host debugger console.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to the
* character.
*
* Return
* None. The RETURN REGISTER is corrupted.
*/
throw std::runtime_error("Semihosting Call not Implemented");
}
case SEMIHOSTING_SYS_WRITE0: {
/*
* Writes a null-terminated string to the debug channel.
* When executed under a semihosting debugger, the characters
* appear on the host debugger console.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to the
* first byte of the string.
*
* Return
* None. The RETURN REGISTER is corrupted.
*/
uint8_t character;
while(1) {
auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, a1, 1, &character);
auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, parameter, 1, &character);
if(res != iss::Ok)
return;
if(character == 0)
break;
putchar(character);
a1++;
parameter++;
}
} else {
throw std::runtime_error("Not Implemented Error");
}
case SEMIHOSTING_USER_CMD_0x100: {
/**
*
* This is a user defined operation (while user cmds 0x100-0x1ff
* are possible, none are currently implemented).
*
* Reads the user operation parameters from target, then fires the
* corresponding target event. When the target callbacks returned,
* cleans up the command parameter buffer.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* two-field data block:
* - field 1 Contains a pointer to the bound command parameter
* string
* - field 2 Contains the command parameter string length
*
* Return
* On exit, the RETURN REGISTER contains the return status.
*/
throw std::runtime_error("Semihosting Call not Implemented");
} /* First user cmd op code */
case SEMIHOSTING_USER_CMD_0x1FF: {
/**
*
* This is a user defined operation (while user cmds 0x100-0x1ff
* are possible, none are currently implemented).
*
* Reads the user operation parameters from target, then fires the
* corresponding target event. When the target callbacks returned,
* cleans up the command parameter buffer.
*
* Entry
* On entry, the PARAMETER REGISTER contains a pointer to a
* two-field data block:
* - field 1 Contains a pointer to the bound command parameter
* string
* - field 2 Contains the command parameter string length
*
* Return
* On exit, the RETURN REGISTER contains the return status.
*/
throw std::runtime_error("Semihosting Call not Implemented");
} /* Last user cmd op code */
default:
throw std::runtime_error("Semihosting Call not Implemented");
}
}
template void semihosting_callback<uint32_t>(iss::arch_if* arch_if_ptr, uint32_t a0, uint32_t a1);
template void semihosting_callback<uint64_t>(iss::arch_if* arch_if_ptr, uint64_t a0, uint64_t a1);
template void semihosting_callback<uint32_t>(iss::arch_if* arch_if_ptr, uint32_t call_number, uint32_t parameter);
template void semihosting_callback<uint64_t>(iss::arch_if* arch_if_ptr, uint64_t call_number, uint64_t parameter);

View File

@ -1,6 +1,57 @@
#ifndef _SEMIHOSTING_H_
#define _SEMIHOSTING_H_
#include <iss/arch_if.h>
template <typename T> void semihosting_callback(iss::arch_if* arch_if_ptr, T a0, T a1);
/*
* According to:
* "Semihosting for AArch32 and AArch64, Release 2.0"
* https://static.docs.arm.com/100863/0200/semihosting.pdf
* from ARM Ltd.
*
* The available semihosting operation numbers passed in R0 are allocated
* as follows:
* - 0x00-0x31 Used by ARM.
* - 0x32-0xFF Reserved for future use by ARM.
* - 0x100-0x1FF Reserved for user applications. These are not used by ARM.
* However, if you are writing your own SVC operations, you are advised
* to use a different SVC number rather than using the semihosted
* SVC number and these operation type numbers.
* - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended
* that you do not use these.
*/
enum semihosting_operation_numbers {
/*
* ARM semihosting operations, in lexicographic order.
*/
SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */
SEMIHOSTING_SYS_CLOCK = 0x10,
SEMIHOSTING_SYS_CLOSE = 0x02,
SEMIHOSTING_SYS_ELAPSED = 0x30,
SEMIHOSTING_SYS_ERRNO = 0x13,
SEMIHOSTING_SYS_EXIT = 0x18,
SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20,
SEMIHOSTING_SYS_FLEN = 0x0C,
SEMIHOSTING_SYS_GET_CMDLINE = 0x15,
SEMIHOSTING_SYS_HEAPINFO = 0x16,
SEMIHOSTING_SYS_ISERROR = 0x08,
SEMIHOSTING_SYS_ISTTY = 0x09,
SEMIHOSTING_SYS_OPEN = 0x01,
SEMIHOSTING_SYS_READ = 0x06,
SEMIHOSTING_SYS_READC = 0x07,
SEMIHOSTING_SYS_REMOVE = 0x0E,
SEMIHOSTING_SYS_RENAME = 0x0F,
SEMIHOSTING_SYS_SEEK = 0x0A,
SEMIHOSTING_SYS_SYSTEM = 0x12,
SEMIHOSTING_SYS_TICKFREQ = 0x31,
SEMIHOSTING_SYS_TIME = 0x11,
SEMIHOSTING_SYS_TMPNAM = 0x0D,
SEMIHOSTING_SYS_WRITE = 0x05,
SEMIHOSTING_SYS_WRITEC = 0x03,
SEMIHOSTING_SYS_WRITE0 = 0x04,
SEMIHOSTING_USER_CMD_0x100 = 0x100, /* First user cmd op code */
SEMIHOSTING_USER_CMD_0x1FF = 0x1FF, /* Last user cmd op code */
};
template <typename T> void semihosting_callback(iss::arch_if* arch_if_ptr, T call_number, T parameter);
#endif