Using interrupts on Amstrad CPC with z88dk

Amstrad CPC and NC systems
Post Reply
cpitrat
New member
Posts: 1
Joined: Sat Nov 12, 2022 1:37 pm

Using interrupts on Amstrad CPC with z88dk

Post by cpitrat »

Hello,

I'm trying to write a program where I use interrupts using z88dk.

The idea would be to inject my interrupt callback, do whatever process I want to do (typically changing palette, switching mode, synchronizing things...) and then call the normal interrupt handler.

Reason: this is a very convenient way to handle multiple things, like playing some music while drawing something on the screen, having split screens with different modes/palettes, ...

In a "naked" environment, this would look something like that:

Code: Select all

 di
 // Save original interrupt callback
 ld hl, (0x39)
 ld original_interrupt_callback, hl
 // Replace by my own interrupt callback
 ld hl, interrupt_callback
 ld (0x39), hl
 ei
And then the code of interrupt_callback just calls original_interrupt_callback at the end.

However this doesn't work with z88dk because it's using exactly the same trick.

Looking at how it's done, I couldn't find a good way to inject my callback. There doesn't seem to be a hook planned for that.

I managed to get something that seem to work fine by exporting __fw_int_address__ in lib/target/cpc/classic/cpc_crt0.asm

I then setup my interrupts with code like this:

Code: Select all

void setup_interrupt()                                                                                                                                                                                                          
{                                                                                                                                                                                                                               
  #asm
  di
  // Save original interrupt handler address
  ld hl, (__fw_int_address__)
  ld (_original_int_address), hl
  // Add our own handler
  ld hl, _interrupt_callback
  ld (__fw_int_address__), hl
                                                                                                                                                                                                                                
  ei                                                                                                                                                                                                                            
  #endasm                                                                                                                                                                                                                       
}   
But I'd like to be able to do this kind of thing without having to modify z88dk library!

What do you think? Is there a way to do it? If not, would it make sense to introduce one?
User avatar
dom
Well known member
Posts: 1702
Joined: Sun Jul 15, 2007 10:01 pm

Re: Using interrupts on Amstrad CPC with z88dk

Post by dom »

I suspect the best thing to do, will be to make a change to the crt0 so that interposer_isr looks something like this:

Code: Select all


   EXTERN im1_vectors
   EXTENR asm_interrupt_handler
   
__interposer_isr__:

   call cpc_enable_fw_exx_set
   call 0x0038
   call cpc_enable_process_exx_set
   push    af
   push    hl
   ld      hl,im1_vectors
   call    asm_interrupt_handler
   pop     hl
   pop     af
   ei
   ret
   
   GLOBAL im1_init
   GLOBAL _im1_init
  im1_init:
  _im1_init:
    ret
 
And then you should be able to call add_raster_int() as per other targets and add/remove your own routines from the interrupt.

If you get it working, then a PR would be appreciated.
Timmy
Well known member
Posts: 276
Joined: Sat Mar 10, 2012 4:18 pm

Re: Using interrupts on Amstrad CPC with z88dk

Post by Timmy »

I was trying out CPC programming so I wrote my very first program.

This program also tries to hijack interposer_isr by hooking up my own isr routine. I also just realised it's probably better if I pushed all the registers I used in my interrupt routine.

It's not the best program and it uses too many magic numbers, so this might not work for you. (I've also only tested on 2.2 not on the nightly.)

Anyway, my suggestion is probably that if you add 1 line in interloper_isr, that might be enough. If you know how to add this asm_interrupt_handler, that is. (I don't know.) And you have to make sure that your own isr saves and restores the registers you are using.

But if you don't want to change this, it's probably fine too, because both ways seem to be unsatisfactory.

Code: Select all

__interposer_isr__:

   call cpc_enable_fw_exx_set
   call 0x0038
   di
   call cpc_enable_process_exx_set
   call asm_interrupt_handler   <-- new line
   ei
   ret

Code: Select all

// test 20221114 Timmy
// My first CPC program, so it's really bad :)

// I've built this with:
// zcc +cpc -lndos -lm -subtype=dsk -create-app -o 1 cpctest1.c

// run this in emulator with:
// run"1.cpc"

#include <stdio.h>

extern unsigned int fw_addr_ptr    @ 0x126a; // = 6122  =0x17ea
extern unsigned int isr_routine    @ 0x0039; // = 4741

char flip = 0x44;

void __FASTCALL__  isr_routine1() __naked
{
	#asm
	ld a, (_flip)
	xor 0x18
	ld (_flip), a
	ld bc, 0x7f00
	out (c), c
	out (c), a
	ld bc, 0x7f10
	out (c), c
	out (c), a
	ret
	#endasm
}

void __FASTCALL__ isr_routine_main() __naked
{
	#asm
	call 0x1240
	call 0x0038
	di
	call 0x1260
	call _isr_routine1
	di
	ld hl, _isr_routine_main
	ld (0x0039), hl
	ei
	ret
	#endasm
}

void setup_interrupts()
{
	#asm
	ld hl, _isr_routine_main
	ld (0x0039), hl
	#endasm
}

void main()
{
	printf("Hello World! %u\n", fw_addr_ptr);
	printf("isr_routine: %u\n", isr_routine);
	setup_interrupts();
	while (1);
}
Timmy
Well known member
Posts: 276
Joined: Sat Mar 10, 2012 4:18 pm

Re: Using interrupts on Amstrad CPC with z88dk

Post by Timmy »

Just a quick post here to say I've been playing with the Amstrad part of z88dk lately. Haven't got much to say yet, I hope to do a bit more writing about this later this week/next week.
Post Reply