/**
 * Assemble AVR programs into a list of bytes.
 * Based on code from https://github.com/tadpol/Avrian-Jump,
 * refactored to TypeScript by Uri Shaked.
 *
 * This was written with http://www.atmel.com/atmel/acrobat/doc0856.pdf
 * It is a bit short of features often found in an assembler, but there is enough here to build
 * simple programs and run them.
 *
 * It would be nice someday to add device support, just to give errors on unsupported
 * instructions.  Macros would be nice too.
 *
 * Copyright (C) 2020, Uri Shaked
 * Copyright (c) 2012 Michael Conrad Tadpol Tilstra
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */ export interface LabelTable { [ key ] : number ; } type Pass1Bytes = string | ( ( l : LabelTable ) => string ) | string [ ] | [ ( l : LabelTable ) => [ string , string ] , string ] ; interface LineTablePass1 { line : number ; bytes : Pass1Bytes ; text : string ; byteOffset : number ; } export interface LineTable extends LineTablePass1 { bytes : string ; } /**
 * Get a destination register index from a string and shift it to where it
 * is most commonly found.
 * Also, make sure it is within the valid range.
 */ function destRindex ( r , min = 0 , max = 31 ) { const match = r . match ( /[Rr](\d{1,2})/ ) ; if ( ! match ) { throw 'Not a register: ' + r ; } const d = parseInt ( match [ 1 ] ) ; if ( d < min || d > max ) { throw 'Rd out of range: ' + min + '<>' + max ; } return ( d & 0x1f ) << 4 ; } /**
 * Get a source register index from a string and shift it to where it is
 * most commonly found.
 * Also, make sure it is within the valid range.
 */ function srcRindex ( r , min = 0 , max = 31 ) { const match = r . match ( /[Rr](\d{1,2})/ ) ; if ( ! match ) { throw 'Not a register: ' + r ; } const d = parseInt ( match [ 1 ] ) ; if ( d < min || d > max ) { throw 'Rd out of range: ' + min + '<>' + max ; } let s = d & 0xf ; s |= ( ( d >> 4 ) & 1 ) << 9 ; return s ; } /**
 * Get a constant value and check that it is in range.
 */ function constValue ( r , min = 0 , max = 255 ) { const d = typeof r === 'string' ? parseInt ( r ) : r ; if ( isNaN ( d ) ) { throw 'constant is not a number.' ; } if ( d < min || d > max ) { throw '[Ks] out of range: ' + min + '<>' + max ; } return d ; } function fitTwoC ( r , bits ) { if ( bits < 2 ) { throw 'Need at least 2 bits to be signed.' ; } if ( bits > 16 ) { throw 'fitTwoC only works on 16bit numbers for now.' ; } if ( Math . abs ( r ) > Math . pow ( 2 , bits - 1 ) ) throw 'Not enough bits for number. (' + r + ', ' + bits + ')' ; if ( r < 0 ) { r = 0xffff + r + 1 ; } const mask = 0xffff >> ( 16 - bits ) ; return r & mask ; } /**
 * Determin if input is an address or label and lookup if required.
 * If label that doesn't exist, return NaN.
 * If offset is not 0, convert from absolute address to relative.
 */ function constOrLabel ( c , labels , offset = 0 ) { if ( typeof c === 'string' ) { let d = parseInt ( c ) ; if ( isNaN ( d ) ) { if ( c in labels ) { d = labels [ c ] - offset ; } else { return NaN ; } } c = d ; } return c ; } param len function zeroPad ( r , len = 4 ) { r = Number ( r ) . toString ( 16 ) ; const base = Array ( len + 1 ) . join ( '0' ) ; const t = base . substr ( 0 , len - r . length ) + r ; return t ; } /**
 * Get an Indirect Address Register and shift it to where it is commonly found.
 */ function stldXYZ ( xyz ) { switch ( xyz ) { case 'X' : return 0x900c ; case 'X+' : return 0x900d ; case '-X' : return 0x900e ; case 'Y' : return 0x8008 ; case 'Y+' : return 0x9009 ; case '-Y' : return 0x900a ; case 'Z' : return 0x8000 ; case 'Z+' : return 0x9001 ; case '-Z' : return 0x9002 ; default : throw 'Not -?[XYZ]\\+?' ; } } /**
 * Get an Indirect Address Register with displacement and shift it to where it is commonly found.
 */ function stldYZq ( yzq ) { const d = yzq . match ( /([YZ])\+(\d+)/ ) ; let r = 0x8000 ; if ( d == null ) { throw 'Invalid arguments' ; } switch ( d [ 1 ] ) { case 'Y' : r |= 0x8 ; break ; case 'Z' : break ; default : throw 'Not Y or Z with q' ; } const q = parseInt ( d [ 2 ] ) ; if ( q < 0 || q > 64 ) { throw 'q is out of range' ; } r |= ( ( q & 0x20 ) << 8 ) | ( ( q & 0x18 ) << 7 ) | ( q & 0x7 ) ; return r ; } type opcodeHandler = ( a : string , b : string , byteLoc : number , labels : LabelTable ) => Pass1Bytes ; const SEflag = ( a ) => zeroPad ( 0x9408 | ( constValue ( a , 0 , 7 ) << 4 ) ) ; /**
 * Table of instructions that can be assembled.
 */ const OPTABLE = { ADD ( a , b ) { const r = 0x0c00 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , ADC ( a , b ) { const r = 0x1c00 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , ADIW ( a , b ) { let r = 0x9600 ; const dm = a . match ( /[Rr](24|26|28|30)/ ) ; if ( ! dm ) { throw 'Rd must be 24, 26, 28, or 30' ; } let d = parseInt ( dm [ 1 ] ) ; d = ( d - 24 ) / 2 ; r |= ( d & 0x3 ) << 4 ; const k = constValue ( b , 0 , 63 ) ; r |= ( ( k & 0x30 ) << 2 ) | ( k & 0x0f ) ; return zeroPad ( r ) ; } , AND ( a , b ) { const r = 0x2000 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , ANDI ( a , b ) { let r = 0x7000 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; const k = constValue ( b ) ; r |= ( ( k & 0xf0 ) << 4 ) | ( k & 0xf ) ; return zeroPad ( r ) ; } , ASR ( a ) { const r = 0x9405 | destRindex ( a ) ; return zeroPad ( r ) ; } , BCLR ( a ) { let r = 0x9488 ; const s = constValue ( a , 0 , 7 ) ; r |= ( s & 0x7 ) << 4 ; return zeroPad ( r ) ; } , BLD ( a , b ) { const r = 0xf800 | destRindex ( a ) | ( constValue ( b , 0 , 7 ) & 0x7 ) ; return zeroPad ( r ) ; } , BRBC ( a , b , byteLoc , labels ) { const k = constOrLabel ( b , labels , byteLoc + 2 ) ; if ( isNaN ( k ) ) { return ( l ) => OPTABLE [ 'BRBC' ] ( a , b , byteLoc , l ) as string ; } let r = 0xf400 | constValue ( a , 0 , 7 ) ; r |= fitTwoC ( constValue ( k >> 1 , - 64 , 63 ) , 7 ) << 3 ; return zeroPad ( r ) ; } , BRBS ( a , b , byteLoc , labels ) { const k = constOrLabel ( b , labels , byteLoc + 2 ) ; if ( isNaN ( k ) ) { return ( l ) => OPTABLE [ 'BRBS' ] ( a , b , byteLoc , l ) as string ; } let r = 0xf000 | constValue ( a , 0 , 7 ) ; r |= fitTwoC ( constValue ( k >> 1 , - 64 , 63 ) , 7 ) << 3 ; return zeroPad ( r ) ; } , BRCC ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '0' , a , byteLoc , labels ) ; } , BRCS ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '0' , a , byteLoc , labels ) ; } , BREAK ( ) { return '9598' ; } , BREQ ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '1' , a , byteLoc , labels ) ; } , BRGE ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '4' , a , byteLoc , labels ) ; } , BRHC ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '5' , a , byteLoc , labels ) ; } , BRHS ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '5' , a , byteLoc , labels ) ; } , BRID ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '7' , a , byteLoc , labels ) ; } , BRIE ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '7' , a , byteLoc , labels ) ; } , BRLO ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '0' , a , byteLoc , labels ) ; } , BRLT ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '4' , a , byteLoc , labels ) ; } , BRMI ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '2' , a , byteLoc , labels ) ; } , BRNE ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '1' , a , byteLoc , labels ) ; } , BRPL ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '2' , a , byteLoc , labels ) ; } , BRSH ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '0' , a , byteLoc , labels ) ; } , BRTC ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '6' , a , byteLoc , labels ) ; } , BRTS ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '6' , a , byteLoc , labels ) ; } , BRVC ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBC' ] ( '3' , a , byteLoc , labels ) ; } , BRVS ( a , _ , byteLoc , labels ) { return OPTABLE [ 'BRBS' ] ( '3' , a , byteLoc , labels ) ; } , BSET ( a ) { let r = 0x9408 ; const s = constValue ( a , 0 , 7 ) ; r |= ( s & 0x7 ) << 4 ; return zeroPad ( r ) ; } , BST ( a , b ) { const r = 0xfa00 | destRindex ( a ) | constValue ( b , 0 , 7 ) ; return zeroPad ( r ) ; } , CALL ( a , b , byteLoc , labels ) { let k = constOrLabel ( a , labels ) ; if ( isNaN ( k ) ) { return [ ( l ) => OPTABLE [ 'CALL' ] ( a , b , byteLoc , l ) as [ string , string ] , 'xxxx' ] ; } let r = 0x940e ; k = constValue ( k , 0 , 0x400000 ) >> 1 ; const lk = k & 0xffff ; const hk = ( k >> 16 ) & 0x3f ; r |= ( ( hk & 0x3e ) << 3 ) | ( hk & 1 ) ; return [ zeroPad ( r ) , zeroPad ( lk ) ] ; } , CBI ( a , b ) { const r = 0x9800 | ( constValue ( a , 0 , 31 ) << 3 ) | constValue ( b , 0 , 7 ) ; return zeroPad ( r ) ; } , CRB ( a , b , byteLoc , l ) { const k = constValue ( b ) ; return OPTABLE [ 'ANDI' ] ( a . toString ( ) , ( ~ k & 0xff ) . toString ( ) , byteLoc , l ) ; } , CLC ( ) { return '9488' ; } , CLH ( ) { return '94d8' ; } , CLI ( ) { return '94f8' ; } , CLN ( ) { return '94a8' ; } , CLR ( a , _ , byteLoc , l ) { return OPTABLE [ 'EOR' ] ( a , a , byteLoc , l ) ; } , CLS ( ) { return '94c8' ; } , CLT ( ) { return '94e8' ; } , CLV ( ) { return '94b8' ; } , CLZ ( ) { return '9498' ; } , COM ( a ) { const r = 0x9400 | destRindex ( a ) ; return zeroPad ( r ) ; } , CP ( a , b ) { const r = 0x1400 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , CPC ( a , b ) { const r = 0x0400 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , CPI ( a , b ) { let r = 0x3000 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; const k = constValue ( b ) ; r |= ( ( k & 0xf0 ) << 4 ) | ( k & 0xf ) ; return zeroPad ( r ) ; } , CPSE ( a , b ) { const r = 0x1000 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , DEC ( a ) { const r = 0x940a | destRindex ( a ) ; return zeroPad ( r ) ; } , DES ( a ) { const r = 0x940b | ( constValue ( a , 0 , 15 ) << 4 ) ; return zeroPad ( r ) ; } , EICALL ( ) { return '9519' ; } , EIJMP ( ) { return '9419' ; } , ELPM ( a , b ) { if ( typeof a === 'undefined' || a === '' ) { return '95d8' ; } else { let r = 0x9000 | destRindex ( a ) ; switch ( b ) { case 'Z' : r |= 6 ; break ; case 'Z+' : r |= 7 ; break ; default : throw 'Bad operand' ; } return zeroPad ( r ) ; } } , EOR ( a , b ) { const r = 0x2400 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , FMUL ( a , b ) { const r = 0x0308 | ( destRindex ( a , 16 , 23 ) & 0x70 ) | ( srcRindex ( b , 16 , 23 ) & 0x7 ) ; return zeroPad ( r ) ; } , FMULS ( a , b ) { const r = 0x0380 | ( destRindex ( a , 16 , 23 ) & 0x70 ) | ( srcRindex ( b , 16 , 23 ) & 0x7 ) ; return zeroPad ( r ) ; } , FMULSU ( a , b ) { const r = 0x0388 | ( destRindex ( a , 16 , 23 ) & 0x70 ) | ( srcRindex ( b , 16 , 23 ) & 0x7 ) ; return zeroPad ( r ) ; } , ICALL ( ) { return '9509' ; } , IJMP ( ) { return '9409' ; } , IN ( a , b ) { let r = 0xb000 | destRindex ( a ) ; const A = constValue ( b , 0 , 63 ) ; r |= ( ( A & 0x30 ) << 5 ) | ( A & 0x0f ) ; return zeroPad ( r ) ; } , INC ( a ) { const r = 0x9403 | destRindex ( a ) ; return zeroPad ( r ) ; } , JMP ( a , b , byteLoc , labels ) { let k = constOrLabel ( a , labels ) ; if ( isNaN ( k ) ) { return [ ( l ) => OPTABLE [ 'JMP' ] ( a , b , byteLoc , l ) as [ string , string ] , 'xxxx' ] ; } let r = 0x940c ; k = constValue ( k , 0 , 0x400000 ) >> 1 ; const lk = k & 0xffff ; const hk = ( k >> 16 ) & 0x3f ; r |= ( ( hk & 0x3e ) << 3 ) | ( hk & 1 ) ; return [ zeroPad ( r ) , zeroPad ( lk ) ] ; } , LAC ( a , b ) { if ( a !== 'Z' ) { throw 'First Operand is not Z' ; } const r = 0x9206 | destRindex ( b ) ; return zeroPad ( r ) ; } , LAS ( a , b ) { if ( a !== 'Z' ) { throw 'First Operand is not Z' ; } const r = 0x9205 | destRindex ( b ) ; return zeroPad ( r ) ; } , LAT ( a , b ) { if ( a !== 'Z' ) { throw 'First Operand is not Z' ; } const r = 0x9207 | destRindex ( b ) ; return zeroPad ( r ) ; } , LD ( a , b ) { const r = 0x0000 | destRindex ( a ) | stldXYZ ( b ) ; return zeroPad ( r ) ; } , LDD ( a , b ) { const r = 0x0000 | destRindex ( a ) | stldYZq ( b ) ; return zeroPad ( r ) ; } , LDI ( a , b ) { let r = 0xe000 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; const k = constValue ( b ) ; r |= ( ( k & 0xf0 ) << 4 ) | ( k & 0xf ) ; return zeroPad ( r ) ; } , LDS ( a , b ) { const k = constValue ( b , 0 , 65535 ) ; const r = 0x9000 | destRindex ( a ) ; return [ zeroPad ( r ) , zeroPad ( k ) ] ; } , LPM ( a , b ) { if ( typeof a === 'undefined' || a === '' ) { return '95c8' ; } else { let r = 0x9000 | destRindex ( a ) ; switch ( b ) { case 'Z' : r |= 4 ; break ; case 'Z+' : r |= 5 ; break ; default : throw 'Bad operand' ; } return zeroPad ( r ) ; } } , LSL ( a , _ , byteLoc , l ) { return OPTABLE [ 'ADD' ] ( a , a , byteLoc , l ) ; } , LSR ( a ) { const r = 0x9406 | destRindex ( a ) ; return zeroPad ( r ) ; } , MOV ( a , b ) { const r = 0x2c00 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , MOVW ( a , b ) { const r = 0x0100 | ( ( destRindex ( a ) >> 1 ) & 0xf0 ) | ( ( destRindex ( b ) >> 5 ) & 0xf ) ; return zeroPad ( r ) ; } , MUL ( a , b ) { const r = 0x9c00 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , MULS ( a , b ) { const r = 0x0200 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) | ( srcRindex ( b , 16 , 31 ) & 0xf ) ; return zeroPad ( r ) ; } , MULSU ( a , b ) { const r = 0x0300 | ( destRindex ( a , 16 , 23 ) & 0x70 ) | ( srcRindex ( b , 16 , 23 ) & 0x7 ) ; return zeroPad ( r ) ; } , NEG ( a ) { const r = 0x9401 | destRindex ( a ) ; return zeroPad ( r ) ; } , NOP ( ) { return '0000' ; } , OR ( a , b ) { const r = 0x2800 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , ORI ( a , b ) { let r = 0x6000 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; const k = constValue ( b ) ; r |= ( ( k & 0xf0 ) << 4 ) | ( k & 0xf ) ; return zeroPad ( r ) ; } , OUT ( a , b ) { let r = 0xb800 | destRindex ( b ) ; const A = constValue ( a , 0 , 63 ) ; r |= ( ( A & 0x30 ) << 5 ) | ( A & 0x0f ) ; return zeroPad ( r ) ; } , POP ( a ) { const r = 0x900f | destRindex ( a ) ; return zeroPad ( r ) ; } , PUSH ( a ) { const r = 0x920f | destRindex ( a ) ; return zeroPad ( r ) ; } , RCALL ( a , b , byteLoc , labels ) { const k = constOrLabel ( a , labels , byteLoc + 2 ) ; if ( isNaN ( k ) ) { return ( l ) => OPTABLE [ 'RCALL' ] ( a , b , byteLoc , l ) as string ; } const r = 0xd000 | fitTwoC ( constValue ( k >> 1 , - 2048 , 2047 ) , 12 ) ; return zeroPad ( r ) ; } , RET ( ) { return '9508' ; } , RETI ( ) { return '9518' ; } , RJMP ( a , b , byteLoc , labels ) { const k = constOrLabel ( a , labels , byteLoc + 2 ) ; if ( isNaN ( k ) ) { return ( l ) => OPTABLE [ 'RJMP' ] ( a , b , byteLoc , l ) as string ; } const r = 0xc000 | fitTwoC ( constValue ( k >> 1 , - 2048 , 2047 ) , 12 ) ; return zeroPad ( r ) ; } , ROL ( a , _ , byteLoc , l ) { return OPTABLE [ 'ADC' ] ( a , a , byteLoc , l ) ; } , ROR ( a ) { const r = 0x9407 | destRindex ( a ) ; return zeroPad ( r ) ; } , SBC ( a , b ) { const r = 0x0800 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , SBCI ( a , b ) { let r = 0x4000 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; const k = constValue ( b ) ; r |= ( ( k & 0xf0 ) << 4 ) | ( k & 0x0f ) ; return zeroPad ( r ) ; } , SBI ( a , b ) { const r = 0x9a00 | ( constValue ( a , 0 , 31 ) << 3 ) | constValue ( b , 0 , 7 ) ; return zeroPad ( r ) ; } , SBIC ( a , b ) { const r = 0x9900 | ( constValue ( a , 0 , 31 ) << 3 ) | constValue ( b , 0 , 7 ) ; return zeroPad ( r ) ; } , SBIS ( a , b ) { const r = 0x9b00 | ( constValue ( a , 0 , 31 ) << 3 ) | constValue ( b , 0 , 7 ) ; return zeroPad ( r ) ; } , SBIW ( a , b ) { let r = 0x9700 ; const dm = a . match ( /[Rr](24|26|28|30)/ ) ; if ( ! dm ) { throw 'Rd must be 24, 26, 28, or 30' ; } let d = parseInt ( dm [ 1 ] ) ; d = ( d - 24 ) / 2 ; r |= ( d & 0x3 ) << 4 ; const k = constValue ( b , 0 , 63 ) ; r |= ( ( k & 0x30 ) << 2 ) | ( k & 0x0f ) ; return zeroPad ( r ) ; } , SBR ( a , b ) { let r = 0x6000 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; const k = constValue ( b ) ; r |= ( ( k & 0xf0 ) << 4 ) | ( k & 0x0f ) ; return zeroPad ( r ) ; } , SBRC ( a , b ) { const r = 0xfc00 | destRindex ( a ) | constValue ( b , 0 , 7 ) ; return zeroPad ( r ) ; } , SBRS ( a , b ) { const r = 0xfe00 | destRindex ( a ) | constValue ( b , 0 , 7 ) ; return zeroPad ( r ) ; } , SEC ( ) { return SEflag ( 0 ) ; } , SEH ( ) { return SEflag ( 5 ) ; } , SEI ( ) { return SEflag ( 7 ) ; } , SEN ( ) { return SEflag ( 2 ) ; } , SER ( a ) { const r = 0xef0f | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; return zeroPad ( r ) ; } , SES ( ) { return SEflag ( 4 ) ; } , SET ( ) { return SEflag ( 6 ) ; } , SEV ( ) { return SEflag ( 3 ) ; } , SEZ ( ) { return SEflag ( 1 ) ; } , SLEEP ( ) { return '9588' ; } , SPM ( a ) { if ( typeof a === 'undefined' || a === '' ) { return '95e8' ; } else { if ( a !== 'Z+' ) { throw 'Bad param to SPM' ; } return '95f8' ; } } , ST ( a , b ) { const r = 0x0200 | destRindex ( b ) | stldXYZ ( a ) ; return zeroPad ( r ) ; } , STD ( a , b ) { const r = 0x0200 | destRindex ( b ) | stldYZq ( a ) ; return zeroPad ( r ) ; } , STS ( a , b ) { const k = constValue ( a , 0 , 65535 ) ; const r = 0x9200 | destRindex ( b ) ; return [ zeroPad ( r ) , zeroPad ( k ) ] ; } , SUB ( a , b ) { const r = 0x1800 | destRindex ( a ) | srcRindex ( b ) ; return zeroPad ( r ) ; } , SUBI ( a , b ) { let r = 0x5000 | ( destRindex ( a , 16 , 31 ) & 0xf0 ) ; const k = constValue ( b ) ; r |= ( ( k & 0xf0 ) << 4 ) | ( k & 0xf ) ; return zeroPad ( r ) ; } , SWAP ( a ) { const r = 0x9402 | destRindex ( a ) ; return zeroPad ( r ) ; } , TST ( a , _ , byteLoc , l ) { return OPTABLE [ 'AND' ] ( a , a , byteLoc , l ) ; } , WDR ( ) { return '95a8' ; } , XCH ( a , b ) { const r = 0x9204 | destRindex ( b ) ; if ( a !== 'Z' ) { throw 'Bad param, not Z' ; } return zeroPad ( r ) ; } , } ; function passOne ( inputdata ) { const lines = inputdata . split ( '\n' ) ; const commentReg = /[#;].*$/ ; const labelReg = /^(\w+):/ ; const codeReg = /^\s*(\w+)(?:\s+([^,]+)(?:,\s*(\S+))?)?\s*$/ ; let lt ; let res ; let rets ; let instruction ; let byteOffset = 0 ; const lableTable = { } ; const replacements = { } ; const errorTable = [ ] ; const lineTable = [ ] ; for ( let idx = 0 ; idx < lines . length ; idx ++ ) { res = lines [ idx ] . trim ( ) ; if ( res . length === 0 ) { continue ; } lt = { line : idx + 1 , text : res , bytes : [ ] , byteOffset : 0 } ; res = res . replace ( commentReg , '' ) . trim ( ) ; if ( res . length === 0 ) { continue ; } rets = res . match ( labelReg ) ; if ( rets ) { lableTable [ rets [ 1 ] ] = byteOffset ; res = res . replace ( labelReg , '' ) . trim ( ) ; } if ( res . length === 0 ) { continue ; } const resMatch = res . match ( codeReg ) ; try { if ( resMatch === null ) { throw "doesn't match as code!" ; } if ( ! resMatch [ 1 ] ) { throw 'Empty mnemonic field!' ; } instruction = resMatch [ 1 ] . toUpperCase ( ) . trim ( ) ; switch ( instruction ) { case '_REPLACE' : replacements [ resMatch [ 2 ] ] = resMatch [ 3 ] ; continue ; case '_LOC' : { const num = parseInt ( resMatch [ 2 ] ) ; if ( isNaN ( num ) ) { throw 'Location is not a number.' ; } if ( num & 0x1 ) { throw 'Location is odd' ; } byteOffset = num ; continue ; } case '_IW' : { const num = parseInt ( resMatch [ 2 ] ) ; if ( isNaN ( num ) ) { throw 'Immeadiate Word is not a number.' ; } lt . bytes = zeroPad ( num ) ; lt . byteOffset = byteOffset ; byteOffset += 2 ; continue ; } } if ( ! ( instruction in OPTABLE ) ) { throw 'No such instruction: ' + instruction ; } if ( resMatch [ 2 ] in replacements ) { resMatch [ 2 ] = replacements [ resMatch [ 2 ] ] ; } if ( resMatch [ 3 ] in replacements ) { resMatch [ 3 ] = replacements [ resMatch [ 3 ] ] ; } const bytes = OPTABLE [ instruction ] ( resMatch [ 2 ] , resMatch [ 3 ] , byteOffset , lableTable ) ; lt . byteOffset = byteOffset ; switch ( typeof bytes ) { case 'function' : case 'string' : byteOffset += 2 ; break ; case 'object' : byteOffset += bytes . length * 2 ; break ; default : throw 'unknown return type from optable.' ; } lt . bytes = bytes ; lineTable . push ( lt ) ; } catch ( err ) { errorTable . push ( 'Line ' + idx + ': ' + err ) ; } } return { labels : lableTable , errors : errorTable , lines : lineTable , } ; } function elementSize ( lt ) { return typeof lt . bytes === 'string' ? lt . bytes . length / 2 : lt . bytes . length * 2 ; } /**
 * Handle any forward referenced labels that were deferred in passone.
 */ function passTwo ( lineTable , labels ) { const errorTable = [ ] ; const lastElement = lineTable [ lineTable . length - 1 ] ; const byteSize = lastElement ? lastElement . byteOffset + elementSize ( lastElement ) : 0 ; const resultTable = new Uint8Array ( byteSize ) ; for ( const ltEntry of lineTable ) { try { if ( typeof ltEntry . bytes === 'function' ) { ltEntry . bytes = ltEntry . bytes ( labels ) ; } if ( ltEntry . bytes instanceof Array && ltEntry . bytes . length >= 1 && typeof ltEntry . bytes [ 0 ] === 'function' ) { ltEntry . bytes = ltEntry . bytes [ 0 ] ( labels ) ; } switch ( typeof ltEntry . bytes ) { case 'string' : resultTable [ ltEntry . byteOffset + 1 ] = parseInt ( ltEntry . bytes . substr ( 0 , 2 ) , 16 ) ; resultTable [ ltEntry . byteOffset ] = parseInt ( ltEntry . bytes . substr ( 2 , 4 ) , 16 ) ; break ; case 'object' : if ( ltEntry . bytes . length < 1 ) { throw 'Empty array in lineTable.' ; } for ( let j = 0 , bi = ltEntry . byteOffset ; j < ltEntry . bytes . length ; j ++ , bi += 2 ) { const value = ltEntry . bytes [ j ] ; if ( typeof value !== 'string' ) { throw 'Not an array of strings.' ; } resultTable [ bi + 1 ] = parseInt ( value . substr ( 0 , 2 ) , 16 ) ; resultTable [ bi ] = parseInt ( value . substr ( 2 , 4 ) , 16 ) ; } break ; default : throw 'unknown return type from optable.' ; } } catch ( err ) { errorTable . push ( 'Line: ' + ltEntry . line + ': ' + err ) ; } } return { errors : errorTable , bytes : resultTable , lines : lineTable as LineTable [ ] , labels } ; } /**
 * The assembler.
 */ export function assemble ( input ) { const mid = passOne ( input ) ; if ( mid . errors . length > 0 ) { return { bytes : new Uint8Array ( 0 ) , errors : mid . errors , lines : [ ] , labels : { } , } ; } return passTwo ( mid . lines , mid . labels ) ; }