It is currently Mon Oct 20, 2014 5:48 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 13 posts ] 
Author Message
PostPosted: Mon Jan 17, 2011 3:31 pm 
Offline
 Profile

Joined: Sat Sep 04, 2010 5:28 pm
Posts: 92
Alright I see some of you 6502 and Beeb experts trivial refer to doing this as if it was an every day occurrence but never actually detail the logic or code behind it.

Assuming I need to do two things:
  • Have a screen with a mode 5 image for the top 50% of the screen
  • Change the palette to white on black and then print in mode 4 for the bottom 50%

How would I do this? I can poke like mad on &FE20/&FE21 to mangle mode and palette changes; but I can never get text right (I can get 40 characters, but it stays in the mode 5/mode 4 font).

I've tried looking at the AUG for the System VIA; but the scan's pretty poor and the writing seems to assume that people are only going to use it for device IO. The example in Mastering Assembler Code doesn't seem to work. I've even disassembled the BBC Elite for the mode changing code, which loses me at this bit:
Code:
lsr a
lda #&1e
sta &8b
sta &fe44
lda #&39
sta &fe45
lda &348
bne &116f

(Which seems to be setting up the timer).

Has anybody got any working, sample code for getting the system VIA timer working for a mode/palette switch?


Top
 
PostPosted: Mon Jan 17, 2011 3:57 pm 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
It sounds like you have the mode split working OK, but the OS is configured for Mode 5 when displaying characters. So the simplest thing would be to explicitly select Mode 4 instead, and perform your mode split exactly as you are now - and then, calls to OSWRCH should display Mode 4 characters correctly. I'm assuming you don't need any Mode 5 text at all, right?

The mechanics of the mode split are something like this. I'm going to describe a method which uses 'legal' methods, i.e. work alongside the OS without problems, so you can still do character printing and input via the normal calls.

Hook into the VSync event:

- put the address of your handler in &220/&221
- enable the VSync event with *fx 14,4

In the VSync event handler:

- check if it's a VSync event, like this:
Code:
CMP #4
BNE notvsync
  <handle vsync here>

- set up the Video ULA (&FE20/&FE21) for the top screen mode.
- set an unused timer (e.g. User VIA T2 at &FE68) to interrupt a certain time later. Do that like this:

Code:
LDA #time MOD 256  ; set timer low byte
STA &FE68
LDA #time DIV 256  ; set timer high byte
STA &FE69

As a rough guide, if the screen is positioned with *TV 0,1, the value of time is (5 + screen character row) * 512. There'll be a fair amount of wobble if you're doing it 'legally' like this, due to the latency which arises from the OS's interrupt handler running with interrupts disabled, and not always letting the split mode interrupt get serviced immediately. I recommend, when trying to get the value right, have it change the background colour, so you can clearly see where the split is. Also leave a blank buffer of at least 3 or 4 pixel lines, to hide any wobble.

Enable and hook into the User VIA T2 interrupt:

- Enable the interrupt, like this:
Code:
LDA #&A0
STA &FE6E

- Claim the IRQ1V, like this:
Code:
SEI
LDA #irqhandler MOD 256
STA &204
LDA #irqhandler DIV 256
STA &205
CLI

- Check for the timer interrupt in irqhandler, like this:
Code:
LDA &FE6D
AND #&20  ; check T2 flag
BNE nottimer
STA &FE6D  ; acknowledge interrupt
  ..

- Then perform palette and mode changes for bottom screen mode (&FE20/&FE21), and finally JMP (oldirq1v) which should contain the previous contents of the IRQ1V.

As long as you touch only A (not X or Y), there's no need to preserve any registers or flags on the stack, because the 6502 has already pushed the flags to the stack, and the OS handler stub has already saved A in &FC.

Also, somewhere on this forum, I did post some sample code for doing pretty much this, although it was not an OS-friendly method, so it might not be so useful.


Top
 
PostPosted: Mon Jan 17, 2011 6:40 pm 
Offline
 Profile

Joined: Sat Sep 04, 2010 5:28 pm
Posts: 92
Hmmm, this sort of works, but from the top of the screen until the interrupt point I'm getting a moving line, which starts replacing the colour, then moves down till the interrupt point; then goes back to the top again!

The other question is why are we using VIA2, instead of VIA1, when we could just set up one interrupt to manage both the vsync and timer (as Elite does)?


Top
 
PostPosted: Mon Jan 17, 2011 10:57 pm 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
Hmm, I didn't try the code, so there's every chance I missed something somewhere. I have no idea what the moving line is though. Could you post the code perhaps, and maybe we can spot the problem?

You need two interrupts because you need to set up the Video ULA twice per frame - the VSync makes sure we are synched to the same point each frame, and then the timer (which is set to a constant value relative to the VSync) interrupts at a regular place in the middle of the frame.

It'd be possible to use Timer 1 too, and it could be set up in free-run mode to interrupt every 50Hz (so there was no need to write its value each VSync), but this is a bit more fiddly to set up. I just used Timer 2 in this example so that there's still a free-run timer free should it be needed. Also the example uses the User VIA rather than the System VIA, because the latter is used by the OS, and certain things stop working properly if you change its values.


Top
 
PostPosted: Tue Jan 18, 2011 12:25 am 
Offline
 Profile

Joined: Sat Sep 04, 2010 5:28 pm
Posts: 92
Unfortunately for me - I tried in the BASIC assembler, rather than beebasm, but I managed to get it out using print to file from beebem:
Code:
   20FOR ION=0 TO 2 STEP 2
   30P%=&A00
   40[OPTION
   50.INIT
   60SEI
   70LDA &220
   80STA &70
   90LDA &221
  100STA &71
  110LDA #event MOD 256
  120STA &220
  130LDA #event DIV 256
  140STA &221
  141LDA #14
  142LDX #4
  143JSR &FFF4
  150LDA &204
  160STA &72
  170LDA &205
  180STA &73
  190LDA #irq MOD 256
  200STA &204
  210LDA #irq DIV 256
  220STA &205
  221LDA #&A0
  222STA &FE6E
  230CLI
  240RTS
  250.event
  260CMP #4
  270BNE notvsync
  280TXA:PHA
  290LDX #3
  300.loop LDA pal1,X
  310STA &FE21
  320DEX
  330BPL loop
  340PLA:TAX
  350LDA #3680 MOD 256
  360STA &FE68
  370LDA #3680 DIV 256
  380STA &FE69
  390.notvsync
  400JMP (&70)
  410.irq
  420LDA &FE6D
  430AND #&20
  440BNE nottimer
  450STA &FE6D
  460TXA:PHA
  470LDX #3
  480.loop2:LDA pal2,X
  490STA &FE21
  500DEX
  510BPL loop2
  520PLA:TAX
  530.nottimer
  540JMP (&72)
  550.pal1
  560EQUD &00104050
  570.pal2
  580EQUD &07174757
  590]
  591NEXT
  595MODE 1
  596CALL INIT
  600FOR I=0 TO 25:COLOUR I MOD 4:PRINT "WIBBLE":NEXT
  610GOTO 610

This only does a palette switch of colour 0 at the moment (starting small). I'm using X as an index, so I'm having to save X to the stack (which I know isn't production perfect - even though Elite does this).

I've attached the UEF if that makes it easier.

Also, thanks in advance for helping me with this - I may not even use it (as I may end up using a 16k squeezed mode 1 screen) - but its helpful for me to try and get around the nuts and bolts of how the Beeb hardware works, which I missed as I was an Elk owner!


Attachments:
interupt-mess.zip [99.1 KiB]
Downloaded 10 times
Top
 
PostPosted: Tue Jan 18, 2011 8:14 am 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
Oops, sorry it's my fault.

Change BNE nottimer to BEQ nottimer, and all works as expected!


Top
 
PostPosted: Tue Jan 18, 2011 10:46 pm 
Offline
 Profile

Joined: Sat Sep 04, 2010 5:28 pm
Posts: 92
Yep it now works; that's neat and easy to do (as long as I get the palette right and leave a gap).

I'm impressed that you wrote that from first principles with only one minor error (and I should've spotted it too)!

Thanks very much - even if I don't use it - it's useful to have!


Top
 
PostPosted: Wed Feb 09, 2011 1:19 pm 
Offline
 Profile

Joined: Sat Sep 04, 2010 5:28 pm
Posts: 92
I was messing around with the concepts above... I found the quickest way of getting 7 colours on Mode 1 is to mess around with the flashing colours:
  • Set colours 1-3 with their flashing equivalents (leave 0 black as a safe background colour)
  • Use the above routine, but just toggle the flashing colour bit in &FE20
  • Success!
As shown below:
Attachment:
7colmode1.PNG [2.03 KiB]
Downloaded 198 times

This way the interrupt is simply (for MODE 1):
Code:
LDA #&D8
STA &FE20
and
Code:
LDA #&D9
STA &FE20

Saves messing around with changing all the palette registers!

Now if only I can get this to work in the middle of a line (though I'm not certain that's possible). I'm still not 100% certain how the timing works.


Top
 
PostPosted: Wed Feb 09, 2011 2:14 pm 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
Ahhh that's a good idea - I never thought of that one! You can do the colour/mode split at any point, even halfway across a scanline if you want. But if you're still letting the OS in, the latency is far too high to be able to get this accurate enough - as I say, you'll most likely have an uncertainty of a couple of scanlines (and even more if you keep pressing keys and generating a load of interrupts, or using the disc system, for example).

The timer value can be worked out exactly; it depends on:

  • the *TV setting (or CRTC registers 7 and 8; specifying interlace will change the timing from non-interlaced)
  • the VSync pulse width (specified in CRTC register 3)
  • the number of cycles taken to enter an interrupt (i.e. the 6502 timing for executing an IRQ, plus the OS IRQ entry point cycles, plus cycles taken in your own code, prior to changing anything video related)
  • and obviously whereabouts on the screen you want the split

But it's difficult to come up with a precise formula when trying to do everything legally (so it works alongside the OS). The important thing to know is that (in non-interlaced modes), each scanline takes exactly 128 clock cycles (which is 64 ticks of the 1MHz timers), so from this you can probably position your split more-or-less by trial and error.


Top
 
PostPosted: Mon Feb 14, 2011 7:02 pm 
Offline
 Profile

Joined: Sat Sep 04, 2010 5:28 pm
Posts: 92
I did do some messing around; I couldn't get an interrupt to occur in the middle of a line, but managed about 8 palette changes without massive slowdown. This is on mode 1, so you may be able to get more on mode 5.

Then I had to stop as the flickering lines I used to show me that it was working started to give me a migraine!


Top
 
PostPosted: Wed Feb 23, 2011 2:06 pm 
Offline
User avatar
 Profile

Joined: Fri Jan 11, 2008 12:11 am
Posts: 12
Found this on an eBay disk. Haven't checked the code to see exactly how it works, but thought it applied to this thread! :)


Attachments:
MixMode.zip [3.48 KiB]
Downloaded 35 times
Top
 
PostPosted: Wed Feb 23, 2011 2:44 pm 
Offline
Site Admin
User avatar
 Profile

Joined: Wed Dec 19, 2007 10:41 pm
Posts: 373
Cor blimey! :o


Top
 
PostPosted: Wed Feb 23, 2011 7:09 pm 
Offline
 Profile

Joined: Sat Sep 04, 2010 5:28 pm
Posts: 92
Interesting - it saves the OS vdu variables whilst setting up the screen and copies them all during the interrupt.

Nicely done and the minimal amount of judder is very good! Good find!


Top
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 13 posts ] 

All times are UTC [ DST ]


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:  
cron