├── pictures ├── README.md ├── 4K-test.png ├── apple2dead.gif ├── grid errors.jpg ├── language_card.jpg └── A2 logic board RAM.jpg ├── apple2dead.srcdsk ├── iz8dwf ├── a2vmemnoram.bin └── a2vmemnoram.asm ├── .gitignore ├── feature_requests.md ├── a2_f8rom.cfg ├── Makefile ├── inc ├── a2constants.inc ├── a2console.asm ├── a2macros.inc ├── marchu_zpsp.asm └── marchu.asm ├── apple2dead.asm ├── README.md └── LICENSE.md /pictures/README.md: -------------------------------------------------------------------------------- 1 | Just some pictures for use in the main readme 2 | -------------------------------------------------------------------------------- /apple2dead.srcdsk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterblack1/appleII_deadtest/HEAD/apple2dead.srcdsk -------------------------------------------------------------------------------- /pictures/4K-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterblack1/appleII_deadtest/HEAD/pictures/4K-test.png -------------------------------------------------------------------------------- /iz8dwf/a2vmemnoram.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterblack1/appleII_deadtest/HEAD/iz8dwf/a2vmemnoram.bin -------------------------------------------------------------------------------- /pictures/apple2dead.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterblack1/appleII_deadtest/HEAD/pictures/apple2dead.gif -------------------------------------------------------------------------------- /pictures/grid errors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterblack1/appleII_deadtest/HEAD/pictures/grid errors.jpg -------------------------------------------------------------------------------- /pictures/language_card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterblack1/appleII_deadtest/HEAD/pictures/language_card.jpg -------------------------------------------------------------------------------- /pictures/A2 logic board RAM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterblack1/appleII_deadtest/HEAD/pictures/A2 logic board RAM.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | roms/ 2 | cfg/ 3 | old/ 4 | *.lst 5 | *.o 6 | # *.bin 7 | *.map 8 | 341-0020-00.f8 9 | *.sym 10 | config.inc 11 | *.bin 12 | !a2vmemnoram.bin 13 | *.zip 14 | *.html 15 | *.dsk -------------------------------------------------------------------------------- /feature_requests.md: -------------------------------------------------------------------------------- 1 | ### Adrian's wishlist 2 | 3 | - Port to the C64 because the Commodore dead test is JUNK. (I will work on this -Adrian) 4 | 5 | - Test the language card memory 6 | 7 | - Test the AUX ram on the IIe/IIc 8 | 9 | 10 | #### Done: 11 | 12 | - Longer pause after all RAM good. 5 seconds? 13 | 14 | - Beep out bank error and bit error, in case screen isn't working 15 | 16 | - After ZP/stack test, when printing the welcome banner with RAM size, could we print all 255 characters on screen as well? 17 | 18 | - This will let people know if their character generator supports lower-case or not. 19 | 20 | #### No longer applicable: 21 | 22 | - Welcome screen should print may want to add the message: 23 | 24 | BANK ERROR REPORTS ACCURATE FOR APPLE ][ AND ][+ 25 | -------------------------------------------------------------------------------- /a2_f8rom.cfg: -------------------------------------------------------------------------------- 1 | FEATURES { 2 | STARTADDRESS: default = $F800; 3 | } 4 | SYMBOLS { 5 | __ZPSTART__: type = weak, value = $0000; 6 | } 7 | MEMORY { 8 | ZP: file = "", define = yes, start = __ZPSTART__, size = $FF - __ZPSTART__; 9 | RAM: file = "", define = yes, start = $0100, size = $C000; 10 | ROM: file = %O, type = ro, start = %S, size = $0800, fillval = $FF; 11 | } 12 | SEGMENTS { 13 | ZEROPAGE: load = ZP, type = zp; 14 | STARTUP: load = ROM, type = ro, optional = yes; 15 | LOWCODE: load = ROM, type = ro, optional = yes; 16 | ONCE: load = ROM, type = ro, optional = yes; 17 | CODE: load = ROM, type = ro; 18 | RODATA: load = ROM, type = ro; 19 | DATA: load = ROM, type = ro; 20 | MESSAGES: load = ROM, type = ro, define = yes, optional = yes; 21 | BSS: load = RAM, type = bss, define = yes; 22 | VECTORS: load = ROM, type = overwrite, start = $FFFA; 23 | } 24 | FILES { 25 | %O: format = bin; 26 | } -------------------------------------------------------------------------------- /iz8dwf/a2vmemnoram.asm: -------------------------------------------------------------------------------- 1 | ; Runs on Apple II at $F800 2 | ; Rev 0.2a 3 | ; uses no Page Zero pointers 4 | ; IZ8DWF 2023 5 | ; edited to assemble with ca65 by KI3V 6 | 7 | ; VIDEO RAM is mapped from $0400 to $07FF 8 | 9 | .org $F800 10 | 11 | reset: SEI 12 | CLD 13 | LDX #$FF 14 | TXS ; initialize the stack pointer 15 | 16 | ; soft switches 17 | LDX $C051 ; text mode 18 | LDX $C054 ; page 2 off 19 | 20 | LDA #$A0 21 | ; fills the entire screen 22 | LDX #$00 23 | scf: STA $0400,X 24 | STA $0500,X 25 | STA $0600,X 26 | STA $0700,X 27 | TAY 28 | STY $0,X 29 | LDA $0,X 30 | INX 31 | STA $0400,X 32 | STA $0500,X 33 | STA $0600,X 34 | STA $0700,X 35 | TYA 36 | INX 37 | BNE scf 38 | CLC 39 | ADC #$1 ; change character 40 | LDY $C030 ; tick the speaker 41 | LDY #$0 42 | wait: INX 43 | BNE wait 44 | DEY 45 | BNE wait 46 | JMP scf 47 | endofrom: 48 | ; fills the unused space with $FF 49 | .res ($FFFA-endofrom), $FF ; fills the unused space with $FF 50 | .segment "VECTORS" 51 | .byte $00,$F8,$00,$F8,$00,$F8 52 | 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RAMSIZE = 16K 2 | MAME = mame 3 | 4 | VERSION := $(shell git describe --tags --always --dirty=-LOCAL --broken=-XX 2>/dev/null || echo LOCAL_BUILD) 5 | VERSION_UC := $(shell echo $(VERSION) | tr a-z A-Z) 6 | TAG := $(shell git describe --tags --exact-match 2>/dev/null || echo LOCAL_BUILD) 7 | 8 | $(info Version: $(VERSION)) 9 | $(info Tag: $(TAG)) 10 | 11 | RELEASE_ZIP := appleII_deadtest-BIN-$(VERSION).zip 12 | RELEASE_FILES := apple2dead.bin apple2dead.dsk LICENSE.html README.html 13 | 14 | all: $(RELEASE_FILES) 15 | 16 | apple2dead.bin: config.inc inc/marchu_zpsp.asm inc/marchu.asm inc/a2console.asm inc/a2macros.inc inc/a2constants.inc 17 | 18 | # OUTPUT = apple2.bin 19 | ASSEMBLE = cl65 -t apple2 -C a2_f8rom.cfg -I ./inc -l $(@:%.bin=%.lst) -Ln $(@:%.bin=%.sym) $(QUICKTEST) 20 | CHECKSUM = sha1sum --tag 21 | 22 | 23 | %.bin: %.asm Makefile a2_f8rom.cfg 24 | $(ASSEMBLE) -o $@ $< 25 | -@$(CHECKSUM) $@ 26 | 27 | 28 | config.inc: 29 | @echo ".define VERSION_STR \"$(VERSION_UC)\"" > $@ 30 | 31 | .INTERMEDIATE: config.inc 32 | 33 | export QUICKTEST 34 | 35 | DEBUGGER := none 36 | 37 | debug: DEBUGGER := osx 38 | debug: QUICKTEST := -D QUICKTEST=1 39 | debug: run 40 | 41 | apple2dead.dsk: apple2dead.bin apple2dead.srcdsk 42 | cp apple2dead.srcdsk $@ 43 | cat $< | ac -p $@ DEADTEST BIN 0xF800 44 | 45 | run: apple2dead.bin all 46 | ln -sf $< 341-0020-00.f8 47 | $(MAME) apple2p -ramsize $(RAMSIZE) -keepaspect -volume -10 -window -resolution 800x600 -skip_gameinfo -debug -debugger $(DEBUGGER) 48 | 49 | showver: 50 | @git describe --tags --long --always --dirty=-L --broken=-X 51 | 52 | %.html: %.md 53 | pandoc $< -f markdown -t html -s -o $@ -V mainfont=sans-serif -V maxwidth=50em --metadata title="$*" 54 | 55 | $(RELEASE_ZIP): $(RELEASE_FILES) 56 | @rm -f $@ 57 | zip $@ $^ 58 | 59 | zip: $(RELEASE_ZIP) 60 | 61 | 62 | release: clean $(RELEASE_ZIP) 63 | $(info ) 64 | $(info VERSION: $(VERSION)) 65 | $(info RELEASE: $(TAG)) 66 | ifneq (,$(findstring LOCAL,$(TAG) $(VERSION))) 67 | @echo 68 | @echo "Abort: release only from a clean, tagged commit" 69 | @echo 70 | @git status --short --branch 71 | @echo 72 | @false 73 | else 74 | git push origin $(TAG) 75 | gh release create $(TAG) --draft --generate-notes 76 | gh release upload $(TAG) $(RELEASE_FILES) $(RELEASE_ZIP) 77 | @echo 78 | @echo Release $(TAG) is a draft. Approve or discard on GitHub. 79 | endif 80 | 81 | clean: 82 | rm -f *.lst *.o *.map *.sym $(RELEASE_FILES) appleII_deadtest*.zip 83 | 84 | .PHONY: all test clean cleanall showver run debug release zip 85 | -------------------------------------------------------------------------------- /inc/a2constants.inc: -------------------------------------------------------------------------------- 1 | ; Apple ][ Dead Test RAM Diagnostic ROM 2 | ; Copyright (C) 2023 David Giller 3 | 4 | ; This program is free software; you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation; either version 2 of the License, or 7 | ; (at your option) any later version. 8 | 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | 14 | ; You should have received a copy of the GNU General Public License along 15 | ; with this program; if not, write to the Free Software Foundation, Inc., 16 | ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | ; soft switches 19 | KBD := $C000 ; R - Read keyboard 20 | KBDSTRB := $C010 ; R - Clear keyboard strobe 21 | 22 | CLR80COL := $C000 ; W - Disable 80 column store 23 | SET80COL := $C001 ; W - Enable 80 column store 24 | RDMAINRAM := $C002 ; W - Read RAM on mainboard 25 | RDCARDRAM := $C003 ; W - Read RAM on card 26 | WRMAINRAM := $C004 ; W - Write RAM on mainboard 27 | WRCARDRAM := $C005 ; W - Write RAM on card 28 | SETINTCXROM := $C007 ; W - Set internal CX00 ROM 29 | SETSTDZP := $C008 ; W - Set standard zero page/stack 30 | SETALTZP := $C009 ; W - Set alternate zero page/stack 31 | SETSLOTC3ROM:= $C00B ; W - Enable C300 slot ROM 32 | CLR80VID := $C00C ; W - Disable 80 column video 33 | SET80VID := $C00D ; W - Set 80 column video 34 | CLRALTCHAR := $C00E ; W - Normal lower case, flash upper case 35 | SETALTCHAR := $C00F ; W - Normal/inverse lower case, no flash 36 | 37 | RDLCBNK2 := $C011 ; R - Reads language card bank 2 - >127 if LC bank 2 in use 38 | RDLCRAM := $C012 ; R - Reads language card RAM enable - >127 if LC is read enabled 39 | RDRAMRD := $C013 ; R - Reads RAMREAD state 40 | RDRAMWRT := $C014 ; R - Reads BANKWRT state 41 | RD80COL := $C018 ; R - Reads SET80COL - >127 if 80 column store enabled 42 | RDVBLBAR := $C019 ; R - Reads VBL signal 43 | RDTEXT := $C01A ; R - Reads text mode 44 | RDPAGE2 := $C01C ; R - Reads page 1/2 status 45 | ALTCHARSET := $C01E ; R - Reads SETALTCHAR - >127 if alt charset switched in 46 | RD80VID := $C01F ; R - Reads SET80VID - >127 if 80 column video enabled 47 | 48 | ; Hardware 49 | SPKR := $C030 ; RW - toggle the speaker 50 | 51 | ; Video mode switches 52 | TXTCLR := $C050 ; RW - Display graphics 53 | TXTSET := $C051 ; RW - Display text 54 | MIXCLR := $C052 ; RW - Disable 4 lines of text 55 | MIXSET := $C053 ; RW - Enable 4 lines of text 56 | LOWSCR := $C054 ; RW - Page 1 57 | HISCR := $C055 ; RW - Page 2 58 | LORES := $C056 ; RW - Lores graphics 59 | HIRES := $C057 ; RW - Hires graphics 60 | DHIRESON := $C05E ; RW - Enable double-width graphics 61 | DHIRESOFF := $C05F ; RW - Disable double-width graphics 62 | 63 | ; Game controller 64 | BUTN0 := $C061 ; RW - Open-Apple Key 65 | BUTN1 := $C062 ; RW - Closed-Apple Key 66 | 67 | ; IOU 68 | IOUDISON := $C07E ; RW - Disable IOU 69 | IOUDISOFF := $C07F ; RW - Enable IOU 70 | 71 | ; Language card switches 72 | ROMIN := $C081 ; RW - Swap in D000-FFFF ROM 73 | LCBANK2 := $C083 ; RW - Swap in LC bank 2 74 | LCBANK1 := $C08B ; RW - Swap in LC bank 1 75 | 76 | 77 | ; constants for screen line start addresses 78 | TXTLINE1 := $0400 79 | TXTLINE2 := $0480 80 | TXTLINE3 := $0500 81 | TXTLINE4 := $0580 82 | TXTLINE5 := $0600 83 | TXTLINE6 := $0680 84 | TXTLINE7 := $0700 85 | TXTLINE8 := $0780 86 | TXTLINE9 := $0428 87 | TXTLINE10 := $04A8 88 | TXTLINE11 := $0528 89 | TXTLINE12 := $05A8 90 | TXTLINE13 := $0628 91 | TXTLINE14 := $06A8 92 | TXTLINE15 := $0728 93 | TXTLINE16 := $07A8 94 | TXTLINE17 := $0450 95 | TXTLINE18 := $04D0 96 | TXTLINE19 := $0550 97 | TXTLINE20 := $05D0 98 | TXTLINE21 := $0650 99 | TXTLINE22 := $06D0 100 | TXTLINE23 := $0750 101 | TXTLINE24 := $07D0 102 | -------------------------------------------------------------------------------- /inc/a2console.asm: -------------------------------------------------------------------------------- 1 | ; Apple ][ Dead Test RAM Diagnostic ROM 2 | ; Copyright (C) 2023 David Giller 3 | 4 | ; This program is free software; you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation; either version 2 of the License, or 7 | ; (at your option) any later version. 8 | 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | 14 | ; You should have received a copy of the GNU General Public License along 15 | ; with this program; if not, write to the Free Software Foundation, Inc., 16 | ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | .zeropage 19 | con_loc: .res 2 20 | con_str: .res 2 21 | con_xsave: .res 1 22 | con_ysave: .res 1 23 | con_asave: .res 1 24 | .code 25 | 26 | 27 | 28 | ; .proc con_put 29 | ; rts 30 | ; .endproc 31 | 32 | 33 | 34 | ; ; setup: con_loc is destination location, con_str is the string to write 35 | ; .proc _con_put_sz 36 | ; put: 37 | ; lda (con_str),Y ; fetch char 38 | ; beq end ; finish on zero byte 39 | ; sta (con_loc),Y ; emit the string 40 | ; inc con_loc ; increment location 41 | ; bne nextchar 42 | ; inc con_loc+1 43 | ; nextchar: 44 | ; inc con_str ; increment pointer 45 | ; bne put ; skip if no carry 46 | ; inc con_str+1 ; now con_str points to start of the string 47 | ; jmp put 48 | ; end: 49 | ; rts 50 | ; .endproc 51 | 52 | ; print a string with args immediately embedded after the calling function 53 | ; first screen location, then the string itself, zero-terminated 54 | ; adapted from Don Lancaster's "Assembly Cookbook for the Apple II/IIe" 55 | .proc con_puts_embedded 56 | stx con_xsave 57 | sty con_ysave 58 | sta con_asave 59 | 60 | pla ; fetch address of argument (minus one) 61 | sta con_str 62 | pla 63 | sta con_str+1 64 | 65 | ldy #$00 66 | 67 | inc con_str ; increment pointer 68 | bne :+ ; skip if no carry 69 | inc con_str+1 ; now con_str points to the screen location lo byte 70 | : lda (con_str),Y ; fetch lo byte 71 | sta con_loc ; store new screen location 72 | inc con_str ; increment pointer 73 | bne :+ ; skip if no carry 74 | inc con_str+1 ; now con_str points to the screen location hi byte 75 | : lda (con_str),Y ; fetch hi byte 76 | sta con_loc+1 77 | 78 | ; jsr _con_put_sz::nextchar 79 | nextchar: 80 | inc con_str ; increment pointer 81 | bne :+ ; skip if no carry 82 | inc con_str+1 ; now con_str points to start of the string 83 | : lda (con_str),Y ; fetch char 84 | beq end ; finish on zero byte 85 | sta (con_loc),Y ; emit the string 86 | inc con_loc ; increment location 87 | bne :+ 88 | inc con_loc+1 89 | : clc 90 | bcc nextchar ; branch (always) to next char 91 | 92 | end: 93 | lda con_str+1 ; fix up the stack for return 94 | pha 95 | lda con_str 96 | pha 97 | lda con_asave ; restore regs 98 | lda con_ysave 99 | lda con_xsave 100 | rts 101 | .endproc 102 | 103 | ; print value in A to current screen location 104 | .proc con_put_hex 105 | sta con_asave ; save the value for reuse 106 | 107 | LSR ; shift the high nybble into the low 108 | LSR 109 | LSR 110 | LSR 111 | TAY ; use it as an index 112 | LDA hex_tbl,Y ; into the hex table 113 | LDY #0 114 | STA (con_loc),Y ; store the low nybble 115 | 116 | lda con_asave ; get another copy 117 | AND #$0F ; get low nybble 118 | TAY ; use it as an index 119 | LDA hex_tbl,Y ; into the hex table 120 | LDY #1 121 | STA (con_loc),Y ; store the low nybble 122 | RTS 123 | .endproc 124 | 125 | .proc con_cls 126 | inline_cls 127 | rts 128 | .endproc 129 | 130 | ; params: 131 | ; A = column 132 | ; Y = row 133 | ; doesn't touch X 134 | .proc con_goto 135 | pha ; save A (column) on stack 136 | tya 137 | asl ; multiply Y by 2 138 | tay 139 | lda line_to_base,Y ; get the base address (lo) 140 | sta con_loc 141 | lda line_to_base+1,Y; get the base address (hi) 142 | sta con_loc+1 143 | pla ; get the column back 144 | clc ; add the column to the base 145 | adc con_loc 146 | sta con_loc ; store back to the base address 147 | rts ; we don't need to carry, as no valid line crosses a page 148 | .endproc 149 | 150 | line_to_base: .word $400,$480,$500,$580,$600,$680,$700,$780 151 | .word $428,$4A8,$528,$5A8,$628,$6A8,$728,$7A8 152 | .word $450,$4D0,$550,$5D0,$650,$6D0,$750,$7D0 153 | line_to_base_end = * -------------------------------------------------------------------------------- /apple2dead.asm: -------------------------------------------------------------------------------- 1 | ; Apple ][ Dead Test RAM Diagnostic ROM 2 | ; Copyright (C) 2023 David Giller 3 | 4 | ; This program is free software; you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation; either version 2 of the License, or 7 | ; (at your option) any later version. 8 | 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | 14 | ; You should have received a copy of the GNU General Public License along 15 | ; with this program; if not, write to the Free Software Foundation, Inc., 16 | ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | .list off 19 | .listbytes unlimited 20 | .feature org_per_seg 21 | .feature leading_dot_in_identifiers 22 | .include "config.inc" 23 | .include "inc/a2constants.inc" 24 | .include "inc/a2macros.inc" 25 | .list on 26 | .debuginfo 27 | 28 | .zeropage 29 | .org $0 30 | .code 31 | 32 | .org $F800 ; this is designed to run in a 2K rom on the Apple II/II+ 33 | 34 | romstart: 35 | sei ; stop interrupts 36 | cld ; make sure we're not in decimal mode 37 | ldx #$FF 38 | txs ; initialize the stack pointer 39 | TXA ; init A to a known value 40 | 41 | 42 | sta TXTSET ; turn on text 43 | sta CLRALTCHAR ; turn off alt charset on later apple machines 44 | sta CLR80VID ; turn off 80 col on //e or //c 45 | 46 | lda BUTN0 ; read button 1 to clear the reading 47 | lda BUTN1 ; read button 2 to clear the reading 48 | 49 | inline_beep_xy $20, $C0 50 | inline_cls 51 | 52 | ; do the zero page test, including its own error reporting 53 | .include "inc/marchu_zpsp.asm" 54 | ; now we trust the zero page and stack page 55 | ldx #$FF 56 | txs ; initialize the stack pointer 57 | JSR count_ram ; count how much RAM is installed 58 | 59 | jsr show_banner 60 | puts_centered_at 20, "ZERO/STACK PAGES OK" 61 | jsr show_charset 62 | 63 | ldx #$40 ; cycles 64 | lda #$80 ; period 65 | jsr beep 66 | ldx #$80 ; cycles 67 | lda #$40 ; period 68 | jsr beep 69 | 70 | LDA #5 71 | JSR delay_seconds 72 | 73 | jsr init_results 74 | test_ram: 75 | JSR marchU ; run the test on RAM and report 76 | JMP test_ram 77 | 78 | 79 | .define charset_line_size 32 80 | .macro m_show_charset_lines 81 | .repeat 256/charset_line_size, line 82 | m_con_goto line+5,(40-charset_line_size)/2-charset_line_size*line 83 | jsr show_charset_line 84 | .endrepeat 85 | .endmacro 86 | 87 | .proc show_charset 88 | puts_centered_at 1, "CHARACTER SET" 89 | ldy #0 90 | m_show_charset_lines 91 | rts 92 | .endproc 93 | 94 | ; on entry: 95 | ; Y = first character segment to display 96 | ; con_loc = where to print, minus Y 97 | .proc show_charset_line 98 | ldx #charset_line_size 99 | : tya ; get the character to print 100 | sta (con_loc),Y ; write it to the screen 101 | iny 102 | dex 103 | bne :- 104 | rts 105 | .endproc 106 | 107 | .proc show_banner 108 | jsr con_cls 109 | ; puts_centered_at 22, "APPLE DEAD TEST BY KI3V AND ADRIAN BLACK" 110 | puts_centered_at 22, .concat( "APPLE DEAD TEST - ", VERSION_STR ) 111 | puts_centered_at 23, "TESTING RAM FROM $0200 TO $XXFF" 112 | m_con_goto 23, 31 113 | LDA mu_page_end 114 | SEC 115 | SBC #1 116 | jsr con_put_hex 117 | rts 118 | .endproc 119 | 120 | .proc delay_seconds 121 | sta KBDSTRB 122 | asl ; double the number, since we actually count in half-seconds 123 | loop: PHA 124 | inline_delay_with_pause 500000, onkey 125 | PLA 126 | SEC 127 | SBC #1 128 | BNE loop 129 | RTS 130 | 131 | onkey: pla 132 | puts_centered_at 24, "PAUSED" 133 | bit KBDSTRB ; clear pending key 134 | : bit KBD ; check for a key 135 | bpl :- 136 | bit KBDSTRB ; clear pending key 137 | rts 138 | .endproc 139 | 140 | 141 | .include "inc/marchu.asm" 142 | .include "inc/a2console.asm" 143 | 144 | tst_tbl:.BYTE $80,$40,$20,$10, $08,$04,$02,$01, $00,$FF,$A5,$5A 145 | ; tst_tbl:.BYTE $80 ; while debugging, shorten the test value list 146 | tst_tbl_end = * 147 | 148 | ; banner_msg: 149 | ; .asciiz "APPLE DEAD TEST BY KI3V AND ADRIAN BLACK" 150 | ; banner_end = * 151 | 152 | hex_tbl:.apple2sz "0123456789ABCDEF" 153 | 154 | zp_msg: .apple2sz "TEST ZERO PAGE" 155 | zp_end = * 156 | pe_msg: .apple2sz "PAGE ERRORS FOUND" 157 | pe_end: 158 | 159 | 160 | ;----------------------------------------------------------------------------- 161 | ; end of the code 162 | endofrom = * 163 | .out .sprintf(" size: %d, available %d",endofrom-romstart,$FFFA-endofrom) 164 | .res ($FFFA-endofrom), $FF ; fills the unused space with $FF 165 | 166 | ; vectors 167 | ; .org $FFFA 168 | .segment "VECTORS" 169 | vectors: .word romstart,romstart,romstart 170 | -------------------------------------------------------------------------------- /inc/a2macros.inc: -------------------------------------------------------------------------------- 1 | ; Apple ][ Dead Test RAM Diagnostic ROM 2 | ; Copyright (C) 2023 David Giller 3 | 4 | ; This program is free software; you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation; either version 2 of the License, or 7 | ; (at your option) any later version. 8 | 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | 14 | ; You should have received a copy of the GNU General Public License along 15 | ; with this program; if not, write to the Free Software Foundation, Inc., 16 | ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | .macpack apple2 19 | 20 | .macro .apple2sz string 21 | scrcode string 22 | .byte 0 23 | .endmacro 24 | 25 | .macro inverse_text 26 | .repeat 26, I 27 | .charmap 'A'+I,'A'&$7F+I 28 | .endrepeat 29 | .endmac 30 | 31 | .macro normal_text 32 | .repeat 26, I 33 | .charmap 'A'+I,'A'|$80+I 34 | .endrepeat 35 | .endmac 36 | ; .macro .inverse string 37 | ; .repeat .strlen( string ), i 38 | ; .byte .strat( string, i ) & %00111111 39 | ; .endrepeat 40 | ; .endmacro 41 | 42 | ; doesn't modify A 43 | .macro inline_beep_xy length, period 44 | .local outer, inner 45 | LDX #length 46 | outer: LDY #period 47 | inner: DEY 48 | nop 49 | nop 50 | BNE inner 51 | STA SPKR 52 | DEX 53 | BNE outer 54 | .endmacro 55 | 56 | .macro inline_delay_xy count 57 | .if .blank(count) 58 | .else 59 | .repeat count 60 | .endif 61 | : DEY ; 512X cycles 62 | BNE :- ; 513X cycles 63 | DEX ; 2X cycles 64 | BNE :- ; 2X+1 cycles - total = 1029X + 1 cycles (when Y = $FF) 65 | .if .blank(count) 66 | .else 67 | .endrepeat 68 | .endif 69 | .endmacro 70 | 71 | 72 | ; this macro can delay for an approximate specified number of clock cycles up to 589815 73 | ; downside is it destroys A and X 74 | .macro inline_delay_cycles_ax cycles 75 | .local n 76 | n = cycles/9 77 | lda #>n ; 2 cycles (outside loop) 78 | ldx #n ; 2 cycles (outside loop) 91 | ldy #n 104 | ldy #(loc) 188 | sta con_loc+1 189 | ; lda #<(line+col) 190 | ; sta con_loc 191 | ; lda #>(line+col) 192 | ; sta con_loc+1 193 | .endmacro 194 | 195 | .macro m_erase_line line 196 | m_con_goto line, 0 197 | lda #' '|$80 198 | ldy #39 199 | : sta (con_loc),Y 200 | dey 201 | bpl :- 202 | .endmacro -------------------------------------------------------------------------------- /inc/marchu_zpsp.asm: -------------------------------------------------------------------------------- 1 | ; Apple ][ Dead Test RAM Diagnostic ROM 2 | ; Copyright (C) 2023 David Giller 3 | 4 | ; This program is free software; you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation; either version 2 of the License, or 7 | ; (at your option) any later version. 8 | 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | 14 | ; You should have received a copy of the GNU General Public License along 15 | ; with this program; if not, write to the Free Software Foundation, Inc., 16 | ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | .scope ZPSP 19 | 20 | inline_print zp_msg, TXTLINE21+((40-(zp_end-zp_msg-1))/2) 21 | 22 | start: 23 | LDX #(tst_tbl_end-tst_tbl-1) ; initialize the pointer to the table of values 24 | 25 | ; step 0; up - w0 - write the test value 26 | marchU: 27 | LDA tst_tbl,X ; get the test value into A 28 | TXS ; save the index of the test value into SP 29 | TAX ; save the test value into X 30 | 31 | LDY #$27 ; write value at bottom of screen 32 | : STA $07D0,Y 33 | DEY 34 | BPL :- 35 | 36 | LDY #$00 37 | marchU0: 38 | STA $00,Y ; w0 - write the test value 39 | STA $0100,Y ; - also to stack page 40 | INY ; count up 41 | BNE marchU0 ; repeat until Y overflows back to zero 42 | 43 | ; STY $00 ; simulate an error for testing 44 | ; step 1; up - r0,w1,r1,w0 45 | ; A contains test value 46 | marchU1:EOR $00,Y ; r0 - read and compare with test value (by XOR'ing with accumulator) 47 | BNE zp_bad ; if bits differ, location is bad 48 | TXA ; get the test value 49 | EOR $0100,Y ; r0s - also stack page 50 | BNE zp_bad ; if bits differ, location is bad 51 | TXA ; get the test value 52 | EOR #$FF ; invert 53 | STA $00,Y ; w1 - write the inverted test value 54 | EOR $00,Y ; r1 - read the same value back and compare using XOR 55 | BNE zp_bad ; if bits differ, location is bad 56 | TXA ; get the test value 57 | EOR #$FF ; invert 58 | STA $0100,Y ; w1s - also stack page 59 | EOR $0100,Y ; r1s 60 | BNE zp_bad ; if bits differ, location is bad 61 | TXA ; get a fresh copy of the test value 62 | STA $00,Y ; w0 - write the test value to the memory location 63 | STA $0100,Y ; w0s - also stack page 64 | INY ; count up 65 | BNE marchU1 ; repeat until Y overflows back to zero 66 | 67 | ; 100ms delay for finding bit rot 68 | marchU1delay: 69 | inline_delay_cycles_ay 10000 70 | 71 | LDY #$00 ; reset Y to 0 72 | ; step 2; up - r0,w1 73 | ; A contains test value from prev step 74 | marchU2:TXA ; recover test value 75 | EOR $00,Y ; r0 - read and compare with test value (by XOR'ing with accumulator) 76 | BNE zp_bad ; if bits differ, location is bad 77 | TXA ; get the test value 78 | EOR $0100,Y ; r0s - also stack page 79 | BNE zp_bad ; if bits differ, location is bad 80 | TXA ; get the test value 81 | EOR #$FF ; invert 82 | STA $00,Y ; w1 - write the inverted test value 83 | STA $0100,Y ; w1s - also stack page 84 | INY ; count up 85 | BNE marchU2 ; repeat until Y overflows back to zero 86 | 87 | ; 100ms delay for finding bit rot 88 | marchU2delay: 89 | inline_delay_cycles_ay 10000 90 | JMP continue 91 | 92 | zp_bad: JMP zp_error 93 | 94 | continue: 95 | LDY #$FF ; reset Y to $FF and count down 96 | TXA ; recover test value 97 | EOR #$FF ; invert 98 | ; step 3; down - r1,w0,r0,w1 99 | marchU3:EOR $00,Y ; r1 - read and compare with inverted test value (by XOR'ing with accumulator) 100 | BNE zp_bad ; if bits differ, location is bad 101 | TXA ; get the test value 102 | EOR #$FF 103 | EOR $0100,Y ; r1s - also stack page 104 | BNE zp_bad ; if bits differ, location is bad 105 | TXA ; get the test value 106 | STA $00,Y ; w0 - write the test value 107 | EOR $00,Y ; r0 - read the same value back and compare using XOR 108 | BNE zp_bad ; if bits differ, location is bad 109 | TXA ; get a fresh copy of the test value 110 | STA $0100,Y ; w0s - write the test value 111 | EOR $0100,Y ; r0s - read the same value back and compare using XOR 112 | BNE zp_bad ; if bits differ, location is bad 113 | TXA ; get a fresh copy of the test value 114 | EOR #$FF ; invert 115 | STA $00,Y ; w1 - write the inverted test value 116 | STA $0100,Y ; w1s - also stack page 117 | DEY ; count down 118 | CPY #$FF ; did we wrap? 119 | BNE marchU3 ; repeat until Y overflows back to FF 120 | 121 | ; step 4; down - r1,w0 122 | ; A contains the inverted test value from prev step 123 | marchU4:EOR $00,Y ; r1 - read and compare with inverted test value (by XOR'ing with accumulator) 124 | BNE zp_bad ; if bits differ, location is bad 125 | TXA ; get the test value 126 | EOR #$FF ; invert 127 | EOR $0100,Y ; r1s - read and compare with inverted test value (by XOR'ing with accumulator) 128 | BNE zp_bad ; if bits differ, location is bad 129 | TXA ; get the test value 130 | STA $00,Y ; w0 - write the test value 131 | STA $0100,Y ; w0s - also stack page 132 | EOR #$FF 133 | DEY ; count down 134 | CPY #$FF ; did we wrap? 135 | BNE marchU4 ; repeat until Y overflows back to FF 136 | 137 | TSX ; recover the test value index from SP 138 | DEX ; choose the next one 139 | CPX #$FF ; see if we've wrapped 140 | BNE marchup ; start again with next value 141 | 142 | JMP zp_good 143 | 144 | marchup: 145 | JMP marchU 146 | 147 | 148 | 149 | 150 | 151 | ; A contains the bits (as 1) that were found to be bad 152 | ; Y contains the address (offset) of the address where bad bit(s) were found 153 | 154 | 155 | .proc zp_error 156 | TAX ; bat bit mask is in A, save it to X 157 | TXS ; then save it in the SP 158 | 159 | STA TXTSET ; text mode 160 | sta MIXSET ; mixed mode on 161 | STA LOWSCR ; page 2 off 162 | inline_cls 163 | inline_print bad_msg, $0750 164 | 165 | TSX ; retrieve the test value 166 | TXA 167 | LDY #0 168 | print_bit: 169 | asl ; get top bit into carry flag 170 | tax ; save the current value 171 | lda #'0'|$80 172 | adc #0 ; increment by one if we had a carry 173 | sta $075A,Y ; print bit to screen 174 | txa 175 | iny 176 | cpy #8 177 | bne print_bit ; repeat 8 times 178 | 179 | ; find the bit to beep out 180 | tsx ; get the bad bit mask back into A 181 | txa 182 | LDX #1 ; count up 183 | chkbit: 184 | LSR ; move lowest bit into carry 185 | BCS start_beeping ; bit set, display it 186 | inx ; count down 187 | cpx #$09 188 | bne chkbit ; test next bit 189 | wha:JMP wha ; only get here if there was no bad bit 190 | 191 | ; now X contains the index of the bit, starting at 1 192 | start_beeping: 193 | txs ; save the bit index of the top set bit into SP 194 | beeploop: 195 | lda #1 196 | type_beep: ; beep an annoying chirp to indicate page err 197 | inline_beep_xy $FF, $FF 198 | sec 199 | sbc #1 200 | bpl type_beep 201 | 202 | tsx ; fetch the bit number 203 | txa 204 | bit_beep: 205 | tax 206 | inline_delay_cycles_ay 400000 207 | txa 208 | sta TXTCLR ; turn on graphics 209 | inline_beep_xy $FF, $80 210 | sta TXTSET ; text mode 211 | sec 212 | sbc #1 213 | bne bit_beep 214 | 215 | ; pause betwen beeping ~1.5 sec 216 | ldx #3 217 | dl: inline_delay_cycles_ay 500000 218 | dex 219 | bne dl 220 | 221 | 222 | JMP beeploop 223 | .endproc 224 | 225 | bad_msg:.apple2sz "ZP/SP ERR" 226 | bad_msg_len = * - bad_msg 227 | 228 | zp_good: 229 | ; lda #$18 230 | ; jmp zp_error ; simulate error 231 | 232 | ; inline_print pt_msg, TXTLINE21+((40-(pt_end-pt_msg-1))/2) 233 | 234 | .proc page_test 235 | ; ldx #$F0 ; simulate error 236 | ; jmp page_error 237 | 238 | LDA #0 ; write zero to zp location 0 239 | TAY 240 | wz: STA $00,Y 241 | DEY 242 | BNE wz 243 | 244 | wr: STA $0100,Y ; write to the pages 245 | LDX $00,Y ; check the zp address 246 | BNE page_error 247 | STA $0200,Y 248 | LDX $00,Y ; check the zp address 249 | BNE page_error 250 | STA $0400,Y 251 | LDX $00,Y ; check the zp address 252 | BNE page_error 253 | STA $0800,Y 254 | LDX $00,Y ; check the zp address 255 | BNE page_error 256 | STA $1000,Y 257 | LDX $00,Y ; check the zp address 258 | BNE page_error 259 | STA $2000,Y 260 | LDX $00,Y ; check the zp address 261 | BNE page_error 262 | STA $4000,Y 263 | LDX $00,Y ; check the zp address 264 | BNE page_error 265 | STA $8000,Y 266 | LDX $00,Y ; check the zp address 267 | BNE page_error 268 | INY 269 | BNE wr 270 | 271 | JMP page_ok 272 | .endproc 273 | 274 | .proc page_error 275 | ; TAX ; bat bit mask is in A, save it to X 276 | TXS ; then save it in the SP 277 | 278 | STA TXTSET ; text mode 279 | sta MIXSET ; mixed mode on 280 | STA LOWSCR ; page 2 off 281 | inline_cls 282 | inline_print bad_page_msg, $0750 283 | 284 | TSX ; retrieve the test value 285 | TXA 286 | LDY #0 287 | print_bit: 288 | asl ; get top bit into carry flag 289 | tax ; save the current value 290 | lda #'0'|$80 291 | adc #0 ; increment by one if we had a carry 292 | sta $0759,Y ; print bit to screen 293 | txa 294 | iny 295 | cpy #8 296 | bne print_bit ; repeat 8 times 297 | 298 | ; find the bit to beep out 299 | tsx ; get the bad bit mask back into A 300 | txa 301 | cmp #$FF ; if it's FF, it's a motherboard error 302 | beq start_beeping 303 | LDX #1 ; count up 304 | page_chkbit: 305 | LSR ; move lowest bit into carry 306 | BCS start_beeping ; bit set, display it 307 | inx ; count down 308 | cpx #$09 309 | bne page_chkbit ; test next bit 310 | wha:JMP wha ; only get here if there was no bad bit 311 | 312 | ; now X contains the index of the bit, starting at 1 313 | start_beeping: 314 | txs ; save the bit index of the top set bit into SP 315 | beeploop: 316 | ldx #5 317 | type_beep: ; beep an annoying chirp to indicate page err 318 | inline_delay_cycles_ay 30000 319 | txa 320 | inline_beep_xy $40, $40 321 | tax 322 | dex 323 | bne type_beep 324 | 325 | tsx ; fetch the bit number 326 | txa 327 | cmp #$FF 328 | beq beeploop ; continuous beeping for MB error 329 | bit_beep: 330 | tax 331 | inline_delay_cycles_ay 400000 332 | txa 333 | sta TXTCLR ; turn on graphics 334 | inline_beep_xy $FF, $80 335 | sta TXTSET ; text mode 336 | sec 337 | sbc #1 338 | bne bit_beep 339 | 340 | ; pause betwen beeping ~1.5 sec 341 | ldx #3 342 | dl: inline_delay_cycles_ay 500000 343 | dex 344 | bne dl 345 | 346 | JMP beeploop 347 | 348 | bad_page_msg:.apple2sz "PAGE ERR" 349 | .endproc 350 | 351 | page_ok: 352 | .endscope 353 | -------------------------------------------------------------------------------- /inc/marchu.asm: -------------------------------------------------------------------------------- 1 | ; Apple ][ Dead Test RAM Diagnostic ROM 2 | ; Copyright (C) 2023 David Giller 3 | 4 | ; This program is free software; you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation; either version 2 of the License, or 7 | ; (at your option) any later version. 8 | 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | 14 | ; You should have received a copy of the GNU General Public License along 15 | ; with this program; if not, write to the Free Software Foundation, Inc., 16 | ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | .zeropage 19 | mu_ptr_lo: .res 1 20 | mu_ptr_hi: 21 | count_ptr: .res 1 ; when counting, use same location to store low part of address 22 | mu_page_end: .res 1 23 | mu_page_start: .res 1 24 | mu_test_idx: .res 1 25 | mu_ysave: .res 1 26 | all_errs: .res 1 27 | scratch: .res 2 28 | results: .res 48*4 29 | .code 30 | 31 | FIRST_PAGE = $02 32 | 33 | 34 | .proc count_ram 35 | ; Count RAM. Check start of every 4K block. 36 | ; Reads from empty locations return $FF regardless of what was written there. 37 | LDA #0 38 | STA count_ptr ; low bits. High are at mu_page_end 39 | 40 | LDY #0 ; no offsets 41 | LDX #(sizes_end-sizes-1) 42 | lp: LDA sizes,X ; fetch the size from the table 43 | STA mu_page_end ; use it as the page number in the scratch pointer 44 | LDA #0 45 | STA (count_ptr),Y ; store 0 at the start of the page 46 | LDA (count_ptr),Y ; if no RAM there, will return $FF 47 | CMP #$FF ; if $FF we found the end of RAM 48 | BEQ found 49 | DEX ; try next location 50 | BPL lp 51 | LDA #$C0 ; if not yet found, it's 48K. Don't want to probe at $C000. 52 | STA mu_page_end 53 | found: 54 | RTS 55 | 56 | sizes: .byte $90,$80,$60,$50,$40,$30,$20,$10 57 | sizes_end = * 58 | .endproc 59 | 60 | .proc init_results 61 | lda #0 62 | sta all_errs 63 | tay 64 | lp: sta results,Y 65 | iny 66 | bne lp 67 | rts 68 | .endproc 69 | 70 | .macro checkbad 71 | beq :+ 72 | jsr markbad 73 | 74 | ; STY mu_ysave 75 | ; LDY mu_ptr_hi ; get the page number as index into results array 76 | ; ORA results,Y ; collect any bad bits 77 | ; STA results,Y ; store the accumulated errors back to the results array 78 | ; ORA all_errs ; also store one value that collects all of the bad bits found 79 | ; STA all_errs 80 | ; LDY mu_ysave 81 | : 82 | .endmac 83 | 84 | .proc markbad 85 | STY mu_ysave 86 | LDY mu_ptr_hi ; get the page number as index into results array 87 | ORA results,Y ; collect any bad bits 88 | STA results,Y ; store the accumulated errors back to the results array 89 | ORA all_errs ; also store one value that collects all of the bad bits found 90 | STA all_errs 91 | LDY mu_ysave 92 | RTS 93 | .endproc 94 | 95 | ; marchU 96 | ; returns bitmask of bad bits in A 97 | .proc marchU 98 | sta TXTCLR ; use graphics 99 | sta HIRES ; set high res 100 | sta MIXSET ; mixed mode on 101 | 102 | LDA #FIRST_PAGE ; set starting address (maybe change later to a parameter?) 103 | STA mu_page_start 104 | 105 | LDA #(tst_tbl_end-tst_tbl-1) ; number of test values 106 | STA mu_test_idx 107 | 108 | init: 109 | LDA #0 ; low bits 0 so we test a hardware page at a time 110 | STA mu_ptr_lo 111 | LDY #$00 ; Y will be the pointer into the page 112 | LDX mu_test_idx ; get the index to the test value pages 113 | LDA tst_tbl,X ; get the test value into A 114 | TAX ; X will contain the test val throughout marchU 115 | LDA mu_page_start 116 | STA mu_ptr_hi 117 | 118 | ; lda #08 119 | ; sta results+$19 120 | ; lda #01 121 | ; sta results+$A1 122 | ; sta all_errs 123 | ; jmp show_report ; simulate a run with canned errors 124 | 125 | ; In the descriptions below: 126 | ; up: perform the test from low addresses to high ones 127 | ; down: perform the test from high addresses to low ones 128 | ; r0: read the current location, compare to the test value, fail if different 129 | ; r1: read the current location, compare to the inverted test value, fail if different 130 | ; w0: write the test value to current location 131 | ; w1: write the inverted test value to current location 132 | 133 | ; step 0; up - w0 - write the test value 134 | step0: 135 | TXA ; get the test value 136 | STA (mu_ptr_lo),Y ; w0 - write the test value to current location 137 | INY ; count up 138 | BNE step0 ; repeat until Y overflows back to zero (do the whole page) 139 | 140 | INC mu_ptr_hi ; increment the page 141 | LDA mu_ptr_hi 142 | CMP mu_page_end ; compare with (one page past) the last page 143 | BNE step0 ; if not there yet, loop again 144 | 145 | ; LDA #$08 ; simulate error 146 | ; JMP bad 147 | 148 | ; step 1; up - r0,w1,r1,w0 149 | LDA mu_page_start ; set up the starting page again for next stage 150 | STA mu_ptr_hi 151 | step1: 152 | TXA ; get the test value 153 | EOR (mu_ptr_lo),Y ; r0 - read and compare with test value (by XOR'ing with accumulator) 154 | ; BNE bad ; if bits differ, location is bad 155 | checkbad 156 | TXA ; get the test value 157 | EOR #$FF ; invert 158 | STA (mu_ptr_lo),Y ; w1 - write the inverted test value 159 | EOR (mu_ptr_lo),Y ; r1 - read the same value back and compare using XOR 160 | ; BNE bad ; if bits differ, location is bad 161 | checkbad 162 | TXA ; get the test value 163 | STA (mu_ptr_lo),Y ; w0 - write the test value to the memory location 164 | INY ; count up 165 | BNE step1 ; repeat until Y overflows back to zero 166 | 167 | INC mu_ptr_hi ; increment the page 168 | LDA mu_ptr_hi 169 | CMP mu_page_end ; compare with (one page past) the last page 170 | BNE step1 ; if not there yet, loop again 171 | 172 | ; step 2; up - r0,w1 173 | LDA mu_page_start ; set up the starting page again for next stage 174 | STA mu_ptr_hi 175 | step2: 176 | TXA ; get the test value 177 | EOR (mu_ptr_lo),Y ; r0 - read and compare with test value (by XOR'ing with accumulator) 178 | ; BNE bad ; if bits differ, location is bad 179 | checkbad 180 | TXA ; get the test value 181 | EOR #$FF ; invert 182 | STA (mu_ptr_lo),Y ; w1 - write the inverted test value 183 | INY ; count up 184 | BNE step2 ; repeat until Y overflows back to zero 185 | 186 | INC mu_ptr_hi ; increment the page 187 | LDA mu_ptr_hi 188 | CMP mu_page_end ; compare with (one page past) the last page 189 | BNE step2 ; if not there yet, loop again 190 | 191 | ; step 3; down - r1,w0,r0,w1 192 | LDA mu_page_end 193 | STA mu_ptr_hi 194 | DEC mu_ptr_hi ; start at the end page minus one 195 | JMP continue3 196 | 197 | ; bad: 198 | ; LDY mu_ptr_hi ; get the page number as index into results array 199 | ; ORA results,Y ; collect any bad bits 200 | ; STA results,Y ; store the accumulated errors back to the results array 201 | ; ORA all_errs ; also store one value that collects all of the bad bits found 202 | ; STA all_errs 203 | ; JMP next 204 | 205 | continue3: 206 | LDY #$FF ; start at FF and count down 207 | step3: 208 | TXA ; get the test value 209 | EOR #$FF ; invert 210 | EOR (mu_ptr_lo),Y ; r1 - read and compare with inverted test value (by XOR'ing with accumulator) 211 | ; BNE bad ; if bits differ, location is bad 212 | checkbad 213 | TXA ; get the test value 214 | STA (mu_ptr_lo),Y ; w0 - write the test value 215 | EOR (mu_ptr_lo),Y ; r0 - read the same value back and compare using XOR 216 | ; BNE bad ; if bits differ, location is bad 217 | checkbad 218 | TXA ; get the test value 219 | EOR #$FF ; invert 220 | STA (mu_ptr_lo),Y ; w1 - write the inverted test value 221 | DEY ; determine if we are at offset zero 222 | CPY #$FF ; did we wrap around? 223 | BNE step3 ; repeat until Y overflows back to FF 224 | 225 | DEC mu_ptr_hi ; decrement the page 226 | LDA mu_ptr_hi 227 | CMP mu_page_start ; compare with the first page, which can't be zero 228 | BCS step3 ; if not there yet (mu_ptr_hi>=mu_page_start so carry set), loop again 229 | 230 | ; step 4; down - r1,w0 231 | LDA mu_page_end 232 | STA mu_ptr_hi 233 | DEC mu_ptr_hi ; start at the end page minus one 234 | step4: 235 | TXA ; get the test value 236 | EOR #$FF ; invert 237 | EOR (mu_ptr_lo),Y ; r1 - read and compare with inverted test value (by XOR'ing with accumulator) 238 | ; BNE bad ; if bits differ, location is bad 239 | checkbad 240 | TXA ; get the test value 241 | STA (mu_ptr_lo),Y ; w0 - write the test value 242 | DEY ; determine if we are at offset zero 243 | CPY #$FF ; did we wrap around? 244 | BNE step4 ; repeat until Y overflows back to FF 245 | 246 | next: 247 | DEC mu_ptr_hi ; decrement the page 248 | LDA mu_ptr_hi 249 | CMP mu_page_start ; compare with the first page, which can't be zero 250 | BCS step4 ; if not there yet (mu_ptr_hi>=mu_page_start so carry set), loop again 251 | 252 | ; now, determine whether to repeat with a new test value 253 | LDX mu_test_idx 254 | DEX 255 | STX mu_test_idx 256 | 257 | BMI show_report ; we're done with all values, so show results 258 | 259 | JMP init ; else go to next test value 260 | .endproc 261 | 262 | .proc show_report 263 | sta TXTSET ; turn on text 264 | jsr show_banner 265 | puts_at 1,0, "GITHUB.COM/MISTERBLACK1/APPLEII_DEADTEST" 266 | puts_at 3,0, "PAGE" 267 | 268 | ldx #15 269 | next_head_line: 270 | txa ; go to the correct line 271 | clc 272 | adc #4 ; start on this line 273 | tay 274 | lda #0 275 | jsr con_goto 276 | txa 277 | jsr con_put_hex 278 | lda #'_'|$80 279 | ldy #0 280 | sta (con_loc),Y 281 | lda #':'|$80 282 | ldy #2 283 | sta (con_loc),Y 284 | dex 285 | bpl next_head_line 286 | 287 | 288 | LDX #0 289 | next_page: 290 | txa ; calculate the column 291 | lsr ; get the high nybble 292 | lsr 293 | lsr 294 | lsr 295 | tay ; get the column offset from table 296 | lda columns,Y 297 | pha ; save the column number on the stack 298 | 299 | ldy #2 ; print the heading row 300 | jsr con_goto 301 | txa 302 | jsr con_put_hex 303 | inc con_loc 304 | lda #'_'|$80 305 | ldy #0 306 | sta (con_loc),Y 307 | 308 | 309 | txa ; calculate the line number on the screen 310 | and #$0F ; 16 lines of results 311 | clc 312 | adc #4 ; offset by starting line 313 | tay ; put line into Y 314 | 315 | pla ; retrieve column into A 316 | jsr con_goto ; move to that location on screen 317 | 318 | lda results,X ; get the value to print 319 | bne hex ; see if there's an error 320 | lda #'-'|$80 ; if not, print dashes 321 | ldy #0 322 | sta (con_loc),Y ; put two dashes there 323 | iny 324 | sta (con_loc),Y 325 | jmp next 326 | 327 | hex: ; print a hex value 328 | jsr con_put_hex 329 | 330 | next: 331 | INX ; look for the next page 332 | TXA ; compare to the last page to test 333 | cmp mu_page_end 334 | bne next_page ; continue if there are more to print 335 | 336 | lda all_errs 337 | beq good 338 | jsr beep_bad 339 | jmp done 340 | good: 341 | jsr beep_good 342 | 343 | done: 344 | LDA #8 345 | jsr delay_seconds 346 | jmp marchU 347 | 348 | columns: .byte 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38 349 | .endproc 350 | 351 | 352 | .proc beep_bad 353 | ldx #$20 ; cycles 354 | lda #$80 ; period 355 | jsr beep 356 | ldx #$FF ; cycles 357 | lda #$FF ; period 358 | jsr beep 359 | ldx #$FF ; cycles 360 | lda #$FF ; period 361 | jsr beep 362 | rts 363 | .endproc 364 | 365 | .proc beep_good 366 | ldx #$20 ; cycles 367 | lda #$80 ; period 368 | jsr beep 369 | ldx #$40 ; cycles 370 | lda #$40 ; period 371 | jsr beep 372 | ldx #$00 ; cycles 373 | lda #$20 ; period 374 | jsr beep 375 | RTS 376 | .endproc 377 | 378 | ; A is period, X is cycles, destroys Y 379 | .proc beep 380 | outer: pha 381 | tay 382 | inner: nop 383 | nop 384 | nop 385 | dey 386 | bne inner 387 | STA SPKR 388 | pla 389 | dex 390 | bne outer 391 | rts 392 | .endproc 393 | 394 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apple ][ Dead Test RAM Diagnostic ROM 2 | 3 | ![animation](pictures/apple2dead.gif?raw=true) 4 | 5 | #### A Note on Downloading and Programming 6 | The file you want for programming an EPROM is [`apple2dead.bin`](https://github.com/misterblack1/appleII_deadtest/releases/latest/download/apple2dead.bin). You don't need to compile/assemble this ROM if you just want to use it to diagnose your machine. You can always find the most recent ROM image on the **[Releases](https://github.com/misterblack1/appleII_deadtest/releases)** page, also linked on the right side of this repository page. 7 | 8 | ## Background 9 | Recently while fixing an Apple II+ clone, I was annoyed there seemed to be no diagnostic ROMs available for the Apple II that could test the RAM without using RAM. This ROM was born from that frustration. 10 | 11 | The ROM you have here began as a quick test ROM whipped up by Frank (IZ8DWF) that simply displayed full screens of characters in text mode, all without using zero page. This was seen in my Apple II+ Clone repair series. (Frank's original is included here as `a2vmemnoram.asm` and `a2vmemnoram.bin`.) 12 | 13 | I then wanted to do more, so I found disassembled code for the C64 "Dead Test" ROM on [World Of Janni](https://blog.worldofjani.com/?p=164), and ported that to the Apple II. I had to adjusted the functionality of the initial phase of that ROM to work with the Apple II, but it actually worked even with my limited assembly skills. The main thing that makes this test great is it does NOT rely on DRAM at all. It runs entirely inside the ROM and does not use the **zero page** (`$00`-`$FF`) or the stack (`$100`-`$1FF`). All the other tests I found for the Apple II use Zero Page and stack, which is useless if you have DRAM problems in the first bank of memory. 14 | 15 | The biggest problem with the C64 dead test is the diagnostic is a very simple RAM test that simply filled up the first several pages of RAM with simple patters. It will miss many common RAM problems and also can be fooled by many issues. So that's where David took over the work -- and at this point, essentially none of the original code from Frank or the C64 Dead test code is left. 16 | 17 | ## Scope 18 | This test was designed and tested on the Apple II and Apple II+. (Clones of the Apple II+ should be supported.) 19 | 20 | The test should work fine on the Apple IIe and IIc as well, but please keep in mind that those systems use 64kbit DRAM chips, with some using 64kbit x 1 chips (where you have 8 chips for the lower 64K of RAM) and some later machines use 64kbit x 4 bit chips, where you only have two RAM chips for the lower 64k of RAM. You will need to use the schematics to understand how the bit / address errors we're reporting map to the specific motherboard you have. 21 | 22 | ## How it works 23 | 24 | * Upon power up of the machine, it'll beep the speaker to indicate things are working. 25 | * The ROM will test the zero page and stack using a [March-U](https://www.researchgate.net/profile/Georgi-Gaydadjiev/publication/3349024_March_U_A_test_for_unlinked_memory_faults/links/00b495294fc58a4b87000000/March-U-A-test-for-unlinked-memory-faults.pdf) test, one of the best RAM tests available. This will catch page errors within the zero and stack pages (`$000-$1FF`), and if any issues are detected, it will beep out an error code to indicate which bit is bad. (More on this further down.) 26 | * Next the ROM will do a quick test for page errors inside the RAM. This is a subtle issue that can fool many RAM tests. For the main RAM tests to run, we need fully functional Zero page and stack, and if these will get corrupted by a page error causing the ROM to crash. This page error test tries to prevent that from happening. 27 | * The page error test work by by writing `$00` to address `$0000` (inside zero page) and then writing `$FF` to `$0100`, `$0200`, `$0400`, `$0800`, `$1000`, `$2000`, `$4000`, and `$8000`. If when writing `$FF` to those locations the `$00` at location `$0000` is disturbed, then we have a page error and must halt. (There is a bit more going on, but it's not relevant). 28 | * If the page error test fails, the ROM will beep out which RAM bit is bad (helping indicate which chip has a page fault). In the event location `$00` is corrupted with the value `$FF`, this indicates a logic problem on the motherboard. 29 | * The next phase is to detect how much RAM is installed in the Apple II. Valid configurations of the original Apple II are 4K, 8K, 12K, 16K, 20K, 24K, 32K, 36K, and 48K. Currently this test does NOT test above 48k, because using/testing that RAM requires banking out the ROM. 30 | * The ROM will print a banner showing how much RAM is detected and that the zero page and stack page are good. 31 | * It will then procede to do March-U RAM test on all of the RAM (minus the zero page and stack, which are used to run the diagnostic ROM.) 32 | * A full test of 48k takes about 1 minute 30 seconds. 33 | * When the March-U RAM test finishes, you will get a page displaying the test results for all of the RAM detected in the machine. 34 | 35 | ### If an error is detected in the zero page, stack, or you have a page error, it will beep out the bit that is bad 36 | 37 | * One long low beep followed by a number of medium beeps: 38 | * This is a zero page or stack page error. 39 | * The count of medium beeps tells you which bit, and thus which chip (see below). 40 | * A brief trill of high beeps, followed by a number of medium beeps: 41 | * This is a page error. Address lines inside a chip are being crossed, so writes to one location corrupt bits in another location. 42 | * Again, the count of medium beeps tells you which bit and chip (see below). 43 | * A constant trill of high beeps: 44 | * This is a problem with the address decoding logic on the motherboard. This will only happen during the page error test. 45 | 46 | How to find the offending RAM chip on an Apple II or II+: 47 | 48 | * 1 beep: D0 (data bit 0), RAM chip at location C3 49 | * 2 beeps: D1, RAM chip at location C4 50 | * 3 beeps: D2, RAM chip at location C5 51 | * 4 beeps: D3, RAM chip at location C6 52 | * 5 beeps: D4, RAM chip at location C7 53 | * 6 beeps: D5, RAM chip at location C8 54 | * 7 beeps: D6, RAM chip at location C9 55 | * 8 beeps: D7, RAM chip at location C10 56 | 57 | At the end of the main memory test, the ROM will display a grid of results and play a tone. A low tone indicates there was a memory fault found. A rising two-beep tone indicates no problems found. The test will automatically repeat after 10 seconds. We suggest taking a cell phone picture of the screen for a recording... mixing the new technology with the old, you might say. 58 | 59 | You don't have to worry about losing the results though. All of the errors are cumulative. You can leave the test running for hours, and any page that shows an error will always show that error, even if the error is intermittent. 60 | 61 | ## Interpreting the result display grid 62 | The ROM will display a grid showing you where the bit errors are: 63 | ![RAM errors detected](pictures/grid%20errors.jpg?raw=true) 64 | 65 | If you do not see this grid after the March-U test finishes and beeps, you likely have a problem with the video display circuitry on your Apple II. You must fix that first. 66 | 67 | To understand how to decode the grid, it will show all of the pages that contain a bit error. In the above picture it is telling you: 68 | 69 | * Page `$19` has a bit error `$08` 70 | * Converted to binary -> `0000 1000` or bit 4, aka D3 71 | * This means the RAM chip in location C6 is bad (for the Apple II or II+). 72 | * Page `$A1` has a bit error `$01` 73 | * Converted to binary -> `0000 0001` or bit 1, aka D0.\ 74 | * This means the RAM chip in E3 is bad. 75 | 76 | The II and II+ motherboards have their DRAM laid out like this, at least when using 16K x 1 DRAMs: 77 | 78 | ``` 79 | D0 through D7 is the processor data line 80 | 81 | ROW 3 4 5 6 7 8 9 10 ROW 82 | Pages $80 to $BF E D0 D1 D2 D3 D4 D5 D6 D7 E RAM from $8000 to $BFFF 83 | Pages $40 to $7F D D0 D1 D2 D3 D4 D5 D6 D7 D RAM from $4000 to $7FFF 84 | Pages $00 to $3F C D0 D1 D2 D3 D4 D5 D6 D7 C RAM from $0000 to $3FFF 85 | ROW 3 4 5 6 7 8 9 10 ROW 86 | ``` 87 | 88 | ![Alt text]() 89 | 90 | For beep codes, remember that 1 beep = Bit 1 or D0. 8 beeps = Bit 8 or D7. (There is no way for us to make a beep code for 0, which is why D0-D7 are represented by 1-8 beeps, respectively). 91 | 92 | ## Limitations 93 | 94 | * This ROM is designed as a troubleshooting aid only. 95 | * An indicated bit error from this test does not necessarily mean the DRAM is faulty. It means the CPU is unable to correctly see the bit it expects from the DRAM. This could be caused by a bad RAM chip, but could also be some other fault in the system. 96 | * The RAM subsystem on the Apple II is more complex than that of other contemporary 8-bit systems of the time, so many components in that subsystem can cause indicated bit errors. 97 | * The Apple II has a dedicated DRAM output bus that is used to display text/graphics, and this bus is connected to the CPU data bus as needed via some logic chips. 98 | * Please keep in mind that the ROM will only report the first bit it found in error. 99 | * When beeping or flashing, it starts at bit D0 (1 flashs). So if you have multiple bad chips, the lowest bad bit will usually be indicated. Change that chip and run again. 100 | * The full RAM test's grid will show all of the errors it's seen, but within a given page, it stops searching after the first error. So again, the displays will show ***a*** fault, but not necessarily the only fault. 101 | * Remember that each bit of DRAM in each bank is parallel with the other banks, so even if the test is running on the first 4K of RAM, you could have a bad chip in an adjacent bank causing a bit error in the first 4K. 102 | 103 | ## To use this ROM 104 | (See also on YouTube: [Apple II Dead Test Diagnostic: How it works on a good system](https://youtu.be/60skMMOYuAw?si=O4dUQrf9blEDD3wS)) 105 | 106 | * It is designed to run in the `F8` ROM socket on the Apple II, Apple II+, language card or Apple ROM card. 107 | * Since Apple uses 2316 (2K) mask ROMs on their motherboard and on the Apple ROM card, you will need an adapter to use an EPROM in any of these sockets. (Make one or use a PCB). 108 | * Some languge cards can use 2716 EPROMs in the ROM socket, that may be helpful. 109 | * The Apple //e can use a 2764 (8K) that holds EF ROMs, so you can load this F8 ROM into the top of the chip. (Load into address `$1800` in the EPROM to map into `$F800`) 110 | * The Platinum //e has `CF` ROM which is a 27128 (16K) so you would load this `F8` ROM into the top of the chip (`$3800` to map into `$F800`) 111 | * The Apple //c ROM is 27256 (32k) and we have not tested this on the //c, but you would need to find the right location to load this ROM into the EPROM so it would start at `$F800` in the 6502 memory map. 112 | 113 | There is also a 170K DOS 3.3 disk image [`apple2dead.dsk`](https://github.com/misterblack1/appleII_deadtest/releases/latest/download/apple2dead.dsk) in the [Releases](https://github.com/misterblack1/appleII_deadtest/releases) page. You can try that out if your system is working well enough to boot into DOS *AND* you have a working language card installed in your system (or a machine with built-in language card function... all 64K+ Apples II family machines and most/all 64K+ clones). It will auto start once the disk boots up. (Remember this will only test the first 48K in your system even if you have more. The disk version loads the ROM image into the top 16K language card area.) 114 | 115 | ![ROM adapter in card](pictures/language_card.jpg?raw=true) 116 | 117 | There are a couple projects that help you use the ROM: 118 | 119 | [Motherboard ROM switcher](https://github.com/kerokero5150/appleII_deadtest_Swicher_Board) 120 | 121 | [Language Card Replica](https://github.com/btb/LanguageCard) 122 | 123 | I have tested Bradley's LanguageCard. It has a toggle switch that sticks through the back of the machine that allows you to select from 3 possibel ROMs. (I personally installed Applesoft BASIC, Integer BASIC and the Dead Test ROM.) 124 | 125 | ## To assemble the ROM (Linux or WSL on Windows) 126 | You only need to assemble if you are planning to make changes. Otherwise see the **[Releases](https://github.com/misterblack1/appleII_deadtest/releases)** page. 127 | 128 | * `apt-get install cc65 make` 129 | * Then download the zip from the repo and run `make` 130 | 131 | `a2vmemnoram.asm` and `a2vmemnoram.bin` are Frank IZ8DWF's original test ROM, as shown in my Apple II Clone repair video. 132 | 133 | ## Thanks 134 | 135 | * [World of Jani](https://blog.worldofjani.com) for sharing disassembled C64 dead test code. 136 | * [IZ8DWF](https://www.youtube.com/@iz8dwf) for guidance on this along with some of his ROR-test code for printing a messagae to screen. 137 | * [David KI3V](https://github.com/ki3v) for all the amazing work on this ROM! 138 | 139 | See C64 Dead Test ROM here: http://blog.worldofjani.com/?p=164 140 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU General Public License 2 | ========================== 3 | 4 | _Version 2, June 1991_ 5 | _Copyright © 1989, 1991 Free Software Foundation, Inc.,_ 6 | _51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA_ 7 | 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | ### Preamble 12 | 13 | The licenses for most software are designed to take away your 14 | freedom to share and change it. By contrast, the GNU General Public 15 | License is intended to guarantee your freedom to share and change free 16 | software--to make sure the software is free for all its users. This 17 | General Public License applies to most of the Free Software 18 | Foundation's software and to any other program whose authors commit to 19 | using it. (Some other Free Software Foundation software is covered by 20 | the GNU Lesser General Public License instead.) You can apply it to 21 | your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not 24 | price. Our General Public Licenses are designed to make sure that you 25 | have the freedom to distribute copies of free software (and charge for 26 | this service if you wish), that you receive source code or can get it 27 | if you want it, that you can change the software or use pieces of it 28 | in new free programs; and that you know you can do these things. 29 | 30 | To protect your rights, we need to make restrictions that forbid 31 | anyone to deny you these rights or to ask you to surrender the rights. 32 | These restrictions translate to certain responsibilities for you if you 33 | distribute copies of the software, or if you modify it. 34 | 35 | For example, if you distribute copies of such a program, whether 36 | gratis or for a fee, you must give the recipients all the rights that 37 | you have. You must make sure that they, too, receive or can get the 38 | source code. And you must show them these terms so they know their 39 | rights. 40 | 41 | We protect your rights with two steps: **(1)** copyright the software, and 42 | **(2)** offer you this license which gives you legal permission to copy, 43 | distribute and/or modify the software. 44 | 45 | Also, for each author's protection and ours, we want to make certain 46 | that everyone understands that there is no warranty for this free 47 | software. If the software is modified by someone else and passed on, we 48 | want its recipients to know that what they have is not the original, so 49 | that any problems introduced by others will not reflect on the original 50 | authors' reputations. 51 | 52 | Finally, any free program is threatened constantly by software 53 | patents. We wish to avoid the danger that redistributors of a free 54 | program will individually obtain patent licenses, in effect making the 55 | program proprietary. To prevent this, we have made it clear that any 56 | patent must be licensed for everyone's free use or not licensed at all. 57 | 58 | The precise terms and conditions for copying, distribution and 59 | modification follow. 60 | 61 | ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 62 | 63 | **0.** This License applies to any program or other work which contains 64 | a notice placed by the copyright holder saying it may be distributed 65 | under the terms of this General Public License. The “Program”, below, 66 | refers to any such program or work, and a “work based on the Program” 67 | means either the Program or any derivative work under copyright law: 68 | that is to say, a work containing the Program or a portion of it, 69 | either verbatim or with modifications and/or translated into another 70 | language. (Hereinafter, translation is included without limitation in 71 | the term “modification”.) Each licensee is addressed as “you”. 72 | 73 | Activities other than copying, distribution and modification are not 74 | covered by this License; they are outside its scope. The act of 75 | running the Program is not restricted, and the output from the Program 76 | is covered only if its contents constitute a work based on the 77 | Program (independent of having been made by running the Program). 78 | Whether that is true depends on what the Program does. 79 | 80 | **1.** You may copy and distribute verbatim copies of the Program's 81 | source code as you receive it, in any medium, provided that you 82 | conspicuously and appropriately publish on each copy an appropriate 83 | copyright notice and disclaimer of warranty; keep intact all the 84 | notices that refer to this License and to the absence of any warranty; 85 | and give any other recipients of the Program a copy of this License 86 | along with the Program. 87 | 88 | You may charge a fee for the physical act of transferring a copy, and 89 | you may at your option offer warranty protection in exchange for a fee. 90 | 91 | **2.** You may modify your copy or copies of the Program or any portion 92 | of it, thus forming a work based on the Program, and copy and 93 | distribute such modifications or work under the terms of Section 1 94 | above, provided that you also meet all of these conditions: 95 | 96 | * **a)** You must cause the modified files to carry prominent notices 97 | stating that you changed the files and the date of any change. 98 | * **b)** You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | * **c)** If the modified program normally reads commands interactively 103 | when run, you must cause it, when started running for such 104 | interactive use in the most ordinary way, to print or display an 105 | announcement including an appropriate copyright notice and a 106 | notice that there is no warranty (or else, saying that you provide 107 | a warranty) and that users may redistribute the program under 108 | these conditions, and telling the user how to view a copy of this 109 | License. (Exception: if the Program itself is interactive but 110 | does not normally print such an announcement, your work based on 111 | the Program is not required to print an announcement.) 112 | 113 | These requirements apply to the modified work as a whole. If 114 | identifiable sections of that work are not derived from the Program, 115 | and can be reasonably considered independent and separate works in 116 | themselves, then this License, and its terms, do not apply to those 117 | sections when you distribute them as separate works. But when you 118 | distribute the same sections as part of a whole which is a work based 119 | on the Program, the distribution of the whole must be on the terms of 120 | this License, whose permissions for other licensees extend to the 121 | entire whole, and thus to each and every part regardless of who wrote it. 122 | 123 | Thus, it is not the intent of this section to claim rights or contest 124 | your rights to work written entirely by you; rather, the intent is to 125 | exercise the right to control the distribution of derivative or 126 | collective works based on the Program. 127 | 128 | In addition, mere aggregation of another work not based on the Program 129 | with the Program (or with a work based on the Program) on a volume of 130 | a storage or distribution medium does not bring the other work under 131 | the scope of this License. 132 | 133 | **3.** You may copy and distribute the Program (or a work based on it, 134 | under Section 2) in object code or executable form under the terms of 135 | Sections 1 and 2 above provided that you also do one of the following: 136 | 137 | * **a)** Accompany it with the complete corresponding machine-readable 138 | source code, which must be distributed under the terms of Sections 139 | 1 and 2 above on a medium customarily used for software interchange; or, 140 | * **b)** Accompany it with a written offer, valid for at least three 141 | years, to give any third party, for a charge no more than your 142 | cost of physically performing source distribution, a complete 143 | machine-readable copy of the corresponding source code, to be 144 | distributed under the terms of Sections 1 and 2 above on a medium 145 | customarily used for software interchange; or, 146 | * **c)** Accompany it with the information you received as to the offer 147 | to distribute corresponding source code. (This alternative is 148 | allowed only for noncommercial distribution and only if you 149 | received the program in object code or executable form with such 150 | an offer, in accord with Subsection b above.) 151 | 152 | The source code for a work means the preferred form of the work for 153 | making modifications to it. For an executable work, complete source 154 | code means all the source code for all modules it contains, plus any 155 | associated interface definition files, plus the scripts used to 156 | control compilation and installation of the executable. However, as a 157 | special exception, the source code distributed need not include 158 | anything that is normally distributed (in either source or binary 159 | form) with the major components (compiler, kernel, and so on) of the 160 | operating system on which the executable runs, unless that component 161 | itself accompanies the executable. 162 | 163 | If distribution of executable or object code is made by offering 164 | access to copy from a designated place, then offering equivalent 165 | access to copy the source code from the same place counts as 166 | distribution of the source code, even though third parties are not 167 | compelled to copy the source along with the object code. 168 | 169 | **4.** You may not copy, modify, sublicense, or distribute the Program 170 | except as expressly provided under this License. Any attempt 171 | otherwise to copy, modify, sublicense or distribute the Program is 172 | void, and will automatically terminate your rights under this License. 173 | However, parties who have received copies, or rights, from you under 174 | this License will not have their licenses terminated so long as such 175 | parties remain in full compliance. 176 | 177 | **5.** You are not required to accept this License, since you have not 178 | signed it. However, nothing else grants you permission to modify or 179 | distribute the Program or its derivative works. These actions are 180 | prohibited by law if you do not accept this License. Therefore, by 181 | modifying or distributing the Program (or any work based on the 182 | Program), you indicate your acceptance of this License to do so, and 183 | all its terms and conditions for copying, distributing or modifying 184 | the Program or works based on it. 185 | 186 | **6.** Each time you redistribute the Program (or any work based on the 187 | Program), the recipient automatically receives a license from the 188 | original licensor to copy, distribute or modify the Program subject to 189 | these terms and conditions. You may not impose any further 190 | restrictions on the recipients' exercise of the rights granted herein. 191 | You are not responsible for enforcing compliance by third parties to 192 | this License. 193 | 194 | **7.** If, as a consequence of a court judgment or allegation of patent 195 | infringement or for any other reason (not limited to patent issues), 196 | conditions are imposed on you (whether by court order, agreement or 197 | otherwise) that contradict the conditions of this License, they do not 198 | excuse you from the conditions of this License. If you cannot 199 | distribute so as to satisfy simultaneously your obligations under this 200 | License and any other pertinent obligations, then as a consequence you 201 | may not distribute the Program at all. For example, if a patent 202 | license would not permit royalty-free redistribution of the Program by 203 | all those who receive copies directly or indirectly through you, then 204 | the only way you could satisfy both it and this License would be to 205 | refrain entirely from distribution of the Program. 206 | 207 | If any portion of this section is held invalid or unenforceable under 208 | any particular circumstance, the balance of the section is intended to 209 | apply and the section as a whole is intended to apply in other 210 | circumstances. 211 | 212 | It is not the purpose of this section to induce you to infringe any 213 | patents or other property right claims or to contest validity of any 214 | such claims; this section has the sole purpose of protecting the 215 | integrity of the free software distribution system, which is 216 | implemented by public license practices. Many people have made 217 | generous contributions to the wide range of software distributed 218 | through that system in reliance on consistent application of that 219 | system; it is up to the author/donor to decide if he or she is willing 220 | to distribute software through any other system and a licensee cannot 221 | impose that choice. 222 | 223 | This section is intended to make thoroughly clear what is believed to 224 | be a consequence of the rest of this License. 225 | 226 | **8.** If the distribution and/or use of the Program is restricted in 227 | certain countries either by patents or by copyrighted interfaces, the 228 | original copyright holder who places the Program under this License 229 | may add an explicit geographical distribution limitation excluding 230 | those countries, so that distribution is permitted only in or among 231 | countries not thus excluded. In such case, this License incorporates 232 | the limitation as if written in the body of this License. 233 | 234 | **9.** The Free Software Foundation may publish revised and/or new versions 235 | of the General Public License from time to time. Such new versions will 236 | be similar in spirit to the present version, but may differ in detail to 237 | address new problems or concerns. 238 | 239 | Each version is given a distinguishing version number. If the Program 240 | specifies a version number of this License which applies to it and “any 241 | later version”, you have the option of following the terms and conditions 242 | either of that version or of any later version published by the Free 243 | Software Foundation. If the Program does not specify a version number of 244 | this License, you may choose any version ever published by the Free Software 245 | Foundation. 246 | 247 | **10.** If you wish to incorporate parts of the Program into other free 248 | programs whose distribution conditions are different, write to the author 249 | to ask for permission. For software which is copyrighted by the Free 250 | Software Foundation, write to the Free Software Foundation; we sometimes 251 | make exceptions for this. Our decision will be guided by the two goals 252 | of preserving the free status of all derivatives of our free software and 253 | of promoting the sharing and reuse of software generally. 254 | 255 | ### NO WARRANTY 256 | 257 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 258 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 259 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 260 | PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 261 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 262 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 263 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 264 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 265 | REPAIR OR CORRECTION. 266 | 267 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 268 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 269 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 270 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 271 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 272 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 273 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 274 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 275 | POSSIBILITY OF SUCH DAMAGES. 276 | 277 | END OF TERMS AND CONDITIONS 278 | 279 | ### How to Apply These Terms to Your New Programs 280 | 281 | If you develop a new program, and you want it to be of the greatest 282 | possible use to the public, the best way to achieve this is to make it 283 | free software which everyone can redistribute and change under these terms. 284 | 285 | To do so, attach the following notices to the program. It is safest 286 | to attach them to the start of each source file to most effectively 287 | convey the exclusion of warranty; and each file should have at least 288 | the “copyright” line and a pointer to where the full notice is found. 289 | 290 | 291 | Copyright (C) 292 | 293 | This program is free software; you can redistribute it and/or modify 294 | it under the terms of the GNU General Public License as published by 295 | the Free Software Foundation; either version 2 of the License, or 296 | (at your option) any later version. 297 | 298 | This program is distributed in the hope that it will be useful, 299 | but WITHOUT ANY WARRANTY; without even the implied warranty of 300 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 301 | GNU General Public License for more details. 302 | 303 | You should have received a copy of the GNU General Public License along 304 | with this program; if not, write to the Free Software Foundation, Inc., 305 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 306 | 307 | Also add information on how to contact you by electronic and paper mail. 308 | 309 | If the program is interactive, make it output a short notice like this 310 | when it starts in an interactive mode: 311 | 312 | Gnomovision version 69, Copyright (C) year name of author 313 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 314 | This is free software, and you are welcome to redistribute it 315 | under certain conditions; type `show c' for details. 316 | 317 | The hypothetical commands `show w` and `show c` should show the appropriate 318 | parts of the General Public License. Of course, the commands you use may 319 | be called something other than `show w` and `show c`; they could even be 320 | mouse-clicks or menu items--whatever suits your program. 321 | 322 | You should also get your employer (if you work as a programmer) or your 323 | school, if any, to sign a “copyright disclaimer” for the program, if 324 | necessary. Here is a sample; alter the names: 325 | 326 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 327 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 328 | 329 | , 1 April 1989 330 | Ty Coon, President of Vice 331 | 332 | This General Public License does not permit incorporating your program into 333 | proprietary programs. If your program is a subroutine library, you may 334 | consider it more useful to permit linking proprietary applications with the 335 | library. If this is what you want to do, use the GNU Lesser General 336 | Public License instead of this License. --------------------------------------------------------------------------------