Program hangs when enabling interrupts

I am developing a program using the sdcc compiler. Everything was going fine until I’ve tried enabling some interrupts, at which point the program just hangs.

With sdcc there is no Rabbit BIOS, so everything has to be set up from scratch. I’ve allocated space for the interrupt vector table, pointed the IIR register to it and initialized it with either some stubs (ipres + ret) or small ISRs.

I’ve tried enabling the periodic interrupt and the Timer B interrupt. But as soon as I do either one, the program stops responding.

I am wondering if there some additional initialization steps that I’ve missed, which are done by the Rabbit BIOS. Is there anybody who can help me figure out what’s wrong?

I’ve written a small program to demonstrate the problem (see below). In a nutshell it does the following:

  1. Point IIR to address 0x0100 in Flash, which is pre-filled with ISR stubs.
  2. Connect memory bank 2 to RAM.
  3. Set up stack.
  4. Call the _main function, which:
    a. Disables the watchdog.
    b. Sets parallel port B as output.
    c. Optionally enables either periodic or Timer B interrupts.
    d. Blinks some lights.

If I don’t enable either of the interrupts, the lights blink. As soon as I enable one or both of them, the board hangs.

	.module crt0

GCSR		.equ	0x00 ; Global control / status register
WDTCR       .equ    0x08
WDTTR       .equ    0x09
SWDTR       .equ    0x0c
GOCR        .equ    0x0e
MMIDR		.equ	0x10
STACKSEG	.equ	0x11
SEGSIZE		.equ	0x13
MB0CR		.equ	0x14 ; Memory Bank 0 Control Register
MB1CR		.equ	0x15 ; Memory Bank 1 Control Register
MB2CR		.equ	0x16 ; Memory Bank 2 Control Register
MB3CR		.equ	0x17 ; Memory Bank 3 Control Register
PBDR        .equ    0x40
PBDDR       .equ    0x47
TBCSR       .equ    0xb0
TBCR        .equ    0xb1
TBM1R       .equ    0xb2
TBL1R       .equ    0xb3
TBM2R       .equ    0xb4
TBL2R       .equ    0xb5

	.area	_HEADER (ABS)
	.org 	0

	; setup internal interrupts
	ld	a, #1
	ld	iir, a

	; Configure physical address space.
	; Leave MB0CR Flash at default slow at /OE0, /CS0
	; Assume slow RAM at /CS1, /OE1, /WE1
	ld	a, #0x05
	ioi
	ld	(MB2CR), a;

	; Configure logical address space. 32 KB root segment followed by 8 KB data segment, 16 KB stack segement, 8 KB xpc segment.
	; By default, SDCC will use the root segment for code and constant data, stack segment for data (including stack). data segment and xpc segement are then unused.
	ld	a, #0xa8	; 16 KB stack segment at 0xa000, 8 KB data segment at 0x8000
	ioi
	ld	(SEGSIZE), a

	; Configure mapping to physical address space.
	ld	a, #0x76
	ioi
	ld	(STACKSEG), a	; stack segment base at 0x76000 + 0xa000 = 0x80000

	; Set stack pointer directly above top of stack segment
	ld	sp, #0xe000

	call	_main
	jp	_exit

	.org	0x100 ; Periodic Interrupt
	push	af
	ioi
	ld	a, (GCSR) ; clear interrupt
	pop	af
	ipres
    ret

	.org	0x110 ; Secondary Watchdog - Rabbit 3000A only
    ipres
	ret

	.org	0x120 ; rst 0x10
	ret

	.org	0x130 ; rst 0x18
	ret

	.org	0x140 ; rst 0x20
	ret

	.org	0x150 ; rst 0x28
	ret

	.org	0x160 ; Syscall instruction - Rabbit 3000A only
	ret

	.org	0x170 ; rst 0x38
	ret

	.org	0x180 ; Slave Port
	ipres
	ret

	.org	0x1a0 ; Timer A
	ipres
	ret

	.org	0x1b0 ; Timer B
    push af
    ioi
    ld a, (TBCSR)
    pop af
	ipres
	ret

	.org	0x1c0 ; Serial Port A
	ipres
	ret

	.org	0x1d0 ; Serial Port B
	ipres
	ret

	.org	0x1e0 ; Serial Port C
	ipres
	ret

	.org	0x1f0 ; Serial Port D
	ipres
	ret

	.org	0x200

	.area	_HOME
	.area	_CODE
	.area	_INITIALIZER
	.area   _GSINIT
	.area   _GSFINAL

	.area	_DATA
	.area	_INITIALIZED
	.area	_BSEG
	.area   _BSS
	.area   _HEAP

	.area   _CODE
_exit::
1$:
	jr	1$

_main:
    ipset 0

    ld hl, #0xffff
    call _sleep

    ld a, #0x51     ; disable watchdog
    ioi
    ld (WDTTR), a
    ld a, #0x54
    ioi
    ld (WDTTR), a

    ld a, #0xff     ; parallel B as output
    ioi
    ld (PBDDR), a

    ;call _enable_periodic
    ;call _enable_timer_b

_blink:
    ld hl, #0xffff
    call _sleep

    ld a, #0xff
    ioi
    ld (PBDR), a    ; turn on all LEDs

    ld hl, #0xffff
    call _sleep

    ld a, #0x00
    ioi
    ld (PBDR), a    ; turn off all LEDs

    jr _blink

_sleep:
    ld a, h
    or a, l
    ret z
    dec hl
    jr _sleep

_enable_periodic:
    ld a, #0x01
    ioi
    ld (GCSR), a
    ret

_enable_timer_b:
    ld a, #0x00
    ioi
    ld (TBM1R), a
    ioi
    ld (TBL1R), a
    ld a, #0xff
    ioi
    ld (TBM2R), a
    ioi
    ld (TBL2R), a

    ld a, #0x07
    ioi
    ld (TBCSR), a
    ld a, #0x01
    ioi
    ld (TBCR), a
    ret

I’ve also posted this in the sdcc bug tracker and attached the program there (see 2nd comment):

https://sourceforge.net/p/sdcc/bugs/3652

Well, I got this figured out. It turns out this bit of the manual is very important:

The internal interrupt vector table occupies 512 bytes, and the external interrupt vector
table is 256 bytes in size. Since the RST and SYSCALL vectors use all eight bits of the
IIR for addressing, the lowermost bit of IIR should always be set to zero so to keep some
vectors from inadvertently overlapping.

1 Like