1 /*
   2  * This is my attempt to port the Analog Clock in AEC, previously available
   3  * for Linux and DOS, to WebAssembly.
   4  */
   5 
   6 // Let's import some functions useful for debugging from JavaScript...
   7 Function logString(PointerToCharacter str) Which Returns Nothing Is External;
   8 Function logInteger(Integer32 int) Which Returns Nothing Is External;
   9 
  10 // Now let's define a character array that will be used both by JS and AEC.
  11 Character output[23 * 80];
  12 PointerToCharacter colors[23 * 80]; // Will contain pointers to the CSS names
  13                                     // of colors of the characters in the
  14                                     //"output".
  15 
  16 Function getAddressOfOutput() Which Returns PointerToCharacter
  17     Does { // The curly brace here is optional and is just a hint to advanced
  18            // text editors and IDEs (made mostly for C-like languages) of how to
  19            // format the code, it's ignored by the AEC parser.
  20   Return AddressOf(output[0]); // When I didn't implement the "extern"
  21                                // keyword into my AEC compiler...
  22 }
  23 EndFunction;
  24 
  25 Function getAddressOfColors() Which Returns PointerToPointerToCharacter Does {
  26   Return AddressOf(colors[0]);
  27 }
  28 EndFunction;
  29 
  30 // Now, let's implement some mathematical functions. We can't use JavaScript
  31 //"Math.sin" and similar because they are methods of the global "Math"
  32 // object, and there is, as far as I know, no way to import JavaScript
  33 // objects into WebAssembly (or some language that compiles into it).
  34 // I wonder whether this is what operating system development feels like.
  35 // In the OS development, the only code that's running on a virtual machine
  36 // is your own, and you can only use a debugger running outside of that
  37 // virtual machine. Here, quite some code runs on JavaScript Virtual
  38 // Machine, but the only code I can call is the code I've written myself in
  39 // my own programming language and some debugging functions (logString and
  40 // logInteger).
  41 Decimal32 PRECISION : = 512; // So that we can balance between speed and
  42                              // precision.
  43 Integer32 USE_WEBASSEMBLY_SQRT_INSTRUCTION
  44     : = 1; // When I first ported this program to WebAssembly, I did not know
  45            // WebAssembly had the instruction for computing the square root, and
  46            // not just the four basic arithmetic operations. Let us test how
  47            // fast it is, whether it is any faster than simple binary search I
  48            // can write myself.
  49 Function ln(Decimal32 x) Which Returns Decimal32 Does {
  50   // Natural logarithm is the integral of 1/x from 1 to x, highschool math.
  51   Decimal32 sum : = 0, epsilon : = (x - 1) / (5 * PRECISION), i : = 1;
  52   While(epsilon > 0 and i < x) or (epsilon < 0 and i > x) Loop {
  53     sum += epsilon / i;
  54     i += epsilon;
  55   }
  56   EndWhile; // The semicolon after "EndWhile" and "EndIf" and "EndFunction" is
  57             // optional, but it provides a hint to the IDEs (made for C-like
  58             // languages). I have started a StackExchange question about why
  59             // some languages require a semicolon after "EndIf", "EndWhile" and
  60             // "EndFunction": https://langdev.stackexchange.com/q/392/330
  61   Return sum;
  62 }
  63 EndFunction;
  64 
  65 Function exp(Decimal32 x) Which Returns Decimal32 Does {
  66   // Euler's Algorithm from Mathematics 2...
  67   Decimal32 i : = 0, y : = 1, epsilon : = x / PRECISION;
  68   While(epsilon > 0 and i < x) or (epsilon < 0 and i > x) Loop {
  69     y += epsilon * y;
  70     i += epsilon;
  71   }
  72   EndWhile;
  73   Return y;
  74 }
  75 EndFunction;
  76 
  77 Function sqrt(Decimal32 x) Which Returns Decimal32 Does {
  78   If USE_WEBASSEMBLY_SQRT_INSTRUCTION Then {
  79     Return asm_f32(
  80         R"multiline((f32.sqrt
  81 	(f32.load
  82 		%x ;;The compiler will replace "%x" with assembly code representing a pointer to the variable "x".
  83 	)
  84 ))multiline"); // I have started a StackExchange question about how to better
  85                // implement inline assembly:
  86                // https://langdev.stackexchange.com/q/1715/330
  87   }
  88   EndIf;
  89   // Binary Search Algorithm...
  90   Decimal32 max
  91       : = 80 * 80 + 24 * 24, // This function will be used for calculating the
  92                              // Euclidean distance between cells in the display
  93                              // grid, and there will be 80x24 cells.
  94       min : = 0, i : = (min + max) / 2;
  95   If(max * max < x) Then // Shouldn't happen, but let's deal with that anyway.
  96   {
  97     Return exp(
  98         ln(x) /
  99         2); // Much less precise (and probably slower) than binary search.
 100   }
 101   EndIf;
 102   While((max - min) > 1 / PRECISION) Loop {
 103     If(i * i > x) Then {
 104       max /*
 105            * ClangFormat apparently misinterprets the assignment operator ":="
 106            * as the C label marker ':' followed by the C '=' operator,
 107            * there doesn't appear to be a simple solution to this problem.
 108            * I have opened a StackExchange question about that:
 109            * https://langdev.stackexchange.com/q/1695/330
 110            */
 111           : = i;
 112     }
 113     Else {
 114     min:
 115       = i;
 116     }
 117     EndIf;
 118   i:
 119     = (max + min) / 2;
 120   }
 121   EndWhile;
 122   Return i;
 123 }
 124 EndFunction;
 125 
 126 Function fmod(Decimal32 a, Decimal32 b) Which Returns Decimal32 Does {
 127   Return(a - b * Integer32(a / b));
 128 }
 129 EndFunction;
 130 
 131 // Now, let's implement trigonometric and cyclometric functions.
 132 Decimal32 oneRadianInDegrees
 133     : = 180 / pi; //"180/pi" is a compile-time decimal
 134                   // constant (since we are assigning an
 135                   // initial value to a global variable),
 136                   // and, as such, we can use "pi" to
 137                   // refer to M_PI from the C library,
 138                   // it's available to the compiler.
 139 
 140 Function arctan(Decimal32 x) Which Returns Decimal32 Does {
 141   // Arcus tangens is equal to the integral of 1/(1+x^2), highschool math.
 142   Decimal32 sum : = 0, epsilon : = x / PRECISION, i : = 0;
 143   While(i < x) Loop {
 144     sum += epsilon / (1 + i * i);
 145     i += epsilon;
 146   }
 147   EndWhile;
 148   Return(sum * oneRadianInDegrees);
 149 }
 150 EndFunction;
 151 
 152 Function atan2(Decimal32 y, Decimal32 x) Which Returns Decimal32 Does {
 153   If(y = 0) Then {
 154     If(x < 0) Then { Return 180; }
 155     Else { Return 0; }
 156     EndIf;
 157   }
 158   ElseIf(x = 0) Then {
 159     If y < 0 Then { Return 270; }
 160     Else { Return 90; }
 161     EndIf;
 162   }
 163   Else {
 164     If(x > 0 and y > 0) Then { Return arctan(y / x); }
 165     ElseIf(x < 0 and y > 0) Then { Return 90 + arctan(-x / y); }
 166     ElseIf(x < 0 and y < 0) Then { Return 180 + arctan(y / x); }
 167     Else { Return 270 + arctan(-x / y); }
 168     EndIf;
 169   }
 170   EndIf;
 171 }
 172 EndFunction;
 173 
 174 Function cos(Decimal32 degrees)
 175     Which Returns Decimal32 Is Declared; // Because "sin" and "cos" are
 176                                          // circularly dependent on one another.
 177 
 178 Decimal32 sineMemoisation[91];
 179 
 180 Function sin(Decimal32 degrees) Which Returns Decimal32 Does {
 181   If(degrees < 0) Then { Return - sin(-degrees); }
 182   EndIf;
 183   If degrees > 90 Then { Return cos(degrees - 90); }
 184   EndIf;
 185   If not(sineMemoisation[asm_f32("(f32.nearest (f32.load %degrees))")] = 0)
 186       Then { // I've used inline assembly here because nothing else I
 187              // write will output "f32.nearest" (called "round" in most
 188              // programming languages) WebAssembly directive, and it's way
 189              // more convenient to insert some inline assembly than to modify
 190              // and recompile the compiler.
 191     Return sineMemoisation[asm_f32("(f32.nearest (f32.load %degrees))")];
 192   }
 193   EndIf;
 194   /*
 195    * Sine and cosine are defined in Mathematics 2 using the system of
 196    * equations (Cauchy system):
 197    *
 198    * sin(0)=0
 199    * cos(0)=1
 200    * sin'(x)=cos(x)
 201    * cos'(x)=-sin(x)
 202    * ---------------
 203    *
 204    * Let's translate that as literally as possible to the programming
 205    * language.
 206    */
 207   Decimal32 radians : = degrees / oneRadianInDegrees, tmpsin : = 0,
 208                     tmpcos : = 1, epsilon : = radians / PRECISION, i : = 0;
 209   While((epsilon > 0 and i < radians) or (epsilon < 0 and i > radians)) Loop {
 210     tmpsin += epsilon * tmpcos;
 211     tmpcos -= epsilon * tmpsin;
 212     i += epsilon;
 213   }
 214   EndWhile;
 215   Return sineMemoisation[asm_f32("(f32.nearest (f32.load %degrees))")]
 216       : = tmpsin;
 217 }
 218 EndFunction;
 219 
 220 Function arcsin(Decimal32 x) Which Returns Decimal32 Does {
 221   Return arctan(x / sqrt(1 - x * x)); // Highschool mathematics.
 222 }
 223 EndFunction;
 224 
 225 Function arccos(Decimal32 x) Which Returns Decimal32 Does {
 226   Return 90 - arcsin(x); // Basic common sense to somebody who understands
 227                          // what those terms mean.
 228 }
 229 EndFunction;
 230 
 231 Function cos(Decimal32 degrees) Which Returns Decimal32 Does {
 232   Return sin(90 - degrees); // Again, basic common sense to somebody who
 233                             // understands what those terms means.
 234 }
 235 EndFunction;
 236 
 237 Function tan(Decimal32 degrees) Which Returns Decimal32 Does {
 238   Return sin(degrees) / cos(degrees); // The definition.
 239 }
 240 EndFunction;
 241 
 242 Function abs(Decimal32 x) Which Returns Decimal32 Does {
 243   Return x < 0 ? -x : x;
 244 }
 245 EndFunction;
 246 
 247 // Now, let's implement some string manipulation functions. We can't
 248 // call the methods of the JavaScript "String" class here.
 249 Function strlen(PointerToCharacter ptr) Which Returns Integer32 Does {
 250   Return ValueAt(ptr) = 0 ? 0 : 1 + strlen(ptr + 1);
 251 }
 252 EndFunction;
 253 
 254 Function strcat(PointerToCharacter first,
 255                 PointerToCharacter second) Which Returns Nothing Does {
 256   first += strlen(first);
 257   While ValueAt(second) Loop {
 258     ValueAt(first) : = ValueAt(second);
 259     first += 1;
 260     second += 1;
 261   }
 262   EndWhile;
 263   ValueAt(first) : = 0;
 264 }
 265 EndFunction;
 266 
 267 // And, finally, we can start porting the original AEC Analog Clock,
 268 // which was targeting x86, to WebAssembly.
 269 Function updateClockToTime(Integer32 hour, Integer32 minute,
 270                            Integer32 second) Which Returns Nothing Does {
 271   If((abs(atan2(sin(5), cos(5)) - 5) > 1) or
 272      (abs(atan2(sin(95), cos(95)) - 95) > 1) or
 273      (abs(atan2(sin(185), cos(185)) - 185) > 1) or
 274      (abs(atan2(sin(275), cos(275)) - 275) > 1) or
 275      (abs(arcsin(sin(60)) - 60) > 1) or (abs(arccos(cos(60)) - 60) > 1)) Then {
 276     logString("\"atan2\" function seems not to work!\n");
 277     logString("atan2(sin(5), cos(5))=");
 278     logInteger(atan2(sin(5), cos(5)));
 279     logString("\natan2(sin(95),cos(95))=");
 280     logInteger(atan2(sin(95), cos(95)));
 281     logString("\natan2(sin(185),cos(185))=");
 282     logInteger(atan2(sin(185), cos(185)));
 283     logString("\natan2(sin(275),cos(275))=");
 284     logInteger(atan2(sin(275), cos(275)));
 285     logString("\n\n\n");
 286   }
 287   EndIf;
 288   Integer32 windowWidth : = 80, windowHeight : = 23, i : = 0;
 289   PointerToCharacter lightGrayColor : = "#EEEEEE",
 290                                     lightGreenColor : = "#CCFFCC";
 291   While(i < windowWidth * windowHeight) Loop { // First, fill the window with
 292                                                // spaces and newlines.
 293     If mod(i, windowWidth) = windowWidth - 1 Then { output[i] : = '\n'; }
 294     Else { output[i] : = ' '; }
 295     EndIf;
 296     colors[i] : = lightGrayColor;
 297     i += 1;
 298   }
 299   EndWhile;
 300   Integer32 centerX : = windowWidth / 2, centerY : = windowHeight / 2,
 301                     clockRadius : = (centerX < centerY) ? centerX - 1
 302                                                         : centerY - 1;
 303 i:
 304   = 0;
 305   While(i < windowWidth * windowHeight) Loop { // Next, draw the circle which
 306                                                // represents the clock
 307     Integer32 y : = i / windowWidth, x : = mod(i, windowWidth);
 308     Decimal32 distance : = sqrt((x - centerX) * (x - centerX) +
 309                                 (y - centerY) * (y - centerY)); // Pythagorean
 310                                                                 // Theorem.
 311     If abs(distance - clockRadius) < 3. / 4 Then {
 312       output[i] : = '*';
 313       colors[i] : = lightGreenColor;
 314     }
 315     EndIf;
 316     i += 1;
 317   }
 318   EndWhile;
 319   PointerToCharacter redColor : = "#FFAAAA";
 320   // Label "12"...
 321   output[(centerY - clockRadius + 1) * windowWidth + centerX] : = '1';
 322   output[(centerY - clockRadius + 1) * windowWidth + centerX + 1] : = '2';
 323   colors[(centerY - clockRadius + 1) * windowWidth + centerX]
 324       : = colors[(centerY - clockRadius + 1) * windowWidth + centerX + 1]
 325       : = redColor;
 326   // Label "6"...
 327   output[(centerY + clockRadius - 1) * windowWidth + centerX] : = '6';
 328   colors[(centerY + clockRadius - 1) * windowWidth + centerX] : = redColor;
 329   // Label "3"...
 330   output[centerY * windowWidth + centerX + clockRadius - 1] : = '3';
 331   colors[centerY * windowWidth + centerX + clockRadius - 1] : = redColor;
 332   // Label "9"...
 333   output[centerY * windowWidth + centerX - clockRadius + 1] : = '9';
 334   colors[centerY * windowWidth + centerX - clockRadius + 1] : = redColor;
 335   // Label "1"...
 336   Integer32 y : = centerY - (clockRadius - 1) * cos(360. / 12);
 337   output[y * windowWidth + centerX + sin(360. / 12) * (clockRadius - 1)]
 338       : = '1';
 339   colors[y * windowWidth + centerX + sin(360. / 12) * (clockRadius - 1)]
 340       : = redColor;
 341 // Label "2"...
 342 y:
 343   = centerY - (clockRadius - 1.5) * cos(2 * 360. / 12);
 344   output[y * windowWidth + centerX + sin(2 * 360. / 12) * (clockRadius - 1.5)]
 345       : = '2';
 346   colors[y * windowWidth + centerX + sin(2 * 360. / 12) * (clockRadius - 1.5)]
 347       : = redColor;
 348 // Label "4"...
 349 y:
 350   = centerY - (clockRadius - 1) * cos(4 * 360. / 12);
 351   output[y * windowWidth + centerX + sin(4 * 360. / 12) * (clockRadius - 1) + 1]
 352       : = '4';
 353   colors[y * windowWidth + centerX + sin(4 * 360. / 12) * (clockRadius - 1) + 1]
 354       : = redColor;
 355 // Label "5"...
 356 y:
 357   = centerY - (clockRadius - 1) * cos(5 * 360. / 12);
 358   output[y * windowWidth + centerX + sin(5 * 360. / 12) * (clockRadius - 1) + 1]
 359       : = '5';
 360   colors[y * windowWidth + centerX + sin(5 * 360. / 12) * (clockRadius - 1) + 1]
 361       : = redColor;
 362 // Label "7"...
 363 y:
 364   = centerY - (clockRadius - 1) * cos(7 * 360. / 12);
 365   output[y * windowWidth + centerX + sin(7 * 360. / 12) * (clockRadius - 1)]
 366       : = '7';
 367   colors[y * windowWidth + centerX + sin(7 * 360. / 12) * (clockRadius - 1)]
 368       : = redColor;
 369 // Label "8"...
 370 y:
 371   = centerY - (clockRadius - 1) * cos(8 * 360. / 12);
 372   output[y * windowWidth + centerX + sin(8 * 360. / 12) * (clockRadius - 1)]
 373       : = '8';
 374   colors[y * windowWidth + centerX + sin(8 * 360. / 12) * (clockRadius - 1)]
 375       : = redColor;
 376 // Label "10"...
 377 y:
 378   = centerY - (clockRadius - 1.5) * cos(10 * 360. / 12);
 379   output[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 380          1] : = '1';
 381   output[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 382          2] : = '0';
 383   colors[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 384          1] : = colors[y * windowWidth + centerX +
 385                        sin(10 * 360. / 12) * (clockRadius - 1.5) + 2]
 386       : = redColor;
 387 // Label "11"...
 388 y:
 389   = centerY - (clockRadius - 1.5) * cos(11 * 360. / 12);
 390   output[y * windowWidth + centerX + sin(11 * 360. / 12) * (clockRadius - 1.5) +
 391          1] : = output[y * windowWidth + centerX +
 392                        sin(11 * 360. / 12) * (clockRadius - 1.5) + 2] : = '1';
 393   colors[y * windowWidth + centerX + sin(11 * 360. / 12) * (clockRadius - 1.5) +
 394          1] : = colors[y * windowWidth + centerX +
 395                        sin(11 * 360. / 12) * (clockRadius - 1.5) + 2]
 396       : = redColor;
 397   Integer32 j : = 0;
 398   Decimal32 angle;
 399   PointerToCharacter blueColor : = "#AAAAFF", yellowColor : = "#FFFFAA";
 400   While j < 3 Loop {
 401     PointerToCharacter currentColor;
 402     If j = 0 Then {
 403     angle:
 404       = fmod(hour + minute / 60., 12) * (360. / 12);
 405     currentColor:
 406       = redColor;
 407     }
 408     ElseIf j = 1 Then {
 409     angle:
 410       = minute * (360. / 60);
 411     currentColor:
 412       = yellowColor;
 413     }
 414     Else {
 415     angle:
 416       = second * (360 / 60);
 417     currentColor:
 418       = blueColor;
 419     }
 420     EndIf;
 421     Decimal32 endOfTheHandX
 422         : = centerX +
 423             sin(angle) * clockRadius / (j = 0 ? 2. : j = 1 ? 3. / 2 : 4. / 3),
 424         endOfTheHandY : = centerY - cos(angle) * clockRadius /
 425                                         (j = 0 ? 2. : j = 1 ? 3. / 2 : 4. / 3),
 426         coefficientOfTheDirection
 427         : = (endOfTheHandY - centerY) /
 428             (abs(endOfTheHandX - centerX) = 0 ?
 429                                               // Protect ourselves from
 430                                               // dividing by 0.
 431                                                 1. / 100
 432                                               : endOfTheHandX - centerX);
 433     logString("Drawing line between (");
 434     logInteger(centerX);
 435     logString(",");
 436     logInteger(centerY);
 437     logString(") and (");
 438     logInteger(endOfTheHandX);
 439     logString(",");
 440     logInteger(endOfTheHandY);
 441     logString(").\n");
 442   i:
 443     = 0;
 444     While(i < windowWidth * windowHeight) Loop {
 445       Decimal32 lowerBoundX
 446           : = endOfTheHandX < centerX ? endOfTheHandX : Decimal32(centerX),
 447           upperBoundX : = endOfTheHandX > centerX ? endOfTheHandX
 448                                                   : Decimal32(centerX),
 449           lowerBoundY : = endOfTheHandY < centerY ? endOfTheHandY : centerY,
 450           upperBoundY : = endOfTheHandY > centerY ? endOfTheHandY : centerY;
 451     y:
 452       = i / windowWidth;
 453       Integer32 x : = mod(i, windowWidth), isXWithinBounds;
 454     isXWithinBounds:
 455       = (x > lowerBoundX or x = lowerBoundX) and
 456         (x < upperBoundX or x = upperBoundX);
 457       Integer32 isYWithinBounds;
 458     isYWithinBounds:
 459       = (y > lowerBoundY or y = lowerBoundY) and
 460         (y < upperBoundY or y = upperBoundY);
 461       If isXWithinBounds and isYWithinBounds Then {
 462         Decimal32 expectedX, expectedY;
 463       expectedY:
 464         = (x - centerX) * coefficientOfTheDirection + centerY;
 465       expectedX:
 466         = (y - centerY) * (1 / coefficientOfTheDirection) + centerX;
 467         If coefficientOfTheDirection = 1. / 0 Then {
 468         expectedY:
 469           = 1000; // Bigger than any possible value.
 470         }
 471         EndIf;
 472         If coefficientOfTheDirection = 0 Then {
 473         expectedX:
 474           = 1000; // Again, bigger than any possible.
 475         }
 476         EndIf;
 477         logString("The point (");
 478         logInteger(x);
 479         logString(",");
 480         logInteger(y);
 481         logString(") is within bounds, expectedY is ");
 482         logInteger(expectedY);
 483         logString(" and expectedX is ");
 484         logInteger(expectedX);
 485         logString(".\n");
 486         Character currentSign : = j = 0 ? 'h' : j = 1 ? 'm' : 's';
 487         If((abs(upperBoundX - lowerBoundX) < 1 + 1 / PRECISION or
 488             abs(upperBoundY - lowerBoundY) < 1 + 1 / PRECISION) and
 489                output[i] = ' ') Then {
 490           output[i] : = currentSign;
 491           colors[i] : = currentColor;
 492         }
 493         EndIf;
 494         If(abs(expectedX - x) < 3. / 4 or abs(expectedY - y) < 3. / 4) and
 495             output[i] = ' ' Then {
 496           output[i] : = currentSign;
 497           colors[i] : = currentColor;
 498         }
 499         EndIf;
 500       }
 501       EndIf;
 502       i += 1;
 503     }
 504     EndWhile;
 505     j += 1;
 506   }
 507   EndWhile;
 508   // Let's draw some ornaments.
 509   PointerToCharacter darkGreenColor : = "#AAFFAA";
 510 i:
 511   = 0;
 512   While(i < windowWidth * windowHeight) Loop {
 513   y:
 514     = i / windowWidth;
 515     Integer32 x : = mod(i, windowWidth);
 516     If abs(windowHeight - 2 * ln(1 + abs((x - centerX) / 2.)) -
 517            y)<1 - abs(x - centerX) / (centerX * 95. / 112) and x> 1. /
 518             2 * centerX and x <
 519         3. / 2 * centerX and output[i] =
 520         ' ' Then { // The logarithmic curve looks somewhat
 521                    // like a lemma of a flower.
 522       output[i] : = 'x';
 523       colors[i] : = darkGreenColor;
 524     }
 525     EndIf;
 526     i += 1;
 527   }
 528   EndWhile;
 529 // Let's try to make it look like the bottom of the lemma isn't floating
 530 // in the air.
 531 j:
 532   = 0;
 533   While j < 3 Loop {
 534   i:
 535     = windowWidth * (windowHeight - 1); // So, move to the beginning of
 536                                         // the last line.
 537     While(i < windowWidth * windowHeight) Loop {
 538       If j < 2 and (output[i - windowWidth] =
 539                         'x' and (output[i + 1] = 'x' or output[i - 1] = 'x'))
 540                        Then {
 541         output[i] : = 'x';
 542         colors[i] : = darkGreenColor;
 543       }
 544       ElseIf j =
 545           2 and (output[i + 1] = ' ' and output[i - windowWidth] = 'x') Then {
 546         output[i] : = ' ';
 547       }
 548       EndIf;
 549       i += 1;
 550     }
 551     EndWhile;
 552     j += 1;
 553   }
 554   EndWhile;
 555   // The digital clock in the corner...
 556   output[windowWidth * windowHeight - 2] : = '0' + mod(second, 10);
 557   output[windowWidth * windowHeight - 3] : = '0' + second / 10;
 558   colors[windowWidth * windowHeight - 2]
 559       : = colors[windowWidth * windowHeight - 3] : = blueColor;
 560   output[windowWidth * windowHeight - 4] : = ':';
 561   output[windowWidth * windowHeight - 5] : = '0' + mod(minute, 10);
 562   output[windowWidth * windowHeight - 6] : = '0' + minute / 10;
 563   colors[windowWidth * windowHeight - 5]
 564       : = colors[windowWidth * windowHeight - 6] : = yellowColor;
 565   output[windowWidth * windowHeight - 7] : = ':';
 566   output[windowWidth * windowHeight - 8] : = '0' + mod(hour, 10);
 567   output[windowWidth * windowHeight - 9] : = '0' + hour / 10;
 568   colors[windowWidth * windowHeight - 8]
 569       : = colors[windowWidth * windowHeight - 9] : = redColor;
 570   // My signature...
 571   Character signature[100] : = {0};
 572   PointerToCharacter signature : = AddressOf(signature[0]);
 573   // AEC, unlike C, always makes a clear distinction between
 574   // arrays and pointers.
 575   logString("Empty signature has length of: ");
 576   logInteger(strlen(signature));
 577   logString("\n");
 578   strcat(signature, " Analog Clock for WebAssembly\n");
 579   logString("The first row of the signature has length of: ");
 580   logInteger(strlen(signature));
 581   logString("\n");
 582   strcat(signature, " Made in AEC by\n");
 583   logString("The first two rows of signature have length: ");
 584   logInteger(strlen(signature));
 585   logString("\n");
 586   strcat(signature, " Teo Samarzija");
 587   logString("Signature has length of: ");
 588   logInteger(strlen(signature));
 589   logString(" \n\n"); // Let's mark the last log of this function like this.
 590 i:
 591   = windowWidth * (windowHeight - 3);
 592 j:
 593   = 0;
 594   PointerToCharacter modraColor : = "#AAFFFF"; // Sorry, I don't know how
 595                                                // to say "modra" in English,
 596                                                // and I am not wasting my time
 597                                                // looking that up.
 598   While not(signature[j] = 0) Loop {
 599     If signature[j] = '\n' Then {
 600     i:
 601       = (i / windowWidth + 1) * windowWidth;
 602     }
 603     ElseIf not(signature[j] = 0) Then {
 604       output[i] : = signature[j];
 605       colors[i] : = modraColor;
 606       i += 1;
 607     }
 608     Else { output[i] : = ' '; }
 609     EndIf;
 610     j += 1;
 611   }
 612   EndWhile;
 613 }
 614 EndFunction;
 615