; ; asm.s ; ; Assembly of instructions (TMA) ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Sledgehammer debugger. ; ; Sledgehammer is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; Sledgehammer is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Sledgehammer. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis GET libs:stream ;----- External dependencies ------------------------------------------------ GET quartz:string GET sh.diss ;----- Main code ------------------------------------------------------------ AREA |Hammer$$Code|,CODE,READONLY ; --- asm_assemble --- ; ; On entry: R0 == pointer to the instruction to assemble ; R1 == address at which to assemble instruction ; ; On exit: VC and R0 == the assembled instruction ; VS and R0 == pointer to an error ; ; Use: Assembles the given insruction, returning the result in ; R0. The instruction is NOT placed into the memory location ; passed in R1. EXPORT asm_assemble asm_assemble ROUT STMFD R13!,{R1-R9,R14} ;Stack some registers ; --- Set up 'global' registers --- MOV R9,#0 ;Instruction so far MOV R8,R0 ;Put string pointer in R8 MOV R7,R1 ;And address in R7 ; --- Read the opcode --- BL asm__readOpcode ;Read opcode MOVVC R0,R9 ;Place instruction in R0 LDMFD R13!,{R1-R9,R14} ;Load registers ORRVSS PC,R14,#V_flag ;Return with error BICVCS PC,R14,#V_flag ;Or hopefully without LTORG ; --- asm__readOpcode --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Reads in the current opcode, and deals with it asm__readOpcode ROUT ; --- First, try to find the opcode number --- STMFD R13!,{R14} ;Save link for a bit LDRB R0,[R8,#0] ;Get a byte from input CMP R0,#'B' ;Is it a 'B'? CMPNE R0,#'b' BNE %00asm__readOpcode ;No -- skip ahead LDRB R0,[R8,#1] ;Get the next byte CMP R0,#'I' ;Is it an 'I' CMPNE R0,#'i' LDMNEFD R13!,{R14} ;No -- get the link back BNE asm__branch ;...it must be a branch 00 ADR R6,asm__opTable ;Point to opcode table BL asm__lookup ;Try to find it in there LDMFD R13!,{R14} ;Restore link again ADDCC R8,R8,#3 ;Skip past the opcode ADDCC PC,PC,R5,LSL#2 ;Jump to handling routine B %80asm__readOpcode ;If not there, moan B asm__aluOp3 ;AND B asm__aluOp3 ;EOR B asm__aluOp3 ;SUB B asm__aluOp3 ;RSB B asm__aluOp3 ;ADD B asm__aluOp3 ;ADC B asm__aluOp3 ;SBC B asm__aluOp3 ;RSC B asm__aluOp3 ;ORR B asm__aluOp3 ;BIC B asm__aluOp2 ;MOV B asm__aluOp2 ;MVN B asm__aluOpTest ;TST B asm__aluOpTest ;TEQ B asm__aluOpTest ;CMP B asm__aluOpTest ;CMN B asm__multiply ;MUL B asm__multiply ;MLA B asm__sDataTrans ;LDR B asm__sDataTrans ;STR B asm__mDataTrans ;LDM B asm__mDataTrans ;STM B asm__swi ;SWI B asm__adr ;ADR B asm__swp ;SWP B asm__cdp ;CDP B asm__coDataTrans ;STC B asm__coDataTrans ;LDC B asm__coRegTrans ;MRC B asm__coRegTrans ;MCR 80 ADR R0,asm__noOpcode ;Point to the error message ORRS PC,R14,#V_flag ;Return to caller asm__opTable DCB "AND",0,"EOR",0,"SUB",0,"RSB",0,"ADD",0,"ADC",0 DCB "SBC",0,"RSC",0,"ORR",0,"BIC",0,"MOV",0,"MVN",0 DCB "TST",0,"TEQ",0,"CMP",0,"CMN",0,"MUL",0,"MLA",0 DCB "LDR",0,"STR",0,"LDM",0,"STM",0,"SWI",0,"ADR",0 DCB "SWP",0,"CDP",0,"STC",0,"LDC",0,"MRC",0,"MCR",0 DCD 0 asm__noOpcode DCD 1 DCB "Unknown opcode",0 LTORG ; --- asm__lookup --- ; ; On entry: R6 == pointer to lookup table, with values in words ; R8 == pointer to three character thing to read ; ; On exit: CC if found, and R5 == index into table of match ; CS if not found ; R0-R2, R6 corrupted horridly asm__lookup ROUT MOV R5,#0 ;Zero the initial index BIC R0,R8,#3 ;Get base of thingy AND R2,R8,#3 ;Get non-wordalignedness LDMIA R0,{R0,R1} ;Load the possible bytes MOVS R2,R2,LSL #3 ;Shift offset up to bytes MOVNE R0,R0,LSR R2 ;Shift that bit down RSBNE R2,R2,#32 ;Work out the other shift ORRNE R0,R0,R1,LSL R2 ;Mix in the top bits BIC R0,R0,#&FF000000 ;Clear the top byte BIC R0,R0,#&00200000 ;Force to upper case BIC R0,R0,#&00002000 ;Force to upper case BIC R0,R0,#&00000020 ;Force to upper case 00asm__lookup LDR R1,[R6],#4 ;Get an entry from table CMP R1,#0 ;Are we at end of the table ORREQS PC,R14,#C_flag ;Yes -- return failure CMP R1,R0 ;Are the strings the same? ADDNE R5,R5,#1 ;No -- inc instruction no. BNE %00asm__lookup ;And keep looking BICS PC,R14,#C_flag ;Found it -- return C clear LTORG ; --- asm__condition --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; R0,R1,R5,R6 corrupted ; ; Use: Reads in the current opcode, and deals with it asm__condition ROUT ADR R6,asm__condTable ;Point to the table LDRB R0,[R8,#0] ;Get a character CMP R0,#32 ;Is it a control character? ORRLE R9,R9,#&E0000000 ;Yes -- make it 'AL' MOVLES PC,R14 ;Return to caller MOV R5,#0 ;Set up the count again 00 LDRB R0,[R8,#0] ;Get a character BIC R0,R0,#&00000020 ;Force to upper case LDRB R1,[R6],#2 ;Get a byte from the table BIC R1,R1,#&00000020 ;Force to upper case CMP R0,R1 ;Are they the same LDREQB R0,[R8,#1] ;Yes -- get another character BICEQ R0,R0,#&00000020 ;...force to upper case LDREQB R1,[R6,#-1] ;...and one from the table BICEQ R1,R1,#&00000020 ;...force to upper case CMPEQ R0,R1 ;...and compare them ORREQ R9,R9,R5,LSL #28 ;If we've found it, yippee ADDEQ R8,R8,#2 ;...skip past condition code MOVEQS PC,R14 ;...and return to caller ADD R5,R5,#1 ;Increment the counter CMP R5,#21 ;Have we tried them all? BLE %00asm__condition ;And keep trying if not ORR R9,R9,#&E0000000 ;Make it 'AL' MOVS PC,R14 ;Return to caller asm__condTable DCB "EQ","NE","CS","CC","MI","PL","VS","VC" DCB "HI","LS","GE","LT","GT","LE","AL","NV" DCB "ZS","ZC","HS","LO","NS","NC" LTORG ; --- asm__doSBit --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Reads in an optional 'S', setting the bit, and then skips ; the following spaces (ensuring that there is at least one). asm__doSBit ROUT STMFD R13!,{R14} ;Stack the link register LDRB R14,[R8,#0] ;Read another byte CMP R14,#'S' ;Is it an 'S'? CMPNE R14,#'s' ORREQ R9,R9,#(1<<20) ;Yes -- set the bit ADDEQ R8,R8,#1 ;...skip past the 'S' LDREQB R14,[R8,#0] ;...and read another byte CMP R14,#' ' ;Is it a space? CMPNE R14,#9 ;Allow a tab here too ADRNE R0,asm__opcJunk ;No -- something odd's there BNE %95asm__doSBit ;So moan about it then 10asm__doSBit LDRB R14,[R8],#1 ;Load the next byte CMP R14,#' ' ;Is it a space? CMPNE R14,#9 ;Allow a tab here too BEQ %10asm__doSBit ;Yes -- ignore it then SUB R8,R8,#1 ;We overstepped the mark LDMFD R13!,{R14} ;Get the link back BICS PC,R14,#V_flag ;Return without error 95asm__doSBit LDMFD R13!,{R14} ;Get the link back ORRS PC,R14,#V_flag ;Return without error LTORG asm__opcJunk DCD 1 DCB "Rubbish on end of opcode",0 ; --- asm__doSpaces --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Skips the following spaces, ensureing that there is ; at least one. asm__doSpaces ROUT STMFD R13!,{R14} ;Stack the link register LDRB R14,[R8,#0] ;...and read another byte CMP R14,#' ' ;Is it a space? CMPNE R14,#9 ;Allow a tab here too ADRNE R0,asm__opcJunk ;No -- something odd's there BNE %95asm__doSpaces ;So moan about it then 10asm__doSpaces LDRB R14,[R8],#1 ;Load the next byte CMP R14,#' ' ;Is it a space? CMPNE R14,#9 ;Allow a tab here too BEQ %10asm__doSpaces ;Yes -- ignore it then SUB R8,R8,#1 ;We overstepped the mark LDMFD R13!,{R14} ;Get the link back BICS PC,R14,#V_flag ;Return without error 95asm__doSpaces LDMFD R13!,{R14} ;Get the link back ORRS PC,R14,#V_flag ;Return without error LTORG ; --- asm__aluOp3 --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with 3 operand ALU operations asm__aluOp3 ROUT STMFD R13!,{R14} ;Stack the link register CMP R5,#7 ;Does R5 hold correct code? MOVLE R9,R5,LSL #21 ;Yes -- put it in the inst. CMP R5,#8 ;Is it ORR? MOVEQ R9,#&01800000 ;Yes -- make it so CMP R5,#9 ;Is it a BIC? MOVEQ R9,#&01C00000 ;Yes -- make it so MOV R4,R5 ;Remember the opcode BL asm__condition ;Read the condition BL asm__doSBit ;Skip onto next register ; --- First off is a register name --- BL asm_register ;Read a register name ORRVC R9,R9,R0,LSL #12 ;Insert Rd in nicely BLVC asm__comma ;Read and ignore a comma ; --- So's the next one --- BLVC asm_register ;Read a register name ORRVC R9,R9,R0,LSL #16 ;Insert Rn in nicely BLVC asm__comma ;Read a comma BVS %95asm__aluOp3 ;Barf if there's an error ; --- Now for the last op (easy one, this) --- LDRB R14,[R8] ;Read a character CMP R14,#'#' ;Is is a hash? BNE %50asm__aluOp3 ;No -- Next is a register ; --- Operand 2 is an immediate constant --- ADD R8,R8,#1 ;Skip past the '#' BL asm__constant ;Read the constant MOV R1,R0 ;Remember the number BVS %95asm__aluOp3 ;If error -- barf ORR R9,R9,#(1<<25) ;Operand 2 is immediate BL asm__doConstShift ;Make it into a valid shift BVC %90asm__aluOp3 ;All OK -- return ; --- Try an alternative opcode --- CMP R4,#0 ;Was it an AND CMPNE R4,#9 ;Or a BIC CMPNE R4,#4 ;Or an ADD CMPNE R4,#5 ;Or a ADC CMPNE R4,#2 ;Perhaps a SUB CMPNE R4,#6 ;Or an SBC ADRNE R0,asm__invConst ;No, point to the error BNE %95asm__aluOp3 ;And return with error MOV R0,R1 ;Put the number back in R0 CMP R4,#0 ;Was it an AND MVNEQ R0,R0 ;Yes -- invert the number MOVEQ R4,#9 ;And make it a BIC BEQ %20asm__aluOp3 ;Try the new opcode then CMP R4,#9 ;Was it a BIC MVNEQ R0,R0 ;Yes -- invert the number MOVEQ R4,#0 ;And make it an AND BEQ %20asm__aluOp3 ;Try the new opcode then CMP R4,#4 ;Was it an ADD RSBEQ R0,R0,#0 ;Yes -- negate the number MOVEQ R4,#2 ;And make it a SUB BEQ %20asm__aluOp3 ;Try the new opcode then CMP R4,#2 ;Was it a SUB RSBEQ R0,R0,#0 ;Yes -- negate the number MOVEQ R4,#4 ;And make it an ADD BEQ %20asm__aluOp3 ;Try the new opcode then CMP R4,#5 ;Was it an ADC MVNEQ R0,R0 ;Yes -- negate the number MOVEQ R4,#6 ;And make it a SBC BEQ %20asm__aluOp3 ;Try the new opcode then CMP R4,#6 ;Was it an SBC MVNEQ R0,R0 ;Yes -- negate the number MOVEQ R4,#5 ;And make it a ADC BEQ %20asm__aluOp3 ;Try the new opcode then 20asm__aluOp3 BIC R9,R9,#&01E00000 ;Clear the current opcode CMP R4,#9 ;Is it a BIC? MOVEQ R4,#14 ;Yes -- make it so ORR R9,R9,R4,LSL#21 ;And put in the new one BL asm__doConstShift ;Make it into a valid shift BVC %90asm__aluOp3 ;Return without error BVS %95asm__aluOp3 ;Return with error ; --- Operand 2 is a register --- 50asm__aluOp3 MOV R0,#0 ;No -- set up some flags BL asm__op2reg ;..read in operand 2 90asm__aluOp3 BLVC asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__aluOp3 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__aluOp2 --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with 2 operand ALU operations asm__aluOp2 ROUT STMFD R13!,{R14} ;Stack the link register MOV R9,#&01A00000 ;Make it a MOV CMP R5,#11 ;Was it a MVN? ORREQ R9,R9,#&00400000 ;Yes -- make it so MOV R4,R5 ;Remember the opcode BL asm__condition ;Read the condition BL asm__doSBit ;Skip to first register ; --- First off is a register name --- BL asm_register ;Read a register name BVS %95asm__aluOp2 ;On error -- report it ORR R9,R9,R0,LSL #12 ;Insert Rd in nicely BL asm__comma ;Read a comma BVS %95asm__aluOp2 ;On error -- report it ; --- Now for the last op (easy one, this) --- LDRB R14,[R8] ;Read a character CMP R14,#'#' ;Is is a hash? BNE %50asm__aluOp2 ;No -- Next is a register ; --- Operand 2 is an immediate constant --- ADD R8,R8,#1 ;Skip past the '#' BL asm__constant ;Read the constant MOV R1,R0 ;Remember the number BVS %95asm__aluOp2 ;If error -- barf ORR R9,R9,#(1<<25) ;Operand 2 is immediate BL asm__doConstShift ;Make it into a valid shift BVC %90asm__aluOp2 ;All OK -- return ; --- Try an alternative opcode --- MVN R0,R1 ;Put the number back in R0 CMP R4,#10 ;Was it a MOV MOVEQ R4,#11 ;Yes -- make it an MVN MOVNE R4,#10 ;No -- it's a MOV then 20asm__aluOp2 BIC R9,R9,#&01E00000 ;Clear the current opcode ORR R9,R9,#&01A00000 ;Make it a MOV CMP R4,#11 ;Was it a MVN? ORREQ R9,R9,#&00400000 ;Yes -- make it so BL asm__doConstShift ;Make it into a valid shift BVC %90asm__aluOp2 ;Return without error BVS %95asm__aluOp2 ;Return with error ; --- Operand 2 is a register --- 50asm__aluOp2 MOV R0,#0 ;No -- set up some flags BL asm__op2reg ;..read in operand 2 90asm__aluOp2 BLVC asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__aluOp2 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__aluOpTest --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with comparison ALU operations asm__aluOpTest ROUT STMFD R13!,{R14} ;Stack the link register MOV R9,#&01100000 ;Set top opcode bit, and 'S' MOV R4,R5 ;Remember the opcode SUB R5,R5,#12 ;Index opcode from 0 ADD R9,R9,R5,LSL#21 ;Correct the instruction BL asm__condition ;Read the condition BL asm__doSBit ;Skip to next register ; --- First off is a register name --- BL asm_register ;Read a register name BVS %95asm__aluOpTest ;On an error -- moan ORR R9,R9,R0,LSL #12 ;Insert Rd in nicely BL asm__comma ;Read a comma BVS %95asm__aluOpTest ;On an error -- moan ; --- Now for the last op (easy one, this) --- LDRB R14,[R8] ;Read a character CMP R14,#'#' ;Is is a hash? BNE %50asm__aluOpTest ;No -- Next is a register ; --- Operand 2 is an immediate constant --- ADD R8,R8,#1 ;Skip past the hash sign BL asm__constant ;Read the constant MOV R1,R0 ;Remember the number BVS %95asm__aluOpTest ;If error -- barf ORR R9,R9,#(1<<25) ;Operand 2 is immediate BL asm__doConstShift ;Make it into a valid shift BVC %90asm__aluOpTest ;All OK -- return ; --- Try an alternative opcode --- CMP R4,#14 ;Was it a CMP CMPNE R4,#15 ;Or a CMN ADRNE R0,asm__invConst ;No, point to the error BNE %95asm__aluOpTest ;And return with error RSB R0,R1,#0 ;Negate the number CMP R4,#14 ;Was it a CMP MOVEQ R4,#15 ;Yes -- make it a CMN MOVNE R4,#14 ;No -- make it a CMP then BIC R9,R9,#&00E00000 ;Clear the current opcode SUB R4,R4,#12 ;Index opcode from 0 ADD R9,R9,R4,LSL#21 ;Correct the instruction BL asm__doConstShift ;Make it into a valid shift BVC %90asm__aluOpTest ;Return without error BVS %95asm__aluOpTest ;Return with error ; --- Operand 2 is a register --- 50 MOV R0,#0 ;No -- set up some flags BL asm__op2reg ;..read in operand 2 90 BLVC asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__doConstShift --- ; ; On entry: R0 == The immediate ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Tries to find a way of fitting the given constant into ; 8 bits, with an even rotation. asm__doConstShift ROUT STMFD R13!,{R0-R3,R14} ;Stack some registers MOV R1,#0 ;The current shift count 10 MOV R2,R0,ROR R1 ;Perform the shift AND R3,R2,#&FF ;Just get the bottom byte CMP R2,R3 ;Were any other bits set? BEQ %50asm__doConstShift ;No -- we've found one ADD R1,R1,#2 ;Increment the shift count CMP R1,#30 ;Have we finished BLE %10asm__doConstShift ;No -- keep trying ; --- The constant is invalid --- ADR R0,asm__invConst ;Point to the error message LDMFD R13!,{R0-R3,R14} ;Load the link back ADR R0,asm__invConst ;Point to the error ORRS PC,R14,#V_flag ;Return with error ; --- We have found an valid rotation --- 50 CMP R1,#0 ;Is rotation greater than 0? RSBGT R1,R1,#32 ;Yes -- get 'reverse' value ORR R9,R9,R1,LSL #7 ;Put the rotation in inst ORR R9,R9,R2 ;And put the constant in too LDMFD R13!,{R0-R3,R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error asm__invConst DCD 1 DCB "Invalid immediate constant",0 LTORG ; --- asm__op2reg --- ; ; On entry: R0 == A nice flags word ; R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with the dreaded operand 2 ; (Rx [[,] [(# | Ry)]] asm__op2reg ROUT STMFD R13!,{R14} ;Stack the link register ; --- Read a (shifed) register --- MOV R4,R0 ;Remember the flags word BL asm_register ;Read a register name BVS %95asm__op2reg ;Barf if there's an error ORR R9,R9,R0 ;ORR in register BL asm__comma ;Skip the optional comma ORRVS R4,R4,#(1<<31) ;If error -- remember this ; --- Read in the shift --- MOV R0,R4 ;Put flags in R0 BL asm__readShift ;Read in the shift type BVC %90asm__op2reg ;All OK -- return TST R4,#(1<<31) ;Was there an error before? ADREQ R0,asm__badShift ;No -- we expected a shift BEQ %95asm__op2reg ;Right -- report the error 90asm__op2reg LDMFD R13!,{R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error 95asm__op2reg LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG asm__badShift DCD 1 DCB "Bad or missing shift",0 ; --- asm__comma --- ; ; On entry: R8 == pointer into the instruction ; ; On exit: VS and R0 == pointer to error or ; VC and R8 updated appropriately ; ; Use: Reads in a comma, skipping spaces asm__comma ROUT STMFD R13!,{R14} ;Stack the link register ; --- First we skip spaces --- 00 LDRB R14,[R8],#1 ;Load a new byte CMP R14,#' ' ;Skip over spaces CMPNE R14,#9 ;And tabs too BEQ %00asm__comma ;And stop when it isn't one ; --- No we read a comma --- CMP R14,#',' ;Is it a comma BNE %95asm__comma ;No -- complain ; --- Skip more spaces --- 10 LDRB R14,[R8],#1 ;Load a new byte CMP R14,#' ' ;Skip over spaces CMPNE R14,#9 ;And tabs too BEQ %00asm__comma ;And stop when it isn't one SUB R8,R8,#1 ;We overstepped the mark LDMFD R13!,{R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error ; --- Return an error --- 95asm__comma SUB R8,R8,#1 ;We overstepped the mark LDMFD R13!,{R14} ;Load the link back ADR R0,asm__commaExp ;Point to the message ORRS PC,R14,#V_flag ;Return with error asm__commaExp DCD 1 DCB "Comma expected",0 LTORG ; --- asm__endOfLine --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8 updated appropriately ; ; Use: Ensures that the end of the line has been reached asm__endOfLine ROUT STMFD R13!,{R14} ;Stack the link register ; --- First we skip spaces --- 00 LDRB R14,[R8],#1 ;Load a new byte CMP R14,#' ' ;Skip over spaces CMPNE R14,#9 ;And tabs too BEQ %00asm__endOfLine ;And stop when it isn't one CMP R14,#';' ;Is it a semicolon CMPNE R14,#31 ;Or a control character LDMFD R13!,{R14} ;Get the link back BICLES PC,R14,#V_flag ;All OK -- return nicely ADR R0,asm__eofError ;Point to error message ORRS PC,R14,#V_flag ;And return an error asm__eofError DCD 1 DCB "Rubbish at end of instruction",0 LTORG ; --- asm__constant --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R0 == number, R8 updated appropriately ; ; Use: Reads in a constant from the expression asm__constant ROUT STMFD R13!,{R1-R3,R14} ;Stack some registers LDRB R3,[R8] ;Read in a character CMP R3,#'-' ;Is it a minus character ADDEQ R8,R8,#1 ;Yes -- jump over it MOV R0,#10 ;Read decimal by default MOV R1,R8 ;Point to the string SWI XOS_ReadUnsigned ;Read in the number BVS %95asm__constant ;If error -- report it MOV R8,R1 ;Make R8 correct again MOV R0,R2 ;Put the number in R0 CMP R3,#'-' ;Should we negate number? RSBEQ R0,R0,#0 ;Yes -- make it negative 90asm__constant LDMFD R13!,{R1-R3,R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error 95asm__constant LDMFD R13!,{R1-R3,R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm_register --- ; ; On entry: R8 == pointer into the instruction ; ; On exit: VS and R0 == pointer to error or ; VC and R0 == number, R8 updated appropriately ; ; Use: Reads in a register name EXPORT asm_register asm_register ROUT STMFD R13!,{R1-R5,R14} ;Save some registers 00asm_register LDRB R14,[R8],#1 ;Read a character CMP R14,#32 ;Is it a space? CMPNE R14,#9 ;Or a tab BEQ %00asm_register ;Yes -- skip spaces SUB R8,R8,#1 ;We overshot the mark BL diss_regTable ;Find the register name table MOV R3,R0 ;Look after this pointer ; --- Start the main loop thing --- MOV R0,#15 ;Start testing R15 05asm_register MOV R1,R8 ;Point to the name start LDR R2,[R3,R0,LSL #2] ;Load the correct name ptr 10asm_register LDRB R4,[R2],#1 ;Get a byte from reg name LDRB R5,[R1],#1 ;Get a byte from the string ; --- Mangle R5 to be upper case, digit, or 0 --- CMP R5,#'Z' ;Is it bigger than a 'Z'? BICHI R5,R5,#&20 ;Yes -- make it upper case SUB R14,R5,#'A' ;Subtract upper case 'A' CMP R14,#26 ;Is it a letter of some kind? SUBHS R14,R5,#'0' ;No -- try a digit then CMPHS R14,#10 ;Is it one of them instead MOVHS R5,#0 ;No -- it's not alnum then ; --- Now make R4 upper case if it isn't already --- SUB R14,R4,#'a' ;Subtract lower case 'a' CMP R14,#26 ;Is it a lower case letter? BICLO R4,R4,#&20 ;Yes -- make it upper case ; --- Do the actual comparison then --- CMP R4,R5 ;Do they match nicely? BNE %20asm_register ;No -- try the next register CMP R4,#0 ;Is it the end of the string? BNE %10asm_register ;No -- then try more bytes ; --- We found a match -- yippee --- SUB R8,R1,#1 ;Update the string pointer LDMFD R13!,{R1-R5,R14} ;Unstack some registers BICS PC,R14,#V_flag ;And there wuz no error ; --- Try the next register --- 20asm_register SUBS R0,R0,#1 ;Move down a register BGE %05asm_register ;If more to try, do 'em ADR R0,asm__badReg ;Point to the error message LDMFD R13!,{R1-R5,R14} ;Unstack some registers ORRS PC,R14,#V_flag ;And return with my error asm__badReg DCD 1 DCB "Bad register name",0 LTORG ; --- asm__readShift --- ; ; On entry: R0 == A nice flags word ; R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with the dreaded operand 2 ; (# or Rx [[,] [(# | Ry)]] asm__readShift ROUT STMFD R13!,{R14} ;Stack the link register MOV R4,R0 ;Look after the flags ADR R6,asm__shifts ;Point to the possibilities BL asm__lookup ;Find the one that matches BCS %94asm__readShift ;And return the error ADD R8,R8,#3 ;Skip past shift op CMP R5,#4 ;Is it an RRX? MOVEQ R0,#0 ;Yes -- shift size is 0 then MOVEQ R5,#3 ;And it's realy an ROR BEQ %20asm__readShift ;And skip onwards CMP R5,#5 ;Is it ASL? MOVEQ R5,#0 ;Yes -- ASL doesn't exist! 00 LDRB R14,[R8],#1 ;Load a new byte CMP R14,#' ' ;Skip over spaces CMPNE R14,#9 ;And tabs too BEQ %00asm__readShift ;And stop when it isn't one ; --- We now reach the Shift Operand --- CMP R14,#'#' ;Is it immediate? BEQ %10asm__readShift ;Yes -- read the operand TST R4,#1 ;Is this a single reg trans? BNE %94asm__readShift ;And return the error SUB R8,R8,#1 ;We overstepped as usual BL asm_register ;Get the register value BVS %95asm__readShift ;Failed -- report the error ORR R9,R9,R5,LSL #5 ;Bang in the shift type ORR R9,R9,R0,LSL #8 ;Put that in there too ORR R9,R9,#(1<<4) ;And set reg-shifty bit B %90asm__readShift ;Finished... 'RAY!!! ; --- Immediate shifts --- 10 BL asm__constant ;Read a constant value BVS %95asm__readShift ;Failed -- report the error CMP R0,#0 ;Is this vaguely sensible? BLT %94asm__readShift ;-ve shifts are daft CMPEQ R5,#0 ;Check for LSL by 0 BEQ %20asm__readShift ;This is OK CMP R0,#0 ;Otherwise, is it 0? BEQ %94asm__readShift ;0 has special meanings CMP R0,#32 ;Is it too big? BGT %94asm__readShift ;Yes -- that's really bad BLT %20asm__readShift ;Less is OK though CMP R5,#1 ;32 is allowed for LSR CMPNE R5,#2 ;And for ASR BNE %94asm__readShift ;But not for anything else MOV R0,#0 ;Fix it to be 0 cunningly 20 ORR R9,R9,R5,LSL #5 ;Bang in the shift type ORR R9,R9,R0,LSL #7 ;Put the constant in too 90 LDMFD R13!,{R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error 94 ADR R0,asm__badShift ;Point to bad shift message 95 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__shifts DCB "LSL",0,"LSR",0,"ASR",0,"ROR",0,"RRX",0,"ASL",0 DCD 0 LTORG ; --- asm__branch --- ; ; On entry: R0 == The second character ; R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with branch instructions asm__branch ROUT STMFD R13!,{R14} ;Stack what we need ADD R8,R8,#1 ;Skip past the 'B' CMP R0,#'L' ;Is it a 'BL'? CMPNE R0,#'l' MOVNE R9,#&0A000000 ;No -- make instr a branch BNE %00asm__branch ;And skip ahead LDRB R0,[R8,#2] ;Is there a 3rd letter here? CMP R0,#32 ;What is it? MOVLE R9,#&0A000000 ;Nothing -- must be a branch MOVGT R9,#&0B000000 ;A character -- BL then ADDGT R8,R8,#1 ;...skip past the 'L' 00asm__branch BL asm__condition ;Read in the condition BL asm__doSpaces ;Skip following spaces ; --- Read in the value --- BLVC asm__readAddress ;Read in the address BVS %95asm__branch ;If there's an error, barf BIC R0,R0,#3 ;Clear the lower 2 bits SUB R0,R0,R7 ;Branch is PC relative MOV R0,R0,LSR#2 ;Shift it down a bit SUB R0,R0,#2 ;Allow for pipelining BIC R0,R0,#&FF000000 ;Clear the top bits ORR R9,R9,R0 ;Put value into instruction 90asm__branch BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__branch LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__readAddress --- ; ; On entry: R8 == pointer into the instruction ; ; On exit: VS and R0 == pointer to error or ; VC and R8 updated appropriately ; ; Use: Reads in an address asm__readAddress ROUT STMFD R13!,{R1-R3,R14} ;Stack some registers MOV R0,#10 ;Read decimal by default MOV R1,R8 ;Point to the string SWI XOS_ReadUnsigned ;Read in the number BVS %95asm__readAddress ;If error -- report it MOV R8,R1 ;Make R8 correct again MOV R0,R2 ;Put the number in R0 90 LDMFD R13!,{R1-R3,R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error 95 LDMFD R13!,{R1-R3,R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__multiply --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with multiply operation asm__multiply ROUT STMFD R13!,{R14} ;Stack the link register MOV R9,#&90 ;Make it identifiable CMP R5,#17 ;Is it an MLA? ORREQ R9,R9,#&00200000 ;Yes -- make it so MOV R4,R5 ;Remember the opcode BL asm__condition ;Read in the condition code BL asm__doSBit ;Skip to first register ; --- First off is a register name --- BL asm_register ;Read a register name ORRVC R9,R9,R0,LSL #16 ;Insert Rd in nicely BLVC asm__comma ;Read and ignore a comma ; --- So's the next one --- BLVC asm_register ;Read a register name ORRVC R9,R9,R0 ;Insert Rm in nicely BLVC asm__comma ;Read and ignore a comma ; --- So's the next one --- BLVC asm_register ;Read a register name ORRVC R9,R9,R0,LSL#8 ;Insert Rs in nicely BVS %95asm__multiply ;Barf if there's an error CMP R4,#17 ;Is this an MLA BNE %90asm__multiply ;No -- we're finished then BL asm__comma ;Read and ignore a comma BLVC asm_register ;Read a register name ORRVC R9,R9,R0,LSL#12 ;Insert Rn in nicely BVS %95asm__multiply ;Barf if there's an error 90asm__multiply BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__multiply LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__sDataTrans --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with LDR/STR type instructions asm__sDataTrans ROUT STMFD R13!,{R14} ;Stack the link register MOV R9,#&04000000 ;Make it identifiable CMP R5,#18 ;Is it an LDR? ORREQ R9,R9,#(1<<20) ;Yes -- make it so BL asm__condition ;Read in the condition LDRB R14,[R8,#0] ;Read the next character CMP R14,#'B' ;Is it a 'B'? CMPNE R14,#'b' ORREQ R9,R9,#(1<<22) ;Yes -- make it byte transfer ADDEQ R8,R8,#1 ;...skip over it LDREQB R14,[R8,#0] ;...and read another char CMP R14,#'T' ;Is it a 'T'? CMPNE R14,#'t' ORREQ R9,R9,#(1<<21) ;Yes -- set 'W' bit ADDEQ R8,R8,#1 ;...skip over it BL asm__doSpaces ;Skip over spaces ; --- First off is a register name --- BL asm_register ;Read a register name ORRVC R9,R9,R0,LSL #12 ;Insert Rd in nicely BLVC asm__comma ;Read and ignore a comma BVS %95asm__sDataTrans ;Report a possible error LDRB R14,[R8,#0] ;Read a character CMP R14,#'[' ;Is it an open bracket? BNE %70asm__sDataTrans ;No -- jump ahead ADD R8,R8,#1 ;Skip over the bracket BL asm_register ;Read a register name BVS %95asm__sDataTrans ;Report a possible error ORR R9,R9,R0,LSL #16 ;Insert Rd in nicely LDRB R14,[R8,#0] ;Read a character CMP R14,#']' ;Is it an close bracket? ORRNE R9,R9,#(1<<24) ;No -- set 'P' bit ADDEQ R8,R8,#1 ;Yes -- skip over it BEQ %10asm__sDataTrans ;...and jump ahead a little TST R9,#(1<<21) ;Is the 'W' bit set? ADRNE R0,asm__preAndT ;Yes -- point to an error BNE %95asm__sDataTrans ;And report the error 10 BL asm__comma ;Read in the comma BVS %60asm__sDataTrans ;No comma -- tidy up ; --- Now for the last op (easy one, this) --- LDRB R14,[R8] ;Read a character CMP R14,#'#' ;Is is a hash? BNE %50asm__sDataTrans ;No -- Next is a register ; --- Operand 2 is an immediate constant --- ADD R8,R8,#1 ;Skip past the '#' BL asm__constant ;Read the constant BVS %95asm__sDataTrans ;If error -- barf CMP R0,#0 ;Is it negative RSBLT R0,R0,#0 ;Yes -- make it positive ORRGE R9,R9,#(1<<23) ;No -- offset is +ve TST R0,#&FF000000 ;Are any of these bits set? TSTEQ R0,#&00FF0000 ;Or these bits? TSTEQ R0,#&0000F000 ;Or even these? ADRNE R0,asm__invOffset ;Yes -- point to error BNE %95asm__sDataTrans ;And report error ORR R9,R9,R0 ;ORR in the value B %60asm__sDataTrans ;And finsh off nicely ; --- Operand 2 is a register --- 50 LDRB R14,[R8,#0] ;Read in the next character CMP R14,#'-' ;Is it a minus sign? ADDEQ R8,R8,#1 ;Yes -- skip past it then ORRNE R9,R9,#(1<<23) ;No -- offset is +ve ORR R9,R9,#(1<<25) ;Offset is register based MOV R0,#1 ;Set up some flags BL asm__op2reg ;..read in operand 2 BVS %95asm__sDataTrans ;If error -- barf ; --- Finish off now --- 60 TST R9,#(1<<24) ;Is this pre-indexed? BEQ %90asm__sDataTrans ;No -- return OK LDRB R14,[R8],#1 ;Read a character CMP R14,#']' ;Is it the close character ADRNE R0,asm__noClose ;No -- point to an error BNE %95asm__sDataTrans ;And report an error LDRB R14,[R8,#0] ;Read a character CMP R14,#'!' ;Is it a '!'? ORREQ R9,R9,#(1<<21) ;Yes -- set write back bit ADDEQ R8,R8,#1 ;And skip past the pling B %90asm__sDataTrans ;All OK then ; --- The instruction is PC relative --- 70 BL asm__readAddress ;Read the address BVS %95asm__sDataTrans ;Report the error ORR R9,R9,#(1<<24) ;Make instruction pre-indexed ORR R9,R9,#(15<<16) ;Rn = PC BIC R0,R0,#3 ;Clear the lower 2 bits SUB R0,R0,R7 ;Branch is PC relative SUBS R0,R0,#8 ;Allow for pipelining RSBLT R0,R0,#0 ;If -ve -- make it positive ORRGE R9,R9,#(1<<23) ;Otherwise say offset is +ve BIC R0,R0,#&FF000000 ;Clear the top bits TST R0,#&FF000000 ;Are any of these bits set? TSTEQ R0,#&00FF0000 ;Or these bits? TSTEQ R0,#&0000F000 ;Or even these? ADRNE R0,asm__invOffset ;Yes -- point to error BNE %95asm__sDataTrans ;And report error ORR R9,R9,R0 ;Put value into instruction 90 BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__invOffset DCD 1 DCB "Invalid offset",0 asm__noClose DCD 1 DCB "] Expected",0 asm__preAndT DCD 1 DCB "Can't set Trans pin with pre-indexed " DCB "data transfer",0 LTORG ; --- asm__mDataTrans --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with LDM/STM type instructions asm__mDataTrans ROUT STMFD R13!,{R14} ;Stack the link register MOV R9,#&08000000 ;Make it identifiable CMP R5,#20 ;Is it a load instruction ORREQ R9,R9,#(1<<20) ;Yes -- make it so MOV R4,R5 ;Remember the opcode BL asm__condition ;Read the condition LDRB R14,[R8],#1 ;Read a character CMP R14,#'D' ;Is it a 'D'? CMPNE R14,#'d' BEQ %10asm__mDataTrans ;Yes -- jump ahead a bit CMP R14,#'I' ;Is it an 'I'? CMPNE R14,#'i' ORREQ R9,R9,#(1<<23) ;Yes -- make increase +ve BEQ %10asm__mDataTrans ;...and jump ahead a bit CMP R14,#'F' ;Is it an 'F'? CMPNE R14,#'f' BEQ %20asm__mDataTrans ;Yes -- deal with it CMP R14,#'E' ;Is it an 'E'? CMPNE R14,#'e' BEQ %30asm__mDataTrans ;Yes -- deal with it ADR R0,asm__invStk ;Point to the error message B %95asm__mDataTrans ;And report it ; --- It's an I/D stack --- 10 LDRB R14,[R8],#1 ;Load another character CMP R14,#'B' ;Is it a 'B'? CMPNE R14,#'b' ORREQ R9,R9,#(1<<24) ;Yes -- make it pre-indexed CMPNE R14,#'A' ;Is it a 'A'? CMPNE R14,#'a' ADRNE R0,asm__invStk ;Neither -- Point to error BNE %95asm__mDataTrans ;And report it B %40asm__mDataTrans ;Deal with the rest of it ; --- It's a F stack --- 20 LDRB R14,[R8],#1 ;Load another character CMP R14,#'D' ;Is it a 'D'? CMPNE R14,#'d' CMPEQ R4,#21 ;...and is it a store? ORREQ R9,R9,#(1<<24) ;Yes -- make it pre-indexed BEQ %40asm__mDataTrans ;Deal with the rest of it CMP R14,#'D' ;Is it a 'D'? CMPNE R14,#'d' CMPEQ R4,#20 ;...and is it a load? ORREQ R9,R9,#(1<<23) ;Yes -- make it increasing BEQ %40asm__mDataTrans ;Deal with the rest of it CMP R14,#'A' ;Is it a 'A'? CMPNE R14,#'a' CMPEQ R4,#21 ;...and is it a store? ORREQ R9,R9,#&01800000 ;Yes -- make it pre & +ve BEQ %40asm__mDataTrans ;Deal with the rest of it CMP R14,#'A' ;Is it a 'A'? CMPNE R14,#'a' CMPEQ R4,#20 ;...and is it a load? BEQ %40asm__mDataTrans ;Yes -- deal with rest of it ADR R0,asm__invStk ;Point to error B %95asm__mDataTrans ;And report it ; --- It's a E stack --- 30 LDRB R14,[R8],#1 ;Load another character CMP R14,#'D' ;Is it a 'D'? CMPNE R14,#'d' CMPEQ R4,#21 ;...and is it a store? BEQ %40asm__mDataTrans ;Yes -- deal with rest of it CMP R14,#'D' ;Is it a 'D'? CMPNE R14,#'d' CMPEQ R4,#20 ;...and is it a load? ORREQ R9,R9,#&01800000 ;Yes -- make it increasing BEQ %40asm__mDataTrans ;Deal with the rest of it CMP R14,#'A' ;Is it a 'A'? CMPNE R14,#'a' CMPEQ R4,#21 ;...and is it a store? ORREQ R9,R9,#(1<<23) ;Yes -- make it +ve BEQ %40asm__mDataTrans ;Deal with the rest of it CMP R14,#'A' ;Is it a 'A'? CMPNE R14,#'a' CMPEQ R4,#20 ;...and is it a load? ORREQ R9,R9,#(1<<24) ;Yes -- make it pre-indexed BEQ %40asm__mDataTrans ;Yes -- deal with rest of it ADR R0,asm__invStk ;Point to error B %95asm__mDataTrans ;And report it ; --- Now parse the rest of the instruction --- 40 BL asm__doSpaces ;Skip the following spaces BVS %95asm__mDataTrans ;Report the error BL asm_register ;Read in the next register BVS %95asm__mDataTrans ;Report the error ORR R9,R9,R0,LSL #16 ;Store it in the instruction LDRB R14,[R8,#0] ;Read the next character CMP R14,#'!' ;Is it a pling? ORREQ R9,R9,#(1<<21) ;Yes -- we want write back ADDEQ R8,R8,#1 ;...skip past the pling BL asm__comma ;Skip the comma BVS %95asm__mDataTrans ;Report the error LDRB R14,[R8],#1 ;Read the next character CMP R14,#'{' ;Is it the '{' ADRNE R0,asm__expLOpen ;No -- Point to error BNE %95asm__mDataTrans ;...and report it ; --- Now we read in the register list --- 50 BL asm_register ;Read in the register BVS %95asm__mDataTrans ;Report the error MOV R3,R0 ;Remember this register LDRB R14,[R8,#0] ;Load the next character CMP R14,#'-' ;Is it a register range? MOVNE R4,R3 ;No -- this is the final one BNE %60asm__mDataTrans ;...and jump ahead a little ADD R8,R8,#1 ;Skip over the minus sign BL asm_register ;Read in the register BVS %95asm__mDataTrans ;Report the error MOV R4,R0 ;Remember this register too CMP R3,R4 ;Is first higher than second? ADRGT R0,asm__invListSyn ;No -- Point to error BGT %95asm__mDataTrans ;...and report it 60 MOV R5,#1 ;Get a nice 1 value ORR R9,R9,R5,LSL R3 ;Set a register bit ADD R3,R3,#1 ;Increment the count CMP R3,R4 ;Have we finished? BLE %60asm__mDataTrans ;No -- keep on going LDRB R14,[R8,#0] ;Get the next character CMP R14,#'}' ;Is it a close bracket? BEQ %70asm__mDataTrans ;Yes -- we're nearly finished BL asm__comma ;Read in a comma BVS %95asm__mDataTrans ;Report the error B %50asm__mDataTrans ;Keep looping round 70 ADD R8,R8,#1 ;Skip the bracket LDRB R14,[R8,#0] ;Read in the next character CMP R14,#'^' ;Is it the caret? ORREQ R9,R9,#(1<<22) ;Yes -- set the 'S' bit ADDEQ R8,R8,#1 ;And skip over it 90 BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__invStk DCD 1 DCB "Unrecognised stack type",0 asm__expLOpen DCD 1 DCB "Missing {",0 asm__invListSyn DCD 1 DCB "Invalid list syntax",0 LTORG ; --- asm__swi --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with SWI instructions asm__swi ROUT STMFD R13!,{R14} ;Stack the link register MOV R9,#&0F000000 ;Make it a SWI then BL asm__condition ;Read the condition code BL asm__doSpaces ;Skip over spaces BVS %95asm__swi ;Report possible error ; --- Try to read a number --- MOV R0,#10 ;Default base MOV R1,R8 ;Point to the number SWI XOS_ReadUnsigned ;Read it in MOVVC R8,R1 ;All OK -- point to end MOVVC R0,R2 ;Put number in R0 BVC %80asm__swi ;...and return ; --- It must be a string then --- MOV R1,R8 ;Point to it again SWI XOS_SWINumberFromString ;Translate to a number BVS %04asm__swi ;On error -- look ahead 00asm__swi LDRB R14,[R8],#1 ;Read a character CMP R14,#32 ;Is it a terminator? BGT %00asm__swi ;No -- keep looking SUB R8,R8,#1 ;We overshot B %80asm__swi ;...and return ; --- As a last resort, see if it's an OS_WriteI --- 04asm__swi MOV R4,R8 ;Point to the first character BL str_buffer ;Get a buffer use MOV R0,R1 ;Remember this buffer 05asm__swi LDRB R14,[R4],#1 ;Read a character CMP R14,#'+' ;Is it a plus? BEQ %10asm__swi ;Yes -- jump ahead CMP R14,#31 ;Or a terminatore? BLE %95asm__swi ;Yes -- return STRB R14,[R0],#1 ;Store the character B %05asm__swi ;Keep on searching ; --- We have found a plus sign --- 10asm__swi MOV R14,#0 ;Get a NULL byte STRB R14,[R0,#0] ;Store at end of sting SWI XOS_SWINumberFromString ;Translate to a number BVS %95asm__swi ;Return possible error CMP R0,#&100 ;Is it OS_WriteC? ADRNE R0,asm__unknownSWI ;No -- point to error message BNE %95asm__swi ;..and return error MOV R0,#10 ;Default base to read MOV R1,R4 ;Point to after '+' SWI XOS_ReadUnsigned ;Read in a number BVC %70asm__swi ;All OK -- jump ahead LDRB R14,[R4,#0] ;Read the next character CMP R14,#'"' ;Is it a quote? CMPNE R14,#''' ;Allow single ones too ADRNE R0,asm__unknownSWI ;No -- point to error BNE %95asm__swi ;And return error LDRB R1,[R4,#2] ;Read the next character CMP R1,R14 ;Is it a quote? ADRNE R0,asm__unknownSWI ;No -- point to error BNE %95asm__swi ;And return error LDRB R2,[R4,#1] ;Read the character ADD R1,R4,#3 ;Skip over string 70asm__swi MOV R0,R2 ;Put number in R0 ADD R0,R0,#&100 ;Form correct number MOV R8,R1 ;Point to end of string 80asm__swi TST R0,#&FF000000 ;Are these bits set? ADRNE R0,asm__invSWINum ;Yes -- point to error BNE %95asm__swi ;...and return ORR R9,R9,R0 ;ORR in the number 90asm__swi BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__swi LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__unknownSWI DCD 1 DCB "SWI name not known",0 asm__invSWINum DCD 1 DCB "SWI number too high",0 LTORG ; --- asm__adr --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Copes with the ADR directive asm__adr ROUT STMFD R13!,{R14} ;Stack the link register MOV R9,#&02000000 ;ALU with immediate operand 2 ORR R9,R9,#(15<<16) ;Make Rn = PC BL asm__condition ;Read a condition BL asm__doSpaces ;Skips past spaces BVS %95asm__adr ;Complain bitterley BL asm_register ;Read in a register BVS %95asm__adr ;Complain if we need to ORR R9,R9,R0,LSL#12 ;Set up Rd BL asm__comma ;Skip over the comma BVS %95asm__adr ;Complain if we need to BL asm__readAddress ;Read the following address BVS %95asm__adr ;Complain if we need to SUB R0,R0,#8 ;Allow for pipelining CMP R0,R7 ;What sort of offset is it? SUBLT R0,R7,R0 ;Maybe its positive SUBGE R0,R0,R7 ;Or even positive ORRLT R9,R9,#&00400000 ;Make it a SUB ORRGE R9,R9,#&00800000 ;Or an ADD as appropriate BL asm__doConstShift ;Make it into a valid shift BVS %95asm__adr ;Complain n an error 90asm__adr BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__adr LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__swp --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with SWP instructions asm__swp ROUT STMFD R13!,{R14} ;Save the link MOV R9,#&01000000 ;Make it identifiable ORR R9,R9,#&90 ;Set these bits right too BL asm__condition ;Read the condition LDRB R14,[R8,#0] ;Load the next character CMP R14,#'B' ;Is it a 'B' CMPNE R14,#'b' ADDEQ R8,R8,#1 ;Yes -- skip over it ORREQ R9,R9,#(1<<22) ;...and make it a byte trans. BL asm__doSpaces ;Skip spaces BVS %95asm__swp ;And complain if we can BL asm_register ;Read a register ORRVC R9,R9,R0,LSL #12 ;Put Rd in instruction BLVC asm__comma ;And read a comma BLVC asm_register ;Read a register ORRVC R9,R9,R0 ;Put Rm in instruction BLVC asm__comma ;And read a comma BVS %95asm__swp ;And complain if we can LDRB R14,[R8],#1 ;Load the next character CMP R14,#'[' ;Is it a '[' ADRNE R0,asm__invSWPSyn ;No -- point to the error BNE %95asm__swp ;...and report it BL asm_register ;Read a register BVS %95asm__swp ;And complain if we can ORR R9,R9,R0,LSL #16 ;Put Rn in instruction LDRB R14,[R8],#1 ;Load the next character CMP R14,#']' ;Is it a ']' ADRNE R0,asm__invSWPSyn ;No -- point to the error BNE %95asm__swp ;...and report it 90asm__swp BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__swp LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__invSWPSyn DCD 1 DCB "Invalid synatax for SWP",0 LTORG ; --- asm__coproc --- ; ; On entry: R8 == pointer into the instruction ; ; On exit: VS and R0 == pointer to error or ; VC and R8 updated appropriately ; R0 == co-processor number ; ; Use: Reads a co-processor number of the form CPxx asm__coproc ROUT STMFD R13!,{R14} ;Stack the link register LDRB R14,[R8],#1 ;Read a character CMP R14,#'C' ;Is it a 'C'? CMPNE R14,#'c' ADRNE R0,asm__invCoproc ;No -- point to error BNE %95asm__coproc ;...and return LDRB R14,[R8],#1 ;Read a character CMP R14,#'P' ;Is it a 'P'? CMPNE R14,#'p' ADRNE R0,asm__invCoproc ;No -- point to error BNE %95asm__coproc ;...and return BL asm__constant ;Read in a constant CMP R0,#16 ;Is it in range? ADRHS R0,asm__invCoproc ;No -- point to error BHS %95asm__coproc ;...and return 90asm__coproc LDMFD R13!,{R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error 95asm__coproc LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__invCoproc DCD 1 DCB "Invalid co-processor number",0 LTORG ; --- asm__coprocReg --- ; ; On entry: R8 == pointer into the instruction ; ; On exit: VS and R0 == pointer to error or ; VC and R8 updated appropriately ; R0 == co-processor number ; ; Use: Reads a co-processor register number of the form Cxx asm__coprocReg ROUT STMFD R13!,{R14} ;Stack the link register LDRB R14,[R8],#1 ;Read a character CMP R14,#'C' ;Is it a 'C'? CMPNE R14,#'c' ADRNE R0,asm__invCoReg ;No -- point to error BNE %95asm__coprocReg ;...and return BL asm__constant ;Read in a constant CMP R0,#16 ;Is it in range? ADRHS R0,asm__invCoReg ;No -- point to error BHS %95asm__coprocReg ;...and return 90 LDMFD R13!,{R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error 95 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__invCoReg DCD 1 DCB "Invalid co-processor register",0 LTORG ; --- asm__cpOpc --- ; ; On entry: R0 == maximum number allowed ; R8 == pointer into the instruction ; ; On exit: VS and R0 == pointer to error or ; VC and R8 updated appropriately ; R0 == co-processor number ; ; Use: Reads a co-processor register number of the form xx. ; It ensures that the number is between 0 and R0 asm__cpOpc ROUT STMFD R13!,{R14} ;Stack the link register MOV R1,R0 ;Remember this value BL asm__constant ;Read in a constant CMP R0,R1 ;Is it in range? ADRHI R0,asm__invCoOp ;No -- point to error BHI %95asm__cpOpc ;...and return 90asm__cpOpc LDMFD R13!,{R14} ;Load the link back BICS PC,R14,#V_flag ;Return without error 95asm__cpOpc LDMFD R13!,{R14} ;Lad the link back ORRS PC,R14,#V_flag ;Return with error asm__invCoOp DCD 1 DCB "Operation code is out of range",0 LTORG ; --- asm__cdp --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with CDP instructions asm__cdp ROUT STMFD R13!,{R14} MOV R9,#&0E000000 ;Set up the instruction BL asm__condition ;Read the condition type BL asm__doSpaces ;Skip spaces BLVC asm__coproc ;Read the co-processor number ORRVC R9,R9,R0,LSL #8 ;Put it in the instruction BLVC asm__comma ;Read a comma MOVVC R0,#15 ;Maximum allowed operation BLVC asm__cpOpc ;Read the operation type ORRVC R9,R9,R0,LSL#20 ;Put it in the instruction BLVC asm__comma ;Read a comma BLVC asm__coprocReg ;Read CRd ORRVC R9,R9,R0,LSL#12 ;Put it in the instruction BLVC asm__comma ;Read a comma BLVC asm__coprocReg ;Read CRn ORRVC R9,R9,R0,LSL#16 ;Put it in the instruction BLVC asm__comma ;Read a comma BLVC asm__coprocReg ;Read CRm ORRVC R9,R9,R0 ;Put it in the instruction BVS %95asm__cdp ;Return possible error BL asm__comma ;Read a comma BVS %90asm__cdp ;If no comma, return OK BL asm__constant ;Read a constant BVS %95asm__cdp ;Return possible error CMP R0,#8 ;Is number out of range? ADRHS R0,asm__invAux ;Yes -- point to error BHS %95asm__cdp ;Return possible error ORR R9,R9,R0,LSL #5 ;Store the number 90asm__cdp BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95asm__cdp LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG asm__invAux DCD 1 DCB "Auxilary expression must be 0-7",0 ; --- asm__coRegTrans --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with MCR/MRC instructions asm__coRegTrans ROUT STMFD R13!,{R14} MOV R9,#&0E000000 ;Set up the instruction ORR R9,R9,#(1<<4) ;This bit should be set CMP R5,#29 ;Is it MCR? ORREQ R9,R9,#(1<<20) ;Yes -- make it so BL asm__condition ;Read the condition type BL asm__doSpaces ;Skip spaces BLVC asm__coproc ;Read the co-processor number ORRVC R9,R9,R0,LSL #8 ;Put it in the instruction BLVC asm__comma ;Read a comma MOVVC R0,#7 ;Maximum allowed operation BLVC asm__cpOpc ;Read the operation type ORRVC R9,R9,R0,LSL#21 ;Put it in the instruction BLVC asm__comma ;Read a comma BLVC asm_register ;Read Rd ORRVC R9,R9,R0,LSL#12 ;Put it in the instruction BLVC asm__comma ;Read a comma BLVC asm__coprocReg ;Read CRn ORRVC R9,R9,R0,LSL#16 ;Put it in the instruction BLVC asm__comma ;Read a comma BLVC asm__coprocReg ;Read CRm ORRVC R9,R9,R0 ;Put it in the instruction BVS %95asm__coRegTrans ;Return possible error BL asm__comma ;Read a comma BVS %90asm__coRegTrans ;If no comma, return OK BL asm__constant ;Read a constant BVS %95asm__coRegTrans ;Return possible error CMP R0,#8 ;Is number out of range? ADRHS R0,asm__invAux ;Yes -- point to error BHS %95asm__coRegTrans ;Return possible error ORR R9,R9,R0,LSL #5 ;Store the number 90 BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error LTORG ; --- asm__coDataTrans --- ; ; On entry: R8 == pointer into the instruction ; R9 == instruction so far ; ; On exit: VS and R0 == pointer to error or ; VC and R8,R9 updated appropriately ; ; Use: Deals with LDC/STC instructions asm__coDataTrans ROUT STMFD R13!,{R14} MOV R9,#&0C000000 ;Set up the instruction CMP R5,#26 ;Is it STC? ORRNE R9,R9,#(1<<20) ;No -- make it load then BL asm__condition ;Read the condition type LDRB R14,[R8,#0] ;Read the next character CMP R14,#'L' ;Is it an 'L'? CMPNE R14,#'l' ORREQ R9,R9,#(1<<22) ;Yes -- make it long transfer ADDEQ R8,R8,#1 ;...skip over it LDREQB R14,[R8,#0] ;...and read another char CMP R14,#'T' ;Is it a 'T'? CMPNE R14,#'t' ORREQ R9,R9,#(1<<21) ;Yes -- set 'W' bit ADDEQ R8,R8,#1 ;...skip over it BL asm__doSpaces ;Skip spaces BLVC asm__coproc ;Read the co-processor number ORRVC R9,R9,R0,LSL #8 ;Put it in the instruction BLVC asm__comma ;Read a comma BLVC asm__coprocReg ;Read CRd ORRVC R9,R9,R0,LSL#12 ;Put it in the instruction BLVC asm__comma ;Read a comma BVS %95asm__coDataTrans ;Return possible error LDRB R14,[R8,#0] ;Read a character CMP R14,#'[' ;Is it an open bracket? BNE %70asm__coDataTrans ;No -- jump ahead ADD R8,R8,#1 ;Skip over the bracket BL asm_register ;Read a register name BVS %95asm__coDataTrans ;Report a possible error ORR R9,R9,R0,LSL #16 ;Insert Rn in nicely LDRB R14,[R8,#0] ;Read a character CMP R14,#']' ;Is it an close bracket? ORRNE R9,R9,#(1<<24) ;No -- set 'P' bit ADDEQ R8,R8,#1 ;Yes -- skip over it BEQ %10asm__coDataTrans ;...and jump ahead a little TST R9,#(1<<21) ;Is the 'W' bit set? ADRNEL R0,asm__preAndT ;Yes -- point to an error BNE %95asm__coDataTrans ;And report the error 10 BL asm__comma ;Read in the comma ORRVS R9,R9,#(1<<23) ;No comma -- offset is +ve BVS %60asm__coDataTrans ;...tidy up ; --- Now for the last op (easy one, this) --- LDRB R14,[R8] ;Read a character CMP R14,#'#' ;Is is a hash? ADRNE R0,asm__invCoOffset ;No -- point to error BNE %95asm__coDataTrans ;And return an error ; --- Operand 2 is an immediate constant --- ADD R8,R8,#1 ;Skip past the '#' BL asm__constant ;Read the constant BVS %95asm__coDataTrans ;If error -- barf CMP R0,#0 ;Is it negative RSBLT R0,R0,#0 ;Yes -- make it positive ORRGE R9,R9,#(1<<23) ;No -- offset is +ve MOV R0,R0,LSR#2 ;Scale it a little AND R1,R0,#&FF ;Just get lower bits CMP R1,R0 ;Were just these set? ADRNEL R0,asm__invOffset ;No -- point to error BNE %95asm__coDataTrans ;And report error ORR R9,R9,R0 ;ORR in the value ; --- Finish off now --- 60 TST R9,#(1<<24) ;Is this pre-indexed? BEQ %90asm__coDataTrans ;No -- return OK LDRB R14,[R8],#1 ;Read a character CMP R14,#']' ;Is it the close character ADRNEL R0,asm__noClose ;No -- point to an error BNE %95asm__coDataTrans ;And report an error LDRB R14,[R8,#0] ;Read a character CMP R14,#'!' ;Is it a '!'? ORREQ R9,R9,#(1<<21) ;Yes -- set write back bit ADDEQ R8,R8,#1 ;And skip past the pling B %90asm__coDataTrans ;All OK then ; --- The instruction is PC relative --- 70 BL asm__readAddress ;Read the address BVS %95asm__coDataTrans ;Report the error TST R0,#3 ;Is the address word-aligned ADRNE R0,asm__ldcWord ;No -- point to error BNE %95asm__coDataTrans ;Report the error ORR R9,R9,#(1<<24) ;Make instruction pre-indexed ORR R9,R9,#(15<<16) ;Rn = PC SUB R0,R0,R7 ;Branch is PC relative SUBS R0,R0,#8 ;Allow for pipelining RSBLT R0,R0,#0 ;If -ve -- make it positive ORRGE R9,R9,#(1<<23) ;Otherwise say offset is +ve MOV R0,R0,LSR #2 ;Shift the offset down AND R1,R0,#&FF ;Just get lower bits CMP R1,R0 ;Were just these set? ADRNEL R0,asm__invOffset ;No -- point to error BNE %95asm__coDataTrans ;And report error ORR R9,R9,R0 ;Put value into instruction 90 BL asm__endOfLine ;Make sure it ends here LDMVCFD R13!,{R14} ;Load the link back BICVCS PC,R14,#V_flag ;Return without error 95 LDMFD R13!,{R14} ;Load the link back ORRS PC,R14,#V_flag ;Return with error asm__ldcWord DCD 1 DCB "Target is not word aligned",0 asm__invCoOffset DCD 1 DCB "Offset must be immediate constant",0 LTORG ;----- That's all, folks ---------------------------------------------------- END