Muninn 1.1 release

My account and password database Muninn, was released in version 1.1 today. This is a bug fix and language extension release.

Muninn

– The program can now be used in three languages : Dutch, German and English.

– Various small errors were fixed.

– Entering password is now easier. The copy and paste buffer can be used to paste a password from another source into the password fields. Be careful though, pasting a wrong word isn’t checked using this way.

– When the general password hasn’t been entered, the system beep is heard.

– column sizing is now automatic and stored in the application configuration file.

Muninn is released on :

– Apple Mac OS X 10.6 and higher

– MS Windows 7 and higher, in 32 and 64 bit variants.

– FreeBSD 10.0 64 bit variant. Please not you will have install wxWidgets, SQLite3 and Botan yourself on this platform (no port mechanism has been introduced yet).

A Linux variant will be released at some point (I prefer FreeBSD to Linux so I do not have a Linux system at my disposal).

Please take notice that Muninn’s database file structure and encryption/decryption are completely platform-independent. This means a password database which was created on Apple Mac OS X can be used without any issues on Windows or other platforms, or vice versa. You must however, maintain the same master password if you use the file on multiple computers.

Munnin Binaries can be found on my FTP server in the muninn directory.

 

 

 

Muninn, de Login en Wachtwoord database is bijna klaar

Muninn, mijn nieuwe applicatie om logins en passwords gemakkelijk te onthouden, is bijna klaar. Een beta versie heb ik al  geplaatst in op de FTP Server.

Muninn is een snelle gemakkelijke login en password database. In tegenstelling tot anderen heb ik deze geschreven met het oog op :

  • Maximale eenvoud. Geen poespas met vele mogelijkheden en invulvelden.
  • Modern. Geen archaïsche GUI interface, maar gewoon wxWidgets 3.0 .
  • Cross-Platform :
    • Apple Mac OS X 10.6.8 en hoger
    • Microsoft Windows 7 en hoger, in i386 en x86_64 variant
    • Linux x86_64 Debian installer
    • FreeBSD 10 x86_64
  • Niet web-gebaseerd. Informatie die over het internet wordt verstuurd is kwetsbaar. Muninn bewaart de informatie op de lokale schijf.

Een afbeelding van de beta versie is hier :

MuninnDit is de Mac OS X versie, anderen volgen nog.

 

Kalender nu ook in Windows versie

Ik heb een WIN32 installer gebouwd voor de kalender applicatie, dus de kalender draait nu ook onder Windows.

Verder een voorzichtig begin gemaakt met vertalingen, de kalender kan meer of minder worden omgeschakeld naar Duits en Engels.

Hier is een screenshot van de kalender (Apple versie) :

Alle bestanden zijn te vind op de FTP server.

TI’s new Cortex-M4 line versus the rest

Recently I received an email from Texas Instruments (TI) in which they announced the availability of their new Cortex-M4 line. I really like many products TI makes. When I spoke to TI people at Nürnberg’s Embedded World fair last March, I was thrilled to hear TI would release a Cortex-M4 part in autumn 2011.

I have used their LM3S9B96 processor and am very satisfied with it – excellent peripherals, very good DMA controller, lots of memory and the inclusion of Stellarisware and SafeRTOS in ROM in the chip. I sort of expected the extension of their Cortex-M3 family with Cortex-M4 based microcontrollers that would be faster and  just as well, or better, equipped. To my disappointment, that is not the case:

  • The new Cortex-M4 line (Blizzard) is not faster than the Tempest class Cortex-M3.
  • The new Blizzard class has less internal memory (32K vs 96K).
  • The new Blizzard class has no external bus interface.
  • The new Blizzard class has no Ethernet MAC/PHY like the Tempest class.

The only things going for it seem to be the internal EEPROM, the DSP additions in the core and the fact that the IC is produced using a 65 nm process.

So, why would I use the new Blizzard class instead of the Tempest class? I see no major compelling reasons to migrate. In fact, I think that many if not most DSP style calculations can easily be done on the Tempest class if you use the IQMath library, which is used to map floating point calculations onto fixed-point ones.

Also, when comparing the Blizzard class to competing offerings from other manufacturers, it doesn’t really impress me:

  • Freescale has been offering their Kinetis controller for a while. Running at 150 MHz (the K70 type) and equipped with many useful peripherals (among them, a LCD controller) this seems better equipped than the Blizzard class, and almost twice as fast.
  • NXP will soon be releasing the LPC4000 family, which will run at 180 MHz and which has a Cortex-M4 together with a Cortex-M0 on board. Again, in terms of raw performance, this one leaves the Blizzard class kicking in the dust.
  • ST recently released their STM32F4 series. This stunning Cortex-M4  microcontroller runs (from flash) at 168 MHz – more than double the speed of the Blizzard class. It has loads of memory, many peripherals, an external bus and so on. I seriously doubt TI’s Blizzard class will beat this one.
I hope TI will follow suit and release a Cortex-M4 offering with a performance similar to or better than the ones above. If not, I think they will not compete well in the Cortex-M4 class arena.

Using new and delete in C++ on Cortex-M3

A nice way to precisely time the instantiation of an object in C++  is to use the new and delete operators. Normally, an global object is instantiated at a undefined time – the compiler decides on this, not a language standard.

Whilst delete would not be of great use in an embedded system without good memory management (the easiest implementation would be the  BGET package), new can be used efficiently to cater for a linear instantiation where the user decides when to instantiate.

If you want to use C++ without the bloat, you’re probably forced to provide your own new and delete routines. On Cortex-M3, this can be tricky.

Although the core supports unaligned memory access, unaligned multiple LDM, STM, LDRD and STRD calls will cause a Usage Fault exception. Also, using unaligned data decreases performance. When instantiating a class with a multitude of variables listed in the class, the allocated memory block could well be unaligned. Also, code could conceivably be generated that will execute a number of LDM or LDRD instructions, for example, in the constructor of the object.  It is therefore imperative that a new call will always return an aligned block of data.

A simple way to implement new and delete would be like this :

static void * toewijzing(const size_t size) /* let op: size geeft het aantal bytes aan */
{
    static UInt8 *heap_ptr = (UInt8 *)(__bss_end);
    const UInt8 *base = heap_ptr;       /*  Point to end of heap.  */
    const UInt8 *eindeSRAM = (const UInt8 *)__TOP_STACK;        

    if ((heap_ptr+size) < eindeSRAM)
    {
        heap_ptr += size;       /*  Increase heap met size aantal bytes  */
    }
    else
    {
        while(1);
        KiwandaAssert(0);
    }

    return((void *)base);               /*  Return pointer to start of new heap area.  */
}

void * operator new(size_t size) /* let op: size geeft het aantal bytes aan */
{
    return(toewijzing(size));
} 

void * operator new[](size_t size)
{
    return(toewijzing(size));
} 

void operator delete(void * ptr)
{
    // free(ptr);
} 

void operator delete[](void * ptr)
{
    //free(ptr);
}

The linker will provide the location of the end of the used SRAM space (__bss_end) and the end of the SRAM block (__TOP_STACK).  An example would be the following Linker Load file, used for the Energy Micro EFM32G880F64 processor:

************************************************************************
ARM Cortex M-3 standaard microcontroller loadscript.
Memory I/O adressen, aangepast aan eigenschappen
van de Energy Micro EFM32G880F64 Microcontroller

Alle aanpassingen in dit bestand zijn
(c) 2008-2011 Kiwanda Embedded Systemen
Voor alle overige software geldt het copyright van Atmel

$Id: efm32g880f64.ld 3041 2011-08-28 20:29:52Z oldenburgh $
************************************************************************/

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)

MEMORY
{
  FLASH (rx)   : ORIGIN = 0x00000000, LENGTH = 64K
  DATA (rw)    : ORIGIN = 0x20000000, LENGTH = 16K
}

__TOP_STACK    = ORIGIN(DATA) + LENGTH(DATA);

/* Section Definitions */

SECTIONS
{
  /* first section is .text which is used for code */

  . = ORIGIN(FLASH);
  .text :
  {
        KEEP(*(.cortexm3vectors))      /* core exceptions */
        KEEP(*(.geckovectors))          /* efm32 exceptions */
        . = ALIGN(4);
        KEEP(*(.init))
        CREATE_OBJECT_SYMBOLS
        *(.text .text.*)                   /* C en C++ code */
        *(.gnu.linkonce.t.*)
        *(.glue_7)
        *(.glue_7t)
        *(.vfp11_veneer)
        *(.ARM.extab* .gnu.linkonce.armextab.*)
        *(.gcc_except_table)
        . = ALIGN(4);
   } >FLASH

__exidx_start = .;
.ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
__exidx_end = .;

   . = ALIGN(4);

        .rodata : ALIGN (4)
        {
                *(.rodata .rodata.* .gnu.linkonce.r.*)

                . = ALIGN(4);
                KEEP(*(.init))

                . = ALIGN(4);
                __preinit_array_start = .;
                KEEP (*(.preinit_array))
                __preinit_array_end = .;

                . = ALIGN(4);
                __init_array_start = .;
                KEEP (*(SORT(.init_array.*)))
                KEEP (*(.init_array))
                __init_array_end = .;

                . = ALIGN(4);
                KEEP(*(.fini))

                . = ALIGN(4);
                __fini_array_start = .;
                KEEP (*(.fini_array))
                KEEP (*(SORT(.fini_array.*)))
                __fini_array_end = .;

                . = ALIGN(0x4);

                __CTOR_LIST__ = .;
                LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
                *(.ctors)
                LONG(0)
                __CTOR_END__ = .;
                __DTOR_LIST__ = .;
                LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
                *(.dtors)
                LONG(0)
                __DTOR_END__ = .;

                . = ALIGN(0x4);

                KEEP (*crtbegin.o(.ctors))
                KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
                KEEP (*(SORT(.ctors.*)))
                KEEP (*crtend.o(.ctors))

                . = ALIGN(0x4);
                KEEP (*crtbegin.o(.dtors))
                KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
                KEEP (*(SORT(.dtors.*)))
                KEEP (*crtend.o(.dtors))

                *(.init .init.*)
                *(.fini .fini.*)

                PROVIDE_HIDDEN (__preinit_array_start = .);
                KEEP (*(.preinit_array))
                PROVIDE_HIDDEN (__preinit_array_end = .);
                PROVIDE_HIDDEN (__init_array_start = .);
                KEEP (*(SORT(.init_array.*)))
                KEEP (*(.init_array))
                PROVIDE_HIDDEN (__init_array_end = .);
                PROVIDE_HIDDEN (__fini_array_start = .);
                KEEP (*(.fini_array))
                KEEP (*(SORT(.fini_array.*)))
                PROVIDE_HIDDEN (__fini_array_end = .);

                . = ALIGN (8);
                *(.rom)
                *(.rom.b)

        } >FLASH

   . = ALIGN(4);
   _etext = . ;
   PROVIDE (etext = .);

  /* .data section which is used for initialized data */

   _bdata = . ;
   PROVIDE (bdata = .);

  .data : AT(_bdata)
  {
    _data = . ;
    *(.data)            /* normale data */
    *(.data.*)        

    SORT(CONSTRUCTORS)
    . = ALIGN(4);
  } >DATA

   _edata = . ;
   PROVIDE (edata = .);

  . = ALIGN(4);

  /* .bss section which is used for uninitialized data */
  .bss(NOLOAD) :
  {
    _bss = . ;
    __bss_start = . ;
    __bss_start__ = . ;
    *(.bss)
    *(.bss.*)
    *(COMMON)
   __bss_end__ = . ;
   __bss_end   = . ;
   PROVIDE (_ebss = .) ;
  } >DATA

  . = ALIGN(4);

   _end = .;
   PROVIDE (end = .);    /* einde intern geheugen */

  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
   *(.data)            /* normale data */
    *(.data.*)        

    SORT(CONSTRUCTORS)
    . = ALIGN(4);
  } >DATA

   _edata = . ;
   PROVIDE (edata = .);

  . = ALIGN(4);

  /* .bss section which is used for uninitialized data */
  .bss(NOLOAD) :
  {
    _bss = . ;
    __bss_start = . ;
    __bss_start__ = . ;
    *(.bss)
    *(.bss.*)
    *(COMMON)
   __bss_end__ = . ;
   __bss_end   = . ;
   PROVIDE (_ebss = .) ;
  } >DATA

  . = ALIGN(4);

   _end = .;
   PROVIDE (end = .);    /* einde intern geheugen */

  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }

} /* einde secties */

When the size of the allocated block is not a multiple of 4, the next allocated block will be unaligned. In order to prevent this, allocation must always be done at multiples of 4 bytes. The next example will show this:

static void * toewijzing(const size_t size) /* let op: size geeft het aantal bytes aan */
{
    static UInt8 *heap_ptr = (UInt8 *)(__bss_end);
    const UInt8 *base = heap_ptr;       /*  Point to end of heap.  */
    const UInt8 *eindeSRAM = (const UInt8 *)__TOP_STACK;        

    if ((heap_ptr+size) < eindeSRAM)
    {
        heap_ptr += size;       /*  Increase heap met size aantal bytes en eindig op 32 bit boundary */

        while (((unsigned int)heap_ptr & 0x3) != 0)   /* bit 0 en 1 = 0 --> stappen in 4 * byte = 32 bytes */
            heap_ptr++;
    }
    else
    {
        while(1);
        KiwandaAssert(0);
    }

    return((void *)base);               /*  Return pointer to start of new heap area.  */
}

void * operator new(size_t size) /* let op: size geeft het aantal bytes aan */
{
    return(toewijzing(size));
} 

void * operator new[](size_t size)
{
    return(toewijzing(size));
} 

void operator delete(void * ptr)
{
     /* not applicable in embedded application */
} 

void operator delete[](void * ptr)
{
    /* not applicable in embedded application */
}

Advancing the heap_ptr to the next aligned address will prevent unaligned allocation to take place. The price paid for this is the loss of, at most, 3 bytes in between two allocated blocks. Since most systems have SRAM memory equal to or in excess of 16 KBytes, this won’t pose a major problem. During design of a class, careful placement of the data members will also prevent unaligned variables. Simply stated, group your equal variables together (for example, all unsigned char or unsigned short) and pad them, if necessary,  at the end with a dummy variable to grow to the 32 bit boundary:

UInt8 var1;
UInt16 var2[3];

/* total = 8+2*16 = 40 bits  -> we need 24 bits padding */
UInt8 padding[3];

This will prevent costly unaligned acces.

Compiling and linking C++ code on Cortex-M3 without exception handling and RTTI

Most embedded software is written in C, and software written for ARM Cortex-M3 devices is no exception (pun intended).

C has many qualities – small, clean, relatively easy to learn and above all a superior interface for performing low-level hardware operations – try writing a routine for manipulating I/O memory  in Algol or Fortran …… Why, then, divert to C++ and its supposed steeper learning curve? The answer lies in the size of the software project you write.

The price for using C becomes painfully apparent when projects grow beyond the “hello world” stage – the boundary usually lies around 1200-1500 lines of code. Writing a “hello world” demo in C is feasible and maintaining the source code is not hard. “Hello world” applications are in the order of 10-30 KBytes of Flash on Cortex-M3.

However, many Cortex-M3 devices typically offer more than 256 KByte of Flash, up to 2048 KBytes of Flash. A typical “serious” project for such a device would employ a number of serial interfaces, would perhaps include an embdded web server, would do floating point calculations and would interact with a user through buttons or a touch screen. Such a project would require ten thousand lines of C code or more, which translates into upward of 200 Kbytes of flash programmable binary code. Maintaining order in this code, which is centered around C’s function-oriented set-up, is awkward and error-prone. Doing so with a large developer team is even harder, as I know from experience. In C, devices typically use structs which contain init and state information, but how do you know if and where all information is filled out correctly in 15000 lines of code? Also, manually calling init functions and combining common code into lower level function/data sets requires a lot of discipline from a development team. Most experienced C programmers confronted with situation will start to write code defensively, in an object-oriented approach, in order to prevent errors and bugs. In a way, they use C++features without using the proper tools.

C++ offers everything you need to maintain order in larger embedded programs. Object Oriented programming was a focal point when the language was invented, without sacrificing backwards compatibility with existing C code. Device information is maintained in the class container, togerther with all necessary functions. Common code can be brought into lower base classes, enabling the sharing of the code by virtue of the inheritance mechanism of C++. It is easy to define a base interface prototype in C++ which can be used in your application code, and then use polymorphism to link your interface prototype to your device implementation. Code migration becomes very easy – your application can be brought from, let’s say STM32 to LM3S, and the only thing that you must do is develop a new interface implementation.

Problems occur when C++ is used indiscriminately. The ambivalence of many C programmers towards C++ is based on the fact that a lot of things are included in your C++ binary (bloat) which you don’t need and use.  It is important, though, to understand two things about the superset of features that C++ has with regards to C:

  • There are compile-time enhancements in C++, such as classes, virtual functions or namespaces, which are completely benign and which in most cases will not contribute to bloat. They are part of the framework that enables programmers to scale their software concepts to larger dimensions without losing overview. In larger programs, C++ will most likely lead to more compact code – see this example at GCC compiler family, but this story is also applicable to KEIL and IAR users.

    Compile your code with the -fno-rtti -fno-exceptions flags (the others such as -ggdb aren’t relevant to this discussion but I included them anyway). My compiler lives on a FreeBSD system and is therefore placed in /usr/local/bin. I named it arm-eabi instead of the more common arm-none-eabi. This may be different on your system. When using Linux, for example, every distro seems to have its own peculiar spot to place applications.

    In order to prevent using exception handling and rtti at compile time:

    /usr/local/bin/arm-eabi-g++ -mthumb -c -pipe -mcpu=cortex-m3
    -mno-thumb-interwork -mapcs-frame -funsigned-char
    -fno-threadsafe-statics -Wall -Wimplicit -Wpointer-arith
    -Wswitch -Wredundant-decls -Wreturn-type -Wshadow
    -Wunused -Werror -Winline -ggdb -fno-inline
    -fno-stack-protector -fno-rtti -fno-exceptions
    -fno-unwind-tables  -Wa,-adhlns=LISTFILE -o OBJECTFILE CPPFILE

    LISTFILE, OBJECTFILE AND CPPFILE are of course to be properly named by you.

    This stops C++ from automatically inserting exception handling (and all other stdc++ library components) in your object file. But, this isn’t the complete solution! Even with the insertion of  -fno-exceptions at compile time, you will still get exception handling (and therefore bloat) when using C++ features such as new or delete. You will need to instruct the linker that it, too, should avoid all bloat components.

    At the linking stage, use the -nostdlib option to instruct the linker to omit any library file it would automatically add – in C++‘s case, libstc++.a . I use partial linking, and the linker command for that would be:

     

    /usr/local/bin/arm-eabi-g++
    -Wl,-Ur -nostdlib -Wl,Map=MAPFILE
    -o OBJCTFILE1 OBJECTFILE2 ...

     

    At the final linking step, or at the main linking step if you don’t use partial linking, you must compensate for the missing library components that you would want to include, for instance memcpy or assert. The standard C library provides many of these things and fills the missings references in most cases. Such a linking step would be like:

    /usr/local/bin/arm-eabi-g++ -mcpu=cortex-m3 -nostdlib
    -msoft-float -nostartfiles -fno-rtti -fno-exceptions
    -Wl,--cref -Wl,-Map=MAPFILE -Wl,-TLINKERSCRIPT -o ELFFILE
    -lc -lgcc -lm

    Functionality not found in libc.a such as new or delete must be provided by you – this is the price you pay for not using libstdc++! This, however, is not very difficult to do. In C++, you can use overloaded methods to insert your own version of new and delete:. I have an article that outlines how to write these functions.

     

     

Casio calculators

Recently I purchased a new calculator because someone pointed out to me the truly amazing capabilities (natural textbook display) of Casio’s new fx-115ES calculator. Costing under €30, the calculator offers an astounding 403 functions, conversions and solvers. It also displays fractions as true fractions, in addition to the standards floating point display format.. It can handle tables for linear or cubic interpolation for which the data entry is pretty easy. It performs numerical differential and integration, and solves equations with Newton-Rapson. It can even do (limited) vector and matrix calculations.

Even though I am a big fan of Texas Instruments integrated circuits (especially  their MSP430  microcontrollers, their ADCs (ADS1178) and power ICs (TPS84620) ) I prefer Casio’s calculators over TI’s. They are not as bulky, you can get your results more quickly and they cost a lot less.

When I got the new calculator , I realised I had purchased the fx-115 before. The first model I got was the fx-115 in 1985, when I was in school. Later, it also served me well as a student at Delft University of Technology.  Looking for a model that would help me with complex numbers, I bought  the fx-115W when I worked at Oresis Communications in 2001. This was my main computational workhorse in the new millennium. And, this year I purchased the amazing fx-115ES. In a way I have been quite intimate with the fx-115 family for 26 years!

All calculators, including the 26 years old fx-115, still work flawlessly, a testimony to Casio’s build quality. Begin solar driven, I never had to replace a battery. The fx-115 now lives in a kitchen drawer and is used for domestic purposes.

My calculators are  (running :

CASIOFX115familie2

 

 

 

 

Casio calculators

Recently I purchased a new calculator because someone pointed out to me the truly amazing capabilities (natural textbook display) of Casio’s new fx-115ES calculator. Costing under €30, the calculator offers an astounding 403 functions, conversions and solvers. It also displays fractions as true fractions, in addition to the standards floating point display format.. It can handle tables for linear or cubic interpolation for which the data entry is pretty easy. It performs numerical differential and integration, and solves equations with Newton-Rapson. It can even do (limited) vector and matrix calculations.

Even though I am a big fan of Texas Instruments integrated circuits (especially  their MSP430  microcontrollers, their ADCs (ADS1178) and power ICs (TPS84620) ) I prefer Casio’s calculators over TI’s. They are not as bulky, you can get your results more quickly and they cost a lot less.

When I got the new calculator , I realised I had purchased the fx-115 before. The first model I got was the fx-115 in 1985, when I was in school. Later, it also served me well as a student at Delft University of Technology.  Looking for a model that would help me with complex numbers, I bought  the fx-115W when I worked at Oresis Communications in 2001. This was my main computational workhorse in the new millennium. And, this year I purchased the amazing fx-115ES. In a way I have been quite intimate with the fx-115 family for 26 years!

All calculators, including the 26 years old fx-115, still work flawlessly, a testimony to Casio’s build quality. Begin solar driven, I never had to replace a battery. The fx-115 now lives in a kitchen drawer and is used for domestic purposes.

My calculators are  (running :

CASIOFX115familie2

 

 

 

 

Don’t use the provider Llama Communications!

In early June 2005 I got an email from Llama Communications saying there were closing shop because of a bankruptcy. This caused me a lot of work, because domain registrations as well as email and www traffic were coming through Llamacom.

The person running the company,  Michael Grafft in West Des Moines, IA, was very emphatic but said he couldn’t do anything about it.

I switch to a local DNS registrator, Argeweb, and haven’t had any problems since switching. But Michael Grafft lied about Llamacom! They are still in business. Why they forced me out, I don’t know. He never answered my questions why or how things went wrong. So Michael, if you read this, why did you lie to me?