pstarter.s 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. | TIGCC Program Starter
  2. | Copyright (C) 2004-2005 Kevin Kofler, Lionel Debroux.
  3. |
  4. | This launcher is free software; you can redistribute it and/or
  5. | modify it under the terms of the GNU Lesser General Public
  6. | License as published by the Free Software Foundation; either
  7. | version 2.1 of the License, or (at your option) any later version.
  8. |
  9. | As a special exception, UNMODIFIED copies of this launcher may also be
  10. | redistributed or sold without source code, for any purpose, as long as
  11. | such redistribution or selling is allowed by the license of the chosen
  12. | decompression routine. (The Lesser General Public License restrictions
  13. | do apply in other respects; for example, they cover modification of
  14. | the launcher. Licensing restrictions of the chosen decompression routine
  15. | also apply.) Merely replacing "tempprog" with the name of your program
  16. | shall not be considered a modification for the purposes of this
  17. | paragraph. This exception notice must be removed on modified copies of
  18. | this launcher.
  19. |
  20. | Please make sure you also read the licensing terms of the decompression
  21. | routine of your choice. They are written in the corresponding header
  22. | files.
  23. |
  24. | This launcher is distributed in the hope that it will be useful,
  25. | but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  27. | Lesser General Public License for more details.
  28. |
  29. | You should have received a copy of the GNU Lesser General Public
  30. | License along with this library; if not, write to the Free Software
  31. | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  32. .ifdef lzma
  33. .include "pst-lzma.h"
  34. .endif
  35. .ifdef ttunpack
  36. .include "pst-ttup.h"
  37. .endif
  38. .ifdef ttunpack_fast
  39. .include "pst-ttuf.h"
  40. .endif
  41. .ifdef shrink92
  42. .include "pst-shrn.h"
  43. .endif
  44. .xdef _ti89
  45. .xdef _ti92plus
  46. .xdef _v200
  47. .xdef _ti89ti
  48. .xdef _tigcc_native
  49. .section _st1
  50. | INITIALIZATION
  51. movem.l %d3-%d7/%a2-%a5,-(%sp)
  52. move.l 0xC8.w,%a5
  53. | Check if this is a TI-89 Titanium.
  54. move.l %a5,%d0
  55. | Size optimization: the highest bit (#31) determines the sign (N bit in ccr).
  56. | 23+8 = 31 => we can use rol #8 (2 bytes) instead of btst # (4 bytes).
  57. rol.l #8,%d0 | btst.l #23,%d0
  58. smi.b %d7 | sne.b %d7
  59. | First, look for the current address.
  60. lea.l (%pc),%a1
  61. move.l %a1,%d4
  62. | This is to prevent problems with Kevin's HW2 TSR support.
  63. and.l #0x3FFFF,%d4
  64. | Set the bit instead of clearing it.
  65. moveq.l #1,%d3
  66. | Call function.
  67. jbsr __set_clear_in_use_bit__
  68. | On failure, try using the return address of the program, to do the same for
  69. | a possible launcher.
  70. tst.w %d0
  71. jbne __in_use_done__
  72. | Get the return address.
  73. move.l 32(%sp),%d4
  74. | Check if it is in RAM range.
  75. cmp.l #0x1FFFFF,%d4
  76. | If not, it's not the launcher. Now we don't know who started us.
  77. jbhi __in_use_done__
  78. | This is to prevent problems with Kevin's HW2 TSR support.
  79. and.l #0x3FFFF,%d4
  80. | Call function.
  81. jbsr __set_clear_in_use_bit__
  82. __in_use_done__:
  83. | Save variables for reentrancy
  84. move.l ercatcherpc(%pc),-(%a7)
  85. lea.l DecompressedHandle(%pc),%a0
  86. move.l (%a0),-(%a7)
  87. | Initialize the handles to 0
  88. clr.l (%a0)
  89. __error_return_support:
  90. | Get 60 bytes on the stack (size of an error frame).
  91. lea (%sp,-60),%sp
  92. | Push a pointer to these 60 bytes.
  93. pea.l (%sp)
  94. | Call ER_catch
  95. move.l (%a5,0x154*4),%a0 /* ER_catch */
  96. jsr (%a0)
  97. | Now, this is a little complicated:
  98. | If an error is thrown later, a second return from ER_catch with an error
  99. | in %d0 is simulated.
  100. | So, if %d0 contains 0, then we resume normally, otherwise we go to the
  101. | cleanup block, but remember the error.
  102. | Store the error in a variable.
  103. lea.l __error_num__(%pc),%a0
  104. move.w %d0,(%a0)
  105. jbne __error_returned__
  106. | FIND FILE TO DECOMPRESS
  107. | Convert program name to SYM_STR
  108. lea.l ProgramName(%pc),%a0
  109. symstr_loop:
  110. tst.b (%a0)+
  111. jbne symstr_loop
  112. | SymFindPtr(SYMSTR(ProgramName),0)
  113. clr.w -(%a7)
  114. pea.l -1(%a0)
  115. move.l (%a5,0x283*4),%a0 /* SymFindPtr */
  116. jsr (%a0)
  117. | File found?
  118. move.l %a0,%d0
  119. jbne file_found
  120. file_not_found:
  121. .word 0xA000+850 /* ER_PRGM_NOT_FOUND */
  122. file_found:
  123. | Get handle
  124. lea.l CompressedHandle(%pc),%a2
  125. move.w 12(%a0),%d6
  126. jbeq file_not_found
  127. | Check if locked
  128. move.w %d6,(%a7)
  129. move.l (%a5,0x9B*4),%a0 /* HeapGetLock */
  130. jsr (%a0)
  131. tst.w %d0
  132. jbne already_locked
  133. | Save handle for unlocking if not already locked
  134. move.w %d6,(%a2)
  135. already_locked:
  136. | Lock handle and get pointer to compressed data
  137. move.w %d6,(%a7)
  138. move.l (%a5,0x99*4),%a0 /* HLock */
  139. jsr (%a0)
  140. move.l %a0,%a3
  141. addq.l #6,%a7
  142. | DECOMPRESS FILE
  143. | Check if the archive is valid and compute the uncompressed size
  144. GET_UNCOMPRESSED_SIZE
  145. | Allocate the memory needed to decompress the program
  146. | NOTE: We can't use HeapAllocPtr here because of kernel-based programs.
  147. move.l %d6,-(%a7)
  148. | Add the stub bytes for the Titanium
  149. tst.b %d7
  150. jbeq no_add_bytes
  151. add.l #102,(%a7)
  152. no_add_bytes:
  153. move.l (%a5,0x93*4),%a0 /* HeapAllocThrow */
  154. jsr (%a0)
  155. move.w %d0,(%a7)
  156. move.w %d0,-(%a2) | save handle for freeing
  157. move.l (%a5,0x99*4),%a0 /* HLock */
  158. jsr (%a0)
  159. addq.l #4,%a7
  160. | Decompress the archive
  161. MEM_TO_MEM_DECOMPRESS
  162. | LAUNCH DECOMPRESSED PROGRAM
  163. | NOTE: Some of this code is the same as in ttstart. This is
  164. | because this is code _I_ contributed to ttstart. So
  165. | it is being relicensed by the original author.
  166. | If we're not on a Titanium, switch the execution pointer to
  167. | the ghost space.
  168. tst.b %d7
  169. jbne dont_use_ghost_space
  170. moveq.l #4,%d1
  171. swap %d1
  172. adda.l %d1,%a4
  173. | Now enter the ghost space.
  174. jbsr own_enter_ghost_space
  175. | Set the ERROR_FRAME PC to the ghost space.
  176. ori.w #4,56(%a7)
  177. dont_use_ghost_space:
  178. | Skip the size bytes and read the actual size (upper word of d6 is 0).
  179. move.w (%a4)+,%d6
  180. | Relocate the program.
  181. pea.l -1(%a4,%d6.l) | tag_ptr
  182. pea.l (%a4) | base_addr
  183. move.l (%a5,0x15a*4),%a0 /* EX_patch */
  184. jsr (%a0)
  185. addq.l #8,%a7
  186. | The following code was compiled and hand-edited from:
  187. | /* If we are on AMS 2, we have to set the "last executed program" to somewhere
  188. | in the last 4 KB of RAM, or else APD may crash under certain circumstances.
  189. | The code below looks for the "last executed program" variable. That variable
  190. | is cleared during initialization, immediately after the stack fence is set up.
  191. | So we look for the value of the stack fence (0xDEADDEAD) in the initialization
  192. | code and add 8 to get the wanted short pointer, which must then be
  193. | sign-extended to an actual pointer. (The sign extension is implicit in the
  194. | generated code, as it should be.) */
  195. | if (!(AMS_1xx||*(short*)0x32==('R'<<8)+'O')) {
  196. | /* rb and q are factored out in order to get more efficient code. */
  197. | char *rb=ROM_base;
  198. | char *p=rb+0x12000;
  199. | char *q=rb+0x18000;
  200. |
  201. | while (p<q && *(unsigned long*)p!=0xDEADDEAD) p+=2;
  202. | p+=2[(short *)p]?8:12;
  203. | *(void **)(long)*(short *)p=isTitanium?(dest+2):(void*)0x3f000;
  204. | }
  205. | Then, it was optimized by Lionel Debroux. A consequence is that the assembly
  206. | code does not look like the C code above at all.
  207. cmp.l #1000,-4(%a5)
  208. jbcs .Llastexec1
  209. cmp.w #0x524F,0x32.w
  210. jbeq .Llastexec1
  211. move.l %a5,%d0
  212. andi.l #0xE00000,%d0
  213. add.l #0x12000,%d0
  214. move.l %d0,%a0
  215. move.w #((0x18000-0x12000)/2)-1,%d0 | Set up iteration counter
  216. 0:
  217. cmpi.l #0xDEADDEAD,(%a0) | Look for the stack fence
  218. addq.l #2,%a0 | Increment pointer, condition codes unchanged
  219. dbeq %d0,0b
  220. tst.w 2(%a0)
  221. bne.s 3f
  222. addq.l #4,%a0
  223. 3:
  224. move.w 6(%a0),%a0
  225. tst.b %d7
  226. jbne use_dest_pointer
  227. move.l #0x3F000,(%a0)
  228. jbra .Llastexec1
  229. use_dest_pointer:
  230. move.l %a4,(%a0)
  231. .Llastexec1:
  232. | Now actually run the program.
  233. movem.l %d1-%d7/%a0-%a6,-(%sp)
  234. tst.b %d7
  235. jbeq not_titanium
  236. | Round the length up to an even size
  237. | NOTE: The handle size is rounded up automatically.
  238. addq.l #1,%d6
  239. moveq.l #0xfffffffe,%d0
  240. and.l %d0,%d6
  241. | Save error catcher PC
  242. lea.l ercatcherpc(%pc),%a0
  243. move.l 112(%sp),(%a0)
  244. | Set new error catcher PC
  245. lea.l 0(%a4,%d6.l),%a0
  246. lea.l (ercatcher-middle)(%a0),%a1
  247. move.l %a1,112(%sp)
  248. | Append the stub to the program
  249. lea.l middle(%pc),%a1
  250. moveq.l #50,%d0
  251. stub_copy_loop:
  252. move.w (%a1)+,(%a0)+
  253. dbra %d0,stub_copy_loop
  254. | Unprotect and run the program
  255. pea target(%PC)
  256. movea.l %a4,%a3
  257. pea (%a3,%d6.l)
  258. jbra unprotect_a3_jumpto_a4
  259. target:
  260. | Clean up
  261. lea (%sp,36),%sp
  262. move.l (%sp)+,%d0
  263. bra.s 0f
  264. not_titanium:
  265. | Run the program in the ghost space
  266. jsr (%a4)
  267. | Handle RETURN_VALUE
  268. st.b %d0
  269. bra.s 0f
  270. clr.b %d0
  271. 0: movem.l (%sp)+,%d1-%d7/%a0-%a6
  272. | If %d0==0, we have to fix the return address.
  273. tst.b %d0
  274. jbne no_return_value
  275. lea.l 108(%sp),%a1
  276. move.l (%a1),%a0
  277. cmpi.w #0x21EE,(%a0)
  278. jbne .L__no_add_2
  279. addq.l #2,(%a1)
  280. .L__no_add_2:
  281. addq.l #4,(%a1)
  282. no_return_value:
  283. | ERROR HANDLING
  284. __error_return_support_cleanup:
  285. | We have terminated successfully, so remove the error frame.
  286. move.l (%a5,0x155*4),%a0 /* ER_success */
  287. jsr (%a0)
  288. | This is the place where we should go if an error happened.
  289. | %d0 is not 0 in this case.
  290. __error_returned__:
  291. | Remove the error frame and the parameter from the stack.
  292. lea (%sp,64),%sp
  293. jbsr __set_clear_in_use_bit__
  294. | PSTARTER CLEANUPS
  295. | Free decompressed program handle, if any.
  296. pea.l DecompressedHandle(%pc)
  297. move.l (%a5,0x98*4),%a0 /* HeapFreeIndir */
  298. jsr (%a0)
  299. | Unlock compressed program handle, if it wasn't already locked
  300. move.w CompressedHandle(%pc),(%a7)
  301. jbeq no_unlock
  302. move.l (%a5,0x9f*4),%a0 /* HeapUnlock */
  303. jsr (%a0)
  304. no_unlock:
  305. addq.l #4,%a7
  306. | Restore variables for reentrancy
  307. lea.l ercatcherpc(%pc),%a0
  308. move.l (%a7)+,DecompressedHandle-ercatcherpc(%a0)
  309. move.l (%a7)+,(%a0)
  310. | GENERIC CLEANUPS
  311. | If we had any cleaning up to do, it absolutely MUST be finished at this
  312. | point. That is, we have to be able to return from here under any given
  313. | circumstances.
  314. | Go to __no_error__ if no error happened.
  315. lea.l __error_num__(%pc),%a0
  316. tst.w (%a0)
  317. beq.s __no_error__
  318. | Construct an A-Line value.
  319. or.w #0xA000,(%a0)
  320. __error_return_support_ams_1:
  321. | In AMS 1.xx, we need to unlock our own handle.
  322. | Otherwise, the program cannot be started again.
  323. cmp.l #1000,(%a5,-4) /* TIOS_entries */
  324. jbcc __ams_1_err_handling_not_needed__
  325. | Get the starting address of the program and push it on the stack.
  326. pea.l __ld_entry_point-2(%pc)
  327. | Convert it to a handle.
  328. move.l (%a5,0x23A*4),%a0 /* HeapPtrToHandle */
  329. jsr (%a0)
  330. | The handle is now in %d0. It is 0 on failure.
  331. move.w %d0,(%sp)
  332. jbeq __ams_1_err_handling_not_needed__
  333. | Unlock it.
  334. move.l (%a5,0x9F*4),%a0 /* HeapUnlock */
  335. jsr (%a0)
  336. | No need to clean up the stack, since we are going to throw an error anyway.
  337. __ams_1_err_handling_not_needed__:
  338. | This is where the A-Line error throwing is constructed.
  339. __error_num__:
  340. .word 0
  341. __no_error__:
  342. clr.w (%a0)
  343. movem.l (%sp)+,%d3-%d7/%a2-%a5
  344. rts
  345. | IN-USE BIT SUPPORT
  346. | This function relies on the address of the jump table being in %a5.
  347. | %d4 is an arbitrary address somewhere in our program (or the launcher,
  348. | respectively).
  349. | If %d3.w is 0, clear the bit, otherwise set it. If action is to set the bit,
  350. | the previous value is stored in %d3.
  351. | Register %d5 is destroyed.
  352. __set_clear_in_use_bit__:
  353. | Call SymFindFirst with appropriate parameters to look for the handle.
  354. move.w #0x06,-(%sp) /* FO_RECURSE | FO_SKIP_TEMPS */
  355. clr.l -(%sp)
  356. move.l (%a5,0x6C*4),%a0 /* SymFindFirst */
  357. jbsr (%a0)
  358. addq.l #6,%sp
  359. __symbol_search_loop__:
  360. | Store failure value in %d0
  361. clr.w %d0
  362. | If the SYM_ENTRY pointer is 0, quit.
  363. move.l %a0,%d5
  364. jbeq __symbol_search_done__
  365. | Dereference the handle from the SYM_ENTRY structure.
  366. move.w (%a0,12),-(%sp)
  367. move.l (%a5,0x96*4),%a0 /* HeapDeref */
  368. jbsr (%a0)
  369. addq.l #2,%sp
  370. | If the address returned is higher than the address we are looking for, the
  371. | symbol can't be the one.
  372. | Actually, if it is equal, then something is seriously broken, but we don't
  373. | check for that here.
  374. cmp.l %d4,%a0
  375. jbhi __skip_symbol__
  376. | Add the size of the variable to %a0.
  377. | Note that we would actually need to add 2 more bytes to skip the size
  378. | field, but we make up for that by using a <= comparison.
  379. moveq #0,%d0
  380. move.w (%a0),%d0
  381. add.l %d0,%a0
  382. | If the end address is lower than the address we are looking for, the
  383. | symbol can't be the one.
  384. cmp.l %d4,%a0
  385. jbls __skip_symbol__
  386. | Store success result in %d0.
  387. moveq #1,%d0
  388. | If %d3.w is 0, clear the in-use bit.
  389. move.l %d5,%a0
  390. lea 11(%a0),%a0
  391. tst.w %d3
  392. jbeq __clear_bit__
  393. | Otherwise, store the previous value in %d3, set the bit, and quit.
  394. moveq #0x10,%d1
  395. move.b (%a0),%d3
  396. and.w %d1,%d3
  397. or.b %d1,(%a0)
  398. rts
  399. __skip_symbol__:
  400. | Call SymFindNext.
  401. move.l (%a5,0x6D*4),%a0 /* SymFindNext */
  402. jbsr (%a0)
  403. | Go to beginning of loop.
  404. jra __symbol_search_loop__
  405. __clear_bit__:
  406. and.b #0xEF,(%a0)
  407. __symbol_search_done__:
  408. rts
  409. | GHOST SPACE ENTERING ROUTINE
  410. | WARNING: This routine wants #0x40000 in %d1.l.
  411. own_enter_ghost_space:
  412. move.l (%sp),%d0
  413. cmp.l %d1,%d0
  414. jbcc 2f
  415. cmpi.l #1000,(%a5,-4)
  416. jbcs 2f
  417. movem.l %a2-%a6/%d3-%d7,-(%sp)
  418. lea.l 0x3E000,%a3
  419. lea.l 1f(%pc,%d1.l),%a4
  420. jbra unprotect_a3_jumpto_a4
  421. 1:
  422. lea (%sp,20),%sp
  423. movem.l (%sp)+,%a2-%a6/%d3-%d7
  424. 2:
  425. move.l (%sp)+,%d0
  426. bset.l #18,%d0
  427. movea.l %d0,%a0
  428. jmp (%a0)
  429. | CODE EXECUTED AT END OF PROGRAM
  430. | NOTE: This is the same code as in ttstart, because the code in ttstart
  431. | is actually MY code.
  432. middle:
  433. | Handle RETURN_VALUE
  434. st.b %d0
  435. bra.s 0f
  436. clr.b %d0
  437. 0:
  438. | Unprotect and jump to the launcher
  439. movea.l (%sp)+,%a4
  440. lea __ld_entry_point-target(%a4),%a3
  441. move.l %d0,16(%sp)
  442. | Unprotection routine
  443. unprotect_a3_jumpto_a4:
  444. lea (%sp,-20),%sp
  445. move.l %a5,%d0
  446. andi.l #0xE00000,%d0
  447. addi.l #0x20000,%d0
  448. move.l %d0,(%sp,12)
  449. move.l %d0,(%sp,16)
  450. move.l 20(%sp),(%sp)
  451. move.l 24(%sp),4(%sp)
  452. trap #0xC
  453. move.w #0x2700,%sr
  454. moveq #0xF,%d3
  455. pea (%a4)
  456. clr.w -(%sp)
  457. move.l 0xAC.w,%a0
  458. jmp (%a0)
  459. ercatcherentrypoint: .long __ld_entry_point
  460. ercatcherpc: .long 0
  461. .even
  462. ercatcher:
  463. movem.l %d0-%d7/%a0-%a6,-(%sp)
  464. move.l ercatcherentrypoint(%PC),%a3
  465. lea.l ercatchercleanup-__ld_entry_point(%a3),%a4
  466. jbra unprotect_a3_jumpto_a4
  467. | LAUNCHING CLEANUP CODE EXECUTED IN THE LAUNCHER'S EXECUTION SPACE
  468. ercatchercleanup:
  469. lea (%sp,20),%sp
  470. movem.l (%sp)+,%d0-%d7/%a0-%a6
  471. move.l ercatcherpc(%PC),-(%a7)
  472. rts
  473. | DATA
  474. .data
  475. .byte 0
  476. ProgramName: | program name
  477. .asciz "tempprog" | to be patched in by TIGCC
  478. DecompressedHandle: .word 0 | handle to free, 0 if no freeing needed
  479. CompressedHandle: .word 0 | handle to unlock, 0 if no unlocking needed