Fusion_C and HTIMI

Page 1/5
| 2 | 3 | 4 | 5

By PingPong

Enlighted (4155)

PingPong's picture

28-09-2019, 15:23

Hello, is it possible to install a HTIMI handler under Fusion_C? Is there a function that accept a function ptr being the function that should be called at Vblank ? Does it work under MSX-DOS?

Login or register to post comments

By akumajo

Resident (43)

akumajo's picture

28-09-2019, 15:43

Hi! You can set up your own interrupt routine like this, with or without Fusion-C :
(it's just an example, the program displays the number of loop cycles between two interrupts and stops when you press space)

#include < stdio.h >
#include < stdlib.h >
#include "fusion-c/header/msx_fusion.h"

// io ports for vdp
__sfr __at (0x98) VDP_port1;
__sfr __at (0x99) VDP_port2;

volatile char vdp_status;
volatile int  sync_flag;
volatile int  cpt;

void interrupt_routine () __critical __interrupt(0)
__preserves_regs(a,b,c,d,e,h,l,iyl,iyh) {

// read the vdp status. Mandatory to have a new interrupt!
  vdp_status = VDP_port2;

  cpt++;

  sync_flag=1; // set the synchronization flag to 1

}

void main (void)
{
  unsigned int top=1;
  unsigned int im1;
  unsigned int key=0;
  unsigned int nbb=0;
// save MSX system original interrupt address
  im1=Peekw(57);

//  printf("it=%x\n\r",im1); // causes the interrupt to skip the while statement, and then ends program!

// interrupt setup
  sync_flag=0;
  __critical { *((unsigned int*)57)=(unsigned int)interrupt_routine; }

  cpt=0;

/***** main cycle *****/
  while (top==1) {
    nbb++;
    key=Inkey();
    if (key==32) {top=2;}
    if (cpt==10) {cpt=0;printf("%d ",nbb);nbb=0;}

// interruption detection
// if sync_flag = 0 -> no interrupt occurred since the beginning of main cycle
// if sync_flag = 1 -> 1 or more interrupts have already occurred
    if (!sync_flag) {
      sync_flag=0;
    }

  }

// put back the original interrupt
  Pokew(57,im1); // address, data

  printf("Back to MSX-DOS\n\r");

  Exit(0);
}

By Grauw

Ascended (10821)

Grauw's picture

28-09-2019, 16:07

I don’t think doing it like this is good practice though. Rather than overriding the system ISR completely like you do here, intercepting the VDP interrupt and never calling the hooks or the system ISR, it is much better to just use the standard H.TIMI hook, which is easy to set up with an (interslot) call to your handler.

This way all BIOS functions will continue to operate as normal, and any interrupt handling set up by anything else won’t be disrupted (like the DiskROM, which needs it or disk drives will never stop spinning).

Even if you need a high speed interrupt response (which you won’t if it’s C) for things like a line interrupt and you want to hook directly into 38H or IM 2 for that reason, I think one should still relay to the BIOS interrupt handler in case it’s any other type of interrupt.

@PingPong The topic was also briefly discussed here:

Grauw wrote:

The H.TIMI and H.KEYI hooks work fine in DOS, and are the preferred way of hooking interrupts if you don’t need extremely quick response, and you don’t if you use C.

You need to place the ISR in memory page 3 (C000h-FFFFh), or do an interslot call, to ensure it continues to work while the BIOS (page 0) or DiskROM (page 1) or any other service routine is paged in.

Alternatively, remove the hook when doing a BIOS or BDOS call (if it’s H.KEYI, don’t forget to also disable the interrupt device). It’s more cumbersome but I think it may be difficult to ensure the C code is in page 3 so probably your best option.

Grauw wrote:
zPasi wrote:

Of course! Now that is obvious, when you wrote that. I think page 2 (8000h-BFFFh) should also be safe?

No, the common two (BIOS and DiskROM) are in pages 0 & 1, but afaik MSX-DOS2 uses page 2 as well when loading. Also inter-segment writes use page 2. And you don’t know what other extension is present that may switch out page 2 on some hooks.

zPasi wrote:

I've made some progress. I have a small routine that I'll copy to somewhere in page 3, and hook on H.TIMI. That routine makes an interslot call to a handler that is located anywhere in RAM, and can be written in C or ASM. Seems to work well.

Grauw wrote:

Sounds good. You can use CALLF, which is exactly 5 bytes and fits in a hook, then you won’t need the small helper routine.

By akumajo

Resident (43)

akumajo's picture

28-09-2019, 16:33

You're right Grauw, I'm too focused on games for ROM Tongue , the development for MSX-DOS requires to respect the operating system.

Maybe someone has an example to give ?
Among Fusion-C examples there is :

// Interrupt functions Test
// Fusion-c 1.1

#include "fusion-c/header/msx_fusion.h"
#include 



static unsigned int count = 0;

/* --------------------------------------------------------- */
static char my_interrupt( void ) {

  if( IsVsync() == 0 ) return 0;

  count++;
  return 1;
}

/* --------------------------------------------------------- */
int main( void ) {
  unsigned int prev_count;
  InitInterruptHandler();

  SetInterruptHandler( my_interrupt );

  prev_count = 0;
  while( prev_count < 600 ) {

    DisableInterrupt();
    prev_count = count;
    EnableInterrupt();

    printf( "%d\n\r", prev_count );
  }
  EndInterruptHandler();
  return 0;
}

By zPasi

Champion (499)

zPasi's picture

30-09-2019, 09:11

I have made a new interrupt system for Fusion-C, using H.TIMI. It didn't make it for Fusion-C v1.2, but it is coming.

I could share it here but I don't have it handy. I'll be back with it in the evening.

By PingPong

Enlighted (4155)

PingPong's picture

30-09-2019, 21:19

. @grauw:
About the use of CALLF I see it can be used to make a interslot call. However how I can use it in a C environment under msx dos 1 to hook HTIMI is unclear to me. What value I need to poke in the hook for the slot id parameter?
When I install my interrupt hook I m working on a standard msx dos configuration where all 64k are ram.
While I am able to poke into hook area the 0xc3 lowbyte highbyte sequence of my int hook routine I almost am sure that this will not work when the hook gets called because of paging issues...

By Grauw

Ascended (10821)

Grauw's picture

10-04-2020, 20:02

You can get the slot ID of a currently paged address that you want to call with a GETSLT routine. There isn’t a BIOS routine for this, but it’s described in the MSX2 Technical Handbook and several other references.

Depending on your library it may exist there as well. To use the MSX2 Technical Handbook routine for page 3 you just need to adjust the shifts a bit so that the right bits are combined.

Here’s an implementation for all pages from the MAP:

RSLREG: equ 138H
EXPTBL: equ 0FCC1H
SLTTBL: equ 0FCC5H

; h = memory page
; a <- slot ID formatted FxxxSSPP
; Modifies: af, bc, de, hl
Memory_GetSlot:
	call RSLREG
	bit 7,h
	jr z,PrimaryShiftContinue
	rrca
	rrca
	rrca
	rrca
PrimaryShiftContinue:
	bit 6,h
	jr z,PrimaryShiftDone
	rrca
	rrca
PrimaryShiftDone:
	and 00000011B
	ld c,a
	ld b,0
	ex de,hl
	ld hl,EXPTBL
	add hl,bc
	ld c,a
	ld a,(hl)
	and 80H
	or c
	ld c,a
	inc hl  ; move to SLTTBL
	inc hl
	inc hl
	inc hl
	ld a,(hl)
	ex de,hl
	bit 7,h
	jr z,SecondaryShiftContinue
	rrca
	rrca
	rrca
	rrca
SecondaryShiftContinue:
	bit 6,h
	jr nz,SecondaryShiftDone
	rlca
	rlca
SecondaryShiftDone:
	and 00001100B
	or c
	ret

By zPasi

Champion (499)

zPasi's picture

30-09-2019, 21:33

Almost forgot... Here is my interrupt.s

There may still be room for improvement but for normal use, it's better than the old one in Fusion-C.

; ___________________________________________________________
;/               __           _                              \
;|              / _|         (_)                             |
;|             | |_ _   _ ___ _  ___  _ __                   |
;|             |  _| | | / __| |/ _ \| '_ \                  |
;|             | | | |_| \__ \ | (_) | | | |                 |
;|             |_|  \__,_|___/_|\___/|_| |_| *               |
;|                                                           |
;|               The MSX C Library for SDCC                  |
;|                     V1.1   -  05-2019                     |
;|                                                           |
;|                Eric Boez &  Fernando Garcia               |
;|                                                           |
;|               A S M  S O U R C E   C O D E                |
;|                                                           |
;|                                                           |
;\___________________________________________________________/
;
;
;	Interrupt functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;	2006/11/25	t.hara														;;
;;	2019/09/02	Pasi Kettunen												;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;

 .area _CODE
	.globl	_SetInterruptHandler
	.globl	_EndInterruptHandler
  
; ---------------------------------------------------------
reset_vect	= 0x0000
bdos		= 0x0005
H_TIMI		= 0xFD9F
INTHANDLER	= 0xF975 ; Repurposing BASIC PLAY commands Voice A queue

; ---------------------------------------------------------
;	void SetInterruptHandler( void (*p_handler)( void ) )
; ---------------------------------------------------------
_SetInterruptHandler::

	di
	pop		af
	pop		hl
	push	hl
	push	af
	ld		a,h
	or		l
	jr		z,isnull
	ld		(user_interrupt + 2),hl

	ld		hl,(savedhook + 1)
	ld		a,h
	or		l
	jr		nz,already_set
	ld		hl, #H_TIMI
	ld		de, #savedhook
	ld		bc, #5
	ldir
already_set:
	ld		hl, #inthandler_start
	ld		de, #INTHANDLER
	ld		bc, #inthandler_end - inthandler_start
	ldir
	
	ld		a, #0xc3				;	jp intr_handler
	ld		(H_TIMI), a
	ld		hl,#INTHANDLER
	ld		(H_TIMI+1), hl
isnull:
	ei
	ret

; ---------------------------------------------------------
;	void EndInterruptHandler(void)
; ---------------------------------------------------------
_EndInterruptHandler::
	ld		hl,(savedhook + 1)
	ld		a,h
	or		l
	ret		z

	di
	ld		hl, #savedhook
	ld		de, #H_TIMI
	ld		bc, #5
	ldir
	ld		hl,#0
	ld		(savedhook + 1), hl
	ld		a,#0xF9	; RET
	ld		(savedhook),a
	ei
	ret

; ---------------------------------------------------------
;	intr_handler
; ---------------------------------------------------------

inthandler_start:
	push	af
    push	ix
    push	iy
user_interrupt:
    ld		ix,#0
    ld		iy,(0xF341)      ; main ram slotaddress
    call	0x001c           ; interslotcall
	pop		iy
	pop		ix
pass:
	pop		af

savedhook:
	ret
	.dw		0
	.dw		0

inthandler_end:

Here is how to use it:

  SetInterruptHandler( my_int_function );
...

//when done
  EndInterruptHandler();

I think there might come problems if using disk-rom etc when the handler is active, depending on where in ram "my_int_function" is located. Haven't tested that yet. But most BIOS usage, like KB / joystick reading etc should be fine.

By Grauw

Ascended (10821)

Grauw's picture

30-09-2019, 21:43

zPasi wrote:

I think there might come problems if using disk-rom etc when the handler is active, depending on where in ram "my_int_function" is located. Haven't tested that yet. But most BIOS usage, like KB / joystick reading etc should be fine.

It works fine if all the interrupt handling code is in page 3. If it’s in another page, some calls to e.g. BDOS can cause trouble, to avoid that use a CALLF instead of a jp (C3H). Hooks are exactly 5 bytes especially so that they can fit a CALLF interslot call.

By ducasp

Paladin (712)

ducasp's picture

30-09-2019, 21:46

Interrupt function MUST not be in page 0 (0x00000-0x3fff) if it is a MSX-DOS target, otherwise your hook will call bios if interrupt comes when your code or previous interrupt handler paged bios in page 0,not sure for other targets.

By Grauw

Ascended (10821)

Grauw's picture

30-09-2019, 22:01

To elaborate, that is because even in DOS the BIOS ISR is swapped in and executing from page 0.

Unless you use an interslot call in your hook, then it’s fine to have the handler in page 0 as well.

(Did I mention yet that it’s best to use an interslot call? Tongue)

Page 1/5
| 2 | 3 | 4 | 5