Log inRegister an accountBrowse CSDbHelp & documentationFacts & StatisticsThe forumsAvailable RSS-feeds on CSDbSupport CSDb Commodore 64 Scene Database
You are not logged in - nap
CSDb User Forums


Forums > C64 Coding > C-64 coding cargo cults
2020-01-14 13:33
Krill

Registered: Apr 2002
Posts: 2980
C-64 coding cargo cults

The most-discussed coding cargo cult on the C-64 is probably SEI/CLI around interrupt setup code.

Here's another one: acknowledging VIC raster interrupts.

According to the datasheet http://archive.6502.org/datasheets/mos_6567_vic_ii_preliminary... an active VIC interrupt is acknowledged by writing a "1" to the corresponding bit in $d019.

The usual way to achieve this seems to be "DEC $D019" and to a lesser extent other read-modify-write instructions, saving a few bytes and/or cycles compared to "LDA $D019 : STA $D019" or "LDA #$xF : STA $D019".
This works because RMW instructions on 6502/6510 read a value (here, the pending interrupts) and write the same value again (clearing the interrupt latches) before writing the modified value.
This is also why this technique does not work on SuperCPU's 65816 in native mode, as its RMW instructions lack the dummy-write of the unmodified value.

Now, the cargo cult bit is this: For raster interrupts, it suffices to write any value with bit 0 set (likewise for other VIC interrupts). Clearing all VIC interrupts can be achieved by writing any value with bits 0..3 set.

So, you can save 2 cycles by simply recycling any register value that happens to have bit 0 set, writing that one to $d019 to acknowledge a VIC raster interrupt.

Please post other coding cargo cults here. =)
2020-01-14 14:00
JackAsser

Registered: Jun 2002
Posts: 2014
When you want to ack a timer interrupt and exit the ISR you normally read $ddxd and the do RTI.

This takes 4+6 cycles.

But by instead place the RTI on $ddxc and do a jmp $ddxc you exploit the fact that the 6502 ALWAYS reads at least two bytes even if an instruction is just one byte. So, the read from $ddxd is implicitly done by the CPU while fetching the RTI.

This takes 3+6 cycles.
2020-01-14 14:08
Krill

Registered: Apr 2002
Posts: 2980
Okay, then let's extend this thread to clever hacks as well and not just somewhat superfluous boilerplate code typed in unthinkingly. =)
2020-01-14 14:31
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: Okay, then let's extend this thread to clever hacks as well and not just somewhat superfluous boilerplate code typed in unthinkingly. =)

Ahh ok, now I know the difference!
2020-01-14 15:56
oziphantom

Registered: Oct 2014
Posts: 490
interrupts must always do
pha
txa
pha
tya
pha

pla
....

is another good one.

lda vic reg
and value
ora value
sta vic reg

there are cases where this is needed, and it should be taught this way to hammer the point of setting a bit. But 99.8% of the time you can just do lda sta

also inc $d019 don't think I've ever seen dec
2020-01-14 17:18
Oswald

Registered: Apr 2002
Posts: 5094
I use lsr d019.

Remember flaming on sei/cli. What happens without them if you only changed half of the jump vector?
2020-01-14 17:40
Krill

Registered: Apr 2002
Posts: 2980
Quoting Oswald
Remember flaming on sei/cli. What happens without them if you only changed half of the jump vector?
The point is that you do not need SEI/CLI for regular interrupt setup when starting from BASIC.

There is only one possible interrupt, the CIA1 timer interrupt, which you would disable with LDA #$7F : STA $DC0D. Note that there is no need to acknowledge any pending interrupt by reading $dc0d either.

After that, no interrupts will be triggered, and you can set up a raster interrupt without worrying for a race-condition (interrupt triggering between setting interrupt vector lo- and hi-bytes). Once everything is done you would enable raster interrupts via LDA #$01 : STA $D01A.
2020-01-14 19:45
Oswald

Registered: Apr 2002
Posts: 5094
ok, but honestly thats rather a good practise than a cargo cult. you need to be sure of your enviroment, and then decide if to use sei/cli. not for the faint hearted :)

btw if anyone reads this make sure do also this:

lda #$7f
sta dc0d
sta dd0d

lda $dc0d
lda $dd0d

this will stop all timer irqs, and acknowledge any pending ones. many times have I wondered why my raster irq doesnt work after decades of coding, and it was a missing lda $dc0d often the case.

edit: I missed your note regarding dc0d, but frankly as above written many times I needed to add it in to make raster irq happen from basic!
2020-01-14 20:20
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: ok, but honestly thats rather a good practise than a cargo cult. you need to be sure of your enviroment, and then decide if to use sei/cli. not for the faint hearted :)

btw if anyone reads this make sure do also this:

lda #$7f
sta dc0d
sta dd0d

lda $dc0d
lda $dd0d

this will stop all timer irqs, and acknowledge any pending ones. many times have I wondered why my raster irq doesnt work after decades of coding, and it was a missing lda $dc0d often the case.

edit: I missed your note regarding dc0d, but frankly as above written many times I needed to add it in to make raster irq happen from basic!


especially when linking with other parts. Don't assume anything setup properly by the previous part. Assume crap in all registers imo. But for the initial kernel-takeover, fine no need to sei/cli etc.
2020-01-14 20:24
Golara
Account closed

Registered: Jan 2018
Posts: 212
I use sei/cli when changing irq only in demo parts, for one files I don't think I ever had a situation where I wanted to change the irq when (or where when thinking raster position) an irq could fire (i.e I'm on a way different line)
btw. I do inc $d019, just muscle memory. But that trick with writing anything with bit 0 set is good, i'll remember that.

Here's my little tip, though only about the assembler itself. Kickasm supports labels even in the middle of opcode. For example

lda var1:#$00

var1 points to the $00 itself, it's the same as
var1 = *+1
lda #$00

It's awesome for labeling your registers, it's almost like having variables in C (especially that you can have local labels between {}) My typical irq handler hence looks like this:

irq1:
{
sta rega
stx regx
sty regy
...
inc $d019
lda rega:#$00
ldx regx:#$00
ldy regy:#$00
rti
}
2020-01-14 20:39
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: I use sei/cli when changing irq only in demo parts, for one files I don't think I ever had a situation where I wanted to change the irq when (or where when thinking raster position) an irq could fire (i.e I'm on a way different line)
btw. I do inc $d019, just muscle memory. But that trick with writing anything with bit 0 set is good, i'll remember that.

Here's my little tip, though only about the assembler itself. Kickasm supports labels even in the middle of opcode. For example

lda var1:#$00

var1 points to the $00 itself, it's the same as
var1 = *+1
lda #$00

It's awesome for labeling your registers, it's almost like having variables in C (especially that you can have local labels between {}) My typical irq handler hence looks like this:

irq1:
{
sta rega
stx regx
sty regy
...
inc $d019
lda rega:#$00
ldx regx:#$00
ldy regy:#$00
rti
}


Just beware that such handler is not re-entrant itself, not will it handle background-loading over I/O. At least, have that in mind when linking if "random" stuff occurs.
2020-01-14 23:23
Krill

Registered: Apr 2002
Posts: 2980
Here's another one: X-scrolling.

With the 38-columns mode (without open sideborders), the rightmost column is never visible, regardless of XSCROLL.

Thus, hardscroll handling can safely operate on 39 columns only and ignore the final one, saving cycles in copy loops and the like.

(Bonus: Compared to the 40-columns mode, the left border is collapsed by 7 pixels and the right border by 9 pixels. With XSCROLL = 7, only 38 columns are visible.)
2020-01-15 00:47
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: Here's another one: X-scrolling.

With the 38-columns mode (without open sideborders), the rightmost column is never visible, regardless of XSCROLL.

Thus, hardscroll handling can safely operate on 39 columns only and ignore the final one, saving cycles in copy loops and the like.

(Bonus: Compared to the 40-columns mode, the left border is collapsed by 7 pixels and the right border by 9 pixels. With XSCROLL = 7, only 38 columns are visible.)


Speaking of that. Did anyone ever give a proper explanation with the 7/9 division and not 8/8?
2020-01-15 07:47
Krill

Registered: Apr 2002
Posts: 2980
Quoting JackAsser
Speaking of that. Did anyone ever give a proper explanation with the 7/9 division and not 8/8?
Haven't come across one yet. My guess is that there are technical reasons, something with the internal workings of the sideborder latches vs the pixel clock vs the clocks derived from the pixel clock vs register updates.

One practical upside is that with the more common right-to-left scrolling, the second-to-rightmost column may only be updated one video frame later upon an XSCROLL wrap from 0 to 7 (at a scroll speed of 1 pixel per video frame).
2020-01-15 08:23
Golara
Account closed

Registered: Jan 2018
Posts: 212
Quote: Speaking of that. Did anyone ever give a proper explanation with the 7/9 division and not 8/8?

Yeah, top border is weird too, with the screen being fully visible only at Yscroll = 3
2020-01-15 08:33
Krill

Registered: Apr 2002
Posts: 2980
Quoting Golara
Yeah, top border is weird too, with the screen being fully visible only at Yscroll = 3
I guess that is to do with acceptable Y-centering of the screen, also it's 24 vs 25 char rows, so it makes sense to have the default somewhere at half a char row. Now, why the default is 3 and not 4... =)
2020-01-15 08:50
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: Quoting JackAsser
Speaking of that. Did anyone ever give a proper explanation with the 7/9 division and not 8/8?
Haven't come across one yet. My guess is that there are technical reasons, something with the internal workings of the sideborder latches vs the pixel clock vs the clocks derived from the pixel clock vs register updates.

One practical upside is that with the more common right-to-left scrolling, the second-to-rightmost column may only be updated one video frame later upon an XSCROLL wrap from 0 to 7 (at a scroll speed of 1 pixel per video frame).


Sounds reasonable that it's an effect of the pixel pipeline (that nobody described properly in text).
2020-01-15 08:54
Krill

Registered: Apr 2002
Posts: 2980
Quoting JackAsser
Sounds reasonable that it's an effect of the pixel pipeline (that nobody described properly in text).
Plus it's Commodore, so if there's a fraction of a penny to be saved, a fraction of a penny will be saved.
2020-01-15 09:47
Perplex

Registered: Feb 2009
Posts: 255
Quoting Krill
it's Commodore, so if there's a fraction of a penny to be saved, a fraction of a penny will be saved.


7/9 compared to 8/8, that's more than 22% saved!
2020-01-15 11:40
Krill

Registered: Apr 2002
Posts: 2980
Quoting oziphantom
lda vic reg
and value
ora value
sta vic reg

there are cases where this is needed, and it should be taught this way to hammer the point of setting a bit. But 99.8% of the time you can just do lda sta
I've often seen things like "LDA #$D8 : STA $D016" or similar. That is, setting the unconnected don't-care bits to one (because they are read like that).

I prefer not to set them ($18 in this case), unless i need the don't-care bits with specific values for re-use.
2020-01-15 12:18
Oswald

Registered: Apr 2002
Posts: 5094
pff any idea why I sometimes needed lda $dc0d ? iirc without that the raster irq behaved for me like never acknowledged (altho d019 ack was in) so its like the cia forces a timer interrupt endlessly.
2020-01-15 12:32
Laurent

Registered: Apr 2004
Posts: 40
Quote:
pff any idea why I sometimes needed lda $dc0d ? iirc without that the raster irq behaved for me like never acknowledged (altho d019 ack was in) so its like the cia forces a timer interrupt endlessly.


I believe if you started your init with SEI like it is often the case, and then only you disable the CIA, there is a risk that a CIA interrupt condition raised in between and is now pending. It will trigger the interrupt as soon as you do the CLI. Since you probably don't do the lda $dc0d in your ISR, it will retrigger indefinitely (?)
2020-01-15 13:49
Oswald

Registered: Apr 2002
Posts: 5094
That makes sense!
2020-01-15 14:25
Krill

Registered: Apr 2002
Posts: 2980
So again: no SEI, no need to ack any pending CIA interrupt, as there will be none after disabling them. =)
2020-01-15 17:28
oziphantom

Registered: Oct 2014
Posts: 490
I mean you expect a guy who counted the number of clocks in a line wrong, off by 1, is going to count the number of pixels a border is.. again off by 1..
2020-01-15 17:32
Frantic

Registered: Mar 2003
Posts: 1648
...and 7 * 9 = 63

I suspect a conspiracy going on here.
2020-01-15 17:43
Golara
Account closed

Registered: Jan 2018
Posts: 212
Quote: I mean you expect a guy who counted the number of clocks in a line wrong, off by 1, is going to count the number of pixels a border is.. again off by 1..

What are you talking about ? The 64 and 65 cycle NTSC machines ?
2020-01-15 20:42
tlr

Registered: Sep 2003
Posts: 1790
LDX #$FF
TXS
perhaps?

Most self contained code will run fine with _any_ initial stack pointer. :)

I do use that construct myself though as I like to keep things tidy.

The question could be interpreted on the edge of making the most minimal code that will still run correctly. I guess most interrupt setup uses SEI/CLI for the intent of making extra sure in some way.
And note that trying to reduce code to the optimal case will require a lot more testing to prove it works. For interrupts you need to test for race conditions. For the SP you'll need to fuzz-test your stack pointer.
2020-01-15 21:39
JackAsser

Registered: Jun 2002
Posts: 2014
Quote:
LDX #$FF
TXS
perhaps?

Most self contained code will run fine with _any_ initial stack pointer. :)

I do use that construct myself though as I like to keep things tidy.

The question could be interpreted on the edge of making the most minimal code that will still run correctly. I guess most interrupt setup uses SEI/CLI for the intent of making extra sure in some way.
And note that trying to reduce code to the optimal case will require a lot more testing to prove it works. For interrupts you need to test for race conditions. For the SP you'll need to fuzz-test your stack pointer.


The extra SEI when taking over the kernel IRQ forces you to do the bit $dc0d though. Leave both out.

Anyway, thanks for Krill's OP. Already employing it:

had:
lda #$00
sta $d017
dec $d019
pla
rti

But since I don't use sprite 0 anyway I changed to:
lda #$1
sta $d017
sta $d019
pla
rti

:D 2 cycles is 2 cycles!
2020-01-15 22:39
Krill

Registered: Apr 2002
Posts: 2980
Quoting tlr
Most self contained code will run fine with _any_ initial stack pointer. :)
Code using the KERNAL's interrupt handler breaks with very low stack pointers, as the BRK check code prior to jumping via $0314/5 is... not good.
http://unusedino.de/ec64/technical/aay/c64/romff48.htm

Quoting tlr
I guess most interrupt setup uses SEI/CLI for the intent of making extra sure in some way.
And note that trying to reduce code to the optimal case will require a lot more testing to prove it works.
I have yet to see a case where no SEI with proper interrupt disabling fails and SEI with all that ack stuff before CLI doesn't. =)
2020-01-16 05:57
oziphantom

Registered: Oct 2014
Posts: 490
Quoting Golara
What are you talking about ? The 64 and 65 cycle NTSC machines ?


Yes
2020-01-16 06:30
ChristopherJam

Registered: Aug 2004
Posts: 1409
While most code doesn't specifically require an initial SP of $ff, it's reasonably common to use some of stack space for data (*cough icc*), in which case you at least need to know *something* about the value you initialise it to.

Of course, that could often just as easily be $00 as $ff, or even some arbitrary value in the middle just so long as you know which half a dozen bytes your JSRs and IRQs will be trampling.
2020-01-16 10:13
Golara
Account closed

Registered: Jan 2018
Posts: 212
Quote: Quoting Golara
What are you talking about ? The 64 and 65 cycle NTSC machines ?


Yes


Is there any story behind it ? Is the first VIC problematic for some screens to display or something ? I knew there are 2 VICs in NTSC but never knew the reason
2020-01-16 11:04
oziphantom

Registered: Oct 2014
Posts: 490
Al stuffed up, counted the wrong number of cycles. The first C64s where thrown out the door.. then he noticed and hence fixed it so it worked properly ;) It was mentioned in one of the interview I'm pretty sure probably the one he did with Bob.
2020-01-16 13:34
Krill

Registered: Apr 2002
Posts: 2980
The problems of 64-cycle per line NTSC VIC-IIs and why 64 cycles happened in the first place are explained here https://spectrum.ieee.org/ns/pdfs/commodore64_mar1985.pdf on page 54 of IEEE Spectrum, March 1985 (page 7 in the PDF).

And yet, 64-cycle NTSC C-64s aren't that rare, as the fixed VIC-II revision was rolled out only about 5 months into production.
2020-01-16 18:20
Golara
Account closed

Registered: Jan 2018
Posts: 212
Quote: The problems of 64-cycle per line NTSC VIC-IIs and why 64 cycles happened in the first place are explained here https://spectrum.ieee.org/ns/pdfs/commodore64_mar1985.pdf on page 54 of IEEE Spectrum, March 1985 (page 7 in the PDF).

And yet, 64-cycle NTSC C-64s aren't that rare, as the fixed VIC-II revision was rolled out only about 5 months into production.


Cool, didn't know that. Only 5 months and you say it isn't that rare, interesting. The computer itself is not that rare maybe, but how many people use it today and have that as their only (or main) c64 ? Cuz I understand having that as a collector item, but not for "daily" use, as it also has that flicker
2020-01-16 19:03
Krill

Registered: Apr 2002
Posts: 2980
Quoting Golara
Cool, didn't know that. Only 5 months and you say it isn't that rare, interesting.
5 months worth of production is probably a lot of machines to be sold.

Quoting Golara
The computer itself is not that rare maybe, but how many people use it today and have that as their only (or main) c64 ? Cuz I understand having that as a collector item, but not for "daily" use
It's not worth supporting in new productions, if that's what you mean.

Quoting Golara
as it also has that flicker
What do you mean? The "sparkle" issue? That isn't related to VIC-II but the chargen ROM.
2020-01-16 19:17
Golara
Account closed

Registered: Jan 2018
Posts: 212
Quote: Quoting Golara
Cool, didn't know that. Only 5 months and you say it isn't that rare, interesting.
5 months worth of production is probably a lot of machines to be sold.

Quoting Golara
The computer itself is not that rare maybe, but how many people use it today and have that as their only (or main) c64 ? Cuz I understand having that as a collector item, but not for "daily" use
It's not worth supporting in new productions, if that's what you mean.

Quoting Golara
as it also has that flicker
What do you mean? The "sparkle" issue? That isn't related to VIC-II but the chargen ROM.


Yeah but both issues were fixed about the same time didn't it ?
2020-01-17 00:37
Krill

Registered: Apr 2002
Posts: 2980
Quoting Golara
Yeah but both issues were fixed about the same time didn't it ?
"About" is pretty relative.

Sparkle: "The problem was fixed before Charpentier left the company in September 1982. [...] Only the first few hundred thousand units shipped with the defect."

64 cycles: "The error, which was corrected sometime after Charpentier left the company [...]"

So, there seem to be quite a few 64-cycles C64s without the sparkle bug. At least i've never noticed that problem on my Canadian model. =)
2020-01-17 10:38
oziphantom

Registered: Oct 2014
Posts: 490
surely this is limited to Silver Label NTSC models. Couldn't be more than 500,000 of them. 1/36th of the units.
2020-02-08 22:22
Raz
Account closed

Registered: Aug 2003
Posts: 16
Really nice discussion thread here. I have to admit, I was not aware of the just set bit 0 for ACK of IRQs. I have always done ALS $d019 out of habit.

Reading through the comments, one thing I have realized, is that I have changed my coding style somewhat, after my long break from c64 coding (and coding a lot of scientific software in "real life"): Now I think a lot about readability vs. size/speed in non time critical parts of the code. I don't shy away from doing:
LDA vic_register
AND mask
ORA things_to_set
STA vic_register

If it's just some setup code, but I certainly just do LDA/STA if it matters (and I've, btw, started to do much more timer-critical code, which was otherwise never my strong side back in the day).

Cheers
-Raz
2020-02-10 06:24
ChristopherJam

Registered: Aug 2004
Posts: 1409
Here's another one.

"You can avoid the ADSR bug by using a specific register write order, independent of instrument design"

Um, just no. You can certainly avoid breaking many well behaved instruments by not deviating from the write order they were designed for, but if you're porting instruments from a routine that used a different ordering there's a fair chance you will break them by switching to your favourite.

The advice given just seems to work because
a) many routines already use the same order.
and
b) writing register 04 after register 05 at start of note avoids changing attack just after setting the gate bit, which can be an issue if the preceding note had a long attack and you don't want to stall a fast one. (A functional HR can also avoid this issue)


The important thing is not using a register write to switch from a slow (high register value) rate to a fast one, unless you desire or can tolerate a 33ms freeze.

It makes no difference whether the rate change comes from switching the value for the current envelope phase (eg changing attack after setting gate), or from toggling the gate bit (eg setting gate to change from decay rate to attack rate).
RefreshSubscribe to this thread:

You need to be logged in to post in the forum.

Search the forum:
Search   for   in  
All times are CET.
Search CSDb
Advanced
Users Online
Freeze/Blazon
Guests online: 108
Top Demos
1 Next Level  (9.7)
2 13:37  (9.7)
3 Mojo  (9.7)
4 Coma Light 13  (9.6)
5 Edge of Disgrace  (9.6)
6 What Is The Matrix 2  (9.6)
7 The Demo Coder  (9.6)
8 Uncensored  (9.6)
9 Comaland 100%  (9.6)
10 Wonderland XIV  (9.6)
Top onefile Demos
1 Layers  (9.6)
2 No Listen  (9.6)
3 Party Elk 2  (9.6)
4 Cubic Dream  (9.6)
5 Copper Booze  (9.6)
6 Rainbow Connection  (9.5)
7 Dawnfall V1.1  (9.5)
8 Onscreen 5k  (9.5)
9 Morph  (9.5)
10 Libertongo  (9.5)
Top Groups
1 Performers  (9.3)
2 Booze Design  (9.3)
3 Oxyron  (9.3)
4 Triad  (9.3)
5 Censor Design  (9.3)
Top Organizers
1 Burglar  (9.9)
2 Sixx  (9.8)
3 hedning  (9.7)
4 Irata  (9.7)
5 Tim  (9.7)

Home - Disclaimer
Copyright © No Name 2001-2024
Page generated in: 0.104 sec.