assemblyz80zxspectrum

How to read elements from a DEFB sequentially in Z80 Assembly language and then use the value retrieved at each iteration


I've been learning Z80 Assembly language, and I'm a bit stumped with this. The values in the DEFB represent musical pitches. The program creates a sequential loop, using the A register, starting at 0, and it should read the nth element of the DEFB, transfer the value into the HL register, and then BEEP the note. I cannot work out how to remove my hard-coding and get this to work:

ORG 30000

_MAIN
    LD A, 0         ;Set A to zero

LOOP_POINT:

    LD B, A         ;Load the current value of A into B to loop on
    CP 31           ;Check if the B value is 31
    JP Z, FINISHED      ;If yes, 32 notes have been played, so jump to FINISHED

    PUSH AF         ;Put current A value on the stack, because the "CALL 949" is going to overwrite it

    ;LD HL, (SOUND_BUFFER + A) - THIS DOES NOT WORK, I AM FORCED TO HARD CODE TO 855 FOR THIS EXAMPLE (BELOW)
    LD HL, 855      ;Load the pitch into HL
    LD DE, 24       ;Load the duration  into DE

    CALL 949        ;Will play the pitch in HL for the duration in DE

    POP AF          ;Restore the A value back from the stack

    INC A           ;Add 1

    CALL LOOP_POINT     

FINISHED:
    RET

SOUND_BUFFER                ;32 elements (each is a 1/4 note, so, 4*8 bars = 32 in total)

    DEFB "855,0,0,0,855,855,855,759,759,673,673,673,673,673,673,673,0,0,0,0,855,855,855,759,759,673,673,759,759,855,0,855"

Can anyone help me with the syntax or pointme in the right direction please?


Solution

  • You either have to calculate the pointer from index in A or just keep the pointer and update it as you go through the array. Below is my variant (based on your original code) - not checked, no guarantees. Note also that I've changed your DEFB to DEFW - I'm not familiar with your version of assembler, so this might be wrong, but since the pitch is represented by 16-bit values I would expect DEFW (or DW) to be used.

    EDIT: And, with the way things are coded, you need to compare A with 32, not 31. Otherwise the last note is not played.

    Hope this helps.

      ORG 30000
    
    _MAIN
      LD A, 0         ;Set A to zero
      LD HL,SOUND_BUFFER  ;Store pointer in HL
    
    LOOP_POINT:
    
      ;;;LD B, A         ;Load the current value of A into B to loop on
      CP 32           ;Check if the A value is 32
      JP Z, FINISHED      ;If yes, 32 notes have been played, so jump to FINISHED
    
      PUSH AF         ;Put current A value on the stack, because the "CALL 949" is going to overwrite it
    
      ;;;LD HL, 855      ;Load the pitch into HL
      LD E, (HL)      ;Load least significant byte into E
      INC HL          ;Bump pointer
      LD D,(HL)       ;Load most significant byte into D, DE contains the pitch 
      INC HL          ;Bump pointer
      PUSH HL         ;Save HL (pointing to next pitch)
      EX DE,HL        ;Move pitch from DE to HL
      LD DE, 24       ;Load the duration into DE
    
      CALL 949        ;Will play the pitch in HL for the duration in DE
    
      POP HL          ;Restore HL (pointing to next pitch)
      POP AF          ;Restore the A value back from the stack
    
      INC A           ;Add 1
    
      ;;;CALL LOOP_POINT     ; why CALL?
      JR LOOP_POINT   ; just jump
    
     FINISHED:
      RET
    
    SOUND_BUFFER                ;32 elements (each is a 1/4 note, so, 4*8 bars = 32 in total)
      DEFW 855,0,0,0,855,855,855,759,759,673,673,673,673,673,673,673,0,0,0,0,855,855,855,759,759,673,673,759,759,855,0,855