pstarter.s 14 KB

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