;; this program will test the I/O decoding for the RAM configuration
;; selection.

;;
;; This program assumes a Dk'Tronics/Amstrad/Dobbertin compatible RAM expansion.
;;
;; This program assumes that the expansion is connected/active, and the recommeded
;; I/O port is used to select the configurations. (&7fxx)
;;
;; the program does the following:
;; 1. using recommended I/O port address write byte to ram (using configuration 0)
;; 2. using recommended I/O port address set configuration 4, so 16k of extra ram is 
;; paged into &4000-&7fff
;; 3. using current I/O port address attempt to set configuration 0 (16k of extra ram is paged out)
;; 4. write a byte
;; 5. using recommended I/O port address set configuration 0.
;; 5. test byte
;;
;; if I/O port address is valid, then the previous ram configuration will be restored and the byte
;; will be modified with the new value.
;;
;; if I/O port address is invalid, then the previous ram configuration will not be restored and the byte
;; will not be modified.
;;


;; the ram block will be swapped into &4000-&8000
org &8000
nolist


;; clear result table
ld hl,result_table
ld e,l
ld d,h
inc de
ld (hl),0
ld bc,8191
ldir


;; test all 65536 I/O addressess
ld bc,0
;; this is the initial I/O port address
ld de,0

ld ix,result_table

.next_config
push bc

;; ram address to write
ld hl,&4000

;; set ram configuration 0
;; using recommended I/O port address
ld bc,&7fc0
out (c),c

;; write byte
ld (hl),&00

;; set ram configuration 4
;; using recommended I/O port address
ld bc,&7fc4
out (c),c

;; set ram configuration using a different I/O port address
;; compared to recommended port address
ld b,d
ld c,e
ld a,&c0
out (c),a

;; write byte
ld (hl),&ff

;; if this I/O port can be used, then the byte at &4000
;; will be different to the original value after ram configuration 0 has been selected

;; set ram configuration 0
;; using recommended I/O port address
ld bc,&7fc0
out (c),c

;; get byte
ld a,(hl)

;; has it changed?
;; if yes, then the I/O port address can be used to change the ram configuration
;; if no, then the I/O port address can't be used to change the ram configuration
cp 0
jr z,failure

;; get OR bit
ld a,(or_value)
;; combine with existing byte
or (ix+0)
;; store back
ld (ix+0),a

.failure
pop bc

;; shift OR value
ld a,(or_value)
rlca
ld (or_value),a

;; update bit count and increment position in table
;; if bit count gets to 8.
ld a,(bit_count)
inc a
cp 8
jr c,nbit
inc ix
xor a
.nbit
ld (bit_count),a

inc de   ;; next I/O port address
dec bc   ;; loop
ld a,b
or c
jr nz,next_config

;; operation stages:
;; 1. use XOR => (a XOR b) => where bits are 0, then they are the same. where bits are 1, then they are not the same
;; 2. use NOT => NOT (a XOR b) => generates a mask for bits (1 where a bit is the same in a and b, 0 for a bit that is different)
;; 3. use a AND (NOT (a XOR b)) => keeps values of bits that are the same
;;
;; the mask and value is updated for each value where the I/O address is valid and produces a result

ld a,0
ld (first),a

ld hl,result_table
ld de,0
ld bc,8192
ld ix,io_info
ld (ix+0),0
ld (ix+1),0
.generate_result
push bc

ld c,(hl)
inc hl

;; go through the result byte
;; if a bit is "1" then this I/O port can be used.
;; if a bit is "0" then this I/O port can't be used
ld b,8
.gr2
;; is this bit significant?
ld a,c
and 128
jr z,not_useable

;; yes!

ld a,(first)
or a
jr nz,gr4
ld a,&ff
ld (first),a
ld (ix+0),a
ld (ix+1),a

jr gr5

.gr4

;; calc "important bits mask"
;; bit is 1 in mask if the bit in this position is important
;; bit is 0 in mask if the bit in this position is not important

;; low byte of bits
ld a,(ix+2)
;; current bits
xor e
;; NOT
cpl
;; low byte of mask
and (ix+0)
;; store low byte of mask
ld (ix+0),a

;; high byte of bits
ld a,(ix+3)
;; current bits
xor d
;; NOT
cpl
;; high byte of mask
and (ix+1)
;; store high byte of mask
ld (ix+1),a

.gr5
;; store new value masked with "important bits mask"
ld a,e
and (ix+0)
ld (ix+2),a

ld a,d
and (ix+1)
ld (ix+3),a


.not_useable
inc de
rlc c
djnz gr2


pop bc
dec bc
ld a,b
or c
jp nz,generate_result

;;----------------------------------------------------------------------------
;; display the result
;; "1" indicates that this bit must be 1,
;; "0" indicates that this bit must be 0
;; "-" indicates that this bit is ignored.

;; show result

;; force horizontal to valid number
ld bc,&bc00
out (c),c
ld bc,&bd3f
out (c),c

;; 16-bits
ld b,2
.sr
push bc

ld b,8
ld e,(ix+1) ;; mask for high byte
ld d,(ix+3) ;; bits for high byte

.sr2
ld a,e
and 128

ld a,"-"	;; character for bit is ignored
jr z,sr3

;; setup character for bit that is used
ld a,d
and 128		;; isolate state of bit 7
add a,a		;; bit 7 into carry (A will be 0 after this operation)
adc a,"0"		;; if bit is 0 -> "0" character, if bit is 1 -> "1" character
.sr3
call &bb5a		;; display character

rl e			;; shift mask
rl d			;; shift data
djnz sr2

dec ix		;; now fetch mask for low byte and mask for high byte

pop bc
djnz sr


ret

.OR_value
defb 1
.BIT_count
defb 0

.first
defb 0

.IO_info
defw 0  ;; mask
defw 0  ;; bits


;; a bit will be 0 if failure, 1 if successs
.result_table
defs 8192