6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Nov 21, 2024 11:08 pm

All times are UTC




Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Jan 28, 2013 4:00 pm 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
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:
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


Top
 Profile  
Reply with quote  
PostPosted: Mon Jan 28, 2013 11:29 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 411
Location: Minnesota
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.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2013 9:59 am 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
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:
  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.

_________________
6502 sources on GitHub: https://github.com/Klaus2m5


Last edited by Klaus2m5 on Tue Jan 29, 2013 12:40 pm, edited 3 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2013 10:45 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
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:
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?


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2013 12:27 pm 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
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


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 16, 2013 4:47 am 
Offline
User avatar

Joined: Sun Dec 29, 2002 8:56 pm
Posts: 460
Location: Canada
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:
;------------------------------------------------------------------------------
; 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

_________________
http://www.finitron.ca


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 16, 2013 1:20 pm 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
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


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 19, 2013 1:45 am 
Offline
User avatar

Joined: Sun Dec 29, 2002 8:56 pm
Posts: 460
Location: Canada
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.

_________________
http://www.finitron.ca


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 19, 2013 6:04 am 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
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


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 19, 2013 8:30 am 
Offline
User avatar

Joined: Sun Dec 29, 2002 8:56 pm
Posts: 460
Location: Canada
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:
   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.

_________________
http://www.finitron.ca


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 19, 2013 9:50 am 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
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


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 19, 2013 9:54 pm 
Offline
User avatar

Joined: Sun Dec 29, 2002 8:56 pm
Posts: 460
Location: Canada
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.

_________________
http://www.finitron.ca


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 20, 2013 6:45 am 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
When printing the ready message in EhBASIC the IrqBase gets cleared. It is hard to see how IrqBase can influence the message itsself.
Code:
; 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


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 20, 2013 10:18 am 
Offline
User avatar

Joined: Sun Dec 29, 2002 8:56 pm
Posts: 460
Location: Canada
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:
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

_________________
http://www.finitron.ca


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 20, 2013 3:17 pm 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
V_OUTP routine would also be of interest. However, I do not see how adding just this part of the code
Code:
   ; 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


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: