While working on my personal projects, I decided that my macro code I wrote years ago (a few people may remember) to accomplish high-level like structures in ca65 needed some attention. It worked well, but the macro code itself was unorganized. I've rewritten it and pulled the needed files for this from my project and shared them on Gitlabs: https://gitlab.com/ca65/ca65hl
( There is also a Github, but I won't be updating it anymore: https://github.com/Movax12/ca65hl )
I would consider this beta, though I have tested it quite extensively. With this rewrite, structured code builds about twice as fast as with my old code, though modules would have to be quite large to notice a significant difference in build times vs plain assembly with light macro usage. There is quite a bit of macro code, but this is partly due to quite a bit of error checking: it tries to make sure that either appropriate code or an error message is generated.
Please read the manual.md in the repository for more information.
If anyone uses it, please let me know if you have questions or find any bugs!
Macros for Structured/HL assembly (ca65)
Moderator: Moderators
Re: Macros for Structured/HL assembly (ca65)
This is great. Macros are often misused and misunderstood, or done without good use of conditional assembly to figure out the most efficient code to lay down; so it's always good to see good use of macros promoted to raise the level of the language where people think assembly is so cumbersome. I've gone to extents with nestable program flow control macros, as you can see in that part of my website, at http://wilsonminesco.com/StructureMacros/ . A couple of the many examples I have elsewhere on my site are:
and
In most cases, the macros assemble exactly the same thing you would do by hand, meaning there's no performance or memory cost; it's just that you don't have to look at the ugly internal details every time. They make the code a lot more clear, concise, and maintainable, and lead to fewer bugs since you can see what you're doing better, and you become more productive. The program flow control structures can be nested with others of the same or different type; for example, one IF...ENDIF can be nested inside another IF...ENDIF, inside another, inside a FOR...NEXT, which is inside another FOR...NEXT, or other combinations.
Code: Select all
; For the RX_STATEs:
; When RX_STATE = 0, an $FF byte will be required to increment RX_STATE to 1. Other bytes do nothing. The $FF is discarded.
; When RX_STATE = 1, only a non-$FF byte will be put in READING_BUF and increment RX_STATE to 2. More leading $FF's do nothing.
; When RX_STATE = 2, any value received will be put in READING, READING_BUF will be transferred to READING+1, and RX_STATE will
; be returned to 0.
WATCH_ACIA:
LDA RX_STATE
CASE ACCUM
CASE_OF 0 ; In the case of RX_STATE being 0, an $FF is required to increment it to 1.
IF_BIT ACIA_STAT_REG, 3, IS_SET ; If a byte came in (indicated by bit 3 of the status register),
LDA ACIA_DATA_REG ; get it (this clears the flag in the status register too),
INA ; and see if it's $FF. (INA is 1 byte less than CMP #$FF, and I don't need A again.)
IF_EQ ; If it was $FF,
INC RX_STATE ; move on to watch for a valid (non-$FF) first byte.
END_IF ; The accumulator value gets discarded.
END_IF ; If no byte came in, just exit.
END_OF
CASE_OF 1 ; To get here, we've received the $FF marker and we're looking for a valid 1st byte.
IF_BIT ACIA_STAT_REG, 3, IS_SET ; If a byte came in,
LDA ACIA_DATA_REG ; get it,
IF_PLUS ; see if it's valid as a high byte, ie, that the high bit is clear, and if so,
STA READING_BUF ; store it as valid in the buffer area meant to prevent wrong readings between bytes,
INC RX_STATE ; and move on to watch for the 2nd byte which could be anything.
END_IF ; If it's not a valid high byte, just discard it, and leave RX_STATE as is.
END_IF ; If no byte came in, just exit.
END_OF
; In the case of RX_STATE being 2, we've gotten the high byte of READING,
CASE_OF 2 ; and we're waiting for the second byte which could be anything.
IF_BIT ACIA_STAT_REG, 3, IS_SET ; See if a byte came in. If one did,
COPY VIA_SR, TO, READING ; just transfer it
COPY READING_BUF, TO, READING+1 ; (including the high byte now, which we had saved to prevent glitches
STZ RX_STATE ; in the two-byte READING value), and go back to state 0.
END_IF ; If no byte came in, just exit.
END_OF
STZ RX_STATE ; If there's any chance for state to go invalid, just reset it and start over.
END_CASE
RTS
;-------------------
Code: Select all
SPI_Xceive: ; Start with data to send in A.
STA SPIOUT ; Store output data, and
STZ SPIIN ; initialize the input buffer.
LDY #8 ; Set up bit counter.
LDA #SPI_MOSI ; Put MOSI bit mask in A for TRB/TSB.
LDX SPIMODE
CASE X_REG
CASE_OF mode_0 ; In the case of X saying mode 0,
FOR_Y Y_REG, DOWN_TO, 0 ; start into this loop. Go thru 8x.
Shift_bit_onto_MOSI ; (Shifts SPIOUT to start.)
INC VIA1PA ; Set SCLK high.
Rot_MISO_into_SPIIN ; Move MISO into the receive buffer.
DEC VIA1PA ; Set SCLK low.
NEXT_Y ; Decrement our bit counter.
END_OF
CASE_OF mode_1 ; In the case of X saying mode 1:
FOR_Y Y_REG, DOWN_TO, 0
INC VIA1PA ; Set SCLK high.
Shift_bit_onto_MOSI ; (Shifts SPIOUT to start.)
DEC VIA1PA ; Set SCLK low.
Rot_MISO_into_SPIIN ; Move MISO into receive buffer.
NEXT_Y
END_OF
CASE_OF mode_2
FOR_Y Y_REG, DOWN_TO, 0
Shift_bit_onto_MOSI ; (Shifts SPIOUT to start.)
DEC VIA1PA ; Set SCLK low.
Rot_MISO_into_SPIIN ; Move MISO into receive buffer.
INC VIA1PA ; Set SCLK high.
NEXT_Y
END_OF
CASE_OF mode_3
FOR_Y Y_REG, DOWN_TO, 0
DEC VIA1PA ; Set SCLK low.
Shift_bit_onto_MOSI ; (Shifts SPIOUT to start.)
INC VIA1PA ; Set SCLK high.
Rot_MISO_into_SPIIN ; Move MISO into receive buffer.
NEXT_Y
END_OF
; You could handle invalid SPI modes
; here before END_CASE if desired.
END_CASE
LDA SPIIN ; Returns the received data in Accum.
RTS
;------------------
http://WilsonMinesCo.com/ lots of 6502 resources
Re: Macros for Structured/HL assembly (ca65)
Yes, I can still control exactly what assembly language code is generated. I wouldn't recommend this for beginners just learning assembly though: It's probably better to learn the assembly first to understand how to use something like this properly. (Not that you couldn't use this - but I think it is important to understand how it is working.)
I haven't (re)implemented for loops or switch/case with this (yet?) as I haven't found that to be something I've used much. Usually just something like this works:
Code: Select all
; copy 16 bytes
ldx #$0F
repeat
lda fromhere, x
sta puthere, x
until (dex == negative)
Code: Select all
; loop from x = 1 to 15
ldx #$00
while (inx : x < #$10) do
lda fromhere, x
sta puthere, x
endwhile
Re: Macros for Structured/HL assembly (ca65)
I agree. It's like having kids master multiplying and dividing on paper before they're allowed to use calculators. First they need to understand what the process even is.Movax12 wrote: ↑Fri Mar 04, 2022 8:13 pmI wouldn't recommend this for beginners just learning assembly though: It's probably better to learn the assembly first to understand how to use something like this properly. (Not that you couldn't use this - but I think it is important to understand how it is working.)
http://WilsonMinesCo.com/ lots of 6502 resources