Math Processor Unit Library

libmpu – library of arithmetic functions for integer, real, and complex numbers of increased digit capacity

16 Commits   0 Branches   2 Tags
.\" Copyright 2024 Andrew V.Kosteltsev (kx@radix-linux.su)
.\"
.\"
.TH shifts 3  "December 27, 2024" "libmpu" "LibMPU Programmer's Manual"
.SH NAME
\fBishl\fP, \fBishr\fP, \fBisal\fP, \fBisar\fP \- one\-bit shifts
.SH SYNOPSIS
.nf
.B #include <libmpu.h>
.PP
.BI "void ishl( mpu_int *" c ", mpu_int *" a ", int " nb " );
.BI "void ishr( mpu_int *" c ", mpu_int *" a ", int " nb " );
.BI "void isal( mpu_int *" c ", mpu_int *" a ", int " nb " );
.BI "void isar( mpu_int *" c ", mpu_int *" a ", int " nb " );
.fi
.SH DESCRIPTION
For one\-bit shift operations, the input operand is a variable of size \fBnb\fP bytes located
at address \fBa\fP, the result is placed in a variable of the same size located at address \fBc\fP.
The spaces occupied by the input and output variable in memory may overlap, either partially or
completely, which does not affect the correctness of the result obtained. The content of the
variable located at address \fBa\fP will remain unchanged after the operation if the space
occupied by it does not overlap with the space occupied by variable \fBc\fP.
.PP
When performing shift operations, the carry flag \fBC\fP always contains the value of the last
pushed outward bit. There are the following types of shift operations:
.PP
.RS 3
    SHL \- logical unsigned shift to the left by one bit.
.RE
.RS 3
    SHR \- logical unsigned shift to the right by one bit.
.RE
.RS 3
    SAL \- arithmetic shift to the left by one bit.
.RE
.RS 3
    SAR \- arithmetic shift to the right by one bit.
.RE
.PP
The following tables illustrate the execution of the operations \fBishl\fP, \fBishr\fP.
.nf
.sp
      ┌────────────────┬───┬───────────────────┬────────────┐
      │ SHL(<<):       │ C │   operand value   │  filling   │
      ├────────────────┼───┼───────────────────┼────────────┤
      │ before         │   │      10110111     │     0      │
      ├────────────────┼───┼───────────────────┼────────────┤
      │ after          │ 1 │      01101110     │            │
      └────────────────┴───┴───────────────────┴────────────┘

      ┌────────────────┬────────────┬───────────────────┬───┐
      │ SHR(>>):       │  filling   │   operand value   │ C │
      ├────────────────┼────────────┼───────────────────┼───┤
      │ before         │     0      │      10110111     │   │
      ├────────────────┼────────────┼───────────────────┼───┤
      │ after          │            │      01011011     │ 1 │
      └────────────────┴────────────┴───────────────────┴───┘
.fi
.sp
The tables show that any logical shift always results in a 0 being pushed into the vacated space. 
The overflow flag \fBO\fP is set if the operand has changed sign as a result of the operation.
.PP
The left arithmetic shift operation \fBisal\fP operates in the same way as \fBishl\fP (0 is
also moved to the empty space on the right). The right arithmetic shift operation works
differently: the sign bit of the original operand is copied to the empty position on the left.
The action of the \fBisal\fP and \fBisar\fP operations is shown in the following tables.
.nf
.sp
      ┌────────────────┬───┬───────────────────┬────────────┐
      │ SAL(<<):       │ C │   operand value   │  filling   │
      ├────────────────┼───┼───────────────────┼────────────┤
      │ before         │   │      10110111     │     0      │
      ├────────────────┼───┼───────────────────┼────────────┤
      │ after          │ 1 │      01101110     │            │
      └────────────────┴───┴───────────────────┴────────────┘

      ┌────────────────┬────────────┬───────────────────┬───┐
      │ SAR(>>):       │  filling   │   operand value   │ C │
      ├────────────────┼────────────┼───────────────────┼───┤
      │ before         │   sign(1)  │      10110111     │   │
      ├────────────────┼────────────┼───────────────────┼───┤
      │ after          │            │      11011011     │ 1 │
      └────────────────┴────────────┴───────────────────┴───┘
.fi
.sp
The overflow flag for the \fBisal\fP operation is set in the same way as for the \fBishl\fP
operation. When performing an \fBisar\fP operation, the operand cannot change its sign. Repeated
shifting of \fBisar\fP may eventually cause a loss of value, and a positive number will be shifted
to 0 and a negative number to -1. Therefore, the overflow flag \fBO\fP for the \fBisar\fP operation
is reset to 0.
.PP
When performing \fBisal\fP, \fBisar\fP, \fBishl\fP, \fBishr\fP operations, the parity \fBP\fP,
sign \fBS\fP and zero \fBZ\fP flags are also set. The carry flag from the lowest tetrad \fBA\fP
is not defined and is simply reset to 0.
.PP
Left shift can be used to double numbers, and right shift can be used to divide by 2. These operations
are much faster than multiplication and division operations. Dividing odd numbers (such as 5 or 7) in
half forms smaller values (2 or 3, respectively) and sets the carry flag \fBC\fP to 1. In doing so,
\fBC\fP actually contains the remainder of the division. This use of shift operations requires control
of the overflow flag \fBO\fP. For simplicity, consider flag control on 4\-bit signed variables:
.SS Examples with 4\-bit operands:
\fBMultiplying by 2\fP:
.nf
SAL(0111) =  7; result = 1110 = -2, mistake, there must be 14, CF = 0, OF = 1
.fi
.sp
\fBDividing by 2\fP:
.nf
SHR(1000) = -8; result = 0100 =  4, mistake, there must be -4, CF = 0, OF = 1
.fi
.sp
\fBDividing by 2\fP:
.nf
SAR(1000) = -8; result = 1100 = -4, exactly, CF = 0, OF = 0
.fi
.sp
\fBDividing by 2\fP:
.nf
SAR(0111) =  7; result = 0011 =  3, remainder 1 (see carry flag C), exactly, CF = 1, OF = 0
.fi
.sp
.SH EXAMPLES
.nf
.sp
#include <libmpu.h>
#include <stdio.h>

int main( void )
{
  int  rc = 0;

  __mpu_init();
  __mpu_extra_warnings = 1;

  {
    mpu_int8_t     c, a;
    int            nb = NB_I8;
    __mpu_char8_t  sc[32], sa[32];

    iatoi( a, "0b10110111", nb ); /* evaluate the A variable */

    ishl( c, a, nb );
    iitoa( sa, a, RADIX_BIN, LOWERCASE, nb ); /* convert A value to ASCII string SA */
    iitoa( sc, c, RADIX_BIN, LOWERCASE, nb ); /* convert C value to ASCII string SC */
    printf( "a = %s;\\n", sa ); /* c = 0b10110111; */
    printf( "c = %s;\\n", sc ); /* c = 0b01101110; */
    printf( "carry      = %d;\\n", __mpu_gtc() );  /* Carry Flag    */
    printf( "overflow   = %d;\\n", __mpu_gto() );  /* Overflow Flag */
  }

  __mpu_free_context();

  return( rc );
}
.fi
.SH SEE ALSO
.BR iadd(3),
.BR isub(3),
.BR iadc(3),
.BR isbb(3),
.BR irol(3),
.BR iror(3),
.BR ircl(3),
.BR ircr(3),
.BR ishln(3),
.BR ishrn(3),
.BR isaln(3),
.BR isarn(3),
.BR iroln(3),
.BR irorn(3),
.BR ircln(3),
.BR ircrn(3),
.BR ineg(3),
.BR inot(3),
.BR iand(3),
.BR itest(3),
.BR icmp(3),
.BR ior(3),
.BR ixor(3),
.BR iinc(3),
.BR idec(3),
.BR ixchg(3),
.BR icpy(3),
.BR icvt(3),
.BR imul(3),
.BR ismul(3),
.BR idiv(3),
.BR isdiv(3),
.BR iatoi(3),
.BR iatoui(3),
.BR iitoa(3),
.BR iuitoa(3).