; Tool for controlling tf_hh's SRAM upgrade (512K v4.5, or 576K v2 for 600XL). ; Gets built with various defines, see README.txt. Non-coldstart ; builds can be prepended to another .xex file to set the upgrade's ; mode automatically when that .xex is loaded. ; Reverse-engineered by disassembling setmem.xex. ; Note that the upgrade's mode can *only* be changed while the ; Start key is held down, so this can't be 100% non-interactive. .include "atari.inc" .include "xex.inc" .include "ver.s" .ifndef magic_value .error "You must define magic_value to 0, 1, or 2." .endif .define rambo_msg "for 512K/RAMBO mode" .define compy_msg "for 256K/Compy mode" .define disable_msg "to disable memory upgrade" .if magic_value = 0 .ifdef reverse_logic .define mode_msg compy_msg .define xex_name "U256R" .else .define mode_msg rambo_msg .define xex_name "U512" .endif .else .if magic_value = 1 .define mode_msg disable_msg .define xex_name "UOFF" .else .if magic_value = 2 .ifdef reverse_logic .define mode_msg rambo_msg .define xex_name "U512R" .else .define mode_msg compy_msg .define xex_name "U256" .endif .else .error .sprintf("Invalid magic_value (must be 0, 1, or 2, not %d)", magic_value) .endif .endif .endif .out .sprintf("Using magic_value %d, message '%s'", magic_value, mode_msg) loadaddr = $8000 magic_register = $d3f3 xex_org loadaddr ; runaddr is fake (just an RTS), put here so the tool can be run ; standalone under "smart" DOSes like SDX that want to jump to the ; load address. runaddr: rts magic_bits: .byte magic_value prompt_msg: .byte xex_name .ifdef coldstart .byte "X" .endif .byte " v", VERSION, " by Urchlay", $9b, $9b, "Press Start ", mode_msg .ifdef coldstart .byte $9b, "The Atari will reboot!", $9b .byte "Press any other key to abort.", $9b .endif .byte 0 ;;; subroutines printchr: ; print character in A register. tay ; save A (character to print). lda ICPTH ; set up stack, so it looks like a JSR to the pha ; put-one-byte address for E:, lda ICPTL ; which the OS has conveniently stashed pha ; in IOCB #0. tya ; restore A (put-one-byte argument). rts ; "return" to put-one-byte, which will return to printchr's caller. printmsg: ; print message pointed to by A/X sta FR0 stx FR0+1 lda #0 sta FR0+2 pmloop: ldy FR0+2 lda (FR0),y beq pmdone jsr printchr inc FR0+2 bne pmloop pmdone: rts ; wait for CONSOL to equal A waitconsol: cmp CONSOL bne waitconsol ; a bit of debouncing... sta WSYNC sta WSYNC sta WSYNC sta WSYNC cmp CONSOL bne waitconsol rts ;;; main program entrypoint: ; print our prompt lda #prompt_msg jsr printmsg .if 0 ; no idea why setmem.xex clicks the console speaker... lda #8 sta CONSOL .endif ; just in case: make sure Start's not being held down. lda #7 jsr waitconsol ; save previous contents of PBCTL and PORTB. diddling $d3f3 ; affects PBCTL whether Start is held down or not... and ; changing PBCTL crashes SpartaDOS X. ldx PBCTL ldy PORTB ; wait for the user to press Start. can't skip this, the magic ; register can only be updated when Start is pressed. .ifdef coldstart ; if the user presses any regular key, exit the program without ; changing modes or rebooting. lda #$ff sta CH waitkey: lda CH cmp #$ff beq wk_chkstart lda #$ff sta CH rts wk_chkstart: lda CONSOL cmp #6 bne waitkey .else lda #6 jsr waitconsol .endif ; disable both IRQ and NMI interrupts. sei lda #0 sta NMIEN ; update the magic, the same way setmem.xex does. lda magic_register and #$fc ora magic_bits sta magic_register ; original code waits for the user to release the Start key before ; re-enabling interrupts. I don't think this matters, but I'll do it. lda #7 jsr waitconsol ; restore previous PIA contents. stx PBCTL sty PORTB ; re-enable interrupts and exit. lda #$40 sta NMIEN cli .ifdef coldstart jmp COLDSV .else rts .endif xex_run runaddr ; the real work gets done by an init routine. xex_init entrypoint