trouble with JMP and program counter.

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2033
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

trouble with JMP and program counter.

Post by FrankenGraphics »

So i'm doing this exercise making Garth wilson's Structured Programming Macros NMOS 6502 and Asm6 compatible.

Here's some output from a simple test code running a few macros:

Code: Select all

E4 2D          CPX goal 
D0 FE          IF_EQ
CA             DEX

00             ELSE_
*** Value out of range.
E8             INX
                                	 
01             END_IF
And these are the relevant macros:

Code: Select all

.macro IF_EQ
     BNE $	 	 	 				 
    .include  "STAKPUSH.ASM"		  
	to_push_1 = $	            
.endm			                  
 ;----------------

.macro ELSE_ 
    JMP  $
	cur_adr = $
    .org stk_lvl_1 - 1
    .byte cur_adr-stk_lvl_1
    .org cur_adr
	stk_lvl_1 = $
.endm 
    
.macro END_IF  
	cur_adr = $
    .org  stk_lvl_1 - 1  
    .byte  cur_adr-stk_lvl_1
    .include "STACKPOP.ASM"  
    .org  cur_adr  
.endm
Stakpush.asm etc are simple lists of redefines, like so.

My reasoning so far is that since JMP is absolute, $ isn't what it is expecting. I'm just too green to figure out what to put there instead. Oh, and naturally, BVS would get the job done w/o further modification as long as its condition is known but i want it more versatile and stable than that, for reasons pointed out here.
http://www.frankengraphics.com - personal NES blog
User avatar
dougeff
Posts: 2875
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: trouble with JMP and program counter.

Post by dougeff »

D0 FE

Would be an infinite loop that crashes a program.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2033
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: trouble with JMP and program counter.

Post by FrankenGraphics »

Thanks for pointing that out

Code: Select all

.org stk_lvl_1 - 1
.byte cur_adr-stk_lvl_1
is supposed to overwrite FE at assembly-time with the right operand
http://www.frankengraphics.com - personal NES blog
Garth
Posts: 215
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: trouble with JMP and program counter.

Post by Garth »

I answered your PM before seeing this topic. Feel free to repeat it here if you like. I suppose some readers will be fascinated by the internal details as we figure it out, and others won't be until there's there's something finished and tested. Are you using the ams6 assembler? (I think that's what it's called. I'm new on this forum and have seen references to something like that but I'm not familiar with it.)

For me, having the structure macros really made assembly language far less tedious and more versatile. Suddenly I was more productive, got fewer bugs, could see what I was doing more easily (which is a huge help later when it comes time to go back and check something), and in most cases, there is absolutely no penalty in speed or memory taken on the target computer, because the macros are laying down the same code you would do by hand, but they're more readable and intuitive.
http://WilsonMinesCo.com/ lots of 6502 resources
User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2033
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: trouble with JMP and program counter.

Post by FrankenGraphics »

Here's the relevant verbose listing for its current state, sans definitions. They're there, way above.
Line 38: .byte cur_adr - stk_lvl_1 lays down a #$01 byte right there, despite the previous .base statement. I've also tried .org, that doesn't work. It's like the variables are treated as local, even though they are defined outside and before the macros, or that .base can't reach outside the macro. I'm just guessing at worst case scenarios here, really, for lack of better insight.

Code: Select all

E4 2D                     CPX goal 
                           
                          IF_EQ
D0 FE                          BNE $								; Lay down the BNE op code plus a placekeeper for the operand,	 
                         	    .include  "STAKPUSH.ASM"		; and push a cell on the stack.
									;.enum $					; --commented out as to get full view of the output
									stk_lvl_20  =  stk_lvl_19
									stk_lvl_19  =  stk_lvl_18
									stk_lvl_18  =  stk_lvl_17
									stk_lvl_17  =  stk_lvl_16
									stk_lvl_16  =  stk_lvl_15  
									stk_lvl_15  =  stk_lvl_14  
									stk_lvl_14  =  stk_lvl_13  
									stk_lvl_13  =  stk_lvl_12  
									stk_lvl_12  =  stk_lvl_11  
									stk_lvl_11  =  stk_lvl_10  
									stk_lvl_10  =  stk_lvl_9
									stk_lvl_9   =  stk_lvl_8
									stk_lvl_8   =  stk_lvl_7
									stk_lvl_7   =  stk_lvl_6
									stk_lvl_6   =  stk_lvl_5
									stk_lvl_5   =  stk_lvl_4
									stk_lvl_4   =  stk_lvl_3
									stk_lvl_3   =  stk_lvl_2
									stk_lvl_2   =  stk_lvl_1
                                                 
									;.ende    

						stk_lvl_1 = $					; Put the addr of that operand in that top-of-stack cell. 
                        else_flag = 0		        	 
                        								   
CA                        	dex
							ELSE_
                            								; Lay down the VBS (should be JMP in final; originally BRA) op code plus a placekeeper.
                            	cur_adr = $                 ; Keep the current address to restore below. 
                                $ = stk_lvl_1 - 1     		; Go back to the place for the .if's Bxx's operand, to fill it in now.
  01                            .byte cur_adr - stk_lvl_1 	; Let's use .byte because most assemblers seem to recognise it 
                                $ = cur_adr           		; Now after completing .if's Bxx's operand, come back to resume assembling new code.
  70 FE                     	BVS  $  					; --TEMPORARY!! should be JMP in the final version, but for now... 
                            	stk_lvl_1 = $  				; reusing the same stack level. If the program had worked it'd been free from duty by now. 
                            	else_flag = 1				; Set flag for END_IF to know if it should fill in a one-byte BNE operand or two-byte JMP 
  E8                        	inx
                                	 
                            END_IF
                            	cur_adr = $ 				; Save the current address, needed because we're going back a bit!
                            	$ = stk_lvl_1 - 1			;go back to previous macro's branch (or jump) to prepare for overwriting the placeholder.
                            	.if else_flag == 1
                            		;.dl stk_lvl_1  		;--this is for filling in the JMP (now BVS) operand. Probably doesn't work like this. 
                            		;.dh stk_lvl_1 			;--but that's a later problem. Current problem: get it in place. 
  01                        		.byte cur_adr - stk_lvl_1 ;-- and this is for the BVS surrogate..
                          		 
                            		else_flag = 0	
                            	.else 
                            		.byte  cur_adr - stk_lvl_1 ;this is for filling in the Bxx operand in the if-clause. 
                            	.endif 
                            	.include "STACKPOP.ASM"    ; We're done with this stack level now, so we can drop it.
                                
									stk_lvl_1   =  stk_lvl_2   ; As with the push list, but in reverse
									stk_lvl_2   =  stk_lvl_3    
									stk_lvl_3   =  stk_lvl_4    
									stk_lvl_4   =  stk_lvl_5    
									stk_lvl_5   =  stk_lvl_6   
									stk_lvl_6   =  stk_lvl_7   
									stk_lvl_7   =  stk_lvl_8
									stk_lvl_8   =  stk_lvl_9
									stk_lvl_9   =  stk_lvl_10
									stk_lvl_10  =  stk_lvl_11
									stk_lvl_11  =  stk_lvl_12
									stk_lvl_12  =  stk_lvl_13
									stk_lvl_13  =  stk_lvl_14
									stk_lvl_14  =  stk_lvl_15
									stk_lvl_15  =  stk_lvl_16
									stk_lvl_16  =  stk_lvl_17
									stk_lvl_17  =  stk_lvl_18
									stk_lvl_18  =  stk_lvl_19
									stk_lvl_19  =  stk_lvl_20
                                
								$ = cur_adr ; restore program counter 
And here's Garth:s PM reply
It looks like certain things are working right, like the first $, so BNE's operand was put in the right place, but it's just the wrong value. It has the BNE branching back to the BNE itself. But for the next $, the JMP should have worked, unless your assembler uses $ differently from what I've seen in others. Or, maybe the JMP was originally laid down correctly, but the .byte in END_IF somehow came up with something that couldn't fit in 16 bits, like it if uses a 32-bit number and it was negative, and was also trying to overwrite the JMP op code itself rather than the operand. It might be time to consult its manual. Can you view the relevant part of the list (*.lst) file and include it in the next message? That will tell a lot more of what went on internally. Without it, I think we'll just be guessing.

It looks like if you have the NMOS 6502 without the BRA instruction, END_IF will have to be different for using the ELSE versus not using ELSE. This is because with only IF, END_IF fills in a one-byte relative-branch operand, wheres with the ELSE, it needs to fill in a two-byte absolute JMP operand. They're different. (With CMOS, it was the single-byte relative-branch operand both ways.) One way to have it do it would be to have a flag variable in the assembler (maybe call it ELSE_FLAG) which any IF clears, and ELSE sets, and then the END_IF uses that flag to conditionally assemble the right thing, whether for the JMP in ELSE, or the B__ in IF. The pain about that though is that then you have to stack that too, otherwise you can't have nested IF structures! (Myself, I think I would still do it. Using a separate stack for ELSE_FLAGs might make it easier; but I haven't tried it yet to say for sure.) An easier way up front, but which would lead to more errors later is to have a separate END_IF, like maybe follow it with an _ like END_IF_, and then if you've used and ELSE, use END_IF_ instead of END_IF. You can see that using the wrong one would lead to bugs, like if you start with IF...END_IF, then later decide to insert an ELSE and forget to change to END_IF_, or just use the wrong one the first time.

The END_IF shouldn't lay down any new code at the end, only fix the operand of ELSE's JMP, or, if you don't have the ELSE, fix the operand of the IF's B__.
To which i replied:
Unfortunately, it seems the first BNEs operand wasn't put in place at all - even if i comment out the rest, that's the value i get, meaning that must be the placeholder's value (which makes sense since it is branching back on itself).

I've modified the code some more, including slight reordering, changing .org for .base, letting go of the to_push variable since asm6 doesn't seem to have that bug. Also, it seems to accept nested macros, which may open to some possibilities, but i think the includes are tidy enough for now. Either way, the output is the same.

(...)
http://www.frankengraphics.com - personal NES blog
Post Reply