6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 12:06 pm

All times are UTC




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: assembler macros!
PostPosted: Wed Jun 16, 2010 1:32 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
At viewtopic.php?p=10745 , ElEctric_EyE wrote,
Quote:
I am curious too... I never got around to making macros, or a data table... 2 votes! Maybe a different thread to keep this one pure?

So here it is. Hopefully it will get a bunch of you into macros, even if you have been shying away from them because of that unsettling feeling of unfamiliarity that may have been shooing you away until now. When I was working on my first big (10,000+ lines) 6502 assembly-language project in the late 1980's, I showed it to a neighbor who was a programmer, and he introduced me to macros, showing me how I could make my software monstrosity a lot simpler and more readable by replacing various portions with macros. It turns out that he did me a huge favor.

Early on, I mentioned macros and gave a couple of examples in the "Tips" column, at viewtopic.php?p=2341#p2341 with a couple of cool (but small) examples.

Quote:
I never learned to make macros on the Kowalski assembler, am using jump tables. Do you know how I can do macros on that for a long jump as you mentioned?

I'm not familiar with the Kowalski assembler, but there seem to only be minor differences between assemblers, such that if you're familiar with one, you will probably be at least 90% literate in another. What I have for the C32 assembler from Universal Cross Assemblers for a BEQlong (since that one already came up) is:

Code:
BEQlong: MACRO    LBL
         BNE      bel1
         JMP      LBL
bel1:
         ENDM
 ;----------------------

then if you need a BEQ that's too long of a branch for the normal BEQ, use BEQlong:
Code:
         BEQlong  FOOBAR   ; If zero flag is set, jump to FOOBAR

using a label for an operand the same as you would use BEQ, and the macro assembles the BNE to get around the JMP instruction, like this:
Code:
         BNE      bel1
         JMP      FOOBAR
bel1:    <continue>

The resulting assembled code is exactly the same as you would do by hand, but the source code is shorter and more readable, making it faster to write, easier to maintain, and less error-prone. These traits becomes more and more important as the macros get bigger and replace more spaghetti with a single, more-intuitive line in the source code.

Quote:
I tried synthesizing the '816's branch long instruction in the Kowalski simulator thusly:

So far, I have not tried to make it test to see if a regular conditional branch will reach and substitute the longer version if necessary-- not that it wouldn't have value, but in most cases it's easy to see if it will reach or not.

Macros leave you in complete control and there is normally absolutely zero penalty in runtime speed or memory. The point is to keep all the control and performance of assembly while removing the need to keep seeing all the internal details after you have set them up. Raising the level of the language by taking maximum advantage of macros is a good thing.

I'll resist the temptation to get too long and complex for a starting post, but I will say I'm jazzed to have started successfully using macros for program structures despite my assemblers' lack of assembler variable arrays and pointers into those arrays that I mentioned in the topic, "desirable assembler features" at viewtopic.php?t=1475 . That lack makes nesting them a little more of a pain, but I found it can be done. Edit, 7/28/12: I've done the nesting of structure macros now, and have an article and source code here.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Jun 16, 2010 4:39 pm 
Offline

Joined: Mon Mar 02, 2009 7:27 pm
Posts: 3258
Location: NC, USA
As a total n00b to macro's, I will pose the question that comes first to my mind as an assembly language programmer. Conceptually, is it like a subroutine, except that the assembler inserts the routine so there is no JSR/RTS?

_________________
65Org16:https://github.com/ElEctric-EyE/verilog-6502


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Jun 16, 2010 6:35 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
ElEctric_EyE wrote:
... is it like a subroutine, except that the assembler inserts the routine so there is no JSR/RTS?


Yep - so if you use it 10 times you do get all those bytes ten times over. For a short macro like the long relative branch that's exactly what you wanted, of course. Also, because it happens in the source, you can put parameters on the invocation which are expanded with the macro. The docs explain it better than I can.

The ca65 assembler (part of the cc65 compiler) has some included macros including one which uses a branch if the distance is short.


Top
 Profile  
Reply with quote  
 Post subject: Re: assembler macros!
PostPosted: Wed Jun 16, 2010 7:29 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Quote:
Conceptually, is it like a subroutine, except that the assembler inserts the routine so there is no JSR/RTS?

Sort of. And since you can specify the parameters when you call the macro, it doesn't have to be the same every time like a subroutine would. The number of parameters the macro can use is practically unlimited as long as you can get them all on one line. Macros can do a lot of things that absolutely cannot be done with true subroutines.

Since the "subroutine" (to use your word) is run by the assembler instead of the final running program, there's a lot of pre-processing you could do in the macro, keeping the ugly details hidden except when you first define the macro. Macros must be defined before they are called. For other things like EQUates (constants), a two-pass assembler doesn't need to know them on the first pass. After it has finished the first pass and found everything, it will do the second pass and fix the operands it couldn't do right the first time. But if you call a macro before it is defined, the assembler may think the macro name is a label and then give you error messages about the parameter list.

For macros you will use over and over in different projects without modification (like the BEQlong), it might be good to have them in a separate file with a manageable name like 02MACROS1.ASM and have a line near the beginning of your project's assmebler code say INCL 02MACROS1.ASM. "INCL" is a common assembler directive to INCLude another file at that point, in essence like another kind of assembler subroutine. You can have several INCL lines in your source code. The assembler suspends its assembly of your main file at that point, goes out to the INCLuded file and goes through it, then resumes assembly in your main file where it had left off. Your assembler probably allows quite a few levels of INCLude nesting.

I don't want to overwhelm anyone with lots of long macros (actually my longest ones may not be of any use in your particular applications) but I'll add some more really short macros examples here that will be useful to anyone doing 65816 assembly. The REP and SEP '816 instructions are terribly cryptic. Why not use macros to make them a lot more user-friendly. It takes care of a complaint some here have had about the '816. Note that these macros don't need any parameters at all.
Code:
ACCUM_16: MACRO            ; Put accumulator into 16-bit mode.
          REP  #00100000B
          ENDM
 ;---------------

ACCUM_8:  MACRO            ; Put accumulator into 8-bit mode.
          SEP  #00100000B
          ENDM
 ;---------------

INDEX_16: MACRO            ; Put index registers into 16-bit mode.
          REP  #00010000B
          ENDM
 ;---------------

INDEX_8:  MACRO            ; Put index registers into 8-bit mode.
          SEP  #00010000B
          ENDM
 ;---------------

Each one lays down the appropriate two-byte instruction, with no runtime overhead.

Here's a use of them in my '816 Forth kernel which is nearly always kept with the accumulator in 16-bit mode and X & Y in 8-bit mode. C! (pronounced "see store") stores a character. The address to store it at is in the top cell of the data stack which resides in the direct page (like ZP, but the '816 does not confine it to page zero). The character to store is the low byte of the next cell down in the data stack.
Code:
        HEADER "C!", NOT_IMMEDIATE        ; ( byte  addr  --  )
Cstore: PRIMITIVE
        ACCUM_8
        LDA       2,X
        STA      (0,X)
        ACCUM_16
        JMP       POP2
 ;--------------

Here I sprang a couple of other macros on ya, but they are specific to an indirect-threaded code (ITC) Forth system. The HEADER macro, which gets called hundreds of times, is especially nice for cleaning up a big mess! Its first parameter is a string.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 17, 2010 4:49 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8509
Location: Midwestern USA
Here are macros I defined for the Kowalski simulator to implement most of the W65C816S-specific instructions.

Code:
;================================================================================
;
;W65C816S INSTRUCTION MACROS
;
;   BigDumbDinosaur -- Use at your own risk!
;
;   ---------------------------------------------------------
;   These macros implement 65C02 & many W65C816S instructions
;   that are not recognized by the Kowalski assembler.  Not
;   all 65C02 instructions are compatible with non-WDC parts.
;   ---------------------------------------------------------
;
brl      .macro .ad            ;long relative branch...
         .if .ad               ;won't work for forward...
.isize   =3                    ;branches because of forward...
.os      =*+.isize             ;address reference
.os      =.ad-.os
             .if .os > 32767
                 .error %1 + ": FORWARD BRANCH OUT OF RANGE"
             .endif
             .if .os < -32768
                 .error %1 + ": BACKWARD BRANCH OUT OF RANGE"
             .endif
             .byte $82
             .word .os
         .else
             .error "INSTRUCTION REQUIRES TARGET ADDRESS"
         .endif
         .endm
;
cop      .macro .op            ;co-processor
         .if .op > $ff
             .error "SIGNATURE MUST BE $00 - $FF"
         .endif
         .byte $02,.op
         .endm
;
jml      .macro .ad            ;JMP $bbahal (long JMP)
         .byte $dc
         .word .ad
         .byte .ad >> 16
         .endm
;
jsl      .macro .ad            ;JSL $bbahal (long JSR)
         .byte $22
         .word .ad
         .byte .ad >> 16
         .endm
;
jsx      .macro .ad            ;JSR (<addr>,X)
         .byte $fc
         .word .ad
         .endm
;
mvn      .macro .s,.d          ;move next <sbnk>,<dbnk>
         .if .s > $ff
             .error "SOURCE BANK MUST BE $00 - $FF"
         .endif
         .if .d > $ff
             .error "DESTINATION BANK MUST BE $00 - $FF"
         .endif
         .byte $54,.d,.s
         .endm
;
mvp      .macro .s,.d          ;move prev <sbnk>,<dbnk>
         .if .s > $ff
             .error "SOURCE BANK MUST BE $00 - $FF"
         .endif
         .if .d > $ff
             .error "DESTINATION BANK MUST BE $00 - $FF"
         .endif
         .byte $44,.d,.s
         .endm
;
pea      .macro .ad            ;pea <addr>
         .byte $f4
         .word .ad
         .endm
;
pei      .macro .ad            ;pei (<addr>)
         .if .ad > $ff
             .error "INDIRECT ADDRESS MUST BE $00 - $FF"
         .endif
         .byte $d4,.ad
         .endm
;
phb      .macro                ;push data bank register
         .byte $8b
         .endm
;
phd      .macro                ;push direct page register
         .byte $0b
         .endm
;
phk      .macro                ;push program bank register
         .byte $4b
         .endm
;
plb      .macro                ;pull data bank register
         .byte $ab
         .endm
;
pld      .macro                ;pull direct page register
         .byte $2b
         .endm
;
rep      .macro .op            ;clear status register bits
         .if .op > $ff
             .error "OPERAND MUST BE $00 - $FF"
         .endif
         .byte $c2,.op
         .endm
;
rtl      .macro                ;return long from subroutine
         .byte $6b
         .endm
;
sep      .macro .op            ;set status register bits
         .if .op > $ff
             .error "OPERAND MUST BE $00 - $FF"
         .endif
         .byte $e2,.op
         .endm
;
stp      .macro                ;halt MPU (WDC parts only)
         .byte $db
         .endm
;
tcd      .macro                ;transfer .C to direct page register
         .byte $5b
         .endm
;
tcs      .macro                ;transfer .C to stack pointer
         .byte $1b
         .endm
;
tdc      .macro                ;transfer direct page register to .C
         .byte $7b
         .endm
;
tsc      .macro                ;transfer stack pointer to .C
         .byte $3b
         .endm
;
txy      .macro                ;transfer .X to .Y
         .byte $9b
         .endm
;
tyx      .macro                ;transfer .Y to .X
         .byte $bb
         .endm
;
wai      .macro                ;wait for interrupt (WDC parts only)
         .byte $cb
         .endm
;
xba      .macro                ;swap B & A accumulators
         .byte $eb
         .endm
;
xce      .macro                ;swap carry & emulation bits
         .byte $fb
         .endm
;
;
;   synthesized stack-based accumulator instructions...
;
;   ---------------------------------------------------------------------
;   Stack-based accumulator instructions take the form ***S or ***SI, the
;   latter for indexed indirect operations.   *** represents  the  parent
;   instruction.  For example, LDAS 1 is equivalent to LDA 1,S &  LDASI 1
;   is the equivalent of LDA (1,S),Y.  The actual macro names  are  lower
;   case.  The macro comment indicates the official WDC assembly language
;   syntax for the instruction being synthesized.
;   ---------------------------------------------------------------------
;
adcs     .macro .of            ;ADC <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $63,.of
         .endm
;
adcsi    .macro .of            ;ADC (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $73,.of
         .endm
;
ands     .macro .of            ;AND <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $23,.of
         .endm
;
andsi    .macro .of            ;AND (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $33,.of
         .endm
;
cmps     .macro .of            ;CMP <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $c3,.of
         .endm
;
cmpsi    .macro .of            ;CMP (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $d3,.of
         .endm
;
eors     .macro .of            ;EOR <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $43,.of
         .endm
;
eorsi    .macro .of            ;EOR (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $53,.of
         .endm
;
ldas     .macro .of            ;LDA <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $a3,.of
         .endm
;
ldasi    .macro .of            ;LDA (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $b3,.of
         .endm
;
oras     .macro .of            ;ORA <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $03,.of
         .endm
;
orasi    .macro .of            ;ORA (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $13,.of
         .endm
;
sbcs     .macro .of            ;SBC <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $e3,.of
         .endm
;
sbcsi    .macro .of            ;SBC (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $f3,.of
         .endm
;
stas     .macro .of            ;STA <offset>,S
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $83,.of
         .endm
;
stasi    .macro .of            ;STA (<offset>,S),Y
         .if .of > $ff
             .error "OFFSET MUST BE $00 - $FF"
         .endif
         .byte $93,.of
         .endm
;
;
;   16 bit immediate mode instructions...
;
;   --------------------------------------------------------------------
;   Immediate mode instructions that are able to accept 16 bit  operands
;   take the form ***W, where *** is the parent 8 bit  instruction.  For
;   example, ADCW is the 16 bit form of ADC.  The actual macro names are
;   lower case.   It is the responsibility of the  programmer to  assure
;   that MPU register sizes have been correctly configured before  using
;   ***W instructions.  For example:
;
;      LONGA                 ;16 bit .A & memory
;      LDAW $1234            ;equivalent to LDA #$1234
;      SHORTA                ;8 bit .A & memory
;      LDAW $1234            ;won't work as expected!!!
;
;   The macro comment indicates the official WDC assembly language  syn-
;   tax for the instruction being synthesized.
;   --------------------------------------------------------------------
;
adcw     .macro .op            ;ADC #nnnn
         adc #<.op
         .byte >.op
         .endm
;
andw     .macro .op            ;AND #nnnn
         and #<.op
         .byte >.op
         .endm
;
bitw     .macro .op            ;BIT #nnnn
         bit #<.op
         .byte >.op
         .endm
;         
cmpw     .macro .op            ;CMP #nnnn
         cmp #<.op
         .byte >.op
         .endm
;
cpxw     .macro .op            ;CPX #nnnn
         cpx #<.op
         .byte >.op
         .endm
;
cpyw     .macro .op            ;CPY #nnnn
         cpy #<.op
         .byte >.op
         .endm
;
eorw     .macro .op            ;EOR #nnnn
         eor #<.op
         .byte >.op
         .endm
;
ldaw     .macro .op            ;LDA #nnnn
         lda #<.op
         .byte >.op
         .endm
;
ldxw     .macro .op            ;LDX #nnnn
         ldx #<.op
         .byte >.op
         .endm
;
ldyw     .macro .op            ;LDY #nnnn
         ldy #<.op
         .byte >.op
         .endm
;
oraw     .macro .op            ;ORA #nnnn
         ora #<.op
         .byte >.op
         .endm
;
sbcw     .macro .op            ;SBC #nnnn
         sbc #<.op
         .byte >.op
         .endm
;
;
;   register size macros...
;
;   --------------------------------------------------------------------
;   These macros are a convenient way to change the MPU's register sizes
;   without having to remember the correct bit pattern for the REP & SEP
;   instructions.
;   --------------------------------------------------------------------
;
longa    .macro                ;16 bit accumulator & memory
         rep $20
         .endm
;
longr    .macro                ;16 bit all registers
         rep $30
         .endm
;
longx    .macro                ;16 bit index registers
         rep $10
         .endm
;
shorta   .macro                ;8 bit accumulator & memory
         sep $20
         .endm
;
shortr   .macro                ;8 bit all registers
         sep $30
         .endm
;
shortx   .macro                ;8 bit index registers
         sep $10
         .endm
;
;
;   INT instruction - assembles as BRK followed by signature...
;
int      .macro .op            ;INT <intnum>
         .if .op > $ff
             .error "SIGNATURE MUST BE $00 - $FF"
         .endif
         .byte $00,.op
         .endm
   .end

EDIT: I updated this macro list with some new ones added since I originally posted this.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Thu Jun 07, 2012 5:18 am, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Jun 21, 2010 11:24 pm 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
GARTHWILSON wrote:
Quote:
Conceptually, is it like a subroutine, except that the assembler inserts the routine so there is no JSR/RTS?

Sort of. And since you can specify the parameters when you call the macro, it doesn't have to be the same every time like a subroutine would. The number of parameters the macro can use is practically unlimited as long as you can get them all on one line. Macros can do a lot of things that absolutely cannot be done with true subroutines.



I've never really understood the predilection to think of Macros as similar to subroutines.

Given the number of people I've encountered that complained about Macros exploding their code,
thinking of them as like subroutines probably ought to be actively discouraged.

I suppose if they resemble any thing it's an inlined function.

I'd guess that thinking of Macros as like subroutines comes from the syntactic similarities,
but to me, that's like equating string concatination with addition because they both used "+" as an operator.
Or maybe equating a function and a variable because they're used (syntactically and gramatically) in a similar fashion.

I always thought of Macros as more like a production (partly because when I first learned about them they
were simple substitutions, no parameters or "scripting", but also because they seemed to serve a similar purpose)


Top
 Profile  
Reply with quote  
 Post subject: Re: assembler macros!
PostPosted: Tue Jun 22, 2010 3:56 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Quote:
Given the number of people I've encountered that complained about Macros exploding their code,

probably because they were using a pre-written macro library instead of taking control themselves? (I don't think they would complain about their own work.)

Quote:
thinking of them as like subroutines probably ought to be actively discouraged.

It probably needs to be made clear that it's a subroutine for the assembler to run, not the target processor. During the assmebly process, the assembler can follow a list of instructions and make decisions about how to assemble something, based on the conditions you give it-- but it's all pre-defined by you, the programmer. It can make substitutions, recursions, calculations of operands, etc., and the macro can be very complex without necessarily laying down very many bytes of machine language. Once you work out the macro, the ugly internal details can hide under the hood, accessible but out of sight. Programming errors are reduced, productivity is increased, and source code becomes clearer and easier to maintain.

Rather than write a long initial post, I wanted to just introduce the idea and then bring in more material as issues and questions were raised. It's kind of hard to know what examples would be best when everyone's applications are different and the macros that get me most excited might seem foreign and meaningless to others. I would like to put the structure macros in the source code repository after I have them working and proven. Edit, 7/28/12: I put it on my website, here. (I've done some for PICs but have not been working on 65xx recently.) Something happened to my boss and he's been fighting with everyone in sight in the last couple of weeks, and I have been tempted to bring my 25 years in my industry to an end. If I'm unemployed for awhile, it would give me time to work on some projects that have been on the backburner for many years.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Jun 22, 2010 2:47 pm 
Offline

Joined: Fri Jun 27, 2003 8:12 am
Posts: 618
Location: Meadowbrook
a possible macro use I am hoping for eventually :)

Code:
; Step 4: shut down the matrix
; lites = f0 matrix = 9 so the shutdown is #$f9
   LDA #$40   ; lite/matrix 374
   STA Via_ORB_IRB
   LDA #$F9   ; no matrix or lights
   STA Via_ORA_IRA
   JSR KickCA2
   
; Step 5: load display data to the 374
   LDA #$00   ; display 374
   STA Via_ORB_IRB
   LDA TempHardwareDisplay
   STA Via_ORA_IRA
   JSR KickCA2




the Kick /CA2 does a hardware enable line and times it to allow the chip to respond. that to me is a prime macro candidate.

Garth: if anything happens, email me, definitely doing a head hunting gig right now...

_________________
"My biggest dream in life? Building black plywood Habitrails"


Top
 Profile  
Reply with quote  
 Post subject: Re: assembler macros!
PostPosted: Tue Jun 22, 2010 4:30 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Tony, thanks for the offer.

If your routine there is used in various places in your code and the instructions and operands are always the same, it would normally just be made a subroutine unless the extra JSR/RTS fouled up your cycle count (which I doubt). Even that however (ie, the count of the added cycles from making it a subroutine) could be reduced by replacing the final JSR KickCA2 with JMP KickCA2, so the RTS at the end of KickCA2 brings you all the way back to the calling routine in a single hop.

A scenario where it might be appropriate to make it a macro might be if the operands of the LDA's keep changing but you want the LDA's to remain immediate for best execution speed. Then the operands could go in the parameter list (on the same line with the macro call), or you could have the assembler choose them based on an appropriately named EQUate you use as a parameter, or on conditions it encounters at that point in the assembly.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Jun 22, 2010 5:13 pm 
Offline

Joined: Fri Jun 27, 2003 8:12 am
Posts: 618
Location: Meadowbrook
I am not cycle counting in the routine. I think I use that routine maybe 90 or so times in each loop iteration which runs either 30 or 60 Hz. It simply seemed to make more sense to subroutine it since it has a tiny delay loop to make sure the TTL responds correctly to the command. Some parts do indeed simply change parameters as I step through 8 enable lines with changing parameters. Heck, let me show you the entire routine for each step:

Code:

Matrix0
; step 1: define matrix number and load up the vars
   LDA #$00   ; matrix = 0
   STA MatrixNumber
   
; Step 2: load up the lights temp byte
   LDA LightHardware1   ; Game over light
   STA TempLight0
   LDA LightHardware2   ; Bonus 8k light
   STA TempLight1
   LDA LightHardware3   ; Bonus 9k light
   STA TempLight2
   LDA LightHardware4   ; R extra ball when lit
   STA TempLight3
   JSR PackLightBits
   STA TempHardwareLights

; Step 3: load up the display temp byte
   LDX ScoreDisplayHardwareDigit6
   LDA ScoreDisplayShapeTable,x
   STA TempHardwareDisplay

; Step 4: shut down the matrix
; lites = f0 matrix = 9 so the shutdown is #$f9
   LDA #$40   ; lite/matrix 374
   STA Via_ORB_IRB
   LDA #$F9   ; no matrix or lights
   STA Via_ORA_IRA
   JSR KickCA2
   
; Step 5: load display data to the 374
   LDA #$00   ; display 374
   STA Via_ORB_IRB
   LDA TempHardwareDisplay
   STA Via_ORA_IRA
   JSR KickCA2
   
; Step 6: load lites data to 374
   LDA #$40   ; lites/matrix 374
   STA Via_ORB_IRB   
   LDA TempHardwareLitesMatrix374
   STA Via_ORA_IRA
   JSR KickCA2

; Step 7: turn On Matrix
   LDA TempHardwareLitesMatrix374
   AND #$F0   ; mask out the matrix#
   CLC
   ORA MatrixNumber
   LDA #$40   ; lites/matrix 374
   STA Via_ORB_IRB   
   LDA TempHardwareLitesMatrix374
   STA Via_ORA_IRA
   JSR KickCA2

; Step 8: switch reading
   JSR PullSwitchesFromHardware
   LDA TempSwitch0
   STA SwitchHardware1   ; Outhole switch
   LDA TempSwitch1
   STA SwitchHardware2   ;Front panel test switch
   LDA TempSwitch2
   STA SwitchHardware3   ; R spinner
   LDA TempSwitch3
   STA SwitchHardware4   ; Lower lane 4 switch

; Step 9: persistance delay
   JSR DisplayAndLightsPersistanceDelay
   


I tried to make it verbose to understand what is going on at each step in it. The reason for the screwball sequence is that for each enable line, it hits a display, a light matrix and switch matrix simultaneously, so I need to service each of them at each step.

_________________
"My biggest dream in life? Building black plywood Habitrails"


Top
 Profile  
Reply with quote  
 Post subject: Re: assembler macros!
PostPosted: Tue Jun 22, 2010 6:44 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Without knowing how often these things recur and what differences there may be (if any) from one occurrence to another, I might pick step 2 as possibly the best candidate for a macro:

Code:
    Load_lights    Game_over, Bonus_8K, Bonus_9K, R_extra_ball


The parameter list tells it which lights.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Jun 22, 2010 7:17 pm 
Offline

Joined: Mon Mar 02, 2009 7:27 pm
Posts: 3258
Location: NC, USA
Garth, just had to write in. I sincerely hope all will go well with your job...

I wish I could put your nuggets of software knowledge into practice. Right now, I am deep into the hardware prototyping and still using the original software I wrote last year for plotting graphics.

But, maybe I could post a simple subroutine, and you guys could tear it apart and suggest a macro or two?

_________________
65Org16:https://github.com/ElEctric-EyE/verilog-6502


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Jun 22, 2010 7:48 pm 
Offline

Joined: Fri Jun 27, 2003 8:12 am
Posts: 618
Location: Meadowbrook
Garth, the lights in the loop are 4 per step of 8 steps, for all 32 lights. Then they are called during any turn on or turn off routines. Originally, I went by the light name on the right so they are labels, I now set them as constants so for base hardware layer loop which the programming is of, it is listed as light hardwareX, in case someone makes another pinball design and has a different light name. They can use the light name in other areas of software, so I sort of combine both precepts. I just commented where the lights are assigned in the hardware loop.

for a game logic, a STZ Game_Over_Light will be more understandable than a STZ LightHardware22. Yay constants :D

I made it that way as I plan to offer an SDK for it and I expect people wil re-assign the lights to where they want...

_________________
"My biggest dream in life? Building black plywood Habitrails"


Top
 Profile  
Reply with quote  
 Post subject: Re: assembler macros!
PostPosted: Tue Jun 22, 2010 9:18 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Quote:
Garth, just had to write in. I sincerely hope all will go well with your job...

The boss just now apologized on the phone for his disastrous behavior Thursday, and gave me a lot of credit he had not given me before. What a breath of fresh air.

Quote:
But, maybe I could post a simple subroutine, and you guys could tear it apart and suggest a macro or two?

We can give it a shot, just not make any promises when we're not familiar with the project beyond what you've posted.


But to reiterate bogax's concern, don't confuse subroutines and macros. Let me try another simple example of how they might be used. Let's say you have a subroutine that displays a string that's at the address whose low byte is in X and its high byte in A. Typical use might be:
Code:
        LDA  #>FOOBAR
        LDX  #<FOOBAR
        JSR  DISPLAY_SPECIFIED_STRING

Using a macro, you could make it something like:
Code:
        DISPLAY  FOOBAR

laying down exactly the same code while cutting out two thirds of the lines and making it more readable and at the same time eliminating the chance of having errors to debug like forgetting to put "#" or getting the wrong sign on one of them. Comparing the resulting hex code output from the assembler with versus without the macro, you won't find any differences. Even the DISPLAY_SPECIFIED_STRING subroutine is unchanged. DISPLAY would be defined as:
Code:
DISPLAY: MACRO addr
         LDA  #>addr
         LDX  #<addr
         JSR  DISPLAY_SPECIFIED_STRING
         ENDM
 ;-------------

It's just like what you had before, except that how you're hiding the details. Once the details are worked out, you shouldn't have to keep looking at them. You can close the hood (if I can use the automobile analogy). Closing the hood makes driving easier and safer.

Remember that the macros have to be defined before being called. I put the general-purpose ones near the top of my source code file, although I'm accumulating so many that now I'm starting to put them in a separate INCL file.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Jun 23, 2010 4:18 pm 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
bogax wrote:
I've never really understood the predilection to think of Macros as similar to subroutines.

I suppose if they resemble any thing it's an inlined function.


^^^^ This is why the predilection to think of macros as subroutines, for inline functions are, by definition, subroutines.

Macros, at least in the context of assembly language, are shorthand notations for commonly occurring pieces of code. (Indeed, if you've ever used an abbreviation for something, like "McD's" for McDonalds, you've used a macro!!) Nothing more. They're textual substitutions, with only the smallest amount of integrated intelligence.

Quote:
Or maybe equating a function and a variable because they're used (syntactically and gramatically) in a similar fashion.


From an algebraic point of view, note that a variable is a niladic function, so this can be excused. It wasn't until Fortran that we saw the possibility of re-assigning variables and state mid-program.

Quote:
I always thought of Macros as more like a production (partly because when I first learned about them they
were simple substitutions, no parameters or "scripting", but also because they seemed to serve a similar purpose)


It should be pointed out that text macros are not representative of true macros. While the first instance of macros, they are but a small subset of what true macros offer.

Macros in Lisp, for example, do take parameters, and are used to parametrize the macro's behavior.

A true macro is a function which takes as input one or more programs, and yields a program as a result. In the ideal case, this occurs before parsing happens, let alone compilation of the results. You're literally performing a computation on a piece of program. Note I'm not saying a piece of program text. Hence, with macros, you can physically extend the language in any arbitrary direction you want, so long as the extensions offered can be expressed in terms of an already existing base language (which itself my be implemented using macros; most of Common Lisp's definition is, in fact, specifying macros!). You don't like Lisp's prefix notation for mathematical expressions? Write a macro to expand infix notation instead. You don't like Lisp's default set of looping constructs? Use the (loop) macro instead, which offers insane levels of flexibility with easy to use, near native-language-like syntax. See
http://www.unixuser.org/~euske/doc/cl/loop.html for a great example of what genuine macros can offer, and which doesn't even scratch the surface of what (loop) can do.

Contrast macros against Forth's "immediate words," which have some macro-like behaviors, but which aren't macros. Here are the major differences between macros and immediate constructs:

* Unlike macros, immediate word evaluation occurs during compile-time.

* Immediate words can extend the language in any arbitrary direction, just like macros can, but can do so without expressing the equivalent code in terms of a pre-existing base language. Indeed, there can be no base language at all, for it's operating during compile time, not during parse-time.

Many subroutine-threaded Forth primitives are implemented using immediate words which compile to CPU opcodes directly, not (always) to lower-level Forth.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 22 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: