How to Read the Speaker Countdown Value to Store It?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

How to Read the Speaker Countdown Value to Store It?

Post by ~ »

How can I read the current value of the PC speaker? I want to be able to reuse the default sound frequency.

Is there some command for it?

Where can I find a list of commands for the speaker device?

I only seem to know about the command to write the countdown value, something like this, but I specifically want to retrieve and save the current MSB:LSB countdown values for the speaker by reading ports or using some speaker command:

Code: Select all

beep:
  pusha
  pushf

;Write the sound frequency:
;;
 mov al,0xB6  ;Command -- Write MSB:LSB countdown value
 out 0x43,al
 mov al,00h    ;Port byte LSB
 out 0x42,al
 mov al,128      ;Port byte MSB
 out 0x42,al   ;;;;;MSB:LSB


;Configure the state of the status port:
;;
 in al,61h        ;Read the state of the port
 or al, 00000011b ;Set to 1 bits 0 and 1 to enable the speaker
 out 61h,al       ;Write the new value


;A short delay:
;;
 mov cx,0FFFFh
 l1:
 loop l1


;Disable the port:
;;
 in al,61h           ;Read the state of the port
 and al,11111100b    ;Clear to 0 bits 0 and 1, to disable the speaker
 out 61h,al

  popf
  popa
         ret

Last edited by ~ on Tue Jun 27, 2017 11:06 pm, edited 1 time in total.
User avatar
BrightLight
Member
Member
Posts: 901
Joined: Sat Dec 27, 2014 9:11 am
Location: Maadi, Cairo, Egypt
Contact:

Re: How to read the Speaker Countdown Value?

Post by BrightLight »

I'm not sure about this as I myself have never tried playing with it, but you might want to read the Wiki entry on the PIT, specifically sections Counter Latch Command and Reading The Current Count.
You know your OS is advanced when you stop using the Intel programming guide as a reference.
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: How to Read the Speaker Countdown Value to Store It?

Post by ~ »

I read the code example in page 621 of "The Indispensable PC Hardware Book", Fourth Edition, by Hans Peter Messmer. The code I produced is almost exactly the same, but mine is more reusable and packed in a clean routine.

I have made a manual test and it looks like I can replay the default speaker beep sound if I feed it back the countdown or counter MSB:LSB value from channel 1 that I get with this function. It's supposed that the speaker PIT channel should be set to work in binary mode, not BCD, and one should ensure that it has previously been programmed. But if we know that it's present then it should be already preconfigured by the BIOS.

So it seems that the following routine is capable of saving the current countdown value of the speaker:

Code: Select all

;Return value:
;            AH:AL -- MSB:LSB countdown value
;;
PC_Speaker__Save_Current_Countdown_Value:
  pushf
  push widebx

  ;Read the frequency of counter 1
  ;(indicated in bits 7-6),
  ;supposedly the speaker PIT counter:
  ;;
   mov al,01000000b    ;Specify channel 1
   out 0x43,al
   in al,0x42          ;Read LSB of countdown and put in BL
   mov bl,al
   in al,0x42          ;Read MSB of countdown and put in BH
   mov bh,al



  ;Put MSB:LSB in AX:
  ;;
   xchg bx,ax



  pop widebx
  popf
retwide


It would need extensive testing to ensure that this is actually the right code the PC hardware expects, but in the test I made, it worked.

Here is the full test program I made for my kernel. It allowed me to manually test if it was channel 2 that I had to read, channel 1 or channel 0, but it seems to be channel 1 for the speaker:

Code: Select all

;v2017-06-27
;
;Beep for a moment with the PC speaker.
;;


;If our program base address is 0, it will tell the kernel
;to relocate it elsewhere. If it's not 0, then the kernel
;will load the program to that physical address:
;;
 APPBASE equ 1048576*4

bits 32
org APPBASE
_x86_Portable__PLATFORMBITS_ equ 32
%define _x86_Portable__PLATFORMBITS_ 32

 %include "00000000__x86_Portable.asm"


;Our kernel must read this address, jump
;to it and then skip it:
;;
 IntendedBaseAddress  dq APPBASE
 NumberOfFieldsBelow  dq 8
 CommandLinePtr       dq 0
 CommandLineLength    dq 0
 CommandLineParamsPtr dq 0
 KernelFnExportsTable dq 0
 KernelVrExportsTable dq 0
 AppFnImportsTable    dq myImportsFunctionsTable
 AppVrImportsTable    dq 0
 RawExeEntryPoint     dq _ENTRY


SimpleCodeRelocationsTable:
 SimpleCodeRelocationsCount ww (SimpleCodeRelocationsTable_SZ/WIDEWORD_SZ)-1
 
 ;ww MainProgram__coderelocation_0000_clearScreen
SimpleCodeRelocationsTable__END:
SimpleCodeRelocationsTable_SZ equ (SimpleCodeRelocationsTable__END-SimpleCodeRelocationsTable)


SimpleDataRelocationsTable:
 SimpleDataRelocationsCount ww (SimpleDataRelocationsTable_SZ/WIDEWORD_SZ)-1
 
 ;ww
SimpleDataRelocationsTable__END:
SimpleDataRelocationsTable_SZ equ (SimpleDataRelocationsTable__END-SimpleDataRelocationsTable)




 %include "00000000__x86_Portable_2.asm"

 %include "include/external_exported_variables.inc"
 %include "include/external_exported_functions.inc"



;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++




align 16
db "START Debug Strings"
align 16

_ENTRY:



;Clean the screen:
;;
 ;MainProgram__coderelocation_0000_clearScreen equ $
 call wideword[clrscr]


call beep




;Return control to the kernel:
;;
 ret









beep:
;  pusha
;  pushf

;Read the frequency of counter 2 (bits 7-6),
;supposedly the PIT counter:
;;
 mov al,01000000b
 out 0x43,al
 in al,0x42
 mov bl,al
 in al,0x42
 mov bh,al




;Write the sound frequency:
;;
mov al,0xB6  ;Command -- Write MSB:LSB countdown value
out 0x43,al
;mov al,00h    ;Port byte LSB
mov al,bl    ;Port byte LSB
out 0x42,al
;mov al,128      ;Port byte MSB
mov al,bh      ;Port byte MSB
out 0x42,al   ;;;;;MSB:LSB


;Configure the state of the status port:
;;
in al,61h        ;Read the state of the port
or al, 00000011b ;Set to 1 bits 0 and 1 to enable the speaker
out 61h,al       ;Write the new value

ret


;A short delay:
;;
mov cx,0FFFFh
l1:
loop l1


;Disable the port:
;;
in al,61h           ;Read the state of the port
and al,11111100b    ;Clear to 0 bits 0 and 1, to disable the speaker
out 61h,al

  popf
  popa
         ret














align 16
db "Debug Strings END"




align 16
myImportsFunctionsTable:
                   ImportsCount ww 1
       clrscr                   ww clrscr@KernelCore















align 16
db 0,0,0,0,0,0,0,0,0,0,0,0,"EOF",0


;EOF

User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: How to Read the Speaker Countdown Value to Store It?

Post by ~ »

I have other speaker-controlling functions here. Please tell me if you can see some problem or weakness.

The only question I have is, can the speaker really be set to BCD countdown mode or the different generator modes of the regular PIT? If so, maybe some other functions will need to be written to ensure that it's working in binary countdown mode at least usually. I seem to remember that the speaker could be programmed in different modes for more automatic sound reproduction, so these functions wouldn't be enough.

Code: Select all

;Inputs:
;            AH:AL -- MSB:LSB countdown value
;;
PC_Speaker__Set_Sound_By_Countdown:
 pushf
 push wideax

 ;Write the countdown value:
 ;
 ;                 1193180 
 ;    COUNTDOWN = ---------
 ;                FREQUENCY
 ;
 ;
 ;                 1193180 
 ;    FREQUENCY = ---------
 ;                COUNTDOWN
 ;
 ;;
  push wideax
  mov al,0xB6  ;Command -- Write MSB:LSB countdown value
  out 0x43,al  ;Write PIT Mode/Command register (W)
  pop wideax



 out 0x42,al   ;Write LSB to PIT channel 2 data port (R/W)

 shr wideax,8  ;Get MSB in AL
 out 0x42,al   ;Write MSB to PIT channel 2 data port (R/W)



 pop wideax
 popf
retwide

Code: Select all

;Inputs:
;            EAX -- Frequency value
;            ECX -- 1 round up
;;
PC_Speaker__Set_Sound_By_Frequency:
 pushf
 push wideax
 push widebx
 push widedx

 ;Calculate the countdown for the frequency value:
 ;
 ;              1193180 
 ; COUNTDOWN = ---------
 ;             FREQUENCY
 ;
 ;;
  xor  edx,edx      ;Form EDX:EAX with EDX to 0, EDX:EAX==1193180
  xchg eax,ebx      ;Put EAX argument, in EBX, EBX==FREQUENCY
  mov  eax,1193180  ;Use default PIT frequency, EDX:EAX==1193180
  div  ebx          ;  EDX:EAX / EBX -- EAX result, EDX remainder

  cmp ecx,1         ;See if we want to round up
  jne .noroundup    ;If not, skip adding 1
  cmp edx,0
  je  .noroundup    ;If remainder==0, don't round up
   inc eax          ;If so, add 1 to round up

  .noroundup:




 ;Write the countdown value:
 ;
 ;                 1193180 
 ;    COUNTDOWN = ---------
 ;                FREQUENCY
 ;
 ;
 ;                 1193180 
 ;    FREQUENCY = ---------
 ;                COUNTDOWN
 ;
 ;;
  push wideax
  mov al,0xB6  ;Command -- Write MSB:LSB countdown value
  out 0x43,al  ;Write PIT Mode/Command register (W)
  pop wideax



 out 0x42,al   ;Write LSB to PIT channel 2 data port (R/W)

 shr wideax,8  ;Get MSB in AL
 out 0x42,al   ;Write MSB to PIT channel 2 data port (R/W)



 pop widedx
 pop widebx
 pop wideax
 popf
retwide



Code: Select all

PC_Speaker__enable:
 pushf
 push wideax

 ;Configure the state of the status port:
 ;;
  in al,61h        ;Read the state of the port.
  or al, 00000011b ;Set to 1 bits 0 and 1 to enable the speaker
                   ;of the KBC Port B Control Register (system control port).
                   ;Bits:
                   ;    0 -- PIT channel 2 gate used to enable/disable the speaker.
                   ;    1 -- Speaker Data Enable.

  out 61h,al       ;Write the new value.



 pop wideax
 popf
retwide


Code: Select all

PC_Speaker__disable:
 pushf
 push wideax

 ;Disable the port:
 ;;
  in al,61h        ;Read the state of the port.
  and al,11111100b ;Clear to 0 bits 0 and 1, to disable the speaker
                   ;of the KBC Port B Control Register (system control port).
                   ;Bits:
                   ;    0 -- PIT channel 2 gate used to enable/disable the speaker.
                   ;    1 -- Speaker Data Enable.

  out 61h,al       ;Write the new value.


 pop wideax
 popf
retwide

LtG
Member
Member
Posts: 384
Joined: Thu Aug 13, 2015 4:57 pm

Re: How to Read the Speaker Countdown Value to Store It?

Post by LtG »

Did I misunderstand, or why would you ever want to use BCD (Binary Coded Decimal)?

AFAIK BCD's main use is for displaying to humans, you're driving a PIT, why would you use BCD?
Post Reply