-1

Code in question:

; Copy all the values which are divisible by four to a starting address $1600.
; The indivisible by four is pushed onto the stack, and use the effective address
; to pull them out after finishing the loop. The starting address is $1650.
; Array pointer starts from the beginning, and using DBNE to decrement the loop

N       equ     12
        org     $1600
        fill    0,128           ; each row in the memory displays 16 bytes
cnt     rmb     1               ; indivisible counter
        org     $1ff0
        fill    0,16

        org     $2000           ; start RAM address
        lds     #$2000          ; initialize the stack pointer
        ldy     #$1600          ; load the starting address for divisible by 4
        ldx     #array          ; load the starting address of array to Reg. X
        ldaa    #N              ; use B as the loop counter, B = N
        clr     cnt             ; clear cnt counter
loop    brclr   0,x,$03,yes     ; if bits 1 & 0 are 0s, then branch to yes
        ldab    1,x+
        inc     cnt             ; increment cnt counter for indivisible
        pshb                    ; push onto the stack for the indivisible
        bra     chkend
yes     movb    1,x+,1,y+       ; copy the divisible by 4 value to a new location
chkend  dbne    a,loop          ; decrement B by 1, if B NE 0, branch to loop

; use the effective address to load/pull the elements from the stack

        ldy     #$1650
        ldx     #$1fff          ; first available address for the stack
        ldaa    cnt
loop2   movb    1,x-,1,y+       ; stack is first in last out
        dbne    a,loop2
        swi
array   db      3,4,9,12,19,20,24,31,48,53,64,72
        end

I'm taking mircoprocessors and my professor gave us this code to change, but I'm unsure on how to. This is the first time we're "programming" in Assembly.

He also came us this to help:

EEEN 3449
Lab 4c:
• idiv => Accumulator D / Register X, the remainder refreshes to D
• Register X has the constant 3.
• Register Y is utilized as the array pointer.
• Although there is nothing stored in Accumulator A, A:B => D
• You can use Accumulator B as the loop counter, and store the
starting address for divisible by 3.
• In the beginning of the loop, you have to store loop counter value in
another temporary address, like $15FE.
• clr A, and ldab 0,y will generate [D] = [0 : 1st array element]
• Carry out the idiv and compare Accumulator D with 0.
• If Accumulator D is non-zero, push the accumulator B onto the stack.
• If Accumulator D is zero, reload the updated address for divisible by 3.
• After sorting out the array element where to store them, reload the
loop counter from $15FE, and increment the array pointer.
• Then use dbne b,loop to execute the next iteration.

Thanks to not knowing Assembly, I don't know how to use this to change the code correctly.

The code shown is pulling numbers that are divisble by 4 and putting them into the memory at $1600, I think. I need to change this code and make it pull numbers that are divisble by 3. No clue where to start and begin testing since I don't know a whole lot on Assembly.

Any help would be great as well as if you could help me understand what's happening. I do want to learn, but I can't seem to find much on Assembly.

I've tried testing things myself, but I keep getting errors and don't know where they are or how to fix them.

  • 1
    4 is a power of 2 which makes divisibility testing trivial on a binary computer (just check the low 2 bits). Divisibility by 3 is much harder, but the most efficient check involves one multiply and compare, fewer operations than exact division that would work for values that aren't multiples of 3. [Fast divisibility tests (by 2,3,4,5,.., 16)?](https://stackoverflow.com/q/6896533) – Peter Cordes Mar 06 '23 at 22:00
  • There is no trivial way to check for divisibility by 3 without division. Multiplication approach is great if you want performance, but the simplest division is a loop of repetitive subtraction. Though it can be very slow, if you aren't interested in performance, that's probably the simplest to do and to explain. Simply subtract 3 repetitively until the value becomes < 3. The quotient is how many times were able to subtract 3, while the remainder is the final result of stopping at < 3. If the remainder is zero, then it is evenly divisible by 3, otherwise not. – Erik Eidt Mar 06 '23 at 22:12
  • 1
    What architecture are you programming for? – fuz Mar 06 '23 at 23:30
  • 1
    @ErikEidt There is actually. An 8 bit number is divisible by 3 if `popcnt(x & 0x55) - popcnt(x & 0xaa)` is divisible by 3. That number is in the range `-7..7` and you can simply look up in a table if it is divisible. – fuz Mar 06 '23 at 23:31
  • @fuz, yes, but for a querent who claims no knowledge of assembly, I respectfully submit that neither the multiplication nor the popcnt are approachable. None the less, my comment was off b/c they are supposed to use division.. – Erik Eidt Mar 07 '23 at 00:41
  • @ErikEidt: If someone knows literally nothing about assembly, all SO can do is to do their homework for them, or say something about an algorithm they still won't know how to implement. Teaching the basics of what instructions and registers are is too much for one answer. But other future readers might find this.I think your comment was correct that there's no *trivial* (or obvious) way to check divisibility by non-powers-of-2 on binary computers other than division. So fuz's comment was interesting (for me and other future readers), but didn't contradict anything you said. – Peter Cordes Mar 07 '23 at 01:06

1 Answers1

1

That code is testing the low two bits of the byte of interest (referred to by the X register) for being zero.  Those bits both being zero means that the number is divisible by 4.  When that condition is true the program transfers control to label yes — all that is done in one instruction, the brclr.

To change from divisible by 4 to divisible by 3, you're supposed to use idiv in a sequence that ends with a conditional branch that will have the same effect as the brclr — which is to branch to label yes when the condition of interest holds, and fall through otherwise.

  • First, put the number you want to test in the D register

    • To do this, you need to know that the D register is an alias for the 16-bit register formed by pairing the 8-bit A & B registers.  HCS12 is big endian, and A is the high order, while B is the low order of pair D.  (This pairing is inherent in the architecture, you don't have to do anything else to take advantage of it.)

    • Because your data is byte data (not 16-bit data), you need to load it into B, and make sure that A is clear (zeroes) to widen the unsigned byte data from 8-bits to 16-bits in alias register named D.

    • Also you need to be aware that the byte data you want to put into B is referred to by the X register.

    • So, you'll use an instruction like LDAB (but also you'll need to make sure A is cleared, so load it with 0, for example).

  • Then put the constant 3 in the X register.

  • After setting that up, do an idiv and the remainder will be in the D

  • Do a similar branch as for the divisible by 4 but first test for D = 0, and in that case, branch to label yes.

    • This will require a 2-instruction sequence, where the first instruction compares D with 0 setting the flags, and the second branches to label yes if the compare says zero, e.g. using BEQ (and if the compare fails it will fall through just like when the brclr falls through.

There are some register conflicts: in order to use idiv you'll need to use the X register directly, but the code is already using X for something else.  Suggest before loading X with 3 to push X to the stack and pop it after so you can leave the rest of the code as is.  Looks like the same is true for A, which is holding the count in that algorithm, but also needs to be repurposed to use the idiv.  Your instructions make some alternative suggestions (rather than push and pop) you may choose to follow.

Erik Eidt
  • 23,049
  • 2
  • 29
  • 53