Home AMX User Forum AMXForums Archive Threads Residential Forum

McIntosh MX120 or MX136

Any one out there got a decent module for one of these AVRs that they are willing to give away, sell, trade, barter or let me steel. This is likely a one time usage and I really, really don't want to start from scratch.

Comments

  • DHawthorneDHawthorne Posts: 4,584
    This is an old module that I keep dressing up every time I do a new job, but essentially hasn't changed much since I wrote it for an Axcent3. So ... some of the paradigms are not only dated, but awkward. I've just never seen the need to redo it.

    Source selections, surround mode, and volume ramping and are button channels on the touch panel. You specify whether you are controlling zone A or B with channel 201 (on = B) on the port the McIntosh is connected to (the actual serial port). Passthru, such as it is, is done by setting the string passed to the module with the command you want the Mac to receive; I recently added the ability to use it for discrete volume as well. It's not extensively commented, but you should be able to figure it out. I've also zipped up the original Mac protocol for your reference (or in case you decide to do it properly yourself).
  • viningvining Posts: 4,368
    Dave,

    Very much appreciated!

    Thanks

    Dan
  • a_riot42a_riot42 Posts: 1,624
    It's interesting to see how other programmers do things compared to yourself, especially since Netlinx is not my first language. I am curious about the code in the function that sends the strings to the MX.
    DEFINE_CALL 'Send MX13X'
    {
        STACK_VAR CHAR sMX_Command[25]
    
        IF(bMX_Busy == FALSE)
        {
            sMX_Command = REMOVE_STRING(sMX_Command_Que,"')'", 1)
            {
                DEBUG("'SENDING dvMX13X: ', sMX_Command") ;
                SEND_STRING dvMX13X, "sMX_Command"
                bMX_Busy = TRUE ;
                WAIT MX_SERIAL_DELAY 'MX13X_BUSY'
                    bMX_Busy = FALSE ;
            }
        }
    }
    


    This is how I would write it.
    
    define_function sendCmd (integer nMX132Cmd)
    {
      if (isIdle)	
      {
        isIdle = 0
        send_string dvMX132, "sMX132Cmds[nMX132Cmd],13"
        wait MX_CMD_DELAY
          isIdle = 1
        if (DEBUG)
        {
           print("'Sending command to Mac Rcvr:'")
           print("sMX132Cmds[nMX132Cmd] = ',sMX132Cmds[nMX132Cmd]")
           print("'nMX132Cmd = ',itoa(nMX132Cmd)")
        }
      }
      else
      {
        print("'McIntosh MX132:  Currently processing commands.  Command ', sMX132Cmds[nMX132Cmd], ' ignored'")
      }
    }
    

    My thoughts on this:
    I don't see the point of using DEFINE_CALL ever.
    var == FALSE/TRUE is bad form in most languages.
    You don't immediately set bMX_Busy = TRUE after entering the if statement, which means if another button event happens before it is set you could get more than one command sent immediately after one another with no wait triggered since this code:
    DEFINE_PROGRAM
    
    IF(FIND_STRING(sMX_Command_Que,"')'", 1))
        CALL 'Send MX13X'                   // Send next command in queue
    
    
    could potentially call 'SendMX13X' many times a second if the buffer is full. This is a worry if you are in a multi-threaded environment.

    No point in naming a wait you never cancel, and in this case you would never want to cancel it.
    Why did you use the chardm command? I wonder if using it cures the possible successive commands sent with no delay but at the expense of response time.
    I don't use the chardm but leave 0.5 second wait between commands and ignore all others and volume etc ramp quite quickly.
    Did your MX lock up if you didn't use chardm?

    One thing I have noticed AMX programmers do is write much more in ALL CAPS which I find so much harder to read. Why is that?
    Thanks!
    Paul
  • a_riot42 wrote:
    I don't see the point of using DEFINE_CALL ever.

    That appears to be the consensus - it is archaic and unnecessary.
    a_riot42 wrote:
    var == FALSE/TRUE is bad form in most languages.

    I beg to differ - this form of coding makes it absolutely clear what you mean. Anything you type that makes things clearer is a good thing. I go further and specify things like...
    bMuted = 1
    bUnMuted = 0
    
    if (bMyMuteVariable = bMuted) {etc}
    

    ...so that you can never be unsure what your code means, especially when 1 means "Off"!
    a_riot42 wrote:
    You don't immediately set bMX_Busy = TRUE after entering the if statement, which means if another button event happens before it is set you could get more...

    ...This is a worry if you are in a multi-threaded environment.

    I understand where you are coming from on this, and I have struggled with it many times.

    When writing AMX code you need to understand that (to a first approximation) it is NOT multithreaded. You can *always* assume that one line of your code will never interrupt two other consecutive lines of your code. Everything takes its turn. There are some things which happen "in the background" but you can safely disregard them as long as you understand them.
    a_riot42 wrote:
    No point in naming a wait you never cancel, and in this case you would never want to cancel it.

    Good practice is to always name a wait because good practice is always to cancel it before you start it. That way you never get bitten later.
    a_riot42 wrote:
    Why did you use the chardm command? I wonder if using it cures the possible successive commands sent with no delay but at the expense of response time.
    I don't use the chardm but leave 0.5 second wait between commands and ignore all others and volume etc ramp quite quickly.
    Did your MX lock up if you didn't use chardm?

    chardm controls the interval between bytes, not the interval between commands. I've never used it. I imagine it is historical to allow for controlled gear with old or non-existent UARTS.

    The best way to handle command timing is to wait for the ACK before you send the next one. You'll need a queue.
    a_riot42 wrote:
    One thing I have noticed AMX programmers do is write much more in ALL CAPS which I find so much harder to read. Why is that?

    That is historical, going back to Axcess days; if you don't know what that means, be happy. None of my code is CAPS; like you, I use the CaseConventionOfMyTraining.
  • DHawthorneDHawthorne Posts: 4,584
    I use DEFINE_CALL whenever a return value is not needed. It's just habit from when it was the only way to do it. Since it's not a harmful habit, I never bothered to break myself of it.

    Likewise the caps thing - in Axcent, everything was in caps - I used to code with the caps lock key on. Nowadays, I use all caps for two things: constants and keywords. It's not necessary at all, but in my mind it makes those things stand out so I know exactly what they are at a glance. Yes, I know the editor color codes key words, but for me the caps registers before the color does.

    I name every WAIT, as a matter of policy. It doesn't hurt to do so, and I don't need to worry about collisions. If you do not name your waits, and you call a second one while the first is still active, it won't fire until the first has. You might think this is unlikely, but I have seen it happen, and naming waits solves it.

    CHARDM is there because of hardware issues. It controls the pacing of individual bits in the serial port for devices that cannot handle them sent too quickly. It is not the pacing between strings, but between every single character of the string. There is no point at all writing a routine for this when there is a built-in solution. The early McIntosh products required that ... and I have left it in from sheer lack of will to see if they are still needed. Besides, it's presences ensures the code will work on the older units, and doesn't hurt the newer ones.
  • DHawthorne wrote:
    If you do not name your waits, and you call a second one while the first is still active, it won't fire until the first has.

    A quick experiment shows this isn't so.
    program_name='fred'
    
    define_program
    
    wait 50
      {
    	send_string 0,"'50 ',time"
    	}
    
    wait 10
      {
    	send_string 0,"'10 ',time"
    	}
    
  • DHawthorneDHawthorne Posts: 4,584
    I'm sure I've been corrected on the wait thing before - for some reason, it keeps coming back to me as being that way from an early misunderstanding ... and by that, I mean that is how I understood it during Axcent3 training years ago. Since the practice is still a good one for the reasons you have mentioned, I'll stick with it and stand corrected on that point.
  • viningvining Posts: 4,368
    All un named waits are named by the processor at compile time so that multiple un named waits can exist in the wait queue at one time because they are indeed named or identifiable by the processor. Once in queue they can not be canceled because we have no way of knowing what the system assigned them for names where as waits that we ourselves name, well we know their names so we can.
  • a_riot42a_riot42 Posts: 1,624
    vining wrote:
    All un named waits are named by the processor at compile time so that multiple un named waits can exist in the wait queue at one time because they are indeed named or identifiable by the processor. Once in queue they can not be canceled because we have no way of knowing what the system assigned them for names where as waits that we ourselves name, well we know their names so we can.

    Won't cancel_all_waits cancel the waits? If what you say is true then how is it that I can have a wait 50 in mainline and it only runs once every 5 seconds rather than 200 times a second? It seems to me that an unnamed wait that gets called before it can do what its waiting to do gets collapsed.
    Paul
  • a_riot42 wrote:
    how is it that I can have a wait 50 in mainline and it only runs once every 5 seconds rather than 200 times a second?

    A given unnamed wait will not pile up but will execute one at a time. Don't ask why, just be grateful!
  • viningvining Posts: 4,368
    True, that should cancel everything but that's a system function I've never used and I can't think of an instance where I would want to. If I felt it necassary to cancel a wait I would simply name it and act upon that wait specifically. Cancelling all waits could be problamatic unless the system is small enough where you know where all the waits are and what they do. Most of the time I find it hard enough to remember what module or include file has major segement of code I'm looking for let alone how many waits there are and what they affect. To me cancel_all_waits is a dangerous and unnecassry command and should be avoided like the plague.

    a_riot42 wrote:
    If what you say is true then how is it that I can have a wait 50 in mainline and it only runs once every 5 seconds rather than 200 times a second?

    Once a wait is placed in queue keeping in mind that all waits are actually named the system will see that is is already in queue and not overwrite it because if that were not the case a wait in mainline would never execute it would simple be re-written on every pass.

    Basically when you call a wait the system checks the wait queue to see if that wait is already pending and if it is it ignores the wait and does nothing until the queued wait executes and expires but if the wait is not already in queue then it gets added.
  • vining wrote:
    cancel_all_waits is a dangerous and unnecessary... and should be avoided...

    Too right. How to bite yourself later.
  • Thread drift?

    I am starting to think we need a sticky thread concerning waits considering the topic appears so often.
    vining wrote:
    True, that should cancel everything but that's a system function I've never used and I can't think of an instance where I would want to.
    I have never used Cancel_All_Waits, but I can think of a way it might be useful. Consider a Master to Master system where each master is running its own code. There might be a time it could be used in a reset subroutine in order to sync up multiple systems. The trigger for the reset subroutine would come from an Online Data_Event from the remote system.
    vining wrote:
    Once a wait is placed in (the wait) queue, keeping in mind that all (unnamed) waits are actually named (at compile time), the system will see that it is already in (the wait) queue and not overwrite it...if that were not the case a wait in mainline (the Define_Program section) would never execute, it would simple be re-written on every pass.

    Basically when you call a wait, the system checks the wait queue to see if that wait is already pending...it ignores the (pending) wait and does nothing until the queued wait executes...if the wait is not already in queue then it gets added.
    This description by vining is absolutely correct and not open for dispute.
    a_riot42 wrote:
    No point in naming a wait you never cancel, and in this case you would never want to cancel it..
    Could not agree more.
  • a_riot42a_riot42 Posts: 1,624
    Doesn't this:
    vining wrote:
    All un named waits are named by the processor at compile time so that multiple un named waits can exist in the wait queue at one time because they are indeed named or identifiable by the processor. Once in queue they can not be canceled because we have no way of knowing what the system assigned them for names where as waits that we ourselves name, well we know their names so we can.


    completely contradict this:
    vining wrote:
    Once a wait is placed in queue keeping in mind that all waits are actually named the system will see that is is already in queue and not overwrite it because if that were not the case a wait in mainline would never execute it would simple be re-written on every pass.

    Basically when you call a wait the system checks the wait queue to see if that wait is already pending and if it is it ignores the wait and does nothing until the queued wait executes and expires but if the wait is not already in queue then it gets added.

    From the help file:
    AMX Help wrote:
    If a wait instruction that uses a name currently in the wait list is encountered, the new wait instruction is thrown away, so as not to conflict with the one currently in progress. If this feature is not desired, the current wait must be canceled before processing the new request. For information, refer to the Canceling Waits sub-section below.

    This seems to indicate that the NEW wait is thrown away, not the old one, when the system encounters another named wait of the same name. I always do a cancel_wait before calling a wait to avoid this behavior. However, with an unnamed wait, since you cannot explicitly cancel it, the system seems to cancel it for you.

    So if you use a named wait in define_program and cancel it what happens?
    ie:

    define_program

    cancel_wait 'wait for it'
    wait 50 'wait for it'
    print("'I've waited long enough'")


    Will this ever get printed? It shoudn't.
    Paul
  • a_riot42 wrote:
    So if you use a named wait in define_program and cancel it what happens?
    ie:
    define_program
    
    cancel_wait 'wait for it'
    wait 50 'wait for it'
      print("'I've waited long enough'")
    

    Will this ever get printed? It shoudn't.
    Correct Paul, print("'I've waited long enough'") will never run.

    Simply stated, only one instance of any wait, named or unnamed, can be pending in the wait queue at a time. Cancelling a named wait removes it from the wait queue and the underlying code will never execute.
  • viningvining Posts: 4,368
    a_riot42 wrote:
    Doesn't this:
    ........

    completely contradict this:
    .........
    Nope! I think both are accurate and re-enforce each other not contradict.
Sign In or Register to comment.