It is currently Tue Mar 26, 2019 12:25 pm

All times are UTC - 7 hours

Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Mon Dec 05, 2016 9:48 am 
Formerly WheelInventor
User avatar

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1952
Location: Gothenburg, Sweden
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:

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:
.macro IF_EQ
     BNE $                        
    .include  "STAKPUSH.ASM"       
   to_push_1 = $              

.macro ELSE_
    JMP  $
   cur_adr = $
    .org stk_lvl_1 - 1
    .byte cur_adr-stk_lvl_1
    .org cur_adr
   stk_lvl_1 = $
.macro END_IF 
   cur_adr = $
    .org  stk_lvl_1 - 1 
    .byte  cur_adr-stk_lvl_1
    .include "STACKPOP.ASM" 
    .org  cur_adr 

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.

_________________ - personal NES blog

PostPosted: Mon Dec 05, 2016 10:00 am 
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2468
Location: DIGDUG

Would be an infinite loop that crashes a program.

_________________ -- blog/tutorial on programming for the NES

PostPosted: Mon Dec 05, 2016 10:08 am 
Formerly WheelInventor
User avatar

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1952
Location: Gothenburg, Sweden
Thanks for pointing that out

.org stk_lvl_1 - 1
.byte cur_adr-stk_lvl_1
is supposed to overwrite FE at assembly-time with the right operand

_________________ - personal NES blog

PostPosted: Mon Dec 05, 2016 12:20 pm 

Joined: Wed Nov 30, 2016 4:45 pm
Posts: 131
Location: Southern California
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.

_________________ lots of 6502 resources

PostPosted: Mon Dec 05, 2016 5:33 pm 
Formerly WheelInventor
User avatar

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1952
Location: Gothenburg, Sweden
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.

E4 2D                     CPX goal
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

                  stk_lvl_1 = $               ; Put the addr of that operand in that top-of-stack cell.
                        else_flag = 0                 
CA                           dex
                                                    ; 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
                               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   
                                  .byte  cur_adr - stk_lvl_1 ;this is for filling in the Bxx operand in the if-clause.
                               .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.


_________________ - personal NES blog

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

All times are UTC - 7 hours

Who is online

Users browsing this forum: No registered users and 2 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:  
Powered by phpBB® Forum Software © phpBB Group