Compare commits

...

14 Commits

Author SHA1 Message Date
mgcaret 55c9a71e0d
mention XModem feature 2018-10-17 14:47:04 -07:00
mgcaret 4e38bdf5de
caveat regarding receiver memory 2018-10-15 15:53:58 -07:00
mgcaret a14af25f96 Docs updates for xmodem 2018-10-13 14:18:13 -07:00
mgcaret 65c2a4c7d0 xmodem: don't set up ACIA if closed apple held 2018-10-07 12:41:08 -07:00
mgcaret 464ce2e99d XModem: remove a couple of debug insns 2018-10-05 23:52:32 -07:00
mgcaret 7383c05f9e xmodem: 'C02 optimizations, applesoft routine error handing improved 2018-10-05 23:43:31 -07:00
mgcaret 4affcb82e4 XModem 2018-10-01 16:35:21 -07:00
mgcaret 264bf5ce0d reorganize dispatching to work like 5X 2018-09-29 23:56:04 -07:00
mgcaret ee1faf6cc7 modify dist.sh 2018-09-29 15:51:50 -07:00
mgcaret 8ea78bd613 update reade for 4X 2018-09-18 09:11:47 -07:00
mgcaret 40f2ae5ac3 update reade for 4X 2018-09-18 09:10:01 -07:00
mgcaret ea0b4acb9d
Clean up README.md some more for ROM 5X. 2018-08-02 14:16:21 -07:00
mgcaret 8e0a7007a3
Update README.md, referencing information from #8. 2018-08-02 14:04:31 -07:00
mgcaret a7c0aa2163 READMEs updated as request in #7 2018-01-02 12:51:27 -08:00
21 changed files with 1026 additions and 107 deletions

View File

@ -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.

View File

@ -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}"

View File

@ -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

View File

@ -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
View 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
View 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
View 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

View 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
View 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

View File

@ -1,6 +1,7 @@
; patch PWRUP to call boot4x
.code
.include "iic.defs"
.code
.org $fab4
nop
jmp gobt4x

View File

@ -1,6 +1,7 @@
; patch RESET.X to call reset4x
.code
.include "iic.defs"
.code
.org $fac8
jmp gorst4x

View File

@ -0,0 +1,7 @@
.pc02
.include "iic.defs"
.code
.org $FF73
.ifdef EN_XMODEM
jsr $CFE5 ; R/W patch
.endif

View File

@ -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
View File

@ -0,0 +1,6 @@
; See B0 C7FC patch for description
.include "iic.defs"
.code
.org $c7ff
jmp rom4x_disp

View File

@ -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

View File

@ -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

View File

@ -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).

View File

@ -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
View 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
View 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).

View File

@ -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.)