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("\"atan2\" function seems 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("\n\n\n");
 287   }
 288   EndIf;
 289   Integer32 windowWidth : = 80, windowHeight : = 23, i : = 0;
 290   PointerToCharacter lightGrayColor : = "#EEEEEE",
 291                                     lightGreenColor : = "#CCFFCC";
 292   While(i < windowWidth * windowHeight) Loop { // First, fill the window with
 293                                                // spaces and newlines.
 294     If mod(i, windowWidth) = windowWidth - 1 Then { output[i] : = '\n'; }
 295     Else { output[i] : = ' '; }
 296     EndIf;
 297     colors[i] : = lightGrayColor;
 298     i += 1;
 299   }
 300   EndWhile;
 301   Integer32 centerX : = windowWidth / 2, centerY : = windowHeight / 2,
 302                     clockRadius : = (centerX < centerY) ? centerX - 1
 303                                                         : centerY - 1;
 304 i:
 305   = 0;
 306   While(i < windowWidth * windowHeight) Loop { // Next, draw the circle which
 307                                                // represents the clock
 308     Integer32 y : = i / windowWidth, x : = mod(i, windowWidth);
 309     Decimal32 distance : = sqrt((x - centerX) * (x - centerX) +
 310                                 (y - centerY) * (y - centerY)); // Pythagorean
 311                                                                 // Theorem.
 312     If abs(distance - clockRadius) < 3. / 4 Then {
 313       output[i] : = '*';
 314       colors[i] : = lightGreenColor;
 315     }
 316     EndIf;
 317     i += 1;
 318   }
 319   EndWhile;
 320   PointerToCharacter redColor : = "#FFAAAA";
 321   // Label "12"...
 322   output[(centerY - clockRadius + 1) * windowWidth + centerX] : = '1';
 323   output[(centerY - clockRadius + 1) * windowWidth + centerX + 1] : = '2';
 324   colors[(centerY - clockRadius + 1) * windowWidth + centerX]
 325       : = colors[(centerY - clockRadius + 1) * windowWidth + centerX + 1]
 326       : = redColor;
 327   // Label "6"...
 328   output[(centerY + clockRadius - 1) * windowWidth + centerX] : = '6';
 329   colors[(centerY + clockRadius - 1) * windowWidth + centerX] : = redColor;
 330   // Label "3"...
 331   output[centerY * windowWidth + centerX + clockRadius - 1] : = '3';
 332   colors[centerY * windowWidth + centerX + clockRadius - 1] : = redColor;
 333   // Label "9"...
 334   output[centerY * windowWidth + centerX - clockRadius + 1] : = '9';
 335   colors[centerY * windowWidth + centerX - clockRadius + 1] : = redColor;
 336   // Label "1"...
 337   Integer32 y : = centerY - (clockRadius - 1) * cos(360. / 12);
 338   output[y * windowWidth + centerX + sin(360. / 12) * (clockRadius - 1)]
 339       : = '1';
 340   colors[y * windowWidth + centerX + sin(360. / 12) * (clockRadius - 1)]
 341       : = redColor;
 342 // Label "2"...
 343 y:
 344   = centerY - (clockRadius - 1.5) * cos(2 * 360. / 12);
 345   output[y * windowWidth + centerX + sin(2 * 360. / 12) * (clockRadius - 1.5)]
 346       : = '2';
 347   colors[y * windowWidth + centerX + sin(2 * 360. / 12) * (clockRadius - 1.5)]
 348       : = redColor;
 349 // Label "4"...
 350 y:
 351   = centerY - (clockRadius - 1) * cos(4 * 360. / 12);
 352   output[y * windowWidth + centerX + sin(4 * 360. / 12) * (clockRadius - 1) + 1]
 353       : = '4';
 354   colors[y * windowWidth + centerX + sin(4 * 360. / 12) * (clockRadius - 1) + 1]
 355       : = redColor;
 356 // Label "5"...
 357 y:
 358   = centerY - (clockRadius - 1) * cos(5 * 360. / 12);
 359   output[y * windowWidth + centerX + sin(5 * 360. / 12) * (clockRadius - 1) + 1]
 360       : = '5';
 361   colors[y * windowWidth + centerX + sin(5 * 360. / 12) * (clockRadius - 1) + 1]
 362       : = redColor;
 363 // Label "7"...
 364 y:
 365   = centerY - (clockRadius - 1) * cos(7 * 360. / 12);
 366   output[y * windowWidth + centerX + sin(7 * 360. / 12) * (clockRadius - 1)]
 367       : = '7';
 368   colors[y * windowWidth + centerX + sin(7 * 360. / 12) * (clockRadius - 1)]
 369       : = redColor;
 370 // Label "8"...
 371 y:
 372   = centerY - (clockRadius - 1) * cos(8 * 360. / 12);
 373   output[y * windowWidth + centerX + sin(8 * 360. / 12) * (clockRadius - 1)]
 374       : = '8';
 375   colors[y * windowWidth + centerX + sin(8 * 360. / 12) * (clockRadius - 1)]
 376       : = redColor;
 377 // Label "10"...
 378 y:
 379   = centerY - (clockRadius - 1.5) * cos(10 * 360. / 12);
 380   output[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 381          1] : = '1';
 382   output[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 383          2] : = '0';
 384   colors[y * windowWidth + centerX + sin(10 * 360. / 12) * (clockRadius - 1.5) +
 385          1] : = colors[y * windowWidth + centerX +
 386                        sin(10 * 360. / 12) * (clockRadius - 1.5) + 2]
 387       : = redColor;
 388 // Label "11"...
 389 y:
 390   = centerY - (clockRadius - 1.5) * cos(11 * 360. / 12);
 391   output[y * windowWidth + centerX + sin(11 * 360. / 12) * (clockRadius - 1.5) +
 392          1] : = output[y * windowWidth + centerX +
 393                        sin(11 * 360. / 12) * (clockRadius - 1.5) + 2] : = '1';
 394   colors[y * windowWidth + centerX + sin(11 * 360. / 12) * (clockRadius - 1.5) +
 395          1] : = colors[y * windowWidth + centerX +
 396                        sin(11 * 360. / 12) * (clockRadius - 1.5) + 2]
 397       : = redColor;
 398   Integer32 j : = 0;
 399   Decimal32 angle;
 400   PointerToCharacter blueColor : = "#AAAAFF", yellowColor : = "#FFFFAA";
 401   While j < 3 Loop {
 402     PointerToCharacter currentColor;
 403     If j = 0 Then {
 404     angle:
 405       = fmod(hour + minute / 60., 12) * (360. / 12);
 406     currentColor:
 407       = redColor;
 408     }
 409     ElseIf j = 1 Then {
 410     angle:
 411       = minute * (360. / 60);
 412     currentColor:
 413       = yellowColor;
 414     }
 415     Else {
 416     angle:
 417       = second * (360 / 60);
 418     currentColor:
 419       = blueColor;
 420     }
 421     EndIf;
 422     Decimal32 endOfTheHandX
 423         : = centerX +
 424             sin(angle) * clockRadius / (j = 0 ? 2. : j = 1 ? 3. / 2 : 4. / 3),
 425         endOfTheHandY : = centerY - cos(angle) * clockRadius /
 426                                         (j = 0 ? 2. : j = 1 ? 3. / 2 : 4. / 3),
 427         coefficientOfTheDirection
 428         : = (endOfTheHandY - centerY) /
 429             (abs(endOfTheHandX - centerX) = 0 ?
 430                                               // Protect ourselves from
 431                                               // dividing by 0.
 432                                                 1. / 100
 433                                               : endOfTheHandX - centerX);
 434     logString("Drawing line between (");
 435     logInteger(centerX);
 436     logString(",");
 437     logInteger(centerY);
 438     logString(") and (");
 439     logInteger(endOfTheHandX);
 440     logString(",");
 441     logInteger(endOfTheHandY);
 442     logString(").\n");
 443   i:
 444     = 0;
 445     While(i < windowWidth * windowHeight) Loop {
 446       Decimal32 lowerBoundX
 447           : = endOfTheHandX < centerX ? endOfTheHandX : Decimal32(centerX),
 448           upperBoundX : = endOfTheHandX > centerX ? endOfTheHandX
 449                                                   : Decimal32(centerX),
 450           lowerBoundY : = endOfTheHandY < centerY ? endOfTheHandY : centerY,
 451           upperBoundY : = endOfTheHandY > centerY ? endOfTheHandY : centerY;
 452     y:
 453       = i / windowWidth;
 454       Integer32 x : = mod(i, windowWidth), isXWithinBounds;
 455     isXWithinBounds:
 456       = lowerBoundX <= x <= upperBoundX;
 457       Integer32 isYWithinBounds;
 458     isYWithinBounds:
 459       = lowerBoundY <= y <= upperBoundY;
 460       If isXWithinBounds and isYWithinBounds Then {
 461         Decimal32 expectedX, expectedY;
 462       expectedY:
 463         = (x - centerX) * coefficientOfTheDirection + centerY;
 464       expectedX:
 465         = (y - centerY) * (1 / coefficientOfTheDirection) + centerX;
 466         If coefficientOfTheDirection = 1. / 0 Then {
 467         expectedY:
 468           = 1000; // Bigger than any possible value.
 469         }
 470         EndIf;
 471         If coefficientOfTheDirection = 0 Then {
 472         expectedX:
 473           = 1000; // Again, bigger than any possible.
 474         }
 475         EndIf;
 476         logString("The point (");
 477         logInteger(x);
 478         logString(",");
 479         logInteger(y);
 480         logString(") is within bounds, expectedY is ");
 481         logInteger(expectedY);
 482         logString(" and expectedX is ");
 483         logInteger(expectedX);
 484         logString(".\n");
 485         Character currentSign : = j = 0 ? 'h' : j = 1 ? 'm' : 's';
 486         If((abs(upperBoundX - lowerBoundX) < 1 + 1 / PRECISION or
 487             abs(upperBoundY - lowerBoundY) < 1 + 1 / PRECISION) and
 488                output[i] = ' ') Then {
 489           output[i] : = currentSign;
 490           colors[i] : = currentColor;
 491         }
 492         EndIf;
 493         If(abs(expectedX - x) < 3. / 4 or abs(expectedY - y) < 3. / 4) and
 494             output[i] = ' ' Then {
 495           output[i] : = currentSign;
 496           colors[i] : = currentColor;
 497         }
 498         EndIf;
 499       }
 500       EndIf;
 501       i += 1;
 502     }
 503     EndWhile;
 504     j += 1;
 505   }
 506   EndWhile;
 507   // Let's draw some ornaments.
 508   PointerToCharacter darkGreenColor : = "#AAFFAA";
 509 i:
 510   = 0;
 511   While(i < windowWidth * windowHeight) Loop {
 512   y:
 513     = i / windowWidth;
 514     Integer32 x : = mod(i, windowWidth);
 515     If abs(windowHeight - 2 * ln(1 + abs((x - centerX) / 2.)) -
 516            y)<1 - abs(x - centerX) / (centerX * 95. / 112) and x> 1. /
 517             2 * centerX and x <
 518         3. / 2 * centerX and output[i] =
 519         ' ' Then { // The logarithmic curve looks somewhat
 520                    // like a lemma of a flower.
 521       output[i] : = 'x';
 522       colors[i] : = darkGreenColor;
 523     }
 524     EndIf;
 525     i += 1;
 526   }
 527   EndWhile;
 528 // Let's try to make it look like the bottom of the lemma isn't floating
 529 // in the air.
 530 j:
 531   = 0;
 532   While j < 3 Loop {
 533   i:
 534     = windowWidth * (windowHeight - 1); // So, move to the beginning of
 535                                         // the last line.
 536     While(i < windowWidth * windowHeight) Loop {
 537       If j < 2 and (output[i - windowWidth] =
 538                         'x' and (output[i + 1] = 'x' or output[i - 1] = 'x'))
 539                        Then {
 540         output[i] : = 'x';
 541         colors[i] : = darkGreenColor;
 542       }
 543       ElseIf j =
 544           2 and (output[i + 1] = ' ' and output[i - windowWidth] = 'x') Then {
 545         output[i] : = ' ';
 546       }
 547       EndIf;
 548       i += 1;
 549     }
 550     EndWhile;
 551     j += 1;
 552   }
 553   EndWhile;
 554   // The digital clock in the corner...
 555   output[windowWidth * windowHeight - 2] : = '0' + mod(second, 10);
 556   output[windowWidth * windowHeight - 3] : = '0' + second / 10;
 557   colors[windowWidth * windowHeight - 2]
 558       : = colors[windowWidth * windowHeight - 3] : = blueColor;
 559   output[windowWidth * windowHeight - 4] : = ':';
 560   output[windowWidth * windowHeight - 5] : = '0' + mod(minute, 10);
 561   output[windowWidth * windowHeight - 6] : = '0' + minute / 10;
 562   colors[windowWidth * windowHeight - 5]
 563       : = colors[windowWidth * windowHeight - 6] : = yellowColor;
 564   output[windowWidth * windowHeight - 7] : = ':';
 565   output[windowWidth * windowHeight - 8] : = '0' + mod(hour, 10);
 566   output[windowWidth * windowHeight - 9] : = '0' + hour / 10;
 567   colors[windowWidth * windowHeight - 8]
 568       : = colors[windowWidth * windowHeight - 9] : = redColor;
 569   // My signature...
 570   Character signature[100] : = {0};
 571   PointerToCharacter signature : = AddressOf(signature[0]);
 572   // AEC, unlike C, always makes a clear distinction between
 573   // arrays and pointers.
 574   logString("Empty signature has length of: ");
 575   logInteger(strlen(signature));
 576   logString("\n");
 577   strcat(signature, " Analog Clock for WebAssembly\n");
 578   logString("The first row of the signature has length of: ");
 579   logInteger(strlen(signature));
 580   logString("\n");
 581   strcat(signature, " Made in AEC by\n");
 582   logString("The first two rows of signature have length: ");
 583   logInteger(strlen(signature));
 584   logString("\n");
 585   strcat(signature, " Teo Samarzija");
 586   logString("Signature has length of: ");
 587   logInteger(strlen(signature));
 588   logString(" \n\n"); // Let's mark the last log of this function like this.
 589 i:
 590   = windowWidth * (windowHeight - 3);
 591 j:
 592   = 0;
 593   PointerToCharacter modraColor : = "#AAFFFF"; // Sorry, I don't know how
 594                                                // to say "modra" in English,
 595                                                // and I am not wasting my time
 596                                                // looking that up.
 597   While not(signature[j] = 0) Loop {
 598     If signature[j] = '\n' Then {
 599     i:
 600       = (i / windowWidth + 1) * windowWidth;
 601     }
 602     ElseIf not(signature[j] = 0) Then {
 603       output[i] : = signature[j];
 604       colors[i] : = modraColor;
 605       i += 1;
 606     }
 607     Else { output[i] : = ' '; }
 608     EndIf;
 609     j += 1;
 610   }
 611   EndWhile;
 612 }
 613 EndFunction;
 614