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