Skip to content

Commit

Permalink
Merge PR #16 Add assembly support for AT v3
Browse files Browse the repository at this point in the history
  • Loading branch information
deleterium authored May 1, 2022
2 parents e55798d + cb17969 commit 4771358
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 3 deletions.
8 changes: 8 additions & 0 deletions dev/src/SmartC/__tests__/assembly.a.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ describe('Assembly compilation:', () => {
const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode()
expect(result.MachineCodeHashId).toBe('17223659044509638052')
})
it('should compile: atv3 opCodes and apiCodes', () => {
const code = '^comment new opCodes SIP-37\nSLP\nPOW @base $exp\nMDV @x $y $den\n^comment new APICodes SIP-37\nFUN @ret Check_Sig_B_With_A\nFUN @ret Get_Code_Hash_Id\nFUN @ret Get_Activation_Fee\nFUN Put_Last_Block_GSig_In_A\n^comment new APICodes SIP-38\nFUN @ret Get_Map_Value_Keys_In_A\nFUN Set_Map_Value_Keys_In_A\n^comment new APICodes SIP-39\nFUN @ret Issue_Asset\nFUN Mint_Asset\nFUN Distribute_To_Asset_Holders\nFUN @ret Get_Asset_Holders_Count\n'
const MachineCode = '2a1900000000010000002c02000000030000000400000035060205000000350c0305000000350d0405000000320e043507040500000032080435090405000000320a04320b04350c0405000000'
const MachineData = ''
const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode()
expect(result.ByteCode).toBe(MachineCode)
expect(result.ByteData).toBe(MachineData)
})
})

describe('Assembly wrong code', () => {
Expand Down
17 changes: 15 additions & 2 deletions dev/src/SmartC/assembler/assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ opCode: 0x16, name: 'MOD_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*MOD\s+@(\w+)\s+\$(\w+)\s*$/ },
{ opCode: 0x17, name: 'SHL_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SHL\s+@(\w+)\s+\$(\w+)\s*$/ },
{ opCode: 0x18, name: 'SHR_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SHR\s+@(\w+)\s+\$(\w+)\s*$/ },
{ opCode: 0x19, name: 'POW_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*POW\s+@(\w+)\s+\$(\w+)\s*$/ }, // POW @var $var
{ opCode: 0x1a, name: 'JMP_ADR', size: 5, argsType: ['J'], regex: /^\s*JMP\s+:(\w+)\s*$/ },
{ opCode: 0x1b, name: 'BZR_DAT', size: 6, argsType: ['I', 'B'], regex: /^\s*BZR\s+\$(\w+)\s+:(\w+)\s*$/ },
{ opCode: 0x1e, name: 'BNZ_DAT', size: 6, argsType: ['I', 'B'], regex: /^\s*BNZ\s+\$(\w+)\s+:(\w+)\s*$/ },
Expand All @@ -136,7 +137,9 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ opCode: 0x27, name: 'STZ_DAT', size: 5, argsType: ['I'], regex: /^\s*STZ\s+\$(\w+)\s*$/ },
{ opCode: 0x28, name: 'FIN_IMD', size: 1, argsType: [], regex: /^\s*FIN\s*$/ },
{ opCode: 0x29, name: 'STP_IMD', size: 1, argsType: [], regex: /^\s*STP\s*$/ },
{ opCode: 0x2a, name: 'SLP_IMD', size: 1, argsType: [], regex: /^\s*SLP\s*$/ },
{ opCode: 0x2b, name: 'ERR_ADR', size: 5, argsType: ['J'], regex: /^\s*ERR\s+:(\w+)\s*$/ },
{ opCode: 0x2c, name: 'MDV_DAT', size: 13, argsType: ['I', 'I', 'I'], regex: /^\s*MDV\s+@(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/ }, // MDV @var $var $var
{ opCode: 0x30, name: 'SET_PCS', size: 1, argsType: [], regex: /^\s*PCS\s*$/ },
{ opCode: 0x32, name: 'EXT_FUN', size: 3, argsType: ['F'], regex: /^\s*FUN\s+(\w+)\s*$/ },
{ opCode: 0x33, name: 'EXT_FUN_DAT', size: 7, argsType: ['F', 'I'], regex: /^\s*FUN\s+(\w+)\s+\$(\w+)\s*$/ },
Expand Down Expand Up @@ -196,6 +199,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ name: 'check_HASH160_A_with_B', apiCode: 0x0203, opCode: 0x35 },
{ name: 'SHA256_A_to_B', apiCode: 0x0204, opCode: 0x32 },
{ name: 'check_SHA256_A_with_B', apiCode: 0x0205, opCode: 0x35 },
{ name: 'Check_Sig_B_With_A', apiCode: 0x0206, opCode: 0x35 },
{ name: 'get_Block_Timestamp', apiCode: 0x0300, opCode: 0x35 },
{ name: 'get_Creation_Timestamp', apiCode: 0x0301, opCode: 0x35 },
{ name: 'get_Last_Block_Timestamp', apiCode: 0x0302, opCode: 0x35 },
Expand All @@ -208,13 +212,22 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ name: 'message_from_Tx_in_A_to_B', apiCode: 0x0309, opCode: 0x32 },
{ name: 'B_to_Address_of_Tx_in_A', apiCode: 0x030a, opCode: 0x32 },
{ name: 'B_to_Address_of_Creator', apiCode: 0x030b, opCode: 0x32 },
{ name: 'Get_Code_Hash_Id', apiCode: 0x030c, opCode: 0x35 },
{ name: 'get_Current_Balance', apiCode: 0x0400, opCode: 0x35 },
{ name: 'get_Previous_Balance', apiCode: 0x0401, opCode: 0x35 },
{ name: 'send_to_Address_in_B', apiCode: 0x0402, opCode: 0x33 },
{ name: 'send_All_to_Address_in_B', apiCode: 0x0403, opCode: 0x32 },
{ name: 'send_Old_to_Address_in_B', apiCode: 0x0404, opCode: 0x32 },
{ name: 'send_A_to_Address_in_B', apiCode: 0x0405, opCode: 0x32 },
{ name: 'add_Minutes_to_Timestamp', apiCode: 0x0406, opCode: 0x37 }
{ name: 'add_Minutes_to_Timestamp', apiCode: 0x0406, opCode: 0x37 },
{ name: 'Get_Map_Value_Keys_In_A', apiCode: 0x0407, opCode: 0x35 },
{ name: 'Set_Map_Value_Keys_In_A', apiCode: 0x0408, opCode: 0x32 },
{ name: 'Issue_Asset', apiCode: 0x0409, opCode: 0x35 },
{ name: 'Mint_Asset', apiCode: 0x040a, opCode: 0x32 },
{ name: 'Distribute_To_Asset_Holders', apiCode: 0x040b, opCode: 0x32 },
{ name: 'Get_Asset_Holders_Count', apiCode: 0x040c, opCode: 0x35 },
{ name: 'Get_Activation_Fee', apiCode: 0x040d, opCode: 0x35 },
{ name: 'Put_Last_Block_GSig_In_A', apiCode: 0x040e, opCode: 0x32 }
]
const AsmObj: ASM_OBJECT = {
memory: [],
Expand Down Expand Up @@ -491,7 +504,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
}
const datapages = Math.ceil(AsmObj.memory.length / 32)
const codepages = Math.ceil(AsmObj.bytecode.length / (32 * 16))
const minimumfee = (cspages + uspages + datapages + codepages) * 7350000
const minimumfee = (cspages + uspages + datapages + codepages) * 10000000
return {
DataPages: datapages,
CodeStackPages: cspages,
Expand Down
6 changes: 6 additions & 0 deletions dev/src/__tests__/asmHighlight.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ describe('Assembly compilation:', () => {
const result = asmHighlight(code, true)
expect(result).toBe(highlighted)
})
it('should highlight atv3 new codes', () => {
const code = '^comment new opCodes SIP-37\nSLP\nPOW @base $exp\nMDV @x $y $den\n^comment new APICodes SIP-37\nFUN @ret Check_Sig_B_With_A\nFUN @ret Get_Code_Hash_Id\nFUN @ret Get_Activation_Fee\nFUN Put_Last_Block_GSig_In_A\n^comment new APICodes SIP-38\nFUN @ret Get_Map_Value_Keys_In_A\nFUN Set_Map_Value_Keys_In_A\n^comment new APICodes SIP-39\nFUN @ret Issue_Asset\nFUN Mint_Asset\nFUN Distribute_To_Asset_Holders\nFUN @ret Get_Asset_Holders_Count\n'
const highlighted = "<span class='asmDirective'>^comment</span><span class='asmComment'> new opCodes SIP-37</span><br><span class='asmInstruction'>SLP</span><br><span class='asmInstruction'>POW </span><span class='asmVariable'>@base </span><span class='asmVariable'>$exp</span><br><span class='asmInstruction'>MDV </span><span class='asmVariable'>@x </span><span class='asmVariable'>$y </span><span class='asmVariable'>$den</span><br><span class='asmDirective'>^comment</span><span class='asmComment'> new APICodes SIP-37</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@ret </span><span class='asmInstruction'>Check_Sig_B_With_A</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@ret </span><span class='asmInstruction'>Get_Code_Hash_Id</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@ret </span><span class='asmInstruction'>Get_Activation_Fee</span><br><span class='asmInstruction'>FUN Put_Last_Block_GSig_In_A</span><br><span class='asmDirective'>^comment</span><span class='asmComment'> new APICodes SIP-38</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@ret </span><span class='asmInstruction'>Get_Map_Value_Keys_In_A</span><br><span class='asmInstruction'>FUN Set_Map_Value_Keys_In_A</span><br><span class='asmDirective'>^comment</span><span class='asmComment'> new APICodes SIP-39</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@ret </span><span class='asmInstruction'>Issue_Asset</span><br><span class='asmInstruction'>FUN Mint_Asset</span><br><span class='asmInstruction'>FUN Distribute_To_Asset_Holders</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@ret </span><span class='asmInstruction'>Get_Asset_Holders_Count</span><br><br>"
const result = asmHighlight(code, false)
expect(result).toBe(highlighted)
})
it('should highlight error: ^comment and multi spaces', () => {
const code = 'SET @a #0000000000000100\nC @a\n\nFUN Xclear_A_B\nFUN Xset_A1 $a\nFUN Xset_A1_A2 $a $b\nFUN @a Xcheck_A_equals_B\nFUN @a Xadd_Minutes_to_Timestamp $b $c'
const highlighted = "<span class='asmInstruction'>SET </span><span class='asmVariable'>@a </span><span class='asmNumber'>#0000000000000100</span><br><span class='asmError'>C @a</span><br><br><span class='asmInstruction'>FUN </span><span class='asmError'>Xclear_A_B</span><br><span class='asmInstruction'>FUN </span><span class='asmError'>Xset_A1 </span><span class='asmVariable'>$a</span><br><span class='asmInstruction'>FUN </span><span class='asmError'>Xset_A1_A2 </span><span class='asmVariable'>$a </span><span class='asmVariable'>$b</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@a </span><span class='asmError'>Xcheck_A_equals_B</span><br><span class='asmInstruction'>FUN </span><span class='asmVariable'>@a </span><span class='asmError'>Xadd_Minutes_to_Timestamp </span><span class='asmVariable'>$b </span><span class='asmVariable'>$c</span><br>"
Expand Down
22 changes: 21 additions & 1 deletion dev/src/asmHighlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ opCode: 0x16, size: 9, regex: /^(\s*MOD\s+)(@\w+\s+)(\$\w+\s*)$/ },
{ opCode: 0x17, size: 9, regex: /^(\s*SHL\s+)(@\w+\s+)(\$\w+\s*)$/ },
{ opCode: 0x18, size: 9, regex: /^(\s*SHR\s+)(@\w+\s+)(\$\w+\s*)$/ },
{ opCode: 0x19, size: 9, regex: /^(\s*POW\s+)(@\w+\s+)(\$\w+\s*)$/ }, // POW @var $var
{ opCode: 0x1a, size: 5, regex: /^(\s*JMP\s+)(:\w+\s*)$/ }, // JMP :label
{ opCode: 0x1b, size: 6, regex: /^(\s*BZR\s+)(\$\w+\s+)(:\w+\s*)$/ }, // BZR $var :label
{ opCode: 0x1e, size: 6, regex: /^(\s*BNZ\s+)(\$\w+\s+)(:\w+\s*)$/ }, // BZR $var :label
Expand All @@ -71,7 +72,9 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ opCode: 0x27, size: 5, regex: /^(\s*STZ\s+)(\$\w+\s*)$/ },
{ opCode: 0x28, size: 1, regex: /^\s*FIN\s*$/ },
{ opCode: 0x29, size: 1, regex: /^\s*STP\s*$/ },
{ opCode: 0x2a, size: 1, regex: /^\s*SLP\s*$/ },
{ opCode: 0x2b, size: 5, regex: /^(\s*ERR\s+)(:\w+\s*)$/ }, // ERR :label
{ opCode: 0x2c, size: 13, regex: /^(\s*MDV\s+)(@\w+\s+)(\$\w+\s+)(\$\w+\s*)$/ }, // MDV @var $var $var
{ opCode: 0x30, size: 1, regex: /^\s*PCS\s*$/ },
{ opCode: 0x32, size: 3, regex: /^(\s*FUN\s+)(\w+\s*)$/ },
{ opCode: 0x33, size: 7, regex: /^(\s*FUN\s+)(\w+\s+)(\$\w+\s*)$/ },
Expand Down Expand Up @@ -132,6 +135,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ fnCode: 0x0203, fnName: 'check_HASH160_A_with_B' },
{ fnCode: 0x0204, fnName: 'SHA256_A_to_B' },
{ fnCode: 0x0205, fnName: 'check_SHA256_A_with_B' },
{ fnCode: 0x0206, fnName: 'Check_Sig_B_With_A' },
{ fnCode: 0x0300, fnName: 'get_Block_Timestamp' },
{ fnCode: 0x0301, fnName: 'get_Creation_Timestamp' },
{ fnCode: 0x0302, fnName: 'get_Last_Block_Timestamp' },
Expand All @@ -144,13 +148,22 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ fnCode: 0x0309, fnName: 'message_from_Tx_in_A_to_B' },
{ fnCode: 0x030a, fnName: 'B_to_Address_of_Tx_in_A' },
{ fnCode: 0x030b, fnName: 'B_to_Address_of_Creator' },
{ fnCode: 0x030c, fnName: 'Get_Code_Hash_Id' },
{ fnCode: 0x0400, fnName: 'get_Current_Balance' },
{ fnCode: 0x0401, fnName: 'get_Previous_Balance' },
{ fnCode: 0x0402, fnName: 'send_to_Address_in_B' },
{ fnCode: 0x0403, fnName: 'send_All_to_Address_in_B' },
{ fnCode: 0x0404, fnName: 'send_Old_to_Address_in_B' },
{ fnCode: 0x0405, fnName: 'send_A_to_Address_in_B' },
{ fnCode: 0x0406, fnName: 'add_Minutes_to_Timestamp' }
{ fnCode: 0x0406, fnName: 'add_Minutes_to_Timestamp' },
{ fnCode: 0x0407, fnName: 'Get_Map_Value_Keys_In_A' },
{ fnCode: 0x0408, fnName: 'Set_Map_Value_Keys_In_A' },
{ fnCode: 0x0409, fnName: 'Issue_Asset' },
{ fnCode: 0x040a, fnName: 'Mint_Asset' },
{ fnCode: 0x040b, fnName: 'Distribute_To_Asset_Holders' },
{ fnCode: 0x040c, fnName: 'Get_Asset_Holders_Count' },
{ fnCode: 0x040d, fnName: 'Get_Activation_Fee' },
{ fnCode: 0x040e, fnName: 'Put_Last_Block_GSig_In_A' }
]

/**
Expand Down Expand Up @@ -228,6 +241,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
case 0x16:
case 0x17:
case 0x18:
case 0x19:
return toSpan(parts[1], Config.spanInstructionClass) +
toSpan(parts[2], Config.spanVariableClass) +
toSpan(parts[3], Config.spanVariableClass)
Expand All @@ -245,6 +259,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
case 0x13:
case 0x28:
case 0x29:
case 0x2a:
case 0x30:
case 0x7f:
return toSpan(parts[0], Config.spanInstructionClass)
Expand Down Expand Up @@ -296,6 +311,11 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
toSpan(parts[2], Config.spanVariableClass) +
toSpan(parts[3], Config.spanVariableClass) +
toSpan(parts[4], Config.spanLabelClass)
case 0x2c:
return toSpan(parts[1], Config.spanInstructionClass) +
toSpan(parts[2], Config.spanVariableClass) +
toSpan(parts[3], Config.spanVariableClass) +
toSpan(parts[4], Config.spanVariableClass)
case 0x32:
apiName = parts[2].trim()
if (allowedFunctions.findIndex(Obj => Obj.fnName === apiName) === -1) {
Expand Down
94 changes: 94 additions & 0 deletions samples/EchoAnySize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Echo Any Size
Simple contract that reads the incoming text message until a zero byte is found on last byte of last page read. Clears the rest of message buffer and then send the "same message" to sender. Smart contracts only can send 32 bytes each time, so expect the received message to be multiple of 32, padded with zero bytes. Online at tesnet `TS-LZYH-PE75-JZTB-FJ88Y`. Note that there is no API to get the message size, so the program must handle the input end in some way. Activation amount is huge because the fees to read/send info for a smart contract are much higher than sending manually and the contract must handle input text up the 1000 bytes, the current blockchain limit.

## Source code
Note that this contract is currently not working on stable. It is for reference only and to be updated when the hard fork is online.
```c
#include APIFunctions

#program name EchoAnySize
#program description Reads the incoming message until a zero byte\
is found on last byte of last page read. Clears the rest of buffer\
and then send the same message to sender. Expect text messages.
#program activationAmount 5_0000_0000

#pragma maxConstVars 1

long zero;
B_To_Address_Of_Creator();
long CREATOR = Get_B1();

while (true)
{
while (getNextTxDetails()) {
processTX();
}
Set_B1(CREATOR);
Send_All_To_Address_In_B();
}

// just echoes a received message back to sender.
void processTX(void) {

long messagePage, currentLong;

// Last read on getNextTxDetails
currentLong = 4;
while (currentLong < currentTX.message.length) {
if (((currentTX.message[currentLong - 1]) >> 56) == 0) {
// Found a null byte at last byte of last page that was read.
break;
}
messagePage = currentLong / 4;
Set_A1_A2(currentTX.txId, messagePage);
Message_From_Tx_In_A_To_B();
currentTX.message[currentLong++] = Get_B1();
currentTX.message[currentLong++] = Get_B2();
currentTX.message[currentLong++] = Get_B3();
currentTX.message[currentLong++] = Get_B4();
}
while (currentLong < currentTX.message.length) {
// clear the rest of buffer.
currentTX.message[currentLong++] = zero;
currentTX.message[currentLong++] = zero;
currentTX.message[currentLong++] = zero;
currentTX.message[currentLong++] = zero;
}
Set_B1(currentTX.sender);
currentLong = 0;
do {
Set_A1_A2(currentTX.message[currentLong], currentTX.message[currentLong + 1]);
currentLong += 2;
Set_A3_A4(currentTX.message[currentLong], currentTX.message[currentLong + 1]);
currentLong += 2;
Send_A_To_Address_In_B();
} while (((currentTX.message[currentLong - 1]) >> 56) != 0 && currentLong < currentTX.message.length);
}

struct TXINFO {
long txId;
long timestamp;
long sender;
long amount;
long message[132];
} currentTX;

long getNextTxDetails(void)
{
A_To_Tx_After_Timestamp(currentTX.timestamp);
currentTX.txId = Get_A1();
if (currentTX.txId == 0) {
return false;
}
currentTX.amount = Get_Amount_For_Tx_In_A();
currentTX.timestamp = Get_Timestamp_For_Tx_In_A();
Message_From_Tx_In_A_To_B();
currentTX.message[0] = Get_B1();
currentTX.message[1] = Get_B2();
currentTX.message[2] = Get_B3();
currentTX.message[3] = Get_B4();
B_To_Address_Of_Tx_In_A();
currentTX.sender = Get_B1();
return true;
}
```
Loading

1 comment on commit 4771358

@vercel
Copy link

@vercel vercel bot commented on 4771358 May 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

smart-c – ./

smart-c.vercel.app
smart-c-deleterium.vercel.app
smart-c-git-main-deleterium.vercel.app

Please sign in to comment.