Skip to main content

My journey with ATTiny4313 (part 5)

Part 5: Implementation of a State Machine

What is a State Machine?

Simply stated, it's an algorithm with a finite number of possible states. The machine transition from one state to another based on the input. In our case, we now what the MO44.DRV driver will send: a reset hardware, followed by the data for each of the 4 outputs. Every time, the /STROBE is pulsed down to warn the MO4 that a new byte is available (cf.  part 4).

The algorithm

S1
  If Strobe then State = S2

S2
  If Strobe then State = S3

S3
  If Strobe then State = S4

S4
  If Strobe then State = S5
  
S5
  If Strobe then
    If unit = 0 then
      State = S1
      unit = 4
    else
      Read counter
      If counter = 0 then 
        unit = unit - 1
      else
        If unit = ME then
          State = S62
        else
          State = S61
        End
      End
    End
  End
  
S61
  If Strobe then
    While counter > 0 do nothing
    State = S5
    unit = unit - 1
  End

S62
  If Strobe then
    While counter > 0
      Read data
      Store data to X
      Increment X
    End
    State = S5
    unit = unit - 1
  End
  

The ijmp instruction

This instruction allows to jump to the address pointed by Z (16 bit pointer made by registers r30 and r31). It's very efficient and runs in 2 cycles. Since I know the first incoming byte always represents the quantity of data to follow, I can use the Z register to select what part of code to run. Then, every time /STROBE goes down, it triggers an interrupt; I now what is expected and execute only the good part of code.

I also decided to use a circular buffer in RAM; the X pointer is used to store the data, while another part of the code reads the buffer via the Y pointer and sends the data.

One important thing to consider is the RAM doesn't start at address 0, but at 0x060 and ranges up to 0x160. This has to be taken care of when rotating the address. To simplify and speed up, I truncate the address of the RAM buffer to 255 (0xff), which provides 160 bytes for buffering (0x060-0x0ff). This could sound short, but remember this for one output only, so 160 bytes of backlog for MIDI is quite a lot indeed!

...
; =========================================================
; Interrupt vectors
; =========================================================
Reset:          rjmp    Init
INT1addr:       rjmp    strobe
...
; =========================================================
; Configurations
; =========================================================
Init:
...
    ldi     ZH, hi8(S1)
    ldi     ZL, lo8(S1)             ; Initialize the state machine
    ldi     r22,    0x04            ; Prepare the unit counter
...
; ==========================================
; Main loop: check if there some data
; ready to be sent in the RAM buffer
; ==========================================
Main:
    cpse    XL, YL                  ; If X = Y, no data awaiting
    rjmp    Main                    ; Loop

; ---- data awaiting to be sent via MIDI
    ld      r23, Y+                 ; Load from RAM buffer, increment Y
wait_usart:
    sbis    UCSRA, UDRE             ; Wait USART Data Register empty
    rjmp    wait_usart
    STORE   UDR, r23                ; Store the byte to send in the USART Data Register
    
    andi    YL, 0xff                ; Make sure Y never exceeds 255
    ori     YL, 0x60                ; but at least 96
    andi    YH, 0x00

    rjmp    Main                    ; Loop

; =========================================================
; Interrupt Service Routine (ISR)
; =========================================================
; ---------------------------------
; /STROBE is detected: depending of the current state, perform action and setup the next state.
; ---------------------------------
strobe:
    ijmp                ; Indirect jump to the good state
S1:
    cli
    ldi ZH, hi8(S2)
    ldi ZL, lo8(S2)
    sei
    reti
S2:
    cli
    ldi ZH, hi8(S3)
    ldi ZL, lo8(S3)
    sei
    reti
S3:
    cli
    ldi ZH, hi8(S4)
    ldi ZL, lo8(S4)
    sei
    reti
S4:
    cli
    LOAD  r16,   PORTD
    sbr   r16,   (1<<BUSY)
    STORE PORTD, r16    ; Raise BUSY
    ; Reset the RAM buffer
    clr XH
    ldi XL, 0x60        ; Set register X to 0x60
    clr YH
    ldi YL, 0x60        ; Set register Y to 0x60
    ldi r22, 0x04       ; Prepare the unit counter
    ldi ZH, hi8(S5)
    ldi ZL, lo8(S5)
    sei
    reti
S5:	; Reading the number of bytes coming next
    cli
    in  r21, PORTB      ; Read counter from Port B

    LOAD    r16,   PORTD
    cbr     r16,   (1<<BUSY)
    STORE   PORTD, r16  ; Reset BUSY

    cpi  r21, 0x00      ; Data coming next ?
    breq end            ; No -> end

    cpi	r22, ME			; Are these data for me ?
    breq next           ; Yes -> prepare to read
    ldi	ZH, hi8(S61)    ; No -> prepare to skip the bytes
    ldi	ZL, lo8(S61)
    sei
    reti
next:   ; On the next /STROBE, we must read data. We prepare Z for ijmp.
    ldi ZH, hi8(S62)    ; The next bytes are data
    ldi ZL, lo8(S62)
    sei
    reti
end:    ; No data is coming next for this unit. We decrement the unit number. On the last unit (r22 = 0), we reset the state machine.
    dec	r22             ; Prepare for next unit
    breq restart        ; If last unit, prepare for next
    sei
    reti

S61:    ; Skipping the incoming bytes
    cli

    dec	r21             ; Decrement counter
    breq eod            ; End of data
    sei
    reti
S62:    ; Reading and storing the incoming bytes
    cli

    in    r20, PORTB    ; Read data from Port B
    st    X+, r20       ; Store in RAM buffer, increment X
    andi  XL, 0xff      ; Make sure X never exceeds 255
    ori   XL, 0x60      ; but is at least 96
    andi  XH, 0x00
    dec   r21           ; Decrement counter
    breq  eod           ; End of data
    sei
    reti
eod:    ; Either the data were read or skipped, this is the end of data
    dec   r22           ; Prepare for next unit
    breq restart        ; If last unit, reset the state machine
    ldi  ZH, hi8(S5)    ; The next incoming byte is a counter
    ldi  ZL, lo8(S5)
    sei
    reti

restart:  ; restart the state machine
    ldi ZH, hi8(S1)     ; Reset to S1
    ldi ZL, lo8(S1)
    sei
    reti
  

Follow up:  Part 1  Part 2  Part 3  Part 4  Part 5  Part 6 .

Comments

Popular posts from this blog

Drive replacement for Fostex DMT8-vl

The IDE hard drive on my Fostex DMT8-vl multitrack recorder shows signs of its imminent death; when getting hot, I could not record anymore. Must be said this drive comes from an old Sun Station, and has been replaced because I/O failures were detected by Solaris. It worked at least 5 years in my recorder: not so bad. However, time is now to replace it. The DMT8-vl is not able to handle drives bigger than 8.4 GB. Well, it is able to (the current drive is 15 GB), but only 8.4 GB will be usable. My tought was to use a 8 GB CompactFlash; having no moving parts means no noise, which is quite temptating for a music recording device. I purchased a CompactFlash-IDE adapter on the internet (8$) and I had to build a male-male IDE cable adapter (4$). Unfortunately, this doesn't work. The drive is correctly discovered by the operating system, which proposes to format it ("format IDE?"). After answering "yes", the formating runs pretty fast (faster than on a real drive), ...

Samba: Clients get "system error 1223" (or 123) after a server reboot

Facts: a Linux+Samba server shares anonymously a folder. After a reboot, Win clients could not attach the share drive anymore. C:\>net use \\mylinux\folder Enter the user name for 'mylinux': System error 1223 has occurred. The operation was canceled by the user. C:\>net view \\mylinux\ System error 123 has occurred. The filename, directory name, or volume label syntax is incorrect. The process are present, and tcpdump doesn't provide much information. What's going on? After hours of headscratching, the light came: the firewall was on and no rules for the Samba protocol! Grrr!

Issue with Soundpool MO4

I have a Atari STe with a Soundpool MO4 MIDI extension. It used to work very well, but unfortunatelly doesn't anymore: Cubase still detects it, and I can output MIDI to it but nothing is coming out from any MIDI Out. It took me a while to tackle it (lack of time, lack of tool, other items to play with), but I gave a glance last week-end. The parallel port on the Atari uses only the following signals: Pin 1 : Strobe (Atari -> MO4) Pin 2 : Data 0 (Atari -> MO4) Pin 3 : Data 1 (Atari -> MO4) Pin 4 : Data 2 (Atari -> MO4) Pin 5 : Data 3 (Atari -> MO4) Pin 6 : Data 4 (Atari -> MO4) Pin 7 : Data 5 (Atari -> MO4) Pin 8 : Data 6 (Atari -> MO4) Pin 9 : Data 7 (Atari -> MO4) Pin 11: Busy (MO4 -> Atari) The MO4 also decodes few other pins, but since the Atari doesn't, my guess is the MO4 was also targeted for PC. Inside the box, the MO4 is architectured around a CPLD (IspLSI1016 from Lattice) which contains the logi...