Can't figure out how to write interrupt handler for Z80 using SDCC - interrupt

I'm developing a program in C for the Z80 and compiling using SDCC. I can't figure out how to create interrupt handlers for the NMI interrupt that starts at 0x0066 and the IM1 interrupt that starts at 0x0038. I'm using these calls:
void IM1_InterruptHandler(void) __interrupt
and
void NMI_InterruptHandler (void) __critical __interrupt
and the resulting assembly looks about right but they aren't located at the proper addresses. I did spot this thread:
https://sourceforge.net/p/sdcc/feature-requests/519/
but can't figure out how to use the above example crt0.s file with SDCC for a Z80 target.
Using the --use-crt switch doesn't seem to work.
unknown compiler option '--use-crt=crt0.s' ignored
Anyone experienced with Z80 development with SDCC that can provide some guidance?
Edit:
Still not quite there yet. My crt.s file looks like this:
.module crt0
.globl _main
.globl _IM1_InterruptHandler
.globl _NMI_InterruptHandler
.area _HEADER (ABS)
;; Reset vector
.org 0
jp init
.org 0x08
reti
.org 0x10
reti
.org 0x18
reti
.org 0x20
reti
.org 0x28
reti
.org 0x30
reti
.org 0x38
jp _IM1_InterruptHandler
.org 0x66
jp _NMI_InterruptHandler
.org 0x100
init:
;; Stack at the top of memory.
ld sp,#0x8300
call _main
;; Ordering of segments for the linker.
.area _HOME
.area _CODE
.area _DATA
.area _CODE
And I'm doing the following:
sdasz80 -l -o mycrt.rel crt0.s
sdcc -mz80 --no-std-crt0 --code-loc 0x0000 --data-loc 0x8000 mycrt.rel ppclone_menu.c
Every thing seems to compile just fine but when I bring up the code in the disassembler I don't see any of the crt0 code being inserted above at locations 0x08 through 0x66.

To use a custom crt0 file you first need to compile it using sdasz80, which should be part of your SDCC install:
sdasz80 -o crt0_int.rel crt0_int.asm
Then you compile your program adding the following to the SDCC command line:
--no-std-crt0 crt0_int.rel
So the full command line would be something like:
sdcc --code-loc 0xWhatever --data-loc 0xWhatever -mz80 --no-std-crt0 crt0_int.rel somelibrary.lib yoursource.c
If you need examples of complete crt0 files, you have one in my MSX software repository.
Edit: You are passing --code-loc 0x0000 to sdcc when compiling your source, this will cause the code section to overwrite whatever was defined in crt0. Change it to a more suitable value (for your crt0 looks like 0x0110 would be fine) or leave it out, so the compiler will choose an appropriate value by itself.

Related

Weak function definitions for interrupt vector in a static library are preferred over strong ones

Introduction
arm-none-eabi-gcc version 10.3-2021.10 20210824
device: nordic nRF52840/nRF52832/nRF52833
library affected: Nordic NRF5 SDK 17.1.0 - custom build system (CMake)
I wrote the CMake build system for Nordic NRF5 SDK (natively it only supports makefiles). The build system has a executable (application) and multiple underlying static libraries. The dependencies go like this:
application
...
- NordicAl (abstraction layer)
- nrf5_sdk
...
//root/CMakeLists.txt
add_executable(application)
...
add_subdirectory(lib/NordicAl)
...
target_link_libraries(application PRIVATE
nordic_al
...)
....
//root/lib/NordicAl/CMakeLists.txt
add_library(nordic_al)
...
add_subdirectory(lib/nrf5_sdk)
target_link_libraries(nordic_al PRIVATE
nrf5_sdk
...)
...
//root/lib/NordicAl/lib/nrf5_sdk/CMakeLists.txt
add_library(nrf5_sdk)
...
target_sources(nrf5_sdk PRIVATE
...
${NRF5_SDK_ROOT}/modules/nrfx/mdk/gcc_startup_${PLATFORM_MCU_FAMILY}.S
${NRF5_SDK_ROOT}/components/libraries/hardfault/nrf52/handler/hardfault_handler_gcc.c
)
Problem
I have created a custom C hard-fault handler on top of the Nordic nRF5 SDK. It works with the previous build system (makefile build system). It must be noted that the previous build system does not create the static libraries, as does the new CMake system. It just links everything unconditionally.
In the perfect world, the user of the SDK (i.e., I) should define a callback (HardFault_c_handler) and it will be called by the interrupt vector in case of a hard-fault.
In the nRF5 SDK library, a startup file (modules/nrfx/mdk/gcc_startup_nrf52840.S) is included in the target nrf5_sdk (static library). The relevant code for this problem:
__isr_vector:
.long __StackTop /* Top of Stack */
.long Reset_Handler
.long NMI_Handler
.long HardFault_Handler
...
.weak HardFault_Handler
.type HardFault_Handler, %function
HardFault_Handler:
b .
.size HardFault_Handler, . - HardFault_Handler
Additionally, there is a strong defition of HardFault_Handler in a c file that should take precedence over this weak definition. The file (components/libraries/hardfault/nrf52/handler/hardfault_handler_gcc.c) contains:
extern void HardFault_c_handler(uint32_t *);
void HardFault_Handler(void) __attribute__(( naked ));
void HardFault_Handler(void)
{
__ASM volatile(
...
" .ltorg \n"
: : "X"(HardFault_c_handler)
);
}
The code from the c file should be called by the MCU in a case of the hard-fault, but it does not.
My question is why? How to make it prefer the strong function? My thinking now, although I am not sure. Because this callback, i.e., HardFault_Handler, is not referenced in the main application (or before getting to the startup file) the linker does not need to resolve it. Only when it sees this symbol in the startup file it looks for it, and because this is a static library it only looks for the first occurrence.
Things I tried
removing static libraries, this fixes the problem,
separating weak definition of the HardFault_Handler into separate assembly file, this makes the linker link the function from the file that appears first, using -Wl,-trace-symbol=HardFault_Handler I see that the linker only looks for the first occurence and than stops (irrelevant of weak and strong).
putting c file before the startup file in the sources, does not change the result.
Edit
My linker flags:
-mcpu=cortex-m4
-mfloat-abi=hard
-mfpu=fpv4-sp-d16
-mthumb
-mabi=aapcs
-ffreestanding
-fno-common
-finline-small-functions
-findirect-inlining
-fstack-protector-strong
-ffunction-sections
-fdata-sections
-Wl,--gc-sections
--specs=nano.specs
I figured out, as I am using CMake, that I can supply OBJECT keyword with the add_library() function. In that case the keyword works as expected. Take note that object library linked to another object library does not work properly. And the underlying object library must, also, be included in the top-most (non-object library) target.

How to resolve function name in elf

I wanted to write an elf parser and disassemble the .text section, so I parsed the elf file and gave the .text section to the capstone to disassemble it for me. Unfortunately, capstone doesn't resolve function names.
According to the below assembly code in my elf file, there is a call to a function that I want to resolve its name.
call 8048380
I checked .symtab section but functions that need relocation like printf has a 0 address in the table because their address is unknown until load time.
So how am I gonna resolve its name?
I checked .symtab section but functions that need relocation like printf
The function you are interested in (the one at address 0x8048380) is not like printf and doesn't require runtime relocation.
It's unclear from your question how you obtained this dissassembly:
call 8048380
Chances are you need to use better tool, or you pointed your tool at a stripped binary (don't do that).
Here is an example of what the reasonable output should look like:
int foo() { return 42; }
int main() { return foo(); }
$ gcc t.c
$ gdb -q ./a.out
(gdb) disas main
Dump of assembler code for function main:
0x08048410 <+0>: push %ebp
0x08048411 <+1>: mov %esp,%ebp
0x08048413 <+3>: call 0x8048406 <foo> // GDB resolves the address
0x08048418 <+8>: pop %ebp
0x08048419 <+9>: ret
End of assembler dump.

what is the x86 opcode for assembly variables and constants?

i know that every instruction has opcodes.
i could find opcodes for mov , sub instructions.
but what is the opcode for variables and it's types.
we use assembler directives to define a variable and constant?
how they are represented in x86 opcodes?
nasm assembler x86: segment .bss
largest resb 2 ; reserves two bytes for largest
segment .data
number1 DW 12345 ; defines a constant number1
i tried online this https://defuse.ca/online-x86-assembler.htm#disassembly assembly to opcode conveter. but when i used nasm code to define a variable it shows error!
There's no opcode for variables. There are even no variables in machine code.
There is CPU and memory. Memory contains some values (bytes).
The CPU has cs:ip instruction pointer, pointing to memory address, where is the next instruction to execute, so it will read byte(s) from that address, and interpret them as opcode, and execute it as an instruction.
Whether you have in memory stored data or machine code doesn't matter, both are byte values.
What makes part of memory "data" or "variable" is the logical interpretation created by the running code, it's the code which does use certain part of memory only as "data/variables" and other part of memory as "code" (or eventually as both at the same time, like in this DOS 51B long COM code drawing Greece flag on screen, where the XLAT instruction is using the code opcodes also as source data for blue/white strips configuration).
Whether you write in your source:
x:
add al,al
or
x:
db 0x00, 0xC0
Doesn't matter, the resulting machine code is identical (in both cases the CPU will execute add al,al when pointed to that memory to be executed as instruction, and mov ax,[x] will set ax to 0xC000 in both cases, when used as "variable".
You may want to check listing file from the assembler (-l <listing_file_name> command line option for nasm) to see yourself there's no way to tell which bytes are code and which are data.
Assembler directives like segment, resb, or dw are not instructions and do not correspond to opcodes. That's why they are directives instead of instructions. Roughly speaking, there are two kinds of directives:
one kind of directive configures the assembler. For example, the segment directive configures the assembler to continue assembly in the section you provided.
another kind of directive emits data. For example, the dw directive emits the given datum into the object file. This can be used to place arbitrary data into memory for use with your program.

PIC24 Breakpoint on Either Read Or Write Memory Access

My environment...
PIC24
MpLab (V.8.80)
ICD 3
I have a byte that I want to watch, to see which routines are reading and which ones are actually writing, and when it happens
I can only set a breakpoint on
when the byte is written, or
when it is read
Does anyone know a way that I can set a breakpoint on either access ? i.e., I would like to watch what is written, AND what is read, in what order, by which instruction, in which routine.
All I have are a PICkit 3 and this ICD-3. It's a dual processor system where the two chips are communicating via some wires connected (ultimately, via Peripheral Pin Select) to their respective UARTs
I have never used, or touched, or even seen, a REAL Ice. Would that allow me to do this ? i.e., any bus memory access.
This is what MpLab lets me do right now with ICD-3
Can you enclose your variable in a functions as below into a new .c file. set the define on the header file.
It might be a bit heavy but with a replace it could help. FILE and LINE are compiler flags to let you know from which file on the source code and at which line it is called. The Nop(); is there because the interrupt can happen few instruction late.
Add a watch on file and line to see where it's called from.
#define setvar(x) zsetvar(x, __FILE__, __LINE__)
#define getvar() zgetvar( __FILE__, __LINE__)
byte my_var;
void zsetvar(byte val, volatile char * file, volatile char * line)
{
my_var = val;// set breakpoint
Nop();
}
byte zreadvar(volatile char * file, volatile char * line)
{
Nop(); //set breakpoint
Nop();
return my_var;
}

In an ELF file, how does the address for _start get detemined?

I've been reading the ELF specification and cannot figure out where the program entry point and _start address come from.
It seems like they should have to be in a pretty consistent place, but I made a few trivial programs, and _start is always in a different place.
Can anyone clarify?
The _start symbol may be defined in any object file. Normally it is generated automatically (it corresponds to main in C). You can generate it yourself, for instance in an assembler source file:
.globl _start
_start:
// assembly here
When the linker has processed all object files it looks for the _start symbol and puts its value in the e_entry field of the elf header. The loader takes the address from this field and makes a call to it after it has finished loading all sections in memory and is ready to execute the file.
Take a look at the linker script ld is using:
ld -verbose
The format is documented at: https://sourceware.org/binutils/docs-2.25/ld/Scripts.html
It determines basically everything about how the executable will be generated.
On Binutils 2.24 Ubuntu 14.04 64-bit, it contains the line:
ENTRY(_start)
which sets the entry point to the _start symbol (goes to the ELF header as mentioned by ctn)
And then:
. = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
which sets the address of the first headers to 0x400000 + SIZEOF_HEADERS.
I have modified that address to 0x800000, passed my custom script with ld -T and it worked: readelf -s says that _start is at that address.
Another way to change it is to use the -Ttext-segment=0x800000 option.
The reason for using 0x400000 = 4Mb = getconf PAGE_SIZE is to start at the beginning of the second page as asked at: Why is the ELF execution entry point virtual address of the form 0x80xxxxx and not zero 0x0?
A question describes how to set _start from the command line: Why is the ELF entry point 0x8048000 not changeable with the "ld -e" option?
SIZEOF_HEADERS is the size of the ELF + program headers, which are at the beginning of the ELF file. That data gets loaded into the very beginning of the virtual memory space by Linux (TODO why?) In a minimal Linux x86-64 hello world with 2 program headers it is worth 0xb0, so that the _start symbol comes at 0x4000b0.
I'm not sure but try this link http://www.docstoc.com/docs/23942105/UNIX-ELF-File-Format
at page 8 it is shown where the entry point is if it is executable. Basically you need to calculate the offset and you got it.
Make sure to remember the little endianness of x86 ( i guess you use it) and reorder if you read bytewise edit: or maybe not i'm not quit sure about this to be honest.