Extended vectors - huh?

discussion of beeb/electron applications, languages, utils and educational s/w
Post Reply
crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Extended vectors - huh?

Post by crj » Mon Mar 19, 2018 7:11 am

Up until now, I'd only ever used extended vectors to implement a filing system. For this purpose - where you wish to be the sole claimant of a normal vector - it's fine.

But what if you want to be able to pass a call on to the vector's previous claimant? Worse, what if the previous claimant was another extended vector?

And what if you want to claim IRQ1V/IRQ2V? The extended vector mechanism exits with an RTS, not an RTI.

Acorn spent a bunch of OS workspace on the extended vector table, and a bunch of ROM on the entry trampolines for all those vectors. They must have had some kind of aspiration that they be useful for stuff other than filing systems... right?

So what's the workaround? Sometimes, if you're wanting to stash details of a vector's previous claimant, you're going to need to claim some workspace for your ROM anyway, at which point you could just drop your own trampoline into RAM and avoid the whole thorny problem. But suppose you want to avoid claiming workspace and have somewhere else you can stash data - sideways RAM, for example. Now what?

I can't see any solution which doesn't involve using the forbidden knowledge that, on entry to your extended vector, the stack contains the return address to the extended vector mechanism (hereinafter "XVRA"), the previous ROM number ("PR#"), two junk bytes then the return address to the caller.

Even armed with that knowledge, what you'd have to do is pretty grim:
  • To pass the call on to a previous non-extended claimant: stack two more bytes. Shuffle XVRA and PR# up by two bytes. Insert previous_claimant - 1 into the gap. RTS.
  • To pass the call on to a previous extended claimant: stack seven more bytes. Copy XVRA. Insert the ROM number of the previous claimant and previous_claimant - 1 into the gap. RTS.
  • To return from an interrupt: Decrement the caller address which is below the stack frame. Yoink the saved P up from below the frame. Shuffle XVRA and PR# down by a byte. Restore P manually. RTS.
Am I overlooking a cleaner, easier or more legal way to do any of this?

SteveF
Posts: 510
Joined: Fri Aug 28, 2015 8:34 pm
Contact:

Re: Extended vectors - huh?

Post by SteveF » Sat Mar 24, 2018 12:47 am

If the vector you're claiming can't be called from within an interrupt (so I guess OSWRCH or OSRDCH are OK, OSWORD probably isn't, for example), could you stash the previous values of the normal vector and the extended vector (5 bytes in total) when you claim the vector? Then to pass a call through to the previous claimant, you could restore those 5 bytes, JSR OSWRCH (or whatever) will then call the original claimant, and you can restore your values to re-claim the vector yourself afterwards.

(I haven't tried this. I am wondering whether your ROM would get paged back in afterwards, but I am guessing it would, otherwise you couldn't make OS calls via extended vectors from within paged ROMs.)

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Sat Mar 24, 2018 2:28 am

Unfortunately, as well as knowing the call wouldn't be made from an interrupt (and, actually, EVENTV is one of the more desirable things to claim, just to get called regularly on machines before the Master) a second issue is the risk of re-entrancy, i.e. the handling one call generating another to the same entry point.

To some extent, that could work. (Another issue that's just occurred to me is the risk of coming unstuck if the routine exits via BRK rather than RTS. Ick.) It might conform better, but I'm not sure it's necessarily more elegant let alone more efficient. /-8

SteveF
Posts: 510
Joined: Fri Aug 28, 2015 8:34 pm
Contact:

Re: Extended vectors - huh?

Post by SteveF » Sat Mar 24, 2018 3:16 pm

Yeah, I guess BRK really puts the final nail in the coffin of that idea. Oh well. :-)

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Sat Mar 24, 2018 4:08 pm

Well, you could always work around that by intercepting... BRKV... from... inside... your...

Oh. )-8

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Fri Feb 01, 2019 4:26 pm

Almost a year later, I'm revisiting this issue.

It seems to me you can get a considerable distance by having a primitive for "JMP to code in different ROM". On reflection, I'm kinda surprised that primitive was never made public in the way OSRDRM/OSRDSC was.

Rummaging in JGH's collection of OS ROMs it's clear that such a routine always exists as part of the extended vector return mechanism, just not always at the same address:
  • OS 0.10 for models A,B: not there, but then nor is anything else much to do with sideways ROMs!
  • OS 1.00, 1.20, 1.20US, 2.00 for models A,B,B+: &FF9F
  • OS 1.00 for Electron: &FF9D
  • OS 3.20, 3.50, 4.00, 5.00, 5.10, 5.11 for Master/ET/Compact: &FFA5
The implementations vary a little, but in every case you can call the routine with:
  • A=target ROM
  • X,Y passed to callee
  • P irrelevant
  • Stack (head first):
    • A for callee
    • P for callee
    • Target address -1
Armed with that, it seems you can do something like:

Code: Select all

PHA:PHA \fill in target address later
PHP:PHA \stash callee P,A
TXA:PHA \temporarily stash X
\now we can corrupt A,X,P in populating the target address
TXS
LDA #targetM1LO : STA &104,X : LDA #targetM1HI : STA &105,X
PLA:TAX \recover X
LDA #targetROM
JMP JumpToDifferentROM
This suffices for forwarding a call to a previous extended vector claimant. But what about a previous normal vector claimant?

I'm now wondering: is there really a problem at all? What happens if one simply JMPs to the previous claimant without first switching back to the previous ROM? (It would get switched back later, when a the final claimant does RTS.) Are vector claimants entitled to have a specific ROM paged in? Does anything rely on that in practice? My hunch is... probably not?

That just leaves intercepting interrupts via extended vectors, which continues to look horribly impractical. For your own interrupts, there's service call 5. Maybe I hope that's all I need. /-8

User avatar
jgharston
Posts: 3449
Joined: Thu Sep 24, 2009 11:22 am
Location: Whitby/Sheffield
Contact:

Re: Extended vectors - huh?

Post by jgharston » Sat Feb 02, 2019 4:47 am

I seem to remember years ago a draft proposal the recruit one of the unused vectors to dynamically bounce off through a chain of vector claimants. When you claim the vector, you save the old extended vector in your own workspace, when your code is called, you pass it on by copying the saved vector to one of the unused vectors, and indirectly jump to it. The fiddly bit that I can't remember is releasing the vector, as if somebody else has added to the chain you can't just copy the saved extended vector back to the extended vector space as that chops off everybody before you.

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.25
(C) Copyright J.G.Harston 1989,2005-2015
>_

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Sun Feb 03, 2019 1:34 am

If one was designing a mechanism from scratch, I imagine the sensible solution would be something like:

Allow a page for extended vector chains. Divide it up into 64 entries, each of which is ROM number, address in ROM, address of next entry. That would allow the space to be used as linked lists of vector claimants, which would then be easy for the OS to manipulate as claimants were added and removed.

Unfortunately, we don't get to design a mechanism from scratch. )-8

Coeus
Posts: 1184
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: Extended vectors - huh?

Post by Coeus » Sun Feb 03, 2019 10:24 pm

crj wrote:
Fri Feb 01, 2019 4:26 pm
This suffices for forwarding a call to a previous extended vector claimant. But what about a previous normal vector claimant?
Then you just finish by jumping to the address previously in the non-extended vector. So that means when hooking the vector you need to check where the standard vector in page 2 points. If it is to an extended vector trampoline then you leave the vector in page 2 put and hook the extended vector instead. If it is to anywhere else you hook the page 2 vector.

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Mon Feb 04, 2019 1:22 am

Coeus wrote:
Sun Feb 03, 2019 10:24 pm
Then you just finish by jumping to the address previously in the non-extended vector.
The problem, as I mentioned, is that this means the previous claimant is called with your ROM selected, not the one that was selected when the vector was invoked. (The previous ROM is restored correctly when the final claimant returns.)

I'm not clear: are you saying you believe that's not a problem, or were you ignoring that consideration?

Coeus
Posts: 1184
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: Extended vectors - huh?

Post by Coeus » Mon Feb 04, 2019 1:54 pm

crj wrote:
Mon Feb 04, 2019 1:22 am
I'm not clear: are you saying you believe that's not a problem, or were you ignoring that consideration?
It is certainly possible to imagine a case where it could be a problem. For example, someone writes a piece of code that is assembled from BASIC, calls subroutines in the BASIC ROM and thus relies on BASIC being the currently paged in ROM.

Would someone actually do this, though? If the vector was something that could be called asynchronously, for example EVENTV, how does the writer know that it won't be called when a filing system is the current ROM? Or, in the case of the master, that an OS call hasn't been redirected to a sideways ROM - there's quite a bit of OS overflow in the terminal ROM. Anyone writing code that runs in RAM that relies on a particular ROM being paged in should page it in explicitly first.

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Mon Feb 04, 2019 4:22 pm

One example where it would obviously go seriously wrong is if you trapped BRKV that way.

Fortunately, you can just handle paged ROM service call 6 instead.

On reflection, looking through the other vectors I can see:
  • Filing system vectors - OK
  • Vectors that might be called under interrupt - OK
  • Vectors that are likely to be called indirectly by things a language calls directly (BYTEV, etc.) - almost certainly OK
  • Vectors I can't imagine ever wanting to intercept, like USERV, IND1V
Maybe I should just relax and treat this as a solved problem? (-8

Coeus
Posts: 1184
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: Extended vectors - huh?

Post by Coeus » Mon Feb 04, 2019 8:01 pm

crj wrote:
Mon Feb 04, 2019 4:22 pm
One example where it would obviously go seriously wrong is if you trapped BRKV that way.
Surely not. The OS selects the current language before dispatching via BRKV. See this code from the OS:

Code: Select all

 DC27    TXA             ;save X on stack
 DC28    PHA             ;
 DC29    TSX             ;get status pointer
 DC2A    LDA     &0103,X ;get Program Counter lo
 DC2D    CLD             ;
 DC2E    SEC             ;set carry
 DC2F    SBC     #&01    ;subtract 2 (1+carry)
 DC31    STA     &FD     ;and store it in &FD
 DC33    LDA     &0104,X ;get hi byte
 DC36    SBC     #&00    ;subtract 1 if necessary
 DC38    STA     &FE     ;and store in &FE
 DC3A    LDA     &F4     ;get currently active ROM
 DC3C    STA     &024A   ;and store it in &24A
 DC3F    STX     &F0     ;store stack pointer in &F0
 DC41    LDX     #&06    ;and issue ROM service call 6
 DC43    JSR     &F168   ;(User BRK) to roms
                         ;at this point &FD/E point to byte after BRK
                         ;ROMS may use BRK for their own purposes 
 
 DC46    LDX     &028C   ;get current language
 DC49    JSR     &DC16   ;and activate it
 DC4C    PLA             ;get back original value of X
 DC4D    TAX             ;
 DC4E    LDA     &FC     ;get back original value of A
 DC50    CLI             ;allow interrupts
 DC51    JMP     (&0202) ;and JUMP via BRKV (normally into current language)
with DC16 being:

Code: Select all

 DC16    STX     &F4     ;RAM copy of rom latch 
 DC18    STX     &FE30   ;write to rom latch
 DC1B    RTS             ;and return
So, if you were to do something in RAM, then page in a different ROM, and then finally return to the old caller you just need to re-select the current language before calling the previous caller.
Last edited by Coeus on Mon Feb 04, 2019 8:04 pm, edited 1 time in total.

User avatar
jgharston
Posts: 3449
Joined: Thu Sep 24, 2009 11:22 am
Location: Whitby/Sheffield
Contact:

Re: Extended vectors - huh?

Post by jgharston » Mon Feb 04, 2019 9:55 pm

Ok, here's a first attempt. It appropriates INDV3 for use by the daisy-chaining.

Code: Select all

      \ Daisy-chain into a vector
      \ ws points to my workspace
      \ X=vector to claim, offset from &200

.claim_vector
      LDY #my_vector_store
      LDA &200,X:STA (ws),Y:INY
      LDA &201,X:STA (ws),Y:INY:PHA
      LDA #0:STA (ws),Y:DEY:DEY
      PLA:CMP #&FF              :\ CS=Extended vector
      :
      TXA:PHA:PHP               :\ Save vector number, CC/CS
      LSR A:PHA:TXA:TSX
      ADC &101,X:INX:TXS:TAX    :\ X=(X DIV 2)*3
      :
      PLP:BCC not_extended      :\ Old vector was not extended
      LDA &D9F,X:STA (ws),Y:INY :\ Save old XVEC
      LDA &DA0,X:STA (ws),Y:INY
      LDA &DA1,X:STA (ws),Y
      .not_extended
      :
      PLA:TAY                   :\ Get vector number back
      TXA:STA &200,Y            :\ Set it to extended vector
      LDA #&FF:STA &201,Y
      :
      LDA myvector+0:STA &D9F,X :\ Set XVEC to my handler
      LDA myvector+1:STA &DA0,X
      LDA &F4:STA &DA1,X
      ...


      .myvector
      EQUW myhandler

      .myhandler
      \ blah blah blah
      \
      \ Claim the call:
      RTS
      \
      \ Pass on the call:
      \ ws points to my workspace
      PHA:TYA:PHA
      LDY #my_vector_store
      LDA (ws),Y:STA &DED:INY :\ Set XINDV3
      LDA (ws),Y:STA &DEE:INY
      LDA (ws),Y:STA &DEF
      PLA:TAY:PLA
      JMP &FF4E               :\ Jump through XINDV3

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.25
(C) Copyright J.G.Harston 1989,2005-2015
>_

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Tue Feb 05, 2019 12:49 am

Coeus wrote:
Mon Feb 04, 2019 8:01 pm
crj wrote:
Mon Feb 04, 2019 4:22 pm
One example where it would obviously go seriously wrong is if you trapped BRKV that way.
Surely not. The OS selects the current language before dispatching via BRKV.
Indeed, so there's a risk this could happen:
  • BRK
  • OS selects current language then does JMP (BRKV)
  • BRKV calls the XBRKV extended vector, which selects our ROM and enters it at the specified address
  • We do some stuff
  • We pass the call on to the previous BRKV handler using JMP
  • ...but the previous handler was in the current language, and we're still running from our ROM
So this is an exception - possibly the only exception - to the notion that it's OK to claim a vector via the extended vector mechanism then pass the call on to a previous non-extended claimant using a simple JMP. It seems like every other vector gives no guarantee as to which ROM will be selected when it's invoked so either the previous claimant was the OS, or was in RAM, or was another extended vector.

To fix things up for BRK one would either have to dig about in the extended vector call stack frame to extract the previous ROM number, or rely on it being the current language and using OSBYTE 252 to retrieve it, in either case then pretending we were calling another extended vector.

But, as I say, simply taking the BRK service call is way simpler.

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Tue Feb 05, 2019 12:54 am

jgharston wrote:
Mon Feb 04, 2019 9:55 pm
Ok, here's a first attempt. It appropriates INDV3 for use by the daisy-chaining.
My main misgiving about that approach is that it's not re-entrant. If something uses that mechanism in an interrupt handler while another thing is trying to use it in the foreground, there will be fireworks.

A more minor worry is that if several claimants do that, the stack is deepened by half a dozen bytes each time, when ideally it would remain at constant depth. Admittedly, it's quite unlikely there would ever be enough claimants to matter, but...

User avatar
jgharston
Posts: 3449
Joined: Thu Sep 24, 2009 11:22 am
Location: Whitby/Sheffield
Contact:

Re: Extended vectors - huh?

Post by jgharston » Tue Feb 05, 2019 2:26 pm

crj wrote:
Tue Feb 05, 2019 12:54 am
jgharston wrote:
Mon Feb 04, 2019 9:55 pm
Ok, here's a first attempt. It appropriates INDV3 for use by the daisy-chaining.
My main misgiving about that approach is that it's not re-entrant. If something uses that mechanism in an interrupt handler while another thing is trying to use it in the foreground, there will be fireworks.

A more minor worry is that if several claimants do that, the stack is deepened by half a dozen bytes each time, when ideally it would remain at constant depth. Admittedly, it's quite unlikely there would ever be enough claimants to matter, but...
It only uses XIND3V for the jump to the next daisy chain handler. Once that jump has been executed, once the code at &FF51 has dispatched, XIND3V is available again for the next daisy chain jump. It's re-entrant, as long as an IRQ doesn't occur in the middle of it and use it itself - but, as in the case of BRKs, IRQs should be using the ROM service call anyway.

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.25
(C) Copyright J.G.Harston 1989,2005-2015
>_

crj
Posts: 844
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Extended vectors - huh?

Post by crj » Wed Feb 06, 2019 1:01 am

Say you intercepted BYTEV that way. Then an EVENTV claimant calls OSBYTE in the middle of a foreground OSBYTE call. /-8

Post Reply