aboutsummaryrefslogtreecommitdiff
path: root/tools/apultra/asm/6502
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2021-01-09 09:01:05 +0000
committerJuan J. Martinez <jjm@usebox.net>2021-01-09 09:01:05 +0000
commit9bcf1e97960c0da7322a868efdbc07e2650716fe (patch)
treede6d32ad5b0e567991bd3eb262902c15a77074d9 /tools/apultra/asm/6502
parent3b31adf01305e522f7e28c1435fb47418ce43267 (diff)
downloadubox-msx-lib-9bcf1e97960c0da7322a868efdbc07e2650716fe.tar.gz
ubox-msx-lib-9bcf1e97960c0da7322a868efdbc07e2650716fe.zip
Extra libs: ap.lib
aPLib support with apultra.
Diffstat (limited to 'tools/apultra/asm/6502')
-rw-r--r--tools/apultra/asm/6502/aplib_6502.asm257
-rw-r--r--tools/apultra/asm/6502/aplib_6502_b.asm218
2 files changed, 475 insertions, 0 deletions
diff --git a/tools/apultra/asm/6502/aplib_6502.asm b/tools/apultra/asm/6502/aplib_6502.asm
new file mode 100644
index 0000000..1bc11b4
--- /dev/null
+++ b/tools/apultra/asm/6502/aplib_6502.asm
@@ -0,0 +1,257 @@
+; ***************************************************************************
+; ***************************************************************************
+;
+; aplib_6502.s
+;
+; NMOS 6502 decompressor for data stored in Jorgen Ibsen's aPLib format.
+;
+; Includes support for Emmanuel Marty's enhancements to the aPLib format.
+;
+; The code is 252 bytes long for standard format, 270 for enhanced format.
+;
+; This code is written for the ACME assembler.
+;
+; Copyright John Brandwood 2019.
+;
+; Distributed under the Boost Software License, Version 1.0.
+; (See accompanying file LICENSE_1_0.txt or copy at
+; http://www.boost.org/LICENSE_1_0.txt)
+;
+; ***************************************************************************
+; ***************************************************************************
+
+
+
+; ***************************************************************************
+; ***************************************************************************
+;
+; Decompression Macros
+;
+
+ ;
+ ; Macro to increment the source pointer to the next page.
+ ;
+
+ !macro APL_INC_PAGE {
+ inc <apl_srcptr + 1
+ }
+
+ ;
+ ; Macro to read a byte from the compressed source data.
+ ;
+
+ !macro APL_GET_SRC {
+ lda (apl_srcptr),y
+ inc <apl_srcptr + 0
+ bne .skip
+ +APL_INC_PAGE
+.skip:
+ }
+
+
+
+; ***************************************************************************
+; ***************************************************************************
+;
+; Data usage is last 12 bytes of zero-page.
+;
+
+apl_bitbuf = $F7 ; 1 byte.
+apl_offset = $F8 ; 1 word.
+apl_winptr = $FA ; 1 word.
+apl_srcptr = $FC ; 1 word.
+apl_dstptr = $FE ; 1 word.
+apl_length = apl_winptr
+
+
+; ***************************************************************************
+; ***************************************************************************
+;
+; apl_decompress - Decompress data stored in Jorgen Ibsen's aPLib format.
+;
+; Args: apl_srcptr = ptr to compessed data
+; Args: apl_dstptr = ptr to output buffer
+; Uses: lots!
+;
+; As an optimization, the code to handle window offsets > 64768 bytes has
+; been removed, since these don't occur with a 16-bit address range.
+;
+; As an optimization, the code to handle window offsets > 32000 bytes can
+; be commented-out, since these don't occur in typical 8-bit computer usage.
+;
+
+apl_decompress: ldy #0 ; Initialize source index.
+
+ lda #$80 ; Initialize an empty
+ sta <apl_bitbuf ; bit-buffer.
+
+ ;
+ ; 0 bbbbbbbb - One byte from compressed data, i.e. a "literal".
+ ;
+
+.literal: +APL_GET_SRC
+
+.write_byte: ldx #0 ; LWM=0.
+
+ sta (apl_dstptr),y ; Write the byte directly to
+ inc <apl_dstptr + 0 ; the output.
+ bne .next_tag
+ inc <apl_dstptr + 1
+
+.next_tag: asl <apl_bitbuf ; 0 bbbbbbbb
+ bne .skip0
+ jsr .load_bit
+.skip0: bcc .literal
+
+.skip1: asl <apl_bitbuf ; 1 0 <offset> <length>
+ bne .skip2
+ jsr .load_bit
+.skip2: bcc .copy_large
+
+ asl <apl_bitbuf ; 1 1 0 dddddddn
+ bne .skip3
+ jsr .load_bit
+.skip3: bcc .copy_normal
+
+ ; 1 1 1 dddd - Copy 1 byte within 15 bytes (or zero).
+
+.copy_short: lda #$10
+.nibble_loop: asl <apl_bitbuf
+ bne .skip4
+ pha
+ jsr .load_bit
+ pla
+.skip4: rol
+ bcc .nibble_loop
+ beq .write_byte ; Offset=0 means write zero.
+
+ eor #$FF ; Read the byte directly from
+ tay ; the destination window.
+ iny
+ dec <apl_dstptr + 1
+ lda (apl_dstptr),y
+ inc <apl_dstptr + 1
+ ldy #0
+ beq .write_byte
+
+ ;
+ ; 1 1 0 dddddddn - Copy 2 or 3 within 128 bytes.
+ ;
+
+.copy_normal: +APL_GET_SRC ; 1 1 0 dddddddn
+ lsr
+ beq .finished ; Offset 0 == EOF.
+
+ sta <apl_offset + 0 ; Preserve offset.
+ sty <apl_offset + 1
+ tya ; Y == 0.
+ tax ; Bits 8..15 of length.
+ adc #2 ; Bits 0...7 of length.
+ bne .do_match ; NZ from previous ADC.
+
+ ;
+ ; Subroutines for byte & bit handling.
+ ;
+
+.get_gamma: lda #1 ; Get a gamma-coded value.
+.gamma_loop: asl <apl_bitbuf
+ bne .skip5
+ pha
+ jsr .load_bit
+ pla
+.skip5: rol
+ rol <apl_length + 1
+ asl <apl_bitbuf
+ bne .skip6
+ pha
+ jsr .load_bit
+ pla
+.skip6: bcs .gamma_loop
+
+.finished: rts ; All decompressed!
+
+ ;
+ ; 1 0 <offset> <length> - gamma-coded LZSS pair.
+ ;
+
+.copy_large: jsr .get_gamma ; Bits 8..15 of offset (min 2).
+ sty <apl_length + 1 ; Clear hi-byte of length.
+
+ cpx #1 ; CC if LWM==0, CS if LWM==1.
+ sbc #2 ; -3 if LWM==0, -2 if LWM==1.
+ bcs .normal_pair ; CC if LWM==0 && offset==2.
+
+ jsr .get_gamma ; Get length (A=lo-byte & CC).
+ ldx <apl_length + 1
+ bcc .do_match ; Use previous Offset.
+
+.normal_pair: sta <apl_offset + 1 ; Save bits 8..15 of offset.
+
+ +APL_GET_SRC
+ sta <apl_offset + 0 ; Save bits 0...7 of offset.
+
+ jsr .get_gamma ; Get length (A=lo-byte & CC).
+ ldx <apl_length + 1
+
+ ldy <apl_offset + 1 ; If offset < 256.
+ beq .lt256
+ cpy #$7D ; If offset >= 32000, length += 2.
+ bcs .match_plus2
+ cpy #$05 ; If offset >= 1280, length += 1.
+ bcs .match_plus1
+ bcc .do_match
+.lt256: ldy <apl_offset + 0 ; If offset < 128, length += 2.
+ bmi .do_match
+
+ sec ; aPLib gamma returns with CC.
+
+.match_plus2: adc #1 ; CS, so ADC #2.
+ bcs .match_plus256
+
+.match_plus1: adc #0 ; CS, so ADC #1, or CC if fall
+ bcc .do_match ; through from .match_plus2.
+
+.match_plus256: inx
+
+.do_match: eor #$FF ; Negate the lo-byte of length
+ tay ; and check for zero.
+ iny
+ beq .calc_addr
+ eor #$FF
+
+ inx ; Increment # of pages to copy.
+
+ clc ; Calc destination for partial
+ adc <apl_dstptr + 0 ; page.
+ sta <apl_dstptr + 0
+ bcs .calc_addr
+ dec <apl_dstptr + 1
+
+.calc_addr: sec ; Calc address of match.
+ lda <apl_dstptr + 0
+ sbc <apl_offset + 0
+ sta <apl_winptr + 0
+ lda <apl_dstptr + 1
+ sbc <apl_offset + 1
+ sta <apl_winptr + 1
+
+.copy_page: lda (apl_winptr),y
+ sta (apl_dstptr),y
+ iny
+ bne .copy_page
+ inc <apl_winptr + 1
+ inc <apl_dstptr + 1
+ dex ; Any full pages left to copy?
+ bne .copy_page
+
+ inx ; LWM=1.
+ jmp .next_tag
+
+ ;
+ ; Subroutines for byte & bit handling.
+ ;
+
+.load_bit: +APL_GET_SRC ; Reload an empty bit-buffer
+ rol ; from the compressed source.
+ sta <apl_bitbuf
+ rts
diff --git a/tools/apultra/asm/6502/aplib_6502_b.asm b/tools/apultra/asm/6502/aplib_6502_b.asm
new file mode 100644
index 0000000..7963e02
--- /dev/null
+++ b/tools/apultra/asm/6502/aplib_6502_b.asm
@@ -0,0 +1,218 @@
+; -----------------------------------------------------------------------------
+; aplib_6502_b.s - fast aPLib backward decompressor for 6502 - 253 bytes
+; written for the ACME assembler
+;
+; jsr apl_decompress to unpack data backwards.
+; create backwards compressed data with apultra -b or oapack -b
+;
+; in:
+; * apl_srcptr (low and high byte) = last byte of compressed data
+; * apl_dstptr (low and high byte) = last byte of decompression buffer
+;
+; out:
+; * apl_dstptr (low and high byte) = first byte of decompressed data
+;
+; Copyright (C) 2020 Emmanuel Marty
+; With parts of the code inspired by John Brandwood, Peter Ferrie
+;
+; This software is provided 'as-is', without any express or implied
+; warranty. In no event will the authors be held liable for any damages
+; arising from the use of this software.
+;
+; Permission is granted to anyone to use this software for any purpose,
+; including commercial applications, and to alter it and redistribute it
+; freely, subject to the following restrictions:
+;
+; 1. The origin of this software must not be misrepresented; you must not
+; claim that you wrote the original software. If you use this software
+; in a product, an acknowledgment in the product documentation would be
+; appreciated but is not required.
+; 2. Altered source versions must be plainly marked as such, and must not be
+; misrepresented as being the original software.
+; 3. This notice may not be removed or altered from any source distribution.
+; -----------------------------------------------------------------------------
+
+ ; Zero page locations
+
+apl_gamma2_hi = $F6
+apl_bitbuf = $F7
+apl_offset = $F8
+apl_winptr = $FA
+apl_srcptr = $FC
+apl_dstptr = $FE
+
+ ; Read a byte from the source into A. Trashes X
+
+ !macro APL_GET_SRC {
+ lda (apl_srcptr),y
+ ldx <apl_srcptr+0
+ bne .src_page_done
+ dec <apl_srcptr+1
+.src_page_done: dec <apl_srcptr+0
+ }
+
+ ; Write a byte to the destinatipn
+
+ !macro APL_PUT_DST {
+ sta (apl_dstptr),y
+ lda <apl_dstptr+0
+ bne .dst_page_done
+ dec <apl_dstptr+1
+.dst_page_done: dec <apl_dstptr+0
+ }
+
+ ; Read one bit from the source into the carry, trash A
+
+ !macro APL_GET_BIT {
+ asl <apl_bitbuf
+ bne .has_bits
+ jsr apl_load_bits
+.has_bits:
+ }
+
+ ; Read one bit from the source into the carry, preserve A
+
+ !macro APL_GET_BIT_SAVEA {
+ asl <apl_bitbuf
+ bne .has_bits
+ pha
+ jsr apl_load_bits
+ pla
+.has_bits:
+ }
+
+ ; Decompress aPLib data backwards
+
+apl_decompress: lda #$80 ; initialize empty bit queue
+ sta <apl_bitbuf ; plus bit to roll into carry
+ ldy #$00 ; clear Y for indirect addr
+
+.copy_literal: +APL_GET_SRC ; read literal from source
+.write_literal: +APL_PUT_DST ; write literal to destination
+
+ ldx #$00 ; clear 'follows match' flag
+
+.next_token: +APL_GET_BIT ; read 'literal or match' bit
+ bcc .copy_literal ; if 0: literal
+
+ +APL_GET_BIT ; read '8+n bits or other' bit
+ bcc .long_match ; if 10x: long 8+n bits match
+
+ ; 11x: other type of match
+
+ +APL_GET_BIT ; read '7+1 match or short literal' bit
+ bcs .short_match ; if 111: 4 bit offset for 1-byte copy
+
+ +APL_GET_SRC ; read low byte of offset + length bit
+ lsr ; shift offset into place, len bit into carry
+ beq .done ; check for EOD
+ sta <apl_offset+0 ; store low byte of offset
+ sty <apl_offset+1 ; set high byte of offset to 0
+
+ tya ; set A to 0
+ sty <apl_gamma2_hi ; set high byte of len to 0
+ adc #$02 ; add 2 or 3 depending on len bit in carry
+ ; now, low part of len is in A
+ ; high part of len in apl_gamma2_hi is 0
+ ; offset is written to apl_offset
+ bne .got_len ; go copy matched bytes
+
+.long_match: jsr .get_gamma2 ; 10: read gamma2 high offset bits in A
+ sty <apl_gamma2_hi ; zero out high byte of gamma2
+
+ cpx #$01 ; set carry if following literal
+ sbc #$02 ; substract 3 if following literal, 2 otherwise
+ bcs .no_repmatch
+
+ jsr .get_gamma2 ; read repmatch length: low part in A
+ bcc .got_len ; go copy large match
+ ; (carry is always clear after .get_gamma2)
+
+.short_match: lda #$10 ; clear offset, load end bit into place
+.read_short_offs: +APL_GET_BIT_SAVEA ; read one bit of offset into carry
+ rol ; shift into A, shift end bit as well
+ bcc .read_short_offs ; loop until end bit is shifted out into carry
+
+ beq .write_literal ; zero offset means write a 0
+ tay
+ lda (apl_dstptr),y ; load backreferenced byte
+ ldy #$00 ; clear Y again
+ beq .write_literal ; go write byte to destination
+
+.get_gamma2: lda #$01 ; 1 so it gets shifted to 2
+.gamma2_loop: +APL_GET_BIT_SAVEA ; read data bit
+ rol ; shift into low byte
+ rol <apl_gamma2_hi ; shift into high byte
+ +APL_GET_BIT_SAVEA ; read continuation bit
+ bcs .gamma2_loop ; loop until a zero continuation bit is read
+.done: rts
+
+.no_repmatch: sta <apl_offset+1 ; write high byte of offset
+ +APL_GET_SRC ; read low byte of offset from source
+ sta <apl_offset+0 ; store low byte of offset
+
+ jsr .get_gamma2 ; read match length: low part in A
+
+ ldx <apl_offset+1 ; high offset byte is zero?
+ beq .offset_1byte ; if so, offset < 256
+
+ ; offset is >= 256.
+
+ cpx #$7d ; offset >= 32000 (7d00) ?
+ bcs .offset_incby2 ; if so, increase match len by 2
+ cpx #$05 ; offset >= 1280 (0500) ?
+ bcs .offset_incby1 ; if so, increase match len by 1
+ bcc .got_len ; length is fine, go copy
+
+.offset_1byte: ldx <apl_offset+0 ; offset < 128 ?
+ bmi .got_len ; if so, increase match len by 2
+ sec ; carry must be set below
+
+.offset_incby2: adc #$01 ; add 1 + set carry (from bcs or sec)
+ bcs .len_inchi ; go add 256 to len if overflow
+
+ ; carry clear: fall through for no-op
+
+.offset_incby1: adc #$00 ; add 1 + carry
+ bcc .got_len
+.len_inchi: inc <apl_gamma2_hi ; add 256 to len if low byte overflows
+
+.got_len: tax ; transfer low byte of len into X
+ beq .add_offset
+ inc <apl_gamma2_hi
+
+.add_offset: clc ; add dest + match offset
+ lda <apl_dstptr+0 ; low 8 bits
+ adc <apl_offset+0
+ sta <apl_winptr+0 ; store back reference address
+ lda <apl_dstptr+1 ; high 8 bits
+ adc <apl_offset+1
+ sta <apl_winptr+1 ; store high 8 bits of address
+
+.copy_match_loop: lda (apl_winptr),y ; read one byte of backreference
+ +APL_PUT_DST ; write byte to destination
+
+ lda <apl_winptr+0 ; decrement backreference address
+ bne .backref_page_done
+ dec <apl_winptr+1
+.backref_page_done:
+ dec <apl_winptr+0
+
+ dex ; loop to copy all matched bytes
+ bne .copy_match_loop
+ dec <apl_gamma2_hi
+ bne .copy_match_loop
+
+ ; X is 0 when exiting the loop above
+ inx ; set 'follows match' flag
+ jmp .next_token ; go decode next token
+
+apl_load_bits: lda (apl_srcptr),y ; read 8 bits from source
+ rol ; shift bit queue, and high bit into carry
+ sta <apl_bitbuf ; save bit queue
+
+ lda <apl_srcptr+0
+ bne .bits_page_done
+ dec <apl_srcptr+1
+.bits_page_done: dec <apl_srcptr+0
+ rts