mirror of
https://github.com/mgcaret/rom4x.git
synced 2024-06-18 03:29:30 +00:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
55c9a71e0d | ||
|
4e38bdf5de | ||
|
a14af25f96 | ||
|
65c2a4c7d0 | ||
|
464ce2e99d | ||
|
7383c05f9e | ||
|
4affcb82e4 | ||
|
264bf5ce0d | ||
|
ee1faf6cc7 | ||
|
8ea78bd613 | ||
|
40f2ae5ac3 | ||
|
ea0b4acb9d | ||
|
8e0a7007a3 | ||
|
a7c0aa2163 | ||
|
6ba882ffb9 | ||
|
11a799569d | ||
|
d5969104cd | ||
|
e04fa4d5ce | ||
|
1c5a03e3ea | ||
|
0c442f1d46 | ||
|
2e97ca4fc8 | ||
|
4f38bbccb2 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,4 +11,5 @@ copyrom.sh
|
|||
rom5x/accel5x
|
||||
make_rom.sh
|
||||
rom.sha256
|
||||
**/.DS_Store
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ It adds the following features to the Apple //c and IIc Plus firmware:
|
|||
- IIc only:
|
||||
- The system drops to BASIC if no bootable device is found (this is the default behavior in the IIc Plus).
|
||||
- Configure default boot device by saving a file on the RAM Disk.
|
||||
- [XModem-CRC](rom4x/xmodem.md) support.
|
||||
- IIc Plus only:
|
||||
- Menu control the built-in accelerator.
|
||||
- Accelerator settings persist across resets.
|
||||
|
|
9
dist.sh
9
dist.sh
|
@ -3,7 +3,14 @@
|
|||
rm -f "romXx_dist-*.zip"
|
||||
ROM4X="rom4x/iic_rom4x.bin"
|
||||
ROM5X="rom5x/iic+_rom5x.bin"
|
||||
FNAME="romXx_dist-`date --rfc-3339=date`.zip"
|
||||
case `uname -s` in
|
||||
Linux)
|
||||
FNAME="romXx_dist-`date --rfc-3339=date`.zip"
|
||||
;;
|
||||
*)
|
||||
FNAME="romXx_dist-`date '+%Y-%M-%d'`.zip"
|
||||
;;
|
||||
esac
|
||||
[ -f "${ROM4X}" ] && zip "${FNAME}" "${ROM4X}"
|
||||
[ -f "${ROM5X}" ] && zip "${FNAME}" "${ROM5X}"
|
||||
|
||||
|
|
|
@ -2,16 +2,15 @@ Inventory of zeros of 5 bytes or more in the IIc Plus ROM
|
|||
|
||||
Main Bank
|
||||
C1FB - 5 bytes
|
||||
C4EE - 12 bytes
|
||||
C4EE - 12 bytes - delete key fix
|
||||
C5F3 - 5 bytes
|
||||
C6FB - 5 bytes
|
||||
C9A1 - 9 bytes
|
||||
C6FB - 5 bytes - but Disk II ID here at C6FF
|
||||
C9A1 - 9 bytes - beep merlin fix part 2
|
||||
CFF9 - 7 bytes - ROM 5X switcher
|
||||
|
||||
Aux Bank
|
||||
C572 - 8 bytes
|
||||
C7FB - 8 bytes
|
||||
C7FC - 7 bytes
|
||||
C7FC - 7 bytes - ROM 5X dispatch jump at $C7FF
|
||||
CE00 - 512 bytes not usable (MIG space)
|
||||
D3B5 - 75 bytes - Accelerator menu text
|
||||
D516 - 234 bytes - ROM 5X boot
|
||||
|
|
11
rom4x/B0_C2F0_del_key.s
Normal file
11
rom4x/B0_C2F0_del_key.s
Normal file
|
@ -0,0 +1,11 @@
|
|||
; call RDCHAR, convert DEL to space
|
||||
; for patching into GETLN1/NXTCHAR (at $FD75)
|
||||
.code
|
||||
.include "iic.defs"
|
||||
.org $c2f0
|
||||
jsr $cced
|
||||
cmp #$ff
|
||||
bne :+
|
||||
lda #$88
|
||||
: rts
|
||||
|
|
@ -10,9 +10,5 @@
|
|||
.byte 0 ; rom4x present
|
||||
cbtfail: jsr setvid
|
||||
jsr setkbd
|
||||
lda #>(nbtfail-1)
|
||||
pha
|
||||
lda #<(nbtfail-1)
|
||||
pha
|
||||
jmp swrts2
|
||||
|
||||
lda #$01 ; cmd = boot fail1
|
||||
jmp $c7fc ; to dispatcher
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
.code
|
||||
.include "iic.defs"
|
||||
.org gorst4x
|
||||
sta rombank ; gorst4x
|
||||
jmp rst4xrtn ; in other bank jmp reset4x
|
||||
sta rombank ; gobt4x
|
||||
jmp bt4xrtn ; in other bank jmp boot4x
|
||||
sta rombank ; gobanner
|
||||
jsr banner ; in other bank jmp $c784 for now
|
||||
sta rombank ; return to other bank
|
||||
rts ; should never get here
|
||||
|
10
rom4x/B0_C7FC_4x_switch.s
Normal file
10
rom4x/B0_C7FC_4x_switch.s
Normal file
|
@ -0,0 +1,10 @@
|
|||
; There's a bit of luck in the firmware
|
||||
; there are 4 $00 bytes at $C7FC in the main bank, of which the last
|
||||
; is "AppleTalk version" according to the ROM $03 source code
|
||||
; in the tech ref, and should be left at $00.
|
||||
; There are also 7 $00 bytes at $C7FC in the aux bank. So if we switch at
|
||||
; $C7FC, then we get a 4 bytes in the aux bank, just enough for a jump.
|
||||
.code
|
||||
.org $c7fc
|
||||
sta $c028
|
||||
|
12
rom4x/B0_C9A1_load_save.s
Normal file
12
rom4x/B0_C9A1_load_save.s
Normal file
|
@ -0,0 +1,12 @@
|
|||
.pc02
|
||||
DISPATCH = $C7FC
|
||||
|
||||
.include "iic.defs"
|
||||
.code
|
||||
.org $C9A1 ; 9 bytes here
|
||||
.ifdef EN_XMODEM
|
||||
load: lda #$03
|
||||
.byte $2C ; BIT abs
|
||||
save: lda #$02
|
||||
jmp DISPATCH
|
||||
.endif
|
20
rom4x/B0_CFE5_monrw.s
Normal file
20
rom4x/B0_CFE5_monrw.s
Normal file
|
@ -0,0 +1,20 @@
|
|||
; Monitor R and W command handler for XModem
|
||||
GETNUM = $FFA7
|
||||
DISPATCH = $C7FC
|
||||
|
||||
.pc02
|
||||
|
||||
.include "iic.defs"
|
||||
.code
|
||||
.org $CFE5 ; 20 bytes here
|
||||
.ifdef EN_XMODEM
|
||||
lp: jsr GETNUM
|
||||
;sta $00 ; DEBUG, comment out for release
|
||||
cmp #$EB ; 'R'
|
||||
beq :+
|
||||
cmp #$F0 ; 'W'
|
||||
beq :+
|
||||
rts
|
||||
: jsr DISPATCH
|
||||
bra lp
|
||||
.endif
|
15
rom4x/B0_CFF9_switch_cmd.s
Normal file
15
rom4x/B0_CFF9_switch_cmd.s
Normal file
|
@ -0,0 +1,15 @@
|
|||
; This is the dispatch routine to call the primary ROM 4X
|
||||
; functions of intercepting RESET and the boot process.
|
||||
; If one enters at $CFF9, the command $A9 is loaded and
|
||||
; we go to the BELL1 hijack. If entering at $CFFA, we
|
||||
; load the command $EA and proceed the same way.
|
||||
; thus we get two dispatch codes in 6 bytes.
|
||||
|
||||
.include "iic.defs"
|
||||
.code
|
||||
.org $cff9 ; 7 bytes available here, but don't count on $CFFF
|
||||
lda #$a9 ; lda opcode
|
||||
nop ; jmp/jsr $cffa does lda #$ea
|
||||
jmp $c7fc ; jump to 4X dispatcher
|
||||
; total 6 bytes.
|
||||
|
10
rom4x/B0_D06C_asft_ldsv.s
Normal file
10
rom4x/B0_D06C_asft_ldsv.s
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
.pc02
|
||||
XM_LOAD = $C9A1
|
||||
XM_SAVE = $C9A4
|
||||
.include "iic.defs"
|
||||
.org $D06C
|
||||
.ifdef EN_XMODEM
|
||||
.addr XM_LOAD-1
|
||||
.addr XM_SAVE-1
|
||||
.endif
|
|
@ -1,6 +1,7 @@
|
|||
; patch PWRUP to call boot4x
|
||||
.code
|
||||
|
||||
.include "iic.defs"
|
||||
.code
|
||||
.org $fab4
|
||||
nop
|
||||
jmp gobt4x
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
; patch RESET.X to call reset4x
|
||||
.code
|
||||
|
||||
.include "iic.defs"
|
||||
.code
|
||||
.org $fac8
|
||||
jmp gorst4x
|
||||
|
||||
|
|
6
rom4x/B0_FD75_patch_getln1.s
Normal file
6
rom4x/B0_FD75_patch_getln1.s
Normal file
|
@ -0,0 +1,6 @@
|
|||
; patch GETLN1 to call delete key handler
|
||||
.code
|
||||
.include "iic.defs"
|
||||
.org $fd75
|
||||
jsr $c2f0
|
||||
|
7
rom4x/B0_FF73_patch_nxtitm.s
Normal file
7
rom4x/B0_FF73_patch_nxtitm.s
Normal file
|
@ -0,0 +1,7 @@
|
|||
.pc02
|
||||
.include "iic.defs"
|
||||
.code
|
||||
.org $FF73
|
||||
.ifdef EN_XMODEM
|
||||
jsr $CFE5 ; R/W patch
|
||||
.endif
|
|
@ -1,13 +0,0 @@
|
|||
.code
|
||||
.include "iic.defs"
|
||||
.org gorst4x
|
||||
sta rombank ; gorst4x
|
||||
jmp reset4x ; in other bank jmp rstxrtn
|
||||
sta rombank ; gobt4x
|
||||
jmp boot4x
|
||||
sta rombank ; gobanner
|
||||
jmp $c784 ; in other bank jsr appleii
|
||||
sta rombank
|
||||
rts ; not in other bank
|
||||
|
||||
|
6
rom4x/B1_C7FF_4x_jump.s
Normal file
6
rom4x/B1_C7FF_4x_jump.s
Normal file
|
@ -0,0 +1,6 @@
|
|||
; See B0 C7FC patch for description
|
||||
.include "iic.defs"
|
||||
.code
|
||||
.org $c7ff
|
||||
jmp rom4x_disp
|
||||
|
|
@ -1,16 +1,65 @@
|
|||
.psc02
|
||||
.code
|
||||
.include "iic.defs"
|
||||
.org reset4x
|
||||
|
||||
; to enable/disable XModem, see EN_XMODEM in iic.defs
|
||||
|
||||
.org rom4x_disp
|
||||
.proc dispatch
|
||||
cmp #$a9 ; reset patch
|
||||
bne :+
|
||||
bra reset4x
|
||||
: cmp #$ea ; boot patch
|
||||
bne :+
|
||||
jmp boot4x
|
||||
: cmp #$01 ; $01 = new boot fail routine
|
||||
bne :+
|
||||
jmp nbtfail
|
||||
.ifdef EN_XMODEM
|
||||
; XModem functions
|
||||
: cmp #$02 ; $02 = AppleSoft SAVE
|
||||
bne :+
|
||||
jmp asftsave
|
||||
: cmp #$03 ; $03 = AppleSoft LOAD
|
||||
bne :+
|
||||
jmp asftload
|
||||
: cmp #$F0 ; $F0 = monitor W(rite)
|
||||
bne :+
|
||||
jmp monwrite
|
||||
: cmp #$EB ; $EB = monitor R(ead)
|
||||
bne :+
|
||||
jmp monread
|
||||
.endif
|
||||
: sta $00 ; for debug, comment if not using
|
||||
lda #>(monitor-1)
|
||||
pha
|
||||
lda #<(monitor-1)
|
||||
pha
|
||||
jmp swrts2 ; jump to monitor
|
||||
.endproc
|
||||
|
||||
; next is snippet of code to boot external 5.25
|
||||
.proc bootext
|
||||
lda #$e0
|
||||
ldy #$01 ; unit #
|
||||
ldx #$60 ; slot #
|
||||
jmp $c60b ; jump into Disk II code
|
||||
.endproc
|
||||
|
||||
.proc reset4x
|
||||
stz power2 + rx_mslot ; action = normal boot
|
||||
asl butn1 ; closed apple
|
||||
bcs ckdiag
|
||||
exitrst: jmp gorst4x ; return to RESET.X
|
||||
exitrst: lda #>(rst4xrtn-1)
|
||||
pha
|
||||
lda #<(rst4xrtn-1)
|
||||
pha
|
||||
jmp swrts2
|
||||
; check to see if both apples are down
|
||||
ckdiag: bit butn0 ; open apple
|
||||
bmi exitrst ; return to RESET.X
|
||||
; present menu because only closed apple is down
|
||||
menu4x: jsr gobanner ; "Apple //c"
|
||||
menu4x: jsr ntitle ; "Apple //c"
|
||||
ldx #$00 ; menu start
|
||||
jsr disp ; show it
|
||||
jsr gtkey
|
||||
|
@ -38,6 +87,9 @@ ckkey2: sec
|
|||
stz softev + 1 ; deinit coldstart
|
||||
stz pwerdup ; ditto
|
||||
bra exitrst
|
||||
.endproc
|
||||
|
||||
.proc gtkey
|
||||
gtkey: lda #$60
|
||||
sta ($0),y ; cursor
|
||||
sta kbdstrb ; clr keyboard
|
||||
|
@ -45,8 +97,11 @@ kbdin: lda kbd ; get key
|
|||
bpl kbdin
|
||||
sta kbdstrb ; clear keyboard
|
||||
sta ($0),y ; put it on screen
|
||||
rts
|
||||
rts
|
||||
.endproc
|
||||
|
||||
; display message, input x = message start relative to msg1
|
||||
.proc disp
|
||||
disp: stz $0 ; load some safe defaults
|
||||
lda #$04
|
||||
sta $1
|
||||
|
@ -66,7 +121,10 @@ disp2: sta $1 ; write address high
|
|||
sta $0 ; write address low
|
||||
inx ; set next msg byte
|
||||
bra disp0 ; back to the beginning
|
||||
confirm: pha
|
||||
.endproc
|
||||
|
||||
.proc confirm
|
||||
pha
|
||||
ldx #(msg3-msg1) ; ask confirm
|
||||
jsr disp
|
||||
jsr gtkey
|
||||
|
@ -77,6 +135,8 @@ confirm: pha
|
|||
txa
|
||||
plp
|
||||
rts
|
||||
.endproc
|
||||
|
||||
; msg format
|
||||
; A byte < $20 indicates high byte of address.
|
||||
; Next byte must be low byte of address. Anything
|
||||
|
@ -92,15 +152,14 @@ msg1 = *
|
|||
.byte $04,$2e,"6 Boot Int. 5.25"
|
||||
.byte $04,$ae,"7 Boot Ext. 5.25"
|
||||
.byte $07,$5f,"By M.G."
|
||||
msg2: .byte $07,$db,"ROM 4X 05/27/17"
|
||||
msg2: .byte $07,$db,"ROM 4X 10/01/18"
|
||||
.byte $05,$ae,$00 ; cursor pos in menu
|
||||
msg3: .byte $05,$b0,"SURE? ",$00
|
||||
msg3: .byte $05,$b0,"SURE? ",$00
|
||||
.dword .time ; embed POSIX build time
|
||||
|
||||
; Boot4X - the boot portion of the program
|
||||
.assert * < boot4x, warning, .sprintf("Boot4X overrun! * = %x, > %x", *, boot4x)
|
||||
.res boot4x - *, 0
|
||||
.org boot4x
|
||||
jsr gobanner ; "Apple //c"
|
||||
.proc boot4x
|
||||
jsr ntitle ; "Apple //c"
|
||||
jsr rdrecov ; try to recover ramdisk
|
||||
lda power2 + rx_mslot ; get action saved by reset4x
|
||||
beq :+ ; unset, go look for config on ram card
|
||||
|
@ -115,7 +174,7 @@ msg3: .byte $05,$b0,"SURE? ",$00
|
|||
phx ; config present, save it and move on
|
||||
lda #'C' ; tell user
|
||||
sta $7d1 ; on screen
|
||||
selboot: ldx #(msg2-msg1) ; short banner offset
|
||||
selboot: ldx #(msg2-msg1) ; short offset
|
||||
jsr disp ; display it
|
||||
pla ; get boot selection from stack
|
||||
;sta $7d2
|
||||
|
@ -150,7 +209,7 @@ btc7: cmp #$07 ; boot ext drive
|
|||
bne boot4 ; none of the above
|
||||
; copy small routine to $800 to boot
|
||||
; external 5.25
|
||||
ldy #(bt4xend-bootext+1)
|
||||
ldy #.sizeof(bootext)
|
||||
btc7lp: lda bootext,y
|
||||
sta $800,y
|
||||
dey
|
||||
|
@ -165,8 +224,15 @@ boot6: lda #$c6 ; boot slot 6
|
|||
bootsl: ldx #$00 ; low byte of slot
|
||||
bootadr: stx $0 ; store address
|
||||
sta $1 ; return to bank 0 does jmp (0)
|
||||
endbt4x: jmp gobt4x ; continue boot
|
||||
rdrecov: jsr rdinit ; init ramcard
|
||||
endbt4x: lda #>(bt4xrtn-1)
|
||||
pha
|
||||
lda #<(bt4xrtn-1)
|
||||
pha
|
||||
jmp swrts2
|
||||
.endproc
|
||||
|
||||
.proc rdrecov
|
||||
jsr rdinit ; init ramcard
|
||||
lda pwrup,y ; get power up flag
|
||||
cmp #pwrbyte ; already initialized?
|
||||
beq recovdn ; exit if initialized
|
||||
|
@ -188,8 +254,11 @@ rdrecov: jsr rdinit ; init ramcard
|
|||
lda #'R' ; tell user
|
||||
sta $7d0 ; on screen
|
||||
recovdn: rts
|
||||
.endproc
|
||||
|
||||
; zero ram card space
|
||||
rdclear: jsr rdinit ; init ramcard
|
||||
.proc rdclear
|
||||
jsr rdinit ; init ramcard
|
||||
jsr testsize ; get size
|
||||
lda numbanks,y ; # of 64Ks to write
|
||||
beq clrdone ; no memory
|
||||
|
@ -217,16 +286,28 @@ clrdone: ldx #rx_mslot
|
|||
lda #$a0 ; ' '
|
||||
sta $400 ; clear progress
|
||||
rts
|
||||
rdinit: bit rx_mslot*$100 ; activate registers
|
||||
.endproc
|
||||
|
||||
.proc rdinit
|
||||
bit rx_mslot*$100 ; activate registers
|
||||
ldy #rx_mslot ; slot offset
|
||||
ldx #rx_devno ; register offset
|
||||
rts
|
||||
; next is snippet of code to boot external 5.25
|
||||
bootext: lda #$e0
|
||||
ldy #$01 ; unit #
|
||||
ldx #$60 ; slot #
|
||||
jmp $c60b ; jump into Disk II code
|
||||
bt4xend = *
|
||||
.endproc
|
||||
|
||||
; arrange a sequence of RTS tricks to display title screen
|
||||
.proc ntitle
|
||||
lda #>(swrts2-1) ; put return addr of swrts/swrts2 on stack
|
||||
pha
|
||||
lda #<(swrts2-1)
|
||||
pha
|
||||
lda #>(banner-1) ; put addr of the Title routine on the stack
|
||||
pha
|
||||
lda #<(banner-1)
|
||||
pha
|
||||
jmp swrts2 ; jump to swrts2
|
||||
.endproc
|
||||
|
||||
; --------------------------------------------------
|
||||
; config getter
|
||||
; values and locs
|
||||
|
@ -346,4 +427,27 @@ fname_ = *
|
|||
rts
|
||||
.endproc
|
||||
|
||||
; Display new boot failure message and jump to BASIC
|
||||
.proc nbtfail
|
||||
ldx #msglen
|
||||
lp1: lda bootmsg,x
|
||||
ora #$80
|
||||
sta $7d0+19-(<msglen/2),x
|
||||
dex
|
||||
bpl lp1
|
||||
lda #23 ; last line
|
||||
sta cv
|
||||
lda #>(basic-1)
|
||||
pha
|
||||
lda #<(basic-1)
|
||||
pha
|
||||
jmp swrts2
|
||||
bootmsg:
|
||||
.byte "No bootable device."
|
||||
msglen = * - bootmsg - 1
|
||||
.endproc
|
||||
|
||||
.ifdef EN_XMODEM
|
||||
.include "inc/xmodem.s"
|
||||
.endif
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
.code
|
||||
.include "iic.defs"
|
||||
.org nbtfail
|
||||
ldx #msglen
|
||||
lp1: lda bootmsg,x
|
||||
ora #$80
|
||||
sta $7d0+19-(<msglen/2),x
|
||||
dex
|
||||
bpl lp1
|
||||
lda #23 ; last line
|
||||
sta cv
|
||||
lda #>(basic-1)
|
||||
pha
|
||||
lda #<(basic-1)
|
||||
pha
|
||||
jmp swrts2
|
||||
bootmsg: .byte "No bootable device."
|
||||
msglen = * - bootmsg - 1
|
||||
|
|
@ -11,6 +11,7 @@ It adds the following features to the Apple //c:
|
|||
- Tell the machine to boot the SmartPort, the internal floppy drive, or an external floppy drive.
|
||||
- The system drops to BASIC if no bootable device is found (this is the default behavior in the IIc Plus).
|
||||
- Configure default boot device by saving a file on the RAM Disk.
|
||||
- As of the 10/01/2018 release, provides XModem-CRC features (see [xmodem.md](xmodem.md)).
|
||||
|
||||
RAM expansion cards known to work with ROM 4X include the Apple Memory Expansion Card (but no battery!), and the A2Heaven [RAM Express II](http://a2heaven.com/webshop/index.php?rt=product/product&product_id=146) for the original //c, and the [RAM Express II+](http://a2heaven.com/webshop/index.php?rt=product/product&product_id=144) for the memory-expandable //c and IIc Plus.
|
||||
|
||||
|
@ -43,9 +44,13 @@ Once you have a ROM chip, generally the instructions [here](http://mirrors.apple
|
|||
|
||||
### Emulator
|
||||
|
||||
Copy the ROM into the appropriate location for your emulator. At the time of writing the only emulator I am aware of that can emulate the //c with memory expansion is [Catakig](http://catakig.sourceforge.net/) for MacOS. It's a bit older of an emulator but it runs fine on newer MacOSes.
|
||||
The following emulators are known to run ROM 4X:
|
||||
|
||||
MAME's Apple //c emulation may work, but I have not tried it.
|
||||
* MAME (replace the image for the apple2c4 machine).
|
||||
* [Leon Bottou](https://github.com/leonbottou)'s "universal" versions of GSPlus and KEGS.
|
||||
* Catakig (older emulator for MacOS, will stop working on Mojave).
|
||||
|
||||
Copy the ROM image into the appropriate location for your emulator.
|
||||
|
||||
## Operation
|
||||
|
||||
|
@ -110,6 +115,10 @@ This is like option 6, but using an external 5.25 drive. The only OS I am aware
|
|||
|
||||
This destructively copies a short routine to $800, which under most circumstances is also immediately overwritten by the boot sector, so should not be a problem..
|
||||
|
||||
#### XModem-CRC
|
||||
|
||||
See [xmodem.md](xmodem.md).
|
||||
|
||||
### Configuration File
|
||||
|
||||
If the RAM card is ProDOS-formatted, you can save a binary file in the volume directory called `BOOTX`. ROM 4X will find this file and use the Aux Type field (the load address) to set a default of the menu options above when no option has been selected using the menu. For example, `BSAVE /RAM4/BOOTX,A6,L0` will cause ROM 4X to skip booting the RAM card and go straight to booting the internal floppy drive (menu item 6). The contents of `BOOTX` are irrelevant, only the Aux Type is used. You cannot set it to jump into the monitor because that action happens before the boot code takes over.
|
||||
|
@ -132,7 +141,7 @@ It may work with other ROM dumps, it will *not* work with any other ROM versions
|
|||
|
||||
The Rakefile will download the file from a well-known location if it is not already present. It also verifies the checksum.
|
||||
|
||||
Now you will need a 65C02 cross assembler. The current codebase is developed using ca65 from the [cc65](http://www.cc65.org/) project. (Note: The code was developed originally using [xa](http://www.floodgap.com/retrotech/xa/)).
|
||||
Now you will need a 65C02 cross assembler. The current codebase is developed using ca65 from the [cc65](http://www.cc65.org/) project. Only the assembler and linker are required. Older versions may complain about argument order, generally versions identifying as "2.16" built from the ca65 git master branch work fine.
|
||||
|
||||
Finally you will need [Ruby](https://www.ruby-lang.org/en/) and [Rake](https://github.com/ruby/rake).
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
; options
|
||||
EN_XMODEM = 1 ; comment out to disable XModem support
|
||||
|
||||
; hardware
|
||||
rombank = $c028
|
||||
butn0 = $c061
|
||||
|
@ -39,15 +42,11 @@ rx_mslot = rx_slot + $c0
|
|||
rx_devno = rx_slot * $10 + $88
|
||||
|
||||
; entry points
|
||||
gorst4x = $c763 ; switch to/from reset4x
|
||||
gobt4x = gorst4x + 6 ; switch to/from boot4x
|
||||
gobanner = gobt4x + 6 ; banner from bank 1
|
||||
gorst4x = $cff9 ; reset patch destination
|
||||
gobt4x = $cffa ; boot patch destination
|
||||
rst4xrtn = $facb ; where to return from reset4x
|
||||
;bt4xrtn = $fb12 ; where to return from boot4x
|
||||
bt4xrtn = $fb19 ; where to return from boot4x
|
||||
reset4x = $e000 ; reset routine
|
||||
boot4x = $e180 ; boot routine
|
||||
nbtfail = $e400 ; new boot fail
|
||||
rom4x_disp = $e000 ; main dispatch code
|
||||
banner = $fb60 ; 'Apple //c'
|
||||
testsize = $d99f ; test ramdisk size (rom 3 = $d995)
|
||||
monitor = $ff59 ; monitor
|
||||
|
|
719
rom4x/inc/xmodem.s
Normal file
719
rom4x/inc/xmodem.s
Normal file
|
@ -0,0 +1,719 @@
|
|||
; XMODEM/CRC Sender/Receiver for the 65C02/ROM 4X
|
||||
;
|
||||
; Adapted from Routines by By Daryl Rictor Aug 2002
|
||||
; originally found at http://www.6502.org/source/io/xmodem/xmodem.txt
|
||||
;
|
||||
; Original source header commentary:
|
||||
;
|
||||
; A simple file transfer program to allow transfers between the SBC and a
|
||||
; console device utilizing the x-modem/CRC transfer protocol. Requires
|
||||
; ~1200 bytes of either RAM or ROM, 132 bytes of RAM for the receive buffer,
|
||||
; and 12 bytes of zero page RAM for variable storage.
|
||||
;
|
||||
;**************************************************************************
|
||||
; This implementation of XMODEM/CRC does NOT conform strictly to the
|
||||
; XMODEM protocol standard in that it (1) does not accurately time character
|
||||
; reception or (2) fall back to the Checksum mode.
|
||||
|
||||
; (1) For timing, it uses a crude timing loop to provide approximate
|
||||
; delays. These have been calibrated against a 1MHz CPU clock. I have
|
||||
; found that CPU clock speed of up to 5MHz also work but may not in
|
||||
; every case. Windows HyperTerminal worked quite well at both speeds!
|
||||
;
|
||||
; (2) Most modern terminal programs support XMODEM/CRC which can detect a
|
||||
; wider range of transmission errors so the fallback to the simple checksum
|
||||
; calculation was not implemented to save space.
|
||||
;**************************************************************************
|
||||
;
|
||||
; Files transferred via XMODEM-CRC will have the load address contained in
|
||||
; the first two bytes in little-endian format:
|
||||
; FIRST BLOCK
|
||||
; offset(0) = lo(load start address),
|
||||
; offset(1) = hi(load start address)
|
||||
; offset(2) = data byte (0)
|
||||
; offset(n) = data byte (n-2)
|
||||
;
|
||||
; Subsequent blocks
|
||||
; offset(n) = data byte (n)
|
||||
;
|
||||
; One note, XMODEM send 128 byte blocks. If the block of memory that
|
||||
; you wish to save is smaller than the 128 byte block boundary, then
|
||||
; the last block will be padded with zeros. Upon reloading, the
|
||||
; data will be written back to the original location. In addition, the
|
||||
; padded zeros WILL also be written into RAM, which could overwrite other
|
||||
; data.
|
||||
;
|
||||
;**************************************************************************
|
||||
; MG's changes & notes for this implementation:
|
||||
; entry/exit routines
|
||||
; support routines to support AppleSoft and monitor load/save and r/w commands
|
||||
; Receive address in header only respected if A1L/H = 0
|
||||
; length is written in header as well, in order to not leak AppleSoft RAM
|
||||
; Requires 8 bytes of additional stack to save $00-$07, total about 9+10=19 bytes
|
||||
; needed. The main entry to the xmodem routine checks this.
|
||||
; No prompting or messages, but status codes written on upper right corner of screen
|
||||
; Use 'C02 opcodes in places
|
||||
; Optimize code.
|
||||
; Allow user to cancel xfer with ESC key.
|
||||
; Send ETX when done with send.
|
||||
; move magic numers out of code
|
||||
;-------------------------- The Code ----------------------------
|
||||
;
|
||||
; zero page variables (adjust these to suit your needs)
|
||||
;
|
||||
;
|
||||
|
||||
; we will save and restore ZPSTART+n values to stack
|
||||
ZPSTART = $00
|
||||
lastblk = ZPSTART+0 ; flag for last block
|
||||
blkno = ZPSTART+1 ; block number
|
||||
errcnt = ZPSTART+2 ; error counter 10 is the limit
|
||||
bflag = ZPSTART+3 ; block flag, indicate block 1 received
|
||||
|
||||
crc = ZPSTART+4 ; CRC lo byte (two byte variable)
|
||||
crch = ZPSTART+5 ; CRC hi byte
|
||||
|
||||
retry = ZPSTART+6 ; retry counter
|
||||
retry2 = ZPSTART+7 ; 2nd counter
|
||||
ZPEND = retry2-ZPSTART
|
||||
|
||||
A1L = $3C
|
||||
A1H = $3D
|
||||
ptr = A1L ; data pointer (two byte variable)
|
||||
ptrh = A1H ; " "
|
||||
|
||||
A2L = $3E
|
||||
A2H = $3F
|
||||
eofp = A2L ; end of file address pointer (2 bytes)
|
||||
eofph = A2H ; " " " "
|
||||
|
||||
; AppleSoft ZPs
|
||||
|
||||
PRGEND = $AF ; end of program
|
||||
TXTTAB = $67 ; start of program
|
||||
VARTAB = $69 ; start of variables (usually same as PRGEND)
|
||||
LINNUM = $50 ; line number, usable as temporary storage
|
||||
|
||||
FIXLINKS = $D4F2 ; AppleSoft routine to re-link program
|
||||
SCRTCH = $D64B ; AppleSoft "SCRTCH" - erase program, reset everything
|
||||
PRERR = $FF2D ; print "ERR" and beep
|
||||
|
||||
|
||||
STLOC = $427 ; text page location for status
|
||||
|
||||
KBD = $C000 ; keyboard
|
||||
KBDSTR = $C010 ; keyboard strobe
|
||||
|
||||
; Error values
|
||||
ENOERR = $00 ; no error
|
||||
ENOSTK = $D0 ; not enough free stack
|
||||
EFAILED = $FF ; failed due to too many errors
|
||||
ECANCEL = $FE ; transfer cancelled, receive memory changed
|
||||
EBLKMM = $FD ; block number mismatch
|
||||
EMMBLK = $FC ; block number complement mismatch
|
||||
ECANCELOK = $FB ; transfer cancelled, receive memory unchanged or send
|
||||
|
||||
|
||||
ACIA_SLOT = 2 ; slot # of the ACIA, //c: 1=printer, 2=modem
|
||||
|
||||
; defining STANDALONE allow one to build a test version of the code .orged
|
||||
; at $4000 in order to test the basic bits of the code without doing a full
|
||||
; firmware build. Even works on the enhanced //e with SSC.
|
||||
; comment out if going to //c firmware
|
||||
;STANDALONE = 1
|
||||
|
||||
start_retries = 20 ; number of ~3s timeouts to allow for transfer to start
|
||||
; 20 = ~1 minute, which is more than enough.
|
||||
|
||||
;
|
||||
;
|
||||
; non-zero page variables and buffers
|
||||
;
|
||||
;
|
||||
Rbuff = $0200+256-132 ; temp 132 byte receive buffer, don't cross page boundary
|
||||
; placed over input buffer.
|
||||
;
|
||||
;
|
||||
; tables and constants
|
||||
;
|
||||
; see further down for CRC tables
|
||||
;
|
||||
; XMODEM Control Character Constants
|
||||
SOH = $01 ; start block
|
||||
EOT = $04 ; end of text marker
|
||||
ACK = $06 ; good block acknowledged
|
||||
NAK = $15 ; bad block acknowledged
|
||||
CAN = $18 ; cancel (not standard, not supported)
|
||||
CR = $0d ; carriage return
|
||||
LF = $0a ; line feed
|
||||
ESC = $1b ; ESC to exit
|
||||
|
||||
; XMODEM other constants
|
||||
|
||||
MAXERRS = 10 ; max # of errors before failure (from spec)
|
||||
CPMEOF = $1A ; CP/M EOF char, which is the correct padding
|
||||
|
||||
;
|
||||
;^^^^^^^^^^^^^^^^^^^^^^ Start of Program ^^^^^^^^^^^^^^^^^^^^^^
|
||||
;
|
||||
; Xmodem/CRC transfer routines
|
||||
; By Daryl Rictor, August 8, 2002
|
||||
;
|
||||
; v1.0 released on Aug 8, 2002.
|
||||
; MG's IIc version released Oct 2018
|
||||
;
|
||||
;
|
||||
|
||||
.ifdef STANDALONE
|
||||
.pc02
|
||||
.org $4000
|
||||
; use jmps here for testing on IIe/SSC because IIe mon doesn't
|
||||
; disassemble 'C02 opcodes.
|
||||
jmp monread
|
||||
jmp monwrite
|
||||
jmp asftload
|
||||
; asftsave follows
|
||||
.endif
|
||||
|
||||
; AppleSoft BASIC save/load
|
||||
asftsave: jsr asftprgio
|
||||
sec
|
||||
jsr xmentry ; AppleSoft tape SAVE doesn't check for errors
|
||||
bcs monerr ; but we will
|
||||
.ifdef STANDALONE
|
||||
rts
|
||||
.else
|
||||
jmp swrts2
|
||||
.endif
|
||||
|
||||
asftload: jsr asftprgio ; current length does not matter
|
||||
clc
|
||||
jsr xmentry
|
||||
; sta $300 ; DEBUG, comment out for release
|
||||
bcs loaderr ; if error, execute NEW
|
||||
lda A2L ; otherwise A2L/H have exact end address
|
||||
ldy A2H ; regardless of appended junk
|
||||
sta VARTAB ; mark end of program/start of vars
|
||||
sty VARTAB+1
|
||||
.ifdef STANDALONE
|
||||
jmp FIXLINKS ; finally, re-link the program
|
||||
.else
|
||||
; //c aux bank code
|
||||
lda #>(FIXLINKS-1) ; indirectly if in aux ROM
|
||||
pha
|
||||
lda #<(FIXLINKS-1)
|
||||
pha
|
||||
jmp swrts2
|
||||
.endif
|
||||
|
||||
loaderr: cmp #ECANCELOK ; program memory unchanged?
|
||||
beq :+ ; yes, don't clear program
|
||||
; (in case user accidentally used LOAD)
|
||||
lda #>(SCRTCH-1) ; otherwise set up RTS trick for SCRTCH ('NEW')
|
||||
pha ; whether we are in aux ROM or RAM
|
||||
lda #<(SCRTCH-1)
|
||||
pha
|
||||
: bra monerr ; print ERR first
|
||||
|
||||
|
||||
; set up A1L/H and A2L/H for applesoft program save/load
|
||||
; this is exactly what AppleSoft does for tapes
|
||||
asftprgio: lda TXTTAB
|
||||
ldy TXTTAB+1
|
||||
sta A1L
|
||||
sty A1H
|
||||
lda VARTAB
|
||||
ldy VARTAB+1
|
||||
sta A2L
|
||||
sty A2H
|
||||
rts
|
||||
|
||||
monwrite: sec
|
||||
phy
|
||||
jsr xmentry
|
||||
ply
|
||||
bcs monerr
|
||||
monok:
|
||||
.ifdef STANDALONE
|
||||
rts
|
||||
.else
|
||||
; //c aux bank code
|
||||
jmp swrts2
|
||||
.endif
|
||||
|
||||
monread: clc
|
||||
phy
|
||||
jsr xmentry
|
||||
ply
|
||||
bcc monok
|
||||
monerr:
|
||||
.ifdef STANDALONE
|
||||
jmp PRERR
|
||||
.else
|
||||
; //c aux bank code
|
||||
lda #>(PRERR-1)
|
||||
pha
|
||||
lda #<(PRERR-1)
|
||||
pha
|
||||
jmp swrts2
|
||||
.endif
|
||||
|
||||
;
|
||||
; Enter this routine with the beginning address stored in the zero page address
|
||||
; pointed to by ptr & ptrh and the ending address stored in the zero page address
|
||||
; pointed to by eofp & eofph.
|
||||
;
|
||||
; Carry set = save, clear = load
|
||||
xmentry: php ; Save carry status to select op
|
||||
tsx ; see if enough room on stack
|
||||
cpx #ZPEND+10
|
||||
bcs :+ ; yes, go ahead
|
||||
plp ; otherwise restore saved status
|
||||
lda #ENOSTK ; flag not enough stack
|
||||
sec ; and error out
|
||||
rts
|
||||
: bit butn1 ; check closed apple key
|
||||
bmi :+ ; and if held, don't set up ACIA
|
||||
jsr ACIA_Init ; set up ACIA
|
||||
: plp ; and get carry status back
|
||||
ldx #ZPEND ; save ZP locations we are trashing on stack
|
||||
savelp: lda ZPSTART,x
|
||||
pha
|
||||
dex
|
||||
bpl savelp
|
||||
lda STLOC ; and save char in the screen loc we use
|
||||
pha
|
||||
bcs dosend
|
||||
jsr XModemRcv
|
||||
bra skip1
|
||||
dosend: jsr XModemSend
|
||||
skip1: tay
|
||||
pla
|
||||
sta STLOC ; fix screen
|
||||
ldx #ZPEND ; restore ZP locations
|
||||
restlp: pla
|
||||
sta ZPSTART,x
|
||||
dex
|
||||
bpl restlp
|
||||
tya
|
||||
cmp #$01 ; carry reflects error status
|
||||
rts
|
||||
|
||||
; MG's modified Daryl Rictor XModem routines follow:
|
||||
|
||||
XModemSend: lda #$17 ; 'W' inverse
|
||||
sta STLOC
|
||||
lda #start_retries
|
||||
sta errcnt ; will count retries left
|
||||
stz lastblk ; set flag to false
|
||||
lda #$01
|
||||
sta bflag ; if user cancels, we haven't changed memory
|
||||
sta blkno ; set block # to 1
|
||||
Wait4CRC: lda #$ff ; 3 seconds
|
||||
sta retry2 ;
|
||||
jsr GetByte ;
|
||||
bcs :+
|
||||
lda #'W' ; 'W' flashing
|
||||
sta STLOC
|
||||
jsr ChkCancel ; see if user wants to cancel
|
||||
dec errcnt
|
||||
bne Wait4CRC
|
||||
beq can1
|
||||
: bcc Wait4CRC ; wait for something to come in...
|
||||
cmp #'C' ; is it the "C" to start a CRC xfer?
|
||||
beq SetstAddr ; yes
|
||||
cmp #ESC ; is it a cancel? <Esc> Key
|
||||
bne Wait4CRC ; No, wait for another character
|
||||
can1: jmp SxCancel ; cancel
|
||||
SetstAddr: lda #'S'
|
||||
sta STLOC
|
||||
lda #MAXERRS ; max # of errors
|
||||
sta errcnt ; initialize error counter
|
||||
ldy #$00 ; init data block offset to 0
|
||||
ldx #$06 ; preload X to Receive buffer
|
||||
lda #$01 ; manually load blk number
|
||||
sta Rbuff ; into 1st byte
|
||||
lda #$FE ; load 1's comp of block #
|
||||
sta Rbuff+1 ; into 2nd byte
|
||||
lda ptr ; load low byte of start address
|
||||
sta Rbuff+2 ; into 3rd byte
|
||||
lda ptrh ; load hi byte of start address
|
||||
sta Rbuff+3 ; into 4th byte
|
||||
sec ; now compute length - 1
|
||||
lda eofp
|
||||
sbc ptr
|
||||
sta Rbuff+4
|
||||
lda eofph
|
||||
sbc ptrh
|
||||
sta Rbuff+5
|
||||
bra LdBuff1 ; jump into buffer load routine
|
||||
|
||||
LdBuffer: lda lastblk ; Was the last block sent?
|
||||
beq LdBuff0 ; no, send the next one
|
||||
lda #EOT ; yes, done!
|
||||
jsr Put_Chr ; send EOT
|
||||
lda #ENOERR ; flag no error
|
||||
rts
|
||||
LdBuff0: ldx #$02 ; init pointers
|
||||
ldy #$00 ;
|
||||
inc blkno ; inc block counter
|
||||
jsr BlkStatus ; put current block on screen
|
||||
lda blkno ;
|
||||
sta Rbuff ; save in 1st byte of buffer
|
||||
eor #$FF ;
|
||||
sta Rbuff+1 ; save 1's comp of blkno next
|
||||
|
||||
LdBuff1: lda (ptr),y ; save 128 bytes of data
|
||||
sta Rbuff,x ;
|
||||
LdBuff2: sec ;
|
||||
lda eofp ;
|
||||
sbc ptr ; Are we at the last address?
|
||||
bne LdBuff4 ; no, inc pointer and continue
|
||||
lda eofph ;
|
||||
sbc ptrh ;
|
||||
bne LdBuff4 ;
|
||||
inc lastblk ; Yes, Set last byte flag
|
||||
LdBuff3: inx ;
|
||||
cpx #$82 ; Are we at the end of the 128 byte block?
|
||||
beq SCalcCRC ; Yes, calc CRC
|
||||
lda #CPMEOF ; Fill rest of 128 bytes with CP/M EOF (^Z)
|
||||
sta Rbuff,x ;
|
||||
beq LdBuff3 ; Branch always
|
||||
|
||||
LdBuff4: inc ptr ; Inc address pointer
|
||||
bne LdBuff5 ;
|
||||
inc ptrh ;
|
||||
LdBuff5: inx ;
|
||||
cpx #$82 ; last byte in block?
|
||||
bne LdBuff1 ; no, get the next
|
||||
SCalcCRC: jsr CalcCRC
|
||||
lda crch ; save Hi byte of CRC to buffer
|
||||
sta Rbuff,y ;
|
||||
iny ;
|
||||
lda crc ; save lo byte of CRC to buffer
|
||||
sta Rbuff,y ;
|
||||
Resend: ldx #$00 ;
|
||||
jsr ChkCancel ; see if user wants to cancel
|
||||
lda #SOH
|
||||
jsr Put_Chr ; send SOH
|
||||
SendBlk: lda Rbuff,x ; Send 132 bytes in buffer to the console
|
||||
jsr Put_Chr ;
|
||||
inx ;
|
||||
cpx #$84 ; last byte?
|
||||
bne SendBlk ; no, get next
|
||||
lda #$FF ; yes, set 3 second delay
|
||||
sta retry2 ; and
|
||||
jsr GetByte ; Wait for Ack/Nack
|
||||
bcc Seterror ; No chr received after 3 seconds, resend
|
||||
cmp #ACK ; Chr received... is it:
|
||||
beq LdBuffer ; ACK, send next block
|
||||
cmp #NAK ;
|
||||
beq Seterror ; NAK, inc errors and resend
|
||||
cmp #ESC ;
|
||||
beq SxCancel ; Esc pressed to abort
|
||||
; fall through to error counter
|
||||
Seterror: lda #'E'
|
||||
sta STLOC
|
||||
dec errcnt ; dec error counter
|
||||
bne Resend ; resend block if not zero
|
||||
jsr Flush ; yes, too many errors, flush buffer,
|
||||
lda #EFAILED
|
||||
rts
|
||||
ChkCancel: lda KBD ; see if ESC has been hit by user
|
||||
cmp #ESC|$80
|
||||
beq :+ ; yep, so cancel transfer
|
||||
rts ; nope, as we were
|
||||
: pla ; drop caller address
|
||||
pla
|
||||
sta KBDSTR ; clear keyboard strobe
|
||||
lda #ESC ; send escape
|
||||
jsr Put_Chr ; to the remote end
|
||||
SxCancel: jsr Flush ; flush receive stream
|
||||
lda bflag ; did user memory change?
|
||||
beq :+ ; other error code if so
|
||||
lda #ECANCELOK ; otherwise tell user all is OK
|
||||
rts
|
||||
: lda #ECANCEL ; and indicate a transfer cancelled
|
||||
rts
|
||||
;
|
||||
;
|
||||
;
|
||||
|
||||
XModemRcv: lda #$12 ; 'R' inverse
|
||||
sta STLOC
|
||||
lda #start_retries
|
||||
sta errcnt ; will count retries left
|
||||
lda #$01
|
||||
sta blkno ; set block # to 1
|
||||
sta bflag ; set flag to get address from block 1
|
||||
StartCrc: lda #'C' ; "C" start with CRC mode
|
||||
jsr Put_Chr ; send it
|
||||
lda #$FF
|
||||
sta retry2 ; set loop counter for ~3 sec delay
|
||||
;lda #$00
|
||||
;sta crc
|
||||
;sta crch ; init CRC value
|
||||
stz crc
|
||||
stz crch
|
||||
jsr GetByte ; wait for input
|
||||
bcs GotByte ; byte received, process it
|
||||
jsr ChkCancel
|
||||
lda #'R' ; flashing
|
||||
sta STLOC
|
||||
dec errcnt ; next try
|
||||
bne StartCrc ; resend "C" if more tries
|
||||
beq SxCancel ; otherwise cancel it
|
||||
|
||||
StartBlk: jsr ChkCancel ; see if user wants to quit first
|
||||
lda #$FF ;
|
||||
sta retry2 ; set loop counter for ~3 sec delay
|
||||
jsr GetByte ; get first byte of block
|
||||
bcc StartBlk ; timed out, keep waiting...
|
||||
GotByte: cmp #ESC ; quitting?
|
||||
bne GotByte1 ; no
|
||||
lda #ECANCEL ; Error code in "A" of desired
|
||||
rts ; YES - do BRK or change to RTS if desired
|
||||
GotByte1: cmp #SOH ; start of block?
|
||||
beq BegBlk ; yes
|
||||
cmp #EOT ;
|
||||
bne BadCrc ; Not SOH or EOT, so flush buffer & send NAK
|
||||
jmp RDone ; EOT - all done!
|
||||
BegBlk: ldx #$00
|
||||
GetBlk: lda #$ff ; 3 sec window to receive characters
|
||||
sta retry2 ;
|
||||
GetBlk1: jsr GetByte ; get next character
|
||||
bcc BadCrc ; chr rcv error, flush and send NAK
|
||||
GetBlk2: sta Rbuff,x ; good char, save it in the rcv buffer
|
||||
inx ; inc buffer pointer
|
||||
cpx #$84 ; <01> <FE> <128 bytes> <CRCH> <CRCL>
|
||||
bne GetBlk ; get 132 characters
|
||||
ldx #$00 ;
|
||||
lda Rbuff,x ; get block # from buffer
|
||||
cmp blkno ; compare to expected block #
|
||||
beq GoodBlk1 ; matched!
|
||||
jsr Flush ; mismatched - flush buffer and then exit
|
||||
lda #EBLKMM ; put error code in "A" if desired
|
||||
rts ; unexpected block # - fatal error - BRK or RTS
|
||||
GoodBlk1: eor #$ff ; 1's comp of block #
|
||||
inx ;
|
||||
cmp Rbuff,x ; compare with expected 1's comp of block #
|
||||
beq GoodBlk2 ; matched!
|
||||
jsr Flush ; mismatched - flush buffer and then do BRK
|
||||
lda #EMMBLK ; put error code in "A" if desired
|
||||
rts ; bad 1's comp of block#
|
||||
GoodBlk2: jsr CalcCRC ; calc CRC
|
||||
lda Rbuff,y ; get hi CRC from buffer
|
||||
cmp crch ; compare to calculated hi CRC
|
||||
bne BadCrc ; bad crc, send NAK
|
||||
iny ;
|
||||
lda Rbuff,y ; get lo CRC from buffer
|
||||
cmp crc ; compare to calculated lo CRC
|
||||
beq GoodCrc ; good CRC
|
||||
BadCrc: jsr Flush ; flush the input port
|
||||
lda #'E'
|
||||
sta STLOC
|
||||
lda #NAK ;
|
||||
jsr Put_Chr ; send NAK to resend block
|
||||
jmp StartBlk ; start over, get the block again
|
||||
GoodCrc: jsr BlkStatus ; Display block # mod 8
|
||||
ldx #$02 ;
|
||||
lda blkno ; get the block number
|
||||
cmp #$01 ; 1st block?
|
||||
bne CopyBlk ; no, copy all 128 bytes
|
||||
lda bflag ; is it really block 1, not block 257, 513 etc.
|
||||
beq CopyBlk ; no, copy all 128 bytes
|
||||
lda ptr ; check if ptr is 0
|
||||
eor ptr+1
|
||||
beq DoAddr ; it is, take address from block
|
||||
inx ; otherwise skip first byte of load address
|
||||
bne Blk1Done ; and go skip second and keep user's load address
|
||||
DoAddr: lda Rbuff,x ; get target address from 1st 2 bytes of blk 1
|
||||
sta ptr ; save lo address
|
||||
inx ;
|
||||
lda Rbuff,x ; get hi address
|
||||
sta ptr+1 ; save it
|
||||
Blk1Done: inx ; move to length lo byte
|
||||
clc
|
||||
lda Rbuff,x ; get lo byte
|
||||
adc ptr ; and compute end address
|
||||
sta eofp ; put where caller can get to it
|
||||
inx ; move to length hi byte
|
||||
lda Rbuff,x ; get hi byte
|
||||
adc ptrh ; finish end address compute
|
||||
sta eofph ; put where caller can get to it
|
||||
inx ; point to first byte of data
|
||||
dec bflag ; set the flag so we won't get another address
|
||||
CopyBlk: ldy #$00 ; set offset to zero
|
||||
CopyBlk3: lda Rbuff,x ; get data byte from buffer
|
||||
sta (ptr),y ; save to target
|
||||
inc ptr ; point to next address
|
||||
bne CopyBlk4 ; did it step over page boundary?
|
||||
inc ptr+1 ; adjust high address for page crossing
|
||||
CopyBlk4: inx ; point to next data byte
|
||||
cpx #$82 ; is it the last byte
|
||||
bne CopyBlk3 ; no, get the next one
|
||||
IncBlk: inc blkno ; done. Inc the block #
|
||||
lda #ACK ; send ACK
|
||||
jsr Put_Chr ;
|
||||
jmp StartBlk ; get next block
|
||||
|
||||
RDone: lda #ACK ; last block, send ACK and exit.
|
||||
jsr Put_Chr ;
|
||||
jsr Flush ; get leftover characters, if any
|
||||
lda #ENOERR
|
||||
rts ;
|
||||
|
||||
BlkStatus: lda blkno ; put block # mod 8 in status display
|
||||
and #$07 ; mod 8
|
||||
ora #$30 ; convert to inverse digit
|
||||
sta STLOC ; and put on screen
|
||||
rts
|
||||
;
|
||||
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
;======================================================================
|
||||
; I/O Device Specific Routines
|
||||
;
|
||||
; Two routines are used to communicate with the I/O device.
|
||||
;
|
||||
; "Get_Chr" routine will scan the input port for a character. It will
|
||||
; return without waiting with the Carry flag CLEAR if no character is
|
||||
; present or return with the Carry flag SET and the character in the "A"
|
||||
; register if one was present.
|
||||
;
|
||||
; "Put_Chr" routine will write one byte to the output port. Its alright
|
||||
; if this routine waits for the port to be ready. its assumed that the
|
||||
; character was send upon return from this routine.
|
||||
;
|
||||
; Here is an example of the routines used for a standard 6551 ACIA.
|
||||
; You would call the ACIA_Init prior to running the xmodem transfer
|
||||
; routine.
|
||||
;
|
||||
ACIA_Data = $C088+(ACIA_SLOT*$10)
|
||||
ACIA_Status = $C089+(ACIA_SLOT*$10)
|
||||
ACIA_Command = $C08A+(ACIA_SLOT*$10)
|
||||
ACIA_Control = $C08B+(ACIA_SLOT*$10)
|
||||
|
||||
SPD_19_2_8N1 = $1F
|
||||
SPD_115_2_8N1 = $10
|
||||
|
||||
ACIA_Init: lda #SPD_115_2_8N1 ; speed/bits/etc.
|
||||
sta ACIA_Control ; control reg
|
||||
lda #$0B ; N parity/echo off/rx int off/ dtr active low
|
||||
sta ACIA_Command ; command reg
|
||||
rts ; done
|
||||
;
|
||||
; input chr from ACIA (no waiting)
|
||||
;
|
||||
Get_Chr: clc ; no chr present
|
||||
lda ACIA_Status ; get Serial port status
|
||||
and #$08 ; mask rcvr full bit
|
||||
beq Get_Chr2 ; if not chr, done
|
||||
Lda ACIA_Data ; else get chr
|
||||
sec ; and set the Carry Flag
|
||||
Get_Chr2: rts ; done
|
||||
;
|
||||
; output to OutPut Port
|
||||
;
|
||||
Put_Chr: PHA ; save registers
|
||||
Put_Chr1: lda ACIA_Status ; serial port status
|
||||
and #$10 ; is tx buffer empty
|
||||
beq Put_Chr1 ; no, go back and test it again
|
||||
PLA ; yes, get chr to send
|
||||
sta ACIA_Data ; put character to Port
|
||||
RTS ; done
|
||||
;=========================================================================
|
||||
;
|
||||
; subroutines
|
||||
;
|
||||
;
|
||||
;
|
||||
GetByte: ;lda #$00 ; wait for chr input and cycle timing loop
|
||||
;sta retry ; set low value of timing loop
|
||||
stz retry
|
||||
StartCrcLp: jsr Get_Chr ; get chr from serial port, don't wait
|
||||
bcs GetByte1 ; got one, so exit
|
||||
dec retry ; no character received, so dec counter
|
||||
bne StartCrcLp ;
|
||||
dec retry2 ; dec hi byte of counter
|
||||
bne StartCrcLp ; look for character again
|
||||
clc ; if loop times out, CLC, else SEC and return
|
||||
GetByte1: rts ; with character in "A"
|
||||
;
|
||||
Flush: lda #$70 ; flush receive buffer
|
||||
sta retry2 ; flush until empty for ~1 sec.
|
||||
Flush1: jsr GetByte ; read the port
|
||||
bcs Flush ; if chr recvd, wait for another
|
||||
rts ; else done
|
||||
;
|
||||
;=========================================================================
|
||||
;
|
||||
;
|
||||
; CRC subroutines
|
||||
;
|
||||
;
|
||||
CalcCRC: ;lda #$00 ; yes, calculate the CRC for the 128 bytes
|
||||
;sta crc ;
|
||||
;sta crch ;
|
||||
stz crc ; save 2 bytes with 'C02 code
|
||||
stz crch ;
|
||||
ldy #$02 ;
|
||||
CalcCRC1: lda Rbuff,y ;
|
||||
eor crc+1 ; Quick CRC computation with lookup tables
|
||||
tax ; updates the two bytes at crc & crc+1
|
||||
lda crc ; with the byte send in the "A" register
|
||||
eor crchi,X
|
||||
sta crc+1
|
||||
lda crclo,X
|
||||
sta crc
|
||||
iny ;
|
||||
cpy #$82 ; done yet?
|
||||
bne CalcCRC1 ; no, get next
|
||||
rts ; y=82 on exit
|
||||
;
|
||||
; The following tables are used to calculate the CRC for the 128 bytes
|
||||
; in the xmodem data blocks. You can use these tables if you plan to
|
||||
; store this program in ROM. If you choose to build them at run-time,
|
||||
; then just delete them and define the two labels: crclo & crchi.
|
||||
;
|
||||
.res $100-(* & $FF)
|
||||
; low byte CRC lookup table (should be page aligned)
|
||||
crclo:
|
||||
.byte $00,$21,$42,$63,$84,$A5,$C6,$E7,$08,$29,$4A,$6B,$8C,$AD,$CE,$EF
|
||||
.byte $31,$10,$73,$52,$B5,$94,$F7,$D6,$39,$18,$7B,$5A,$BD,$9C,$FF,$DE
|
||||
.byte $62,$43,$20,$01,$E6,$C7,$A4,$85,$6A,$4B,$28,$09,$EE,$CF,$AC,$8D
|
||||
.byte $53,$72,$11,$30,$D7,$F6,$95,$B4,$5B,$7A,$19,$38,$DF,$FE,$9D,$BC
|
||||
.byte $C4,$E5,$86,$A7,$40,$61,$02,$23,$CC,$ED,$8E,$AF,$48,$69,$0A,$2B
|
||||
.byte $F5,$D4,$B7,$96,$71,$50,$33,$12,$FD,$DC,$BF,$9E,$79,$58,$3B,$1A
|
||||
.byte $A6,$87,$E4,$C5,$22,$03,$60,$41,$AE,$8F,$EC,$CD,$2A,$0B,$68,$49
|
||||
.byte $97,$B6,$D5,$F4,$13,$32,$51,$70,$9F,$BE,$DD,$FC,$1B,$3A,$59,$78
|
||||
.byte $88,$A9,$CA,$EB,$0C,$2D,$4E,$6F,$80,$A1,$C2,$E3,$04,$25,$46,$67
|
||||
.byte $B9,$98,$FB,$DA,$3D,$1C,$7F,$5E,$B1,$90,$F3,$D2,$35,$14,$77,$56
|
||||
.byte $EA,$CB,$A8,$89,$6E,$4F,$2C,$0D,$E2,$C3,$A0,$81,$66,$47,$24,$05
|
||||
.byte $DB,$FA,$99,$B8,$5F,$7E,$1D,$3C,$D3,$F2,$91,$B0,$57,$76,$15,$34
|
||||
.byte $4C,$6D,$0E,$2F,$C8,$E9,$8A,$AB,$44,$65,$06,$27,$C0,$E1,$82,$A3
|
||||
.byte $7D,$5C,$3F,$1E,$F9,$D8,$BB,$9A,$75,$54,$37,$16,$F1,$D0,$B3,$92
|
||||
.byte $2E,$0F,$6C,$4D,$AA,$8B,$E8,$C9,$26,$07,$64,$45,$A2,$83,$E0,$C1
|
||||
.byte $1F,$3E,$5D,$7C,$9B,$BA,$D9,$F8,$17,$36,$55,$74,$93,$B2,$D1,$F0
|
||||
|
||||
; hi byte CRC lookup table (should be page aligned)
|
||||
crchi:
|
||||
.byte $00,$10,$20,$30,$40,$50,$60,$70,$81,$91,$A1,$B1,$C1,$D1,$E1,$F1
|
||||
.byte $12,$02,$32,$22,$52,$42,$72,$62,$93,$83,$B3,$A3,$D3,$C3,$F3,$E3
|
||||
.byte $24,$34,$04,$14,$64,$74,$44,$54,$A5,$B5,$85,$95,$E5,$F5,$C5,$D5
|
||||
.byte $36,$26,$16,$06,$76,$66,$56,$46,$B7,$A7,$97,$87,$F7,$E7,$D7,$C7
|
||||
.byte $48,$58,$68,$78,$08,$18,$28,$38,$C9,$D9,$E9,$F9,$89,$99,$A9,$B9
|
||||
.byte $5A,$4A,$7A,$6A,$1A,$0A,$3A,$2A,$DB,$CB,$FB,$EB,$9B,$8B,$BB,$AB
|
||||
.byte $6C,$7C,$4C,$5C,$2C,$3C,$0C,$1C,$ED,$FD,$CD,$DD,$AD,$BD,$8D,$9D
|
||||
.byte $7E,$6E,$5E,$4E,$3E,$2E,$1E,$0E,$FF,$EF,$DF,$CF,$BF,$AF,$9F,$8F
|
||||
.byte $91,$81,$B1,$A1,$D1,$C1,$F1,$E1,$10,$00,$30,$20,$50,$40,$70,$60
|
||||
.byte $83,$93,$A3,$B3,$C3,$D3,$E3,$F3,$02,$12,$22,$32,$42,$52,$62,$72
|
||||
.byte $B5,$A5,$95,$85,$F5,$E5,$D5,$C5,$34,$24,$14,$04,$74,$64,$54,$44
|
||||
.byte $A7,$B7,$87,$97,$E7,$F7,$C7,$D7,$26,$36,$06,$16,$66,$76,$46,$56
|
||||
.byte $D9,$C9,$F9,$E9,$99,$89,$B9,$A9,$58,$48,$78,$68,$18,$08,$38,$28
|
||||
.byte $CB,$DB,$EB,$FB,$8B,$9B,$AB,$BB,$4A,$5A,$6A,$7A,$0A,$1A,$2A,$3A
|
||||
.byte $FD,$ED,$DD,$CD,$BD,$AD,$9D,$8D,$7C,$6C,$5C,$4C,$3C,$2C,$1C,$0C
|
||||
.byte $EF,$FF,$CF,$DF,$AF,$BF,$8F,$9F,$6E,$7E,$4E,$5E,$2E,$3E,$0E,$1E
|
||||
;
|
||||
;
|
||||
; End of File
|
||||
;
|
53
rom4x/xmodem.md
Normal file
53
rom4x/xmodem.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# ROM 4X XModem-CRC
|
||||
|
||||
The 10/01/2018 release of ROM 4X includes XModem-CRC functionality.
|
||||
|
||||
The feature restores functionality to the SAVE and LOAD commands in AppleSoft BASIC, and re-introduces the W and R commands in the monitor.
|
||||
|
||||
The commands send/receive data through the Modem Port of the Apple //c.
|
||||
|
||||
The XModem-CRC functionality has been tested between the Apple //c and a PC running [Qodem](http://qodem.sourceforge.net) and between two Apple //cs using a null modem cable.
|
||||
|
||||
## Use of the XModem-CRC features
|
||||
|
||||
By default the data is sent at 115,200 bps 8N1. If you want to keep the current serial port speed/bits setting, hold the Closed Apple key while pressing the RETURN key for the below commands. Note that if the serial port is configured for 7 bit data, the transfer will fail.
|
||||
|
||||
**Caveats**: An XModem block is 128 bytes, and that is the minimum size that will be transmitted (extra filled with Ctrl+Z) and received (extra is copied to memory!). In the case of the receiver, multiples of 128 bytes (124 for first block) of memory will be overwritten as each block is received. 4 bytes are added to the transmitted data for a header (see below). So sending between 1 and 124 bytes of data will overwrite 124 bytes in the target machine's memory, but sending 125 bytes will overwrite 252 bytes! Keep this in mind!
|
||||
|
||||
### AppleSoft BASIC
|
||||
|
||||
To send the current program through the modem port, type `SAVE`. A 'W' will appear in the upper right corner of the screen, and after several seconds will start flashing. The save routine will wait approximately one minute for a receiver to be ready, otherwise it will exit and say "ERR". If you accidentally type SAVE, hit the ESC key and you will be returned to AppleSoft (again, with 'ERR'). Once the transfer starts, the upper right corner will cycle from 0 through 7 as each block is received (this is the lower 3 bits of the block number).
|
||||
|
||||
To receive a program through the modem port, type `LOAD`. A 'R' will appear in the upper right corner, and start flashing after a few seconds. The load routine will attempt to initiate the transfer every 3 seconds for one minute, otherwise exit with 'ERR'. If you wish to cancel the transfer, hit the ESC key. If you hit ESC before any blocks are received, your current program will stay intact. Once blocks are received, if the transfer fails or is cancelled, it will be as if NEW had been executed. A successful transfer results in the downloaded program being in memory.
|
||||
|
||||
### Monitor
|
||||
|
||||
To send data, type `xxxx.yyyyW`, as if you were using a machine with a cassette port, to send the data between addresses `xxxx` and `yyyy` (inclusive). The routine has the same wait/error conditions as the AppleSoft `SAVE` command, above.
|
||||
|
||||
To receive data, you can type `0R` to receive at the same address from which the data was written, or `zzzzR` to receive at address `zzzz` instead. The routine has the same receive/error conditions as the AppleSoft `LOAD` command, above.
|
||||
|
||||
As an example, you can copy the hi-res graphics page 1 from one machine to another via null modem cable by typing `2000.3FFFW` on the machine with the graphics and `0R` on the other machine.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Occasionally the serial port gets finicky and receiving will instantly result in an ERR. Doing a trivial `SAVE` or `W` on the receiver, and then canceling and restarting the receive, will usually resolve this.
|
||||
|
||||
## Data Format
|
||||
|
||||
The data is sent/received via the XModem-CRC protocol, **without fallback to plain XModem**. Most modern terminal programs support XModem-CRC.
|
||||
|
||||
The data is sent with a 4-byte header in the first block that contains the address and length (minus one) of the data being sent. The exact length is sent in order to avoid leaking memory when receiving an AppleSoft program, as XModem does not have a built-in means of providing the length of the transfer.
|
||||
|
||||
When receiving, the machine will continue to receive blocks regardless of the length it receives in the first block, until the sender indicates it is done sending.
|
||||
|
||||
When transferring between an Apple //c and a PC, the 4-byte header must be included when sending a file to the //c. This will be present in any file originally received from a //c via XModem-CRC, but you will need to find a way to add it to anything that did not.
|
||||
|
||||
## Credits
|
||||
|
||||
I got the idea to implement this while discussing on Facebook the fact that `SAVE` and `LOAD` on the //c jump to the ampersand vector. The Facebook "Apple II Enthusiasts" group has inspired a lot of what I put into ROM 4X/5X.
|
||||
|
||||
I'd like to thank Reactive Micro for stepping up to deliver the ROM to people who can't build/burn it on their own. I have no desire to be in the taking orders/shipping business, and I am grateful, as are others, that there is someone who is.
|
||||
|
||||
Finally, the XModem-CRC routines are modified versions of Daryl Rictor's routines available at [6502.org](http://www.6502.org/source/io/xmodem/xmodem.htm).
|
||||
|
||||
|
8
rom5x/B0_C2FC_fix_merlin_1.s
Normal file
8
rom5x/B0_C2FC_fix_merlin_1.s
Normal file
|
@ -0,0 +1,8 @@
|
|||
; Code to fix merlin incompatibility with the beep patch
|
||||
; this arises because switching to the aux firmware messes up the
|
||||
; memory map slightly. The IIc Plus WAIT routine has a fix, we adopt it here.
|
||||
.code
|
||||
.pc02
|
||||
.org $c2fc
|
||||
phx
|
||||
jmp $c9a1 ; next step
|
11
rom5x/B0_C4EE_del_key.s
Normal file
11
rom5x/B0_C4EE_del_key.s
Normal file
|
@ -0,0 +1,11 @@
|
|||
; call RDCHAR, convert DEL to space
|
||||
; for patching into GETLN1/NXTCHAR (at $FD75)
|
||||
.code
|
||||
.include "iic+.defs"
|
||||
.org $c4ee
|
||||
jsr $cced
|
||||
cmp #$ff
|
||||
bne :+
|
||||
lda #$88
|
||||
: rts
|
||||
|
10
rom5x/B0_C7FC_5x_switch.s
Normal file
10
rom5x/B0_C7FC_5x_switch.s
Normal file
|
@ -0,0 +1,10 @@
|
|||
; There's a bit of luck in the IIc Plus firmware
|
||||
; there are 4 $00 bytes at $C7FC in the main bank, of which the last
|
||||
; is "AppleTalk version" according to the ROM $03 source code
|
||||
; in the tech ref, and should be left at $00.
|
||||
; There are also 7 $00 bytes at $C7FC in the aux bank. So if we switch at
|
||||
; $C7FC, then we get a 4 bytes in the aux bank, just enough for a jump.
|
||||
.code
|
||||
.org $c7fc
|
||||
sta $c028
|
||||
|
7
rom5x/B0_C9A1_fix_merlin_2.s
Normal file
7
rom5x/B0_C9A1_fix_merlin_2.s
Normal file
|
@ -0,0 +1,7 @@
|
|||
; see B0 C2FC patch for commentary
|
||||
.code
|
||||
.pc02
|
||||
.org $c9a1
|
||||
jsr $cfe5 ; get memory config we need to fix
|
||||
jsr $c7fc ; call ROM 5X dispatch
|
||||
jmp $c2f5 ; fix memory, restore x, a=$00
|
|
@ -10,6 +10,6 @@
|
|||
.org $cff9 ; 7 bytes available here, but don't count on $CFFF
|
||||
lda #$a9 ; lda opcode
|
||||
nop ; jmp/jsr $cffa does lda #$ea
|
||||
jmp $fbdf ; jump to bell1 hijack
|
||||
jmp $c7fc ; jump to 5X dispatcher
|
||||
; total 6 bytes.
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
; Hijack the BELL1 monitor routine to do our bidding.
|
||||
; BELL1 implements the beep sound heard on reset or
|
||||
; Ctrl-G, etc. It starts with
|
||||
; LDA #$40
|
||||
; JSR WAIT ; delay .1 sec
|
||||
; followed by code to actually beep the speaker
|
||||
; In our case, BELL1 always loads the accumulator with
|
||||
; a fixed number, and executes a 3-byte instruction
|
||||
; Well, it turns out that to switch banks we need
|
||||
; 3 bytes, and as luck would have it the other bank
|
||||
; is empty here.
|
||||
; So the routine on the other side is the ROM 5X
|
||||
; dispatcher. It will take what is in the accumulator
|
||||
; and use that to determine the next action.
|
||||
; Obviously, $40 should beep the speaker, anything
|
||||
; else can do whatever we want.
|
||||
|
||||
.code
|
||||
.org $fbdf
|
||||
sta $c028
|
||||
|
13
rom5x/B0_FBE6_beep_fix.s
Normal file
13
rom5x/B0_FBE6_beep_fix.s
Normal file
|
@ -0,0 +1,13 @@
|
|||
; Fix the beep
|
||||
; inspired by http://quinndunki.com/blondihacks/?p=2471
|
||||
; see commentary in B1 FB3C patch
|
||||
.code
|
||||
.pc02
|
||||
.org $fbe6
|
||||
jmp $c2fc ; to 5X beep with merlin fix
|
||||
.res $fbef-*,$ea ; fill up the rest with NOPs
|
||||
.assert * = $fbef, error, "ROM 5X beep fix alignment problem"
|
||||
; the rts at $fbef is sacred
|
||||
|
||||
|
||||
|
6
rom5x/B0_FD75_patch_getln1.s
Normal file
6
rom5x/B0_FD75_patch_getln1.s
Normal file
|
@ -0,0 +1,6 @@
|
|||
; patch GETLN1 to call delete key handler
|
||||
.code
|
||||
.include "iic+.defs"
|
||||
.org $fd75
|
||||
jsr $c4ee
|
||||
|
5
rom5x/B1_C7FF_5x_jump.s
Normal file
5
rom5x/B1_C7FF_5x_jump.s
Normal file
|
@ -0,0 +1,5 @@
|
|||
; See B0 C7FC patch for description
|
||||
.code
|
||||
.org $c7ff
|
||||
jmp $fb3c ; to 5X dispatch
|
||||
|
|
@ -1,55 +1,56 @@
|
|||
.code
|
||||
.psc02
|
||||
.include "iic+.defs"
|
||||
.org misc5x ; max 306 bytes
|
||||
bra domenu ; Display menu
|
||||
bra dobann ; Display banner (title + By MG)
|
||||
bra gtkey ; get a key
|
||||
bra confirm ; ask SURE?
|
||||
bra ntitle ; display "Apple IIc +"
|
||||
.org misc5x ; max 306 bytes
|
||||
bra domenu ; Display menu
|
||||
bra dobann ; Display banner (title + By MG)
|
||||
bra gtkey ; get a key
|
||||
bra confirm ; ask SURE?
|
||||
bra ntitle ; display "Apple IIc +"
|
||||
dobann: jsr ntitle
|
||||
ldx #(msg2-msg1) ; msg display entry point
|
||||
jmp disp
|
||||
ldx #(msg2-msg1) ; msg display entry point
|
||||
jmp disp
|
||||
domenu: jsr ntitle ; "Apple ||c +"
|
||||
ldx #$00 ; menu start
|
||||
jsr disp ; show it
|
||||
rts
|
||||
ldx #$00 ; menu start
|
||||
jsr disp ; show it
|
||||
rts
|
||||
gtkey: lda #$60
|
||||
sta ($0),y ; cursor
|
||||
sta kbdstrb ; clr keyboard
|
||||
sta ($0),y ; cursor
|
||||
sta kbdstrb ; clr keyboard
|
||||
kbdin: lda kbd ; get key
|
||||
bpl kbdin
|
||||
sta kbdstrb ; clear keyboard
|
||||
sta ($0),y ; put it on screen
|
||||
rts
|
||||
bpl kbdin
|
||||
sta kbdstrb ; clear keyboard
|
||||
sta ($0),y ; put it on screen
|
||||
rts
|
||||
; display message, input x = message start relative to msg1
|
||||
disp: ldy #$0 ; needs to be zero
|
||||
disp: ldy #$0 ; needs to be zero
|
||||
disp0: lda msg1,x ; get message byte
|
||||
bne disp1 ; proceed if nonzero
|
||||
rts ; exit if 0
|
||||
bne disp1 ; proceed if nonzero
|
||||
rts ; exit if 0
|
||||
disp1: inx ; next byte either way
|
||||
cmp #$20 ; ' '
|
||||
bcc disp2 ; start of ptr if < 20
|
||||
eor #$80 ; invert high bit
|
||||
sta ($0),y ; write to mem
|
||||
inc $0 ; inc address low byte
|
||||
bra disp0 ; back to the beginning
|
||||
cmp #$20 ; ' '
|
||||
bcc disp2 ; start of ptr if < 20
|
||||
eor #$80 ; invert high bit
|
||||
sta ($0),y ; write to mem
|
||||
inc $0 ; inc address low byte
|
||||
bra disp0 ; back to the beginning
|
||||
disp2: sta $1 ; write address high
|
||||
lda msg1,x ; get it
|
||||
sta $0 ; write address low
|
||||
inx ; set next msg byte
|
||||
bra disp0 ; back to the beginning
|
||||
confirm: pha
|
||||
ldx #(msg3-msg1) ; ask confirm
|
||||
jsr disp
|
||||
jsr gtkey
|
||||
plx
|
||||
ora #$20 ; to lower
|
||||
cmp #$f9 ; "y"
|
||||
php
|
||||
txa
|
||||
plp
|
||||
rts
|
||||
lda msg1,x ; get it
|
||||
sta $0 ; write address low
|
||||
inx ; set next msg byte
|
||||
bra disp0 ; back to the beginning
|
||||
confirm:
|
||||
pha
|
||||
ldx #(msg3-msg1) ; ask confirm
|
||||
jsr disp
|
||||
jsr gtkey
|
||||
plx
|
||||
ora #$20 ; to lower
|
||||
cmp #$f9 ; "y"
|
||||
php
|
||||
txa
|
||||
plp
|
||||
rts
|
||||
; display "Apple IIc +" in a convoluted manner
|
||||
; we push the address of swrts/swrts2 onto the stack
|
||||
; and then the address of the title routine
|
||||
|
@ -57,30 +58,32 @@ confirm: pha
|
|||
; display "Apple IIc +", which then RTS to swrts, which
|
||||
; switches banks back to here and RTS to our caller.
|
||||
ntitle: lda #>(swrts2-1) ; put return addr of swrts/swrts2 on stack
|
||||
pha
|
||||
lda #<(swrts2-1)
|
||||
pha
|
||||
lda #>(banner-1) ; put addr of the Title routine on the stack
|
||||
pha
|
||||
lda #<(banner-1)
|
||||
pha
|
||||
jmp swrts2 ; jump to swrts2
|
||||
pha
|
||||
lda #<(swrts2-1)
|
||||
pha
|
||||
lda #>(banner-1) ; put addr of the Title routine on the stack
|
||||
pha
|
||||
lda #<(banner-1)
|
||||
pha
|
||||
jmp swrts2 ; jump to swrts2
|
||||
; msg format
|
||||
; A byte < $20 indicates high byte of address.
|
||||
; Next byte must be low byte of address. Anything
|
||||
; else are characters to display and will have their
|
||||
; upper bit inverted before being written to the screen.
|
||||
msg1 = *
|
||||
.byte $05,$06,"0 Monitor"
|
||||
.byte $05,$86,"1 Reboot"
|
||||
.byte $06,$06,"2 Zero RAM Card"
|
||||
.byte $06,$86,"3 Sys Diags"
|
||||
.byte $07,$06,"4 RAM Card Diags"
|
||||
.byte $07,$86,"5 Boot 3.5/SmartPort"
|
||||
.byte $04,$2e,"6 Boot 5.25"
|
||||
.byte $04,$ae,"7 Accelerator"
|
||||
.byte $07,$5f,"By M.G."
|
||||
msg2: .byte $07,$db,"ROM 5X 04/08/17"
|
||||
.byte $05,$ae,$00 ; cursor pos in menu
|
||||
msg3: .byte $05,$b0,"SURE? ",$00
|
||||
|
||||
.byte $05,$06,"0 Monitor"
|
||||
.byte $05,$86,"1 Reboot"
|
||||
.byte $06,$06,"2 Zero RAM Card"
|
||||
.byte $06,$86,"3 Sys Diags"
|
||||
.byte $07,$06,"4 RAM Card Diags"
|
||||
.byte $07,$86,"5 Boot 3.5/SmartPort"
|
||||
.byte $04,$2e,"6 Boot 5.25"
|
||||
.byte $04,$ae,"7 Accelerator"
|
||||
.byte $07,$5f,"By M.G."
|
||||
msg2: .byte $07,$db,"ROM 5X 12/10/17"
|
||||
.byte $05,$ae,$00 ; cursor pos in menu
|
||||
msg3: .byte $05,$b0,"SURE? ",$00
|
||||
; metadata to identify build conditions
|
||||
.dword .time
|
||||
.word .version
|
||||
|
|
|
@ -1,47 +1,44 @@
|
|||
.code
|
||||
.psc02
|
||||
.include "iic+.defs"
|
||||
.org reset5x ; max 157 bytes
|
||||
stz power2 + rx_mslot ; action = normal reset
|
||||
lda #>(rst5xrtn-1) ; common case
|
||||
pha
|
||||
lda #<(rst5xrtn-1)
|
||||
pha ; note that this stays on stack
|
||||
asl butn1 ; option (closed apple)
|
||||
bcs ckdiag
|
||||
exitrst: jmp swrts2
|
||||
.org reset5x ; max 157 bytes
|
||||
stz power2 + rx_mslot ; action = normal reset
|
||||
lda #>(rst5xrtn-1) ; common case
|
||||
pha
|
||||
lda #<(rst5xrtn-1)
|
||||
pha ; note that this stays on stack
|
||||
asl butn1 ; option (closed apple)
|
||||
bcs ckdiag
|
||||
exitrst: jmp swrts2
|
||||
; check to see if cmd_option (both apples) are down
|
||||
ckdiag: bit butn0 ; command (open apple)
|
||||
bmi exitrst ; return to RESET.X
|
||||
ckdiag: bit butn0 ; command (open apple)
|
||||
bmi exitrst ; return to RESET.X
|
||||
; present menu because only closed apple is down
|
||||
menu: jsr menu5x ; display menu
|
||||
jsr gkey5x
|
||||
cmp #$b0 ; "0"
|
||||
bne ckkey1
|
||||
ldx #$ff ; reset stack
|
||||
txs
|
||||
lda #>(monitor-1) ; monitor entry on stack
|
||||
pha
|
||||
lda #<(monitor-1)
|
||||
pha
|
||||
jmp swrts2 ; rts to enter monitor
|
||||
ckkey1: cmp #$b2 ; "2"
|
||||
beq doconf
|
||||
cmp #$b4 ; "4"
|
||||
bne ckkey2
|
||||
doconf: jsr conf5x
|
||||
bne menu ; go back to menu4x
|
||||
ckkey2: cmp #$b7 ; "7"
|
||||
bne ckkey3
|
||||
jsr $fd02 ; accelerator menu
|
||||
bra menu
|
||||
ckkey3: sec
|
||||
sbc #$b0 ; ascii->number
|
||||
bmi menu ; < 0 not valid
|
||||
cmp #$07 ; we will use 7 for accelerator later
|
||||
bpl menu ; > 7 not valid
|
||||
sta power2 + rx_mslot ; for boot5x
|
||||
stz softev + 1 ; deinit coldstart
|
||||
stz pwerdup ; ditto
|
||||
bra exitrst
|
||||
menu: jsr menu5x ; display menu
|
||||
jsr gkey5x
|
||||
cmp #$b0 ; "0"
|
||||
bne ckkey1
|
||||
ldx #$ff ; reset stack
|
||||
txs
|
||||
txa
|
||||
jmp $fb3c ; now has crash-to-monitor function
|
||||
ckkey1: cmp #$b2 ; "2"
|
||||
beq doconf
|
||||
cmp #$b4 ; "4"
|
||||
bne ckkey2
|
||||
doconf: jsr conf5x
|
||||
bne menu ; go back to menu4x
|
||||
ckkey2: cmp #$b7 ; "7"
|
||||
bne ckkey3
|
||||
jsr $fd02 ; accelerator menu
|
||||
bra menu
|
||||
ckkey3: sec
|
||||
sbc #$b0 ; ascii->number
|
||||
bmi menu ; < 0 not valid
|
||||
cmp #$07 ; we will use 7 for accelerator later
|
||||
bpl menu ; > 7 not valid
|
||||
sta power2 + rx_mslot ; for boot5x
|
||||
stz softev + 1 ; deinit coldstart
|
||||
stz pwerdup ; ditto
|
||||
bra exitrst
|
||||
|
||||
|
|
|
@ -1,45 +1,51 @@
|
|||
.code
|
||||
.psc02
|
||||
.include "iic+.defs"
|
||||
.org $fb3c ; ~165 bytes free here
|
||||
cmp #$a9 ; reset patch
|
||||
bne chk2
|
||||
jmp reset5x
|
||||
chk2: cmp #$ea ; boot patch
|
||||
bne chk3
|
||||
jmp boot5x
|
||||
.if newbeep
|
||||
chk3:
|
||||
.else
|
||||
chk3: cmp #$40 ; beep
|
||||
bne dowait
|
||||
.org $fb3c ; ~165 bytes free here
|
||||
.proc dispatch
|
||||
cmp #$a9 ; reset patch
|
||||
bne :+
|
||||
jmp reset5x
|
||||
: cmp #$ea ; boot patch
|
||||
bne :+
|
||||
jmp boot5x
|
||||
: cmp #$0c
|
||||
beq oldbelle1
|
||||
; jump to monitor
|
||||
lda #>(monitor-1) ; monitor entry on stack as return address
|
||||
pha
|
||||
lda #<(monitor-1)
|
||||
pha
|
||||
jmp swrts2 ; switch bank and rts to monitor
|
||||
.endproc
|
||||
|
||||
; "classic air raid beep"
|
||||
; inspired by http://quinndunki.com/blondihacks/?p=2471
|
||||
; jsr $fcb5 ; (new) WAIT for .1 sec delay
|
||||
jsr owait
|
||||
ldy #$c0
|
||||
obell2: lda #$0c
|
||||
jsr owait ; old wait for correct sound
|
||||
lda $c030
|
||||
dey
|
||||
bne obell2
|
||||
bra dexit ; back to caller
|
||||
.endif
|
||||
dowait: jsr $fcb5 ; do delay if anything else
|
||||
lda #>($fbe2-1) ; return to other bank here (in BELL1)
|
||||
pha ; by pushing address onto
|
||||
lda #<($fbe2-1) ; the stack
|
||||
pha
|
||||
lda #$00 ; in case someone assumes this
|
||||
dexit: jmp swrts2 ; back to other bank
|
||||
.proc oldbell
|
||||
ldy #$c0
|
||||
obell2: lda #$0c
|
||||
dowait: jsr oldwait ; old wait for correct sound, we also enter here
|
||||
lda $c030
|
||||
dey
|
||||
bne obell2
|
||||
jmp swrts2
|
||||
.endproc
|
||||
; We jump into the old bell routine mid-way because it's possible for
|
||||
; someone to want to call the bell routine with a different duration
|
||||
; by putting a custom value in Y and calling fbe4 (BELL2)
|
||||
oldbelle1 := oldbell::dowait
|
||||
|
||||
; old wait - no ACIA access to enforce delay at
|
||||
; accelerated speeds, speaker delay tkes care of it
|
||||
; accelerated speeds, speaker delay takes care of it
|
||||
; when we do the old beep
|
||||
owait: sec
|
||||
owait2: pha
|
||||
owait3: sbc #$01
|
||||
bne owait3
|
||||
pla
|
||||
sbc #$01
|
||||
bne owait2
|
||||
rts
|
||||
.proc oldwait
|
||||
sec
|
||||
owait2: pha
|
||||
owait3: sbc #$01
|
||||
bne owait3
|
||||
pla
|
||||
sbc #$01
|
||||
bne owait2
|
||||
rts
|
||||
.endproc
|
||||
|
||||
.assert * <= $fc00, error, "ROM 5X dispatch overruns $fc00"
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
.code
|
||||
.include "iic+.defs"
|
||||
.org $fbe2 ; ~29 bytes free here
|
||||
jmp $fb3c
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
# ROM 5X by MG
|
||||
|
||||
ROM 5X is a collection of enhancements to the Apple //c version 4. See the top level [README.md](../README.md) for more general information on ROM 4X and ROM 5X.
|
||||
ROM 5X is a collection of enhancements to the Apple //c Plus (ROM version 5). See the top level [README.md](../README.md) for more general information on ROM 4X and ROM 5X.
|
||||
|
||||
It adds the following features to the Apple //c:
|
||||
It adds the following features to the Apple //c Plus:
|
||||
|
||||
- Enter the monitor unconditionally.
|
||||
- Reboot the machine (enter standard boot sequence).
|
||||
- Zero the RAM card, in case it is corrupted.
|
||||
- Execute the machine and RAM card diagnostics.
|
||||
- Tell the machine to boot the SmartPort, the internal floppy drive, or an external floppy drive.
|
||||
- Menu control the built-in accelerator.
|
||||
- Tell the machine to boot the SmartPort/3.5 drive or the internal floppy drive.
|
||||
- Menu control the built-in accelerator (via main menu or ctrl+tab+reset).
|
||||
- Accelerator settings persist across resets.
|
||||
- Build option to default the system to 1 MHz.
|
||||
- Changes ctrl+esc+reset to toggle the accelerator rather than turn it off only.
|
||||
|
||||
RAM expansion cards known to work with ROM 4X include the AE RAM Express Cards (but no battery!), and the A2Heaven [RAM Express II](http://a2heaven.com/webshop/index.php?rt=product/product&product_id=146) for the original //c, and the [RAM Express II+](http://a2heaven.com/webshop/index.php?rt=product/product&product_id=144) for the memory-expandable //c and IIc Plus.
|
||||
RAM expansion cards known to work with ROM 5X include the AE RAM Express Cards (but no battery!), and the A2Heaven [RAM Express II+](http://a2heaven.com/webshop/index.php?rt=product/product&product_id=144) for the memory-expandable //c and IIc Plus.
|
||||
|
||||
# User Guide
|
||||
|
||||
|
@ -45,9 +45,10 @@ Once you have a ROM chip, generally the instructions [here](http://mirrors.apple
|
|||
|
||||
### Emulator
|
||||
|
||||
Copy the ROM into the appropriate location for your emulator. At the time of writing the only emulator I am aware of that can emulate the //c with memory expansion is [Catakig](http://catakig.sourceforge.net/) for MacOS. It's a bit older of an emulator but it runs fine on newer MacOSes.
|
||||
Copy the ROM into the appropriate location for your emulator. As of July 2018, the following emulators are known to successfully emulate the Apple IIc Plus:
|
||||
|
||||
MAME's Apple //c emulation may work, but I have not tried it.
|
||||
* [Leon Bottou](https://github.com/leonbottou)'s "universal" versions of GSPlus and KEGS.
|
||||
* MAME after [this commit](https://github.com/mamedev/mame/commit/31aaae7491ea4233de75456af178054e650f4344).
|
||||
|
||||
## Operation
|
||||
|
||||
|
@ -132,7 +133,7 @@ It may work with other ROM dumps, it will *not* work with any other ROM versions
|
|||
|
||||
The Rakefile will download the file from a well-known location if it is not already present. It also verifies the checksum.
|
||||
|
||||
Now you will need a 65C02 cross assembler. The current codebase is developed using ca65 from the [cc65](http://www.cc65.org/) project. (Note: The code was developed originally using [xa](http://www.floodgap.com/retrotech/xa/)).
|
||||
Now you will need a 65C02 cross assembler. The current codebase is developed using ca65 from the [cc65](http://www.cc65.org/) project. Only the assembler and linker are required. Older versions may complain about argument order, generally versions identifying as "2.16" built from the ca65 git master branch work fine.
|
||||
|
||||
Finally you will need [Ruby](https://www.ruby-lang.org/en/) and [Rake](https://github.com/ruby/rake).
|
||||
|
||||
|
@ -144,7 +145,6 @@ If you intend to build an image for a 512-kbit chip such as the SST27SF512, do `
|
|||
|
||||
### Build Options
|
||||
|
||||
|
||||
There are some build options in accel5x.s - some functional, others needing more
|
||||
work, the most popular of which will no doubt be the option to reset the system
|
||||
with the accelerator in the disabled state. The "extra commands" option will
|
||||
|
@ -155,28 +155,21 @@ experimental purposes.
|
|||
|
||||
### First Thing's First
|
||||
|
||||
Sadly, there are no working emulations of the IIc Plus at this time.
|
||||
See above for the list of working emulators that can run the Apple IIc Plus firmware.
|
||||
|
||||
Sice you must test on a real machine, be aware that the ROM socket is not rated for a large number of insertions and you *will* break something after a while. You may consider putting a machine-pin DIP socket or a ZIF socket into the CPU socket position. This can be done by desoldering the original socket if you have the skills, or by plugging the new socket into the existing CPU socket. If you do do the latter you should consider the new socket permanent as the socket pins are thicker than a ROM chip's and removing it may leave the socket in such a state as to not be able to make good contact with a subsequent chip.
|
||||
If you will test on a real machine, be aware that the ROM socket is not rated for a large number of insertions and you *will* break something after a while. You may consider putting a machine-pin DIP socket or a ZIF socket into the CPU socket position. This can be done by desoldering the original socket if you have the skills, or by plugging the new socket into the existing CPU socket. If you do do the latter you should consider the new socket permanent as the socket pins are thicker than a ROM chip's and removing it may leave the socket in such a state as to not be able to make good contact with a subsequent chip.
|
||||
|
||||
### Nitty Gritty
|
||||
|
||||
There are almost no free bytes in the main bank of the IIc Plus firmware, so I had to get creative to get into the alternate bank, where I then had to split the code up across multiple smaller free spaces due to the massive 3.5 drive handling code. Ironically this makes the code larger as well.
|
||||
|
||||
For those interested, I hijack the monitor BEEP1 routine. The beep routine has an LDA #$40 and then calls WAIT with this value for a .1 second delay, presumably so that multiple beeps are distinct from each other.
|
||||
|
||||
I patch the JSR WAIT to be STA $C028, which switches to the other bank. The code in the other bank checks the accumulator and for two values calls either reset5x or boot5x, for a third value ($40 loaded by BEEP1) does the classic Apple II "air raid" beep sound, and for any other value executes the WAIT
|
||||
(assuming that we got there from BEEP1) and returns back to BEEP1.
|
||||
|
||||
Then, in only 6 bytes I can create two entry points that load the right values into the A register that we need for the reset or boot routines, and then jump to the above patch.
|
||||
|
||||
### Apple //c Technical Reference and other Documentation
|
||||
|
||||
You need this.
|
||||
|
||||
The Apple //c Technical Reference Manual that is available on the internet has the firmware listing for ROM 3. ROM 4 fixes a few bugs that were in ROM 3, including with the memory card driver. The changes are minor and affect some of the offsets of routines in the RAM card support, but it is easy to figure them out.
|
||||
The Apple //c Technical Reference Manual that is available on the internet has the firmware listing for ROM 3. ROM 4 fixes a few bugs that were in ROM 3, including with the memory card driver. The changes are minor and affect some of the offsets of routines in the RAM card support, but it is easy to figure them out. ROM 5 adds the 3.5 drive code, but largely leaves the main firmwware bank untouched.
|
||||
|
||||
[This](http://www.1000bit.it/support/manuali/apple/technotes/memx/tn.memx.1.html) tech note is also helpful as it documents the screen holes and some of the card behavior including under what conditions it reformats. Though the power2 byte is *not* used by the Apple //c code -- it is commented out in the firmware listings in the Technical Reference. ROM 4X uses it for the menu function.
|
||||
[This](http://www.1000bit.it/support/manuali/apple/technotes/memx/tn.memx.1.html) tech note is also helpful as it documents the screen holes and some of the card behavior including under what conditions it reformats. Though the power2 byte is *not* used by the Apple //c code -- it is commented out in the firmware listings in the Technical Reference. ROM 5X uses it for the menu function.
|
||||
|
||||
[This](http://www.1000bit.it/support/manuali/apple/technotes/aiic/tn.aiic.5.html) technical note is a little less helpful for this project.
|
||||
|
||||
|
@ -216,6 +209,6 @@ Check each item, the expectation is that the sytem does what is listed in the me
|
|||
|
||||
### Ideas for Future
|
||||
|
||||
- Replace Apple Slinky code with RamFactor code. (Difficulty: Very Hard or Impossible)
|
||||
- Replace Apple Slinky code with RamFactor code. (Difficulty: Hard. May require sacrificing the diagnostics.)
|
||||
|
||||
|
||||
|
|
12
rom5x/options/mame/B0_C500_MAME_no_35_1.s
Normal file
12
rom5x/options/mame/B0_C500_MAME_no_35_1.s
Normal file
|
@ -0,0 +1,12 @@
|
|||
; remove 3.5" functionality for MAME
|
||||
; this bit causes slot 5 boot to go to do 6 boot instead
|
||||
; and also overwrites the firmware protocol bits to not
|
||||
; identify as a block device.
|
||||
.code
|
||||
.include "iic+.defs"
|
||||
.org $c500
|
||||
ldx #$00
|
||||
lda #$c6
|
||||
stx $00
|
||||
sta $01
|
||||
jmp ($00)
|
8
rom5x/options/mame/B0_C5F8_MAME_no_35_2.s
Normal file
8
rom5x/options/mame/B0_C5F8_MAME_no_35_2.s
Normal file
|
@ -0,0 +1,8 @@
|
|||
; remove 3.5" functionality for MAME
|
||||
; this bit removes protocol converter initialization
|
||||
; so the 3.5" drive code is never called
|
||||
.code
|
||||
.include "iic+.defs"
|
||||
.org $c5f8
|
||||
rts
|
||||
|
6
rom5x/options/mame/B0_C61A_MAME_no_35_3.s
Normal file
6
rom5x/options/mame/B0_C61A_MAME_no_35_3.s
Normal file
|
@ -0,0 +1,6 @@
|
|||
; remove 3.5" functionality for MAME
|
||||
; this bit removes some 3.5" cruft added to the 5.25" boot code
|
||||
.code
|
||||
.include "iic+.defs"
|
||||
.org $c61a
|
||||
lda $c089,x
|
Loading…
Reference in New Issue
Block a user