o <circular_boss> sub
    ; front end for slot_pocket
    ; current as of 2017-11-25 1100
    ; input
    ;    #<_ChannelWidth>
    ;    #<_CoolantFinish>
    ;    #<_CoolantEndRough>
    ;    #<_CoolantSideRough>
    ;    #<_Diameter>
    ;    #<_DOCFinish>
    ;    #<_DOCEndRough>
    ;    #<_DOCSideRough>
    ;    #<_FeedFinish>
    ;    #<_FeedEndRough>
    ;    #<_FeedSideRough>
    ;    #<_MinutesHere>
    ;    #<_OldA>
    ;    #<_RampAngle>       from options
    ;    #<_RampFeedFactor>  from options
    ;    #<_ShowProgress>
    ;    #<_SpeedFinish>
    ;    #<_SpeedEndRough>
    ;    #<_SpeedSideRough>
    ;    #<_ToolOD>          from <set_tool>
    ;    #<_ToolRadius>      from <set_tool>
    ;    #<_TotalMinutes>
    ;    #<_WOCFinish>
    ;    #<_WOCEndRough>
    ;    #<_WOCSideRough>
    ;    #<_XCenter>
    ;    #<_YCenter>
    ;    #<_ZBottom>
    ;    #<_ZClear>
    ;    #<_ZTop>

;    plan (Oh 640-690)
;      ramp to ZBottom
;      flatten base of ramp rough
;      flatten base of ramp finish
;      radial passes of <= RoughWOC
;        downward passes of <= RoughDOC
;        flatten bottom by FinishDOC
;      in to finished radius
;      finishing circuit at ZBottom

    #<ChannelOD> = [#<_Diameter> + 2 * #<_ChannelWidth>]
    #<RampID> = [#<ChannelOD> - 2 * #<_ToolOD>]
    #<_RampPathRadius> = [#<RampID>/2 + #<_ToolRadius>]
    #<TotalRoughDOC> = [#<_ZTop> - #<_ZBottom> - #<_DOCFinish>]
    ; (debug, RampPathRadius = #<_RampPathRadius>)

    ; ****** ramp computation ***********************************
    #<RampCircumference> = [2 * 3.14159 * #<_RampPathRadius>]
    #<DepthPerTurn> = [#<RampCircumference> * tan[#<_RampAngle>]]
    #<_RampTurns> = 0
    #<RampLength> = 0
    o 642 if [#<TotalRoughDOC> GT 0]
      #<_RampTurns> = [fup[#<TotalRoughDOC> / #<DepthPerTurn>]]
      #<RampLength> = [#<TotalRoughDOC> / sin[#<_RampAngle>]]
    o 642 endif

    ; ****** radial passes ********************************************
    #<_Radius> = [#<_Diameter> / 2]
    #<TotalRoughWOC> = [#<RampID> / 2 - [#<_Radius> + #<_WOCFinish>]]
    #<_VerticalPasses> = 0
    #<_WOC> = 0
    #<_DOC> = 0
    #<_RadialPasses> = 0
    o 645 if [#<TotalRoughWOC> GT 0]
        #<_RadialPasses> = [fup[#<TotalRoughWOC> / #<_WOCSideRough>]]
        #<_WOC> = [#<TotalRoughWOC> / #<_RadialPasses>]
        o 6451 if [#<TotalRoughDOC> GT 0]
          #<_VerticalPasses> = [fup[#<TotalRoughDOC> / #<_DOCSideRough>]]
          #<_DOC> = [#<TotalRoughDOC> / #<_VerticalPasses>]
        o 6451 endif
    o 645 endif
    ; (debug, TotalRoughWOC = #<TotalRoughWOC>, RadialPasses = #<_RadialPasses>)
    ; ****** progress computation **************************************
    #<ProgressHere> = 0
    o 650 if [#<_ShowProgress>]                                                ; progress increments
        #<ProgressHere> = [100 * #<_MinutesHere> / #<_TotalMinutes>]      ; ..
    o 650 endif

    #<RampCircuits>     = [#<_RampTurns> + 2]
    #<RadialCircuits>   = [#<_RadialPasses> * [#<_VerticalPasses> + 1]]
    #<FinishingCircuit> = 1
    #<TotalCircuits>    = [#<RampCircuits> + #<RadialCircuits> + #<FinishingCircuit>]
    #<_ProgPerCircuit>  = [#<ProgressHere> / #<TotalCircuits>]
    #<_ProgRamp>        = [#<_RampTurns> * #<_ProgPerCircuit>]

; ********** action ***********************************************************
;
    S [#<_SpeedEndRough>] F [#<_FeedEndRough>]    ; must say something
    M3                                            ; spindle on
    o 655 if [#<_RampTurns> GT 0]                 ; * not already close of ZBottom
        M98 P660                                  ; .. ramp
    o 655 endif                                   ; ..
    o 656 if [#<_RadialPasses> GT 0]              ; * not already near desired radius
        M98 P670                                  ; .. radial passes inward
    o 656 endif                                   ; ..
    M98 P680                                      ; final finishing cut
    G00 Z [#<_ZClear>]                            ; neutral position
    M5 M9                                         ; spindle & coolant off
o <circular_boss> endsub                          ; return to caller

; ********** end action ***********************************************************

    o 660 ; ********************** ramp
        ; uses
        ;   ProgRamp
        ;   ProgRampBase
        ;   RampPathRadius
        ;   RampTurns
        ; starts unknown
        (msg, ramp)
        G00 Z [#<_ZClear>]                                        ; define position
        G00 X [#<_XCenter> + #<_RampPathRadius>] Y [#<_YCenter>]  ; .. above 3 o'clock
        S [#<_SpeedEndRough>]                                     ; .. roughing
        F [#<_RampFeedFactor> * #<_FeedEndRough>]                 ; ..
        o <coolant> call [#<_CoolantEndRough>]                    ; ..
        #<_OldA> = [#<_OldA> + #<_ProgRamp>]                      ; progress
        G00 Z [#<_ZTop>]                                          ; start at top
        G02 X [#<_XCenter> + #<_RampPathRadius>] Z [#<_ZBottom> + #<_DOCFinish>] I [-#<_RampPathRadius>] P [#<_RampTurns>] A [#<_OldA>]

        (msg, ramp bottom)
        F [#<_FeedEndRough>]                                      ; flatten ramp bottom
        #<_OldA> = [#<_OldA> + #<_ProgPerCircuit>]                  ; progress
        G02 X [#<_XCenter> + #<_RampPathRadius>] I [-#<_RampPathRadius>] A [#<_OldA>]
        S [#<_SpeedFinish>] F [#<_FeedFinish>]                    ; finishing base of ramp
        o <coolant> call [#<_CoolantFinish>]                      ; ..
        G01 Z [#<_ZBottom>]                                       ; dig in
        #<_OldA> = [#<_OldA> + #<_ProgPerCircuit>]                  ; flatten ramp bottom
        G02 X [#<_XCenter> + #<_RampPathRadius>] I [-#<_RampPathRadius>] A [#<_OldA>]
        M99                                                       ; return to caller

    o 670 ; ********************** radial passes
        ; uses
        ;    DOC
        ;    ProgVertical
        ;    RadialPasses
        ;    RampPathRadius
        ;    VerticalPasses
        ;    WOC
        ; starts undefined or at bottom of ramp

        G00 Z [#<_ZClear>]                                 ; define position
         #<CutRadius> = [#<_RampPathRadius>]                ; ..
        G00 X [#<_XCenter> + #<CutRadius>] Y [#<_YCenter>] ; .. above 3 o'clock
        o 672 repeat [#<_RadialPasses>]                    ; next inward pass
           #<CutRadius> = [#<CutRadius> - #<_WOC>]         ; ..
           ; (debug, radial pass with radius #<CutRadius>)
           ; #<Remaining> = [2 * #<CutRadius> - #<_ToolOD>]
           ; (debug, remaining boss diameter will be #<Remaining>)
           o 6721 if [#<_VerticalPasses> GT 0]             ; (not ready for FinishDOC)
             S [#<_SpeedSideRough>] F [#<_FeedSideRough>]  ; .. roughing
             o <coolant> call [#<_CoolantSideRough>]       ; ..
             #<ZCut> = [#<_ZTop>]                          ; old cut
             G00 Z [#<ZCut>]                               ; rapid through old cut
             o 67211 repeat [#<_VerticalPasses>]           ;
               (msg,   vertical pass)
               #<ZCut> = [#<ZCut> - #<_DOC>]               ; .. new depth
               G01 Z [#<ZCut>]                             ; dig in
               #<_OldA> = [#<_OldA> + #<_ProgPerCircuit>]  ; progress
               G02 X [#<_XCenter> + #<CutRadius>] I [-#<CutRadius>] A [#<_OldA>]
             o 67211 endrepeat                             ; end of one vertical pass
           o 6721 endif                                    ; end of vertical roughing
           S [#<_SpeedFinish>] F [#<_FeedFinish>]          ; finishing bottom of radial pass
           o <coolant> call [#<_CoolantFinish>]            ; ..
           G01 Z [#<_ZBottom>]                             ; dig in
           ; (msg, radial bottom)
           #<_OldA> = [#<_OldA> + #<_ProgPerCircuit>]      ; progress
           G02 X [#<_XCenter> + #<CutRadius>] I [-#<CutRadius>] A [#<_OldA>]
       o 672 endrepeat                                     ; end of radial passes
       M99                                                 ; return to caller


    o 680 ; ********************** finishing the whole boss
        ; uses
        ;    ProgFinish
        ;    Radius
        ; starts undefined or at bottom somewhere
        ; (msg, finishing the boss)
        S [#<_SpeedFinish>] F [#<_FeedFinish>]             ; finishing
        o <coolant> call [#<_CoolantFinish>]               ; ..
        #<CutRadius> = [#<_Radius> + #<_ToolRadius>]       ; cut at target surface
        G00 Z [#<_ZClear>]                                 ; rapid through old cut
        G00 X [#<_XCenter> + #<CutRadius>] Y [#<_YCenter>] ; ..
        G00 Z [#<_ZBottom> + #<_DOCFinish>]                ; ..
        G01 Z [#<_ZBottom>]                                ; dig in
        #<_OldA> = [#<_OldA> + #<_ProgPerCircuit>]         ; progress
        G02 X [#<_XCenter> + #<CutRadius>] I [-#<CutRadius>] A [#<_OldA>]
        M99
