6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 10, 2024 5:38 am

All times are UTC




Post new topic Reply to topic  [ 47 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
PostPosted: Mon Mar 30, 2020 10:52 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10975
Location: England
Here's a mini-challenge for you. I have a real application in mind too, so it's not artificial.

You have an 8 bit input port at some absolute address, and the challenge is to write a subroutine which waits for every pin to toggle (change state) at least twice, and then returns. (It should of course return as soon as every pin has toggled twice, regardless of the starting state of the inputs and the order in which they toggle. They might all change together, or one by one.)

For example, if the port initially reads 00, then the next read is FF, and the next read is 00, we're done. Or if the first read is 01, then 02, shifting left to 80, then 01 again, we'd be done. Or it might take thousands of samples: it might even never happen. There might be many repeated values, or no repeats at all.

Edit: adding a bit of padding to keep replies further down the page...
.
..
.
...
.
..
.
....
.
..
.
...
.
..
.


Last edited by BigEd on Mon Mar 30, 2020 12:09 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 11:59 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Oh, this looks fun! My approach would use three bytes of ZP; one maintaining the previous state of the port, and two masks indicating whether the first and second transitions have occurred on each bit; I think that's the minimum required to function. And I'm going to use CMOS instructions.
Code:
port = $xxxx
zp = $zz

prev = zp+0
first = zp+1
second = zp+2

WaitPort8:
  ; initialise
  LDA port
  STA prev
  STZ first
  LDA #$FF
  STA second

  ; main loop
: LDA port
  TAX
  EOR prev
  BEQ :-      ; speed optimisation, omit if size critical
  STX prev
  TAX          ; mask of changed bits
  AND first    ; bits that have already changed once; this is their second
  TRB second   ; clear them out of the terminating condition
  TXA          ; return to bits just changed
  TSB first    ; record that fact
  LDA second   ; now test to see if all flags cleared
  BNE :-
  RTS
This is 34 bytes, or 32 if you leave out the early loopback which reduces port response latency. One further byte can be saved at a slight speed penalty by inverting the polarity of the second register:
Code:
port = $xxxx
zp = $zz

prev = zp+0
first = zp+1
second = zp+2

WaitPort8:
  ; initialise
  LDA port
  STA prev
  STZ first
  STZ second

  ; main loop
: LDA port
  TAX
  EOR prev
  STX prev
  TAX          ; mask of changed bits
  AND first    ; bits that have already changed once; this is their second
  TSB second   ; set them in the terminating condition
  TXA          ; return to bits just changed
  TSB first    ; record that fact
  LDX second   ; now test to see if all second flags set
  INX
  BNE :-
  RTS
The above is 31 bytes. This form is also easier to convert to be NMOS compatible, by replacing each TSB with ORA/STA and doing the usual fix on STZ, but this adds 6 bytes to the code size and 2 cycles to the sampling interval.

In the first example, the port is sampled every 12 cycles if it hasn't changed in any bit, with a gap of 37 cycles if it has. If the short-circuit is removed, the sampling interval becomes a consistent 35 cycles. The second example takes 37 cycles consistently. These characteristics will define timing requirements for the external hardware, in terms of the shortest pulse that will consistently be detected; at 8MHz CPU clock, 40 cycles (5µs) might be considered safe.


Last edited by Chromatix on Mon Mar 30, 2020 3:00 pm, edited 5 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 12:27 pm 
Offline

Joined: Tue Apr 20, 2010 4:02 pm
Posts: 34
Same idea here. 6502 instruction set.

Code:
ch_1   = 129
ch_2   = 130

   org $2000
START
         lda #0
         sta ch_1
         sta ch_2
         lda test
         sta prev

         ldy #1
@         lda test,y
         jsr REGISTER
         iny
         cpy #test_end-test
         bne @-

loop      jmp loop
         rts

REGISTER
         tax
         eor prev
         stx prev
         tax

         and ch_1
         ora ch_2      ;leave only those, that were previously set to 0
         sta ch_2

         txa
         ora ch_1      ;accumulate 1 bits
         sta ch_1

;##TRACE  "%08b %08b %08b" db(prev) db(ch_1) db(ch_2)

         lda ch_2
         cmp #$FF         ;Z = 1 - done
         rts
test
         dta %00000010
         dta %00000011
         dta %00000001
         dta %00000111
         dta %00000110
         dta %00000100
         dta %11111000
         dta %00000000
test_end



Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 1:19 pm 
Offline

Joined: Fri Apr 15, 2016 1:03 am
Posts: 139
Code:
WaitFor2Changes:
    lda port    ; remember initial values
    sta @orig
    stz @change1    ; no 1st changes yet
    stz @change2    ; no 2nd changes yet

@loop:
    lda port    ; get changes
    eor @orig
    sta @temp1

    ora @change1    ; accum 1st change
    sta @change1

    eor @temp1  ; accum 2nd change
    ora @change2
    sta @change2

    ina     ; until all 2nd change set
    bne @loop

    rts

@orig:  .byte       ; original value
@change1: .byte     ; 1st change seen
@change2: .byte     ; 2nd change seen
@temp1  .byte       ; temporary


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 1:36 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
That is an interesting alternative approach. Essentially you're looking for bits to be set and then cleared, after first transforming them relative to the first value read, rather than explicitly looking for two changes between successive reads. It's potentially a little smaller code than mine, but requires an extra byte of storage.

As written, it would end up quite large because of the locally-allocated storage (which requires absolute addressing). If you move the variables into ZP, it's 30 bytes.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 2:21 pm 
Offline

Joined: Thu Jan 21, 2016 7:33 pm
Posts: 279
Location: Placerville, CA
Oh, interesting...gonna have to have a think on this one.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 4:26 pm 
Offline

Joined: Mon Sep 17, 2018 2:39 am
Posts: 138
Hi!

BigEd wrote:
Here's a mini-challenge for you. I have a real application in mind too, so it's not artificial.

You have an 8 bit input port at some absolute address, and the challenge is to write a subroutine which waits for every pin to toggle (change state) at least twice, and then returns. (It should of course return as soon as every pin has toggled twice, regardless of the starting state of the inputs and the order in which they toggle. They might all change together, or one by one.)


This kind of problem (having to perform logic for all bits in a byte/word) can be solved by a technique called "vertical counters", see https://everything2.com/title/vertical+counter for a little explanation.

In your case, you can define a state machine with three states, representing the number of transitions seen, state A, B and C. You got from A to B of from B to C if you detect a transition, and do nothing in all other cases. If you encode with two bits, "x" and "y", using A = "00", B = "01" and "C = "11", your logic table is:

Code:
In xy  x'y'
-----------
0  00   00
0  01   01
0  10   --
0  11   11
1  00   01
1  01   11
1  10   --
1  11   11


This simplifies to the two equations: x' = x+I*y and y' = y + I.

Now, the (untested) code:

Code:
   ' Three ZP locations for our variables:
OLD_IN = $10
S_X = $11
S_Y = $12

wait:
   ' Init old input var
   lda INPUTS
   sta OLD_IN
   lda #0
   ldy #0
loop:
   ' Store state
   sta S_X
   sty S_Y
   ' Check toggled bits updating old value
   lda OLD_IN
   tax
   eor INPUTS
   stx OLD_IN
   ' Store for later
   tax
   ' Now, get new Y
   ora S_Y
   tay
   ' And the get new X
   txa
   and S_Y
   ora S_X
   ' Compare X with 255 (al bits set)
   cmp #255
   bne loop
   ' Ok!
   rts


Have Fun!


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 4:32 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
I count 35 bytes, 38 cycles per loop, and at least one visible bug…

Otherwise, it's conceptually similar to my solution, just implemented differently.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 4:38 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10975
Location: England
Hope everyone's having fun! Great to see the code posted. (For myself, I think my vague idea was to register rising transitions in one byte's worth of bits, and register falling transitions in another. When both of those bytes fill up we're done. But I hadn't written any code. There's always more than one way to do it!)


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 4:59 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
BigEd wrote:
Hope everyone's having fun! Great to see the code posted. (For myself, I think my vague idea was to register rising transitions in one byte's worth of bits, and register falling transitions in another. When both of those bytes fill up we're done. But I hadn't written any code. There's always more than one way to do it!)

Hmm, sketching out that idea:
Code:
  LDA port
  STA prev
  LDA #$FF
  STA first
  STA second

: LDA port
  TAX
  EOR prev
  BEQ :-      ; speed optimisation, omit if size critical
  AND prev     ; falling transitions have bits set
  TRB falling
  TXA
  EOR #$FF
  ORA prev    ; rising transitions have bits cleared
  AND rising
  STA rising
  STX prev
  ORA falling
  BNE :-
  RTS
That makes 39 bytes, and I'm not even going to bother counting the cycles. It just turns out to be more awkward to identify the specific transitions rather than just looking for *any* transition.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 5:13 pm 
Offline

Joined: Mon Sep 17, 2018 2:39 am
Posts: 138
Hi!

Chromatix wrote:
I count 35 bytes, 38 cycles per loop, and at least one visible bug…

Otherwise, it's conceptually similar to my solution, just implemented differently.


Yes, I saw your code after posting mine, I tried to show how to arrive at a generic solution because I have used vertical-counters in microcontrollers before, for debouncing and general counting of transitions.

Also, my code is 6502 only, and I still don't see the bug yet :-P

Have Fun!


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 5:16 pm 
Offline

Joined: Mon Sep 17, 2018 2:39 am
Posts: 138
Hi!

dmsc wrote:
Hi!

Chromatix wrote:
I count 35 bytes, 38 cycles per loop, and at least one visible bug…

Otherwise, it's conceptually similar to my solution, just implemented differently.


Yes, I saw your code after posting mine, I tried to show how to arrive at a generic solution because I have used vertical-counters in microcontrollers before, for debouncing and general counting of transitions.

Also, my code is 6502 only, and I still don't see the bug yet :-P

Have Fun!


Now I see it, fixed code bellow:


Code:
   ' Three ZP locations for our variables:
OLD_IN = $10
S_X = $11
S_Y = $12

wait:
   ' Init old input var
   lda INPUTS
   sta OLD_IN
   lda #0
   ldy #0
loop:
   ' Store state
   sta S_X
   sty S_Y
   ' Check toggled bits updating old value
   lda INPUT
   tax
   eor OLD_IN
   stx OLD_IN
   ' Store for later
   tax
   ' Now, get new Y
   ora S_Y
   tay
   ' And the get new X
   txa
   and S_Y
   ora S_X
   ' Compare X with 255 (al bits set)
   cmp #255
   bne loop
   ' Ok!
   rts


Have Fun!


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 9:57 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
I haven't tried to analyze anyone else's code yet, and started with a clean sheet of paper and a #2 pencil (I'll be using the NMOS instruction set obviously) ...
Code:
attempt:
    lda  #0        ; initialize bit trackers
    sta  once
    tax
cont:
    sta  twice     ; track bits that flipped >= twice
    txa
    ora  once      ; 1s for bits that flipped >= once
    sta  once      ; track bits that flipped >= once
    ldx  PORT
wait:
    txa
    eor  PORT      ; wait for some PORT activity
    beq  wait
    tax            ; 1s for bits that flipped this time
    and  once
    ora  twice     ; 1s for bits that flipped >= twice
    cmp  #$ff      ; have all 8 bits flipped >= twice?
    bne  cont
    rts

I don't have any time to test it, because I'm sneaking it in at work, but it looks like I did it in 30 bytes +RTS +two bytes of ZP. I'll try to test it tonight, but I would certainly appreciate any early feedback from you guys. Thanks,

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 30, 2020 10:01 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Hmm, I spy a race condition in that last one. When any bit transition is seen, any future transitions are only detected relative to a second port read. So any transitions occurring between those two reads will be lost.


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 31, 2020 12:08 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
I must be a bit confused or near-sighted, because I don't immediately see how I'll miss any port transition events unless they burst less than ~30 cycles apart, which is about how long my routine takes to allegedly close its eyes to digest each event. I guess I'll have to try to grok what the rest of you have posted to see where I failed ...

Would this one have a smaller "blind spot"? (it's one byte smaller too, and still NMOS, baby!)
Code:
attempt2:
    lda  #0        ; initialize bit trackers
    sta  once
cont:
    sta  twice     ; track bits that flipped >= twice
    beq  first
    txa
    ora  once      ; 1s for bits that flipped >= once
    sta  once      ; track bits that flipped >= once
    tya
    eor  PORT      ; check for PORT activity
first:
    ldy  PORT
    tax            ; 1s for bits that flipped this time
    and  once
    ora  twice     ; 1s for bits that flipped >= twice
    cmp  #$ff      ; have all 8 bits flipped >= twice?
    bne  cont
    rts

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Tue Mar 31, 2020 4:10 pm, edited 2 times in total.

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

All times are UTC


Who is online

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