Joined: Fri Aug 30, 2002 1:09 am Posts: 8543 Location: Southern California
|
Quote: I see Garth is about to define some new macros, but they're not included because I don't know what they are! I've been working on a project with a PIC16F72 where I'm trying to kill as many birds with one stone as possible, one being to get more structure macro experience in assembly and see what other structures I would want and add them and make sure they work right. One I've done there is FOR...NEXT for single-byte counters. I have one for two-byte variables in the structure macros 6502 page, but I need to add ones for single bytes and especially X and Y that will work similarly to the PIC ones below, able to count up and down, and be nestable with other identical ones. The PIC doesn't really have index registers so the loop counter pretty much has to be in a RAM variable; but the 6502 frequently uses X or Y as a loop counter.
My PIC ones so far (with syntax examples buried in the code) (Darn! I thought the code portions were not supposed to wrap lines!):
Code: ; +--------------------+ ; | FOR...NEXT | ; +--------------------+
; FOR...NEXT is nestable. Its limitations are: ; 1. The counter has to be a file register. ; 2. The index must start as a constant, or it can start as W. The macro cannot fetch it from a variable. ; 3. The limit must start as a constant, ie, not a value computed in the program or fetched from a variable. ; 4. It is limited to one-byte counters. ; 5. W gets altered in setting up the loop unless the initial index value comes in on W. ; 6. W gets altered by NEXT if the limit is non-0. ; 7. Since the INC or DEC in NEXT does the comparing and drops through if there's a match, the loop will not be run with ; the final "to" value. IOW, "FOR COUNT, 8, DOWN_TO, 0" will run 8 times, not 9: 8, 7, 6, 5, 4, 3, 2, and 1, not 0.
; LEAVE_LOOP could be implemented, but since it would always be used with a condition, maybe it would be best to just use an ; IF_<condx>...END_IF to complete the loop. Otherwise, what you could do is use a fourth stack level, and have FOR init it ; as 0. Then if there's a LEAVE_LOOP, it would store the address of its GOTO instruction in that stack cell, and NEXT would ; test it to see if that cell is non-0 and fill it in with a GOTO to the end if so. You would have to be careful not to put ; the LEAVE_LOOP inside another structure that might be using the macro structure stack. And as always, "compiler" security ; is up the to programmer.
; The number of instruction cycles taken for a loop loading its own index (call it "N") into W and decrementing it to 0 is: ; ; 2 for loading W and storing it in the variable ; + N * loop_contents ie, your code in the loop, which might be 0 if it's a do-nothing loop for delay only ; + (N-1) * 3 for DECFSZ and GOTO top_of_loop ; + 2 for final DECFSZ skipping over the GOTO ; ; So for: ; FOR LOOP_COUNT, 8, DOWN_TO, 0 ; NEXT LOOP_COUNT ; you have 2 + 0 + 21 + 2 = 25 instruction cycles, or 100 clocks for this empty loop.
DOWN_TO: EQU 0 ; These two are used in the FOR-NEXT structure below which uses DECFSZ UP_TO: EQU 1 ; and INCFSZ. It needs to know which one to use. This and the limit and ; the file register addr get put on the stack since FOR-NEXT is nestable.
FOR: MACRO var_name, index, action, limit ; Syntax example: FOR LOOP_COUNT, 8, DOWN_TO, 0 (1-byte counter only.) IF index != W ; The .INC file assigns a value of 0 to W, and it's better to allow the index to MOVLW index ; start with the value of W than to allow counting from 0 to 0. ENDIF MOVWF var_name ; The variable name doesn't need to get stored since we will require NEXT to include ; the name, and an "I", "J", etc. reference can just use the variable name also. STACK_PUSH STACK_PUSH ; Add three cells to the stack. STACK_PUSH STK_LVL_1: SET $ ; This is the addr of the top of the loop for NEXT to branch up to. STK_LVL_2: SET action ; Record whether the NEXT should INC or DEC. STK_LVL_3: SET limit ; Record what ending number NEXT should be watching for. ENDM ;--------------------------
NEXT: MACRO var_name ; Syntax example: NEXT LOOP_COUNT
IF (STK_LVL_2 == DOWN_TO) && (STK_LVL_3 == 0) ; MPASM does not allow nested IF...ENDIF DECFSZ var_name,F ; conditional assembly, so do it this way. ENDIF
IF (STK_LVL_2 == UP_TO) && (STK_LVL_3 == 0) INCFSZ var_name,F ENDIF
IF (STK_LVL_2 == DOWN_TO) && (STK_LVL_3 != 0) DECF var_name,F MOVF var_name,W SUBLW STK_LVL_3 BTFSS STATUS,Z ENDIF
IF (STK_LVL_2 == UP_TO) && (STK_LVL_3 != 0) INCF var_name,F MOVF var_name,W SUBLW STK_LVL_3 BTFSS STATUS,Z ENDIF ; Unless the limit has been reached and the above instruction GOTO STK_LVL_1 ; branches around this, branch back up to the top of the loop. STACK_POP2 STACK_POP ENDM ;--------------------------
Another set will be for testing individual bits of absolute addresses, similar to BBR and BBS in ZP. Again my PIC version which I will later carry out in 6502 also is:
Code: IF_BIT: MACRO FL_ADR, BIT_NR, CONDX ; (tests a bit in a file register) Syn: IF_BIT INTCON, T0IF, IS_SET IF CONDX == IS_SET BTFSS FL_ADR,BIT_NR ELSE BTFSC FL_ADR,BIT_NR ENDIF STACK_PUSH ; Add a cell to the stack, and STK_LVL_1: SET $ ; put the current address in that cell. A GOTO instruction will go at this address. RES 1 ; REServe 1 program word space, like ORG $+1, for a GOTO to be added here later, either ENDM ; by the ELSE_ (if there is one), or the END_IF. ;--------------------------
and an example of the usage is (and as you can see, it's in part of a CASE statement):
Code: CASE_OF 4 ; We were waiting for the low address byte to be shifted out. RAM_BANK 1 ; Is that finished yet? (Check buffer-full bit in SSP status register.) IF_BIT SSPSTAT, BF, IS_SET ; If so, RAM_BANK 0 CLRF SSPBUF ; send out a dummy byte in order to get the first data byte to read, INCF FLASH_RD_STATE,F ; then increment the state. END_IF END_OF
In this case, the serial-port shifting for the given hardware takes time, and there's too much to do to twiddle our thumbs, so I have kind of a multitasking system where every time a task is called up, it looks to see if there's anything it can or should do at the moment, and if not, lets the computer move on to do other necessary things while it's waiting. [Edit, 5/15/14: I posted an article on simple methods of doing multitasking without a multitasking OS, at http://wilsonminesco.com/multitask/index.html.]
Curiously, the first time I used a CASE statement, I had 10 different cases to watch for, and 10 is how many I made the macros to be able to do; so I guess I will be increasing the maximum to 16. (If the cases are sequential numbers, the 6502 does well with a jump table; but the PIC's jump-table ability is more limited.) This portion of the code goes two pages without needing a single label, because of the program structures.
There will be others. There are so many things I have on the burners, with one of the goals being to eventually be able to offer a complete handheld 6502/816 workbench computer with its own keyboard, display, flash file storage, and a reasonably tough case, a computer which the user can, if necessary, develop software on without needing a host computer like a PC (although that will still be possible).
_________________ 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?
|
|