├── iOSAppInAssembly ├── en.lproj │ └── InfoPlist.strings ├── Utilities_ARMv7.s ├── iOSAppInAssembly-Info.plist ├── main_ARMv7.s ├── View_ARMv7.s └── AppDelegate_ARMv7.s ├── iOSAppInAssembly.xcodeproj ├── xcuserdata │ └── angrybeast.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── iOSAppInAssembly.xcscheme ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── angrybeast.xcuserdatad │ │ ├── WorkspaceSettings.xcsettings │ │ └── xcdebugger │ │ └── Expressions.xcexplist └── project.pbxproj └── README.md /iOSAppInAssembly/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /iOSAppInAssembly.xcodeproj/xcuserdata/angrybeast.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /iOSAppInAssembly.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOSAppInAssembly.xcodeproj/project.xcworkspace/xcuserdata/angrybeast.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /iOSAppInAssembly.xcodeproj/project.xcworkspace/xcuserdata/angrybeast.xcuserdatad/xcdebugger/Expressions.xcexplist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /iOSAppInAssembly.xcodeproj/xcuserdata/angrybeast.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | iOSAppInAssembly.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C33F1D171860F4A800D3EE14 16 | 17 | primary 18 | 19 | 20 | C33F1D321860F4A800D3EE14 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOSAppInAssembly/Utilities_ARMv7.s: -------------------------------------------------------------------------------- 1 | // Utilities_ARMv7.s 2 | // This file contains a collection of utilities used throughout the application. 3 | 4 | 5 | // Helper function to convert C-string to Objective-C string. 6 | // 7 | // Parameters: 8 | // r0: input pointer 9 | // Results: 10 | // r0: result pointer 11 | .section __TEXT,__text,regular,pure_instructions 12 | .global util_getCFString 13 | .align 4 14 | util_getCFString: 15 | push {r4-r7, lr} // save LR, R7, R4-R6 16 | add r7, sp, #12 // adjust R7 to point to saved R7 17 | push {r8, r10, r11} // save remaining GPRs (R8, R10, R11) 18 | vstmdb sp!, {d8-d15} // save VFP/Advanced SIMD registers D8 19 | 20 | // To convert our string, we are calling the function CFStringCreateWithCString. 21 | // 22 | // Parameter 1 (r0) is the allocator. 23 | // Parameter 2 (r1) is the string. 24 | // Parameter 3 (r2) is the encoding. 25 | // 26 | // Its resulting string will be stored in the r0 register. 27 | mov r1, r0 28 | mov r0, #0 29 | mov r2, #1536 30 | bl _CFStringCreateWithCString 31 | 32 | vldmia sp!, {d8-d15} // restore VFP/Advanced SIMD registers 33 | pop {r8, r10, r11} // restore R8-R11 34 | pop {r4-r7, pc} // restore R4-R6, saved R7, return to saved LR 35 | -------------------------------------------------------------------------------- /iOSAppInAssembly/iOSAppInAssembly-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.richardjrossiii.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations~ipad 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationPortraitUpsideDown 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## An iOS App In Assembly 2 | 3 | It's just what it sounds like. Hand written, delicately debugged, well-commented, ARMv7 assembly. Work on this started before ARM64 devices were a thing, so support for them may come in the future. 4 | 5 | ### Goals 6 | 7 | Rather simple, make an app that compiles, runs, and draws something on the screen, using only hand written assembly. The only times I used the assembly output of clang was to determine the proper `.section`s for things, to let lldb be able to debug my functions. 8 | 9 | The basic structure of this app is based on my [iOS App In Pure C](https://github.com/richardjrossiii/CBasediOSApp), with a 'main' file which contains all the set-up code, and two supporting files, for each of the classes (AppDelegate, and View). 10 | 11 | The drawing code is all done using CoreGraphics, and displays the string 'Hello, Assembly!' in red on the screen. Here's a screenshot of the app running on my iPhone 5S: 12 | 13 | ![](http://i.imgur.com/mulfx8nl.png) 14 | 15 | ### Notes 16 | 17 | If running the app with any accessibility features enabled (switch control, guided access, etc.) the app crashes when the runtime tries to see if my App Delegate responds to the selector `accessibilityInitialize`, and I'm not entirely sure why. This may be fixed in the future. 18 | 19 | ### Resources 20 | 21 | - [Apple's iOS ABI Reference](https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Introduction/Introduction.html#//apple_ref/doc/uid/TP40009020-SW1) 22 | - [The ARM ABI Reference](http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html) 23 | - [The ARM Developer Suite Assembler Guide](http://infocenter.arm.com/help/topic/com.arm.doc.dui0068b/index.html) 24 | 25 | And, as always, the mighty power of Google. 26 | 27 | ### License 28 | 29 | Copyright 2014 Richard J. Ross III. 30 | 31 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 32 | 33 | http://www.apache.org/licenses/LICENSE-2.0 34 | 35 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 36 | -------------------------------------------------------------------------------- /iOSAppInAssembly/main_ARMv7.s: -------------------------------------------------------------------------------- 1 | // main_ARMv7.s 2 | // This is the entry point of the application, in ARMv7 assembly. 3 | 4 | // Firstly, we will setup all strings that will be used for the first part of this app. 5 | // Then, we will setup an auto-release pool to manage our dangling objects, 6 | // And finally, we will jump right into UIApplicationMain. 7 | 8 | // Create a raw C string to hold the data for the App Delegate 9 | .section __TEXT,__cstring 10 | s_delegateClassNameCStr: 11 | .asciz "AppDelegate" 12 | 13 | // 14 | // Entry point of the application. 15 | // 16 | // Parameters: 17 | // None. 18 | // 19 | // Results: 20 | // r0: error code to return to OS. 21 | // 22 | // 23 | .section __TEXT,__text,regular,pure_instructions 24 | .global _main 25 | .align 4 26 | _main: 27 | // Note: This was taken from the official ARMv7 calling conventions document: 28 | // https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv7FunctionCallingConventions.html#//apple_ref/doc/uid/TP40009022-SW5 29 | // I will assume that it is the correct way to start and finish functions throughout the project. 30 | push {r4-r7, lr} // save LR, R7, R4-R6 31 | add r7, sp, #12 // adjust R7 to point to saved R7 32 | push {r8, r10, r11} // save remaining GPRs (R8, R10, R11) 33 | vstmdb sp!, {d8-d15} // save VFP/Advanced SIMD registers D8 34 | 35 | // Setup the autorelease pool. This is done using the (non-documented) function objc_autoreleasePoolPush. 36 | // 37 | // It takes no parameters. 38 | // 39 | // It returns no results. 40 | bl _objc_autoreleasePoolPush 41 | 42 | // Setup our app delegate's class. This is done using our function AppDelegate_Setup. 43 | // 44 | // It takes no parameters. 45 | // 46 | // It returns no results. 47 | bl AppDelegate_Setup 48 | 49 | // Setup our custom view's class. This is done using our function View_Setup. 50 | // 51 | // It takes no parameters. 52 | // 53 | // It returns no results. 54 | bl View_Setup 55 | 56 | // Next, we need to actually start up the visual application. This is done in 2 steps. 57 | // 58 | // 1: Create a Objective-C string from a C-String for our App Delegate's name. 59 | // 2: Start the visual application. 60 | 61 | // Turn our C string into a NSString, using our function util_getCFString. 62 | // 63 | // Parameter 1 (r0) is the string we wish to convert. 64 | // 65 | // The newly created string object is stored in r0 after calling the function. 66 | mov r0, s_delegateClassNameCStr 67 | bl util_getCFString 68 | 69 | // The next step is to start the visual application. This is done using the function UIApplicationMain. 70 | // 71 | // Parameter 1 (r0) is the argument count passed to main. For the purposes of this demo, it is irrelevant. 72 | // Parameter 2 (r1) is the argument list passed to main. For the purposes of this demo, it is irrelevant. 73 | // Parameter 3 (r2) is the name of the principle class. This would be a subclass of UIApplication if we had one. 74 | // Parameter 4 (r3) is the name of the delegate class. This is our AppDelegate string. 75 | // 76 | // The result code of the application is stored in r0 after calling the function. 77 | mov r3, r0 78 | mov r2, #0 79 | mov r1, #0 80 | mov r0, #0 81 | bl _UIApplicationMain 82 | 83 | // Finally, we must clean up our auto-release pool. This is done using the function objc_autoreleasePoolPop. 84 | // 85 | // It takes no parameters. 86 | // 87 | // It returns no usable results. However, it clobbers r0, so we save and re-load that variable around it. 88 | push {r0} 89 | bl _objc_autoreleasePoolPop 90 | pop {r0} 91 | 92 | vldmia sp!, {d8-d15} // restore VFP/Advanced SIMD registers 93 | pop {r8, r10, r11} // restore R8-R11 94 | pop {r4-r7, pc} // restore R4-R6, saved R7, return to saved LR -------------------------------------------------------------------------------- /iOSAppInAssembly.xcodeproj/xcuserdata/angrybeast.xcuserdatad/xcschemes/iOSAppInAssembly.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /iOSAppInAssembly/View_ARMv7.s: -------------------------------------------------------------------------------- 1 | // View_ARMv7.s 2 | // This is the file that will contain the class for our view. 3 | 4 | // Setup all strings that will be used in this file. 5 | .section __TEXT,__cstring 6 | View_ParentClassName: 7 | .asciz "UIView" 8 | 9 | .section __TEXT,__cstring 10 | View_ClassName: 11 | .asciz "View" 12 | 13 | .section __TEXT,__cstring 14 | View_MethodName: 15 | .asciz "drawRect:" 16 | 17 | .section __TEXT,__cstring 18 | View_MethodEncoding: 19 | .asciz "v@:{CGRect={CGPoint=ff}{CGSize=ff}}" 20 | 21 | .section __TEXT,__cstring 22 | View_DrawString: 23 | .asciz "Hello, Assembly!" 24 | 25 | // Global variables for storing the class 26 | .section __DATA,regular 27 | .align 4 28 | View_Class: 29 | .long 0 30 | 31 | .section __DATA,regular 32 | .align 4 33 | View_FillRect: 34 | .float 0 35 | .float 0 36 | .float 1000 37 | .float 1000 38 | 39 | .section __DATA,regular 40 | .align 4 41 | View_FillColor: 42 | .float 1, 1, 0, 1 // RGBA 43 | 44 | .section __DATA,regular 45 | .align 4 46 | View_TextPosition: 47 | .float 100 48 | .float 100 49 | 50 | .section __DATA,regular 51 | .align 4 52 | View_TextMatrix: 53 | .float 1 // a 54 | .float 0 // b 55 | .float 0 // c 56 | .float -1 // d 57 | .float 0 // tx 58 | .float 0 // ty 59 | 60 | .section __DATA,regular 61 | .align 4 62 | View_FontName: 63 | .asciz "Helvetica" 64 | 65 | .section __DATA,regular 66 | .align 4 67 | View_FontSize: 68 | .float 20 69 | 70 | .section __DATA,regular 71 | .align 4 72 | View_DrawColor: 73 | .float 1, 0, 0, 1 // RGBA 74 | 75 | .section __DATA,regular 76 | .align 4 77 | View_DrawStringLength: 78 | .int 16 79 | 80 | // 81 | // The setup method for the view class. 82 | // 83 | // Parameters: 84 | // none. 85 | // 86 | // Results: 87 | // none. 88 | // 89 | // Registers used: 90 | // 91 | // r0-r3: Arguments 92 | // r4: Class Pointer. 93 | // r5-r6: Temporary values. 94 | // r7: Frame (stack) pointer. 95 | // r8: Temporary value. 96 | // r9: Scratch register. 97 | // r10-r11: Temporary values. 98 | // 99 | // Stack Used: 100 | // 101 | // 4 bytes, offset 0: Parameter passing. 102 | // 103 | .section __TEXT,__text,regular,pure_instructions 104 | .global View_Setup 105 | .align 4 106 | View_Setup: 107 | push {r4-r7, lr} // save LR, R7, R4-R6 108 | add r7, sp, #12 // adjust R7 to point to saved R7 109 | push {r8, r10, r11} // save remaining GPRs (R8, R10, R11) 110 | vstmdb sp!, {d8-d15} // save VFP/Advanced SIMD registers D8 111 | 112 | // Our goal here is to create a view class. We'll use the following steps to accomplish this: 113 | // 114 | // 1: Fetch the superclass of our new class. (In this case, UIView) 115 | // 2: Create our new class. 116 | // 3: Save it to our global variable in case we need to use it again. (In this case, View_Class) 117 | // 4: Setup the -drawRect: method of our class. 118 | // 119 | 120 | // Parameter 1 (r0) will be the name of the superclass. 121 | // The superclass will then be stored in r0 after calling the function. 122 | mov r0, View_ParentClassName 123 | bl _objc_getClass 124 | 125 | // Parameter 1 (r0) will be the superclass, which is already in the r0 register. 126 | // Parameter 2 (r1) will be the new class's name. 127 | // Parameter 3 (r2) will be the extra byte count of this class, in this case 0. 128 | // The new class will then be stored in the r0 register after calling the function. 129 | mov r1, View_ClassName 130 | mov r2, #0 131 | bl _objc_allocateClassPair 132 | 133 | // Now that we have our class, it's time to store it to our global variable, 134 | // and our r4 register for use inside this function.. 135 | mov r1, View_Class 136 | str r0, [r1] 137 | mov r4, r0 138 | 139 | // The next step to attach the method to our class. We'll do this in two steps: 140 | // 141 | // 1: Fetch the unique selector for the method we're implementing. 142 | // 2: Add the method to the class we've created. 143 | 144 | // To fetch the selector, we will use the function sel_getUid. 145 | // 146 | // Parameter 1 (r0) will be the name of the selector to get. 147 | // 148 | // The result of this function will then be stored in the r0 register. 149 | mov r0, View_MethodName 150 | bl _sel_getUid 151 | 152 | // To actually add the method to the class, we'll use the function class_addMethod. 153 | // 154 | // Parameter 1 (r0) will be the class (which is currently in r4). 155 | // Parameter 2 (r1) will be the selector (which is currently in r0). 156 | // Parameter 3 (r2) will be the address of the implementation. 157 | // Parameter 4 (r3) will be the encoding of this method. 158 | // 159 | // The success of this function will then be stored in the r0 register upon completion. 160 | mov r1, r0 161 | mov r0, r4 162 | mov r2, View_DrawRect 163 | mov r3, View_MethodEncoding 164 | bl _class_addMethod 165 | 166 | // If we've gotten this far, our class is ready to go, we just need to actually hook it into the runtime. 167 | // This is done using the function objc_registerClassPair. 168 | // 169 | // Parameter 1 (r0) will be the class, which is currently in the r4 register. 170 | // 171 | // This function has no results. 172 | mov r0, r4 173 | bl _objc_registerClassPair 174 | 175 | // Now, everything else should be done automatically! 176 | // Our drawRect will be called, and we shouldn't have to touch the runtime again! 177 | 178 | vldmia sp!, {d8-d15} // restore VFP/Advanced SIMD registers 179 | pop {r8, r10, r11} // restore R8-R11 180 | pop {r4-r7, pc} // restore R4-R6, saved R7, return to saved LR 181 | 182 | // 183 | // The method used for drawing the view. 184 | // 185 | // Parameters: 186 | // r0: View object. 187 | // r1: Selector passed (-drawRect:) 188 | // r2-r4,[stack-stack, offset 4]: Dirty rect. 189 | // 190 | // Results: 191 | // none. 192 | // 193 | // Registers used: 194 | // 195 | // r0-r3: Arguments 196 | // r4: CGContext Pointer. 197 | // r5-6: Temporary values. 198 | // r7: Frame (stack) pointer. 199 | // r8: Temporary value. 200 | // r9: Scratch register. 201 | // r10-r11: Temporary values. 202 | // 203 | // Stack Used: 204 | // 205 | // 4 bytes, offset 0: Parameter passing. 206 | // 207 | .section __TEXT,__text,regular,pure_instructions 208 | .align 4 209 | View_DrawRect: 210 | push {r4-r7, lr} // save LR, R7, R4-R6 211 | add r7, sp, #12 // adjust R7 to point to saved R7 212 | push {r8, r10, r11} // save remaining GPRs (R8, R10, R11) 213 | vstmdb sp!, {d8-d15} // save VFP/Advanced SIMD registers D8 214 | 215 | sub sp, sp, #12 // Allocate stack storage 216 | 217 | // Our goal in this method is to simply draw something to the screen. 218 | // We will use the QuartzCore functions for this, but we'll make things 219 | // a little bit more interesting by drawing something other than a rectangle. 220 | 221 | // First, let's fetch the current context with UIGraphcisGetCurrentContext(). 222 | // 223 | // It takes no praremters. 224 | // 225 | // The context will be placed in the r0 register upon returning, and we will then store it in the r4 register. 226 | bl _UIGraphicsGetCurrentContext 227 | mov r4, r0 228 | 229 | // Now, we'll clear the screen, using CGContextSetFillColor. 230 | // 231 | // Parameter 1 (r0) will be the context, which is currently in the r0 register. 232 | // Parameter 2 (r1) will be the pointer to the first element in our color array. 233 | // 234 | // This method returns no results 235 | mov r1, View_FillColor 236 | bl _CGContextSetFillColor 237 | 238 | // To actually fill the screen, we must now use CGContextFillRect. 239 | // 240 | // Parameter 1 (r0) will be the context, which is currently in the r4 register. 241 | // Parameter 2 (r1) will be the x element of the rect to fill. 242 | // Parameter 3 (r2) will be the y element of the rect to fill. 243 | // Parameter 4 (r3) will be the width element of the rect to fill. 244 | // Parameter 5 (Stack, offset 0) will be the height element of the rect to fill. 245 | // 246 | // This method returns no results. 247 | mov r0, r4 248 | 249 | // CGRect loading 250 | mov r9, View_FillRect 251 | ldr r1, [r9] 252 | ldr r2, [r9, #4] 253 | ldr r3, [r9, #8] 254 | ldr r9, [r9, #12] 255 | str r9, [sp] 256 | 257 | bl _CGContextFillRect 258 | 259 | // Now, we'll set the font. We'll do this with CGContextSelectFont 260 | // 261 | // Parameter 1 (r0) will be the context, which is currently in the r4 register 262 | // Parameter 2 (r1) will be the font name, which is Helvetica. 263 | // Parameter 3 (r2) will be the font size as a float 264 | // Parameter 4 (r3) will be the encoding of the text, which we will set at MacRoman, 1 265 | // 266 | // This method returns no results. 267 | mov r0, r4 268 | mov r1, View_FontName 269 | mov r9, View_FontSize 270 | ldr r2, [r9] 271 | mov r3, #1 272 | 273 | bl _CGContextSelectFont 274 | 275 | // Before we can draw this text, we need to set the text color. This will be done using CGContextSetFillColor. 276 | // 277 | // Parameter 1 (r0) will be the context, which is currently in register r4. 278 | // Parameter 2 (r1) will be the pointer to the first element in our color array. 279 | // 280 | // This method returns no results. 281 | mov r0, r4 282 | mov r1, View_DrawColor 283 | bl _CGContextSetFillColor 284 | 285 | // Now, at this point there is an issue - CGContext draws everything upside down, 286 | // and we need to scale it to flip it the right-side up. This is done using CGContextSetTextMatrix. 287 | // 288 | // Parameter 1 (r0) will be the context. 289 | // Parameter 2 (r1) will be the first element in the matrix. 290 | // Parameter 3 (r2) will be the second element in the matrix. 291 | // Parameter 4 (r3) will be the third element in the matrix. 292 | // Parameter 5 (Stack, offset 0) will be the fourth element in the matrix. 293 | // Parameter 6 (Stack, offset 4) will be the fifth element in the matrix. 294 | // Parameter 7 (Stack, offset 8) will be the sixth element in the matrix. 295 | // 296 | // This method returns no results. 297 | mov r0, r4 298 | mov r9, View_TextMatrix 299 | ldr r1, [r9] // 1 300 | ldr r2, [r9, #4] // 2 301 | ldr r3, [r9, #8] // 3 302 | 303 | ldr r5, [r9, #12] 304 | str r5, [sp] // 4 305 | 306 | ldr r5, [r9, #16] 307 | str r5, [sp, #4] // 5 308 | 309 | ldr r5, [r9, #20] 310 | str r5, [sp, #8] // 6 311 | 312 | bl _CGContextSetTextMatrix 313 | 314 | // Next, we'll set the text position. We'll do this with CGContextSetTextPosition 315 | // 316 | // Parameter 1 (r0) will be the context, which is currently in the r4 register 317 | // Parameter 2 (r1) will be the x position to set 318 | // Parameter 3 (r2) will be the y position to set 319 | // 320 | // This method returns no results. 321 | mov r0, r4 322 | 323 | mov r9, View_TextPosition 324 | ldr r1, [r9] 325 | ldr r2, [r9, #4] 326 | bl _CGContextSetTextPosition 327 | 328 | // If we did this correctly, now we should be able to draw the text to the screen! 329 | // This will be done with CGContextShowText 330 | // 331 | // Parameter 1 (r0) will be the context, which is currently in register r4. 332 | // Parameter 2 (r1) will be the pointer to the string. 333 | // Parameter 3 (r2) will be the length of the string. 334 | // 335 | // This method returns no results. 336 | mov r0, r4 337 | mov r1, View_DrawString 338 | mov r2, View_DrawStringLength 339 | ldr r2, [r2] 340 | bl _CGContextShowText 341 | 342 | // That's it! Now we simply return to the calling function, and we have successfully created an iOS app in ARM assembler. 343 | add sp, sp, #12 // Deallocate stack storage. 344 | 345 | vldmia sp!, {d8-d15} // restore VFP/Advanced SIMD registers 346 | pop {r8, r10, r11} // restore R8-R11 347 | pop {r4-r7, pc} // restore R4-R6, saved R7, return to saved LR 348 | -------------------------------------------------------------------------------- /iOSAppInAssembly.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C33F1D1C1860F4A800D3EE14 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33F1D1B1860F4A800D3EE14 /* Foundation.framework */; }; 11 | C33F1D1E1860F4A800D3EE14 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33F1D1D1860F4A800D3EE14 /* CoreGraphics.framework */; }; 12 | C33F1D201860F4A800D3EE14 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33F1D1F1860F4A800D3EE14 /* UIKit.framework */; }; 13 | C33F1D261860F4A800D3EE14 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C33F1D241860F4A800D3EE14 /* InfoPlist.strings */; }; 14 | C33F1D4B1860F4C400D3EE14 /* main_ARMv7.s in Sources */ = {isa = PBXBuildFile; fileRef = C33F1D4A1860F4C400D3EE14 /* main_ARMv7.s */; }; 15 | C36F5B20186167E5001F75A5 /* Utilities_ARMv7.s in Sources */ = {isa = PBXBuildFile; fileRef = C36F5B1F186167E5001F75A5 /* Utilities_ARMv7.s */; }; 16 | C3BFE152188B13AA00A67483 /* View_ARMv7.s in Sources */ = {isa = PBXBuildFile; fileRef = C36F5B1D1861674D001F75A5 /* View_ARMv7.s */; }; 17 | C3FB5AD818D3B19500FBED82 /* AppDelegate_ARMv7.s in Sources */ = {isa = PBXBuildFile; fileRef = C36F5B1B186165F2001F75A5 /* AppDelegate_ARMv7.s */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | C33F1D181860F4A800D3EE14 /* iOSAppInAssembly.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSAppInAssembly.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | C33F1D1B1860F4A800D3EE14 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 23 | C33F1D1D1860F4A800D3EE14 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 24 | C33F1D1F1860F4A800D3EE14 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 25 | C33F1D231860F4A800D3EE14 /* iOSAppInAssembly-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOSAppInAssembly-Info.plist"; sourceTree = ""; }; 26 | C33F1D251860F4A800D3EE14 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 27 | C33F1D341860F4A800D3EE14 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 28 | C33F1D4A1860F4C400D3EE14 /* main_ARMv7.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = main_ARMv7.s; sourceTree = ""; }; 29 | C36F5B1B186165F2001F75A5 /* AppDelegate_ARMv7.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = AppDelegate_ARMv7.s; sourceTree = ""; }; 30 | C36F5B1D1861674D001F75A5 /* View_ARMv7.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = View_ARMv7.s; sourceTree = ""; }; 31 | C36F5B1F186167E5001F75A5 /* Utilities_ARMv7.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = Utilities_ARMv7.s; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | C33F1D151860F4A800D3EE14 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | C33F1D1E1860F4A800D3EE14 /* CoreGraphics.framework in Frameworks */, 40 | C33F1D201860F4A800D3EE14 /* UIKit.framework in Frameworks */, 41 | C33F1D1C1860F4A800D3EE14 /* Foundation.framework in Frameworks */, 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | C33F1D0F1860F4A800D3EE14 = { 49 | isa = PBXGroup; 50 | children = ( 51 | C33F1D211860F4A800D3EE14 /* iOSAppInAssembly */, 52 | C33F1D1A1860F4A800D3EE14 /* Frameworks */, 53 | C33F1D191860F4A800D3EE14 /* Products */, 54 | ); 55 | sourceTree = ""; 56 | }; 57 | C33F1D191860F4A800D3EE14 /* Products */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | C33F1D181860F4A800D3EE14 /* iOSAppInAssembly.app */, 61 | ); 62 | name = Products; 63 | sourceTree = ""; 64 | }; 65 | C33F1D1A1860F4A800D3EE14 /* Frameworks */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | C33F1D1B1860F4A800D3EE14 /* Foundation.framework */, 69 | C33F1D1D1860F4A800D3EE14 /* CoreGraphics.framework */, 70 | C33F1D1F1860F4A800D3EE14 /* UIKit.framework */, 71 | C33F1D341860F4A800D3EE14 /* XCTest.framework */, 72 | ); 73 | name = Frameworks; 74 | sourceTree = ""; 75 | }; 76 | C33F1D211860F4A800D3EE14 /* iOSAppInAssembly */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | C36F5B1B186165F2001F75A5 /* AppDelegate_ARMv7.s */, 80 | C36F5B1D1861674D001F75A5 /* View_ARMv7.s */, 81 | C33F1D4A1860F4C400D3EE14 /* main_ARMv7.s */, 82 | C36F5B1F186167E5001F75A5 /* Utilities_ARMv7.s */, 83 | C33F1D221860F4A800D3EE14 /* Supporting Files */, 84 | ); 85 | path = iOSAppInAssembly; 86 | sourceTree = ""; 87 | }; 88 | C33F1D221860F4A800D3EE14 /* Supporting Files */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | C33F1D231860F4A800D3EE14 /* iOSAppInAssembly-Info.plist */, 92 | C33F1D241860F4A800D3EE14 /* InfoPlist.strings */, 93 | ); 94 | name = "Supporting Files"; 95 | sourceTree = ""; 96 | }; 97 | /* End PBXGroup section */ 98 | 99 | /* Begin PBXNativeTarget section */ 100 | C33F1D171860F4A800D3EE14 /* iOSAppInAssembly */ = { 101 | isa = PBXNativeTarget; 102 | buildConfigurationList = C33F1D441860F4A800D3EE14 /* Build configuration list for PBXNativeTarget "iOSAppInAssembly" */; 103 | buildPhases = ( 104 | C33F1D141860F4A800D3EE14 /* Sources */, 105 | C33F1D151860F4A800D3EE14 /* Frameworks */, 106 | C33F1D161860F4A800D3EE14 /* Resources */, 107 | ); 108 | buildRules = ( 109 | ); 110 | dependencies = ( 111 | ); 112 | name = iOSAppInAssembly; 113 | productName = iOSAppInAssembly; 114 | productReference = C33F1D181860F4A800D3EE14 /* iOSAppInAssembly.app */; 115 | productType = "com.apple.product-type.application"; 116 | }; 117 | /* End PBXNativeTarget section */ 118 | 119 | /* Begin PBXProject section */ 120 | C33F1D101860F4A800D3EE14 /* Project object */ = { 121 | isa = PBXProject; 122 | attributes = { 123 | LastUpgradeCheck = 0510; 124 | ORGANIZATIONNAME = richardjrossiii; 125 | }; 126 | buildConfigurationList = C33F1D131860F4A800D3EE14 /* Build configuration list for PBXProject "iOSAppInAssembly" */; 127 | compatibilityVersion = "Xcode 3.2"; 128 | developmentRegion = English; 129 | hasScannedForEncodings = 0; 130 | knownRegions = ( 131 | en, 132 | ); 133 | mainGroup = C33F1D0F1860F4A800D3EE14; 134 | productRefGroup = C33F1D191860F4A800D3EE14 /* Products */; 135 | projectDirPath = ""; 136 | projectRoot = ""; 137 | targets = ( 138 | C33F1D171860F4A800D3EE14 /* iOSAppInAssembly */, 139 | ); 140 | }; 141 | /* End PBXProject section */ 142 | 143 | /* Begin PBXResourcesBuildPhase section */ 144 | C33F1D161860F4A800D3EE14 /* Resources */ = { 145 | isa = PBXResourcesBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | C33F1D261860F4A800D3EE14 /* InfoPlist.strings in Resources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXResourcesBuildPhase section */ 153 | 154 | /* Begin PBXSourcesBuildPhase section */ 155 | C33F1D141860F4A800D3EE14 /* Sources */ = { 156 | isa = PBXSourcesBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | C3FB5AD818D3B19500FBED82 /* AppDelegate_ARMv7.s in Sources */, 160 | C3BFE152188B13AA00A67483 /* View_ARMv7.s in Sources */, 161 | C33F1D4B1860F4C400D3EE14 /* main_ARMv7.s in Sources */, 162 | C36F5B20186167E5001F75A5 /* Utilities_ARMv7.s in Sources */, 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXSourcesBuildPhase section */ 167 | 168 | /* Begin PBXVariantGroup section */ 169 | C33F1D241860F4A800D3EE14 /* InfoPlist.strings */ = { 170 | isa = PBXVariantGroup; 171 | children = ( 172 | C33F1D251860F4A800D3EE14 /* en */, 173 | ); 174 | name = InfoPlist.strings; 175 | sourceTree = ""; 176 | }; 177 | /* End PBXVariantGroup section */ 178 | 179 | /* Begin XCBuildConfiguration section */ 180 | C33F1D421860F4A800D3EE14 /* Debug */ = { 181 | isa = XCBuildConfiguration; 182 | buildSettings = { 183 | ALWAYS_SEARCH_USER_PATHS = NO; 184 | ARCHS = armv7; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_MODULES = YES; 188 | CLANG_ENABLE_OBJC_ARC = YES; 189 | CLANG_WARN_BOOL_CONVERSION = YES; 190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 191 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 192 | CLANG_WARN_EMPTY_BODY = YES; 193 | CLANG_WARN_ENUM_CONVERSION = YES; 194 | CLANG_WARN_INT_CONVERSION = YES; 195 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 197 | CODE_SIGN_IDENTITY = "iPhone Developer"; 198 | COPY_PHASE_STRIP = NO; 199 | GCC_C_LANGUAGE_STANDARD = gnu99; 200 | GCC_DYNAMIC_NO_PIC = NO; 201 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 202 | GCC_OPTIMIZATION_LEVEL = 0; 203 | GCC_PREPROCESSOR_DEFINITIONS = ( 204 | "DEBUG=1", 205 | "$(inherited)", 206 | ); 207 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 208 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 209 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 210 | GCC_WARN_UNDECLARED_SELECTOR = YES; 211 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 212 | GCC_WARN_UNUSED_FUNCTION = YES; 213 | GCC_WARN_UNUSED_VARIABLE = YES; 214 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 215 | ONLY_ACTIVE_ARCH = YES; 216 | SDKROOT = iphoneos; 217 | TARGETED_DEVICE_FAMILY = 2; 218 | }; 219 | name = Debug; 220 | }; 221 | C33F1D431860F4A800D3EE14 /* Release */ = { 222 | isa = XCBuildConfiguration; 223 | buildSettings = { 224 | ALWAYS_SEARCH_USER_PATHS = NO; 225 | ARCHS = armv7; 226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 227 | CLANG_CXX_LIBRARY = "libc++"; 228 | CLANG_ENABLE_MODULES = YES; 229 | CLANG_ENABLE_OBJC_ARC = YES; 230 | CLANG_WARN_BOOL_CONVERSION = YES; 231 | CLANG_WARN_CONSTANT_CONVERSION = YES; 232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 233 | CLANG_WARN_EMPTY_BODY = YES; 234 | CLANG_WARN_ENUM_CONVERSION = YES; 235 | CLANG_WARN_INT_CONVERSION = YES; 236 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 237 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 238 | CODE_SIGN_IDENTITY = "iPhone Distribution"; 239 | COPY_PHASE_STRIP = YES; 240 | ENABLE_NS_ASSERTIONS = NO; 241 | GCC_C_LANGUAGE_STANDARD = gnu99; 242 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 243 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 244 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 245 | GCC_WARN_UNDECLARED_SELECTOR = YES; 246 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 247 | GCC_WARN_UNUSED_FUNCTION = YES; 248 | GCC_WARN_UNUSED_VARIABLE = YES; 249 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 250 | SDKROOT = iphoneos; 251 | TARGETED_DEVICE_FAMILY = 2; 252 | VALIDATE_PRODUCT = YES; 253 | }; 254 | name = Release; 255 | }; 256 | C33F1D451860F4A800D3EE14 /* Debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 260 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 261 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 262 | GCC_DYNAMIC_NO_PIC = YES; 263 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 264 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 265 | GCC_PREFIX_HEADER = ""; 266 | GCC_STRICT_ALIASING = NO; 267 | INFOPLIST_FILE = "iOSAppInAssembly/iOSAppInAssembly-Info.plist"; 268 | LD_NO_PIE = YES; 269 | OTHER_CFLAGS = "-mno-thumb"; 270 | OTHER_LDFLAGS = ""; 271 | PRODUCT_NAME = "$(TARGET_NAME)"; 272 | TARGETED_DEVICE_FAMILY = 1; 273 | VALID_ARCHS = armv7; 274 | WRAPPER_EXTENSION = app; 275 | }; 276 | name = Debug; 277 | }; 278 | C33F1D461860F4A800D3EE14 /* Release */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 282 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 284 | GCC_DYNAMIC_NO_PIC = YES; 285 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 286 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 287 | GCC_PREFIX_HEADER = ""; 288 | GCC_STRICT_ALIASING = NO; 289 | INFOPLIST_FILE = "iOSAppInAssembly/iOSAppInAssembly-Info.plist"; 290 | LD_NO_PIE = YES; 291 | OTHER_CFLAGS = "-mno-thumb"; 292 | OTHER_LDFLAGS = ""; 293 | PRODUCT_NAME = "$(TARGET_NAME)"; 294 | TARGETED_DEVICE_FAMILY = 1; 295 | VALID_ARCHS = armv7; 296 | WRAPPER_EXTENSION = app; 297 | }; 298 | name = Release; 299 | }; 300 | /* End XCBuildConfiguration section */ 301 | 302 | /* Begin XCConfigurationList section */ 303 | C33F1D131860F4A800D3EE14 /* Build configuration list for PBXProject "iOSAppInAssembly" */ = { 304 | isa = XCConfigurationList; 305 | buildConfigurations = ( 306 | C33F1D421860F4A800D3EE14 /* Debug */, 307 | C33F1D431860F4A800D3EE14 /* Release */, 308 | ); 309 | defaultConfigurationIsVisible = 0; 310 | defaultConfigurationName = Release; 311 | }; 312 | C33F1D441860F4A800D3EE14 /* Build configuration list for PBXNativeTarget "iOSAppInAssembly" */ = { 313 | isa = XCConfigurationList; 314 | buildConfigurations = ( 315 | C33F1D451860F4A800D3EE14 /* Debug */, 316 | C33F1D461860F4A800D3EE14 /* Release */, 317 | ); 318 | defaultConfigurationIsVisible = 0; 319 | defaultConfigurationName = Release; 320 | }; 321 | /* End XCConfigurationList section */ 322 | }; 323 | rootObject = C33F1D101860F4A800D3EE14 /* Project object */; 324 | } 325 | -------------------------------------------------------------------------------- /iOSAppInAssembly/AppDelegate_ARMv7.s: -------------------------------------------------------------------------------- 1 | // AppDelegate_ARMv7.s 2 | // This is the file that will contain the class for our application's delegate. 3 | 4 | // Setup all strings that will be used in this file. 5 | .section __TEXT,__cstring 6 | AppDelegate_ParentClassName: 7 | .asciz "NSObject" 8 | 9 | .section __TEXT,__cstring 10 | AppDelegate_ClassName: 11 | .asciz "AppDelegate" 12 | 13 | .section __TEXT,__cstring 14 | AppDelegate_IvarName: 15 | .asciz "window" 16 | 17 | .section __TEXT,__cstring 18 | AppDelegate_IvarEncoding: 19 | .asciz "@" 20 | 21 | .section __TEXT,__cstring 22 | AppDelegate_MethodName: 23 | .asciz "application:didFinishLaunchingWithOptions:" 24 | 25 | .section __TEXT,__cstring 26 | AppDelegate_MethodEncoding: 27 | .asciz "c@:@@" 28 | 29 | .section __TEXT,__cstring 30 | AppDelegate_ScreenClassName: 31 | .asciz "UIScreen" 32 | 33 | .section __TEXT,__cstring 34 | AppDelegate_ScreenSingletonMethodName: 35 | .asciz "mainScreen" 36 | 37 | .section __TEXT,__cstring 38 | AppDelegate_ScreenSizePropertyName: 39 | .asciz "bounds" 40 | 41 | .section __TEXT,__cstring 42 | AppDelegate_WindowClassName: 43 | .asciz "UIWindow" 44 | 45 | .section __TEXT,__cstring 46 | AppDelegate_WindowAllocateMethodName: 47 | .asciz "alloc" 48 | 49 | .section __TEXT,__cstring 50 | AppDelegate_WindowInitMethodName: 51 | .asciz "initWithFrame:" 52 | 53 | .section __TEXT,__cstring 54 | AppDelegate_ViewControllerClassName: 55 | .asciz "UIViewController" 56 | 57 | .section __TEXT,__cstring 58 | AppDelegate_ViewControllerAllocateMethodName: 59 | .asciz "alloc" 60 | 61 | .section __TEXT,__cstring 62 | AppDelegate_ViewControllerInitMethodName: 63 | .asciz "init" 64 | 65 | .section __TEXT,__cstring 66 | AppDelegate_ViewControllerAttachViewMethodName: 67 | .asciz "setView:" 68 | 69 | .section __TEXT,__cstring 70 | AppDelegate_ViewControllerAttachMethodName: 71 | .asciz "setRootViewController:" 72 | 73 | .section __TEXT,__cstring 74 | AppDelegate_ViewClassName: 75 | .asciz "View" 76 | 77 | .section __TEXT,__cstring 78 | AppDelegate_ViewAllocateMethodName: 79 | .asciz "alloc" 80 | 81 | .section __TEXT,__cstring 82 | AppDelegate_ViewInitMethodName: 83 | .asciz "initWithFrame:" 84 | 85 | .section __TEXT,__cstring 86 | AppDelegate_WindowMakeVisibleMethodName: 87 | .asciz "makeKeyAndVisible" 88 | 89 | 90 | // Global variable for holding the class. 91 | .section __DATA,regular 92 | .align 4 93 | AppDelegate_Class: 94 | .long 0 95 | 96 | // Global variable for holding the window 97 | .section __DATA,regular 98 | .align 4 99 | AppDelegate_Window: 100 | .long 0 101 | 102 | .section __DATA,regular 103 | .align 4 104 | AppDelegate_ScreenSize: 105 | .float 0, 0, 0, 0 // x, y, width, height 106 | 107 | // 108 | // The setup method for the app delegate class. 109 | // 110 | // Parameters: 111 | // none. 112 | // 113 | // Results: 114 | // none. 115 | // 116 | // Registers used: 117 | // 118 | // r0-r3: Arguments 119 | // r4: Class Pointer. 120 | // r5-r6: Temporary values. 121 | // r7: Frame (stack) pointer 122 | // r8: Temporary value 123 | // r9: Scratch register 124 | // r10-r11: Temporary values. 125 | // 126 | // Stack Used: 127 | // 128 | // 4 bytes, offset 0: Parameter passing. 129 | // 130 | .section __TEXT,__text,regular,pure_instructions 131 | .global AppDelegate_Setup 132 | .align 4 133 | AppDelegate_Setup: 134 | push {r4-r7, lr} // save LR, R7, R4-R6 135 | add r7, sp, #12 // adjust R7 to point to saved R7 136 | push {r8, r10, r11} // save remaining GPRs (R8, R10, R11) 137 | vstmdb sp!, {d8-d15} // save VFP/Advanced SIMD registers D8 138 | sub sp, sp, #4 // Allocate stack storage 139 | 140 | // Before we can do anything else, we must first create a class to use as the application delegate. The basic process for this is as follows: 141 | // 142 | // 1: Fetch the superclass of the desired new class. (In this case, UIResponder) 143 | // 4: Create the new class. 144 | // 5: Save it out to a global varaible so that it can easily be re-used. (In this case, AppDelegate_Class) 145 | // 6: Setup any protocols that this class will conform to. (In this case, none) 146 | // 7: Setup any ivars that this class will use. (In this case, one, called window) 147 | // 8: Setup any methods that this class will implement. (In this case, the -application:didFinishLaunchingWithOptions: method) 148 | // 9: Register the class with the runtime. 149 | // 150 | 151 | // Parameter 1 (r0) will be the name of the superclass. 152 | // The superclass will then be stored in r0 after calling the function. 153 | mov r0, AppDelegate_ParentClassName 154 | bl _objc_getClass 155 | 156 | // Parameter 1 (r0) will be the superclass, which is already in register r0. 157 | // Parameter 2 (r1) will be the new class's name. 158 | // Parameter 3 (r2) will be the extra byte count of this class, in this case 0. 159 | // The new class will then be stored in r0 after calling the function. 160 | mov r1, AppDelegate_ClassName 161 | mov r2, #0 162 | bl _objc_allocateClassPair 163 | 164 | // We now have a perfectly working class inside register r0. 165 | // Now, we save it out to the global value, and our register used throughout the function. 166 | mov r1, AppDelegate_Class 167 | str r0, [r1] 168 | mov r4, r0 169 | 170 | // Next, we need to add the iVar to the class we have. This can be done with a single method call. 171 | // 172 | // Parameter 1 (r0) will be the class 173 | // Parameter 2 (r1) will be the ivar's name 174 | // Parameter 3 (r2) will be the size of the argument, which is the size of a pointer. On ARMv7, this is 4 bytes. 175 | // Parameter 4 (r3) will be the offset of the argument, in this case it will be 0. 176 | // Parameter 5 (Stack) will be the encoding of the argument 177 | // 178 | // The success of this function will then be stored in r0 after calling the function. 179 | mov r0, r4 180 | mov r1, AppDelegate_IvarName 181 | mov r2, #4 182 | mov r3, #0 183 | mov r5, AppDelegate_IvarEncoding 184 | str r5, [sp] 185 | bl _class_addIvar 186 | 187 | // Our next step is to register the method used for launching the app. We can do this in two steps. 188 | // 189 | // 1: Fetch the unique selector for the method we are implementing. (In this case, the -application:didFinishLaunchingWithOptions: method) 190 | // 2: Add the method to the class we have created. 191 | 192 | // To fetch the selector, we will use the function sel_getUid. 193 | // 194 | // Parameter 1 (r0) will be the name of the selector to get. 195 | // 196 | // The result of this function will then be stored in the r0 register. 197 | mov r0, AppDelegate_MethodName 198 | bl _sel_getUid 199 | 200 | // To add the method to the class, we will use the function class_addMethod. 201 | // 202 | // Parameter 1 (r0) will be the class. 203 | // Parameter 2 (r1) will be the selector (which is currently in r0). 204 | // Parameter 3 (r2) will be the address of the implementation. 205 | // Parameter 4 (r3) will be the encoding of the method. 206 | // 207 | // The success of this function will then be stored in r0 after calling the function. 208 | mov r1, r0 209 | mov r0, r4 210 | mov r2, AppDelegate_DidFinishLaunchingWithOptions 211 | mov r3, AppDelegate_MethodEncoding 212 | bl _class_addMethod 213 | 214 | // At this point, our class is fully set-up, and all that is left to do is register the class with the runtime. 215 | // This is done using the function objc_reigsterClassPair. 216 | // 217 | // Parameter 1 (r0) will be the class. 218 | // 219 | // This function has no results. 220 | mov r0, r4 221 | bl _objc_registerClassPair 222 | 223 | // We did it! Now the class should be all ready to go with the runtime, and our delegate's launching method will be called shortly. 224 | add sp, sp, #4 // Deallocate stack storage. 225 | vldmia sp!, {d8-d15} // restore VFP/Advanced SIMD registers 226 | pop {r8, r10, r11} // restore R8-R11 227 | pop {r4-r7, pc} // restore R4-R6, saved R7, return to saved LR 228 | 229 | // 230 | // This method should be called when the application finishes launching. 231 | // 232 | // Parameters: 233 | // r0: self (id) 234 | // r1: _cmd (SEL) 235 | // r2: application (UIApplication *) 236 | // r3: options (NSDictionary *) 237 | // 238 | // Results: 239 | // r0: success (BOOL) 240 | // 241 | // Registers used: 242 | // 243 | // r0-r3: Arguments 244 | // r4: Window Pointer. 245 | // r5: View Controller Pointer. 246 | // r6: View Pointer. 247 | // r7: Frame (stack) pointer 248 | // r8,r10,r11: Temporary values. 249 | // r9: Scratch register 250 | // 251 | // Stack Used: 12 bytes total. 252 | // 253 | // 4 bytes, offset 8: self. 254 | // 4 bytes, offset 4: extra parameter. 255 | // 4 bytes, offset 0: extra parameter. 256 | // 257 | .section __TEXT,__text,regular,pure_instructions 258 | .align 4 259 | AppDelegate_DidFinishLaunchingWithOptions: 260 | push {r4-r7, lr} // save LR, R7, R4-R6 261 | add r7, sp, #12 // adjust R7 to point to saved R7 262 | push {r8, r10, r11} // save remaining GPRs (R8, R10, R11) 263 | vstmdb sp!, {d8-d15} // save VFP/Advanced SIMD registers D8 264 | sub sp, sp, #12 // Allocate stack space. 265 | 266 | // Save self off to the stack. 267 | str r0, [sp, #8] 268 | 269 | // 270 | // Our goal here is to create a window, view controller, and view to show on the screen. 271 | // This will be done with the following steps: 272 | // 273 | // 1: Fetch the size of the screen, using the UIScreen singleton. 274 | // 2: Create an instance of type UIWindow, and store it in self's window variable. 275 | // 3: Create a View Controller, and set that as the window's root controller. 276 | // 4: Create an instance of our view class, and add it to the view controller's heirarchy. 277 | // 5: Make the window the key window on the screen. 278 | // 279 | 280 | // Our first step is to get the size of the screen. 281 | // This poses quite the problem, because the 'bounds' property of the screen returns a struct. 282 | // Thus, we must use objc_msgSend_stret instead of the usual objc_msgSend. 283 | // 284 | // However, first we must get the singleton instance. 285 | // 286 | // Parameter 1 (r0) will be the name of the class to fetch. 287 | // 288 | // The found class will then be placed in the r0 register upon return. We will store it in the r8 register for now. 289 | mov r0, AppDelegate_ScreenClassName 290 | bl _objc_getClass 291 | mov r8, r0 292 | 293 | // The next step is to prepare the selector for use with objc_msgSend. 294 | // This will be done via sel_getUid 295 | // 296 | // Parameter 1 (r0) will be the name of the selector to fetch. 297 | // 298 | // The results of this function will be stored in the r0 register upon return. 299 | mov r0, AppDelegate_ScreenSingletonMethodName 300 | bl _sel_getUid 301 | 302 | // Now, we take the selector, and our target object, and use objc_msgSend to fetch the singleton. 303 | // 304 | // Parameter 1 (r0) will be the target of the message, in this case the UIScreen class, which we stored in the r8 register. 305 | // Parameter 2 (r1) will be the message to pass, in this case, '-mainScreen', which is currently in the r0 register. 306 | // 307 | // The results of this function (the singleton) will be placed in the r0 register upon return. We will then store it into the r8 register, as we no longer need the class there. 308 | mov r1, r0 309 | mov r0, r8 310 | bl _objc_msgSend 311 | mov r8, r0 312 | 313 | // Now that we have our singleton, the next step is to call the bounds method. We have to be careful with this, because of the struct return, but for the most part it is the same as any other method call. 314 | // 315 | // Before we can do that, if you haven't guessed by now, we need to convert our string into a selector. 316 | mov r0, AppDelegate_ScreenSizePropertyName 317 | bl _sel_getUid 318 | 319 | // Since we now have our blessed SEL, it is time to do the objc_msgSend_stret call. 320 | // 321 | // Parameter 1 (r0) will be the address of the struct in memory, which we store in AppDelegate_ScreenSize. 322 | // Parameter 2 (r1) will be the target of this message, which is our singleton, stored in the r8 register. 323 | // Parameter 3 (r2) will be the message to pass, which we just got from sel_getUid, and is in the r0 register. 324 | 325 | // Always return YES (1) to the calling function. 326 | mov r2, r0 327 | mov r1, r8 328 | mov r0, AppDelegate_ScreenSize 329 | bl _objc_msgSend_stret 330 | 331 | // Now that we have the screen size, it is time for us to create our window. 332 | // To create our window, we will need to fetch the class first, however. 333 | // 334 | // Parameter 1 (r0) will be the name of the class to fetch. 335 | // 336 | // The results of this function will be placed in the r0 register upon return. However, we will store it in the r8 register for now. 337 | mov r0, AppDelegate_WindowClassName 338 | bl _objc_getClass 339 | mov r8, r0 340 | 341 | // Next, we need to fetch the sel wthat we will use to allocate our window, using the sel_getUid function. 342 | // 343 | // Parameter 1 (r0) will be the name of the selector to fetch. 344 | // 345 | // The resutls of this function will be stored in the r0 register upon return. 346 | mov r0, AppDelegate_WindowAllocateMethodName 347 | bl _sel_getUid 348 | 349 | // Our next move is to actually allocate our class, using the objc_msgSend function. 350 | // 351 | // Parameter 1 (r0) will be the target of this message, in this case the class which is currently stored in r8. 352 | // Parameter 2 (r1) will be the message to pass to the target, which is currently in r0. 353 | // 354 | // The results of this function will be stored in the r0 register upon return, and we will move it into the r4 register. 355 | mov r1, r0 356 | mov r0, r8 357 | bl _objc_msgSend 358 | mov r4, r0 359 | 360 | // Now that we have our window, it's time to initialize it, but first we need to get our selector. 361 | // 362 | // Parameter 1 (r0) will be the name of the selector to fetch. 363 | // 364 | // The results of this function will be stored in the r0 register upon return. 365 | mov r0, AppDelegate_WindowInitMethodName 366 | bl _sel_getUid 367 | 368 | // To fully initialize the window, we need to pass our struct to the function. 369 | // This can simply be done by pushing the entire contents of the struct onto the stack. We will use objc_msgSend for this. 370 | // 371 | // Parameter 1 (r0) will be the target of this message, which is currently in r4. 372 | // Parameter 2 (r1) will be the message to send, which is currently in r0. 373 | // Parameter 3 (r2) will be the x element of the CGRect. 374 | // Parameter 4 (r3) will be the y element of the CGRect. 375 | // Parameter 5 (Stack, offset 0) will be the width element of the CGRect. 376 | // Parameter 6 (Stack, offset 4) will be the height element of the CGRect. 377 | // 378 | // The results of this function will be placed in the r0 register upon return. We will then retain it, and put it in our global variable. 379 | mov r1, r0 380 | mov r0, r4 381 | 382 | // CGRect Passing 383 | mov r8, AppDelegate_ScreenSize 384 | ldr r2, [r8] // load x element 385 | ldr r3, [r8, #4] // load y element 386 | ldr r10, [r8, #8] // load width element 387 | str r10, [sp] // store width to stack 388 | ldr r10, [r8, #12] // load height element 389 | str r10, [sp, #4] // store height to stack 390 | 391 | bl _objc_msgSend 392 | bl _objc_retain 393 | 394 | mov r1, AppDelegate_Window 395 | str r0, [r1] 396 | 397 | // Our window is now fully initialized. We need to then save it to self, which is currently on the stack. 398 | // The offset of the variable should be 4 bytes, as there is the sole 'isa' field that is store alongside our variable as well. 399 | ldr r0, [sp, #8] 400 | ldr r0, [r0] 401 | str r4, [r0, #4] 402 | 403 | // Now that we have set the ivar properly, it is time to allocate our view controller. 404 | // To do this, we will need to fetch the class and selector that we will use. 405 | // Obviously, we will start with objc_getClass. 406 | // 407 | // Parameter 1 (r0) will be the name of the class to fetch. 408 | // 409 | // The results of this function will be placed in the r0 register, 410 | // but we will move it to the r8 register for now. 411 | mov r0, AppDelegate_ViewControllerClassName 412 | bl _objc_getClass 413 | mov r8, r0 414 | 415 | // Before we can allocate our class, we need to fetch our selector (surprise surprise). 416 | // 417 | // Parameter 1 (r0) will be the name of the selector to fetch. 418 | // 419 | // The results of this function will be placed in the r0 register. 420 | mov r0, AppDelegate_ViewControllerAllocateMethodName 421 | bl _sel_getUid 422 | 423 | // Now we can finally allocate our class. Just like we've done before, we will use objc_msgSend for this. 424 | // 425 | // Parameter 1 (r0) will be the target of the message, which is in the r8 register. 426 | // Parameter 2 (r1) will be the message to pass, which is currently in the r0 register. 427 | // 428 | // The results of this function will be placed in the r0 register. We will store it in the r8 register for now. 429 | mov r1, r0 430 | mov r0, r8 431 | bl _objc_msgSend 432 | mov r8, r0 433 | 434 | // Next up is to get our selector for initializing the class. 435 | // 436 | // Parameter 1 (r0) will be the name of the selector to fetch. 437 | // 438 | // The results of this function will be placed in the r0 register. 439 | mov r0, AppDelegate_ViewControllerInitMethodName 440 | bl _sel_getUid 441 | 442 | // Finally, we can initialize our view controller. There's nothing really special about this, as we don't need to pass any fancy arguments to the method. 443 | // 444 | // Parameter 1 (r0) will be the target of the message, which is currently stored in the r8 register. 445 | // Parameter 2 (r1) will be the message to pass, which is currently stored in the r0 register. 446 | // 447 | // The results of this function will be placed in the r0 register, which we will then move to the r5 register. 448 | mov r1, r0 449 | mov r0, r8 450 | bl _objc_msgSend 451 | mov r5, r0 452 | 453 | // Now that we (finally) have our view controller. We should attach it to our window. 454 | // 455 | // Firstly, we should get our next selector through sel_getUid. You should know the drill by now. 456 | // 457 | // Parameter 1 (r0) will be the name of the selector to fetch. 458 | // 459 | // The results of this function will be placed in the r0 register. 460 | mov r0, AppDelegate_ViewControllerAttachMethodName 461 | bl _sel_getUid 462 | 463 | // 464 | // Next, we attach the view controller through the window via our best friend, objc_msgSend. 465 | // 466 | // Parameter 1 (r0) will be the target of the method, which is our window in the r4 register. 467 | // Parameter 2 (r1) will be the message to send, which is in the r0 register. 468 | // Parameter 3 (r2) will be the new view controller to attach, which is in the r5 register. 469 | // 470 | // This method has no results. 471 | mov r1, r0 472 | mov r0, r4 473 | mov r2, r5 474 | bl _objc_msgSend 475 | 476 | // Creating the view will be done in several steps: 477 | // 478 | // 1: Fetch our view class 479 | // 2: Allocate a new instance of our view class 480 | // 3: Set our new view instance as the view controller's view 481 | 482 | // It's time for us to get the view's class object. This will be done with objc_getClass 483 | // 484 | // Parameter 1 (r0) will be the name of the class to fetch. 485 | // 486 | // The found class will then be placed in the r0 register upon return. We will store it in the r8 register for now. 487 | mov r0, AppDelegate_ViewClassName 488 | bl _objc_getClass 489 | mov r8, r0 490 | 491 | // Next we need to get the selector for allocating our new view, using sel_getUid. 492 | // 493 | // Parameter 1 (r0) will be the name of the selector to fetch. 494 | // 495 | // The results of this function will be placed in the r0 register. 496 | mov r0, AppDelegate_ViewAllocateMethodName 497 | bl _sel_getUid 498 | 499 | // Now, we can allocate our class, using objc_msgSend. 500 | // 501 | // Parameter 1 (r0) will be the class to allocate an instance of, which is our class in the r8 register. 502 | // Parameter 2 (r1) will be the message to send, in this case the selector which is in the r0 register. 503 | // 504 | // The created instance will then be stored in the r0 function after completion, we will however store it in the r6 register. 505 | mov r1, r0 506 | mov r0, r8 507 | bl _objc_msgSend 508 | mov r6, r0 509 | 510 | // Next up is to initialize our class, once again done using our two favorite methods in the world, sel_getUid and objc_msgSend. 511 | // 512 | // Parameter 1 (r0) will be the name of the selector to fetch. 513 | // 514 | // The results of this function will be placed in the r0 register. 515 | mov r0, AppDelegate_ViewInitMethodName 516 | bl _sel_getUid 517 | 518 | // Now we fcan actually pass the message, using objc_msgSend 519 | // 520 | // Parameter 1 (r0) will be the object to pass the method to, in this case our view in the r6 register. 521 | // Parameter 2 (r1) will be the message to pass, which is currently in the r0 register. 522 | // Parameter 3 (r2) will be the x element of the CGRect. 523 | // Parameter 4 (r3) will be the y element of the CGRect. 524 | // Parameter 5 (Stack, offset 0) will be the width element of the CGRect. 525 | // Parameter 6 (Stack, offset 4) will be the height element of the CGRect. 526 | mov r1, r0 527 | mov r0, r6 528 | 529 | // CGRect Passing 530 | mov r8, AppDelegate_ScreenSize 531 | ldr r2, [r8] // load x element 532 | ldr r3, [r8, #4] // load y element 533 | ldr r10, [r8, #8] // load width element 534 | str r10, [sp] // store width to stack 535 | ldr r10, [r8, #12] // load height element 536 | str r10, [sp, #4] // store height to stack 537 | 538 | bl _objc_msgSend 539 | 540 | // We're just about done, there's only one thing really left to go, and that's attaching the view to our view controller. 541 | // Before we can do that, we must first fetch our selector. 542 | // 543 | // Parameter 1 (r0) will be the name of the selector to fetch. 544 | // 545 | // The results of this function will be placed in the r0 register. 546 | mov r0, AppDelegate_ViewControllerAttachViewMethodName 547 | bl _sel_getUid 548 | 549 | // Next, we can send the message to our view controller, using objc_msgSend 550 | // 551 | // Parameter 1 (r0) will be the target of this message, which is our view controller in register r5. 552 | // Parameter 2 (r1) will be the message to pass, which is currently in r0. 553 | // Parameter 3 (r2) will be the view to set, which is currently in the r6 register. 554 | // 555 | // This method returns no results. 556 | mov r1, r0 557 | mov r0, r5 558 | mov r2, r6 559 | bl _objc_msgSend 560 | 561 | // Now, make the window visible on-screen! To do this, we use (you guessed it!) sel_getUid and objc_msgSend. 562 | // 563 | // Parameter 1 (r0) will be the name of the selector to fetch. 564 | // 565 | // The results of this function will be placed in the r0 register. 566 | mov r0, AppDelegate_WindowMakeVisibleMethodName 567 | bl _sel_getUid 568 | 569 | // 570 | // Next up is to simply pass the message along with objc_msgSend. 571 | // 572 | // Parameter 1 (r0) will be the target of the message, which is our window in the r4 register. 573 | // Parameter 2 (r1) will be the message we're sending, which is currently in the r0 register. 574 | // 575 | // This method has no results. 576 | mov r1, r0 577 | mov r0, r4 578 | bl _objc_msgSend 579 | 580 | // WE DID IT! If done properly, we should now have stuff happening on the screen! 581 | // Next, we just release the various objects we have allocated. 582 | // Over the course of the method, we allocated the following objects: 583 | // Window (r4) 584 | // View Controller (r5) 585 | // View (r6) 586 | mov r0, r4 587 | bl _objc_release 588 | 589 | mov r0, r5 590 | bl _objc_release 591 | 592 | mov r0, r6 593 | bl _objc_release 594 | 595 | // Always return YES to the caller. 596 | mov r0, #1 597 | 598 | add sp, sp, #12 // Deallocate stack space. 599 | vldmia sp!, {d8-d15} // restore VFP/Advanced SIMD registers 600 | pop {r8, r10, r11} // restore R8-R11 601 | pop {r4-r7, pc} // restore R4-R6, saved R7, return to saved LR --------------------------------------------------------------------------------