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 (I guess it's called Calculus
 196    * 2 in the English-speaking world) using the system of equations (Cauchy
 197    * system):
 198    *
 199    * sin(0)=0
 200    * cos(0)=1
 201    * sin'(x)=cos(x)
 202    * cos'(x)=-sin(x)
 203    * ---------------
 204    *
 205    * Let's translate that as literally as possible to the programming
 206    * language.
 207    */
 208   Decimal32 radians : = degrees / oneRadianInDegrees, tmpsin : = 0,
 209                     tmpcos : = 1, epsilon : = radians / PRECISION, i : = 0;
 210   While((epsilon > 0 and i < radians) or (epsilon < 0 and i > radians)) Loop {
 211     tmpsin += epsilon * tmpcos;
 212     tmpcos -= epsilon * tmpsin;
 213     i += epsilon;
 214   }
 215   EndWhile;
 216   Return sineMemoisation[asm_f32("(f32.nearest (f32.load %degrees))")]
 217       : = tmpsin;
 218 }
 219 EndFunction;
 220 
 221 Function arcsin(Decimal32 x) Which Returns Decimal32 Does {
 222   Return arctan(x / sqrt(1 - x * x)); // Highschool mathematics.
 223 }
 224 EndFunction;
 225 
 226 Function arccos(Decimal32 x) Which Returns Decimal32 Does {
 227   Return 90 - arcsin(x); // Basic common sense to somebody who understands
 228                          // what those terms mean.
 229 }
 230 EndFunction;
 231 
 232 Function cos(Decimal32 degrees) Which Returns Decimal32 Does {
 233   Return sin(90 - degrees); // Again, basic common sense to somebody who
 234                             // understands what those terms means.
 235 }
 236 EndFunction;
 237 
 238 Function tan(Decimal32 degrees) Which Returns Decimal32 Does {
 239   Return sin(degrees) / cos(degrees); // The definition.
 240 }
 241 EndFunction;
 242 
 243 Function abs(Decimal32 x) Which Returns Decimal32 Does {
 244   Return x < 0 ? -x : x;
 245 }
 246 EndFunction;
 247 
 248 // Now, let's implement some string manipulation functions. We can't
 249 // call the methods of the JavaScript "String" class here.
 250 Function strlen(PointerToCharacter ptr) Which Returns Integer32 Does {
 251   Return ValueAt(ptr) = 0 ? 0 : 1 + strlen(ptr + 1);
 252 }
 253 EndFunction;
 254 
 255 Function strcat(PointerToCharacter first,
 256                 PointerToCharacter second) Which Returns Nothing Does {
 257   first += strlen(first);
 258   While ValueAt(second) Loop {
 259     ValueAt(first) : = ValueAt(second);
 260     first += 1;
 261     second += 1;
 262   }
 263   EndWhile;
 264   ValueAt(first) : = 0;
 265 }
 266 EndFunction;
 267 
 268 // And, finally, we can start porting the original AEC Analog Clock,
 269 // which was targeting x86, to WebAssembly.
 270 Function updateClockToTime(Integer32 hour, Integer32 minute,
 271                            Integer32 second) Which Returns Nothing Does {
 272   If((abs(atan2(sin(5), cos(5)) - 5) > 1) or
 273      (abs(atan2(sin(95), cos(95)) - 95) > 1) or
 274      (abs(atan2(sin(185), cos(185)) - 185) > 1) or
 275      (abs(atan2(sin(275), cos(275)) - 275) > 1) or
 276      (abs(arcsin(sin(60)) - 60) > 1) or (abs(arccos(cos(60)) - 60) > 1)) Then {
 277     logString("The cyclometric functions seem not to work!\n");
 278     logString("atan2(sin(5), cos(5))=");
 279     logInteger(atan2(sin(5), cos(5)));
 280     logString("\natan2(sin(95),cos(95))=");
 281     logInteger(atan2(sin(95), cos(95)));
 282     logString("\natan2(sin(185),cos(185))=");
 283     logInteger(atan2(sin(185), cos(185)));
 284     logString("\natan2(sin(275),cos(275))=");
 285     logInteger(atan2(sin(275), cos(275)));
 286     logString("\narcsin(sin(60))=");
 287     logInteger(arcsin(sin(60)));
 288     logString("\narccos(cos(60))=");
 289     logInteger(arccos(cos(60)));
 290     logString("\n\n");
 291   }
 292   EndIf;
 293   Integer32 windowWidth : = 80, windowHeight : = 23, i : = 0;
 294   PointerToCharacter lightGrayColor : = "#EEEEEE",
 295                                     lightGreenColor : = "#CCFFCC";
 296   While(i < windowWidth * windowHeight) Loop { // First, fill the window with
 297                                                // spaces and newlines.
 298     If mod(i, windowWidth) = windowWidth - 1 Then { output[i] : = '\n'; }
 299     Else { output[i] : = ' '; }
 300     EndIf;
 301     colors[i] : = lightGrayColor;
 302     i += 1;
 303   }
 304   EndWhile;
 305   Integer32 centerX : = windowWidth / 2, centerY : = windowHeight / 2,
 306                     clockRadius : = (centerX < centerY) ? centerX - 1
 307                                                         : centerY - 1;
 308 i:
 309   = 0;
 310   While(i < windowWidth * windowHeight) Loop { // Next, draw the circle which
 311                                                // represents the clock
 312     Integer32 y : = i / windowWidth, x : = mod(i, windowWidth);
 313     Decimal32 distance : = sqrt((x - centerX) * (x - centerX) +
 314                                 (y - centerY) * (y - centerY)); // Pythagorean
 315                                                                 // Theorem.
 316     If abs(distance - clockRadius) < 3. / 4 Then {
 317       output[i] : = '*';
 318       colors[i] : = lightGreenColor;
 319     }
 320     EndIf;
 321     i += 1;
 322   }
 323   EndWhile;
 324   PointerToCharacter redColor : = "#FFAAAA";
 325   // Label "12"...
 326   output[(centerY - clockRadius + 1) * windowWidth + centerX] : = '1';
 327   output[(centerY - clockRadius + 1) * windowWidth + centerX + 1] : = '2';
 328   colors[(centerY - clockRadius + 1) * windowWidth + centerX]
 329       : = colors[(centerY - clockRadius + 1) * windowWidth + centerX + 1]
 330       : = redColor;
 331   // Label "6"...
 332   output[(centerY + clockRadius - 1) * windowWidth + centerX] : = '6';
 333   colors[(centerY + clockRadius - 1) * windowWidth + centerX] : = redColor;
 334   // Label "3"...
 335   output[centerY * windowWidth + centerX + clockRadius - 1] : = '3';
 336   colors[centerY * windowWidth + centerX + clockRadius - 1] : = redColor;
 337   // Label "9"...
 338   output[centerY * windowWidth + centerX - clockRadius + 1] : = '9';
 339   colors[centerY * windowWidth + centerX - clockRadius + 1] : = redColor;
 340   // Label "1"...
 341   Integer32 y : = centerY - (clockRadius - 1) * cos(360. / 12);
 342   output[y * windowWidth + centerX + sin(360. / 12) * (clockRadius - 1)]
 343       : = '1';
 344   colors[y * windowWidth + centerX + sin(360. / 12) * (clockRadius - 1)]
 345       : = redColor;
 346 // Label "2"...
 347 y:
 348   = centerY - (clockRadius - 1.5) * cos(2 * 360. / 12);
 349   output[y * windowWidth + centerX + sin(2 * 360. / 12) * (clockRadius - 1.5)]
 350       : = '2';
 351   colors[y * windowWidth + centerX + sin(2 * 360. / 12) * (clockRadius - 1.5)]
 352       : = redColor;
 353 // Label "4"...
 354 y:
 355   = centerY - (clockRadius - 1) * cos(4 * 360. / 12);
 356   output[y * windowWidth + centerX + sin(4 * 360. / 12) * (clockRadius - 1) + 1]
 357       : = '4';
 358   colors[y * windowWidth + centerX + sin(4 * 360. / 12) * (clockRadius - 1) + 1]
 359       : = redColor;
 360 // Label "5"...
 361 y:
 362   = centerY - (clockRadius - 1) * cos(5 * 360. / 12);
 363   output[y * windowWidth + centerX + sin(5 * 360. / 12) * (clockRadius - 1) + 1]
 364       : = '5';
 365   colors[y * windowWidth + centerX + sin(5 * 360. / 12) * (clockRadius - 1) + 1]
 366       : = redColor;
 367 // Label "7"...
 368 y:
 369   = centerY - (clockRadius - 1) * cos(7 * 360. / 12);
 370   output[y * windowWidth + centerX + sin(7 * 360. / 12) * (clockRadius - 1)]
 371       : = '7';
 372   colors[y * windowWidth + centerX + sin(7 * 360. / 12) * (clockRadius - 1)]
 373       : = redColor;
 374 // Label "8"...
 375 y:
 376   = centerY - (clockRadius - 1) * cos(8 * 360. / 12);
 377   output[y * windowWidth + centerX + sin(8 * 360. / 12) * (clockRadius - 1)]
 378       : = '8';
 379   colors[y * windowWidth + centerX + sin(8 * 360. / 12) * (clockRadius - 1)]
 380       : = redColor;
 381 // Label "10"...
 382 y:
 383   = centerY - (clockRadius - 1.5) * cos(10 * 360. / 12);
 384   output[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 385          1] : = '1';
 386   output[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 387          2] : = '0';
 388   colors[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 389          1] : = colors[y * windowWidth + centerX +
 390                        sin(10 * 360. / 12) * (clockRadius - 1.5) + 2]
 391       : = redColor;
 392 // Label "11"...
 393 y:
 394   = centerY - (clockRadius - 1.5) * cos(11 * 360. / 12);
 395   output[y * windowWidth + centerX + sin(11 * 360. / 12) * (clockRadius - 1.5) +
 396          1] : = output[y * windowWidth + centerX +
 397                        sin(11 * 360. / 12) * (clockRadius - 1.5) + 2] : = '1';
 398   colors[y * windowWidth + centerX + sin(11 * 360. / 12) * (clockRadius - 1.5) +
 399          1] : = colors[y * windowWidth + centerX +
 400                        sin(11 * 360. / 12) * (clockRadius - 1.5) + 2]
 401       : = redColor;
 402   Integer32 j : = 0;
 403   Decimal32 angle;
 404   PointerToCharacter blueColor : = "#AAAAFF", yellowColor : = "#FFFFAA";
 405   While j < 3 Loop {
 406     PointerToCharacter currentColor;
 407     If j = 0 Then {
 408     angle:
 409       = fmod(hour + minute / 60., 12) * (360. / 12);
 410     currentColor:
 411       = redColor;
 412     }
 413     ElseIf j = 1 Then {
 414     angle:
 415       = minute * (360. / 60);
 416     currentColor:
 417       = yellowColor;
 418     }
 419     Else {
 420     angle:
 421       = second * (360 / 60);
 422     currentColor:
 423       = blueColor;
 424     }
 425     EndIf;
 426     Decimal32 endOfTheHandX
 427         : = centerX +
 428             sin(angle) * clockRadius / (j = 0 ? 2. : j = 1 ? 3. / 2 : 4. / 3),
 429         endOfTheHandY : = centerY - cos(angle) * clockRadius /
 430                                         (j = 0 ? 2. : j = 1 ? 3. / 2 : 4. / 3),
 431         coefficientOfTheDirection
 432         : = (endOfTheHandY - centerY) /
 433             (abs(endOfTheHandX - centerX) = 0 ?
 434                                               // Protect ourselves from
 435                                               // dividing by 0.
 436                                                 1. / 100
 437                                               : endOfTheHandX - centerX);
 438     logString("Drawing line between (");
 439     logInteger(centerX);
 440     logString(",");
 441     logInteger(centerY);
 442     logString(") and (");
 443     logInteger(endOfTheHandX);
 444     logString(",");
 445     logInteger(endOfTheHandY);
 446     logString(").\n");
 447   i:
 448     = 0;
 449     While(i < windowWidth * windowHeight) Loop {
 450       Decimal32 lowerBoundX
 451           : = endOfTheHandX < centerX ? endOfTheHandX : Decimal32(centerX),
 452           upperBoundX : = endOfTheHandX > centerX ? endOfTheHandX
 453                                                   : Decimal32(centerX),
 454           lowerBoundY : = endOfTheHandY < centerY ? endOfTheHandY : centerY,
 455           upperBoundY : = endOfTheHandY > centerY ? endOfTheHandY : centerY;
 456     y:
 457       = i / windowWidth;
 458       Integer32 x : = mod(i, windowWidth), isXWithinBounds;
 459     isXWithinBounds:
 460       = lowerBoundX <= x <= upperBoundX;
 461       Integer32 isYWithinBounds;
 462     isYWithinBounds:
 463       = lowerBoundY <= y <= upperBoundY;
 464       If isXWithinBounds and isYWithinBounds Then {
 465         Decimal32 expectedX, expectedY;
 466       expectedY:
 467         = (x - centerX) * coefficientOfTheDirection + centerY;
 468       expectedX:
 469         = (y - centerY) * (1 / coefficientOfTheDirection) + centerX;
 470         If coefficientOfTheDirection = 1. / 0 Then {
 471         expectedY:
 472           = 1000; // Bigger than any possible value.
 473         }
 474         EndIf;
 475         If coefficientOfTheDirection = 0 Then {
 476         expectedX:
 477           = 1000; // Again, bigger than any possible.
 478         }
 479         EndIf;
 480         logString("The point (");
 481         logInteger(x);
 482         logString(",");
 483         logInteger(y);
 484         logString(") is within bounds, expectedY is ");
 485         logInteger(expectedY);
 486         logString(" and expectedX is ");
 487         logInteger(expectedX);
 488         logString(".\n");
 489         Character currentSign : = j = 0 ? 'h' : j = 1 ? 'm' : 's';
 490         If((abs(upperBoundX - lowerBoundX) < 1 + 1 / PRECISION or
 491             abs(upperBoundY - lowerBoundY) < 1 + 1 / PRECISION) and
 492                output[i] = ' ') Then {
 493           output[i] : = currentSign;
 494           colors[i] : = currentColor;
 495         }
 496         EndIf;
 497         If(abs(expectedX - x) < 3. / 4 or abs(expectedY - y) < 3. / 4) and
 498             output[i] = ' ' Then {
 499           output[i] : = currentSign;
 500           colors[i] : = currentColor;
 501         }
 502         EndIf;
 503       }
 504       EndIf;
 505       i += 1;
 506     }
 507     EndWhile;
 508     j += 1;
 509   }
 510   EndWhile;
 511   // Let's draw some ornaments.
 512   PointerToCharacter darkGreenColor : = "#AAFFAA";
 513 i:
 514   = 0;
 515   While(i < windowWidth * windowHeight) Loop {
 516   y:
 517     = i / windowWidth;
 518     Integer32 x : = mod(i, windowWidth);
 519     If abs(windowHeight - 2 * ln(1 + abs((x - centerX) / 2.)) -
 520            y)<1 - abs(x - centerX) / (centerX * 95. / 112) and x> 1. /
 521             2 * centerX and x <
 522         3. / 2 * centerX and output[i] =
 523         ' ' Then { // The logarithmic curve looks somewhat
 524                    // like a lemma of a flower.
 525       output[i] : = 'x';
 526       colors[i] : = darkGreenColor;
 527     }
 528     EndIf;
 529     i += 1;
 530   }
 531   EndWhile;
 532 // Let's try to make it look like the bottom of the lemma isn't floating
 533 // in the air.
 534 j:
 535   = 0;
 536   While j < 3 Loop {
 537   i:
 538     = windowWidth * (windowHeight - 1); // So, move to the beginning of
 539                                         // the last line.
 540     While(i < windowWidth * windowHeight) Loop {
 541       If j < 2 and (output[i - windowWidth] =
 542                         'x' and (output[i + 1] = 'x' or output[i - 1] = 'x'))
 543                        Then {
 544         output[i] : = 'x';
 545         colors[i] : = darkGreenColor;
 546       }
 547       ElseIf j =
 548           2 and (output[i + 1] = ' ' and output[i - windowWidth] = 'x') Then {
 549         output[i] : = ' ';
 550       }
 551       EndIf;
 552       i += 1;
 553     }
 554     EndWhile;
 555     j += 1;
 556   }
 557   EndWhile;
 558   // The digital clock in the corner...
 559   output[windowWidth * windowHeight - 2] : = '0' + mod(second, 10);
 560   output[windowWidth * windowHeight - 3] : = '0' + second / 10;
 561   colors[windowWidth * windowHeight - 2]
 562       : = colors[windowWidth * windowHeight - 3] : = blueColor;
 563   output[windowWidth * windowHeight - 4] : = ':';
 564   output[windowWidth * windowHeight - 5] : = '0' + mod(minute, 10);
 565   output[windowWidth * windowHeight - 6] : = '0' + minute / 10;
 566   colors[windowWidth * windowHeight - 5]
 567       : = colors[windowWidth * windowHeight - 6] : = yellowColor;
 568   output[windowWidth * windowHeight - 7] : = ':';
 569   output[windowWidth * windowHeight - 8] : = '0' + mod(hour, 10);
 570   output[windowWidth * windowHeight - 9] : = '0' + hour / 10;
 571   colors[windowWidth * windowHeight - 8]
 572       : = colors[windowWidth * windowHeight - 9] : = redColor;
 573   // My signature...
 574   Character signature[100] : = {0};
 575   PointerToCharacter signature : = AddressOf(signature[0]);
 576   // AEC, unlike C, always makes a clear distinction between
 577   // arrays and pointers.
 578   logString("Empty signature has length of: ");
 579   logInteger(strlen(signature));
 580   logString("\n");
 581   strcat(signature, " Analog Clock for WebAssembly\n");
 582   logString("The first row of the signature has length of: ");
 583   logInteger(strlen(signature));
 584   logString("\n");
 585   strcat(signature, " Made in AEC by\n");
 586   logString("The first two rows of signature have length: ");
 587   logInteger(strlen(signature));
 588   logString("\n");
 589   strcat(signature, " Teo Samarzija");
 590   logString("Signature has length of: ");
 591   logInteger(strlen(signature));
 592   logString("\n\n"); // Let's mark the last log of this function like this.
 593 i:
 594   = windowWidth * (windowHeight - 3);
 595 j:
 596   = 0;
 597   PointerToCharacter modraColor : = "#AAFFFF"; // Sorry, I don't know how
 598                                                // to say "modra" in English,
 599                                                // and I am not wasting my time
 600                                                // looking that up.
 601   While not(signature[j] = 0) Loop {
 602     If signature[j] = '\n' Then {
 603     i:
 604       = (i / windowWidth + 1) * windowWidth;
 605     }
 606     ElseIf not(signature[j] = 0) Then {
 607       output[i] : = signature[j];
 608       colors[i] : = modraColor;
 609       i += 1;
 610     }
 611     Else { output[i] : = ' '; }
 612     EndIf;
 613     j += 1;
 614   }
 615   EndWhile;
 616 }
 617 EndFunction;
 618