PageRenderTime 30ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/cc65-2.13.2/libsrc/cbm510/crt0.s

https://github.com/gilligan/snesdev
Assembly | 585 lines | 347 code | 122 blank | 116 comment | 0 complexity | 6ec9ae9e245b4358023d37fd06302432 MD5 | raw file
  1. ;
  2. ; Startup code for cc65 (CBM 500 version)
  3. ;
  4. .export _exit
  5. .export __STARTUP__ : absolute = 1 ; Mark as startup
  6. .import _clrscr, initlib, donelib, callirq_y
  7. .import push0, callmain
  8. .import __CHARRAM_START__, __CHARRAM_SIZE__, __VIDRAM_START__
  9. .import __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
  10. .import __INTERRUPTOR_COUNT__
  11. .import scnkey, UDTIM
  12. .include "zeropage.inc"
  13. .include "extzp.inc"
  14. .include "cbm510.inc"
  15. ; ------------------------------------------------------------------------
  16. ; BASIC header and a small BASIC program. Since it is not possible to start
  17. ; programs in other banks using SYS, the BASIC program will write a small
  18. ; machine code program into memory at $100 and start that machine code
  19. ; program. The machine code program will then start the machine language
  20. ; code in bank 0, which will initialize the system by copying stuff from
  21. ; the system bank, and start the application.
  22. ;
  23. ; Here's the basic program that's in the following lines:
  24. ;
  25. ; 10 for i=0 to 4
  26. ; 20 read j
  27. ; 30 poke 256+i,j
  28. ; 40 next i
  29. ; 50 sys 256
  30. ; 60 data 120,169,0,133,0
  31. ;
  32. ; The machine program in the data lines is:
  33. ;
  34. ; sei
  35. ; lda #$00
  36. ; sta $00 <-- Switch to bank 0 after this command
  37. ;
  38. ; Initialization is not only complex because of the jumping from one bank
  39. ; into another. but also because we want to save memory, and because of
  40. ; this, we will use the system memory ($00-$3FF) for initialization stuff
  41. ; that is overwritten later.
  42. ;
  43. .segment "BASICHDR"
  44. .byte $03,$00,$11,$00,$0a,$00,$81,$20,$49,$b2,$30,$20,$a4,$20,$34,$00
  45. .byte $19,$00,$14,$00,$87,$20,$4a,$00,$27,$00,$1e,$00,$97,$20,$32,$35
  46. .byte $36,$aa,$49,$2c,$4a,$00,$2f,$00,$28,$00,$82,$20,$49,$00,$39,$00
  47. .byte $32,$00,$9e,$20,$32,$35,$36,$00,$4f,$00,$3c,$00,$83,$20,$31,$32
  48. .byte $30,$2c,$31,$36,$39,$2c,$30,$2c,$31,$33,$33,$2c,$30,$00,$00,$00
  49. ;------------------------------------------------------------------------------
  50. ; A table that contains values that must be transfered from the system zero
  51. ; page into out zero page. Contains pairs of bytes, first one is the address
  52. ; in the system ZP, second one is our ZP address. The table goes into page 2,
  53. ; but is declared here, because it is needed earlier.
  54. .SEGMENT "PAGE2"
  55. .proc transfer_table
  56. .byte $9F, DEVNUM
  57. .byte $CA, CURS_Y
  58. .byte $CB, CURS_X
  59. .byte $EC, CHARCOLOR
  60. .endproc
  61. ;------------------------------------------------------------------------------
  62. ; Page 3 data. This page contains the break vector and the bankswitch
  63. ; subroutine that is copied into high memory on startup. The space occupied by
  64. ; this routine will later be used for a copy of the bank 15 stack. It must be
  65. ; saved, since we're going to destroy it when calling bank 15.
  66. .segment "PAGE3"
  67. BRKVec: .addr _exit ; BRK indirect vector
  68. .proc callbank15
  69. excrts = $FEFE
  70. .org $FEC3
  71. entry: php
  72. pha
  73. lda #$0F ; Bank 15
  74. sta IndReg
  75. txa
  76. pha
  77. tya
  78. pha
  79. sei
  80. ldy #$FF
  81. lda (sysp1),y
  82. tay
  83. lda ExecReg
  84. sta (sysp1),y
  85. dey
  86. lda #.hibyte(excrts-1)
  87. sta (sysp1),y
  88. dey
  89. lda #.lobyte(excrts-1)
  90. sta (sysp1),y
  91. tya
  92. sec
  93. sbc #7
  94. sta $1FF ; Save new sp
  95. tay
  96. tsx
  97. pla
  98. iny
  99. sta (sysp1),y
  100. pla
  101. iny
  102. sta (sysp1),y
  103. pla
  104. iny
  105. sta (sysp1),y
  106. pla
  107. iny
  108. sta (sysp1),y
  109. lda $105,x
  110. sec
  111. sbc #3
  112. iny
  113. sta (sysp1),y
  114. lda $106,x
  115. sbc #0
  116. iny
  117. sta (sysp1),y
  118. ldy $1FF ; Restore sp in bank 15
  119. lda #.hibyte(expull-1)
  120. sta (sysp1),y
  121. dey
  122. lda #.lobyte(expull-1)
  123. sta (sysp1),y
  124. dey
  125. pla
  126. pla
  127. tsx
  128. stx $1FF
  129. tya
  130. tax
  131. txs
  132. lda IndReg
  133. jmp $FFF6
  134. expull: pla
  135. tay
  136. pla
  137. tax
  138. pla
  139. plp
  140. rts
  141. .if (expull <> $FF26)
  142. .error "Symbol expull must be aligned with kernal in bank 15"
  143. .endif
  144. .reloc
  145. .endproc
  146. ;------------------------------------------------------------------------------
  147. ; The code in the target bank when switching back will be put at the bottom
  148. ; of the stack. We will jump here to switch segments. The range $F2..$FF is
  149. ; not used by any kernal routine.
  150. .segment "STARTUP"
  151. Back: sta ExecReg
  152. ; We are at $100 now. The following snippet is a copy of the code that is poked
  153. ; in the system bank memory by the basic header program, it's only for
  154. ; documentation and not actually used here:
  155. sei
  156. lda #$00
  157. sta ExecReg
  158. ; This is the actual starting point of our code after switching banks for
  159. ; startup. Beware: The following code will get overwritten as soon as we
  160. ; use the stack (since it's in page 1)! We jump to another location, since
  161. ; we need some space for subroutines that aren't used later.
  162. jmp Origin
  163. ; Hardware vectors, copied to $FFFA
  164. .proc vectors
  165. sta ExecReg
  166. rts
  167. nop
  168. .word nmi ; NMI vector
  169. .word 0 ; Reset - not used
  170. .word irq ; IRQ vector
  171. .endproc
  172. ; Initializers for the extended zeropage. See extzp.s
  173. .proc extzp
  174. .word $0100 ; sysp1
  175. .word $0300 ; sysp3
  176. .word $d800 ; vic
  177. .word $da00 ; sid
  178. .word $db00 ; cia1
  179. .word $dc00 ; cia2
  180. .word $dd00 ; acia
  181. .word $de00 ; tpi1
  182. .word $df00 ; tpi2
  183. .word $eab1 ; ktab1
  184. .word $eb11 ; ktab2
  185. .word $eb71 ; ktab3
  186. .word $ebd1 ; ktab4
  187. .endproc
  188. ; Switch the indirect segment to the system bank
  189. Origin: lda #$0F
  190. sta IndReg
  191. ; Initialize the extended zeropage
  192. ldx #.sizeof(extzp)-1
  193. L1: lda extzp,x
  194. sta <__EXTZP_RUN__,x
  195. dex
  196. bpl L1
  197. ; Save the old stack pointer from the system bank and setup our hw sp
  198. tsx
  199. txa
  200. ldy #$FF
  201. sta (sysp1),y ; Save system stack point into $F:$1FF
  202. ldx #$FE ; Leave $1FF untouched for cross bank calls
  203. txs ; Set up our own stack
  204. ; Copy stuff from the system zeropage to ours
  205. lda #.sizeof(transfer_table)
  206. sta ktmp
  207. L2: ldx ktmp
  208. ldy transfer_table-2,x
  209. lda transfer_table-1,x
  210. tax
  211. lda (sysp0),y
  212. sta $00,x
  213. dec ktmp
  214. dec ktmp
  215. bne L2
  216. ; Set the interrupt, NMI and other vectors
  217. ldx #.sizeof(vectors)-1
  218. L3: lda vectors,x
  219. sta $10000 - .sizeof(vectors),x
  220. dex
  221. bpl L3
  222. ; Setup the C stack
  223. lda #.lobyte(callbank15::entry)
  224. sta sp
  225. lda #.hibyte(callbank15::entry)
  226. sta sp+1
  227. ; Setup the subroutine and jump vector table that redirects kernal calls to
  228. ; the system bank.
  229. ldy #.sizeof(callbank15)
  230. @L1: lda callbank15-1,y
  231. sta callbank15::entry-1,y
  232. dey
  233. bne @L1
  234. ; Setup the jump vector table. Y is zero on entry.
  235. ldx #45-1 ; Number of vectors
  236. @L2: lda #$20 ; JSR opcode
  237. sta $FF6F,y
  238. iny
  239. lda #.lobyte(callbank15::entry)
  240. sta $FF6F,y
  241. iny
  242. lda #.hibyte(callbank15::entry)
  243. sta $FF6F,y
  244. iny
  245. dex
  246. bpl @L2
  247. ; Set the indirect segment to bank we're executing in
  248. lda ExecReg
  249. sta IndReg
  250. ; Zero the BSS segment. We will do that here instead calling the routine
  251. ; in the common library, since we have the memory anyway, and this way,
  252. ; it's reused later.
  253. lda #<__BSS_RUN__
  254. sta ptr1
  255. lda #>__BSS_RUN__
  256. sta ptr1+1
  257. lda #0
  258. tay
  259. ; Clear full pages
  260. ldx #>__BSS_SIZE__
  261. beq Z2
  262. Z1: sta (ptr1),y
  263. iny
  264. bne Z1
  265. inc ptr1+1 ; Next page
  266. dex
  267. bne Z1
  268. ; Clear the remaining page
  269. Z2: ldx #<__BSS_SIZE__
  270. beq Z4
  271. Z3: sta (ptr1),y
  272. iny
  273. dex
  274. bne Z3
  275. Z4: jmp Init
  276. ; ------------------------------------------------------------------------
  277. ; We are at $200 now. We may now start calling subroutines safely, since
  278. ; the code we execute is no longer in the stack page.
  279. .segment "PAGE2"
  280. ; Copy the character rom from the system bank into the execution bank
  281. Init: lda #<$C000
  282. sta ptr1
  283. lda #>$C000
  284. sta ptr1+1
  285. lda #<__CHARRAM_START__
  286. sta ptr2
  287. lda #>__CHARRAM_START__
  288. sta ptr2+1
  289. lda #>__CHARRAM_SIZE__ ; 16 * 256 bytes to copy
  290. sta tmp1
  291. ldy #$00
  292. ccopy: lda #$0F
  293. sta IndReg ; Access the system bank
  294. ccopy1: lda (ptr1),y
  295. sta __VIDRAM_START__,y
  296. iny
  297. bne ccopy1
  298. lda ExecReg
  299. sta IndReg
  300. ccopy2: lda __VIDRAM_START__,y
  301. sta (ptr2),y
  302. iny
  303. bne ccopy2
  304. inc ptr1+1
  305. inc ptr2+1 ; Bump high pointer bytes
  306. dec tmp1
  307. bne ccopy
  308. ; Clear the video memory. We will do this before switching the video to bank 0
  309. ; to avoid garbage when doing so.
  310. jsr _clrscr
  311. ; Reprogram the VIC so that the text screen and the character ROM is in the
  312. ; execution bank. This is done in three steps:
  313. lda #$0F ; We need access to the system bank
  314. sta IndReg
  315. ; Place the VIC video RAM into bank 0
  316. ; CA (STATVID) = 0
  317. ; CB (VICDOTSEL) = 0
  318. ldy #TPI::CR
  319. lda (tpi1),y
  320. sta vidsave+0
  321. and #%00001111
  322. ora #%10100000
  323. sta (tpi1),y
  324. ; Set bit 14/15 of the VIC address range to the high bits of __VIDRAM_START__
  325. ; PC6/PC7 (VICBANKSEL 0/1) = 11
  326. ldy #TPI::PRC
  327. lda (tpi2),y
  328. sta vidsave+1
  329. and #$3F
  330. ora #<((>__VIDRAM_START__) & $C0)
  331. sta (tpi2),y
  332. ; Set the VIC base address register to the addresses of the video and
  333. ; character RAM.
  334. ldy #VIC_VIDEO_ADR
  335. lda (vic),y
  336. sta vidsave+2
  337. and #$01
  338. ora #<(((__VIDRAM_START__ >> 6) & $F0) | ((__CHARRAM_START__ >> 10) & $0E) | $02)
  339. ; and #$0F
  340. ; ora #<(((>__VIDRAM_START__) << 2) & $F0)
  341. sta (vic),y
  342. ; Switch back to the execution bank
  343. lda ExecReg
  344. sta IndReg
  345. ; Activate chained interrupt handlers, then enable interrupts.
  346. lda #.lobyte(__INTERRUPTOR_COUNT__*2)
  347. sta irqcount
  348. cli
  349. ; Call module constructors.
  350. jsr initlib
  351. ; Push arguments and call main()
  352. jsr callmain
  353. ; Call module destructors. This is also the _exit entry and the default entry
  354. ; point for the break vector.
  355. _exit: pha ; Save the return code on stack
  356. jsr donelib ; Run module destructors
  357. lda #$00
  358. sta irqcount ; Disable custom irq handlers
  359. ; Address the system bank
  360. lda #$0F
  361. sta IndReg
  362. ; Switch back the video to the system bank
  363. ldy #TPI::CR
  364. lda vidsave+0
  365. sta (tpi1),y
  366. ldy #TPI::PRC
  367. lda vidsave+1
  368. sta (tpi2),y
  369. ldy #VIC_VIDEO_ADR
  370. lda vidsave+2
  371. sta (vic),y
  372. ; Copy stuff back from our zeropage to the systems
  373. .if 0
  374. lda #.sizeof(transfer_table)
  375. sta ktmp
  376. @L0: ldx ktmp
  377. ldy transfer_table-2,x
  378. lda transfer_table-1,x
  379. tax
  380. lda $00,x
  381. sta (sysp0),y
  382. dec ktmp
  383. dec ktmp
  384. bne @L0
  385. .endif
  386. ; Place the program return code into ST
  387. pla
  388. ldy #$9C ; ST
  389. sta (sysp0),y
  390. ; Setup the welcome code at the stack bottom in the system bank.
  391. ldy #$FF
  392. lda (sysp1),y ; Load system bank sp
  393. tax
  394. iny ; Y = 0
  395. lda #$58 ; CLI opcode
  396. sta (sysp1),y
  397. iny
  398. lda #$60 ; RTS opcode
  399. sta (sysp1),y
  400. lda IndReg
  401. sei
  402. txs
  403. jmp Back
  404. ; -------------------------------------------------------------------------
  405. ; The IRQ handler goes into PAGE2. For performance reasons, and to allow
  406. ; easier chaining, we do handle the IRQs in the execution bank (instead of
  407. ; passing them to the system bank).
  408. ; This is the mapping of the active irq register of the 6525 (tpi1):
  409. ;
  410. ; Bit 7 6 5 4 3 2 1 0
  411. ; | | | | ^ 50 Hz
  412. ; | | | ^ SRQ IEEE 488
  413. ; | | ^ cia
  414. ; | ^ IRQB ext. Port
  415. ; ^ acia
  416. irq: pha
  417. txa
  418. pha
  419. tya
  420. pha
  421. lda IndReg
  422. pha
  423. lda ExecReg
  424. sta IndReg ; Be sure to address our segment
  425. tsx
  426. lda $105,x ; Get the flags from the stack
  427. and #$10 ; Test break flag
  428. bne dobrk
  429. ; It's an IRQ
  430. cld
  431. ; Call chained IRQ handlers
  432. ldy irqcount
  433. beq irqskip
  434. jsr callirq_y ; Call the functions
  435. ; Done with chained IRQ handlers, check the TPI for IRQs and handle them
  436. irqskip:lda #$0F
  437. sta IndReg
  438. ldy #TPI::AIR
  439. lda (tpi1),y ; Interrupt Register 6525
  440. beq noirq
  441. ; 50/60Hz interrupt
  442. cmp #%00000001 ; ticker irq?
  443. bne irqend
  444. jsr scnkey ; Poll the keyboard
  445. jsr UDTIM ; Bump the time
  446. ; Done
  447. irqend: ldy #TPI::AIR
  448. sta (tpi1),y ; Clear interrupt
  449. noirq: pla
  450. sta IndReg
  451. pla
  452. tay
  453. pla
  454. tax
  455. pla
  456. nmi: rti
  457. dobrk: jmp (BRKVec)
  458. ; -------------------------------------------------------------------------
  459. ; Data area
  460. .data
  461. vidsave:.res 3
  462. .bss
  463. irqcount: .byte 0