diff --git a/manual/luatex-languages.tex b/manual/luatex-languages.tex
index 11e8e6c9951f2b72b6217d9b1fe2ae2e4cabed5a..990a8dcc038d568ccf74fe8cc6686ab5bd8cfc38 100644
--- a/manual/luatex-languages.tex
+++ b/manual/luatex-languages.tex
@@ -345,21 +345,30 @@ character|-|handling code have been moved back inline. When \type
 {\tracingcommands} is on, this is visible because the full word is reported,
 instead of just the initial character.
 
-Because we tend to make hard codes behaviour configurable two new primitives have
-been added:
+Because we tend to make hard codes behaviour configurable a few new primitives
+have been added:
 
 \starttyping
-\automatichyphenmode
+\hyphenpenaltymode
 \automatichyphenpenalty
+\explicithyphenpenalty
 \stoptyping
 
 The first parameter has the following consequences for automatic discs (the ones
 resulting from an \type {\exhyphenchar}:
 
-\starttabulate[|T||]
-\NC 0 \NC assign \type {\exhyphenpenalty} \NC \NR
-\NC 1 \NC assign \type {\hyphenpenalty} \NC \NR
-\NC 2 \NC assign \type {\automatichyphenpenalty} \NC \NR
+\starttabulate[|Tc|l|l|]
+\BC mode \BC automatic disc \type{-}         \BC explicit disc \type{\-}         \NC \NR
+\HL
+\NC 0    \NC \type {\exhyphenpenalty}        \NC \type {\exhyphenpenalty}        \NC \NR
+\NC 1    \NC \type {\hyphenpenalty}          \NC \type {\hyphenpenalty}          \NC \NR
+\NC 2    \NC \type {\exhyphenpenalty}        \NC \type {\hyphenpenalty}          \NC \NR
+\NC 3    \NC \type {\hyphenpenalty}          \NC \type {\exhyphenpenalty}        \NC \NR
+\NC 4    \NC \type {\automatichyphenpenalty} \NC \type {\explicithyphenpenalty}  \NC \NR
+\NC 5    \NC \type {\exhyphenpenalty}        \NC \type {\explicithyphenpenalty}  \NC \NR
+\NC 6    \NC \type {\hyphenpenalty}          \NC \type {\explicithyphenpenalty}  \NC \NR
+\NC 7    \NC \type {\automatichyphenpenalty} \NC \type {\exhyphenpenalty}        \NC \NR
+\NC 8    \NC \type {\automatichyphenpenalty} \NC \type {\hyphenpenalty}          \NC \NR
 \stoptabulate
 
 other values do what we always did in \LUATEX: insert \type {\exhyphenpenalty}.
diff --git a/manual/luatex.pdf b/manual/luatex.pdf
index 9b609ca963110b20691314ee6fadf866ad56c222..19182e8764b5213147d626aa4348cfc74665493f 100644
Binary files a/manual/luatex.pdf and b/manual/luatex.pdf differ
diff --git a/source/texk/web2c/luatexdir/lang/texlang.w b/source/texk/web2c/luatexdir/lang/texlang.w
index c3d1ad203fb754d7b1acdd9968fc446a714eb4be..f000cfb5a3d0db1fdf4ae03aec68490b9adc6b70 100644
--- a/source/texk/web2c/luatexdir/lang/texlang.w
+++ b/source/texk/web2c/luatexdir/lang/texlang.w
@@ -321,13 +321,8 @@ void load_tex_hyphenation(int curlang, halfword head)
     load_hyphenation(get_language(curlang), (unsigned char *) s);
 }
 
-@ TODO: clean this up. The |delete_attribute_ref()| statements are not very nice,
-but needed. Also, in the post-break, it would be nicer to get the attribute list
-from |vlink(n)|. No rush, as it is currently not used much.
-
-@c
-halfword insert_discretionary(halfword t, halfword pre, halfword post,
-                              halfword replace, int penalty)
+@ @c
+halfword insert_discretionary(halfword t, halfword pre, halfword post, halfword replace, int penalty)
 {
     halfword g, n;
     int f;
@@ -417,24 +412,21 @@ halfword insert_syllable_discretionary(halfword t, lang_variables * lan)
     return n;
 }
 
-halfword insert_word_discretionary(halfword t, lang_variables * lan)
-{
-    halfword pre = null, pos = null;
-    if (lan->pre_exhyphen_char > 0)
-        pre = insert_character(null, lan->pre_exhyphen_char);
-    if (lan->post_exhyphen_char > 0)
-        pos = insert_character(null, lan->post_exhyphen_char);
-    return insert_discretionary(t, pre, pos, null,ex_hyphen_penalty_par);
-}
-
 @ @c
 halfword compound_word_break(halfword t, int clang)
 {
-    int disc;
-    lang_variables langdata;
-    langdata.pre_exhyphen_char = get_pre_exhyphen_char(clang);
-    langdata.post_exhyphen_char = get_post_exhyphen_char(clang);
-    disc = insert_word_discretionary(t, &langdata);
+    halfword disc = null;
+    halfword pre = null;
+    halfword pos = null;
+    halfword pre_exhyphen_char = get_pre_exhyphen_char(clang);
+    halfword post_exhyphen_char = get_post_exhyphen_char(clang);
+    if (pre_exhyphen_char > 0)
+        pre = insert_character(null,pre_exhyphen_char);
+    if (post_exhyphen_char > 0)
+        pos = insert_character(null,post_exhyphen_char);
+    disc = insert_discretionary(t,pre,pos,null,ex_hyphen_penalty_par);
+    subtype(disc) = automatic_disc;
+    set_automatic_disc_penalty(disc);
     return disc;
 }
 
@@ -443,7 +435,7 @@ halfword insert_complex_discretionary(halfword t, lang_variables * lan,
                                       halfword replace)
 {
     (void) lan;
-    return insert_discretionary(t, pre, pos, replace,hyphen_penalty_par);
+    return insert_discretionary(t,pre,pos,replace,hyphen_penalty_par);
 }
 
 halfword insert_character(halfword t, int c)
@@ -721,25 +713,6 @@ there was not the best idea ever.
 
 */
 
-#define check_automatic_disc(t) \
-switch (automatic_hyphen_mode_par) { \
-    case 0: \
-        /* we take ex_hyphen_penalty */ \
-        disc_penalty(t) = ex_hyphen_penalty_par; \
-        break ; \
-    case 1: \
-        /* we take hyphen_penalty */ \
-        disc_penalty(t) = hyphen_penalty_par; \
-        break ; \
-    case 2: \
-        /* we take automatic_hyphen_penalty */ \
-        disc_penalty(t) = automatic_hyphen_penalty_par; \
-        break ; \
-    default: \
-        disc_penalty(t) = ex_hyphen_penalty_par; \
-        break ; \
-} \
-
 static halfword find_next_wordstart(halfword r, halfword first_language, halfword strict_bound)
 {
     register int l;
@@ -794,9 +767,7 @@ static halfword find_next_wordstart(halfword r, halfword first_language, halfwor
                     */
                     t = vlink(r) ;
                     if ((start_ok == 0) && (t!=null) && (type(t) == glyph_node) && (character(t) != ex_hyphen_char_par)) {
-                        t = compound_word_break(r, char_lang(r));
-                        check_automatic_disc(t);
-                        subtype(t) = automatic_disc;
+                        compound_word_break(r, char_lang(r));
                         start_ok = 1 ;
                     } else {
                         start_ok = 0;
@@ -967,16 +938,13 @@ void hnj_hyphenation(halfword head, halfword tail)
                     set of explicit hyphens
                 */
                 halfword rr = r;
-                halfword t = null;
 #ifdef VERBOSE
                 formatted_warning("hyphenation","explicit hyphen(s) found in %s (c=%d)", utf8word, clang);
 #endif
                 while (rr != wordstart) {
                 if (is_simple_character(rr)) {
                         if (character(rr) == ex_hyphen_char_par) {
-                            t = compound_word_break(rr, clang);
-                            check_automatic_disc(t);
-                            subtype(t) = automatic_disc;
+                            compound_word_break(rr, clang);
                             while (character(alink(rr)) == ex_hyphen_char_par)
                                 rr = alink(rr);
                             if (rr == wordstart)
@@ -1011,13 +979,13 @@ void hnj_hyphenation(halfword head, halfword tail)
                     /* what is right overruns left .. a bit messy */
                 }
                 /* maybe an extra check ... */
-                /* if (left && right) { */
+                /* |if (left && right) {| */
 #ifdef VERBOSE
                     formatted_warning("hyphenation","hyphenate %s (c=%d,l=%d,r=%d) from %c to %c",
                         utf8word, clang, lhmin, rhmin, character(left), character(right));
 #endif
                     (void) hnj_hyphen_hyphenate(lang->patterns, wordstart, end_word, wordlen, left, right, &langdata);
-                /* } */
+                /* |}| */
             }
         }
         explicit_hyphen = false;
diff --git a/source/texk/web2c/luatexdir/tex/commands.w b/source/texk/web2c/luatexdir/tex/commands.w
index 1e223839b71509f3a26672e71762d081e69b2bd8..84235914c64a269e989dcd12f6e2611feeea99c1 100644
--- a/source/texk/web2c/luatexdir/tex/commands.w
+++ b/source/texk/web2c/luatexdir/tex/commands.w
@@ -157,8 +157,9 @@ void initialize_commands(void)
     primitive_luatex("pageheight", assign_dimen_cmd, dimen_base + page_height_code, dimen_base);
     primitive_luatex("pxdimen", assign_dimen_cmd, dimen_base + px_dimen_code, dimen_base);
     primitive_luatex("predisplaygapfactor", assign_int_cmd, int_base + math_pre_display_gap_factor_code, int_base);
-    primitive_luatex("automatichyphenmode", assign_int_cmd, int_base + automatic_hyphen_mode_code, int_base);
+    primitive_luatex("hyphenpenaltymode", assign_int_cmd, int_base + hyphen_penalty_mode_code, int_base);
     primitive_luatex("automatichyphenpenalty", assign_int_cmd, int_base + automatic_hyphen_penalty_code, int_base);
+    primitive_luatex("explicithyphenpenalty", assign_int_cmd, int_base + explicit_hyphen_penalty_code, int_base);
 
     /* Many of \TeX's primitives need no |equiv|, since they are identifiable
        by their |eq_type| alone. These primitives are loaded into the hash table
diff --git a/source/texk/web2c/luatexdir/tex/equivalents.h b/source/texk/web2c/luatexdir/tex/equivalents.h
index a97a6cf8b5a0b6e60aeea26134161721ca04ad81..d7ca036e25afa8caa832a79e3d0526b7c7396441 100644
--- a/source/texk/web2c/luatexdir/tex/equivalents.h
+++ b/source/texk/web2c/luatexdir/tex/equivalents.h
@@ -288,10 +288,11 @@ the |number_regs| \.{\\dimen} registers.
 #  define hyphenation_bounds_code 97
 #  define math_skip_mode_code 98
 #  define math_pre_display_gap_factor_code 99
-#  define automatic_hyphen_mode_code 100
+#  define hyphen_penalty_mode_code 100
 #  define automatic_hyphen_penalty_code 101
+#  define explicit_hyphen_penalty_code 102
 
-#  define math_option_code (automatic_hyphen_penalty_code+1)
+#  define math_option_code (explicit_hyphen_penalty_code+1)
 
 #  define mathoption_int_base_code (math_option_code+1)                 /* one reserve */
 #  define mathoption_int_last_code (mathoption_int_base_code+8)
@@ -769,8 +770,9 @@ extern halfword last_cs_name;
 #define default_skew_char_par              int_par(default_skew_char_code)
 #define saving_hyph_codes_par              int_par(saving_hyph_codes_code)
 
-#define automatic_hyphen_mode_par          int_par(automatic_hyphen_mode_code)
+#define hyphen_penalty_mode_par            int_par(hyphen_penalty_mode_code)
 #define automatic_hyphen_penalty_par       int_par(automatic_hyphen_penalty_code)
+#define explicit_hyphen_penalty_par        int_par(explicit_hyphen_penalty_code)
 
 #define cur_lang_par                       int_par(cur_lang_code)
 #define cur_font_par                       equiv(cur_font_loc)
@@ -809,4 +811,71 @@ extern halfword last_cs_name;
 #define xspace_skip_subtype (xspace_skip_code + 1)
 #define space_skip_subtype  (space_skip_code + 1)
 
+/*
+
+hyphen_penalty_mode_par   automatic_disc (-)                explicit_disc (\-)
+---------------------------------------------------------------------------------
+0 (default)               ex_hyphen_penalty_par             ex_hyphen_penalty_par
+1                         hyphen_penalty_par                hyphen_penalty_par
+2                         ex_hyphen_penalty_par             hyphen_penalty_par
+3                         hyphen_penalty_par                ex_hyphen_penalty_par
+4                         automatic_hyphen_penalty_par      explicit_disc_penalty_par
+5                         ex_hyphen_penalty_par             explicit_disc_penalty_par
+6                         hyphen_penalty_par                explicit_disc_penalty_par
+7                         automatic_hyphen_penalty_par      ex_hyphen_penalty_par
+8                         automatic_hyphen_penalty_par      hyphen_penalty_par
+*/
+
+#define set_automatic_disc_penalty(n) \
+    switch (hyphen_penalty_mode_par) { \
+        case 0: \
+        case 2: \
+        case 5: \
+            /* we take ex_hyphen_penalty */ \
+            disc_penalty(n) = ex_hyphen_penalty_par; \
+            break; \
+        case 1: \
+        case 3: \
+        case 6: \
+            /* we take hyphen_penalty */ \
+            disc_penalty(n) = hyphen_penalty_par; \
+            break; \
+        case 4: \
+        case 7: \
+        case 8: \
+            /* we take automatic_hyphen_penalty */ \
+            disc_penalty(n) = automatic_hyphen_penalty_par; \
+            break; \
+        default: \
+            /* what we've done since the beginning */ \
+            disc_penalty(n) = ex_hyphen_penalty_par; \
+            break; \
+    }
+
+#define set_explicit_disc_penalty(n) \
+    switch (hyphen_penalty_mode_par) { \
+        case 0: \
+        case 3: \
+        case 7: \
+            /* we take ex_hyphen_penalty */ \
+            disc_penalty(n) = ex_hyphen_penalty_par; \
+            break; \
+        case 1: \
+        case 2: \
+        case 8: \
+            /* we take hyphen_penalty */ \
+            disc_penalty(n) = hyphen_penalty_par; \
+            break; \
+        case 4: \
+        case 5: \
+        case 6: \
+            /* we take automatic_hyphen_penalty */ \
+            disc_penalty(n) = explicit_hyphen_penalty_par; \
+            break; \
+        default: \
+            /* what we've done since the beginning */ \
+            disc_penalty(n) = ex_hyphen_penalty_par; \
+            break; \
+    }
+
 #endif
diff --git a/source/texk/web2c/luatexdir/tex/maincontrol.w b/source/texk/web2c/luatexdir/tex/maincontrol.w
index 3bb5913d2656c45f2f60527a5a4d56d49ba707d8..965334021113f577efdb080660a7926281c38eb8 100644
--- a/source/texk/web2c/luatexdir/tex/maincontrol.w
+++ b/source/texk/web2c/luatexdir/tex/maincontrol.w
@@ -1770,7 +1770,7 @@ void append_discretionary(void)
             alink(vlink(post_break(tail))) = post_break(tail);
             tlink(post_break(tail)) = vlink(post_break(tail));
         }
-        disc_penalty(tail) = ex_hyphen_penalty_par;
+        set_explicit_disc_penalty(tail);
     } else {
         /* \discretionary */
         if (scan_keyword("penalty")) {