Converting numbers in Ada - random

I am wondering how to convert an integer to a long_integer and a long_integer to a Positive_Count. Every way I have tried has given me and error even though the conversion should be easy in that instance.
For example, doing
long := long_integer(int1) + long_integer(int2);
will make long a negative value sometimes even though both integer were positive.
The code of the function I'm running , steps split for debugging:
--calcKey--
procedure calcKey(x: in String16; key: out External_IO.Positive_Count) is
s1, s2 : String2;
int1, int2 : integer;
long1, long2 : long_integer;
begin
s1 := x(12..13);
s2 := x(15..16);
put_line("s1: " &s1& "- s2: " &s2);
int1 := abs StringToInt(s1);
int2 := abs StringToInt(s2);
put("int1: " & Integer'image(int1) & " | int: " & Integer'Image(int2)); new_line;
long1 := long_integer(int1);
long2 := long_integer(int2);
long1 := long1 + long2;
put_line("long := " & long_integer'Image(long1));
long1 := (long1 mod 256) + 1;
key := External_IO.Positive_Count(long1);
put_line("Key : " & External_IO.Positive_Count'Image(key));
new_line;
end calcKey;
calling the function:
calcKey("0123456789abcdef",k);
calcKey("0123456789abcdef",k);
calcKey("0123456789abcdef",k);
calcKey("0123456789abcdef",k);
calcKey("fedvba9876543210",k);
calcKey("fedvba9876543210",k);
The output:
s1: bc- s2: ef
int1: 2011929758 | int: 1667393125
long := -615644413
Key : 4
s1: bc- s2: ef
int1: 287586 | int: 1667393125
long := 1667680711
Key : 200
s1: bc- s2: ef
int1: 13132642 | int: 1667393125
long := 1680525767
Key : 200
s1: bc- s2: ef
int1: 13132642 | int: 1667393125
long := 1680525767
Key : 200
s1: 43- s2: 10
int1: 13120308 | int: 859058225
long := 872178533
Key : 102
s1: 43- s2: 10
int1: 6697780 | int: 859058225
long := 865756005
Key : 102

Previous answer is correct on the need (and correct way) to check integer sizes.
Alternatively, define your own integer types and be done with the "problem"!
But if integer addition is overflowing and returning negative numbers, you are not using an Ada compiler!
It is unfortunate that Gnat is not an Ada compiler by default.
With Gnat, you need to set compiler flags to enable checks such as overflow, that really ought to be on by default. Then such an overflow will raise the Constraint_Error exception with a message pointing directly at the line of code which failed - makes testing much easier than having to reverse-engineer what went wrong!
gnatmake -gnataeEfoUv -fstack-check my_main.adb
is a fairly comprehensive set, that probably includes some style checks you don't want : check the Gnat documentation for more details.
Other suggestions for preferred flag sets welcome.
If you are using another compiler, I'd be interested to hear which it is.
Incidentally, you haven't provided the StringToInt function so nobody else can test your example, but I would point out that the values it is generating are quite unlike the values I would expect from the strings you provide... is it a random hash generator?

(THis should be a comment, but it's too long so I'm submitting it as an answer)
First thing I would do is actually verify that long_int is what you think it is, i.e doing INTEGER'SIZE and LONG_INTEGER'SIZE, it could very well be that on your platform they are the very same size,
From the Ada definition:
Note that the ranges and sizes of these types can be different in
every platform (except of course for Boolean and
[[Wide_]Wide_]Character). There is an implementation requirement that
the size of type Integer is at least 16 bits, and that of Long_Integer
at least 32 bits (if present) RM 3.5.4 (21..22) (Annotated). So if you
want full portability of your types, do not use types from Standard
(except where you must, see below), rather define you own types. A
compiler will reject any type declaration whose range it cannot
satisfy.
If they are the same size you could be overflowing when you add 2 really large ints, thus giving the results you see.
Source: http://en.wikibooks.org/wiki/Ada_Programming/Libraries/Standard

Related

Binary file error in Lazarus Pascal with custom records - error SIGSEGV

I don't work with Pascal very often so I apologise if this question is basic. I am working on a binary file program that writes an array of custom made records to a binary file.
Eventually I want it to be able to write multiple arrays of different custom record types to one single binary file.
For that reason I thought I would write an integer first being the number of bytes that the next array will be in total. Then I write the array itself. I can then read the first integer type block - to tell me the size of the next blocks to read in directly to an array.
For example - when writing the binary file I would do something like this:
assignfile(f,MasterFileName);
{$I-}
reset(f,1);
{$I+}
n := IOResult;
if n<> 0 then
begin
{$I-}
rewrite(f);
{$I+}
end;
n:= IOResult;
If n <> 0 then
begin
writeln('Error creating file: ', n);
end
else
begin
SetLength(MyArray, 2);
MyArray[0].ID := 101;
MyArray[0].Att1 := 'Hi';
MyArray[0].Att2 := 'MyArray 0 - Att2';
MyArray[0].Value := 1;
MyArray[1].ID := 102;
MyArray[1].Att1:= 'Hi again';
MyArray[1].Att2:= MyArray 1 - Att2';
MyArray[1].Value:= 5;
SizeOfArray := sizeOf(MyArray);
writeln('Size of character array: ', SizeOfArray);
writeln('Size of integer var: ', sizeof(SizeOfArray));
blockwrite(f,sizeOfArray,sizeof(SizeOfArray),actual);
blockwrite(f,MyArray,SizeOfArray,actual);
Close(f);
Then you could re-read the file with something like this:
Assign(f, MasterFileName);
Reset(f,1);
blockread(f,SizeOfArray,sizeof(SizeOfArray),actual);
blockread(f,MyArray,SizeOfArray,actual);
Close(f);
This has the idea that after these blocks have been read that you can then have a new integer recorded and a new array then saved etc.
It reads the integer parts of the records in but nothing for the strings. The record would be something like this:
TMyType = record
ID : Integer;
att1 : string;
att2 : String;
Value : Integer;
end;
Any help gratefully received!!
TMyType = record
ID : Integer;
att1 : string; // <- your problem
That field att1 declared as string that way means that the record contains a pointer to the actual string data (att1 is really a pointer). The compiler manages this pointer and the memory for the associated data, and the string can be any (reasonable) length.
A quick fix for you would be to declare att1 something like string[64], for example: a string which can be at maximum 64 chars long. That would eliminate the pointer and use the memory of the record (the att1 field itself, which now is a special static array) as buffer for string characters. Declaring the maximum length of the string, of course, can be slightly dangerous: if you try to assign the string a string too long, it will be truncated.
To be really complete: it depends on the compiler; some have a switch to make your declaration "string" usable, making it an alias for "string[255]". This is not the default though. Consider also that using string[...] is faster and wastes memory.
You have a few mistakes.
MyArray is a dynamic array, a reference type (a pointer), so SizeOf(MyArray) is the size of a pointer, not the size of the array. To get the length of the array, use Length(MyArray).
But the bigger problem is saving long strings (AnsiStrings -- the usual type to which string maps --, WideStrings, UnicodeStrings). These are reference types too, so you can't just save them together with the record. You will have to save the parts of the record one by one, and for strings, you will have to use a function like:
procedure SaveStr(var F: File; const S: AnsiString);
var
Actual: Integer;
Len: Integer;
begin
Len := Length(S);
BlockWrite(F, Len, SizeOf(Len), Actual);
if Len > 0 then
begin
BlockWrite(F, S[1], Len * SizeOf(AnsiChar), Actual);
end;
end;
Of course you should normally check Actual and do appropriate error handling, but I left that out, for simplicity.
Reading back is similar: first read the length, then use SetLength to set the string to that size and then read the rest.
So now you do something like:
Len := Length(MyArray);
BlockWrite(F, Len, SizeOf(Len), Actual);
for I := Low(MyArray) to High(MyArray) do
begin
BlockWrite(F, MyArray[I].ID, SizeOf(Integer), Actual);
SaveStr(F, MyArray[I].att1);
SaveStr(F, MyArray[I].att2);
BlockWrite(F, MyArray[I].Value, SizeOf(Integer), Actual);
end;
// etc...
Note that I can't currently test the code, so it may have some little errors. I'll try this later on, when I have access to a compiler, if that is necessary.
Update
As Marco van de Voort commented, you may have to do:
rewrite(f, 1);
instead of a simple
rewrite(f);
But as I replied to him, if you can, use streams. They are easier to use (IMO) and provide a more consistent interface, no matter to what exactly you try to write or read. There are streams for many different kinds of I/O, and all derive from (and are thus compatible with) the same basic abstract TStream class.

VHDL integer'image(some_int) results in "Expression is not constant" error

What is the correct way to convert an always changing integer variable to a string (to be displayed on a VGA monitor)? I have a series of if statements that take care of padding (so that the resulting string is always a certain length but as soon as I change:
resulting_string <= integer'image(87465);
to:
resulting_string <= integer'image(some_int_var);
I get an "Expression is not constant" error. What is the correct way to convert an always changing integer variable (that could be any int within the integer limits) to a string?
edit: not duplicate of the other question
'image (..) does not work on signals.
I think you are still missing the main problem: A monitor can not display strings or chars!
You need to implement
1. a graphic buffer,
2. a buffer reader that outputs VGA data.
Then you need to implement a manipulator to
draw shapes
clear the screen
move areas and
copy pictures into the buffer.
The glyphs are stored in a ROM and are choosen by a BCD value for each digit.
Converting an integer to a 'string' does not need a ASCII string, because the result is a BCD code (not ASCII). So you need to implement an (un)signed to BCD converter, too.
I went at it from every direction and finally found that I had to make a giant case block to get it to work. Now I can finally display rapidly changing variables that are really helpful for debugging. It's unfortunate that the solution had to be so retarded though..
(I already have a ROM for displaying text that the resulting string is sent to.)
function int_to_str(int : integer) return string is
variable a : natural := 0;
variable r : string(1 to 11);
begin
a := abs (int);
case a is
when 0 => r := "0 ";
when 1 => r := "1 ";
when 2 => r := "2 ";
when 3 => r := "3 ";
.
.
.
when 1000 => r := "1000 ";
when others => r := "???????????";
end case;
if (int < 0) then
r := '-' & r(1 to 10);
end if;
return r;
end int_to_str;

How would I create a function to convert from an integer to std_logic vector in VHDL?

I am seeking help as I am learning this language construct.
Here is what I have:
function int_slv(val,width: integer) return std_logic_vector is
variable R: std_logic_vector(0 to width-1):=(others=>'0')
variable b:integer:= width;
begin
if (b>32) then
b=32;
else
assert 2**bits >val report
"value too big for std_logic_vector"
severity warning
end if;
for i in 0 to b-1 loop
if val ((val/(2**i)) MOD 2 = 1) then
R(i)='1';
end if;
end loop;
return(R);
end int_slv;
In addition to 5 syntax errors, one wrong identifier and a modulo reduction expressions expressed as an element of an array as well as several sets of redundant parentheses, your modified code:
library ieee;
use ieee.std_logic_1164.all;
package int2bv_pkg is
function int_slv (val, width: integer) return std_logic_vector;
end package;
package body int2bv_pkg is
function int_slv (val, width: integer) return std_logic_vector is
variable R: std_logic_vector(0 to width-1):=(others=>'0'); -- added ';'
variable b:integer:= width;
begin
if b > 32 then
b := 32; -- ":=" is used for variable assignment
else
assert 2 ** width > val report -- width not bits
"value too big for std_logic_vector"
severity warning; -- missing semicolon at the end of assertion
end if;
for i in 0 to b - 1 loop
if val/2 ** i MOD 2 = 1 then -- not val (...)
R(i) := '1'; -- ":=" variable assign.
end if;
end loop;
return R; -- parentheses not needed
end int_slv;
end package body int2bv_pkg;
analyzes (compiles). The exponentiation operator "**" is the highest priority, the division operators "/" and "mod" are the same priority and executed in the order they are found (left to right). It's likely worthwhile learning VHDL operator precedence.
You were using "=" for variable assignment when you should have been using ":=" in two places, you were missing two semicolons and were using the identifier bits (which isn't declared in your function) where apparently you meant width.
The modified example analyzes, and hasn't been tested absent a Minimal, Complete and Verifiable example in the question.
Note that a package body is a design unit as is a package declaration. There are various other places in other design units you can introduce a function body.
You could also note the 2 ** 31 is outside the guaranteed range of an integer in VHDL equal to 2147483648, while the INTEGER value range guaranteed to be from -2147483647 to +2147483647 at a minimum.
This implies that were ever you are using a value that derived from an expression equivalent to 2 ** 31 you can incur a range error during execution (either at elaboration or during simulation).
This pretty much says you need a VHDL implementation with a larger INTEGER value range or you need to rethink what you're doing.
As a matter of course there are integer to unsigned and integer to signed functions found in package numeric_std in library IEEE.
The result of such can be type converted to std_logic_vector, and the source code can make great learning aids on how to wend through the limitations VHDL imposes. These to_signed or to_unsigned functions would be capable of dealing with the maximum value an INTEGER can hold and specify the length of the resulting array type while providing zero or sign filling for array lengths greater than the INTEGER's binary value. That utility extends to clipping using length as well.
VHDL -2008 package numeric_std_unsigned contains a function To_StdLogicVector that does what your int_slv function is intended to do although limited to a NATURAL range for the integer type input.
As #user1155120 has already indicated, the VHDL-2008 package numeric_std_unsigned has a builtin to_stdlogicvector. And #user1155120 already pointed out the to_signed and to_unsigned in numeric_std are available as well.
So, to expand on the previous answer, you can do:
constant C : integer := -6817563;
constant C_VEC : std_logic_vector(31 downto 0) := std_logic_vector(to_signed(c, 32));
And this mechanism will accept the full range of integer. You can also use to_unsigned, but this is limited to the range of natural.

Set comparison with different types on either side

The following code, taken from Gnu Pascal test code, will compile nicely in Free Pascal. And I can understand how it works.
var s1 : set of 0..255;
s2 : set of 64..128;
ok : boolean;
procedure p1;
begin
if s1 = s2 then begin
writeln('failed1');
ok := false;
end;
end;
However, I'm slightly curious what the rules are for set compatibility and what you can expect. For example:
program p;
var
a : set of 0..10;
b : set of 20..100;
s : integer;
begin
b := [20];
a := [];
if a = b then
writeln('a')
else
writeln('b');
end.
This prints 'b'. But if I have two empty sets (b := [];) then they are considered equal.
I'm just trying to get my head around how this actually gets implemented.
(What I'm kind of thinking is that the two sets are converted to the union of the ranges, so set of 0..100 are created, and two temporaries from a and b as set of 0..100, and then comparison is done those temporaries).
Sets with integers as base type (the type after the set of) are always compatible.
Your assumption is correct: in general both sets are converted to temporaries with common ranges and then the temporaries are compared. The conversion is done by calls to fpc_varset_load.
If you are interested in more details, the code which decides how to convert the sets, is located in nadd.pas (see 1) starting at line 1593.

What tips are there for rewriting stream code so it doesn't use any units?

I am trying to port some xor-encryption code so it doesn't use any other units. I want to use just the commands, variables, and types that are supported natively by the compiler.
For example, here's some of the original code:
[...]
while (StreamIn.Position < StreamIn.Size) and
((StreamIn.Size -StreamIn.Position) >= szBuffer) do begin
(* read 4 bytes at a time into a local integer variable *)
StreamIn.ReadBuffer(buffer, szBuffer);
(* the XOR encryption/decryption *)
buffer := buffer xor theKey;
buffer := buffer xor $E0F;
(* write data to output stream *)
StreamOut.WriteBuffer(buffer, szBuffer);
end;
[...]
This is my code:
function __NativeEncrypt (const Key, Source : String) : String;
// this function should not be used directly
// use EncryptText and DecryptText
const
szBuffer = SizeOf(Integer); (* 4 bytes *)
szByteBuffer = SizeOf(Byte); (* 1 byte *)
var
byteBuffer,
buffer,
index,
theKey: Integer;
StreamIn : String;
StreamOut : String;
i : Integer;
begin
theKey := hashKey(Key);
StreamIn := Source;
StreamOut := '';
for i := 1 to Length (StreamIn) do begin
buffer := Integer(StreamIn[i]);
buffer := buffer xor thekey;
buffer := buffer xor $E0F;
StreamOut := StreamOut + char(Buffer);
end;
result := StreamOut; // wrong results.
// to continue...
end;
What tips are there for this task?
The only reason not to use library-provided units is as a learning exercise. I see no other reason to intentionally cripple yourself by refusing to use built-in features of your tools. Any answer to your general request for tips would rob you of the learning experience.
Most developers end up rewriting something from scratch at some point in their careers. However, unless it was imposed by a supervisor who suffers from extreme not-invested-here syndrome, it's nearly always a personal experience. You won't profit from their experience the same way you will from doing the work yourself. Doing it yourself will give you an understanding of what jobs the built-in tools do, and may give you some insight into why they're designed the way they are. Although you might be able to get those explanations from other people, unless you've actually tried to do it yourself, you won't really appreciate the explanations anyway.
My tip to you is to proceed with your project. I hope you find it interesting, and I wish you luck. If you eventually find yourself unable to make further progress, then identify the specific problem you're stuck on, and then ask others for help with that roadblock.

Resources