; ; ll_6502.s -- Linux Logo in 6502 Assembly for the Apple II v0.40 ; ; by Vince Weaver ; ; decompresses the same lzss data as the Linux versions ; but displays it to the high-res Apple II screen ; ; Also prints some rough system info for Apple II class ; computers, though not for the IIgs ; ; File I/O routines based on sample code in ; "Beneath Apple DOS" by Don Worth and Pieter Lechner .define EQU = ;; Standard zero page allocations that we use CH EQU $24 CV EQU $25 BASL EQU $28 BASH EQU $29 ;; Our Zero Page Allocations ;; for the LZSS part of the code LOGOH EQU $FF LOGOL EQU $FE OUTPUTH EQU $FD OUTPUTL EQU $FC STORERH EQU $FB STORERL EQU $FA LOADRH EQU $F9 LOADRL EQU $F8 EFFECTRH EQU $F7 EFFECTRL EQU $F6 MSELECT EQU $F5 COUNT EQU $F4 ;; for the graphics code QUOTIENT EQU $FF REMAINDER EQU $FE ;; FD is OUTPUTH ;; FC is OUTPUTL DIVISORH EQU $FB DIVISORL EQU $FA DIVIDENDH EQU $F9 DIVIDENDL EQU $F8 YADDRH EQU $F7 YADDRL EQU $F6 APPLEY EQU $F5 ;; F4 is COUNT COLOR EQU $F3 APPLEXH EQU $F2 APPLEXL EQU $F1 MASK EQU $F0 BLOCK EQU $EF OUTH EQU $EE OUTL EQU $ED HGRPNTH EQU $EC HGRPNTL EQU $EB ;; for the sysinfo code RAMSIZE EQU $F7 NUM2 EQU $F3 NUM1 EQU $F2 NUM0 EQU $F1 CHAR1 EQU $F0 CHAR2 EQU $EF ENDCHAR EQU $EE FINDH EQU $ED FINDL EQU $EC STRCATH EQU $EB STRCATL EQU $EA ;; For the disk-read code RWTSH EQU $F1 RWTSL EQU $F0 DOSBUFH EQU $EF DOSBUFL EQU $EE FILEMH EQU $ED FILEML EQU $EC ;; DOS Constants OPEN EQU $01 CLOSE EQU $02 READ EQU $03 WRITE EQU $04 DELETE EQU $05 CATALOG EQU $06 LOCK EQU $07 UNLOCK EQU $08 RENAME EQU $09 POSITION EQU $0A INIT EQU $0B VERIFY EQU $0C ;; OUTPUT EQU $4000 ;; hgr page2, should be unused ;; VECTORS BASIC EQU $3D0 ;; VECTOR for return to Applesoft KEYPRESS EQU $C000 KEYRESET EQU $C010 LOCATE_FILEM_PARAM EQU $3DC LOCATE_RWTS_PARAM EQU $3E3 FILEMANAGER EQU $3D6 ;; SOFT SWITCHES GR EQU $C050 TEXT EQU $C051 FULLGR EQU $C052 TEXTGR EQU $C053 PAGE0 EQU $C054 PAGE1 EQU $C055 LORES EQU $C056 HIRES EQU $C057 ;; MONITOR ROUTINES PRBL2 EQU $F94A ;; Print Blanks monitor routine BASCALC EQU $FBC1 HOME EQU $FC58 ;; Clear the text screen SETNORM EQU $FE84 ;; NORMAL COUT EQU $FDED COUT1 EQU $FDF0 ;; output A to screen CROUT EQU $FD8E ;; char out monitor routine ;; LZSS Parameters N EQU 1024 F EQU 64 THRESHOLD EQU 2 P_BITS EQU 10 POSITION_MASK EQU 3 ;; OPTIMIZATION ;; 1651 - original ;; 1649 - change jmps branches (-1 byte each) ;; 1647 - remove unneeded branch ;; 1640 - share some code ;; 1639 - another branch adjust ;; 1611 - remove extraneous address loads ;; 1600 - rearrange find string ;; 1595 - inline strlen ;; 1575 - hack up RAM detection ;; 1573 - more JMP elimination ;========================================================== ; MAIN() ;========================================================== ; save zero page ; otherwise we can't return to BASIC ldx #$e8 ; we save $E8-$FF ldy #0 lda #>zp_save sta OUTPUTH lda #LOGO ; load logo pointer sta LOGOH lda #(N-F) ; load R value sta STORERH lda #<(N-F) sta STORERL ldy #0 ; setup Y for indirect zero page ; addressing, we always want +0 decompression_loop: lda #8 ; set mask counter sta COUNT lda (LOGOL),Y ; load byte sta MSELECT ; store it ldx #LOGOL jsr inc16 ; increment pointer test_flags: lda LOGOH ; compare to see if we've reached end cmp #>LOGO_END bcc not_match lda LOGOL cmp #>8) ; Mask so mod N sta LOADRH adc #>R sta EFFECTRH lda (EFFECTRL),Y ; Load byte R[LOADR] inc LOADRL ; increment address bne load_carry1 inc LOADRH ; handle overflow load_carry1: store_byte: sta (OUTPUTL),Y ; store byte to output inc OUTPUTL ; increment address bne sb_carry inc OUTPUTH ; handle overflow sb_carry: pha ; calculate R+STORER clc lda STORERL adc #>8) ; mask so mod N sta STORERH adc #>R sta EFFECTRH pla ; restore from stack sta (EFFECTRL),Y ; store A there too inc STORERL ; increment address bne store_carry2 inc STORERH ; handle overflow store_carry2: dex ; count down the out counter bne output_loop ; loop to output_loop if not 0 dec COUNT ; count down the mask counter bne test_flags ; loop to test_flags if not zero beq decompression_loop ; restart whole process discrete_char: ldx #1 ; want to write a single byte bne store_byte ; go and store it (1 byte less than jmp) done_logo: ;========================== ; print ANSI to HGR display ;========================== tya ; get zero from Y sta (OUTPUTL),Y ; make sure we are null terminated jsr reset_output ; restore OUTPUT pointer ; now as input sty COUNT ; set count to zero sty COLOR ; set color to zero lda #64 ; start Y at 64 (partway down screen) sta APPLEY sty APPLEXH ; set X to 20 (centered) lda #20 ; X can be up to 280, so 16-bit val sta APPLEXL jsr y_to_addr ; convert Y value to an address rle_loop: lda (OUTPUTL),Y ; load a byte beq rle_done ; if zero, we are done cmp #27 ; are we escape? bne not_escape escape: ldx COUNT ; don't display if COUNT==0 beq dont_output jsr flush_line ; we finished a color, display it ; flush_line should set COUNT to 0 dont_output: jsr inc_pointer ; point after escape lda (OUTPUTL),Y ; load next byte (should be [ ) find_m: cmp #$6D ; looking for 'm' beq found_m cmp #$33 ; now looking for '3' bne not_three jsr inc_pointer ; if we have 3, the next number after is our color ; (these are ANSI escape codes) ; convert to apple HGR colors lda (OUTPUTL),Y ; load ascii number and #7 ; mask asl A cmp #8 bmi ok ; make sure red, yellow map to orange lsr A ok: ; I'm out of meaningful label names! and #3 sta COLOR ; save our computed color not_three: jsr inc_pointer lda (OUTPUTL),Y jmp find_m ; loop until we find a 'm' found_m: jsr inc_pointer ; done with escape jmp rle_loop ; rejoin main loop not_escape: cmp #10 ; check for linefeed bne not_linefeed linefeed: jsr flush_line ; if linefeed, flush line ; flush_line should set count to 0 jsr inc_pointer sty APPLEXH ; reset APPLEX to 20 lda #20 sta APPLEXL clc ; increment Y by 8 (we are doing lda APPLEY ; 8-pixel high blocks) adc #8 sta APPLEY jsr y_to_addr jmp rle_loop ; loop not_linefeed: inc COUNT ; normal case. we increment run by 3 inc COUNT ; because 80*3 fits on 280 wide screen inc COUNT jsr inc_pointer jmp rle_loop ; loop rle_done: ;================================ ; read from disk ;================================ jsr LOCATE_FILEM_PARAM ; load file manager param list ; Y=low A=high sta FILEMH sty FILEML ldy #7 ; file type offset = 7 lda #0 ; 0 = text sta (FILEML),y iny ; filename ptr offset = 8 lda #filename sta (FILEML),y ldx #1 ; open existing file jsr open ; open file jsr read ; read buffer jsr close ; close file ;================================= ; print the system info ;================================= ldy #0 ; be sure Y is zero sty CH ; set HTAB to 0 lda #20 ; set VTAB to 20 (visible under sta CV ; the graphics) jsr BASCALC ; update output pointers jsr detect_ram ; get some system info ;========================= ; print version info ;========================= jsr reset_output lda #>VERSION sta STRCATH lda #zp_save sta OUTPUTH lda #disk_buff sta FINDH clc ; set return value find_loop1: jsr find_loop ; find first 2 chars bcc find_string_done ; exit if error find_colon: lda #(':'+$80) ; find a colon sta CHAR1 lda #(' '+$80) ; followed by a space sta CHAR2 jsr find_loop bcc find_string_done out_loop: lda (FINDL),Y ; load byte beq find_string_done ; quit if zero cmp ENDCHAR ; quit if endhcar matched beq find_string_done sta (OUTPUTL),Y ; store to output ldx #FINDL jsr inc16 ; increment FIND pointer jsr inc_pointer ; increment OUT pointer bne out_loop ; loop (OUT is never in page 0) find_string_done: tya ; null terminate (y is 0) sta (OUTPUTL),Y rts find_loop: lda (FINDL),Y ; load byte beq find_loop_done ; if zero, we're done ldx #FINDL ; jsr inc16 ; increment pointer cmp CHAR1 bne find_loop ; jmp. disk buffer never in 0 page lda (FINDL),Y ; load byte cmp CHAR2 ; compare 2nd char bne find_loop found_it: ldx #FINDL jsr inc16 ; increment pointer sec ; set that we were correct find_loop_done: rts ;============================================= ; center_and_print - centers and prints string ;============================================= center_and_print: ;================================ ; inlined strlen - count length of string ;================================ strlen: jsr reset_output ; reset the output pointer sty COUNT ; set count to zero strlen_loop: lda (OUTPUTL),Y beq strlen_done inc COUNT ; if not zero, increment count jsr inc_pointer bne strlen_loop ; same as jmp, pointer never in 0 page strlen_done: sec lda #40 ; width of screen sbc COUNT bmi no_center lsr A ; divide by 2 adc #0 ; round up if carry tax jsr PRBL2 ; print X blanks jsr reset_output ; reset output pointer no_center: ldx COUNT print_loop: lda (OUTPUTL),Y jsr COUT ; output a char inc OUTPUTL ; increment address bne cap_carry inc OUTPUTH ; handle overflow cap_carry: dex bne print_loop jsr CROUT ; output to screen rts ;========================================= ; reset_output - reset OUTPUT H&L pointers ;========================================= reset_output: lda #OUTPUT sta OUTPUTH rts ;================================= ; detect_ram ;================================= ; ; uses lookup table from ; http://web.pdx.edu/~heiss/technotes/misc/tn.misc.07.html ; to get model type. ; Then make some intelligent guesses about RAM size detect_ram: ldx #64 ; set default ram to 64 bytes lda $FBB3 ; this address cmp #$38 ; is $38 on an original apple ii bne apple_iiplus apple_ii: beq done_detecting ; we were apple II, done apple_iiplus: cmp #$EA ; are we an apple iie bne apple_iie ; if so keep going ; if we get here we're a ii+ or iii in emulation mode ldx #48 ; we're an Apple II+ ; we assume 48k even though it can be ; 64k if language card ; too lazy to detect that beq done_detecting apple_iie: lda $FBC0 beq apple_iic ; check for apple iic cmp #$E0 ; if we're an Apple IIe (original) bne done_detecting ; then use 64K and finish apple_iie_enhanced: apple_iic: ldx #128 done_detecting: stx RAMSIZE rts error: jmp exit ;================================= ; get_dos_buffer ;================================= ; ; Dos buffer format ; 0x000-0x0ff = data buffer ; 0x100-0x1ff = t/s list buffer ; 0x200-0x22c = file manager workarea (45 bytes) ; 0x22d-0x24a = file name buffer ; 0x24b-0x24c = address of file manager workarea ; 0x24d-0x24e = address of t/s list buffer ; 0x24f-0x250 = adress of data sector buffer ; 0x251-0x252 = address of file name field for the next buffer ; In DOS, $3D2 points to 0x22d of first buffer ; add 0x24 to get chain pointer open: ; allocate one of the DOS buffers so we don't have to set them up allocate_dos_buffer: lda $3D2 ; DOS load point sta DOSBUFH ldy #0 sty DOSBUFL buf_loop: lda (DOSBUFL),Y ; locate next buffer pha ; push on stack ; we need this later ; to test validity iny ; increment y lda (DOSBUFL),Y ; load next byte sta DOSBUFH ; store to buffer pointerl pla ; restore value from stack sta DOSBUFL ; store to buffer pointerh bne found_buffer ; if not zero, found a buffer lda DOSBUFH ; also if not zero, found a buffer beq error ; no buffer found, exit found_buffer: ldy #0 ; get filename lda (DOSBUFL),Y ; get first byte beq good_buffer ; if zero, good buffer ; in use ldy #$24 ; point to next bne buf_loop ; and loop good_buffer: lda #$78 sta (DOSBUFL),Y ; mark as in use (can be any !0) keep_opening: ldy #0 lda #OPEN ; set operation code to OPEN sta (FILEML),y ldy #2 ; point to record length lda #0 ; set it to zero (16-bits) sta (FILEML),y iny sta (FILEML),y iny ; point to volume num (0=any) sta (FILEML),y jsr LOCATE_RWTS_PARAM ; get current RWTS parameters ; so we can get disk/slot info sty RWTSL sta RWTSH ldy #1 lda (RWTSL),y ; get slot*16 lsr a lsr a lsr a lsr a ; divide by 16 ldy #6 ; address of slot sta (FILEML),y ; store it ldy #2 lda (RWTSL),y ; get drive ldy #5 ; address of drive sta (FILEML),y filemanager_interface: ldy #$1E dbuf_loop: lda (DOSBUFL),y ; get three buffer pointers pha ; push onto stack iny ; increment pointer cpy #$24 ; have we incremented 6 times? bcc dbuf_loop ; if not, loop ldy #$11 ; point to the end of the same struct ; in file manager fmgr_loop: pla ; pop value sta (FILEML),Y ; store it dey ; work backwards cpy #$c ; see if we are done bcs fmgr_loop ; loop jmp FILEMANAGER ; run filemanager ;==================== ; close DOS file ;==================== close: ldy #0 ; command offset lda #CLOSE ; load close sta (FILEML),y jsr filemanager_interface ldy #0 ; mark dos buffer as free again tya sta (DOSBUFL),y rts ;========================= ; read from dos file ;========================= read: ldy #0 ; command offset lda #READ sta (FILEML),y iny ; point to sub-opcode lda #2 ; "range of bytes" sta (FILEML),y ldy #6 ; point to number of bytes to read lda #$ff sta (FILEML),y ; we want to read 255 bytes iny lda #$00 sta (FILEML),y ldy #8 ; buffer address lda #disk_buff sta (FILEML),y bne filemanager_interface ; same as JMP ;; ********************* ;; BSS ;; ********************* .bss R: .res (N-F) zp_save: .res 32 disk_buff: .res 256 ;; ********************* ;; DATA ;; ********************* .data filename: ; CPUINFO__6502 (padded to be 30 chars long) .byte $C3,$D0,$D5,$C9,$CE,$C6,$CF,$DF .byte $DF,$B6,$B5,$B0,$B2,$A0,$A0,$A0 .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 .byte $A0,$A0,$A0,$A0,$A0,$A0 VERSION: ; "Linux Version 2.6.22.6, Compiled 2007" .byte $CC,$E9,$EE,$F5,$F8,$A0,$D6,$E5,$F2,$F3,$E9,$EF,$EE,$A0,$B2,$AE .byte $B6,$AE,$B2,$B2,$AE,$B6,$AC,$A0,$C3,$EF,$ED,$F0,$E9,$EC,$E5,$E4 .byte $A0,$B2,$B0,$B0,$B7,$00 ONE: ; "One " .byte $CF,$EE,$E5,$A0,$00 MHZ: ; "MHz " .byte $CD,$C8,$FA,$A0,$00 PROCESSOR: ; " Processor, " .byte $A0,$D0,$F2,$EF,$E3,$E5,$F3,$F3,$EF,$F2,$AC,$A0,$00 RAM: ; "kB RAM" .byte $EB,$C2,$A0,$D2,$C1,$CD,$00 LOGO: .byte 255,27,91,48,59,49,59,51,55 .byte 159,59,52,55,109,35,204,247,192,7,51 .byte 141,48,200,27,27,91,196,7,203,31,28,12,59 .byte 15,52,48,109,10,192,247,1,96,26,56,44,156 .byte 31,27,91,51,49,109,204,4,65,172,13,36 .byte 2,28,16,79,13,32,16,65,147,152,131,52,28,52,204,16 .byte 16,12,36,111,57,236,167,28,8,51,22,20,137,85,44,96 .byte 0,43,97,214,113,226,200,203,8,212,9,211,16,43,89,245,209 .byte 0,128,17,210,24,13,40,28,20,13,44,28,28,240,74,26,91 .byte 0,13,80,95,101,135,101,43,85,245,205,205,40,205,20,137,65 .byte 0,29,135,66,75,114,83,28,120,15,98,135,109,85,88,247,193 .byte 0,232,43,244,151,73,120,61,176,27,95,151,176,18,43,171,202 .byte 16,223,22,26,245,90,245,217,63,51,27,86,146,91,176,2 .byte 0,12,29,211,200,172,57,23,102,50,246,110,109,236,68,96,94 .byte 8,175,10,166,105,20,1,48,51,11,222,31,49,15,211,188 .byte 0,175,79,25,86,170,69,82,219,40,82,70,127,8,83,219,35 .byte 0,169,85,170,53,24,33,18,104,145,42,200,34,178,104,112,45 .byte 0,198,80,178,121,145,74,112,49,248,81,243,40,221,23,255,23 .byte 8,2,54,3,36,229,66,10 LOGO_END: