; ============================================================================== ; Apple II [$D0 ROM] (341-0016) - Programmer's Aid #1 [1978] ; ------------------------------------------------------------------------------ ; Part 5 [$D5B0~$D691]: RAM Test Routine by Steve Wozniak [WOZ], 1977-06; ; Copyright (c) 1978 by Apple Computer Inc. All Rights Reserved ; ------------------------------------------------------------------------------ ; Instructions are in the Programmer's Aid #1 Installation and Operating Manual ; ============================================================================== ; Analyzed (via McFadden's SourceGen) by James Davis [Last Updated: 2020/06/15] ; ============================================================================== ; ; RAM Test Routine Equates: ; DATA EQU $00 {addr/1} ;[Normal] Test DATA ($00|$FF) NDATA EQU $01 {addr/1} ;Inverted Test NDATA ($FF|$00) TESTD EQU $02 {addr/1} ;Gallop TEST Data (see below) R3L EQU $06 {addr/1} ;Auxiliary or Sweet 16 Register #3, Low R3H EQU $07 {addr/1} ;Auxiliary or Sweet 16 Register #3, High R4L EQU $08 {addr/1} ;Auxiliary or Sweet 16 Register #4, Low R4H EQU $09 {addr/1} ;Auxiliary or Sweet 16 Register #4, High R5L EQU $0A {addr/1} ;Auxiliary or Sweet 16 Register #5, Low R5H EQU $0B {addr/1} ;Auxiliary or Sweet 16 Register #5, High R6L EQU $0C {addr/1} ;Auxiliary or Sweet 16 Register #6, Low R6H EQU $0D {addr/1} ;Auxiliary or Sweet 16 Register #6, High R7L EQU $0E {addr/1} ;Auxiliary or Sweet 16 Register #7, Low R7H EQU $0F {addr/1} ;Auxiliary or Sweet 16 Register #7, High A1H EQU $3D {addr/1} ;Start of Test Block Address, High A2L EQU $3E {addr/2} ;Length (Pages) [from Monitor] INBUFF EQU $0200 ;Input Buffer (6502 Page 2) USRADR EQU $03F8 ;Monitor User Command (Ctrl-Y) Vector PRBYTE EQU $FDDA ;Print A-Reg as Two-Digit Hex Number COUT EQU $FDED ;Print A-Reg to Output Device PRERR EQU $FF2D ;Print "ERR" & Sound Bell BELL EQU $FF3A ;Monitor Sound Bell Subroutine ORG $D5B0 ; ; ============================================================================== ; Monitor User Command (Ctrl-Y) Vector Setup Routine: ; ============================================================================== ; Shared by: RAM Test, Relocater, & Tape Verify Routines ; ------------------------------------------------------------------------------ ; *v* (Not in "RAMTEST" listing in the "Installation and Operating Manual"!) *v* ; ------------------------------------------------------------------------------ ; ; ----------------------------------- ;Setup Mon User Cmd (Ctrl-Y) Vector: D5B0: 8D F9 03 SETUSRADR STA USRADR+1 ;Preset User Address, Low D5B3: 8C FA 03 STY USRADR+2 ;Preset User Address, High D5B6: A9 4C LDA #$4C ;Get JMP OpCode D5B8: 8D F8 03 STA USRADR ;Preset to JMP OpCode D5BB: 60 RTS ;Return to Caller ; ============================================================================== ; RAM Test Routines: ; ============================================================================== ; ; ------------------------------------------------------------------------------ ; Set Monitor User Command (Ctrl-Y) Vector to RAM Test Routine Location: ; ------------------------------------------------------------------------------ ; ; *** Typing "D5BCG <Return>" at Monitor Prompt (*) init's the RAM Test Program. ; ; ----------------------------------- ;Setup Mon User Cmd (Ctrl-Y) Location: D5BC: A9 C3 SETRTCYV LDA #<RAMTEST ;Get RAM Test Routine Address, Low D5BE: A0 D5 LDY #>RAMTEST ;Get RAM Test Routine Address, High D5C0: 4C B0 D5 JMP SETUSRADR ;Setup Mon User Cmd (Ctrl-Y) Vector ; ============================================================================== ; RAM Test Routine: ; ============================================================================== ; ; *** Typing "Adrs.Pgs Ctrl-Y <Return>" at the Monitor Prompt (*) tests RAM, ; starting at the hexadecimal address (Adrs) given, and continuing through the ; number of hexadecimal pages (Pgs) given. The starting address (Adrs) low byte ; must be typed in, but its value (<> $00) is ignored in this routine, so RAM ; Tests always start & end at a 6502 page boundary. Pgs must be <= Adrs page. ; See additional instructions in the "Programmer's Aid #1 Installation and ; Operating Manual" (Ch.6 RAM Test, Pages 29~34). ; ; ----------------------------------- ;Main (Ctrl-Y) Entry Point: D5C3: A9 00 RAMTEST LDA #0 ;Set [Normal] Test DATA = Zero D5C5: 20 D0 D5 JSR RAMTEST0 ;Test RAM with [Normal] Test DATA D5C8: A9 FF LDA #$FF ;Set [Normal] Test DATA = ($FF|255|-1) D5CA: 20 D0 D5 JSR RAMTEST0 ;Test RAM with [Normal] Test DATA D5CD: 4C 3A FF JMP BELL ;Sound Bell (Beep); Returns to Caller ; ----------------------------------- ;Initialize DATA & Inverse NDATA Safes: D5D0: 85 00 RAMTEST0 STA DATA ;Save Test DATA ($00|$FF) D5D2: 49 FF EOR #%11111111 ;Invert Test DATA ($00|$FF)->($FF|$00) D5D4: 85 01 STA NDATA ;Save Inverted Test NDATA ($FF|$00) ; ----------------------------------- ;Initialize Pointers: D5D6: A5 3D LDA A1H ;Get Start of Test Block Address, High D5D8: 85 07 STA R3H ;Init Pointer: Aux-Register #3, High D5DA: 85 09 STA R4H ;Init Pointer: Aux-Register #4, High D5DC: 85 0B STA R5H ;Init Pointer: Aux-Register #5, High D5DE: A0 00 LDY #0 ;Get Start of Test Block Address, Low ; ;^[A1L is ignored, so RAM Tests always ] ; ; [start & end at a 6502 Page Boundary!] D5E0: 84 06 STY R3L ;Init Pointer: Aux-Register #3, Low D5E2: 84 08 STY R4L ;Init Pointer: Aux-Register #4, Low D5E4: 84 0A STY R5L ;Init Pointer: Aux-Register #5, Low D5E6: A6 3E LDX A2L ;Get Length (Pages) [from Monitor] ; ----------------------------------- ;Set Entire Test Block (Bytes)=(DATA): D5E8: A5 00 LDA DATA ;Retrieve Test DATA ($00|$FF) D5EA: 91 08 RAMTEST1 STA (R4L),Y ;Store (DATA) in each Byte of Test Block D5EC: C8 INY ;Advance Byte Pointer [0..255] D5ED: D0 FB BNE RAMTEST1 ;Loop until 256 Memory Bytes have been Set D5EF: E6 09 INC R4H ;Advance Page Pointer D5F1: CA DEX ;Reduce Page Counter (Length) D5F2: D0 F6 BNE RAMTEST1 ;Loop until all Pages are Done ; ----------------------------------- ;Verify Entire Test Block (Bytes)=(DATA): D5F4: A6 3E LDX A2L ;Get Length (Pages) [from Monitor] D5F6: B1 06 RAMTEST2 LDA (R3L),Y ;Retrieve Memory Byte from Test Block D5F8: C5 00 CMP DATA ;Assure it is Correct [(Memory) = (DATA)] D5FA: F0 13 BEQ RAMTEST3 ;Branch if it is Correct [(A) = (DATA)] ; ----------------------------------- ;Else, Memory is Incorrect; Handle Error; ; ;Print Bad Memory's Adrs & Bit-Error Data: D5FC: 48 PHA ;Preserve Bad (Memory Failure) Data ; ;Print Memory Address: D5FD: A5 07 LDA R3H ;Get Start of Test Block Address, High D5FF: 20 DA FD JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number D602: 98 TYA ;Get Byte Pointer D603: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space ; ;Print Expected Data & Bad Memory Data: D606: A5 00 LDA DATA ;Retrieve Test DATA ($00|$FF) D608: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space D60B: 68 PLA ;Retrieve Bad (Memory Failure) Data ; ------------------------------------------------------------------------------ D60C: 20 92 D6 JSR RTBADMEM ;Print Bad Memory Chip's Location: The ... ; ;Row & Column on Apple II Motherboards ; ------------------------------------------------------------------------------ ; The line above is code from an actual PA1 ROM. It is changed from that in ; the "Programmer's Aid #1 Installation and Operating Manual" "RAMTEST" listing ; (pages 84~86). [$D60C:2092D6 JSR $D692 versus 207FD6 JSR $D67F, respectively] ; ------------------------------------------------------------------------------ ; [versus] JSR RTPRBYCR ;Print (A):[##], Error + Beep, Space + CR; ; ------------------------------------------------------------------------------ ; ; ----------------------------------- ;Continue Test Block Verification: D60F: C8 RAMTEST3 INY ;Advance Byte Pointer [0..255] D610: D0 E4 BNE RAMTEST2 ;Loop until 256 Bytes have been Verified D612: E6 07 INC R3H ;Advance Byte Pointer [0..255] D614: CA DEX ;Reduce Page Counter (Length) D615: D0 DF BNE RAMTEST2 ;Loop until all Pages are Done ; ----------------------------------- ;Set Entire Test Block (Bytes)=(NDATA) & ; ;Gallop Through Memory Bytes [Test Loops]: ; ;^[Testing Memory Test Bytes for Changes ] ; ;^[when Neighboring Address Bytes are Set] D617: A6 3E LDX A2L ;Get Length (Pages) [from Monitor] D619: A5 01 RAMTEST4 LDA NDATA ;Get Inverted Test NDATA ($FF|$00) D61B: 91 0A STA (R5L),Y ;Store (NDATA) in each Byte of Test Block ; ------------------------- ;Setup Gallop Bit Mask (Aux-Register #6): D61D: 84 0D STY R6H ;Set Bit Mask, High = Byte Ptr [0..255] D61F: 84 0C STY R6L ;Set Bit Mask, Low = Byte Ptr [0..255] D621: E6 0C INC R6L ;Advance Gallop Bit Mask, Low [1..256|0] ; ----------------------------------- ;Gallop Through Memory Bytes [Test Loop]: D623: A5 01 RAMTEST5 LDA NDATA ;Retrieve Inverted Test NDATA ($FF|$00) D625: 20 45 D6 JSR RAMTEST6 ;Gallop with Inverted Test NDATA D628: A5 00 LDA DATA ;Retrieve [Normal] Test DATA ($00|$FF) D62A: 20 45 D6 JSR RAMTEST6 ;Gallop with [Normal] Test DATA ; ------------------------- ;Shift Gallop Bit Mask (Aux-Register #6) ; ;to Test Next Neighbor Bit: D62D: 06 0C ASL R6L ;Shift Gallop Test Bit Mask, Low D62F: 26 0D ROL R6H ;Shift Gallop Test Bit Mask, High D631: A5 0D LDA R6H ;Retrieve Gallop Test Bit Mask, High D633: C5 3E CMP A2L ;Is Bit-Mask > Length? (Pages) D635: 90 EC BCC RAMTEST5 ;NO, Loop if Not Done D637: A5 00 LDA DATA ;YES, Done; Retrieve Test DATA ($00|$FF) D639: 91 0A STA (R5L),Y ;Restore Memory Test Byte D63B: E6 0A INC R5L ;Advance Memory Byte Pointer D63D: D0 DA BNE RAMTEST4 ;Loop until 256 Bytes have been Tested D63F: E6 0B INC R5H ;Advance Memory Page Pointer D641: CA DEX ;Reduce Page Counter (Length) D642: D0 D5 BNE RAMTEST4 ;Loop until all Pages are Done D644: 60 RT_RTS1 RTS ;Return to Caller ; ----------------------------------- ;Gallop Through Memory Bytes [Test S/R]: D645: 85 02 RAMTEST6 STA TESTD ;Save Gallop TEST Data ; ------------------------- ;Set Pointer (Aux-Register #4) ; ;for Neighbor Address (1-Bit Difference): D647: A5 0A LDA R5L ;Get Memory Byte Pointer D649: 45 0C EOR R6L ;Set (A)=(R5L XOR R6L) D64B: 85 08 STA R4L ;Set Pointer (Aux-Register #4, Low) D64D: A5 0B LDA R5H ;Get Memory Page Pointer D64F: 45 0D EOR R6H ;Set (A)=(R5H XOR R6H) D651: 85 09 STA R4H ;Set Pointer (Aux-Register #4, High) ; ------------------------- ;Test Memory Test Bytes for Changes ; ;when Neighboring Address Bytes are Set: D653: A5 02 LDA TESTD ;Retrieve Gallop TEST Data D655: 91 08 STA (R4L),Y ;Set Neighbor Memory Byte = TEST Data D657: B1 0A LDA (R5L),Y ;Retrieve Memory Test Byte (Different Ptr) D659: C5 01 CMP NDATA ;Check for a Change: is (A)=(NDATA)? D65B: F0 E7 BEQ RT_RTS1 ;Return to Caller if OK: (A)=(NDATA) ; ----------------------------------- ;Else, Test Byte is Changed; Handle Error: D65D: 48 PHA ;Preserve Bad (Memory Failure) Data D65E: A5 0B LDA R5H ;Get Memory Test Page Pointer D660: 20 DA FD JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number D663: A5 0A LDA R5L ;Get Memory Test Byte Pointer D665: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space D668: A5 01 LDA NDATA ;Retrieve Inverted Test NDATA ($FF|$00) D66A: 91 0A STA (R5L),Y ;Replace/Correct Bad Memory with NDATA D66C: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space D66F: 68 PLA ;Retrieve Bad (Memory Failure) Data ; ------------------------------------------------------------------------------ ; ** This jump causes a crash into the Monitor unless there is code at $02CB! ** ; ** There is info about this out in Cyberspace, but I have not found it yet! ** ; ------------------------------------------------------------------------------ D670: 4C CB 02 JMP INBUFF+203 ;Go to User Routine near End of Input Page ; ------------------------------------------------------------------------------ ; The line above is code from an actual PA1 ROM. It is changed from that in ; the "Programmer's Aid #1 Installation and Operating Manual" "RAMTEST" listing ; (pages 84~86). [$D670:4CCB02 JMP $02CB versus 208AD6 JSR $D68A, respectively] ; ------------------------------------------------------------------------------ ; [versus] JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space ; ------------------------------------------------------------------------------ ; ** The following cannot happen unless the User Routine jumps back to $D673! ** ; ------------------------------------------------------------------------------ ; ; ----------------------------------- ;Continue/Finish Handling Error: D673: A5 09 RTERRFIN LDA R4H ;Get Neighbor Memory Test Page Pointer D675: 20 DA FD JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number D678: A5 08 LDA R4L ;Get Neighbor Memory Test Byte Pointer D67A: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space D67D: A5 02 LDA TESTD ;Retrieve Gallop TEST Data ; ; ------------------------------------------------------------------------------ ; The following subroutine was used as such (see "[versus]" above at $D60C) in ; the "Programmer's Aid #1 Installation and Operating Manual" "RAMTEST" listing ; (pages 84~86). Now, in PA1 ROMs, it is not used as a separate subroutine. ; ------------------------------------------------------------------------------ ; ; ----------------------------------- ;Print (A):[##], Error + Beep, Space + CR: D67F: 20 8A D6 RTPRBYCR JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space D682: 20 2D FF JSR PRERR ;Print "ERR" & Sound Bell ; ; ------------------------------------------------------------------------------ ; ** (End of what cannot happen unless the User Routine jumps back to $D673!) ** ; ------------------------------------------------------------------------------ ; D685: A9 8D RTCROUT LDA #$8D ;Get a Carriage Return (Ctrl-M) Character D687: 4C ED FD JMP COUT ;Print A-Reg; Returns to Caller ; ; ----------------------------------- ;Print (A):[as Two-Digit Hex #] & a Space D68A: 20 DA FD RTPRBYSP JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number D68D: A9 A0 LDA #' ' | $80 ;Get a Blank|Space Character D68F: 4C ED FD JMP COUT ;Print A-Reg; Returns to Caller ; ============================================================================== ; *v* (End of "RAMTEST" listing in the "Installation and Operating Manual"!) *v* ; ============================================================================== ; 03F8:4C C3 D5 USRADR JMP RAMTEST ;Entry from Monitor (Ctrl-Y) <-[Renamed] ; ------------------------------------------------------------------------------ ; ; ; ============================================================================== ; Print Bad Memory Chip's Row & Column Location on Apple II Moterboards: ; ============================================================================== ; *v* (Not in "RAMTEST" listing in the "Installation and Operating Manual"!) *v* ; ------------------------------------------------------------------------------ ; ; ----------------------------------- ;Flag Bad Bits in Bad Memory Byte: D692: 84 0F RTBADMEM STY R7H ;Save Byte Pointer [0..255] D694: 85 0E STA R7L ;Save Bad (Memory Failure) Data D696: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space D699: 20 2D FF JSR PRERR ;Print "ERR" & Sound Bell D69C: A5 00 LDA DATA ;Get [Normal] Test DATA ($00|$FF) D69E: 45 0E EOR R7L ;Compute [(Test DATA) XOR (Bad DATA)] D6A0: 85 0E STA R7L ;Save XOR Result = Bad Bits in Memory Byte ; ----------------------------------- ;Determine Chip's Motherboard Location: D6A2: A0 07 LDY #7 ;Init Loop Counter (for 8 Iterations) D6A4: 46 0E RTLOCLOOP LSR R7L ;Is Current Least Significant Bit Set? D6A6: 90 23 BCC RTLOCNEXT ;Branch if Not a Bad Bit in Memory Byte ; ----------------------------------- ;Else, there is a Bad Bit in Memory Byte; ; ;Print Chip's (Row/Column) Location: D6A8: A9 A0 LDA #' ' | $80 ;Get a Blank/Space Character D6AA: 20 ED FD JSR COUT ;Print A-Reg to Output Device D6AD: A5 3D LDA A1H ;Get Start of Test Block Address, High D6AF: C9 50 CMP #$50 ;Set Carry if [(A)>=(80)] D6B1: A9 C4 LDA #'D' | $80 ;Get a 'D' Character D6B3: 69 00 ADC #0 ;Add Carry from Comparison D6B5: 20 ED FD JSR COUT ;Print Motherboard Row [(A)=('D'|'E')] ; ;^[How does this print 'C' (for Row 'C')?] D6B8: A9 AD LDA #'-' | $80 ;Get a Dash Character D6BA: 20 ED FD JSR COUT ;Print A-Reg to Output Device D6BD: 98 TYA ;Get Loop Counter: (Y)=(7,6,5,4,3,2,1,0)= ; ;^[(Y)=(Index to get Motherboard Column)] D6BE: D0 05 BNE RTLOCPRDN ;Skip prefixing '1' character when (Y>0) D6C0: A9 B1 LDA #'1' | $80 ;Get a '1' char to print '10' when (Y=0) D6C2: 20 ED FD JSR COUT ;Print A-Reg to Output Device D6C5: B9 D3 D6 RTLOCPRDN LDA DECNUMS,Y ;Get Decimal Number (Motherboard Column)= ; ;^[(A)=(3,4,5,6,7,8,9,0):(0 is column 10)] D6C8: 20 ED FD JSR COUT ;Print A-Reg to Output Device D6CB: 88 RTLOCNEXT DEY ;Reduce Loop Counter D6CC: 10 D6 BPL RTLOCLOOP ;Loop until [(Y)=(-1)] D6CE: A4 0F LDY R7H ;Retrieve Byte Pointer [0..255] D6D0: 4C 85 D6 JMP RTCROUT ;Print Carriage Return; Returns to Caller ; ------------------------------------------------------------------------------ ; Decimal Numbers Table: For RAM Chip Columns on A2 Motherboards ; ------------------------------------------------------------------------------ ; [RAM Chip Rows = (C|D|E), Columns = (3,4,5,6,7,8,9,10)] ; ; ----------------------------------- ;RAM Chip Columns: D6D3: B0 DECNUMS DFB '0' | $80 ;RAM Chip Column 10 D6D4: B9 DFB '9' | $80 ;RAM Chip Column 9 D6D5: B8 DFB '8' | $80 ;RAM Chip Column 8 D6D6: B7 DFB '7' | $80 ;RAM Chip Column 7 D6D7: B6 DFB '6' | $80 ;RAM Chip Column 6 D6D8: B5 DFB '5' | $80 ;RAM Chip Column 5 D6D9: B4 DFB '4' | $80 ;RAM Chip Column 4 D6DA: B3 DFB '3' | $80 ;RAM Chip Column 3 D6DB: B2 DFB '2' | $80 ;Not a RAM Chip Column D6DC: B1 DFB '1' | $80 ;Not a RAM Chip Column