User Tools

Site Tools


features

This is an old revision of the document!


Features

Here are some of the more notable features of 10LC (both the language and the compiler) and how it differs from 9LC.

Stream and Line Comments

In addition to the standard 9LC comment feature, you can also use // for a line comment and /* */ for stream/block comments. You can also use the stream comment in the middle of a statement.

// This Is A Line Comment
 
/* And This
Is A Block
Comment */
 
If RegA == 0x42 Goto /*ThatPlace*/ ThisPlace;

Decimal and Hexadecimal Numbers

You can freely mix decimal and hexadecimal values in code, so feel free to use whatever is most convenient or appropriate.

Write @ 100 = 220;
Write @ 0x929 = 0x62; 
Write @ 0x3F33 = 227;

Enhanced Semantic Checks and Warnings

I like to think that 10LC is a little smarter than 9LC and because of that, there some additional checks that 10LC can perform during the compilation and post-compile steps that might help spot bugs in your code.

For example, if you have a the Z80 Pod specified (an 8-bit CPU), and perform a Write operation that writes a value greater than 0x100 (255) to a memory location, a warning will now be emitted because that value is too large for an 8-bit word.

Also, address ranges are validated against the specified Pod as well.

The 10LC compiler will also check for things like Programs, Labels, Constants or Aliases that are never referenced, helping you look for bugs like cut-n-paste errors where you may have forgotten to change something. Here are some example diagnostic warnings:

ScriptFile.s(45:1): Warning L0525: A global Constant named Speech1 was created but was never referenced
ScriptFile.s(51:1): Warning L0529: A global Alias named Unused was created but was never referenced
ScriptFile.s(74:5): Warning L0518: Program Main contains a defined Label UnusedLabel which was never referenced
ScriptFile.s(230:5): Warning L0519: Program TestSound contains a constant named None which was never referenced
ScriptFile.s(231:5): Warning L0519: Program TestSound contains a constant named Mute1 which was never referenced
ScriptFile.s(237:5): Warning L0519: Program TestSound contains a constant named Enable2 which was never referenced

You can check out the full list of Warnings and Errors that can be emitted by 10LC.

Enhanced Symbolic Names for Programs and Labels

9LC already supported the use of symbolic identifiers for things like Program Names and Labels. However, their use was somewhat limited.

As an example, because 9LC did not prefix hexadecimal values (see above), you could not create symbolic identifiers that consisted solely of hexadecimal digits. Also, 9LC's symbolic names only used 8 characters of significance, so it could not disambiguate identifiers like THISNAME0 and THISNAME5. With 10LC, the length of symbol names is effectively unbounded, and all characters are significant.

Lastly, note that all symbols in 10LC are case-insensitive so displaytest, DisplayTest, and DISPLAYTEST all refer to the same symbol.

Program CodeToTestInputs;
Program DisplayTest;
Program DeadBeef;
 
:DammitJimImADoctorNotATechnician 
:ABC

Enhanced Program Numbering Support

Like with 9LC, you can force a Program to a specific program number, but with 10LC even if you do, you can also use a symbolic name at the same time. Because of this, there is little reason to force specific program numbers.

Additionally, if you specify a specific program number, you will not create “holes” in the numbering system. Unlike 9LC, 10LC numbers Programs dynamically in the order they are encountered during compilation, and they draw from a pool of available program numbers so that there are no limitations on the numbers chosen.

For example, in 9LC, the third Program below would have a value of 6, but in 10LC it will have a value of 1. Also, if you continue and create another five dynamically numbered Programs, they will be numbered 2, 3, 4, 6, and 7. The manually-specified program number will not mess up the numbering.

Program TestInputs;     // Create Program TestInputs, Defaults To Program 0 
Program TestDisplay 5;  // Create Program TestDisplay, Sets Program Number To 5 
Program DEADBeef;       // Create Program DEADBeef, Defaults To Program **1**, Not 6 

Register Aliases

Aliases allow you to refer to the standard register set (REG0-REGF) using symbolic names. This is similar to existing 9LC functionality but without its restrictions. Unless disabled by the NoPreDefines compiler option, 10LC will automatically create the following legacy register Aliases:

  • BITMASK = REGA
  • ROMSIG = REGB
  • STSCTL = REGC
  • BITNUM = REGD
  • DAT = REGE
  • ADR = REGF
  • PDBAT = REG0

One of the differences between 10LC's Alias statement and 9LC's ASSIGN statement is that an Alias can be created anywhere within a Program (or globally, see below) - they do not have to be all grouped together in a dedicated section at the top of the file. Once an Alias has been defined, it cannot be undefined nor redefined to another Register.

Alias Control1Bits = Reg1; 
Alias Control2Bits = Reg2;

Aliases can be Global or Local. A Global Alias is created by setting an Alias statement outside of a Program. Global Aliases are visible to all other Programs being compiled. Local Aliases are created within a Program and are only visible to that specific Program.

Constant Values

You can create constant values that work like register Aliases but are strictly for constant numeric values and can be used anywhere a numeric value would be allowed. Once a Const has been defined in a Program, it cannot be undefined nor redefined to another value. Constants are not global and are scoped to the Program in which they are defined.

Like Aliases, Constants can be Global or Local. A Global Constant is created by setting a Const statement outside of a Program. Global Constants are visible to all other Programs being compiled. Local Constants are created within a Program and are only visible to that specific Program.

Const JoystickNone  = 0; 
Const JoystickUp    = 0x01; 
Const JoystickDown  = 0x02; 
Const JoystickLeft  = 0x04; 
Const JoystickRight = 0x08;

Less-Than and Less-Than-Or-Equals Tests

10LC implements LT and LTE tests by reversing the test expression and using the existing GT/GTE tests.

If Control1Bits < Control2Bits Goto TestButtons;
If Control1Bits <= Control2Bits Goto TestButtons; 

Operator Equals Shortcuts

C-style Operator-Equals expressions are supported, which may be more familiar and natural to people familiar with other programming languages.

Control1Bits = ~0x23;
Control2Bits = ~Reg5;
Control1Bits = ~Control2Bits;
Reg3 <<= 4;
Reg1 >>= 3;
Reg1 &= 0x8F;
Reg1 |= Reg5;

Standalone Unary Operators

The Complement, Increment and Decrement operations can be written using a unary shorthand.

~reg3;
++Reg5;
--Control1Bits;
Reg5--;
Control2Bits++;

(Since statements each execute atomically, there is no difference between prefix or postfix notation for increment and decrement.)

READ INTO Shortcut

The READ statement normally reads values into Register E (REGE), so if you want to preserve the value you just obtained and perform another READ, you have to copy the value into another register. The READ INTO variation of the READ statement automatically inserts this copy code for you:

Read From 0x3200 Into Control1Bits; 
Read From 0x3201 Into Control2Bits;

Sequence Reading Shortcut

If you need to read a small block of contiguous memory into a set of registers, you normally have to create a block of Read and register copy statements. 10LC has a ReadEx statement that provides a shortcut way of doing this. This statement allows you to set a starting place in memory followed by a list of registers. 10LC will automatically unroll this ReadEx statement into multiple Read and register copy statements (essentially Read Into statements), automatically incrementing the address as it goes. Aliases and Registers can all be used for the target registers. For example:

ReadEx From 0x2000 Into RegA RegB Control2Bits Reg1;

The above code will compile into the equivalent of the following discrete Read Into statements:

Read From 0x2000 Into RegA;
Read From 0x2001 Into RegB;
Read From 0x2002 Into Reg2;
Read From 0x2003 Into Reg1;

Sequence Writing Shortcut

If you need to fill small block of contiguous memory with specific values, you have to create a block of Write statements. 10LC has a WriteEx statement that provides a shortcut way of doing this. This statement allows you to set a starting place in memory followed by a list of values. 10LC will automatically unroll this WriteEx statement into multiple Write statements, automatically incrementing the address as it goes. Numeric values, Aliases and Registers can all be used. For example:

WriteEx @ 0x1000 = 1 2 4 0x10 0x20 0x40 Control2Bits Reg4;

The above code will compile into the equivalent of the following discrete Write statements:

Write @ 1000 = 1;
Write @ 1001 = 2;
Write @ 1002 = 4;
Write @ 1003 = 0x10;
Write @ 1004 = 0x20;
Write @ 1005 = 0x40;
Write @ 1006 = Reg2;
Write @ 1007 = Reg4;

Compiler Directives

You can add special directives to your code to change the behavior of the compiler. For example, any compiler option can be enabled or disabled in code via a special directive. This can be used to do things like temporarily disable optimizations to help ensure that a block of code runs exactly as you have written it.

Options/Features are enabled by adding a plus character to the end of the option/feature and are disabled by adding a minus character.

#DisableOptimizations+
#EnableIntrinsicCommands+

Optimizations

The 10LC compiler will perform a few optimizations when it detects static constant tests or redundant code. For example, assigning a register to itself usually does nothing other than waste code space and will be optimized away by the compiler. Likewise, an IF expression that will always be true will optimize to the resulting GOTO statement, and an IF expression that is always false will be optimized away entirely.

Value = Value;                           /* Will Be Optimized Away */
If ~Reg1 == ~Reg1 Goto OptimizedLabel;   /* True-Optimized To Just The GOTO */
If ~Reg1 < ~Reg1 Goto OptimizedLabel;    /* False-Optimized Away*/

Debugging Steps

A “step over pause” can be simulated by inserting code in-between each compiled statement that calls a program which shows the source code line number and stops execution until you press <Cont> on the 9010A. This feature is enabled and disabled by compiler directives in your code. For example, the following code would cause cause a pause and the line number to be shown before the execution of each of the last two WRITE operations.

Write @ 0x2323 = 127;
Write @ 0x2324 = 128;
#DebugStep+
Write @ 0x2325 = 0x20;
Write @ 0x2326 = 0x40;
#DebugStep-

Address Range Validation

If you specify the SetupPod statement and select a Pod, information about that Pod's CPU is used to do things like validate address values. For example, if you select a Pod whose CPU only has a 14-bit address space, and then in a program you specify an address that exceeds that 14-bit range, a warning is issued by the compiler. This can help you find out-of-range addresses before you actually run your code.

Signature Calculation

The compiler executable has the feature of being able to calculate 9010A signature values. It can do this with single files, multiple files, and even files contained within ZIP archives. It can output information in one of three ways: standard, minimal or CSV.

Example of standard output from calculating signatures on C:\TenLCStuff\MyGamesRoms.zip:

Extracting files from C:\TenLCStuff\MyGamesRoms.zip
C:\TenLCStuff\MyGamesRoms.zip - 2048 bytes
1h.bin: 0x1BB5
C:\TenLCStuff\MyGamesRoms.zip - 2048 bytes
1k.bin: 0xE28A
C:\TenLCStuff\MyGamesRoms.zip - 32 bytes
6l.bpr: 0xC471
C:\TenLCStuff\MyGamesRoms.zip - 2048 bytes
7l: 0x804C
C:\TenLCStuff\MyGamesRoms.zip - 2048 bytes
galmidw.u: 0xFFDE
C:\TenLCStuff\MyGamesRoms.zip - 2048 bytes
galmidw.v: 0x4055
C:\TenLCStuff\MyGamesRoms.zip - 2048 bytes
galmidw.w: 0xFCD2
C:\TenLCStuff\MyGamesRoms.zip - 2048 bytes
galmidw.y: 0x317B

Example of minimal output from calculating signatures on C:\TenLCStuff\MyGamesRoms.zip:

1h.bin: 0x1BB5
1k.bin: 0xE28A
6l.bpr: 0xC471
7l: 0x804C
galmidw.u: 0xFFDE
galmidw.v: 0x4055
galmidw.w: 0xFCD2
galmidw.y: 0x317B

Example of CSV output from calculating signatures on C:\TenLCStuff\MyGamesRoms.zip:

Source,Filename,Signature
"C:\TenLCStuff\MyGamesRoms.zip","1h.bin","7093",
"C:\TenLCStuff\MyGamesRoms.zip","1k.bin","57994",
"C:\TenLCStuff\MyGamesRoms.zip","6l.bpr","50289",
"C:\TenLCStuff\MyGamesRoms.zip","7l","32844",
"C:\TenLCStuff\MyGamesRoms.zip","galmidw.u","65502",
"C:\TenLCStuff\MyGamesRoms.zip","galmidw.v","16469",
"C:\TenLCStuff\MyGamesRoms.zip","galmidw.w","64722",
"C:\TenLCStuff\MyGamesRoms.zip","galmidw.y","12667",

The algorithm used to calculate the signatures was derived from multiple sources including: https://github.com/ieure/flukesig/blob/master/README.md http://www.paladingrp.com/brianb/fluke_9010a/checksum.txt http://tech.quarterarcade.com/tech/Fluke/9010A/CalculateSignature.aspx

features.1582756778.txt.gz · Last modified: 2020/02/26 16:39 by jtwine