Page 1 of 2

My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 6:45 am
by olof-a
Yesterday night I was trying to write a subroutine to set the registers of an MC6845. I haven't spent much time at all working on software for the 65C02, but I have written
subroutines in the past and I have the feeling that I shouldn't be having so much trouble with such a simple thing.
My first suspect was that my 1024*4-bit SRAM chips are faulty. I tried writing $FF and $00 to $0100 and $0101 (where the stack pointer should be after reset?) And then
reading those bytes back and outputting them onto port B of my PIA. I got no bits set/not set when they shouldn't have been.
I know that the MC6845's registers get set properly. With the following program:

Code: Select all

PIA_PORT_A = $b000
PIA_CONTR_REG_A = $b001
PIA_PORT_B = $b002
PIA_CONTR_REG_B = $b003
CRTC_ADDR_REG = $a000
CRTC_DATA_REG = $a001

reset:
  lda #$00              ;
  sta PIA_CONTR_REG_A   ; Select data direction of port A
  sta PIA_CONTR_REG_B   ; and of port B
  lda #$ff              ;
  sta PIA_PORT_A        ; Set data direction to all outputs
  sta PIA_PORT_B        ;

  lda #$04              ;
  sta PIA_CONTR_REG_A   ; Select I/O register of port A
  lda #$24              ;
  sta PIA_CONTR_REG_B   ; and of port B???
  lda #$55
  sta $0300
  lda $0300
  sta PIA_PORT_B

  lda #0
  sta CRTC_ADDR_REG
  lda #63
  sta CRTC_DATA_REG
  lda #1
  sta CRTC_ADDR_REG
  lda #40
  sta CRTC_DATA_REG
  lda #2
  sta CRTC_ADDR_REG
  lda #50
  sta CRTC_DATA_REG
  lda #3
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  lda #4
  sta CRTC_ADDR_REG
  lda #32
  sta CRTC_DATA_REG
  lda #5
  sta CRTC_ADDR_REG
  lda #16
  sta CRTC_DATA_REG
  lda #6
  sta CRTC_ADDR_REG
  lda #25
  sta CRTC_DATA_REG
  lda #7
  sta CRTC_ADDR_REG
  lda #29
  sta CRTC_DATA_REG
  lda #9
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
I get the correct sync frequencies from the MC6845. However, moving those beautifully repetitive LDAs and STAs into a subroutine will neither set the 6845's timings
correctly or let the rest of the program run:

Code: Select all

reset:
  jsr init_crtc

  lda #$00              ;
  sta PIA_CONTR_REG_A   ; Select data direction of port A
  sta PIA_CONTR_REG_B   ; and of port B
  lda #$ff              ;
  sta PIA_PORT_A        ; Set data direction to all outputs
  sta PIA_PORT_B        ;

  lda #$04              ;
  sta PIA_CONTR_REG_A   ; Select I/O register of port A
  lda #$24              ;
  sta PIA_CONTR_REG_B   ; and of port B???
  lda #$55
  sta $0300
  lda $0300
  sta PIA_PORT_B



loop:
  jmp loop



init_crtc:
  lda #0
  sta CRTC_ADDR_REG
  lda #63
  sta CRTC_DATA_REG
  lda #1
  sta CRTC_ADDR_REG
  lda #40
  sta CRTC_DATA_REG
  lda #2
  sta CRTC_ADDR_REG
  lda #50
  sta CRTC_DATA_REG
  lda #3
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  lda #4
  sta CRTC_ADDR_REG
  lda #32
  sta CRTC_DATA_REG
  lda #5
  sta CRTC_ADDR_REG
  lda #16
  sta CRTC_DATA_REG
  lda #6
  sta CRTC_ADDR_REG
  lda #25
  sta CRTC_DATA_REG
  lda #7
  sta CRTC_ADDR_REG
  lda #29
  sta CRTC_DATA_REG
  lda #9
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  rts
The 6845 doesn't get configured and the PIA doesn't output $55.

I'm stumped.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 7:47 am
by barnacle
Might we perhaps see the listing for the compiled code? I note that there is neither an .org statement in what you've shown, nor a stack setting instruction. I wonder if your code is being assembled in such a way that its thumping the stack?

Neil

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 10:24 am
by BigDumbDinosaur
olof-a wrote:
Yesterday night I was trying to write a subroutine to set the registers of an MC6845...My first suspect was that my 1024*4-bit SRAM chips are faulty.

Unlikely, but why did you use 1K × 4 SRAMs in an eight-bit system?  Eight-bit SRAM is readily available in a wide range of sizes.

Quote:
I tried writing $FF and $00 to $0100 and $0101 (where the stack pointer should be after reset?)

Further to what Neil said, if you very carefully :D read the 65C02 data sheet, you will realize that following a hard reset, the state of the stack pointer (SP, referred to as S in the data sheet) is not defined.  In fact, SP could be anything from $00 to $FF.  This is definitely an example of what happens when you assume things.

As a fairly general rule, code such as the following should be executed in your reset handler to establish an environment:

Code: Select all

hrst      = *                  ;start of reset handler
          sei                  ;redundant, but recommended
          cld                  ;likewise in the 65C02
          ldx #$ff             ;set top...
          txs                  ;of stack

          ... do reset things, e.g., configure I/O hardware ...

          cli                  ;allow IRQs

          ...continue with bringing up system...

The purpose of the redundant SEI and CLD instructions is to handle the case where the reset routine is entered from software with IRQs enabled and/or decimal arithmetic selected, e.g., with JMP ($FFFC).  It is unwise to make any assumptions about the system’s state when that happens.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 11:01 am
by BigEd
Instead of testing with 00 and FF it might be worth trying with AA and 55 as a partial check for shorted days lines. Having said which, not sure if that’s a great hypothesis.

More importantly, check that 01ff and 01fe can take different values at the same time.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 11:52 am
by olof-a
Quote:
Might we perhaps see the listing for the compiled code? I note that there is neither an .org statement in what you've shown, nor a stack setting instruction.
I do have orgs in the "full" assembly. A tiny bit foolish not to include them but I can say for sure that the program starts at the correct address of the ROM ($4000) and that
the reset vector points to the correct address in the 65C02's memory space ($c000.)
Quote:
Unlikely, but why did you use 1K × 4 SRAMs in an eight-bit system? Eight-bit SRAM is readily available in a wide range of sizes.
For a few reasons. I first of all had them lying around and I wanted to put them to use. Second, this project is built using 100x160mm perfboards. 2 4-bit chips are more
space-efficient, atleast with how I've layed out the ICs. Other than that, the video board which I am currently working on will have an 8Kb SRAM, with 1000 characters per
screen, this will effectively be a 7192-byte extension. It won't be contiguous, though.
Two nibbles make a byte, of course.


When I have the opportunity I'll experiment with the advice given here. I should be back by around 17:00 or so.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 2:44 pm
by BigDumbDinosaur
BigEd wrote:
Instead of testing with 00 and FF it might be worth trying with AA and 55 as a partial check for shorted days lines. Having said which, not sure if that’s a great hypothesis.

More importantly, check that 01ff and 01fe can take different values at the same time.

Further to Ed’s advice, you need to perform some sort of memory test on both zero page and the stack, which are the two “must work” areas in RAM for a 65C02.  Writing $00 and $FF to one or two addresses doesn’t really prove much.  You could have one or more bad address bus lines and still get a successful comparison.

Using the $AA and $55 pattern suggested by Ed, write each location, waste a little time (e.g., some NOPs in a loop) and then compare what you wrote to what’s in RAM.  If a failed comparison occurs, you have a hardware issue.  Perform zero page and stack memory tests with linear code (no subroutines) and IRQs masked.  Once you have qualified those areas, you can continue with reset.

For a thorough memory test, you can also use $5A and $A5 after initial testing with the $AA and $55 patterns, and follow those tests with a “walking bit” test.  The latter test will blow up if there are any misbehaving address and/or data bus lines.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 3:40 pm
by olof-a
Quote:
Might we perhaps see the listing for the compiled code? I note that there is neither an .org statement in what you've shown, nor a stack setting instruction. I wonder if your code is being assembled in such a way that its thumping the stack?
Here's both the full assembly program as well as a hexdump.

Code: Select all

; $0000 - $03ff On-board RAM
; $8000 - $9fff Video RAM
; $a000 - $afff CRTC
; $b000 - $bfff PIA
; $c000 - $ffff ROM

ROM_START_ADDR  = $c000 ; ROM bottom address in 6502 address space
PIA_PORT_A      = $b000
PIA_CONTR_REG_A = $b001
PIA_PORT_B      = $b002
PIA_CONTR_REG_B = $b003
CRTC_ADDR_REG   = $a000
CRTC_DATA_REG   = $a001

  .org $0000            ; Pad to correct size for a 32kB ROM
  .byte $00
  .org $4000            ; Program location in ROM
reset:
  sei
  cld

  ldx #$ff
  txs

  lda #$00              ;
  sta PIA_CONTR_REG_A   ; Select data direction of port A
  sta PIA_CONTR_REG_B   ; and of port B
  lda #$ff              ;
  sta PIA_PORT_A        ; Set data direction to all outputs
  sta PIA_PORT_B        ;

  lda #$04              ;
  sta PIA_CONTR_REG_A   ; Select I/O of port A
  lda #$24              ;
  sta PIA_CONTR_REG_B   ; and of port B???

  jsr init_crtc

  lda #$55
  sta $0300
  lda $0300
  sta PIA_PORT_B

  cli



loop:
  jmp loop



init_crtc:
  lda #0
  sta CRTC_ADDR_REG
  lda #63
  sta CRTC_DATA_REG
  lda #1
  sta CRTC_ADDR_REG
  lda #40
  sta CRTC_DATA_REG
  lda #2
  sta CRTC_ADDR_REG
  lda #50
  sta CRTC_DATA_REG
  lda #3
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  lda #4
  sta CRTC_ADDR_REG
  lda #32
  sta CRTC_DATA_REG
  lda #5
  sta CRTC_ADDR_REG
  lda #16
  sta CRTC_DATA_REG
  lda #6
  sta CRTC_ADDR_REG
  lda #25
  sta CRTC_DATA_REG
  lda #7
  sta CRTC_ADDR_REG
  lda #29
  sta CRTC_DATA_REG
  lda #9
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  ;jmp init_crtc
  rts



  .org $7ffc
  .word ROM_START_ADDR
  .word $0000

Code: Select all

0000000  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
*
0004000  78  d8  a2  ff  9a  a9  00  8d  01  b0  8d  03  b0  a9  ff  8d
0004010  00  b0  8d  02  b0  a9  04  8d  01  b0  a9  24  8d  03  b0  20
0004020  31  40  a9  55  8d  00  03  ad  00  03  8d  02  b0  58  4c  2e
0004030  40  a9  00  8d  00  a0  a9  3f  8d  01  a0  a9  01  8d  00  a0    <-- Subroutine starts at $4031, if I'm not mistaken
0004040  a9  28  8d  01  a0  a9  02  8d  00  a0  a9  32  8d  01  a0  a9
0004050  03  8d  00  a0  a9  08  8d  01  a0  a9  04  8d  00  a0  a9  20
0004060  8d  01  a0  a9  05  8d  00  a0  a9  10  8d  01  a0  a9  06  8d
0004070  00  a0  a9  19  8d  01  a0  a9  07  8d  00  a0  a9  1d  8d  01
0004080  a0  a9  09  8d  00  a0  a9  08  8d  01  a0  60  00  00  00  00
0004090  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
*
0007ff0  00  00  00  00  00  00  00  00  00  00  00  00  00  c0  00  00
0008000
I want to very quickly point out that the first problem at hand here isn't faulty RAM. That would of course mess with returning from the subroutine,
except the subroutine doesn't seem to run at all.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 4:28 pm
by barnacle
You may be confusing things with your addressing (you're confusing me!)...

If you have a rom which by reason of hardware lives at $c000, you would normally assemble the code with the absolute addresses from $c000 and up.

Code: Select all

    .org $c000
...
    stuff
...
   .org $fffa
vectors:
    dw nmi_vec
    dw res_vec
    dw irq_vec
This ensures that the code builds with, and the hex file contains, absolute addresses in the place you want them. When you write the hex to the (e(e))prom, you apply an offset so that the hex address $C000 goes to the start of the prom (as viewed from the processor's address space). For my own systems, I generally use the bottom half of a 32k prom to start at $c000 - by tying the A14 line low (except when I forget!) and my programmer is told to subtract $c000 from all the hex records' address fields to give a correct target address.

I'm wondering if you've shot yourself in the foot somehow here?

Neil

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 5:40 pm
by olof-a

Code: Select all

$0000 - $03ff On-board RAM
; $8000 - $9fff Video RAM
; $a000 - $afff CRTC
; $b000 - $bfff PIA
; $c000 - $ffff ROM

PIA_PORT_A      = $b000
PIA_CONTR_REG_A = $b001
PIA_PORT_B      = $b002
PIA_CONTR_REG_B = $b003
CRTC_ADDR_REG   = $a000
CRTC_DATA_REG   = $a001

  .org $8000
  .org $c000
reset:
  sei
  cld

  ldx #$ff
  txs

  lda #$00              ;
  sta PIA_CONTR_REG_A   ; Select data direction of port A
  sta PIA_CONTR_REG_B   ; and of port B
  lda #$ff              ;
  sta PIA_PORT_A        ; Set data direction to all outputs
  sta PIA_PORT_B        ;

  lda #$04              ;
  sta PIA_CONTR_REG_A   ; Select I/O of port A
  lda #$24              ;
  sta PIA_CONTR_REG_B   ; and of port B???

  jsr init_crtc

  lda #$55
  sta $0300
  lda $0300
  sta PIA_PORT_B

  cli



loop:
  jmp loop



init_crtc:
  lda #0
  sta CRTC_ADDR_REG
  lda #63
  sta CRTC_DATA_REG
  lda #1
  sta CRTC_ADDR_REG
  lda #40
  sta CRTC_DATA_REG
  lda #2
  sta CRTC_ADDR_REG
  lda #50
  sta CRTC_DATA_REG
  lda #3
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  lda #4
  sta CRTC_ADDR_REG
  lda #32
  sta CRTC_DATA_REG
  lda #5
  sta CRTC_ADDR_REG
  lda #16
  sta CRTC_DATA_REG
  lda #6
  sta CRTC_ADDR_REG
  lda #25
  sta CRTC_DATA_REG
  lda #7
  sta CRTC_ADDR_REG
  lda #29
  sta CRTC_DATA_REG
  lda #9
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  ;jmp init_crtc
  rts



  .org $fffc
  .word reset
  .word $0000
I was able to get the program shown above to assemble with the correct reset vector and all. I have been distrusting of .org in vasm due to a few earlier experiences. I want
to reiterate that the program shown above as well as in earlier posts do actually run, but no instructions in my subroutine seem to be run, and the subroutine doesn't return
either.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Fri Dec 05, 2025 8:39 pm
by leepivonka
.org is important! It tells the assembler where the following code is in the memory space.

If the assembler thinks the code you are JSRing to is at $802f it will code that absolute address into the instruction. When it executes, it'll jump to $802f & start executing data not intended to be instructions & probably crash the machine.

Can you tell vasm to generate a listing with the generated bytes listed? This sort of problem will be more obvious in that listing.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Sat Dec 06, 2025 4:06 pm
by fachat
olof-a wrote:

Code: Select all

$0000 - $03ff On-board RAM
; $8000 - $9fff Video RAM
; $a000 - $afff CRTC
; $b000 - $bfff PIA
; $c000 - $ffff ROM

PIA_PORT_A      = $b000
PIA_CONTR_REG_A = $b001
PIA_PORT_B      = $b002
PIA_CONTR_REG_B = $b003
CRTC_ADDR_REG   = $a000
CRTC_DATA_REG   = $a001

  .org $8000
  .org $c000
reset:
  sei
  cld

  ldx #$ff
  txs

  lda #$00              ;
  sta PIA_CONTR_REG_A   ; Select data direction of port A
  sta PIA_CONTR_REG_B   ; and of port B
  lda #$ff              ;
  sta PIA_PORT_A        ; Set data direction to all outputs
  sta PIA_PORT_B        ;

  lda #$04              ;
  sta PIA_CONTR_REG_A   ; Select I/O of port A
  lda #$24              ;
  sta PIA_CONTR_REG_B   ; and of port B???

  jsr init_crtc

  lda #$55
  sta $0300
  lda $0300
  sta PIA_PORT_B

  cli



loop:
  jmp loop



init_crtc:
  lda #0
  sta CRTC_ADDR_REG
  lda #63
  sta CRTC_DATA_REG
  lda #1
  sta CRTC_ADDR_REG
  lda #40
  sta CRTC_DATA_REG
  lda #2
  sta CRTC_ADDR_REG
  lda #50
  sta CRTC_DATA_REG
  lda #3
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  lda #4
  sta CRTC_ADDR_REG
  lda #32
  sta CRTC_DATA_REG
  lda #5
  sta CRTC_ADDR_REG
  lda #16
  sta CRTC_DATA_REG
  lda #6
  sta CRTC_ADDR_REG
  lda #25
  sta CRTC_DATA_REG
  lda #7
  sta CRTC_ADDR_REG
  lda #29
  sta CRTC_DATA_REG
  lda #9
  sta CRTC_ADDR_REG
  lda #8
  sta CRTC_DATA_REG
  ;jmp init_crtc
  rts



  .org $fffc
  .word reset
  .word $0000
I was able to get the program shown above to assemble with the correct reset vector and all. I have been distrusting of .org in vasm due to a few earlier experiences. I want
to reiterate that the program shown above as well as in earlier posts do actually run, but no instructions in my subroutine seem to be run, and the subroutine doesn't return
either.
Are you assembling in place? I don't assume so.

Are you burning this into a ROM? I would check the size of the output, if your assembler fills in the space between the end of the code and your .org $fffc. If not, your reset vectors are not where they should be at the end of the ROM but directly after your code

André

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Sat Dec 06, 2025 4:35 pm
by Paganini
Since you're using VASM, I suspect you come from a Ben Eater background. Do you have his arduino single-stepper available? Or, even better (but a heavier workload) would be to get and learn to use Hoglet's decoder. It's really cool (but you would have to buy some additional hardware).

Have you got a photo of your hardware you could share?

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Sat Dec 06, 2025 7:38 pm
by teamtempest
In addition to what Big Ed and BDD have already said, I'd have added that the JSR to the CRTC init code was placed before the init of the PIA in the first listing. If something in the init of the CRTC depended on the PIA being initialized first, then that wouldn't work.

Then I noticed that in subsequent listings the JSR had been moved to after the init of the PIA. So never mind about that.

At this point I'm a little curious about this bit of code:
Quote:
lda #7
sta CRTC_ADDR_REG
lda #29
sta CRTC_DATA_REG
lda #9
sta CRTC_ADDR_REG
lda #8
sta CRTC_DATA_REG
I don't know anything about this chip, but up to here CRTC_ADDR_REG has been loaded with the values zero to seven before placing some value into CRTC_DATA_REG. Why skip eight and jump to nine?

If this isn't a mistake and you really do mean to go from seven to nine, then perhaps another way to check progress would be to use some RAM location you can look at after running your program. Set it to to zero to start and then and increment it each time you reach some "check point" in your program. For instance, did your subroutine really run?

This is of course a really primitive form of PRINT debugging, but since my own screw-ups are rarely more complicated than this, it usually works for me.

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Sun Dec 07, 2025 8:11 am
by olof-a
I think I found out the issue. It was in the hardware, but not a fault in my wiring, a bad solder joint that had gotten loose, or in the design itself.

Part of the address decoding for the SRAM is a diode logic OR-gate. I thought that perhaps the slower fall/rise times of diode logic was causing
noisy output on the 74HC00 inverter which the OR-gate leads to, so I replaced the 74HC04 with a schmitt trigger 74HC14. That seems to have
caused the computer not to run at all, so I swapped the 74HC00 back in... And now it works!

Thanks for all the patience. :D

Re: My 65C02 can't jump to subroutines and I don't get why

Posted: Wed Dec 10, 2025 11:55 am
by jgharston
Once you've got the faulty joint working, this:
init_crtc:
lda #0
sta CRTC_ADDR_REG
lda #63
sta CRTC_DATA_REG
lda #1
sta CRTC_ADDR_REG
blah blah blah

should be:
LDX #0
.loop
LDA inittable,X
STX CRTC_ADDR
STA CRTC_DATA
INX
CPX #max
BNE loop
...
.inittable
defb foo,foo,foo,foo,foo,etc.