Question about HTIMI hook (FD9Fh)

Page 1/2
| 2

By Pbk71

Expert (67)

Pbk71's picture

28-04-2021, 18:34

Hi all,

I'm learning assembly and I've got a question about the HTIMI hook at memory location FD9Fh.

I've written a small assembly program that uses this HTIMI-hook. It produces a beep about 1 time every second.

BEEP:       equ 0x00C0		; bios call to beep
HTIMI:      equ 0xFD9F		; Hook memory adress

SPEED:      equ 51			; speed (51 is about 1 once every second)
            
            org 0xD000		; the address of our program

start:      di     			; disable interrupts, just incase this interrupt would be called during changing the hook
            ld a,0xc3		; jp instruction opcode
            ld (HTIMI),a	; load into the hook memory adress
            ld hl, beepcue	; load the adress of the beep routine that's invoked every time in hl
            ld (HTIMI+1), HL ; load adress into hook after the jp instruction             
            ei   			; enable interrupts
            ret             ; return to BASIC

beepcue:	
            ld hl, counter 	; load memoryadress of the counter
            ld a,(hl)		; load counter value into a
            dec a           ; decrease counter value
            ld (hl),a		; store new counter value into memory
            ret nz       	; return id counter is not zero
            ld a, SPEED		; reset counter to 51
            ld (hl),a		; store new counter value into memory
            call BEEP		; call the BIOS routine that makes the computer beep
            ret				; return

counter:
            db SPEED		; determines the speed of the beep

            ; use the label "start" as the entry point
            end start

MSXPen-link: https://msxpen.com/codes/-MZOESeqYLl6INTvq6Xl

It works fine, but there's something I don't understand. Before I change the memory at location FD9Fh it has got the value of F7h. This is the instruction RST 30h With this page I found out that this is a function that calls an interslot call (CALLF). And that's where I got confused ;)

Why is RST 30h normally executed at this hook after every VBLANK? And why can I change it without consequences? Or are there consequences?
At other memory locations from hooks is a ret instruction (C9h), for example at FD9Ah (HKEYI hook). I did expect this for the HTIMI hook as well.

Can anyone explain how this works? Thanks in advance!

Login or register to post comments

By Grauw

Ascended (9762)

Grauw's picture

28-04-2021, 18:41

The hook is probably installed by the DiskROM to stop the drive spinning a while after it had activity. Consequently, if you do not call it the drive will continue to spin indefinitely which is not very nice. Other hardware can also potentially install hooks (though it’s not common).

When you install a hook, the proper procedure is to copy the old hook to some other memory location in the upper memory (> C000H), and jump to it at the end of your hook to make sure the original hook is called as well.

By ToriHino

Paladin (689)

ToriHino's picture

28-04-2021, 19:04

As a small optimization, for

            ld a,(hl)		; load counter value into a
            dec a           ; decrease counter value
            ld (hl),a		; store new counter value into memory

you can just use

            dec  (hl)          ; decrease counter value

And as Grauw already stated, indeed looks like another hook is already installed. If you startup a plain and simple MSX setup, the value of HTIMI will be C9h as well.

By Grauw

Ascended (9762)

Grauw's picture

28-04-2021, 19:15

            ld a, SPEED		; reset counter to 51
            ld (hl),a		; store new counter value into memory
            ld (hl),SPEED	; reset counter to 51

You don’t need to worry about optimisation when you’re just learning assembly, however I think it’s good to know that operations on (hl) are quite flexible.

By ToriHino

Paladin (689)

ToriHino's picture

28-04-2021, 19:22

Yes totally agree on that. In general it is far more important to first get it working before starting to optimize (even when not just learning assembly Wink )

By Pbk71

Expert (67)

Pbk71's picture

28-04-2021, 19:35

@Grauw & @ToriHino: thanks for the optimisation tips. I will use them to change my code.

I use MSXPen that is based on WebMSX. As soon as I boot it contains these bytes from FD9Fh:
F7h,8Bh,E8h,77h
I did not try another emulator yet.

I've implemented the old hook in my timed beep-routine and it seems to work:

beepcue:	
            ld hl, counter 	; load memoryadress of the counter
            dec(hl)			; decrease counter value	
            ld a,(hl)		; load counter value into a
            jp nz, oldhook  ; do nothing if counter is not zero
            ld (hl),SPEED	; reset counter to 51
            call BEEP		; call the BIOS routine that makes the computer beep
oldhook:    db 0xF7,0x8B,0xE8,0x77 ; call the old hook
            ret				

I know that I should have copied the memory from FD9F to 'oldhook', but this was just a quick way to test it.
Is this, besides that, the proper way to do it to preserve the old hook?

By Pbk71

Expert (67)

Pbk71's picture

28-04-2021, 19:54

Ok, I found the LDIR instruction and that was not too difficult to understand. My new, optimized version that preserves the old hook is now like this:

BEEP:       equ 0x00C0		; bios call to beep
HTIMI:      equ 0xFD9F      ; Hook memory adress

SPEED:      equ 51          ; speed (51 is about 1 once every second)
            
            org 0xD000		; the address of our program

start:      di              ; disable interrupts, just incase this interrupt would be called during changing the hook

            ; --- preserve the old hook ---
            ld de, oldhook  ; adress memory should be copied to
            ld hl, HTIMI    ; adress where the old hook is stored
            ld bc,4         ; 4 bytes to copy
            LDIR            ; this copies the 4 bytes   
            
            ; --- install the new hook ---
            ld a,0xc3		; jp instruction opcode
            ld (HTIMI),a	; load into the hook memory adress
            ld hl, beepcue	; load the adress of the beep routine that's invoked every time in hl
            ld (HTIMI+1),HL ; load adress into hook after the jp instruction             
            ei              ; enable interrupts
            ret             ; return to BASIC

beepcue:	
            ld hl, counter 	; load memoryadress of the counter
            dec(hl)         ; decrease counter value	
            ld a,(hl)		; load counter value into a
            jp nz, oldhook  ; return id counter is not zero
            ld (hl),SPEED	; reset counter to 51
            call BEEP		; call the BIOS routine that makes the computer beep
oldhook:    db 0,0,0,0      ; this is where the old hook will be stored
            ret				
counter:
            db SPEED		; determines the speed of the beep

            ; use the label "start" as the entry point
            end start

Thanks for the help!

By Grauw

Ascended (9762)

Grauw's picture

28-04-2021, 20:00

Looks good! But gotta copy one more byte since hooks are 5 bytes long.

Also, no need for the ld a,(hl) after the dec (hl). The dec sets the zero flag already.

By ro

Scribe (4371)

ro's picture

28-04-2021, 20:08

Looking good. If you are new to ASM, it's nice code already. impressed.

something about interrupts. I don't like EI and DI instructions, they flip interrupts on/off while you don't know the state of ints perse. So I would do this:
ld a,#C9 ;opcode for RET
ld (HTIMI),a
ld hl,Beepcue
ld (HTIMI+1),hl
ld (HTIMI+3),A ;ret
ld a,#C3 ;opcode for JP
ld (HTIMI),a
;this will first disable HTIME, then set ADR+RET and then JUMP.
;oh, well.

and, your SPEED is 51. If you have a 50Hz refresh, you have 50 frames per sec. So SPEED should be 50 to match Smile on 60Hz machines you'd nee SPEED=60 ofcourse.

Learning ASM is awesome!

By Pbk71

Expert (67)

Pbk71's picture

28-04-2021, 20:09

Aha, thanks. I also can remove the ret instruction now. I had to put it there to make it work, but this was just the fifth byte that was not copied Tongue

Only the changed parts here:

   ; --- preserve the old hook ---
            ld de, oldhook  ; adress memory should be copied to
            ld hl, HTIMI    ; adress where the old hook is stored
            ld bc,5         ; 5 bytes to copy
            LDIR            ; this copies the 5 bytes   
            
         
beepcue:	
            ld hl, counter 	; load memoryadress of the counter
            dec(hl)         ; decrease counter value	
            jp nz, oldhook  ; return id counter is not zero
            ld (hl),SPEED	; reset counter to 51
            call BEEP		; call the BIOS routine that makes the computer beep
oldhook:    db 0,0,0,0,0    ; this is where the old hook will be stored 5BYTES!
                                        ; No ret instruction anymore
            end start

By Pbk71

Expert (67)

Pbk71's picture

28-04-2021, 20:17

@ro: thanks for the compliment, really like to puzzle with asm Smile

This program was based on the code of someone else who used 51 instead of 50. This already puzzled me. I'll change it to 50.

I will study your way to work around EI and DI. That's an interesting approach.

The new, finished version can be found here now: https://msxpen.com/codes/-MZOgY93P9kR8pusvJJZ

Page 1/2
| 2