; CIS-261
; make_beeps.asm
; M09 demo: program that beeps
;
; @topic W100120 Lab M09 demo for Beep, Sleep, and GetLastError API calls
; @brief Lab M09, <a href="http://www.c-jump.com/bcc/c261c/MLabs/M09arrays/M09arrays.html" target="_blank">Data Arrays and Windows API Calls</a>, 
;
.586P
.MODEL FLAT         ; Flat memory model
option casemap:none ; Treat labels as case-sensitive

INCLUDE IO.H        ; header file for input/output

EXTERN _Beep@8:NEAR
EXTERN _GetLastError@0:NEAR
EXTERN _Sleep@4:NEAR

.CONST              ; Constant data segment
WHITE_SPACE        BYTE ' ', 0
PROPMT_FREQ        BYTE "Enter an array of frequencies:", 0
PROPMT_DURATION BYTE "Enter an array of durations:", 0
NEWLINE            BYTE 13, 10, 0

.STACK 100h         ; (default is 1-kilobyte stack)

.DATA               ; Begin initialized data segment
    buffer      BYTE    12 DUP (?), 0   ; input buffer for user interaction
    dtoa_buffer BYTE    11 DUP (?), 0

    frequency   DWORD   16 DUP (?)      ; array of frequencies in Hertz
    frequency_end EQU   OFFSET frequency + SIZEOF frequency
                DWORD   0               ; zero marks freq end

    duration    DWORD   16 DUP (50)     ; array sound duration in ms
    duration_end    EQU        OFFSET duration + SIZEOF duration

.CODE           ; Begin code segment
_main PROC      ; Beginning of code
repeat_freq_input:
    xor     edi, edi       ; set EDI = 0, to be used as array index
    output  NEWLINE
    output  PROPMT_FREQ
    output  NEWLINE
get_next_freq:
    input   buffer, 12
    szlen   buffer                  ; check the length of input
    or      eax, eax                ; if input is empty (EAX == 0)
    jz      repeat_duration_input   ; done with the input
    atod    buffer                  ; convert user input, result in EAX
    jno     @F                      ; Check the overflow flag
    ; Handle input error:
    ;...
    jmp        repeat_freq_input
@@:
    ; store freq or zero at the end of the array
    mov     frequency[ edi * 4 ], eax
    inc     edi                         ; increment array index
    lea     eax, frequency[ edi * 4 ]   ; set EAX equal address of the array element
    cmp     eax, frequency_end          ; out of bounds ?
    je      @F                          ; if yes, terminate the input loop
    jmp     get_next_freq
@@:
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; input duratioin array
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
repeat_duration_input:
    xor     edi, edi            ; set EDI = 0, to be used as array index
    output  PROPMT_DURATION
    output  NEWLINE
get_next_duration:
    input   buffer, 12
    szlen   buffer              ; check the length of input
    or      eax, eax            ; if input is empty (EAX == 0)
    jz      produce_sounds      ; done with the input
    atod    buffer              ; convert user input, result in EAX
    jno     @F                  ; Check the overflow flag
    ; Handle input error:
    ;...
    jmp        repeat_duration_input
@@:
    ; store freq or zero at the end of the array
    mov     duration[ edi * 4 ], eax
    inc     edi                      ; increment array index
    lea     eax, duration[ edi * 4 ] ; set EAX equal address of the array element
    cmp     eax, duration_end        ; out of bounds ?
    je      @F                       ; if yes, terminate the input loop
    jmp     get_next_duration
@@:
produce_sounds:
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; loop to produce sounds with Beep
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov cl, LENGTHOF frequency       ; loop counter
    mov esi, 0                       ; frequency array index

repeat_beep:
    mov eax, DWORD PTR frequency[esi*TYPE DWORD]
    or  eax, eax    ; if freq iz zero, stop
    jnz @F
    jmp repeat_freq_input
@@:
    mov ebx, DWORD PTR [duration]
    call make_beep
    or eax, eax
    jz @F           ; everything is okay
    ; report an error...
@@:
    inc esi         ; increment array index
    dec cl          ; decrement loop counter
    jnz repeat_beep ; repeat
    ret             ; Exit the program
    
_main ENDP

; POSTCONDITION: modifies ebx
; input params: eax freq
;               ebx duration
; returns: eax == 0 if no error, otherwise GetLastError result
make_beep PROC         ; sample procedure
    dtoa    dtoa_buffer, eax        ; convert 32-bit signed integer to string
    output  dtoa_buffer             ; print frequency
    output  WHITE_SPACE             ; print space

    ; preserve all gp registers
    pushad

    push ebx                        ; duration param
    push eax                        ; freq param
    call _Beep@8                    ; make sound

    ; EAX != 0 indicates succeess, error otherwise
    or eax, eax
    jnz @F                          ; success

    popad
    call _GetLastError@0
    ; EAX contains the error code to report
    ret
@@:
    pushd 300                       ; duration of sleep in MS
    call _Sleep@4

    ; restore all registers
    popad
    xor eax, eax    ; set EAX = 0
    ret             ; return from procedure
make_beep ENDP

END _main           ; Marks the end of the module and sets the program entry point label
