Bitwise indirect addressing on a 14M2

cpedw

Senior Member
I'm trying to access each bit of a byte variable in a for-next loop on a 14M2. The IF-BIT structure on X parts would do this tidily. Or a bitwise version of bptr would be fine. I'm getting short of available variables so I want to avoid this type of thing:

Code:
    READ char,temp                 ; temp is to be examined bit by bit
    bittest=1                            ; point to bit 0
    FOR count = 0 TO 7            ; Extract bit by bit
        bitly = temp & bittest
        IF bitly=1 THEN
            command = 150  
        ELSE
            command = 151 
        ENDIF
        serout SIGIN,T2400,(command,Xtim,Ytim)
        bittest = bittest * 2        ; point to next bit
    NEXT count
bitly could be a single bit variable but bittest must be a byte which is in short supply.
Is there a neat trick that might save on variables?
 

AllyCat

Senior Member
Hi,

Personally, I always reserve b1 as a "local" or "temporary" byte and w1 (b2, b3) as a "temporary word" (and b0 for gl0bal flags, if required) in all my programs. These also serve for passing a byte and/or word to/from any of my (generalised) subroutines, etc.. All these bytes (b0 - b3) are directly bit-addressable as bit0 to bit31, which incidentally is much more efficient than the IF ... BIT .... structure of X2 devices. I suspect that the latter is not much more than a "system macro" using M2-type commands (check the number of program bytes used by such instructions, given by the "Check Syntax" operation).

Also, a trick I'm often using is that bit 7 of any byte (or bit 15 of a word) can be tested directly with an IF byte > 127 THEN ... (or of course IF byte < 128 THEN ...) ;)

If you're running short of RAM, have you yet used S_W1 to S_W6 word variables which give the equivalent of at least another 12 bytes (but not separately bit or byte addressable), or the additional 484 bytes accessible @bptr (or 100 bytes in the 08M2)? And the @bptrINC (or DEC) is another "no cost" option. :)

Cheers, Alan.
 
Last edited:

Aries

New Member
There are the system variables S_W0 etc which, although they are word variables, can be used for AND/OR etc using only the lower byte.
 

Flenser

Senior Member
I need some more explaination of what you are trying to do.
What is the range of possible values for temp?
You always send SEROUT 8 times so it appears that what you intend is to send the following 8 sequences of the command
- when temp=%00000001 you want to send SEROUT 8 times with the sequence of commands 150, 151, 151, 151, 151, 151, 151, 151
- when temp=%00000010 you want to send SEROUT 8 times with the sequence of commands 151, 150, 151, 151, 151, 151, 151, 151
- when temp=%00000100 you want to send SEROUT 8 times with the sequence of commands 151, 151, 150, 151, 151, 151, 151, 151
- when temp=%11111111 you want to send SEROUT 8 times with the sequence of commands 150, 150, 150, 150, 150, 150, 150, 150
- when temp=%10101010 you want to send SEROUT 8 times with the sequence of commands 150, 151, 150, 151, 150, 151, 150, 151
Is this correct?

If I understand your intention correcty then my first comment is that your eample code does not do what you intend:
Code:
bittest=1                            ; point to bit 0
FOR loopcounter = 0 TO 7            ; Extract bit by bit
  bitly = temp & bittest
  IF bitly=1 THEN
  ... lines cut ...
  bittest = bittest * 2        ; point to next bit
Your example code does not test bits 0-7. It sets bittest to the values 00000001, 00000010, 00000100, 00001000, 00010000, 00010000, 00100000, 01000000 & 10000000 and this means your test "temp & bittest" can only ever return 1 when bittest =1
- temp & bittest for bittest=00000001 can only be 0 or 1 and will send the sequence of commands 150, 151, 151, 151, 151, 151, 151, 151 for any value of temp that has bit0=1. i.e. for temp=00000001, 00000011, 11111111, 01010101, 0000111, etc
- temp & bittest for bittest=00000010 can only be 0 or 2 and so will always send a sequence of 8 x 151 commands for all values of temp
- temp & bittest for bittest=00000100 can only be 0 or 4 and so will always send a sequence of 8 x 151 commands for all values of temp
- etc

As your code is outputting serial commands using SEROUT you can test it in the PE simulator by replacing your SEROUT commands with SERTXD commands.
Try running this code. I've set temp to a value that has a mixture of 1's & 0's and for any value of temp you test that has bit0 set to 0 it always outputs 8 x 151 commands
Code:
temp=%10101010                 ; temp is to be examined bit by bit
bittest=1                            ; point to bit 0
FOR loopcounter = 0 TO 7            ; Extract bit by bit
  SERTXD (#bittest, ": ")
  bitly = temp & bittest
  IF bitly=1 THEN
      command = 150  
  ELSE
      command = 151 
  ENDIF
  SERTXD (#command, cr, lf)
  bittest = bittest * 2        ; point to next bit
NEXT loopcounter
One way to correct this is to divide temp by bittest to move the current bit at position bittest into position 0 amd then always test bit positioin 0, like this:
Code:
READ char,temp                 ; temp is to be examined bit by bit
bittest=1                            ; point to bit 0
FOR loopcounter = 0 TO 7            ; Extract bit by bit
  bitly = temp / bittest & %0000001
  IF bitly=1 THEN
      command = 150  
  ELSE
      command = 151 
  ENDIF
  serout SIGIN,T2400,(command,Xtim,Ytim)
  bittest = bittest * 2        ; point to next bit
NEXT loopcounter
Going back to your original question.
You can remove the loopcounter variable by using a DO LOOP and testing the value of bittest.
Note that this do loop will correctly repeat for bit7 after calculatiing bittest*2=1000000 but the final calculation bittest = bittest * 2 will overflow the value of the byte variable and set bittest to 0, so that is we it tests "UNTIl bittest=0".
bitly must be a byte variable for this DO LOOP to work.
Code:
READ char,temp                 ; temp is to be examined bit by bit
bittest=1                            ; point to bit 0
DO             ; Extract bit by bit
  bitly = temp / bittest & %0000001
  IF bitly=1 THEN
      command = 150  
  ELSE
      command = 151 
  ENDIF
  serout SIGIN,T2400,(command,Xtim,Ytim)
  bittest = bittest * 2        ; point to next bit
LOOP UNTIL bittest =0
You can remove the command variable by putting the SEROUT commands inside the IF, THEN, ELSE code:
A syntax check of this code reports exactly the same size as the DO LOOP code above.
Code:
READ char,temp                 ; temp is to be examined bit by bit
bittest=1                            ; point to bit 0
DO             ; Extract bit by bit
  bitly = temp / bittest & %0000001
  IF bitly=1 THEN
      serout SIGIN,T2400,(150,Xtim,Ytim) 
  ELSE
      serout SIGIN,T2400,(151,Xtim,Ytim) 
  ENDIF
    bittest = bittest * 2        ; point to next bit
LOOP UNTIL bittest = 0
And if you have enough spare program space in the 14M2 then you can remove the bittest variable by "unrolling" the DO LOOP and coding it as 8 x If, THEN, ELSE commands with the bittest value replaced by a constant:
Code:
READ char,temp                 ; temp is to be examined bit by bit
bitly = temp & %0000001    ; Test bit0
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF

bitly = temp / 2 & %0000001    ; Test bit1
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF

bitly = temp / 4 & %0000001    ; Test bit2
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF

bitly = temp / 8 & %0000001    ; Test bit3
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF

bitly = temp / 16 & %0000001    ; Test bit4
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF

bitly = temp / 32 & %0000001    ; Test bit5
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF

bitly = temp / 64 & %0000001    ; Test bit6
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF

bitly = temp / 128 & %0000001    ; Test bit7
IF bitly=1 THEN
    serout SIGIN,T2400,(150,Xtim,Ytim) 
ELSE
    serout SIGIN,T2400,(151,Xtim,Ytim) 
ENDIF
 

Flenser

Senior Member
And if you would like to keep the DO LOOP and have enough free space in the scratchpad memory then you can remove the bitly variable by:
1) Copying the EEPROM data into the scratchpad in your program initialization.
2) Using "bptr=bptr_start+char" instead of "READ char,temp" to set bptr to the location in the scratchpad where you copied the value pointed to by the location char in the eeprom.
3) Using @bptr instread of temp in the code to calculate the bitly value you test to for whether the bit is 1 or 0
4) The code no longer uses the temp variable to hold the value at the location pointed to by char so you can remove the bitly variable and use the temp variable in its place
NOTE that temp must be a byte variable for this to work.

I haven't used the bptr much so my example code below is based on my reading today of the section "Variables - Storage" in Manual 2, which states that for the 14M2 chip the storage variables are locations 28 to 511 so I've started using the scratchpad memory at location 28, the next location after the highest b27 b-variable defined for the M2 chips
Perhaps one of the other forum members can comment on whether this is correct.

Here is the last of my examples that uses the DO LOOP modifed so that it no longer uses the bitly variable:
Code:
; In the initializaton section of your code that is run once at startup
symbol eeprom_start=0
symbol eeprom_end=2
symbol bptr_start=28

; Store your char values in eeprom
; I'm defining three values for this example at eeprom locations 0, 1, & 2
EEPROM eeprom_start, (%00000001, %10101010, %11111111)

; Copy eeprom values to Scratchpad RAM
bptr=bptr_start
FOR temp = eeprom_start TO eeprom_end STEP 1
    read temp, @bptrinc
NEXT

: Then in the main loop of your program
; Use bptr=bptr_start+char instead of read char,temp to read the byte from the scratchpad memory using bptr
; - char still takes the same values as it does in your program
; Remove the bitly variable and use the teimp variable in its place.
bptr=bptr_start+char
bittest=1                            ; point to bit 0
DO             ; Extract bit by bit
  temp = @bptr / bittest & %0000001
  IF temp =1 THEN
      serout SIGIN,T2400,(150,Xtim,Ytim)
  ELSE
      serout SIGIN,T2400,(151,Xtim,Ytim)
  ENDIF
    bittest = bittest * 2        ; point to next bit
LOOP UNTIL bittest = 0        ; bittest gets the values 1,2,4,8,16,32,64,128 and 2x128
                    ; which overflows the byte varaible value to 0
21/01/2024 Edited to remove the bitly variable instead of the temp variable
 
Last edited:

cpedw

Senior Member
Thanks all for comments and assistance. Flenser has correctly identified my intention and spotted that it doesn't work. That was easily fixed by adding "/ bittest" to the end of the line "command = bittest & temp" so that command is only 0 or 1 after that line.

The target is to make a "large digit" (16 by 10 pixel) digital clock using a cheap (they were £6 recently) Graphic LCD but, having fixed my software I found it was very slow to write each digit as each pixel was set or cleared one by one. It's now much quicker using FIllSquares to blank the exisitng pattern then setting only the pixels that are black. Still quite slow but tolerable - the LCD module will only work at 2400 baud.

What I have found is that it's good at drawing an analogue clock face using the Pointer command.

I'm sorry to have changed the problem when you put a lot of effort into solving the first problem. I'm still absorbing the rest of your posts to see if I can get easy access to more variable space. I'm trying to use those S_Wn words for bytes without confusing myself completely!
 
Top