diff --git a/lang/cem/cemcom.ansi/BigPars b/lang/cem/cemcom.ansi/BigPars
index bf92e22fe..ad758c8a6 100644
--- a/lang/cem/cemcom.ansi/BigPars
+++ b/lang/cem/cemcom.ansi/BigPars
@@ -55,6 +55,7 @@
 #define SZ_WORD		4
 #define	SZ_INT		4
 #define	SZ_LONG		4
+#define	SZ_LNGLNG	-1
 #define	SZ_FLOAT	4
 #define	SZ_DOUBLE	8
 #define	SZ_LNGDBL	8	/* for now */
@@ -66,6 +67,7 @@
 #define AL_WORD		SZ_WORD
 #define	AL_INT		SZ_WORD
 #define	AL_LONG		SZ_WORD
+#define	AL_LNGLNG	SZ_WORD
 #define	AL_FLOAT	SZ_WORD
 #define	AL_DOUBLE	SZ_WORD
 #define	AL_LNGDBL	SZ_WORD
diff --git a/lang/cem/cemcom.ansi/SmallPars b/lang/cem/cemcom.ansi/SmallPars
index 4ef50e48e..01f7073b1 100644
--- a/lang/cem/cemcom.ansi/SmallPars
+++ b/lang/cem/cemcom.ansi/SmallPars
@@ -55,6 +55,7 @@
 #define SZ_WORD		4
 #define	SZ_INT		4
 #define	SZ_LONG		4
+#define	SZ_LNGLNG	-1
 #define	SZ_FLOAT	4
 #define	SZ_DOUBLE	8
 #define	SZ_LNGDBL	8	/* for now */
@@ -66,6 +67,7 @@
 #define AL_WORD		SZ_WORD
 #define	AL_INT		SZ_WORD
 #define	AL_LONG		SZ_WORD
+#define	AL_LNGLNG	SZ_WORD
 #define	AL_FLOAT	SZ_WORD
 #define	AL_DOUBLE	SZ_WORD
 #define	AL_LNGDBL	SZ_WORD
diff --git a/lang/cem/cemcom.ansi/align.h b/lang/cem/cemcom.ansi/align.h
index 79b9be97b..67bb24265 100644
--- a/lang/cem/cemcom.ansi/align.h
+++ b/lang/cem/cemcom.ansi/align.h
@@ -10,6 +10,7 @@
 #ifndef NOCROSS
 extern int
 	short_align, word_align, int_align, long_align,
+	lnglng_align,
 	float_align, double_align, lngdbl_align,
 	pointer_align,
 	struct_align, union_align;
@@ -18,6 +19,7 @@ extern int
 #define word_align	((int)AL_WORD)
 #define int_align	((int)AL_INT)
 #define long_align	((int)AL_LONG)
+#define lnglng_align	((int)AL_LNGLNG)
 #define float_align	((int)AL_FLOAT)
 #define double_align	((int)AL_DOUBLE)
 #define	lngdbl_align	((int)AL_LNGDBL)
diff --git a/lang/cem/cemcom.ansi/arith.c b/lang/cem/cemcom.ansi/arith.c
index 6504a7305..e1c3311c3 100644
--- a/lang/cem/cemcom.ansi/arith.c
+++ b/lang/cem/cemcom.ansi/arith.c
@@ -12,6 +12,7 @@
 */
 
 #include	<assert.h>
+#include	<stddef.h>
 #include    "parameters.h"
 #include	<alloc.h>
 #include	<flt_arith.h>
@@ -45,7 +46,8 @@ void arithbalance(register struct expr **e1p, int oper, register struct expr **e
 		have a floating type, in which case the flags shouldn't
 		travel upward in the expression tree.
 	*/
-	register int t1, t2, u1, u2;
+	struct type *convert1, *convert2;
+	int t1, t2, u1, u2;
 	int shifting = (oper == LEFT || oper == RIGHT
 			|| oper == LEFTAB || oper == RIGHTAB);
 	int ptrdiff = 0;
@@ -56,9 +58,11 @@ void arithbalance(register struct expr **e1p, int oper, register struct expr **e
 	if (int_size != pointer_size) {
 		if (ptrdiff = ((*e1p)->ex_flags & EX_PTRDIFF)
 			    || ((*e2p)->ex_flags & EX_PTRDIFF)) {
-			if (!((*e1p)->ex_flags & EX_PTRDIFF) && t1 == LONG)
+			if (!((*e1p)->ex_flags & EX_PTRDIFF)
+			    && (t1 == LONG || t1 == LNGLNG))
 				ptrdiff = 0;
-			if (!((*e2p)->ex_flags & EX_PTRDIFF) && t2 == LONG
+			if (!((*e2p)->ex_flags & EX_PTRDIFF)
+			    && (t2 == LONG || t2 == LNGLNG)
 			    && !shifting)
 				ptrdiff = 0;
 		}
@@ -67,7 +71,9 @@ void arithbalance(register struct expr **e1p, int oper, register struct expr **e
 		(*e2p)->ex_flags &= ~EX_PTRDIFF;
 	}
 
-	/* Now t1 and t2 are either INT, LONG, FLOAT, DOUBLE, or LNGDBL */
+	/*	Now t1 and t2 are either INT, LONG, LNGLNG,
+		FLOAT, DOUBLE, or LNGDBL
+	*/
 
 	/*	If any operand has the type long double, the other operand
 		is converted to long double.
@@ -82,11 +88,12 @@ void arithbalance(register struct expr **e1p, int oper, register struct expr **e
 		}
 		return;
 	} else if (t2 == LNGDBL) {
-		if (t1 != LNGDBL)
+		if (t1 != LNGDBL) {
 		    if (t1 == DOUBLE || t1 == FLOAT)
 			float2float(e1p, lngdbl_type);
 		    else
 			int2float(e1p, lngdbl_type);
+		}
 		return;
 	}
 
@@ -120,38 +127,63 @@ void arithbalance(register struct expr **e1p, int oper, register struct expr **e
 		return;
 	}
 
-	/* Now they are INT or LONG */
+	/* Now they are INT, LONG or LNGLNG */
 	u1 = (*e1p)->ex_type->tp_unsigned;
 	u2 = (*e2p)->ex_type->tp_unsigned;
+	convert1 = NULL;
+	convert2 = NULL;
 
-	/*	If either operand has type unsigned long int, the other
-		operand is converted to unsigned long int.
-	*/
-	if (t1 == LONG && u1 && (t2 != LONG || !u2))
-		t2 = int2int(e2p, ulong_type);
-	else if (t2 == LONG && u2 && (t1 != LONG || !u1)
-			&& !shifting)	/* ??? */
-		t1 = int2int(e1p, ulong_type);
+	/*	If either operand is a long long, the other operand
+		is converted to long long; else if either operand is
+		a long, the other operand is converted to a long.
 
-	/*	If one operand has type long int and the other has type unsigned
-		int, if a long int can represent all values of an unsigned int,
-		the operand of type unsigned int is converted to long int; if
-		a long int cannot represent all values of an unsigned int,
-		both operands are converted to unsigned long int.
+		If one operand is signed and the other operand is
+		unsigned, if the signed type can represent all values
+		of the unsigned type, the unsigned operand is
+		converted to the signed type, else both operands are
+		converted to an unsigned type.
 	*/
-	if (t1 == LONG && t2 == INT && u2)
-		t2 = int2int(e2p, (int_size<long_size)? long_type : ulong_type);
-	else if (t2 == LONG && t1 == INT && u1 && !shifting)	/* ??? */
-		t1 = int2int(e1p, (int_size<long_size)? long_type : ulong_type);
+	if (t1 == LNGLNG && u1 && (t2 != LNGLNG || !u2))
+		convert2 = ulnglng_type;
+	else if (t2 == LNGLNG && u2 && (t1 != LNGLNG || !u1))
+		convert1 = ulnglng_type;
+	else if (t1 == LNGLNG && t2 != LNGLNG && u2) {
+		if ((t2 == LONG ? long_size : int_size) < lnglng_size)
+			convert2 = lnglng_type;
+		else
+			convert1 = convert2 = ulnglng_type;
+	} else if (t2 == LNGLNG && t1 != LNGLNG && u1) {
+		if ((t1 == LONG ? long_size : int_size) < lnglng_size)
+			convert1 = lnglng_type;
+		else
+			convert1 = convert2 = ulnglng_type;
+	} else if (t1 == LNGLNG && t2 != LNGLNG)
+		convert2 = lnglng_type;
+	else if (t2 == LNGLNG && t1 != LNGLNG)
+		convert1 = lnglng_type;
+	else if (t1 == LONG && u1 && (t2 != LONG || !u2))
+		convert2 = ulong_type;
+	else if (t2 == LONG && u2 && (t1 != LONG || !u1))
+		convert1 = ulong_type;
+	else if (t1 == LONG && t2 == INT && u2) {
+		if (int_size < long_size)
+			convert2 = long_type;
+		else
+			convert1 = convert2 = ulong_type;
+	} else if (t2 == LONG && t1 == INT && u1) {
+		if (int_size < long_size)
+			convert1 = long_type;
+		else
+			convert1 = convert2 = ulong_type;
+	} else if (t1 == LONG && t2 != LONG)
+		convert2 = long_type;
+	else if (t2 == LONG && t1 != LONG)
+		convert1 = long_type;
 
-	/*	If either operand has type long int, the other operand is con-
-		verted to long int.
-	*/
-	if (t1 == LONG && t2 != LONG)
-		t2 = int2int(e2p, long_type);
-	else
-	if (t2 == LONG && t1 != LONG && !shifting)	/* ??? */
-		t1 = int2int(e1p, long_type);
+	if (convert1 && !shifting)	/* ??? */
+		t1 = int2int(e1p, convert1);
+	if (convert2)
+		t2 = int2int(e2p, convert2);
 
 	u1 = (*e1p)->ex_type->tp_unsigned;
 	u2 = (*e2p)->ex_type->tp_unsigned;
@@ -161,10 +193,10 @@ void arithbalance(register struct expr **e1p, int oper, register struct expr **e
 		Otherwise, both operands have type int.
 	*/
 	if (u1 && !u2 && !shifting)
-		t2 = int2int(e2p, (t1 == LONG) ? ulong_type : uint_type);
+		t2 = int2int(e2p, uint_type);
 	else
 	if (!u1 && u2 && !shifting)
-		t1 = int2int(e1p, (t2 == LONG) ? ulong_type : uint_type);
+		t1 = int2int(e1p, uint_type);
 
 	if (int_size != pointer_size) {
 		if (ptrdiff) {
@@ -259,6 +291,7 @@ any2arith(register struct expr **expp, register int oper)
 		break;
 	case INT:
 	case LONG:
+	case LNGLNG:
 		break;
 	case ENUM:
 #ifndef	LINT
@@ -457,7 +490,7 @@ void opnd2integral(register struct expr **expp, int oper)
 {
 	register int fund = (*expp)->ex_type->tp_fund;
 
-	if (fund != INT && fund != LONG)	{
+	if (fund != INT && fund != LONG && fund != LNGLNG) {
 		expr_error(*expp, "%s operand to %s",
 				symbol2str(fund), symbol2str(oper));
 		erroneous2int(expp);
@@ -486,6 +519,7 @@ void opnd2logical(register struct expr **expp, int oper)
 	case SHORT:
 	case INT:
 	case LONG:
+	case LNGLNG:
 	case ENUM:
 	case POINTER:
 	case FLOAT:
diff --git a/lang/cem/cemcom.ansi/ch3.c b/lang/cem/cemcom.ansi/ch3.c
index 3132ccbb9..69c75fed6 100644
--- a/lang/cem/cemcom.ansi/ch3.c
+++ b/lang/cem/cemcom.ansi/ch3.c
@@ -58,6 +58,7 @@ void ch3sel(struct expr **expp, int oper, struct idf *idf)
 				break;
 			case INT:
 			case LONG:
+			case LNGLNG:
 				/* An error is given in idf2sdef() */
 				ch3cast(expp, CAST, pa_type);
 				sd = idf2sdef(idf, tp);
@@ -82,6 +83,7 @@ void ch3sel(struct expr **expp, int oper, struct idf *idf)
 		break;
 	case INT:
 	case LONG:
+	case LNGLNG:
 		/* warning will be given by idf2sdef() */
 		break;
 	default:
@@ -679,6 +681,7 @@ int is_integral_type(register struct type *tp)
 	case SHORT:
 	case INT:
 	case LONG:
+	case LNGLNG:
 	case ENUM:
 		return 1;
 #ifndef NOBITFIELD
@@ -697,6 +700,7 @@ int is_arith_type(register struct type *tp)
 	case SHORT:
 	case INT:
 	case LONG:
+	case LNGLNG:
 	case ENUM:
 	case FLOAT:
 	case DOUBLE:
diff --git a/lang/cem/cemcom.ansi/code.c b/lang/cem/cemcom.ansi/code.c
index d50700056..cef708cbf 100644
--- a/lang/cem/cemcom.ansi/code.c
+++ b/lang/cem/cemcom.ansi/code.c
@@ -101,6 +101,10 @@ void init_code(char *dst_file)
 		stb_typedef(ushort_type, "unsigned short");
 		stb_typedef(ulong_type, "unsigned long");
 		stb_typedef(uint_type, "unsigned int");
+		if (lnglng_size >= 0) {
+			stb_typedef(lnglng_type, "long long");
+			stb_typedef(ulnglng_type, "unsigned long long");
+		}
 		stb_typedef(float_type, "float");
 		stb_typedef(double_type, "double");
 		stb_typedef(lngdbl_type, "long double");
diff --git a/lang/cem/cemcom.ansi/conversion.c b/lang/cem/cemcom.ansi/conversion.c
index 60d4c45b5..13af3cef8 100644
--- a/lang/cem/cemcom.ansi/conversion.c
+++ b/lang/cem/cemcom.ansi/conversion.c
@@ -137,6 +137,7 @@ static int convtype(register struct type *tp)
 	case INT:
 	case ERRONEOUS:
 	case LONG:
+	case LNGLNG:
 	case ENUM:
 		return tp->tp_unsigned ? T_UNSIGNED : T_SIGNED;
 	case FLOAT:
diff --git a/lang/cem/cemcom.ansi/cstoper.c b/lang/cem/cemcom.ansi/cstoper.c
index 774e356d6..61c3fd6e3 100644
--- a/lang/cem/cemcom.ansi/cstoper.c
+++ b/lang/cem/cemcom.ansi/cstoper.c
@@ -175,7 +175,11 @@ void init_cst(void)
 	register int i = 0;
 	register arith bt = (arith)0;
 
-	while (!(bt < 0))	{
+	/*	FIXME arith is insufficient for long long.  We ignore
+		this problem and write masks up to full_mask[8], but
+		masks are wrong after bt < 0.
+	*/
+	while (!(bt < 0) || i < 8) {
 		bt = (bt << 8) + 0377, i++;
 		if (i > MAXSIZE)
 			fatal("array full_mask too small for this machine");
diff --git a/lang/cem/cemcom.ansi/declar.g b/lang/cem/cemcom.ansi/declar.g
index 92adefb5f..8bec8e72d 100644
--- a/lang/cem/cemcom.ansi/declar.g
+++ b/lang/cem/cemcom.ansi/declar.g
@@ -123,9 +123,13 @@ single_decl_specifier /* non_empty */ (register struct decspecs *ds;)
 	}
 |
 	[ SHORT | LONG ]
-	{	if (ds->ds_size)
-			error("repeated size specifier");
-		ds->ds_size = DOT;
+	{	if (ds->ds_size == LONG && DOT == LONG)
+			ds->ds_size = LNGLNG;
+		else {
+			if (ds->ds_size)
+				error("repeated size specifier");
+			ds->ds_size = DOT;
+		}
 	}
 |
 	[ SIGNED | UNSIGNED ]
diff --git a/lang/cem/cemcom.ansi/decspecs.c b/lang/cem/cemcom.ansi/decspecs.c
index 42465e213..8af08eacb 100644
--- a/lang/cem/cemcom.ansi/decspecs.c
+++ b/lang/cem/cemcom.ansi/decspecs.c
@@ -69,7 +69,8 @@ void do_decspecs(register struct decspecs *ds)
 	}
 	if (ds->ds_size)
 	{
-		register int ds_isshort = (ds->ds_size == SHORT);
+		int ds_isshort = (ds->ds_size == SHORT);
+		int ds_islong = (ds->ds_size == LONG);
 
 		if (ds->ds_typedef)
 			goto SIZE_ERROR;
@@ -78,10 +79,18 @@ void do_decspecs(register struct decspecs *ds)
 		{
 			if (ds_isshort)
 				tp = short_type;
-			else
+			else if (ds_islong)
 				tp = long_type;
+			else
+			{
+				assert(ds->ds_size == LNGLNG);
+				if (no_long_long())
+					tp = error_type;
+				else
+					tp = lnglng_type;
+			}
 		}
-		else if (tp == double_type && !ds_isshort)
+		else if (tp == double_type && ds_islong)
 		{
 			tp = lngdbl_type;
 		}
@@ -122,6 +131,11 @@ void do_decspecs(register struct decspecs *ds)
 			if (ds_isunsigned)
 				tp = ulong_type;
 		}
+		else if (tp == lnglng_type)
+		{
+			if (ds_isunsigned)
+				tp = ulnglng_type;
+		}
 		else
 		{
 			SIGN_ERROR: error("%s with illegal type",
diff --git a/lang/cem/cemcom.ansi/eval.c b/lang/cem/cemcom.ansi/eval.c
index 352d590b3..338ca51cb 100644
--- a/lang/cem/cemcom.ansi/eval.c
+++ b/lang/cem/cemcom.ansi/eval.c
@@ -133,13 +133,15 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 		case '+':
 			/*	We have the following possibilities :
 				int + int, pointer + int, pointer + long,
-				long + long, double + double
+				long + long, long long + long long,
+				double + double
 			*/
 			operands(expr, gencode);
 			if (gencode) {
 				switch (tp->tp_fund) {
 				case INT:
 				case LONG:
+				case LNGLNG:
 					if (tp->tp_unsigned)
 						C_adu(tp->tp_size);
 					else
@@ -165,6 +167,7 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 					switch (tp->tp_fund) {
 					case INT:
 					case LONG:
+					case LNGLNG:
 					case POINTER:
 						C_ngi(tp->tp_size);
 						break;
@@ -181,7 +184,8 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 			}
 			/*	else binary; we have the following flavours:
 				int - int, pointer - int, pointer - long,
-				pointer - pointer, long - long, double - double
+				pointer - pointer, long - long,
+				long long - long long, double - double
 			*/
 			operands(expr, gencode);
 			if (!gencode)
@@ -189,6 +193,7 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 			switch (tp->tp_fund) {
 			case INT:
 			case LONG:
+			case LNGLNG:
 				if (tp->tp_unsigned)
 					C_sbu(tp->tp_size);
 				else
@@ -224,6 +229,7 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 				switch (tp->tp_fund) {
 				case INT:
 				case LONG:
+				case LNGLNG:
 				case POINTER:
 					if (tp->tp_unsigned)
 						C_mlu(tp->tp_size);
@@ -246,6 +252,7 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 				switch (tp->tp_fund) {
 				case INT:
 				case LONG:
+				case LNGLNG:
 				case POINTER:
 					if (tp->tp_unsigned)
 						C_dvu(tp->tp_size);
@@ -264,7 +271,8 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 			break;
 		case '%':
 			operands(expr, gencode);
-			assert(tp->tp_fund==INT || tp->tp_fund==LONG);
+			assert(tp->tp_fund==INT || tp->tp_fund==LONG ||
+			       tp->tp_fund==LNGLNG);
 			if (gencode)
 				if (tp->tp_unsigned)
 					C_rmu(tp->tp_size);
@@ -301,6 +309,7 @@ void EVAL(register struct expr *expr, int val, int code, label true_label, label
 				switch (tp->tp_fund) {
 				case INT:
 				case LONG:
+				case LNGLNG:
 					if (left->ex_type->tp_unsigned)
 						C_cmu(size);
 					else
@@ -736,6 +745,7 @@ void assop(register struct type *type, int oper)
 	case SHORT:
 	case INT:
 	case LONG:
+	case LNGLNG:
 	case ENUM:
 		switch (oper) {
 		case PLUSAB:
@@ -1014,10 +1024,13 @@ void load_val(register struct expr *expr, int rlval)
 
 void load_cst(arith val, arith siz)
 {
+	/*	EM can't encode ldc with constant over 4 bytes.
+		Such a constant must go into rom.
+	*/
 	if ((int)siz <= (int)word_size)
 		C_loc(val);
 	else
-	if ((int)siz == (int)dword_size)
+	if ((int)siz == (int)dword_size && (int)dword_size <= 4)
 		C_ldc(val);
 	else {
 		label datlab;
diff --git a/lang/cem/cemcom.ansi/ival.g b/lang/cem/cemcom.ansi/ival.g
index 398343ed4..995d46248 100644
--- a/lang/cem/cemcom.ansi/ival.g
+++ b/lang/cem/cemcom.ansi/ival.g
@@ -518,6 +518,7 @@ void check_ival(struct expr **expp, register struct type *tp)
 	case SHORT:
 	case INT:
 	case LONG:
+	case LNGLNG:
 	case ENUM:
 	case POINTER:
 		ch3cast(expp, '=', tp);
diff --git a/lang/cem/cemcom.ansi/main.c b/lang/cem/cemcom.ansi/main.c
index e7988bf66..da5ced81d 100644
--- a/lang/cem/cemcom.ansi/main.c
+++ b/lang/cem/cemcom.ansi/main.c
@@ -57,6 +57,7 @@ arith
 	dword_size = (2 * SZ_WORD),
 	int_size = SZ_INT,
 	long_size = SZ_LONG,
+	lnglng_size = SZ_LNGLNG,
 	float_size = SZ_FLOAT,
 	double_size = SZ_DOUBLE,
 	lngdbl_size = SZ_LNGDBL,
@@ -67,6 +68,7 @@ int
 	word_align = AL_WORD,
 	int_align = AL_INT,
 	long_align = AL_LONG,
+	lnglng_align = AL_LNGLNG,
 	float_align = AL_FLOAT,
 	double_align = AL_DOUBLE,
 	lngdbl_align = AL_LNGDBL,
@@ -227,6 +229,10 @@ static void init(void)
 	long_type = standard_type(LONG, 0, long_align, long_size);
 	ulong_type = standard_type(LONG, UNSIGNED, long_align, long_size);
 
+	lnglng_type = standard_type(LNGLNG, 0, lnglng_align, lnglng_size);
+	ulnglng_type = standard_type(LNGLNG, UNSIGNED, lnglng_align,
+				     lnglng_size);
+
 	float_type = standard_type(FLOAT, 0, float_align, float_size);
 	double_type = standard_type(DOUBLE, 0, double_align, double_size);
 	lngdbl_type = standard_type(LNGDBL, 0, lngdbl_align, lngdbl_size);
diff --git a/lang/cem/cemcom.ansi/options.c b/lang/cem/cemcom.ansi/options.c
index 0c37c85d4..2cdfe3835 100644
--- a/lang/cem/cemcom.ansi/options.c
+++ b/lang/cem/cemcom.ansi/options.c
@@ -159,6 +159,12 @@ next_option:			/* to allow combined one-char options */
 				if (algn != 0)
 					long_align = algn;
 				break;
+			case 'q':	/* long long	*/
+				if (sz != (arith)0)
+					lnglng_size = sz;
+				if (algn != 0)
+					lnglng_align = algn;
+				break;
 			case 'f':	/* float	*/
 				if (sz != (arith)0)
 					float_size = sz;
diff --git a/lang/cem/cemcom.ansi/sizes.h b/lang/cem/cemcom.ansi/sizes.h
index 0f38c5668..f943d184b 100644
--- a/lang/cem/cemcom.ansi/sizes.h
+++ b/lang/cem/cemcom.ansi/sizes.h
@@ -10,6 +10,7 @@
 #ifndef NOCROSS
 extern arith
 	short_size, word_size, dword_size, int_size, long_size,
+	lnglng_size,
 	float_size, double_size, lngdbl_size,
 	pointer_size;
 
@@ -20,6 +21,7 @@ extern arith max_int, max_unsigned;	/* cstoper.c	*/
 #define dword_size	((arith)2*SZ_WORD)
 #define int_size	((arith)SZ_INT)
 #define long_size	((arith)SZ_LONG)
+#define lnglng_size	((arith)SZ_LNGLNG)
 #define float_size	((arith)SZ_FLOAT)
 #define double_size	((arith)SZ_DOUBLE)
 #define	lngdbl_size	((arith)SZ_LNGDBL)
diff --git a/lang/cem/cemcom.ansi/stab.c b/lang/cem/cemcom.ansi/stab.c
index a7a7928c9..5a002301f 100644
--- a/lang/cem/cemcom.ansi/stab.c
+++ b/lang/cem/cemcom.ansi/stab.c
@@ -74,9 +74,10 @@ static void adds_db_str(char *s)
 
 static void stb_type(register struct type *tp)
 {
-	char buf[128];
+	char buf[128], *range;
 	static int stb_count;
 	long l;
+	int uns;
 
 	if (tp->tp_dbindex > 0)
 	{
@@ -101,18 +102,26 @@ static void stb_type(register struct type *tp)
 		break;
 	case INT:
 	case LONG:
+	case LNGLNG:
 	case CHAR:
 	case SHORT:
-		l = full_mask[(int) tp->tp_size];
-		if (tp->tp_unsigned)
+		switch ((tp->tp_size << 3) + !tp->tp_unsigned)
 		{
-			adds_db_str(sprint(buf, "r%d;0;%ld", tp->tp_dbindex, l));
-		}
-		else
-		{
-			l &= ~(1L << ((int) tp->tp_size * 8 - 1));
-			adds_db_str(sprint(buf, "r%d;%ld;%ld", tp->tp_dbindex, -l - 1, l));
+#define R(s) range = #s; break
+		case 0010: R(0;255);
+		case 0011: R(-128;127);
+		case 0020: R(0;65535);
+		case 0021: R(-32768;32767);
+		default:   R(0;-1); /* acts as 0;4294967295 */
+		case 0041: R(-2147483648;2147483647);
+		/*	The stabs reader in gdb(1) needs an octal integer
+			when its value doesn't fit in type long.
+		*/
+		case 0100: R(0;01777777777777777777777);
+		case 0101: R(01000000000000000000000;0777777777777777777777);
+#undef R
 		}
+		adds_db_str(sprint(buf, "r%d;%s", tp->tp_dbindex, range));
 		break;
 	case FLOAT:
 	case DOUBLE:
diff --git a/lang/cem/cemcom.ansi/struct.c b/lang/cem/cemcom.ansi/struct.c
index b69acd206..d8f6ef2a6 100644
--- a/lang/cem/cemcom.ansi/struct.c
+++ b/lang/cem/cemcom.ansi/struct.c
@@ -360,6 +360,7 @@ add_field(
 	case SHORT:
 	case ENUM:
 	case LONG:
+	case LNGLNG:
 		strict("non-portable field type");
 	case INT:
 		/* right type; size OK? */
diff --git a/lang/cem/cemcom.ansi/switch.c b/lang/cem/cemcom.ansi/switch.c
index 24cb82c6f..7db1f28bd 100644
--- a/lang/cem/cemcom.ansi/switch.c
+++ b/lang/cem/cemcom.ansi/switch.c
@@ -61,7 +61,7 @@ void code_startswitch(struct expr **expp)
 	register label l_break = text_label();
 	register struct switch_hdr *sh = new_switch_hdr();
 	int fund = any2arith(expp, SWITCH);
-				    /* INT, LONG, FLOAT, DOUBLE or LNGDBL */
+			/* INT, LONG, LNGLNG, FLOAT, DOUBLE or LNGDBL */
 	
 	switch (fund) {
 	case FLOAT:
diff --git a/lang/cem/cemcom.ansi/tokenname.c b/lang/cem/cemcom.ansi/tokenname.c
index b430abbd8..fb3bae349 100644
--- a/lang/cem/cemcom.ansi/tokenname.c
+++ b/lang/cem/cemcom.ansi/tokenname.c
@@ -104,6 +104,7 @@ struct tokenname tkidf[] =	{	/* names of the identifier tokens */
 
 #ifdef	____
 struct tokenname tkfunny[] =	{	/* internal keywords */
+	{LNGLNG, "long long"},
 	{LNGDBL, "long double"},
 	{ULONG, "unsigned long"},
 
diff --git a/lang/cem/cemcom.ansi/type.c b/lang/cem/cemcom.ansi/type.c
index 750ed9a8a..57a6cd98e 100644
--- a/lang/cem/cemcom.ansi/type.c
+++ b/lang/cem/cemcom.ansi/type.c
@@ -25,6 +25,7 @@
  */
 struct type *schar_type, *uchar_type, *short_type, *ushort_type, *word_type,
 		*uword_type, *int_type, *uint_type, *long_type, *ulong_type,
+		*lnglng_type, *ulnglng_type,
 		*float_type, *double_type, *lngdbl_type, *void_type, *string_type,
 		*funint_type, *error_type;
 
@@ -291,3 +292,20 @@ void completed(struct type *tp)
 		atp = atp->next;
 	}
 }
+
+int no_long_long(void)
+{
+	static int shown = 0;
+
+	if (lnglng_size < 0)
+	{
+		if (!shown)
+		{
+			error("no long long for this machine");
+			shown = 1;
+		}
+		return 1;
+	}
+	else
+		return 0;
+}
diff --git a/lang/cem/cemcom.ansi/type.str b/lang/cem/cemcom.ansi/type.str
index b98ffbb6c..ada366231 100644
--- a/lang/cem/cemcom.ansi/type.str
+++ b/lang/cem/cemcom.ansi/type.str
@@ -71,6 +71,7 @@ extern struct type
 	*word_type, *uword_type,
 	*int_type, *uint_type,
 	*long_type, *ulong_type,
+	*lnglng_type, *ulnglng_type,
 	*float_type, *double_type, *lngdbl_type,
 	*void_type,
 	*string_type, *funint_type, *error_type;
@@ -93,6 +94,7 @@ void idf2type(struct idf *idf, struct type **tpp);
 arith align(arith pos, int al);
 struct type * standard_type(int fund, int sgn, int algn, arith sz);
 void completed(struct type *tp);
+int no_long_long(void);
 
 
 /* ALLOCDEF "type" 50 */
diff --git a/plat/linux386/descr b/plat/linux386/descr
index fd033f423..8a7dc5f47 100644
--- a/plat/linux386/descr
+++ b/plat/linux386/descr
@@ -23,7 +23,8 @@ var CPP_F=-D__unix
 var ALIGN=-a0:4 -a1:4 -a2:4 -a3:4 -b0:0x08048054
 var C_LIB={PLATFORMDIR}/libc-ansi.a
 # bitfields reversed for compatibility with (g)cc.
-var CC_ALIGN=-Vr
+# long long enabled.
+var CC_ALIGN=-Vrq8.4
 var OLD_C_LIB={C_LIB}
 var MACHOPT_F=-m10
 var EGO_PLAT_FLAGS=-M{EM}/share/ack/ego/{ARCH}.descr