EhBASIC IRQ support in min_mon.asm

A forum for users of EhBASIC (Enhanced BASIC), a portable BASIC interpreter for 6502 microcomputers written by Lee Davison.
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

I am trying to implement EhBasic for my little project viewtopic.php?f=8&t=2407 and have a question regarding IRQ support in min_mon.asm.

Code: Select all

08837    ; EhBASIC IRQ support
08838    
08839    IRQ_CODE
08840    FF7D  48           	PHA				; save A
08841    FF7E  A5 DF        	LDA	IrqBase		; get the IRQ flag byte
08842    FF80  4A           	LSR				; shift the set b7 to b6, and on down ...
08843    FF81  05 DF        	ORA	IrqBase		; OR the original back in
08844    FF83  85 DF        	STA	IrqBase		; save the new IRQ flag byte
08845    FF85  68           	PLA				; restore A
08846    FF86  40           	RTI
Shouldn't this code return with interrupt disable set until the ON IRQ target in the BASIC program gets a chance to handle the source of the interrupt? Otherwise the IRQ input would stay low and take another interrupt as soon as the CPU executes the RTI pulling the previous processor state with the I bit off.
6502 sources on GitHub: https://github.com/Klaus2m5
teamtempest
Posts: 443
Joined: 08 Nov 2009
Location: Minnesota
Contact:

Re: EhBASIC IRQ support in min_mon.asm

Post by teamtempest »

Not really a hardware guy, but:

1) setting the I-flag to "disable" would involve pulling the saved status flag off the stack, manipulating it and putting it back, all within the interrupt handler. Messy.

2) the code as shown does, I believe, clear the interrupt source anyway (the write-back to IrqBase). Otherwise you'd be correct in that an uncleared interrupt would trigger again as soon as the RTI executed.
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

Thank you for your answer. I should have mentioned, that IrqBase is just an address in zeropage.

IrqBase bits: 7=enabled, 6=IRQ handler setup, 5=interrupt happened

The periodic Ctrl-C (break) handler in EhBASIC picks up the interrupt happened flag and if set performs a GOSUB to the interrupt handler set by ON IRQ. It requires a RETIRQ to reenable the interrupt handler again.

I am missing the connection to the interrupt disable bit in the processor status, which would be required until RETIRQ to prevent the same interrupt source from interrupting again.

To leave IRQ disabled at RTI would be as simple as

Code: Select all

  PHA  ;save a & x
  TXA
  PHA
...
  TSX  ;point x to saved processor status
  INX
  INX
  INX
  LDA $100,x  ;set interrupt disable on stack
; could use LDA/STA $103,x instead if you assume, that one never uses the bottom of the stack
  ORA #4
  STA $100,x
...
  PLA  ;restore x & a
  TAX
  PLA
But I am asking, since there is also a couple of SEI and CLI required in the EhBASIC code to accomodate this. I might be missing the point why it is not done that way.

edit: as Garth points out in the next post, the saved registers are a problem. So I added 2 more INX, but his solution is better anyway.
Last edited by Klaus2m5 on Tue Jan 29, 2013 12:40 pm, edited 3 times in total.
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
GARTHWILSON
Forum Moderator
Posts: 8776
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: EhBASIC IRQ support in min_mon.asm

Post by GARTHWILSON »

I didn't say anything earlier because I'm not familiar with EhBASIC and I thought the IrqBase might be some sort of hardware thing and not just a variable. But since this part does not need to be re-entrant, you could simplify things a little by storing the accumulator in a variable instead of on the stack, then do something like I have in my 6502 Forth (showing the RTI as well):

Code: Select all

setirq:              ; Use to record IRQ for NEXT.  Put this address in MIRQVEC. 
     STZ  irqnot     ; Record that interrupt was req'ed by storing 0 in irqnot.  
     STA  tempA      ; Temporarily save accumulator in tempA to restore below.   
        PLA          ; Pull saved processor status byte off the μP stack,        
        ORA  #4      ; set the bit corresponding to the interrupt disable,       
        PHA          ; and push the revised status byte back on the stack.       
     LDA  tempA      ; Restore the accumulator content.                          
     RTI             ; Return from interrupt.  μP status gets restored modified.
The way NEXT works, it has to load Y with 0 for interrupt or 1 for non-interrupt continuation anyway, so it's virtually no extra work for it to load from irqnot instead of immediate.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

Thanks Garth, that is much better than my idea to keep interrupts disabled after an RTI. I will have to correct my example as I forgot about the saved registers on the stack. :oops:
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
Rob Finch
Posts: 465
Joined: 29 Dec 2002
Location: Canada
Contact:

Re: EhBASIC IRQ support in min_mon.asm

Post by Rob Finch »

I'm trying to implement IRQ's but when I set the IrqBase flags I get syntax errors in BASIC. If I comment out the code then BASIC seems to work fine. Has anybody else run into a syntax error problem ?
Note that I clear the source of the interrupt in this routine so there's no need to mess with the interrupt enable flag on the stack.
Note also this is RTF65002 code.

Code: Select all

;------------------------------------------------------------------------------
; 100 Hz interrupt
; - takes care of "flashing" the cursor
;------------------------------------------------------------------------------
;
p100Hz:
	cld			; clear extended precision mode
	pha
	phx
	phy

	; support EhBASIC's IRQ functionality
	; code derived from minimon.asm
;	lda		#3				; Timer is IRQ #3
;	sta		IrqSource		; stuff a byte indicating the IRQ source for PEEK()
;	lb		r1,IrqBase		; get the IRQ flag byte
;	lsr						; shift the set b7 to b6, and on down ...
;	orb		r1,r1,IrqBase	; OR the original back in
;	sb		r1,IrqBase		; save the new IRQ flag byte

	; support tiny basic's IRQ rout
	inc		IRQFlag			

	inc		TEXTSCR+83
	stz		0xFFDCFFFC		; clear interrupt
	
	; flash the cursor
	lda		CursorFlash
	beq		p100Hz1
	jsr		CalcScreenLoc
	tay
	lda		$10000,y
	ldx		IRQFlag
	and		r2,r2,#$0F
	and		#$F0
	or		r1,r1,r2
	sta		$10000,y
p100Hz1
	ply
	plx
	pla
	rti
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

When I modified minmon.asm for use with my emulator I remember seeing syntax errors as well when interrupts occured faster than the Basic program could service them. I decided to change the way EHBasic inserts and removes bits in IRQBase from shifts to ORA/AND immediate ops. You may still loose interrupts but IRQBase will not be trashed.

The modifications are here: http://2m5.de/6502_Emu/ehbasic.zip
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
Rob Finch
Posts: 465
Joined: 29 Dec 2002
Location: Canada
Contact:

Re: EhBASIC IRQ support in min_mon.asm

Post by Rob Finch »

Okay, I tried the simpler ORA/AND logic and it improved things. I no longer get syntax errors. Instead I get:
"mjReadymj" as the ready prompt. The carriage return and line feed are substituted with the characters 'm' and 'j'.
This looks like the string is being bit fiddled by the interrupt routine somehow. But it's only the carriage return and line feed.
It looks almost like the IrqBase is being or'd with the characters. Resulting in 6D? and 6A? instead of 0D, and 0A.

I tried slowing the interrupts down from 100 Hz to 50 Hz, but it didn't make a difference.
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

One of the problems of adding code in EhBASIC is the hardcoded zeropage addresses. I would suspect an address conflict between character output and interrupt handling in page zero.
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
Rob Finch
Posts: 465
Joined: 29 Dec 2002
Location: Canada
Contact:

Re: EhBASIC IRQ support in min_mon.asm

Post by Rob Finch »

Quote:
I would suspect an address conflict between character output and interrupt handling in page zero.
It's a good thought, but I've tripled checked, and checked again. I leave zero page addresses $00 to $FF strictly to EhBASIC. With a 4KW zero page space in 32 bit mode, I placed the other zero page locations at the high end of the 4KW space.

Code: Select all

   200 = 000000DC                      NmiBase		EQU		0xDC
    201 = 000000DF                      IrqBase		EQU		0xDF
                                        
                                        
                                        
    205 = 00000500                      SP8Save		EQU		0x500		; SP8Save area $500 to $5FF
    206 = 00000600                      SPSave		EQU		0x600		; SPSave area $600 to $6FF
                                        ; TinyBasic AREA = 0x700 to 0x77F
                                        
    209 = 00000780                      HeadRdy0	EQU		0x780
    210 = 00000781                      HeadRdy1	EQU		HeadRdy0+1
    211 = 00000782                      HeadRdy2	EQU		HeadRdy1+1
    212 = 00000783                      HeadRdy3	EQU		HeadRdy2+1
I tried to manually trace through the code to see how the 'syntax error' would show up, to see if I could get hints to this problem.
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

There are two things, that are a bit strange about the output modification of the Ready string.

The "R" in Ready does not get modified to lower case. If it is true, that the string output is ored with IrqBase somehow, the string shoud be "mjreadymj". The string is sent by EhBASIC as a whole and it doesn't make sense that only cr & lf get modified but not "R".

IrqBase should never be allowed to contain the enable bit set (bit 6) without the setup bit (bit 7). So I would expect some strange characters from the language dependent part of the ASCII character set above $80. Of course your terminal program may mask the most significant bit and only display characters in the range of 0-$7f instead.
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
Rob Finch
Posts: 465
Joined: 29 Dec 2002
Location: Canada
Contact:

Re: EhBASIC IRQ support in min_mon.asm

Post by Rob Finch »

Quote:
The "R" in Ready does not get modified to lower case. If it is true,
Yes, it's true. The 'R' does not get modified to lower case.

I dumped the ROM to make sure it wasn't being overwritten somehow. Nope. ROM contents look good.

I use a wide 'or' function on the databus input to the cpu. I thought maybe another device was being enabled somehow. But I don't see how.

If I write a little program to print text to the screen, then the CR/LF come out okay. It seems to be just the ready prompt that's screwy.
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

When printing the ready message in EhBASIC the IrqBase gets cleared. It is hard to see how IrqBase can influence the message itsself.

Code: Select all

; BASIC warm start entry point
; wait for Basic command

LAB_1274
					; clear ON IRQ/NMI bytes
	LDA	#$00			; clear A
	STA	IrqBase		; clear enabled byte
	STA	NmiBase		; clear enabled byte
	CLI				; allow interrupts
	LDA	#<LAB_RMSG		; point to "Ready" message low byte
	LDY	#>LAB_RMSG		; point to "Ready" message high byte

	JSR	LAB_18C3		; go do print string
However, in my version interrupts are enabled at the same time because I do set interrupt disable in the PS on stack just before RTI to prevent flooding with interrupts if there are some that do not get silenced in the minmon interrupt handler. I will CLI in the CTRL-C check to allow just one interrupt per BASIC program line.

The original version enables interrupts when it sees an ON IRQ statement and never disables them again. It would be interrupted after every machine instruction in EhBASIC until the interrupt gets silenced by the interrupt handler in the BASIC program. I consider this to be a bug.

Could you please post your complete minmon.asm and tell me which EhBASIC you are using, mine, Lee's or yours (additional modifications)?
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
Rob Finch
Posts: 465
Joined: 29 Dec 2002
Location: Canada
Contact:

Re: EhBASIC IRQ support in min_mon.asm

Post by Rob Finch »

Quote:
Could you please post your complete minmon.asm and tell me which EhBASIC you are using, mine, Lee's or yours (additional modifications)?
Umm, urr, my "minimon.asm" is 6,000 lines of code (including 3,000 for TinyBasic). I'm trying to evolve it into a multi-tasking kernel. There's very few lines of code required to support EhBASIC though.

I'm using Lee's version of EhBASIC, with a minor modification in startup to set the V_INPT and V_OUTP vectors.
I included the V_INPT and V_OUTP routines in the basic.asm file.

The following is the ISR.

* It could still be a processor bug.

Code: Select all

strStartQue:
	db		1,0,0,0,2,0,0,0,3,1,0,0,4,0,0,0

;------------------------------------------------------------------------------
; 100 Hz interrupt
; - takes care of "flashing" the cursor
; - switching tasks
;------------------------------------------------------------------------------
;
p100Hz:
	; Handle every other interrupt because 100Hz interrupts may be too fast.
	pha
	lda		IRQFlag
	ina
	sta		IRQFlag
	ror
	bcc		p100Hz11	
	pla
	rti

p100Hz11:

	cld		; clear extended precision mode

	; save off regs on the stack
	phx
	phy
	push	r4
	push	r5
	push	r6
	push	r7
	push	r8
	push	r9
	push	r10
	push	r11
	push	r12
	push	r13
	push	r14
	push	r15
	ldx		TaskNo
	and		r2,r2,#$FF
	tsa						; save off the stack pointer
	sta		TCB_SPSave,x
	tsr		sp8,r1			; and the eight bit mode stack pointer
	sta		TCB_SP8Save,x

	; support EhBASIC's IRQ functionality
	; code derived from minimon.asm
	lda		#3				; Timer is IRQ #3
	sta		IrqSource		; stuff a byte indicating the IRQ source for PEEK()
	lb		r1,IrqBase		; get the IRQ flag byte
	ora		#$20			; set IRQ pending bit
	sb		r1,IrqBase


	inc		TEXTSCR+83		; update IRQ live indicator on screen
	stz		0xFFDCFFFC		; clear interrupt
	
	; flash the cursor
	lda		CursorFlash		; test if we want a flashing cursor
	beq		p100Hz1a
	jsr		CalcScreenLoc	; compute cursor location in memory
	tay
	lda		$10000,y		; get color code $10000 higher in memory
	ldx		IRQFlag			; get counter
	and		r2,r2,#$0F		; limit to low order nybble
	and		#$F0			; prepare to or in new value, mask off foreground color
	or		r1,r1,r2		; set new foreground color for cursor
	sta		$10000,y		; store the color code back to memory
p100Hz1a

	; Check the timeout list to see if there are items ready to be removed from
	; the list. Also decrement the timeout of the item at the head of the list.

p100Hz15:	
	ldx		TimeoutList
	bmi		p100Hz12				; are there any entries in the timeout list ?
	lda		TCB_Timeout,x
	bne		p100Hz14				; has this entry timed out ?
	txa
	jsr		RemoveFromTimeoutList
	jsr		AddTaskToReadyList
	bra		p100Hz15				; go back and see if there's another task to be removed

p100Hz14:
	dea								; decrement the entries timeout
	sta		TCB_Timeout,x
	
p100Hz12:
	; Search the ready queues for a ready task.
	; The search is occasionally started at a lower priority queue in order
	; to prevent starvation of lower priority tasks. This is managed by 
	; using a tick count as an index to a string containing the start que.

	ld		r6,#5			; number of queues to search
	ldy		IRQFlag			; use the IRQFlag as a buffer index
	lsr		r3,r3,#1		; the LSB is always the same
	and		r3,r3,#$0F		; counts from 0 to 15
	lb		r3,strStartQue,y	; get the queue to start search at
p100Hz2:
	lda		HeadRdy0,y
	bmi		p100Hz1
	; Move the head of the ready queue to the tail
	ldx		TailRdy0,y
	sta		TCB_NxtRdy,x
	sta		TailRdy0,y
	lda		HeadRdy0,y
	tax
	lda		TCB_NxtRdy,x
	php
	ld	    r4,#-1	
	st		r4,TCB_NxtRdy,x
	plp
	bpl		p100Hz4
	; here there is no next ready task
	; so we just stay with the same task
	lda		HeadRdy0,y
p100Hz4:
	sta		HeadRdy0,y
	sta		TaskNo
	bra		p100Hz3
p100Hz1:
	iny
	cpy		#5
	bne		p100Hz5
	ldy		#0
p100Hz5
	dec		r6
	bne		p100Hz2

	; here there were no tasks ready
	stz		TaskNo			; select BIOS task
p100Hz3:
p100Hz10
	ldx		TaskNo
	and		r2,r2,#$FF
	lda		TCB_SP8Save,x		; get back eight bit stack pointer
	trs		r1,sp8
	ldx		TCB_SPSave,x		; get back stack pointer
	txs
	; restore registers
	pop		r15
	pop		r14
	pop		r13
	pop		r12
	pop		r11
	pop		r10
	pop		r9
	pop		r8
	pop		r7
	pop		r6
	pop		r5
	pop		r4
	ply
	plx
	pla
	rti


;------------------------------------------------------------------------------
; 1000 Hz interrupt
; This IRQ must be fast.
; Increments the millisecond counter
;------------------------------------------------------------------------------
;
p1000Hz:
	stz		0xFFDCFFFD				; acknowledge interrupt
	inc		Milliseconds			; increment milliseconds count
	rti
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: EhBASIC IRQ support in min_mon.asm

Post by Klaus2m5 »

V_OUTP routine would also be of interest. However, I do not see how adding just this part of the code

Code: Select all

   ; support EhBASIC's IRQ functionality
   ; code derived from minimon.asm
   lda      #3            ; Timer is IRQ #3
   sta      IrqSource      ; stuff a byte indicating the IRQ source for PEEK()
   lb      r1,IrqBase      ; get the IRQ flag byte
   ora      #$20         ; set IRQ pending bit
   sb      r1,IrqBase
could cause the bug you are seeing, unless it is directly related to the memory locations and registers altered in this part of the code. r1 is synonymous for the 8-bit accumulator I suppose? PHA/PLA saves r1 completely?
6502 sources on GitHub: https://github.com/Klaus2m5
Post Reply