diff options
| author | Andy Moreton | 2018-08-04 10:28:13 -0600 |
|---|---|---|
| committer | Tom Tromey | 2018-08-04 10:28:13 -0600 |
| commit | bc8ff54efee05f4a2769be32046866ed1e152b41 (patch) | |
| tree | c6dac43f3b9abfc6bde54a9d245c04e5dbb360d5 | |
| parent | 76715f8921dca740880cd22c644a6328cd810846 (diff) | |
| download | emacs-bc8ff54efee05f4a2769be32046866ed1e152b41.tar.gz emacs-bc8ff54efee05f4a2769be32046866ed1e152b41.zip | |
Make bignums work better when EMACS_INT is larger than long
* lisp/international/ccl.el (ccl-fixnum): New function.
(ccl-embed-data, ccl-embed-current-address, ccl-dump): Use it.
* src/alloc.c (make_number): Handle case where EMACS_INT is
larger than long.
* src/data.c (bignumcompare): Handle case where EMACS_INT is
larger than long.
(arith_driver): Likewise. Coerce markers.
(float_arith_driver): Coerce markers.
(Flogcount): Use mpz_sgn.
(ash_lsh_impl): Fix bugs.
(Fsub1): Fix underflow check.
* src/lisp.h (NUMBERP): Don't check BIGNUMP.
(CHECK_FIXNUM_OR_FLOAT_COERCE_MARKER): Fix indentation.
* test/lisp/international/ccl-tests.el: New file.
| -rw-r--r-- | lisp/international/ccl.el | 16 | ||||
| -rw-r--r-- | src/alloc.c | 28 | ||||
| -rw-r--r-- | src/data.c | 96 | ||||
| -rw-r--r-- | src/lisp.h | 4 | ||||
| -rw-r--r-- | test/lisp/international/ccl-tests.el | 219 |
5 files changed, 340 insertions, 23 deletions
diff --git a/lisp/international/ccl.el b/lisp/international/ccl.el index d2f490d59cd..d1b82ceb9ce 100644 --- a/lisp/international/ccl.el +++ b/lisp/international/ccl.el | |||
| @@ -184,11 +184,17 @@ | |||
| 184 | (defvar ccl-current-ic 0 | 184 | (defvar ccl-current-ic 0 |
| 185 | "The current index for `ccl-program-vector'.") | 185 | "The current index for `ccl-program-vector'.") |
| 186 | 186 | ||
| 187 | ;; This is needed because CCL assumes the pre-bigint (wrapping) | ||
| 188 | ;; semantics of integer overflow. | ||
| 189 | (defun ccl-fixnum (code) | ||
| 190 | "Convert a CCL code word to a fixnum value." | ||
| 191 | (- (logxor (logand code #x0fffffff) #x08000000) #x08000000)) | ||
| 192 | |||
| 187 | (defun ccl-embed-data (data &optional ic) | 193 | (defun ccl-embed-data (data &optional ic) |
| 188 | "Embed integer DATA in `ccl-program-vector' at `ccl-current-ic' and | 194 | "Embed integer DATA in `ccl-program-vector' at `ccl-current-ic' and |
| 189 | increment it. If IC is specified, embed DATA at IC." | 195 | increment it. If IC is specified, embed DATA at IC." |
| 190 | (if ic | 196 | (if ic |
| 191 | (aset ccl-program-vector ic data) | 197 | (aset ccl-program-vector ic (ccl-fixnum data)) |
| 192 | (let ((len (length ccl-program-vector))) | 198 | (let ((len (length ccl-program-vector))) |
| 193 | (if (>= ccl-current-ic len) | 199 | (if (>= ccl-current-ic len) |
| 194 | (let ((new (make-vector (* len 2) nil))) | 200 | (let ((new (make-vector (* len 2) nil))) |
| @@ -196,7 +202,7 @@ increment it. If IC is specified, embed DATA at IC." | |||
| 196 | (setq len (1- len)) | 202 | (setq len (1- len)) |
| 197 | (aset new len (aref ccl-program-vector len))) | 203 | (aset new len (aref ccl-program-vector len))) |
| 198 | (setq ccl-program-vector new)))) | 204 | (setq ccl-program-vector new)))) |
| 199 | (aset ccl-program-vector ccl-current-ic data) | 205 | (aset ccl-program-vector ccl-current-ic (ccl-fixnum data)) |
| 200 | (setq ccl-current-ic (1+ ccl-current-ic)))) | 206 | (setq ccl-current-ic (1+ ccl-current-ic)))) |
| 201 | 207 | ||
| 202 | (defun ccl-embed-symbol (symbol prop) | 208 | (defun ccl-embed-symbol (symbol prop) |
| @@ -230,7 +236,8 @@ proper index number for SYMBOL. PROP should be | |||
| 230 | `ccl-program-vector' at IC without altering the other bit field." | 236 | `ccl-program-vector' at IC without altering the other bit field." |
| 231 | (let ((relative (- ccl-current-ic (1+ ic)))) | 237 | (let ((relative (- ccl-current-ic (1+ ic)))) |
| 232 | (aset ccl-program-vector ic | 238 | (aset ccl-program-vector ic |
| 233 | (logior (aref ccl-program-vector ic) (ash relative 8))))) | 239 | (logior (aref ccl-program-vector ic) |
| 240 | (ccl-fixnum (ash relative 8)))))) | ||
| 234 | 241 | ||
| 235 | (defun ccl-embed-code (op reg data &optional reg2) | 242 | (defun ccl-embed-code (op reg data &optional reg2) |
| 236 | "Embed CCL code for the operation OP and arguments REG and DATA in | 243 | "Embed CCL code for the operation OP and arguments REG and DATA in |
| @@ -986,7 +993,8 @@ is a list of CCL-BLOCKs." | |||
| 986 | (defun ccl-get-next-code () | 993 | (defun ccl-get-next-code () |
| 987 | "Return a CCL code in `ccl-code' at `ccl-current-ic'." | 994 | "Return a CCL code in `ccl-code' at `ccl-current-ic'." |
| 988 | (prog1 | 995 | (prog1 |
| 989 | (aref ccl-code ccl-current-ic) | 996 | (let ((code (aref ccl-code ccl-current-ic))) |
| 997 | (if (numberp code) (ccl-fixnum code) code)) | ||
| 990 | (setq ccl-current-ic (1+ ccl-current-ic)))) | 998 | (setq ccl-current-ic (1+ ccl-current-ic)))) |
| 991 | 999 | ||
| 992 | (defun ccl-dump-1 () | 1000 | (defun ccl-dump-1 () |
diff --git a/src/alloc.c b/src/alloc.c index 1dc1bbb031a..367bb73fc15 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -3815,6 +3815,34 @@ make_number (mpz_t value) | |||
| 3815 | } | 3815 | } |
| 3816 | } | 3816 | } |
| 3817 | 3817 | ||
| 3818 | /* Check if fixnum can be larger than long. */ | ||
| 3819 | if (sizeof (EMACS_INT) > sizeof (long)) | ||
| 3820 | { | ||
| 3821 | size_t bits = mpz_sizeinbase (value, 2); | ||
| 3822 | int sign = mpz_sgn (value); | ||
| 3823 | |||
| 3824 | if (bits < FIXNUM_BITS + (sign < 0)) | ||
| 3825 | { | ||
| 3826 | EMACS_INT v = 0; | ||
| 3827 | size_t limbs = mpz_size (value); | ||
| 3828 | mp_size_t i; | ||
| 3829 | |||
| 3830 | for (i = 0; i < limbs; i++) | ||
| 3831 | { | ||
| 3832 | mp_limb_t limb = mpz_getlimbn (value, i); | ||
| 3833 | v |= (EMACS_INT) ((EMACS_UINT) limb << (i * GMP_NUMB_BITS)); | ||
| 3834 | } | ||
| 3835 | if (sign < 0) | ||
| 3836 | v = -v; | ||
| 3837 | |||
| 3838 | if (!FIXNUM_OVERFLOW_P (v)) | ||
| 3839 | { | ||
| 3840 | XSETINT (obj, v); | ||
| 3841 | return obj; | ||
| 3842 | } | ||
| 3843 | } | ||
| 3844 | } | ||
| 3845 | |||
| 3818 | obj = allocate_misc (Lisp_Misc_Bignum); | 3846 | obj = allocate_misc (Lisp_Misc_Bignum); |
| 3819 | b = XBIGNUM (obj); | 3847 | b = XBIGNUM (obj); |
| 3820 | /* We could mpz_init + mpz_swap here, to avoid a copy, but the | 3848 | /* We could mpz_init + mpz_swap here, to avoid a copy, but the |
diff --git a/src/data.c b/src/data.c index 0deebdca1ae..3d55d9d17d5 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -2409,7 +2409,18 @@ bignumcompare (Lisp_Object num1, Lisp_Object num2, | |||
| 2409 | if (FLOATP (num2)) | 2409 | if (FLOATP (num2)) |
| 2410 | cmp = mpz_cmp_d (XBIGNUM (num1)->value, XFLOAT_DATA (num2)); | 2410 | cmp = mpz_cmp_d (XBIGNUM (num1)->value, XFLOAT_DATA (num2)); |
| 2411 | else if (FIXNUMP (num2)) | 2411 | else if (FIXNUMP (num2)) |
| 2412 | cmp = mpz_cmp_si (XBIGNUM (num1)->value, XINT (num2)); | 2412 | { |
| 2413 | if (sizeof (EMACS_INT) > sizeof (long) && XINT (num2) > LONG_MAX) | ||
| 2414 | { | ||
| 2415 | mpz_t tem; | ||
| 2416 | mpz_init (tem); | ||
| 2417 | mpz_set_intmax (tem, XINT (num2)); | ||
| 2418 | cmp = mpz_cmp (XBIGNUM (num1)->value, tem); | ||
| 2419 | mpz_clear (tem); | ||
| 2420 | } | ||
| 2421 | else | ||
| 2422 | cmp = mpz_cmp_si (XBIGNUM (num1)->value, XINT (num2)); | ||
| 2423 | } | ||
| 2413 | else | 2424 | else |
| 2414 | { | 2425 | { |
| 2415 | eassume (BIGNUMP (num2)); | 2426 | eassume (BIGNUMP (num2)); |
| @@ -2422,10 +2433,19 @@ bignumcompare (Lisp_Object num1, Lisp_Object num2, | |||
| 2422 | if (FLOATP (num1)) | 2433 | if (FLOATP (num1)) |
| 2423 | cmp = - mpz_cmp_d (XBIGNUM (num2)->value, XFLOAT_DATA (num1)); | 2434 | cmp = - mpz_cmp_d (XBIGNUM (num2)->value, XFLOAT_DATA (num1)); |
| 2424 | else | 2435 | else |
| 2425 | { | 2436 | { |
| 2426 | eassume (FIXNUMP (num1)); | 2437 | eassume (FIXNUMP (num1)); |
| 2427 | cmp = - mpz_cmp_si (XBIGNUM (num2)->value, XINT (num1)); | 2438 | if (sizeof (EMACS_INT) > sizeof (long) && XINT (num1) > LONG_MAX) |
| 2428 | } | 2439 | { |
| 2440 | mpz_t tem; | ||
| 2441 | mpz_init (tem); | ||
| 2442 | mpz_set_intmax (tem, XINT (num1)); | ||
| 2443 | cmp = - mpz_cmp (XBIGNUM (num2)->value, tem); | ||
| 2444 | mpz_clear (tem); | ||
| 2445 | } | ||
| 2446 | else | ||
| 2447 | cmp = - mpz_cmp_si (XBIGNUM (num2)->value, XINT (num1)); | ||
| 2448 | } | ||
| 2429 | } | 2449 | } |
| 2430 | 2450 | ||
| 2431 | switch (comparison) | 2451 | switch (comparison) |
| @@ -2860,7 +2880,7 @@ arith_driver (enum arithop code, ptrdiff_t nargs, Lisp_Object *args) | |||
| 2860 | { | 2880 | { |
| 2861 | /* Using args[argnum] as argument to CHECK_NUMBER... */ | 2881 | /* Using args[argnum] as argument to CHECK_NUMBER... */ |
| 2862 | val = args[argnum]; | 2882 | val = args[argnum]; |
| 2863 | CHECK_NUMBER (val); | 2883 | CHECK_NUMBER_COERCE_MARKER (val); |
| 2864 | 2884 | ||
| 2865 | if (FLOATP (val)) | 2885 | if (FLOATP (val)) |
| 2866 | return unbind_to (count, | 2886 | return unbind_to (count, |
| @@ -2871,7 +2891,15 @@ arith_driver (enum arithop code, ptrdiff_t nargs, Lisp_Object *args) | |||
| 2871 | case Aadd: | 2891 | case Aadd: |
| 2872 | if (BIGNUMP (val)) | 2892 | if (BIGNUMP (val)) |
| 2873 | mpz_add (accum, accum, XBIGNUM (val)->value); | 2893 | mpz_add (accum, accum, XBIGNUM (val)->value); |
| 2874 | else if (XINT (val) < 0) | 2894 | else if (sizeof (EMACS_INT) > sizeof (long)) |
| 2895 | { | ||
| 2896 | mpz_t tem; | ||
| 2897 | mpz_init (tem); | ||
| 2898 | mpz_set_intmax (tem, XINT (val)); | ||
| 2899 | mpz_add (accum, accum, tem); | ||
| 2900 | mpz_clear (tem); | ||
| 2901 | } | ||
| 2902 | else if (XINT (val) < 0) | ||
| 2875 | mpz_sub_ui (accum, accum, - XINT (val)); | 2903 | mpz_sub_ui (accum, accum, - XINT (val)); |
| 2876 | else | 2904 | else |
| 2877 | mpz_add_ui (accum, accum, XINT (val)); | 2905 | mpz_add_ui (accum, accum, XINT (val)); |
| @@ -2888,6 +2916,14 @@ arith_driver (enum arithop code, ptrdiff_t nargs, Lisp_Object *args) | |||
| 2888 | } | 2916 | } |
| 2889 | else if (BIGNUMP (val)) | 2917 | else if (BIGNUMP (val)) |
| 2890 | mpz_sub (accum, accum, XBIGNUM (val)->value); | 2918 | mpz_sub (accum, accum, XBIGNUM (val)->value); |
| 2919 | else if (sizeof (EMACS_INT) > sizeof (long)) | ||
| 2920 | { | ||
| 2921 | mpz_t tem; | ||
| 2922 | mpz_init (tem); | ||
| 2923 | mpz_set_intmax (tem, XINT (val)); | ||
| 2924 | mpz_sub (accum, accum, tem); | ||
| 2925 | mpz_clear (tem); | ||
| 2926 | } | ||
| 2891 | else if (XINT (val) < 0) | 2927 | else if (XINT (val) < 0) |
| 2892 | mpz_add_ui (accum, accum, - XINT (val)); | 2928 | mpz_add_ui (accum, accum, - XINT (val)); |
| 2893 | else | 2929 | else |
| @@ -2896,6 +2932,14 @@ arith_driver (enum arithop code, ptrdiff_t nargs, Lisp_Object *args) | |||
| 2896 | case Amult: | 2932 | case Amult: |
| 2897 | if (BIGNUMP (val)) | 2933 | if (BIGNUMP (val)) |
| 2898 | mpz_mul (accum, accum, XBIGNUM (val)->value); | 2934 | mpz_mul (accum, accum, XBIGNUM (val)->value); |
| 2935 | else if (sizeof (EMACS_INT) > sizeof (long)) | ||
| 2936 | { | ||
| 2937 | mpz_t tem; | ||
| 2938 | mpz_init (tem); | ||
| 2939 | mpz_set_intmax (tem, XINT (val)); | ||
| 2940 | mpz_mul (accum, accum, tem); | ||
| 2941 | mpz_clear (tem); | ||
| 2942 | } | ||
| 2899 | else | 2943 | else |
| 2900 | mpz_mul_si (accum, accum, XINT (val)); | 2944 | mpz_mul_si (accum, accum, XINT (val)); |
| 2901 | break; | 2945 | break; |
| @@ -2915,6 +2959,14 @@ arith_driver (enum arithop code, ptrdiff_t nargs, Lisp_Object *args) | |||
| 2915 | xsignal0 (Qarith_error); | 2959 | xsignal0 (Qarith_error); |
| 2916 | if (BIGNUMP (val)) | 2960 | if (BIGNUMP (val)) |
| 2917 | mpz_tdiv_q (accum, accum, XBIGNUM (val)->value); | 2961 | mpz_tdiv_q (accum, accum, XBIGNUM (val)->value); |
| 2962 | else if (sizeof (EMACS_INT) > sizeof (long)) | ||
| 2963 | { | ||
| 2964 | mpz_t tem; | ||
| 2965 | mpz_init (tem); | ||
| 2966 | mpz_set_intmax (tem, XINT (val)); | ||
| 2967 | mpz_tdiv_q (accum, accum, tem); | ||
| 2968 | mpz_clear (tem); | ||
| 2969 | } | ||
| 2918 | else | 2970 | else |
| 2919 | { | 2971 | { |
| 2920 | EMACS_INT value = XINT (val); | 2972 | EMACS_INT value = XINT (val); |
| @@ -2982,8 +3034,9 @@ float_arith_driver (double accum, ptrdiff_t argnum, enum arithop code, | |||
| 2982 | 3034 | ||
| 2983 | for (; argnum < nargs; argnum++) | 3035 | for (; argnum < nargs; argnum++) |
| 2984 | { | 3036 | { |
| 2985 | val = args[argnum]; /* using args[argnum] as argument to CHECK_FIXNUM_... */ | 3037 | /* using args[argnum] as argument to CHECK_NUMBER_... */ |
| 2986 | CHECK_FIXNUM_OR_FLOAT_COERCE_MARKER (val); | 3038 | val = args[argnum]; |
| 3039 | CHECK_NUMBER_COERCE_MARKER (val); | ||
| 2987 | 3040 | ||
| 2988 | if (FLOATP (val)) | 3041 | if (FLOATP (val)) |
| 2989 | { | 3042 | { |
| @@ -3277,7 +3330,7 @@ representation. */) | |||
| 3277 | 3330 | ||
| 3278 | if (BIGNUMP (value)) | 3331 | if (BIGNUMP (value)) |
| 3279 | { | 3332 | { |
| 3280 | if (mpz_cmp_si (XBIGNUM (value)->value, 0) >= 0) | 3333 | if (mpz_sgn (XBIGNUM (value)->value) >= 0) |
| 3281 | return make_fixnum (mpz_popcount (XBIGNUM (value)->value)); | 3334 | return make_fixnum (mpz_popcount (XBIGNUM (value)->value)); |
| 3282 | mpz_t tem; | 3335 | mpz_t tem; |
| 3283 | mpz_init (tem); | 3336 | mpz_init (tem); |
| @@ -3314,8 +3367,10 @@ ash_lsh_impl (Lisp_Object value, Lisp_Object count, bool lsh) | |||
| 3314 | mpz_init (result); | 3367 | mpz_init (result); |
| 3315 | if (XINT (count) >= 0) | 3368 | if (XINT (count) >= 0) |
| 3316 | mpz_mul_2exp (result, XBIGNUM (value)->value, XINT (count)); | 3369 | mpz_mul_2exp (result, XBIGNUM (value)->value, XINT (count)); |
| 3317 | else | 3370 | else if (lsh) |
| 3318 | mpz_tdiv_q_2exp (result, XBIGNUM (value)->value, - XINT (count)); | 3371 | mpz_tdiv_q_2exp (result, XBIGNUM (value)->value, - XINT (count)); |
| 3372 | else | ||
| 3373 | mpz_fdiv_q_2exp (result, XBIGNUM (value)->value, - XINT (count)); | ||
| 3319 | val = make_number (result); | 3374 | val = make_number (result); |
| 3320 | mpz_clear (result); | 3375 | mpz_clear (result); |
| 3321 | } | 3376 | } |
| @@ -3325,14 +3380,21 @@ ash_lsh_impl (Lisp_Object value, Lisp_Object count, bool lsh) | |||
| 3325 | mpz_t result; | 3380 | mpz_t result; |
| 3326 | eassume (FIXNUMP (value)); | 3381 | eassume (FIXNUMP (value)); |
| 3327 | mpz_init (result); | 3382 | mpz_init (result); |
| 3328 | if (lsh) | 3383 | |
| 3329 | mpz_set_uintmax (result, XUINT (value)); | 3384 | mpz_set_intmax (result, XINT (value)); |
| 3330 | else | 3385 | |
| 3331 | mpz_set_intmax (result, XINT (value)); | ||
| 3332 | if (XINT (count) >= 0) | 3386 | if (XINT (count) >= 0) |
| 3333 | mpz_mul_2exp (result, result, XINT (count)); | 3387 | mpz_mul_2exp (result, result, XINT (count)); |
| 3334 | else | 3388 | else if (lsh) |
| 3335 | mpz_tdiv_q_2exp (result, result, - XINT (count)); | 3389 | { |
| 3390 | if (mpz_sgn (result) > 0) | ||
| 3391 | mpz_fdiv_q_2exp (result, result, - XINT (count)); | ||
| 3392 | else | ||
| 3393 | mpz_fdiv_q_2exp (result, result, - XINT (count)); | ||
| 3394 | } | ||
| 3395 | else /* ash */ | ||
| 3396 | mpz_fdiv_q_2exp (result, result, - XINT (count)); | ||
| 3397 | |||
| 3336 | val = make_number (result); | 3398 | val = make_number (result); |
| 3337 | mpz_clear (result); | 3399 | mpz_clear (result); |
| 3338 | } | 3400 | } |
| @@ -3414,7 +3476,7 @@ Markers are converted to integers. */) | |||
| 3414 | else | 3476 | else |
| 3415 | { | 3477 | { |
| 3416 | eassume (FIXNUMP (number)); | 3478 | eassume (FIXNUMP (number)); |
| 3417 | if (XINT (number) > MOST_POSITIVE_FIXNUM) | 3479 | if (XINT (number) > MOST_NEGATIVE_FIXNUM) |
| 3418 | XSETINT (number, XINT (number) - 1); | 3480 | XSETINT (number, XINT (number) - 1); |
| 3419 | else | 3481 | else |
| 3420 | { | 3482 | { |
diff --git a/src/lisp.h b/src/lisp.h index 4208634fa95..b404f9d89aa 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -2778,7 +2778,7 @@ NATNUMP (Lisp_Object x) | |||
| 2778 | INLINE bool | 2778 | INLINE bool |
| 2779 | NUMBERP (Lisp_Object x) | 2779 | NUMBERP (Lisp_Object x) |
| 2780 | { | 2780 | { |
| 2781 | return INTEGERP (x) || FLOATP (x) || BIGNUMP (x); | 2781 | return INTEGERP (x) || FLOATP (x); |
| 2782 | } | 2782 | } |
| 2783 | 2783 | ||
| 2784 | INLINE bool | 2784 | INLINE bool |
| @@ -2947,7 +2947,7 @@ CHECK_INTEGER (Lisp_Object x) | |||
| 2947 | if (MARKERP (x)) \ | 2947 | if (MARKERP (x)) \ |
| 2948 | XSETFASTINT (x, marker_position (x)); \ | 2948 | XSETFASTINT (x, marker_position (x)); \ |
| 2949 | else \ | 2949 | else \ |
| 2950 | CHECK_TYPE (FIXED_OR_FLOATP (x), Qnumber_or_marker_p, x); \ | 2950 | CHECK_TYPE (FIXED_OR_FLOATP (x), Qnumber_or_marker_p, x); \ |
| 2951 | } while (false) | 2951 | } while (false) |
| 2952 | 2952 | ||
| 2953 | #define CHECK_NUMBER_COERCE_MARKER(x) \ | 2953 | #define CHECK_NUMBER_COERCE_MARKER(x) \ |
diff --git a/test/lisp/international/ccl-tests.el b/test/lisp/international/ccl-tests.el new file mode 100644 index 00000000000..d0c254ce91b --- /dev/null +++ b/test/lisp/international/ccl-tests.el | |||
| @@ -0,0 +1,219 @@ | |||
| 1 | (require 'ert) | ||
| 2 | (require 'ccl) | ||
| 3 | (require 'seq) | ||
| 4 | |||
| 5 | |||
| 6 | (ert-deftest shift () | ||
| 7 | ;; shift left +ve 5628 #x00000000000015fc | ||
| 8 | (should (= (ash 5628 8) 1440768)) ; #x000000000015fc00 | ||
| 9 | (should (= (lsh 5628 8) 1440768)) ; #x000000000015fc00 | ||
| 10 | |||
| 11 | ;; shift left -ve -5628 #x3fffffffffffea04 | ||
| 12 | (should (= (ash -5628 8) -1440768)) ; #x3fffffffffea0400 | ||
| 13 | (should (= (lsh -5628 8) -1440768)) ; #x3fffffffffea0400 | ||
| 14 | |||
| 15 | ;; shift right +ve 5628 #x00000000000015fc | ||
| 16 | (should (= (ash 5628 -8) 21)) ; #x0000000000000015 | ||
| 17 | (should (= (lsh 5628 -8) 21)) ; #x0000000000000015 | ||
| 18 | |||
| 19 | ;; shift right -ve -5628 #x3fffffffffffea04 | ||
| 20 | (should (= (ash -5628 -8) -22)) ; #x3fffffffffffffea | ||
| 21 | |||
| 22 | ;; shift right -5628 #x3fffffffffffea04 | ||
| 23 | (cond | ||
| 24 | ((fboundp 'bignump) | ||
| 25 | (should (= (lsh -5628 -8) -22))) ; #x3fffffffffffffea bignum | ||
| 26 | ((= (logb most-negative-fixnum) 61) | ||
| 27 | (should (= (lsh -5628 -8) | ||
| 28 | (string-to-number | ||
| 29 | "18014398509481962")))) ; #x003fffffffffffea master (64bit) | ||
| 30 | ((= (logb most-negative-fixnum) 29) | ||
| 31 | (should (= (lsh -5628 -8) 4194282))) ; #x003fffea master (32bit) | ||
| 32 | )) | ||
| 33 | |||
| 34 | ;; CCl program from `pgg-parse-crc24' in lisp/obsolete/pgg-parse.el | ||
| 35 | (defconst prog-pgg-source | ||
| 36 | '(1 | ||
| 37 | ((loop | ||
| 38 | (read r0) (r1 ^= r0) (r2 ^= 0) | ||
| 39 | (r5 = 0) | ||
| 40 | (loop | ||
| 41 | (r1 <<= 1) | ||
| 42 | (r1 += ((r2 >> 15) & 1)) | ||
| 43 | (r2 <<= 1) | ||
| 44 | (if (r1 & 256) | ||
| 45 | ((r1 ^= 390) (r2 ^= 19707))) | ||
| 46 | (if (r5 < 7) | ||
| 47 | ((r5 += 1) | ||
| 48 | (repeat)))) | ||
| 49 | (repeat))))) | ||
| 50 | |||
| 51 | (defconst prog-pgg-code | ||
| 52 | [1 30 14 114744 114775 0 161 131127 1 148217 15 82167 | ||
| 53 | 1 1848 131159 1 1595 5 256 114743 390 114775 19707 | ||
| 54 | 1467 16 7 183 1 -5628 -7164 22]) | ||
| 55 | |||
| 56 | (defconst prog-pgg-dump | ||
| 57 | "Out-buffer must be as large as in-buffer. | ||
| 58 | Main-body: | ||
| 59 | 2:[read-register] read r0 (0 remaining) | ||
| 60 | 3:[set-assign-expr-register] r1 ^= r0 | ||
| 61 | 4:[set-assign-expr-const] r2 ^= 0 | ||
| 62 | 6:[set-short-const] r5 = 0 | ||
| 63 | 7:[set-assign-expr-const] r1 <<= 1 | ||
| 64 | 9:[set-expr-const] r7 = r2 >> 15 | ||
| 65 | 11:[set-assign-expr-const] r7 &= 1 | ||
| 66 | 13:[set-assign-expr-register] r1 += r7 | ||
| 67 | 14:[set-assign-expr-const] r2 <<= 1 | ||
| 68 | 16:[jump-cond-expr-const] if !(r1 & 256), jump to 23(+7) | ||
| 69 | 19:[set-assign-expr-const] r1 ^= 390 | ||
| 70 | 21:[set-assign-expr-const] r2 ^= 19707 | ||
| 71 | 23:[jump-cond-expr-const] if !(r5 < 7), jump to 29(+6) | ||
| 72 | 26:[set-assign-expr-const] r5 += 1 | ||
| 73 | 28:[jump] jump to 7(-21) | ||
| 74 | 29:[jump] jump to 2(-27) | ||
| 75 | At EOF: | ||
| 76 | 30:[end] end | ||
| 77 | ") | ||
| 78 | |||
| 79 | (ert-deftest ccl-compile-pgg () | ||
| 80 | (should (equal (ccl-compile prog-pgg-source) prog-pgg-code))) | ||
| 81 | |||
| 82 | (ert-deftest ccl-dump-pgg () | ||
| 83 | (with-temp-buffer | ||
| 84 | (ccl-dump prog-pgg-code) | ||
| 85 | (should (equal (buffer-string) prog-pgg-dump)))) | ||
| 86 | |||
| 87 | (ert-deftest pgg-parse-crc24 () | ||
| 88 | ;; Compiler | ||
| 89 | (require 'pgg) | ||
| 90 | (should (equal pgg-parse-crc24 prog-pgg-code)) | ||
| 91 | ;; Interpreter | ||
| 92 | (should (equal (pgg-parse-crc24-string "foo") (concat [#x4f #xc2 #x55]))) | ||
| 93 | (should (equal (pgg-parse-crc24-string "bar") (concat [#x51 #xd9 #x53]))) | ||
| 94 | (should (equal (pgg-parse-crc24-string "baz") (concat [#xf0 #x58 #x6a])))) | ||
| 95 | |||
| 96 | (ert-deftest pgg-parse-crc24-dump () | ||
| 97 | ;; Disassembler | ||
| 98 | (require 'pgg) | ||
| 99 | (with-temp-buffer | ||
| 100 | (ccl-dump pgg-parse-crc24) | ||
| 101 | (should (equal (buffer-string) prog-pgg-dump)))) | ||
| 102 | |||
| 103 | ;;---------------------------------------------------------------------------- | ||
| 104 | ;; Program from 'midikbd-decoder in midi-kbd-0.2.el GNU ELPA package | ||
| 105 | (defconst prog-midi-source | ||
| 106 | '(2 | ||
| 107 | (loop | ||
| 108 | (loop | ||
| 109 | ;; central message receiver loop here. | ||
| 110 | ;; When it exits, the command to deal with is in r0 | ||
| 111 | ;; Any arguments are in r1 and r2 | ||
| 112 | ;; r3 contains: 0 if no arguments are accepted | ||
| 113 | ;; 1 if 1 argument can be accepted | ||
| 114 | ;; 2 if 2 arguments can be accepted | ||
| 115 | ;; 3 if the first of two arguments has been accepted | ||
| 116 | ;; Arguments are read into r1 and r2. | ||
| 117 | ;; r4 contains the current running status byte if any. | ||
| 118 | (read-if (r0 < #x80) | ||
| 119 | (branch r3 | ||
| 120 | (repeat) | ||
| 121 | ((r1 = r0) (r0 = r4) (break)) | ||
| 122 | ((r1 = r0) (r3 = 3) (repeat)) | ||
| 123 | ((r2 = r0) (r3 = 2) (r0 = r4) (break)))) | ||
| 124 | (if (r0 >= #xf8) ; real time message | ||
| 125 | (break)) | ||
| 126 | (if (r0 < #xf0) ; channel command | ||
| 127 | ((r4 = r0) | ||
| 128 | (if ((r0 & #xe0) == #xc0) | ||
| 129 | ;; program change and channel pressure take only 1 argument | ||
| 130 | (r3 = 1) | ||
| 131 | (r3 = 2)) | ||
| 132 | (repeat))) | ||
| 133 | ;; system common message, we swallow those for now | ||
| 134 | (r3 = 0) | ||
| 135 | (repeat)) | ||
| 136 | (if ((r0 & #xf0) == #x90) | ||
| 137 | (if (r2 == 0) ; Some Midi devices use velocity 0 | ||
| 138 | ; for switching notes off, | ||
| 139 | ; so translate into note-off | ||
| 140 | ; and fall through | ||
| 141 | (r0 -= #x10) | ||
| 142 | ((r0 &= #xf) | ||
| 143 | (write 0) | ||
| 144 | (write r0 r1 r2) | ||
| 145 | (repeat)))) | ||
| 146 | (if ((r0 & #xf0) == #x80) | ||
| 147 | ((r0 &= #xf) | ||
| 148 | (write 1) | ||
| 149 | (write r0 r1 r2) | ||
| 150 | (repeat))) | ||
| 151 | (repeat)))) | ||
| 152 | |||
| 153 | (defconst prog-midi-code | ||
| 154 | [2 72 4893 16 128 1133 5 6 9 12 16 -2556 32 1024 6660 32 865 | ||
| 155 | -4092 64 609 1024 4868 795 20 248 3844 3099 16 240 128 82169 | ||
| 156 | 224 1275 18 192 353 260 609 -9468 97 -9980 82169 240 4091 | ||
| 157 | 18 144 1371 18 0 16407 16 1796 81943 15 20 529 305 81 -14588 | ||
| 158 | 82169 240 2555 18 128 81943 15 276 529 305 81 -17660 -17916 22]) | ||
| 159 | |||
| 160 | (defconst prog-midi-dump | ||
| 161 | "Out-buffer must be 2 times bigger than in-buffer. | ||
| 162 | Main-body: | ||
| 163 | 2:[read-jump-cond-expr-const] read r0, if !(r0 < 128), jump to 22(+20) | ||
| 164 | 5:[branch] jump to array[r3] of length 4 | ||
| 165 | 11 12 15 18 22 | ||
| 166 | 11:[jump] jump to 2(-9) | ||
| 167 | 12:[set-register] r1 = r0 | ||
| 168 | 13:[set-register] r0 = r4 | ||
| 169 | 14:[jump] jump to 41(+27) | ||
| 170 | 15:[set-register] r1 = r0 | ||
| 171 | 16:[set-short-const] r3 = 3 | ||
| 172 | 17:[jump] jump to 2(-15) | ||
| 173 | 18:[set-register] r2 = r0 | ||
| 174 | 19:[set-short-const] r3 = 2 | ||
| 175 | 20:[set-register] r0 = r4 | ||
| 176 | 21:[jump] jump to 41(+20) | ||
| 177 | 22:[jump-cond-expr-const] if !(r0 >= 248), jump to 26(+4) | ||
| 178 | 25:[jump] jump to 41(+16) | ||
| 179 | 26:[jump-cond-expr-const] if !(r0 < 240), jump to 39(+13) | ||
| 180 | 29:[set-register] r4 = r0 | ||
| 181 | 30:[set-expr-const] r7 = r0 & 224 | ||
| 182 | 32:[jump-cond-expr-const] if !(r7 == 192), jump to 37(+5) | ||
| 183 | 35:[set-short-const] r3 = 1 | ||
| 184 | 36:[jump] jump to 38(+2) | ||
| 185 | 37:[set-short-const] r3 = 2 | ||
| 186 | 38:[jump] jump to 2(-36) | ||
| 187 | 39:[set-short-const] r3 = 0 | ||
| 188 | 40:[jump] jump to 2(-38) | ||
| 189 | 41:[set-expr-const] r7 = r0 & 240 | ||
| 190 | 43:[jump-cond-expr-const] if !(r7 == 144), jump to 59(+16) | ||
| 191 | 46:[jump-cond-expr-const] if !(r2 == 0), jump to 52(+6) | ||
| 192 | 49:[set-assign-expr-const] r0 -= 16 | ||
| 193 | 51:[jump] jump to 59(+8) | ||
| 194 | 52:[set-assign-expr-const] r0 &= 15 | ||
| 195 | 54:[write-const-string] write char \"\x00\" | ||
| 196 | 55:[write-register] write r0 (2 remaining) | ||
| 197 | 56:[write-register] write r1 (1 remaining) | ||
| 198 | 57:[write-register] write r2 (0 remaining) | ||
| 199 | 58:[jump] jump to 2(-56) | ||
| 200 | 59:[set-expr-const] r7 = r0 & 240 | ||
| 201 | 61:[jump-cond-expr-const] if !(r7 == 128), jump to 71(+10) | ||
| 202 | 64:[set-assign-expr-const] r0 &= 15 | ||
| 203 | 66:[write-const-string] write char \"\x01\" | ||
| 204 | 67:[write-register] write r0 (2 remaining) | ||
| 205 | 68:[write-register] write r1 (1 remaining) | ||
| 206 | 69:[write-register] write r2 (0 remaining) | ||
| 207 | 70:[jump] jump to 2(-68) | ||
| 208 | 71:[jump] jump to 2(-69) | ||
| 209 | At EOF: | ||
| 210 | 72:[end] end | ||
| 211 | ") | ||
| 212 | |||
| 213 | (ert-deftest ccl-compile-midi () | ||
| 214 | (should (equal (ccl-compile prog-midi-source) prog-midi-code))) | ||
| 215 | |||
| 216 | (ert-deftest ccl-dump-midi () | ||
| 217 | (with-temp-buffer | ||
| 218 | (ccl-dump prog-midi-code) | ||
| 219 | (should (equal (buffer-string) prog-midi-dump)))) | ||