mirror of
https://codeberg.org/hkzlab/TK2000_floppicator.git
synced 2025-12-25 09:02:28 +11:00
Add empty floppicator project
This commit is contained in:
parent
667ffdd197
commit
be70ff936e
29 changed files with 2153 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
obj/*.o
|
||||
obj/*.lst
|
||||
out/*.bin
|
||||
out/*.hex
|
||||
out/*.wav
|
||||
48
Makefile
Normal file
48
Makefile
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
VPATH = src
|
||||
|
||||
# Apple Commander tool JAR
|
||||
ACMD=/d/Users/hkzla/software/applecommander/AppleCommander-ac-1.11.0.jar
|
||||
JAVA=/c/Program\ Files/Microsoft/jdk-21.0.8.9-hotspot/bin/java.exe
|
||||
|
||||
# Program output
|
||||
SW_NAME=floppicator
|
||||
|
||||
PRG=master
|
||||
|
||||
# Libraries
|
||||
LIBS=clib-6502.a
|
||||
|
||||
ASM_SRCS = tk2k_startup.s preserve_zero_pages.s disk2.s
|
||||
C_SRCS = main.c
|
||||
|
||||
# Object files
|
||||
OBJS = $(ASM_SRCS:%.s=%.o) $(C_SRCS:%.c=%.o)
|
||||
|
||||
all: $(SW_NAME).woz
|
||||
|
||||
%.o: %.s
|
||||
as6502 --core=6502 --list-file=$(@:%.o=obj/%.lst) -o obj/$@ $<
|
||||
|
||||
%.o: %.c
|
||||
cc6502 --core=6502 -O2 --list-file=$(@:%.o=obj/%.lst) --char-is-unsigned --pedantic-errors -o obj/$@ $<
|
||||
|
||||
$(PRG).hex: $(OBJS)
|
||||
(cd obj ; ln6502 -g ../linker-files/linker.scm $^ -o ../out/$@ $(LIBS) -l --cross-reference --cstartup=tk2k --no-automatic-placement-rules --output-format intel-hex --rom-code)
|
||||
|
||||
$(PRG).bin: $(PRG).hex
|
||||
(cd out ; objcopy -I ihex -O binary $(PRG).hex $(PRG).bin)
|
||||
|
||||
$(SW_NAME).dsk: $(PRG).bin
|
||||
(cd out ; cp ../dsk/TK2048_AUTO_BRUN.dsk ./$(SW_NAME).dsk; \
|
||||
cat $(PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk HELLO B 0x800;)
|
||||
|
||||
$(SW_NAME).woz: $(SW_NAME).dsk
|
||||
(cd out ; dsk2woz ./$(SW_NAME).dsk ./$(SW_NAME).woz)
|
||||
|
||||
clean:
|
||||
-rm obj/*.o
|
||||
-rm obj/*.lst
|
||||
-rm out/*.hex
|
||||
-rm out/*.bin
|
||||
-rm out/*.dsk
|
||||
-rm out/*.woz
|
||||
BIN
dsk/TK2048_AUTO_BRUN.dsk
Normal file
BIN
dsk/TK2048_AUTO_BRUN.dsk
Normal file
Binary file not shown.
BIN
dsk/TK2048_AUTO_RUN.dsk
Normal file
BIN
dsk/TK2048_AUTO_RUN.dsk
Normal file
Binary file not shown.
21
linker-files/linker.scm
Normal file
21
linker-files/linker.scm
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
(define memories
|
||||
'((memory zeroPage (address (#x56 . #xff)) (type ram)
|
||||
(section registers zpage zzpage))
|
||||
(memory firstPage (address (#x100 . #x1ff)) (section stack))
|
||||
(memory reserved (address (#x200 . #x7ff)) (type ram))
|
||||
(memory program (address (#x800 . #x15ff)) (type ram)
|
||||
(section (programStart #x800) (dii_critical_wr_code #x803) (dii_critical_rd_code #x90b) startup code switch idata cdata data_init_table))
|
||||
(memory dataMem (address (#x1600 . #x1fff)) (type ram) (section cstack zdata data heap zpsave))
|
||||
(memory displayPage1 (address (#x2000 . #x3fff)) (type ram))
|
||||
(memory upperMem (address (#x4000 . #x9bff)) (type ram))
|
||||
(memory diskBuffer (address (#x9c00 . #x9eff)) (type ram)) ;;; This memory will be used by the disk II routines as buffer
|
||||
(memory zeroPageBackup (address (#x9f00 . #x9fff)) (type ram) (section (zpsave #x9f00)))
|
||||
(memory displayPage2 (address (#xa000 . #xbfff)) (type ram))
|
||||
(memory io (address (#xc000 . #xc0ff)) (type ram))
|
||||
(memory rombank (address (#xc100 . #xffff)) (type rom))
|
||||
|
||||
(block cstack (size #x600))
|
||||
(block heap (size #x000))
|
||||
(block stack (size #x100))
|
||||
))
|
||||
|
||||
0
obj/empty
Normal file
0
obj/empty
Normal file
0
out/empty
Normal file
0
out/empty
Normal file
24
src/charset.h
Normal file
24
src/charset.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef _CHARSET_HEADER_
|
||||
#define _CHARSET_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// @ A B C D E F G H I
|
||||
// J K L M N O P Q R S
|
||||
// T U V W X Y Z [ \ ]
|
||||
// ^ _ ! " # $ % & ' (
|
||||
// ) * + , - . / 0 1 2
|
||||
// 3 4 5 6 7 8 9 : ; <
|
||||
// = > ?
|
||||
// Then graphic characters follow
|
||||
|
||||
#define CHAR_HEIGHT 8
|
||||
|
||||
#define ALPHA_OFFSET (1 * CHAR_HEIGHT
|
||||
#define SYMBOL_OFFSET (28 * CHAR_HEIGHT)
|
||||
#define NUM_OFFSET (48 * CHAR_HEIGHT)
|
||||
#define GRAPH_OFFSET (58 * CHAR_HEIGHT)
|
||||
|
||||
const uint8_t* const CHARSET = (uint8_t*)0xF200;
|
||||
|
||||
#endif /* _CHARSET_HEADER_ */
|
||||
46
src/disk2.h
Normal file
46
src/disk2.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef _DISK2_HEADER_
|
||||
#define _DISK2_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DII_MAX_TRACK 96
|
||||
#define DECODING_MAPPING_TABLE_DEFAULT_ADDRESS 0x0356
|
||||
#define ENCODING_MAPPING_TABLE_DEFAULT_ADDRESS 0x9D56
|
||||
#define WRITE_SIXES_BUFFER_DEFAULT_ADDRESS 0x9C00
|
||||
#define WRITE_TWOS_BUFFER_DEFAULT_ADDRESS 0x9D00
|
||||
|
||||
void dii_generate_6bit_decoding_mapping_table(uint8_t* dest);
|
||||
void dii_generate_6bit_encoding_mapping_table(uint8_t* dest);
|
||||
void dii_encode_gcr62_data(uint8_t *src, uint8_t* dest6, uint8_t* dest2);
|
||||
void dii_power_on(uint8_t io_offset, uint8_t drive_no);
|
||||
void dii_power_off(uint8_t io_offset);
|
||||
void dii_head_reposition(uint8_t io_offset, uint8_t cur_track, uint8_t dest_track);
|
||||
|
||||
/**
|
||||
* Patch the dii_write_sector routine to change the memory addresses used.
|
||||
* buf2 and buf6 must start at the beginning of a memory page, nibtab must fit completely inside a page.
|
||||
*/
|
||||
void dii_patch_write(uint8_t* buf6, uint8_t* buf2, uint8_t *nibtab);
|
||||
|
||||
/**
|
||||
* This routine requires the decoding mapping table to be initialized beforehand at
|
||||
* DECODING_MAPPING_TABLE_DEFAULT_ADDRESS, and uses space in page 3 as a buffer
|
||||
*
|
||||
* If the return value is 0, the read failed
|
||||
*/
|
||||
uint8_t dii_read_sector(uint8_t io_offset, uint8_t track, uint8_t sector, uint8_t *buffer, uint8_t aonly);
|
||||
|
||||
/**
|
||||
* Writes gcr 6+2 encoded data to the chosen sector. The track number is used to verify that we're on the correct
|
||||
* track before writing. This routine does not move the head.
|
||||
* The data must be pre-encoded.
|
||||
*
|
||||
* By default, it writes 86 bytes from the "twos" buffer located at 0x9C00, 256 bytes of "sixes" from the buffer
|
||||
* at 0x9D00 and using a 63 entries translation table preloaded at 0x9C56.
|
||||
* If these addresses need to be changes, use the dii_patch_write routine.
|
||||
*
|
||||
* If the return value is > 0, the write failed
|
||||
*/
|
||||
uint8_t dii_write_sector(uint8_t io_offset, uint8_t track, uint8_t sector);
|
||||
|
||||
#endif /* _DISK2_HEADER_ */
|
||||
726
src/disk2.s
Normal file
726
src/disk2.s
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
.rtmodel version,"1"
|
||||
.rtmodel core,"6502"
|
||||
|
||||
.extern _Vfp
|
||||
.extern _Vsp
|
||||
.extern _Zp
|
||||
|
||||
;;; I/O offsets
|
||||
DII_BASE: .equ 0xC080
|
||||
PH0_OFF: .equ DII_BASE + 0x00
|
||||
PH0_ON: .equ DII_BASE + 0x01
|
||||
PH1_OFF: .equ DII_BASE + 0x02
|
||||
PH1_ON: .equ DII_BASE + 0x03
|
||||
PH2_OFF: .equ DII_BASE + 0x04
|
||||
PH2_ON: .equ DII_BASE + 0x05
|
||||
PH3_OFF: .equ DII_BASE + 0x06
|
||||
PH3_ON: .equ DII_BASE + 0x07
|
||||
PWR_DWN: .equ DII_BASE + 0x08
|
||||
PWR_UP: .equ DII_BASE + 0x09
|
||||
SEL_D1: .equ DII_BASE + 0x0A
|
||||
SEL_D2: .equ DII_BASE + 0x0B
|
||||
READ_SW: .equ DII_BASE + 0x0C
|
||||
WRITE_SW: .equ DII_BASE + 0x0D
|
||||
CLEAR_SW: .equ DII_BASE + 0x0E
|
||||
SHIFT_SW: .equ DII_BASE + 0x0F
|
||||
|
||||
;;; Monitor routines
|
||||
MON_WAIT .equ 0xFCA8
|
||||
|
||||
;;; Memory addresses
|
||||
CONV_TABLE .equ 0x0356 ; 6+2 conversion table generated by BOOT0
|
||||
PAGE3_BUF .equ 0x0300
|
||||
|
||||
;;; Summing up the calling convention as...
|
||||
;;; Parameters are passed in register A and pseudo registers _Zp[0-7].
|
||||
;;; Registers X, Y and pseudo registers 0–23 are destroyed by a function call.
|
||||
;;; Pseudo registers 24–47 must be preserved.
|
||||
|
||||
.section code,text
|
||||
|
||||
;;; dii_generate_6bit_decoding_mapping_table:
|
||||
;;; Generates a 6+2 decoding table at the chosen address
|
||||
;;; Parameters:
|
||||
;;; - address [_Zp[0], _Zp[1]]: Address for the generation of the table
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
dii_generate_6bit_decoding_mapping_table:
|
||||
T_REGX$: .equ _Zp+4
|
||||
T_REGY$: .equ _Zp+3
|
||||
T_BITS$: .equ _Zp+2
|
||||
P_DESTH$: .equ _Zp+1
|
||||
P_DESTL$: .equ _Zp+0
|
||||
|
||||
ldy #0x00
|
||||
ldx #0x03
|
||||
|
||||
CreateTableLoop$:
|
||||
stx zp:T_BITS$
|
||||
txa
|
||||
asl a ; Arithmetic Shift Left, will put the high bit in carry
|
||||
bit zp:T_BITS$ ; Check of the overlap for the shifted value. If two one overlaps it means that there are two adjacent 1s
|
||||
beq reject$ ; Nope, refuse this number
|
||||
ora zp:T_BITS$ ; Merge shifted and non-shifted number
|
||||
eor #0xff ; Invert the value, we'll look for adjacent zeros (which have now become 1s)
|
||||
and #0x7e ; Discard msb and lsb, as one is mandatory and the other falsely generated with the shift
|
||||
check_dub0$:bcs reject$ ; Branch on C = 1. Initial bhi bit set or adjacent 0 bits set
|
||||
lsr a ; Shift right. lower bit into carry
|
||||
bne check_dub0$ ; Check all the bits until we're left with a 0
|
||||
tya ; We have a good one. Store it in the table
|
||||
sty zp:T_REGY$ ; Save y
|
||||
stx zp:T_REGX$ ; Save x
|
||||
ldy zp:T_REGX$ ; Y <- X
|
||||
sta (zp:P_DESTL$),y ; Bytes that reference this will have highest bit set, so this'll be accessed using CONV_TABLE-128
|
||||
ldy zp:T_REGY$ ; Recover y
|
||||
iny
|
||||
reject$: inx ; Next candidate
|
||||
bpl CreateTableLoop$
|
||||
|
||||
rts
|
||||
|
||||
;;; dii_generate_6bit_encoding_mapping_table:
|
||||
;;; Generates a 6bit mapping table to be used with GCR 6+2
|
||||
;;; Parameters:
|
||||
;;; - address [_Zp[0], _Zp[1]]: Address for the generation of the table
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
dii_generate_6bit_encoding_mapping_table:
|
||||
T_BITS$: .equ _Zp+2
|
||||
P_DESTH$: .equ _Zp+1
|
||||
P_DESTL$: .equ _Zp+0
|
||||
|
||||
ldy #0x00
|
||||
ldx #0x03
|
||||
|
||||
CreateTableLoop$:
|
||||
stx zp:T_BITS$
|
||||
txa
|
||||
asl a
|
||||
bit zp:T_BITS$
|
||||
beq reject$
|
||||
ora zp:T_BITS$
|
||||
eor #0xff
|
||||
and #0x7e
|
||||
check_dub0$:bcs reject$
|
||||
lsr a
|
||||
bne check_dub0$
|
||||
txa
|
||||
ora #0x80
|
||||
sta (zp:P_DESTL$),y
|
||||
iny
|
||||
reject$: inx ; Next candidate
|
||||
bpl CreateTableLoop$
|
||||
|
||||
rts
|
||||
|
||||
;;; dii_encode_gcr62_data:
|
||||
;;; Split 256 bytes of data into 256 + 86 6-bit nibbles
|
||||
;;; Parameters:
|
||||
;;; - source [_Zp[0], _Zp[1]]: Address for the 256 bytes data source
|
||||
;;; - buf6 [_Zp[2], _Zp[3]]: Address for the destination buffer that will contain 256 "sixes" nibble entries
|
||||
;;; - buf2 [_Zp[4], _Zp[5]]: Address for the destination buffer that will contain 86 "twos" nibble entries
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
;;;
|
||||
;;; We will go through the 86 bytes three times (which means a total of 258 runs). Note that we have a source
|
||||
;;; of 256 bytes, plus, the result will have to be 256+86, so, in the "twos" buffer we'll have one entry containing
|
||||
;;; (86*3)-256 = 2 spurious (duplicated entries);
|
||||
;;;
|
||||
dii_encode_gcr62_data:
|
||||
T_REGY$: .equ _Zp+7
|
||||
T_BITS$: .equ _Zp+6
|
||||
P_DEST2H$: .equ _Zp+5
|
||||
P_DEST2L$: .equ _Zp+4
|
||||
P_DEST6H$: .equ _Zp+3
|
||||
P_DEST6L$: .equ _Zp+2
|
||||
P_SRCH$: .equ _Zp+1
|
||||
P_SRCL$: .equ _Zp+0
|
||||
|
||||
ldx #0x00 ; This will address the current byte of the "twos"
|
||||
ldy #0x02 ; We'll use this as an offset into the source (and sixes) buffer. Note that this means the first call
|
||||
; will have BASE+1, the second BASE+0, while the third BASE+255, and will decrease from there
|
||||
; onward. Here are the two spurious entries.
|
||||
prenib1$: dey
|
||||
sty zp:T_REGY$ ; Save y
|
||||
txa ; Copy x to a
|
||||
tay ; then a to y ---- x -> y
|
||||
lda (P_DEST2L$),y ; Recover the current 'two'
|
||||
sta zp:T_BITS$ ; Save it on a temp register
|
||||
ldy zp:T_REGY$ ; Recover y
|
||||
|
||||
lda (zp:P_SRCL$),y ; Get current byte from the source buffer
|
||||
lsr a ; Put the two least significant bits into the corrisponding byte of the "twos"
|
||||
rol zp:T_BITS$
|
||||
lsr a
|
||||
rol zp:T_BITS$ ; Move two bits in
|
||||
sta (zp:P_DEST6L$),y; The remaining six bites will go in the 'sixes' buffer
|
||||
sty zp:T_REGY$ ; Save y
|
||||
txa ; Move X to Y
|
||||
tay
|
||||
lda zp:T_BITS$ ; Recover the updated 'two'
|
||||
sta (P_DEST2L$),y ; Save the updated 'two'
|
||||
ldy zp:T_REGY$ ; Recover y
|
||||
inx
|
||||
cpx #86
|
||||
bcc prenib1$ ; We'll repeat this 86 times
|
||||
ldx #0
|
||||
tya
|
||||
bne prenib1$ ; After decreasing y 258 times, we'll end up with 0 here (2 - 86 - 86 - 86 = -256) and go on
|
||||
|
||||
ldy #85
|
||||
prenib2$: lda (zp:P_DEST2L$),y
|
||||
and #0x3f
|
||||
sta (zp:P_DEST2L$),y
|
||||
dey
|
||||
bpl prenib2$
|
||||
|
||||
rts
|
||||
|
||||
;;; dii_power_on:
|
||||
;;; Selects and powers on a disk drive
|
||||
;;; Parameters:
|
||||
;;; - offset [A]: offset from the base address (DII_BASE) to access the Disk II interface
|
||||
;;; - drive [_Zp[0]]: Drive number, 0 for first drive, 1 for the next. Only the first bit will be considered
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
dii_power_on:
|
||||
P_OFFSET$: .equ _Zp+1
|
||||
P_DRVNO$: .equ _Zp+0
|
||||
|
||||
and #0x70 ; Mask the offset with 0x70, so we keep the bits relevant for "slot" selection
|
||||
sta zp:P_OFFSET$ ; Save the cleaned offset, we'll use it later to calculate the offset to turn on the drive
|
||||
|
||||
tax
|
||||
lda CLEAR_SW,x ; Enter read mode
|
||||
lda READ_SW,x
|
||||
|
||||
lda #0x01 ; Keep only the lsb of the drive number parameter
|
||||
and zp:P_DRVNO$
|
||||
ora zp:P_OFFSET$ ; Combine the result with the "slot" offset
|
||||
tax
|
||||
lda SEL_D1,x ; Select the proper drive
|
||||
|
||||
lda zp:P_OFFSET$
|
||||
tax
|
||||
lda PWR_UP,x ; Drive Power up
|
||||
|
||||
jsr __short_wait ; Wait for the driver to power on and the motor to spin up
|
||||
rts
|
||||
|
||||
;;; dii_power_off:
|
||||
;;; Selects and powers on a disk drive
|
||||
;;; Parameters:
|
||||
;;; - offset [A]: Offset from the base address (DII_BASE) to access the Disk II interface
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
dii_power_off:
|
||||
and #0x70 ; Mask the offset with 0x70, so we keep the bits relevant for "slot" selection
|
||||
tax
|
||||
lda PWR_DWN,x ; Drive Power down
|
||||
|
||||
rts
|
||||
|
||||
;;; dii_head_reposition:
|
||||
;;; Repositions the head according to the current track and destination track.
|
||||
;;; Keep in mind that the parameters to this function are taking "phase movements" into consideration, not the
|
||||
;;; DOS track number.
|
||||
;;; There is a correspondance of 1:2 between DOS tracks and phase movements, which means that TWO phase movements
|
||||
;;; will shift ONE track.
|
||||
;;; With standard DOS, all EVEN numbered tracks (0 included) are positioned under "phase 0" of the head, all ODD
|
||||
;;; numbered tracks will be positioned under "phase 2". Moving to phase 1 or 3 will get us to half-tracks.
|
||||
;;; Parameters:
|
||||
;;; - offset [A]: Offset from the base address (DII_BASE) to access the Disk II interface
|
||||
;;; - curtrack [_Zp[0]]: Current track number
|
||||
;;; - desttrack [_Zp[1]]: Desired destination track number
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
dii_head_reposition:
|
||||
P_OFFSET$: .equ _Zp+2
|
||||
P_DTRACK$: .equ _Zp+1
|
||||
P_CTRACK$: .equ _Zp+0
|
||||
|
||||
and #0x70
|
||||
sta zp:P_OFFSET$ ; Save the slot offset into a zp register
|
||||
H_MNLOOP$: lda zp:P_CTRACK$ ; Check and branch depending on relationship between current and destination track
|
||||
cmp zp:P_DTRACK$
|
||||
beq H_ALLDONE$
|
||||
bcc H_MOVUP$
|
||||
bcs H_MOVDOWN$
|
||||
|
||||
H_MOVDOWN$: dec zp:P_CTRACK$ ; Prepare to move down a track
|
||||
jmp H_DOWORK$
|
||||
|
||||
H_MOVUP$: inc zp:P_CTRACK$ ; Prepare to move up a track
|
||||
|
||||
H_DOWORK$: lda zp:P_CTRACK$
|
||||
and #0x03 ; We're going to keep only the 3 least significant bits of the current track
|
||||
asl a ; Shifting them left, now we're using the 4 msb of the byte
|
||||
; This multiplication is necessary because the phase on (or off) registers
|
||||
; are spaced by two between each other, and this calculates the offset. See later.
|
||||
ora zp:P_OFFSET$ ; And now combine the slot number with the track value we calculated in the previous step...
|
||||
tay ; a -> y
|
||||
lda PH0_ON,y ; We are offsetting the base to reach for both the slot number and the proper phase
|
||||
lda #0x56
|
||||
jsr MON_WAIT ; Call the monitor wait routine
|
||||
lda PH0_OFF,y ; Now turn the phase off
|
||||
jmp H_MNLOOP$ ; And go back checking the status of the relationship between current and destination tracks
|
||||
H_ALLDONE$: rts ; We're done
|
||||
|
||||
|
||||
__short_wait:
|
||||
lda #0xEF
|
||||
sta zp:_Zp ; We'll use the first two zp registers to hold a counter
|
||||
lda #0xD8
|
||||
sta zp:_Zp+1
|
||||
MWAITA$: ldy #0x12
|
||||
MWAITB$: dey
|
||||
bne MWAITB$
|
||||
inc zp:_Zp
|
||||
bne MWAITA$
|
||||
inc zp:_Zp+1
|
||||
bne MWAITA$
|
||||
rts
|
||||
|
||||
|
||||
;;; dii_patch_write:
|
||||
;;; Modifies the addresses in the dii_write_sector routine to change where data is read
|
||||
;;; Parameters:
|
||||
;;; - buf6 [_Zp[0] and _Zp[1]]: Address for the "sixes" buffer. 256 bytes in length. Must start at the beginning of a page+
|
||||
;;; - buf2 [_Zp[2] and _Zp[3]]: Address for the "twos" buffer. 86 bytes in length. Must start at the beginning of a page
|
||||
;;; - nibtab [_Zp[4] and _Zp[5]]: Address for the translation table for nibbles. 63 bytes in length.
|
||||
;;;
|
||||
;;; Returns: nothing
|
||||
;;;
|
||||
dii_patch_write:
|
||||
P_NIBTH$: .equ _Zp+5
|
||||
P_NIBTL$: .equ _Zp+4
|
||||
P_BUF2H$: .equ _Zp+3
|
||||
P_BUF2L$: .equ _Zp+2
|
||||
P_BUF6H$: .equ _Zp+1
|
||||
P_BUF6L$: .equ _Zp+0
|
||||
|
||||
;;; Common changes
|
||||
lda #0x00 ; Make sure we start at the beginning of the page
|
||||
sta _b2add01+1
|
||||
sta _b2add02+1
|
||||
sta _b6add01+1
|
||||
sta _b6add02+1
|
||||
|
||||
;;; Change the address for buf2
|
||||
lda zp:P_BUF2H$ ; Overwrite the most significant byte
|
||||
sta _b2add01+2
|
||||
sta _b2add02+2
|
||||
|
||||
dec zp:P_BUF2H$ ; Make sure we save this address for buf2 - 1, needed for page crossing
|
||||
lda zp:P_BUF2H$
|
||||
sta _b2madd01+2
|
||||
|
||||
lda #0xff
|
||||
sta _b2madd01+1
|
||||
|
||||
;;; Change the address for buf6
|
||||
lda zp:P_BUF6H$ ; Overwrite the most significant byte
|
||||
sta _b6add01+2
|
||||
sta _b6add02+2
|
||||
|
||||
;;; Nibble table address
|
||||
lda zp:P_NIBTL$
|
||||
sta _nibadd01+1
|
||||
sta _nibadd02+1
|
||||
sta _nibadd03+1
|
||||
|
||||
lda zp:P_NIBTH$
|
||||
sta _nibadd01+2
|
||||
sta _nibadd02+2
|
||||
sta _nibadd03+2
|
||||
|
||||
rts
|
||||
|
||||
;;; Timing critical code here, must not cross page boundary
|
||||
|
||||
.section dii_critical_rd_code,text,noreorder
|
||||
|
||||
;;; dii_read_sector:
|
||||
;;; Reads and decode a 256 byte sector from the Disk II interface.
|
||||
;;; It is shamelessly copied and then slightly adaptet from the BOOT0 code in the Disk II interface PROM.
|
||||
;;; Parameters:
|
||||
;;; - offset [A]: Offset from the base address (DII_BASE) to access the Disk II interface
|
||||
;;; - track [_Zp[0]]: Search for this track
|
||||
;;; - sector [_Zp[1]]: Search for the sector specified in the low nibble of this byte. If high nibble is != 0, do not read the data, and return after address header
|
||||
;;; - destination [_Zp[2] and _Zp[3]]: Destination address for a 256b buffer for the data
|
||||
;;; - destination [_Zp[4]]: If != 0, only the address data will be read
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
insta_return:
|
||||
rts
|
||||
dii_read_sector:
|
||||
RETRIES$: .equ 0xff
|
||||
T_HCHKS$: .equ _Zp+8
|
||||
T_BITS$: .equ _Zp+7
|
||||
T_RETRYC$: .equ _Zp+6
|
||||
P_OFFSET$: .equ _Zp+5
|
||||
P_ADDRONLY$:.equ _Zp+4
|
||||
P_DESTH$: .equ _Zp+3
|
||||
P_DESTL$: .equ _Zp+2
|
||||
P_SECTOR$: .equ _Zp+1
|
||||
P_TRACK$: .equ _Zp+0
|
||||
|
||||
; Save the slot offset, properly cleaned, into a zp register
|
||||
and #0x70
|
||||
sta zp:P_OFFSET$
|
||||
tax
|
||||
|
||||
; Load the retry counter
|
||||
lda #RETRIES$ ; Set up the retry counter
|
||||
sta zp:T_RETRYC$
|
||||
|
||||
;;; Address header is the sequence (D5 AA 96). Data header is (D5 AA AD).
|
||||
RdSect$:
|
||||
dec zp:T_RETRYC$
|
||||
beq insta_return ; Out of retries
|
||||
|
||||
clc ; Clear carry. C=0 will have the code look for an address, C=1 for data
|
||||
RdSect_C$: php ; Push state on stack
|
||||
|
||||
RdByte1$: lda READ_SW,x
|
||||
bpl RdByte1$ ; Read the first byte
|
||||
ChkD5$: eor #0xD5 ; An xor of 0xD5 with 0xD5 will get ourselves a 0, something else otherwise
|
||||
bne RdByte1$ ; Not the first byte, keep searching
|
||||
|
||||
RdByte2$: lda READ_SW,x
|
||||
bpl RdByte2$ ; Read the second byte
|
||||
cmp #0xAA
|
||||
bne ChkD5$ ; Not the second byte, check if it's the first...
|
||||
nop
|
||||
|
||||
RdByte3$: lda READ_SW,x
|
||||
bpl RdByte3$
|
||||
cmp #0x96 ; Check if third byte is 0x96
|
||||
beq FoundAddr$ ; We found an address!
|
||||
|
||||
plp ; Pull the status byte
|
||||
bcc RdSect$ ; We did not want to read data (C=0), so we start searching again
|
||||
eor #0xAD ; We did want to read data! So check if it's the last byte of a data sequence
|
||||
beq FoundData$ ; It is, read the data! (A = 0 at this point)
|
||||
|
||||
;;; ADDRESS: Data in the address header is stored in 4+4 GCR encoding
|
||||
FoundAddr$: lda #0x00 ; Clear A
|
||||
sta zp:T_HCHKS$ ; Save 0 to the temporary checksum in ZP
|
||||
ldy #0x05 ; Dummy write (+5), Volume # (+4), Track # (+3), Sector # (+2), checksum (+1), in decreasing order. We'll ignore the epilogue bytes
|
||||
hdr_loop$: sta (zp:P_DESTL$),y ; We'll use the destination buffer, temporarily...
|
||||
eor zp:T_HCHKS$ ; Running and saving the checksum
|
||||
sta zp:T_HCHKS$
|
||||
|
||||
RdByteA1$: lda READ_SW,x
|
||||
bpl RdByteA1$
|
||||
rol a ; Rotate the bits left (the first byte is odd encoded 1 b7 1 b5 1 b3 1 b1).
|
||||
sta zp:T_BITS$
|
||||
RdByteA2$: lda READ_SW,x
|
||||
bpl RdByteA2$ ; Read the second byte (the second byte is even encoded 1 b6 1 b4 1 b2 1 b0)
|
||||
and zp:T_BITS$ ; Merge with the first byte
|
||||
dey
|
||||
bne hdr_loop$ ; Are we at the last element?
|
||||
|
||||
;RdByteA3$: lda READ_SW,x ; Ignore the epilogue bytes...
|
||||
; bpl RdByteA3$
|
||||
;ChkDE$ eor #0xDE
|
||||
; bne RdByteA3$
|
||||
;
|
||||
;RdByteA4$: lda READ_SW,x
|
||||
; bpl RdByteA4$
|
||||
; eor #0xAA
|
||||
; bne ChkDE$
|
||||
|
||||
plp ; Pull the status byte
|
||||
|
||||
lda zp:T_HCHKS$ ; Verify if checksum is bad.
|
||||
bne RdSect$ ; If so, go back searching for an address...
|
||||
|
||||
ldy #2 ; Point to the saved sector # in memory
|
||||
lda (zp:P_DESTL$),y
|
||||
cmp zp:P_SECTOR$ ; Check if this is the sector we want
|
||||
bne RdSect$ ; If not, go back searching for an address...
|
||||
iny ; Point to the saved track # in memory
|
||||
lda (zp:P_DESTL$),y
|
||||
cmp zp:P_TRACK$ ; Check if it is the track we want
|
||||
bne RdSect$ ; If not, go back searching for an address...
|
||||
|
||||
lda zp:P_ADDRONLY$
|
||||
bne insta_return
|
||||
bcs RdSect_C$ ; Yes, go back reading the sector without clearing the Carry, which will be 1, so we'll read data at next run
|
||||
|
||||
;;; DATA. 6+2 encoded sector data
|
||||
;;; The values we'll read are encoded in a way that they'll have the range 0x96 - 0xff
|
||||
;;; A will be 0 on entry here
|
||||
;;; Read the 2s!
|
||||
FoundData$: ldy #86 ; We'll read 86 bytes of data in 0x300-0x355
|
||||
Read2Loop$: sty zp:T_BITS$ ; Each byte of these will contain 3 sets of "2 bits" encoded inside
|
||||
RdByteD1$: ldy READ_SW,x
|
||||
bpl RdByteD1$
|
||||
eor CONV_TABLE-128,y; Note that we have 0x02d6 + 0x96 = 0x36c, our first table entry for the conversion. Also, note we're doing a rolling xor
|
||||
ldy zp:T_BITS$
|
||||
dey
|
||||
sta PAGE3_BUF,y ; Store the result of the 2s decoding in page 3
|
||||
bne Read2Loop$ ; Loop until we read them all
|
||||
|
||||
;;; Read the 6s!
|
||||
Read6Loop$: sty zp:T_BITS$ ; y should be 255 at this point, which means we'll read 256 bytes
|
||||
RdByteD2$: ldy READ_SW,x
|
||||
bpl RdByteD2$
|
||||
eor CONV_TABLE-128,y; Going on with the rolling xor
|
||||
ldy zp:T_BITS$
|
||||
sta (zp:P_DESTL$),y ; Store the result on the destination buffer
|
||||
iny
|
||||
bne Read6Loop$ ; Keep reading and converting 256 6s
|
||||
|
||||
;;; Verify the checksum!
|
||||
RdByteD3$: ldy READ_SW,x
|
||||
bpl RdByteD3$
|
||||
eor CONV_TABLE-128,y; Finally check if the checksum matches...
|
||||
bne EndRdSect$ ; No, probably something got corrupted. Retry the read...
|
||||
|
||||
;;; Decode 6+2 data. The high 6 bits of every byte are already in place. We need to get the remaining 2 in
|
||||
ldy #0x00
|
||||
init_x$: ldx #86 ; We'll go through the 2-bit pieces three times
|
||||
decod_loop$:dex ; Decrement x
|
||||
bmi init_x$ ; Branch on N = 1 (negative set), make sure we roll x. 0x02ff -> 0x0355 as it is an offset
|
||||
lda (zp:P_DESTL$),y ; For each byte in the data buffer
|
||||
lsr PAGE3_BUF,x ; grab the low two bits from the 2s at 0x0300 - 0x0355 (we'll shift them in the carry)
|
||||
rol a ; and roll them in the least significant bits of the byte (and roll the carry in!)
|
||||
lsr PAGE3_BUF,x
|
||||
rol a
|
||||
sta (zp:P_DESTL$),y ; Store the decoded byte back
|
||||
iny
|
||||
bne decod_loop$
|
||||
|
||||
;;; Done!
|
||||
lda zp:T_RETRYC$ ; Recover the number of retries
|
||||
|
||||
rts
|
||||
;;; We're too far off the beginning, we cannot do a quick branch with an offset
|
||||
EndRdSect$:
|
||||
jmp RdSect$
|
||||
|
||||
;;; Timing critical code here, must not cross page boundary
|
||||
|
||||
.section dii_critical_wr_code,text,noreorder
|
||||
|
||||
|
||||
;;; dii_write_sector:
|
||||
;;; Writes a 342 byte from the pointed GCR 6+2 data buffers.
|
||||
;;; Parameters:
|
||||
;;; - offset [A]: Offset from the base address (DII_BASE) to access the Disk II interface
|
||||
;;; - track [_Zp[0]]: Check that we're writing on this track
|
||||
;;; - sector [_Zp[1]]: Write on this sector
|
||||
;;;
|
||||
;;; Returns: 0 for a completed write, 0x10 for a write protected disk
|
||||
;;;
|
||||
;;; MAKE SURE THE FOLLOWING ALL FITS INSIDE A SINGLE PAGE!!!
|
||||
;;; Note that the addresses used in this function will modified by calling dii_patch_write
|
||||
;;;
|
||||
wr_err_return:
|
||||
lda READ_SW,x ; Back into read mode
|
||||
lda #0x10 ; This will be the error code
|
||||
rts
|
||||
dii_write_sector:
|
||||
RETRIES$: .equ 0xff
|
||||
; Note that the following buffers MUST be at the start of a page for write timing to be respected
|
||||
__BUF2S: .equ 0x9D00 ; Default to this, it'll be overwritten when modifying this code. 86 entries.
|
||||
__BUF6S: .equ 0x9C00 ; 256 entries.
|
||||
__NIBTAB: .equ 0x9D56 ; This will contain the translation table for writing nibbles. 63 entries.
|
||||
__OFFSETP6 .equ 0x0678
|
||||
|
||||
__T_VOL: .equ _Zp+9
|
||||
__T_TRK: .equ _Zp+8
|
||||
__T_SEC: .equ _Zp+7
|
||||
__T_CHK: .equ _Zp+6 ; 7, 8, 9 also taken
|
||||
__T_HCHKS: .equ _Zp+5
|
||||
__T_HOLDNIB:.equ _Zp+4
|
||||
__T_RETRYC: .equ _Zp+3
|
||||
__P_OFFSET: .equ _Zp+2
|
||||
__P_SECTOR: .equ _Zp+1
|
||||
__P_TRACK: .equ _Zp+0
|
||||
; Save the slot offset, properly cleaned, into a zp register and on page 6
|
||||
and #0x70
|
||||
sta zp:__P_OFFSET
|
||||
sta __OFFSETP6
|
||||
tax
|
||||
|
||||
lda #RETRIES$ ; Set up the retry counter
|
||||
sta zp:__T_RETRYC
|
||||
|
||||
;;; Search for the address prologue (D5 AA 96)
|
||||
RdSect$:
|
||||
dec zp:__T_RETRYC
|
||||
beq wr_err_return ; Out of retries
|
||||
|
||||
;;; Read the address prologue
|
||||
RdByte1$: lda READ_SW,x
|
||||
bpl RdByte1$ ; Read the first byte
|
||||
ChkD5$: cmp #0xD5
|
||||
bne RdByte1$ ; Not the first byte, keep searching
|
||||
nop
|
||||
|
||||
RdByte2$: lda READ_SW,x
|
||||
bpl RdByte2$
|
||||
cmp #0xaa
|
||||
bne ChkD5$
|
||||
ldy #0x03
|
||||
|
||||
RdByte3$: lda READ_SW,x
|
||||
bpl RdByte3$
|
||||
cmp #0x96
|
||||
bne ChkD5$
|
||||
|
||||
;;; Read the address fields and store them in ZP
|
||||
lda #0x00
|
||||
RdAddrFld$: sta zp:__T_HCHKS
|
||||
RdByte4$: lda READ_SW,x
|
||||
bpl RdByte4$
|
||||
rol a
|
||||
sta zp:__T_HOLDNIB
|
||||
RdByte5$: lda READ_SW,x
|
||||
bpl RdByte5$
|
||||
and zp:__T_HOLDNIB
|
||||
sta __T_CHK,y
|
||||
eor zp:__T_HCHKS
|
||||
dey
|
||||
bpl RdAddrFld$
|
||||
|
||||
tay
|
||||
bne RdSect$ ; Checksum error, retry
|
||||
|
||||
;;; Read the bit-slip nibbles
|
||||
RdByte6$: lda READ_SW,x
|
||||
bpl RdByte6$
|
||||
cmp #0xde
|
||||
bne RdSect$
|
||||
nop
|
||||
|
||||
RdByte7$: lda READ_SW,x
|
||||
bpl RdByte7$
|
||||
cmp #0xaa
|
||||
bne RdSect$
|
||||
nop
|
||||
|
||||
;;; Check we're on the correct track
|
||||
lda __T_TRK
|
||||
cmp zp:__P_TRACK
|
||||
bne wr_err_return
|
||||
|
||||
;;; Check we're on the correct sector, or retry the read
|
||||
lda __T_SEC
|
||||
cmp zp:__P_SECTOR
|
||||
bne RdSect$
|
||||
|
||||
lda WRITE_SW,x ; Combination of these two in sequence checks for write protect
|
||||
lda CLEAR_SW,x ; and enables the write sequence.
|
||||
bmi wr_err_return ; Write protected disk. Branch on N = 1
|
||||
_b2add01:
|
||||
lda __BUF2S ; Get the first 2-bit encode byte
|
||||
sta zp:__T_HOLDNIB ; and save it for future use.
|
||||
|
||||
;;; Write a 5-sync gap between the address epilogue and the data prologue
|
||||
;;; Write of the sync bytes will take 36 cycles
|
||||
lda #0xff ; Sync byte
|
||||
sta SHIFT_SW,x ; Write 1 sync byte
|
||||
ora READ_SW,x
|
||||
pha ; (3 cycles)
|
||||
pla ; (4 cycles)
|
||||
nop ; (2 cycles)
|
||||
ldy #4 ; (2 cycles), plus load a counter to write 4 more syncs
|
||||
__wr4ff: pha ; (3 cycles)
|
||||
pla ; (4 cycles)
|
||||
jsr __write2 ; (12 cycles before, 6 after)
|
||||
dey ; (2 cycles)
|
||||
bne __wr4ff ; (2 or 3 cycles)
|
||||
|
||||
;;; Write the data prologue (D5 AA AD)
|
||||
lda #0xd5 ; (2 cycles)
|
||||
jsr __write1 ; (14 cycles before, 6 after)
|
||||
lda #0xaa ; (2 cycles)
|
||||
jsr __write1 ; (14 cycles before, 6 after)
|
||||
lda #0xad ; (2 cycles)
|
||||
jsr __write1 ; (14 cycles before, 6 after)
|
||||
|
||||
;;; Convert & write contents of the buffers to the disk!
|
||||
;;; Start with the 2-bits
|
||||
;;;
|
||||
;;; At this point, we're 10 cycles after the previous write
|
||||
;;; Writes will take 32 cycles
|
||||
tya
|
||||
ldy #0x56 ; We'll use this to count back from the beginning of the "twos" buffer
|
||||
bne __doeor ; This will ALWAYS be taken
|
||||
_b2add02:
|
||||
__getnibl: lda __BUF2S,y
|
||||
_b2madd01:
|
||||
__doeor: eor __BUF2S - 1,y ; Note that this will always cross a page boundary, and thus be 5 cycles!!
|
||||
tax ; Get index for translation table
|
||||
_nibadd01:
|
||||
lda __NIBTAB,x ; Get byte from translation table
|
||||
ldx zp:__P_OFFSET
|
||||
sta WRITE_SW,x
|
||||
lda READ_SW,x
|
||||
dey
|
||||
bne __getnibl
|
||||
|
||||
;;; Proceed with the 6-bits
|
||||
;;; y will be 0 at this point
|
||||
lda zp:__T_HOLDNIB
|
||||
nop
|
||||
_b6add01:
|
||||
__scndeor: eor __BUF6S,y
|
||||
tax ; Get index for translation table
|
||||
_nibadd02:
|
||||
lda __NIBTAB,x ; Get byte from translation table
|
||||
ldx __OFFSETP6 ; Retrieve the slot offset from page 6
|
||||
sta WRITE_SW,x ; Write "sixes", byte 87 and onward
|
||||
lda READ_SW,x
|
||||
_b6add02:
|
||||
lda __BUF6S,y ; Read the next byte
|
||||
iny ; 00 to ff
|
||||
bne __scndeor
|
||||
|
||||
;;; Convert and write the checksum byte
|
||||
tax ; Get the index on the table
|
||||
_nibadd03:
|
||||
lda __NIBTAB,x
|
||||
ldx zp:__P_OFFSET
|
||||
jsr __write3 ; Write the checksum
|
||||
|
||||
;;; Write the epilogue (DE AA EB)
|
||||
lda #0xde ; (2 cycles)
|
||||
jsr __write1 ; (14 cycles before, 6 after)
|
||||
lda #0xaa ; (2 cycles)
|
||||
jsr __write1 ; (14 cycles before, 6 after)
|
||||
lda #0xeb ; (2 cycles)
|
||||
jsr __write1 ; (14 cycles before, 6 after)
|
||||
|
||||
;;; Write a sync byte
|
||||
lda #0xff
|
||||
jsr __write1 ; (14 cycles before, 6 after)
|
||||
lda CLEAR_SW,x
|
||||
lda READ_SW,x ; Back into read mode
|
||||
lda #0x00
|
||||
rts
|
||||
|
||||
__write1:
|
||||
clc ; (2 cycles)
|
||||
__write2:
|
||||
pha ; (3 cycles)
|
||||
pla ; (4 cycles)
|
||||
__write3:
|
||||
sta WRITE_SW,x ; (5 cycles) Load latch from data bus
|
||||
ora READ_SW,x ; (4 cycles) and write byte. Must have previously selected SHIFT_SW
|
||||
rts ; (6 cycles)
|
||||
|
||||
;;; Declaration of public symbols
|
||||
.public dii_generate_6bit_decoding_mapping_table
|
||||
.public dii_generate_6bit_encoding_mapping_table
|
||||
.public dii_encode_gcr62_data
|
||||
.public dii_power_on
|
||||
.public dii_power_off
|
||||
.public dii_head_reposition
|
||||
.public dii_patch_write
|
||||
.public dii_read_sector
|
||||
.public dii_write_sector
|
||||
418
src/game_graphics.c
Normal file
418
src/game_graphics.c
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
#include "game_graphics.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "utility.h"
|
||||
#include "mem_map.h"
|
||||
#include "mem_registers.h"
|
||||
#include "line_data.h"
|
||||
#include "game_logic.h"
|
||||
#include "tiles.h"
|
||||
#include "charset.h"
|
||||
#include "monitor_subroutines.h"
|
||||
#include "graph_misc_data.h"
|
||||
#include "arrows_pic.h"
|
||||
|
||||
#define SCREEN_WIDTH 280
|
||||
#define SCREEN_HEIGHT 192
|
||||
|
||||
#define SCREEN_WIDTH_B 40
|
||||
#define GRID_CELL_SIDE 35
|
||||
|
||||
#define TOP_OFFSET 7
|
||||
#define LEFT_OFFSET_B 1 // Left is offset by 1 bytes (7 pixels)
|
||||
|
||||
static uint8_t *front_buf;
|
||||
static uint8_t *back_buf;
|
||||
|
||||
#define BOX_WIDTH 32
|
||||
#define BOX_HEIGHT 17
|
||||
#define BOX_CONTENT_SIZE (BOX_WIDTH * BOX_HEIGHT)
|
||||
|
||||
static const uint8_t box_content_win[BOX_CONTENT_SIZE] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 77, 0, 77, 0, 83, 77, 84, 0, 77, 0, 77, 0, 0, 77, 0, 0, 0, 77, 0, 77, 0, 77, 0, 77, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 77, 0, 77, 0, 77, 0, 77, 0, 77, 0, 77, 0, 0, 77, 0, 77, 0, 77, 0, 77, 0, 77, 84, 77, 0, 0, 0, 0,
|
||||
0, 0, 0, 0,212, 77,211, 0, 77, 0, 77, 0, 77, 0, 77, 0, 0, 77, 0, 77, 0, 77, 0, 77, 0, 77, 0, 77, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 77, 0, 0,212, 77,211, 0,212, 77,211, 0, 0,212,211, 0,212,211, 0, 77, 0, 77, 0, 77, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 25, 15, 21, 18, 0, 19, 3, 15, 18, 5, 0, 9, 19, 58, 0, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 16, 21, 19, 8, 0, 1, 14, 25, 0, 11, 5, 25, 0, 20, 15, 0, 3, 15, 14, 20, 9, 14, 21, 5, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static const uint8_t box_content_lose[BOX_CONTENT_SIZE] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 1, 13, 5, 0, 0, 15, 22, 5, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 77, 0, 77, 0, 83, 77, 84, 0, 77, 0, 77, 0, 0, 77, 0, 0, 0, 83, 77, 84, 0, 83, 77,192, 0, 77, 77, 77, 0, 0,
|
||||
0, 0, 77, 0, 77, 0, 77, 0, 77, 0, 77, 0, 77, 0, 0, 77, 0, 0, 0, 77, 0, 77, 0,212, 77, 84, 0, 77, 0, 0, 0, 0,
|
||||
0, 0,212, 77,211, 0, 77, 0, 77, 0, 77, 0, 77, 0, 0, 77, 0, 0, 0, 77, 0, 77, 0, 0,203, 77, 0, 77,193, 0, 0, 0,
|
||||
0, 0, 0, 77, 0, 0,212, 77,211, 0,212, 77,211, 0, 0,212, 77, 77, 0,212, 77,211, 0, 77, 77,211, 0, 77, 77, 77, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 25, 15, 21, 18, 0, 19, 3, 15, 18, 5, 0, 9, 19, 58, 0, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 16, 21, 19, 8, 0, 1, 14, 25, 0, 11, 5, 25, 0, 20, 15, 0, 3, 15, 14, 20, 9, 14, 21, 5, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static const uint8_t box_content_start[BOX_CONTENT_SIZE] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 16, 21, 19, 8, 0, 1, 14, 25, 0, 11, 5, 25, 0, 20, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 83, 77,192, 0, 77, 77, 77, 0, 83, 77, 84, 0, 77, 77, 84, 0, 77, 77, 77, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,212, 77, 84, 0, 0, 77, 0, 0, 77, 0, 77, 0, 77, 0, 77, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,203, 77, 0, 0, 77, 0, 0, 77,192, 77, 0, 77, 77,211, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 77, 77,211, 0, 0, 77, 0, 0, 77, 0, 77, 0, 77,198, 84, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 20, 11, 50, 48, 52, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 50, 46, 48, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 8, 9, 7, 8, 45, 19, 3, 15, 18, 5, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static const uint8_t box_new_hi_score[BOX_WIDTH] = {
|
||||
0, 0,102, 0, 23, 5, 0, 7, 15, 20, 0, 1, 0, 14, 5, 23, 0, 8, 9, 7, 8, 45, 19, 3, 15, 18, 5, 33, 0,102, 0, 0
|
||||
};
|
||||
|
||||
#define INSTR_BOX_WIDTH 12
|
||||
#define INSTR_BOX_HEIGHT 8
|
||||
#define INSTR_BOX_SIZE (INSTR_BOX_HEIGHT * INSTR_BOX_WIDTH)
|
||||
#define INSTR_BOX_X 28
|
||||
#define INSTR_BOX_Y 127
|
||||
static const uint8_t instruction_box[INSTR_BOX_SIZE] = {
|
||||
3, 20, 18, 12, 45, 18, 0, 18, 5, 19, 5, 20,
|
||||
3, 20, 18, 12, 45, 19, 0, 19, 1, 22, 5, 0,
|
||||
3, 20, 18, 12, 45, 12, 0, 12, 15, 1, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
18, 5, 1, 3, 8, 0, 0, 50, 48, 52, 56, 33,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
79, 0, 50, 48, 50, 53, 0, 50, 46, 48, 0, 79
|
||||
};
|
||||
|
||||
|
||||
// The grid is 5x5 squares,
|
||||
// It is offset on the left side by 7 pixels and on the top by 14
|
||||
// Every square is 35x35 pixels
|
||||
|
||||
void draw_field_borders_on_buffer(uint8_t brd, uint8_t* buf);
|
||||
void draw_picture(uint8_t w, uint8_t h, uint8_t x, uint8_t y, const uint8_t *data, uint8_t *dest);
|
||||
void direct_draw_number(uint16_t n, uint8_t len, uint8_t x, uint8_t y, uint8_t *disp_buf);
|
||||
void draw_graph_char_box(uint8_t x_offset, uint8_t y_offset, uint8_t width, uint8_t height, uint8_t const *data, uint8_t* buf);
|
||||
|
||||
void ddraw_field_borders_on_buffer(uint8_t brd) {
|
||||
draw_field_borders_on_buffer(brd, front_buf);
|
||||
}
|
||||
|
||||
|
||||
#define HIGH_TEXT_X 32
|
||||
#define HIGH_TEXT_Y 107
|
||||
#define HIGH_TEXT_WIDTH 5
|
||||
|
||||
void draw_game_background(uint16_t hi_score) {
|
||||
// Draw the background on display page 1
|
||||
uint8_t* buf = (uint8_t*)DISPLAY_PAGE_1;
|
||||
|
||||
// Draw the borders
|
||||
draw_field_borders_on_buffer(0x0F, buf);
|
||||
|
||||
// Draw required pics
|
||||
draw_picture(SCORE_PIC_WIDTH_BYTES, SCORE_PIC_HEIGHT, 31, 14, score_pic_data, buf);
|
||||
draw_picture(MOVES_PIC_WIDTH_BYTES, MOVES_PIC_HEIGHT, 31, 45, moves_pic_data, buf);
|
||||
draw_picture(HIGH_PIC_WIDTH_BYTES, HIGH_PIC_HEIGHT, 31, 76, high_pic_data, buf);
|
||||
draw_picture(SCORE_PIC_WIDTH_BYTES, SCORE_PIC_HEIGHT, 31, 90, score_pic_data, buf);
|
||||
|
||||
// Draw the high-score. This won't change at every turn, so makes sense to just draw once
|
||||
direct_draw_number(hi_score, HIGH_TEXT_WIDTH, HIGH_TEXT_X, HIGH_TEXT_Y, front_buf);
|
||||
|
||||
// Draw instruction box
|
||||
draw_graph_char_box(INSTR_BOX_X, INSTR_BOX_Y, INSTR_BOX_WIDTH, INSTR_BOX_HEIGHT, instruction_box, front_buf);
|
||||
|
||||
// Copy the data from display page 1 to 2
|
||||
memcpy((void*)DISPLAY_PAGE_2, (void*)DISPLAY_PAGE_1, DISPLAY_PAGE_SIZE);
|
||||
|
||||
}
|
||||
|
||||
// This will draw directly to the front buffer
|
||||
void ddraw_single_tile(uint8_t offset) {
|
||||
uint8_t* grid = get_front_grid();
|
||||
if(!grid[offset]) return; // The tile is not there, nothing to do
|
||||
|
||||
const uint8_t *tile_data = tiles + (TILE_WIDTH_BYTES * TILE_HEIGHT * (grid[offset] - 1));
|
||||
|
||||
uint8_t col = offset % GRID_SIDE;
|
||||
uint8_t row = offset / GRID_SIDE;
|
||||
|
||||
uint8_t delay = 0xFF;
|
||||
|
||||
for(uint8_t h = 0; h < TILE_HEIGHT; h++) {
|
||||
memcpy(front_buf + line_offset_map[TOP_OFFSET + 7 + (row * GRID_CELL_SIDE) + h] + LEFT_OFFSET_B + 1 + (col * GRID_CELL_SIDE/7),
|
||||
tile_data + (TILE_WIDTH_BYTES * h), TILE_WIDTH_BYTES);
|
||||
WAIT(48);
|
||||
}
|
||||
}
|
||||
|
||||
void direct_draw_number(uint16_t n, uint8_t len, uint8_t x, uint8_t y, uint8_t *disp_buf) {
|
||||
uint8_t buf[len];
|
||||
|
||||
// Decode the number into the buffer
|
||||
num_to_decbuf(n, len, buf);
|
||||
|
||||
for(uint8_t row = 0; row < CHAR_HEIGHT; row++) {
|
||||
uint16_t offset = line_offset_map[y + row];
|
||||
for(uint8_t col = 0; col < len; col++) {
|
||||
disp_buf[(offset + (len - 1) - col) + x] = CHARSET[NUM_OFFSET + (buf[col] * CHAR_HEIGHT) + row];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_number(uint16_t n, uint8_t len, uint8_t x, uint8_t y) {
|
||||
direct_draw_number(n, len, x, y, back_buf);
|
||||
}
|
||||
|
||||
void draw_tiles(void) {
|
||||
uint8_t* grid = get_front_grid();
|
||||
|
||||
// Clear the grid so we'll be able to draw the boxes on
|
||||
clear_box(GRID_SIDE * (GRID_CELL_SIDE/7) + 1, (GRID_SIDE * GRID_CELL_SIDE) + 4, LEFT_OFFSET_B, TOP_OFFSET + 1, back_buf);
|
||||
|
||||
for (uint8_t tile = 0; tile < GRID_SIDE * GRID_SIDE; tile++) {
|
||||
if(grid[tile]) {
|
||||
const uint8_t *tile_data = tiles + (TILE_WIDTH_BYTES * TILE_HEIGHT * (grid[tile] - 1));
|
||||
uint8_t col = tile % GRID_SIDE;
|
||||
uint8_t row = tile / GRID_SIDE;
|
||||
|
||||
draw_picture(TILE_WIDTH_BYTES, TILE_HEIGHT, LEFT_OFFSET_B + 1 + (col * GRID_CELL_SIDE/7), TOP_OFFSET + 7 + (row * GRID_CELL_SIDE), tile_data, back_buf);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-draw the borders, to restore the correct width
|
||||
draw_field_borders_on_buffer(0x0F, back_buf);
|
||||
}
|
||||
|
||||
#define ENDGAME_BOX_X_OFFSET 2
|
||||
#define ENDGAME_BOX_Y_OFFSET 16
|
||||
#define ENDGAME_BOX_SCORE_LEN 5
|
||||
void ddraw_endgame_box(int8_t done, uint16_t score, uint16_t hi_score) {
|
||||
// Clear the part of the screen where we'll draw
|
||||
clear_box((SCREEN_WIDTH_B - (ENDGAME_BOX_X_OFFSET * 2)) + 1, (SCREEN_HEIGHT - (ENDGAME_BOX_Y_OFFSET * 2)) + CHAR_HEIGHT, ENDGAME_BOX_X_OFFSET, ENDGAME_BOX_Y_OFFSET, front_buf);
|
||||
|
||||
// Horizontal lines
|
||||
for(uint8_t row = 0; row < CHAR_HEIGHT; row++) {
|
||||
uint16_t offset_top = line_offset_map[ENDGAME_BOX_Y_OFFSET + CHAR_HEIGHT + row] + ENDGAME_BOX_X_OFFSET + 1;
|
||||
uint16_t offset_bottom = line_offset_map[SCREEN_HEIGHT - ENDGAME_BOX_Y_OFFSET - CHAR_HEIGHT + row] + ENDGAME_BOX_X_OFFSET + 1;
|
||||
for(uint8_t col = 0; col < SCREEN_WIDTH_B - ((ENDGAME_BOX_X_OFFSET * 2) + 2); col++) {
|
||||
front_buf[offset_top + col] = CHARSET[GRAPH_OFFSET + (12 * CHAR_HEIGHT) + row];
|
||||
front_buf[offset_bottom + col] = CHARSET[GRAPH_OFFSET + (12 * CHAR_HEIGHT) + row];
|
||||
}
|
||||
}
|
||||
|
||||
// Corners
|
||||
for(uint8_t row = 0; row < CHAR_HEIGHT; row++) {
|
||||
uint16_t offset_top = line_offset_map[ENDGAME_BOX_Y_OFFSET + CHAR_HEIGHT + row] + ENDGAME_BOX_X_OFFSET + 1;
|
||||
uint16_t offset_bottom = line_offset_map[SCREEN_HEIGHT - ENDGAME_BOX_Y_OFFSET - CHAR_HEIGHT + row] + ENDGAME_BOX_X_OFFSET + 1;
|
||||
|
||||
front_buf[offset_top] = CHARSET[GRAPH_OFFSET + (25 * CHAR_HEIGHT) + row];
|
||||
front_buf[offset_bottom] = CHARSET[GRAPH_OFFSET + (27 * CHAR_HEIGHT) + row];
|
||||
front_buf[offset_top + (SCREEN_WIDTH_B - ((ENDGAME_BOX_X_OFFSET * 2) + 2))] = CHARSET[GRAPH_OFFSET + (26 * CHAR_HEIGHT) + row];
|
||||
front_buf[offset_bottom + (SCREEN_WIDTH_B - ((ENDGAME_BOX_X_OFFSET * 2) + 2))] = CHARSET[GRAPH_OFFSET + (28 * CHAR_HEIGHT) + row];
|
||||
}
|
||||
|
||||
// Vertical lines
|
||||
for(uint8_t row = 0; row < ((SCREEN_HEIGHT - (ENDGAME_BOX_Y_OFFSET * 3) - CHAR_HEIGHT)) + 1; row++) {
|
||||
uint16_t offset = line_offset_map[ENDGAME_BOX_Y_OFFSET + (CHAR_HEIGHT * 2) + row] + ENDGAME_BOX_X_OFFSET + 1;
|
||||
|
||||
front_buf[offset] = CHARSET[GRAPH_OFFSET + (19 * CHAR_HEIGHT) + (row % CHAR_HEIGHT)];
|
||||
front_buf[offset + (SCREEN_WIDTH_B - ((ENDGAME_BOX_X_OFFSET * 2) + 2))] = CHARSET[GRAPH_OFFSET + (19 * CHAR_HEIGHT) + (row % CHAR_HEIGHT)];
|
||||
}
|
||||
|
||||
uint8_t const *content;
|
||||
|
||||
// Decide which type of content to show
|
||||
if(done == 0) content = box_content_start;
|
||||
else if (done > 0) content = box_content_win;
|
||||
else content = box_content_lose;
|
||||
|
||||
// And now, the content!!!
|
||||
draw_graph_char_box(ENDGAME_BOX_X_OFFSET + 2, (ENDGAME_BOX_Y_OFFSET + (2 * CHAR_HEIGHT)), BOX_WIDTH, BOX_HEIGHT, content, front_buf);
|
||||
|
||||
if(done != 0) { // Print the score
|
||||
direct_draw_number(score, ENDGAME_BOX_SCORE_LEN, ENDGAME_BOX_X_OFFSET + 23, ENDGAME_BOX_Y_OFFSET + (11 * CHAR_HEIGHT), front_buf);
|
||||
|
||||
if(score > hi_score) {
|
||||
draw_graph_char_box(ENDGAME_BOX_X_OFFSET + 2, (ENDGAME_BOX_Y_OFFSET + (2 * CHAR_HEIGHT) + (13 * CHAR_HEIGHT)), BOX_WIDTH, 1, box_new_hi_score, front_buf);
|
||||
}
|
||||
} else { // Print the loaded high score
|
||||
direct_draw_number(hi_score, ENDGAME_BOX_SCORE_LEN, ENDGAME_BOX_X_OFFSET + 22, ENDGAME_BOX_Y_OFFSET + (16 * CHAR_HEIGHT), front_buf);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_graph_char_box(uint8_t x_offset, uint8_t y_offset, uint8_t width, uint8_t height, uint8_t const *data, uint8_t* buf) {
|
||||
// Draw box
|
||||
for(uint16_t tile = 0; tile < width * height; tile++) {
|
||||
if(!data[tile]) continue;
|
||||
|
||||
uint8_t x = tile % width;
|
||||
uint8_t y = tile / width;
|
||||
uint8_t invert = data[tile] & 0x80;
|
||||
uint8_t ch_num = data[tile] & 0x7F;
|
||||
|
||||
for(uint8_t row = 0; row < CHAR_HEIGHT; row++) {
|
||||
uint16_t offset = line_offset_map[y_offset + (y * CHAR_HEIGHT) + row] + x_offset + x;
|
||||
buf[offset] = invert ? ((~CHARSET[(ch_num * CHAR_HEIGHT) + row]) & 0x7F) : CHARSET[(ch_num * CHAR_HEIGHT) + row];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note that the horizontal values here are in group of 7 pixels
|
||||
void clear_box(uint8_t w, uint8_t h, uint8_t off_x, uint8_t off_y, uint8_t *disp_buf) {
|
||||
for(uint8_t y = off_y; y < off_y + h; y++) {
|
||||
uint16_t line_counter = line_offset_map[y];
|
||||
|
||||
for(uint8_t x = off_x; x < off_x + w; x++) {
|
||||
disp_buf[line_counter + x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void swap_display_buffers(void) {
|
||||
uint8_t *temp = front_buf;
|
||||
front_buf = back_buf;
|
||||
back_buf = temp;
|
||||
|
||||
// Show the current buffer
|
||||
PEEK(((uint16_t)front_buf == DISPLAY_PAGE_1) ? IO_DISPLAY_PAGE1 : IO_DISPLAY_PAGE2);
|
||||
}
|
||||
|
||||
void sync_display1_buffer(void) {
|
||||
if(((uint16_t)front_buf == DISPLAY_PAGE_1)) return; // We're already displaying buffer 1
|
||||
|
||||
// We need to copy from the secondary display to the primary
|
||||
memcpy(back_buf, front_buf, DISPLAY_PAGE_SIZE);
|
||||
}
|
||||
|
||||
void initialize_display_buffers(void) {
|
||||
PEEK(IO_DISPLAY_PAGE1); // Select the first display page
|
||||
|
||||
// Restore the buffer ordering
|
||||
front_buf = (uint8_t*)DISPLAY_PAGE_1;
|
||||
back_buf = (uint8_t*)DISPLAY_PAGE_2;
|
||||
}
|
||||
|
||||
void clear_display_buffers(void) {
|
||||
// Clear the buffers
|
||||
memset((void*)DISPLAY_PAGE_1, 0, DISPLAY_PAGE_SIZE);
|
||||
memset((void*)DISPLAY_PAGE_2, 0, DISPLAY_PAGE_SIZE);
|
||||
|
||||
initialize_display_buffers();
|
||||
}
|
||||
|
||||
void draw_field_borders_on_buffer(uint8_t brd, uint8_t* buf) {
|
||||
// Horizontal borders
|
||||
for(uint8_t col = 0; col < (GRID_SIDE * (GRID_CELL_SIDE/7)) + 1; col++) {
|
||||
buf[line_offset_map[TOP_OFFSET - 1] + col + LEFT_OFFSET_B] = BRD_SKIP_UP(brd) ? 0x00: 0x7F;
|
||||
buf[line_offset_map[TOP_OFFSET - 0] + col + LEFT_OFFSET_B] = BRD_DOUBLING_UP(brd) && !BRD_SKIP_UP(brd) ? 0x7F : 0x00;
|
||||
|
||||
buf[line_offset_map[TOP_OFFSET + (GRID_CELL_SIDE * GRID_SIDE) + 7] + col + LEFT_OFFSET_B] = BRD_SKIP_DOWN(brd) ? 0x00: 0x7F;
|
||||
buf[line_offset_map[TOP_OFFSET + (GRID_CELL_SIDE * GRID_SIDE) + 6] + col + LEFT_OFFSET_B] = BRD_DOUBLING_DOWN(brd) && !BRD_SKIP_DOWN(brd) ? 0x7F : 0x00;
|
||||
}
|
||||
|
||||
// Vertical borders
|
||||
for(uint8_t row = 0; row < (GRID_CELL_SIDE * GRID_SIDE) + 7; row++) {
|
||||
buf[line_offset_map[row + TOP_OFFSET] + LEFT_OFFSET_B - 1] = BRD_SKIP_LEFT(brd) ? 0x00 : (BRD_DOUBLING_LEFT(brd) ? 0x60 : 0x40);
|
||||
buf[line_offset_map[row + TOP_OFFSET] + LEFT_OFFSET_B + (GRID_SIDE * (GRID_CELL_SIDE/7)) + 1] = BRD_SKIP_RIGHT(brd) ? 0x00 : (BRD_DOUBLING_RIGHT(brd) ? 0x03 : 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_picture(uint8_t w, uint8_t h, uint8_t x, uint8_t y, const uint8_t *data, uint8_t *dest) {
|
||||
for(uint8_t row = 0; row < h; row++) {
|
||||
memcpy(dest + line_offset_map[row + y] + x, data + (w * row), w);
|
||||
}
|
||||
}
|
||||
|
||||
void ddraw_direction_arrows(uint8_t dir) {
|
||||
uint8_t pic_buffer[ARROWS_HEIGHT];
|
||||
|
||||
int8_t start, step, end, flip;
|
||||
uint8_t ext, x, y;
|
||||
|
||||
switch(dir) {
|
||||
case GRAPH_ARROW_UP:
|
||||
x = 2;
|
||||
y = TOP_OFFSET + 1;
|
||||
ext = 1;
|
||||
start = 1;
|
||||
step = 1;
|
||||
end = ARROWS_HEIGHT;
|
||||
flip = 0;
|
||||
break;
|
||||
case GRAPH_ARROW_DOWN:
|
||||
x = 2;
|
||||
y = TOP_OFFSET + (GRID_SIDE * GRID_CELL_SIDE);
|
||||
ext = 1;
|
||||
start = ARROWS_HEIGHT - 1;
|
||||
step = -1;
|
||||
end = 0;
|
||||
flip = 0;
|
||||
break;
|
||||
case GRAPH_ARROW_LEFT:
|
||||
x = 1;
|
||||
y = TOP_OFFSET + 7;
|
||||
ext = 0;
|
||||
start = ARROWS_HEIGHT;
|
||||
step = 1;
|
||||
end = (ARROWS_HEIGHT * 2);
|
||||
flip = 0;
|
||||
break;
|
||||
case GRAPH_ARROW_RIGHT:
|
||||
x = 1 + (GRID_SIDE * (GRID_CELL_SIDE/7));
|
||||
y = TOP_OFFSET + 7;
|
||||
ext = 0;
|
||||
start = ARROWS_HEIGHT;
|
||||
step = 1;
|
||||
end = (ARROWS_HEIGHT * 2);
|
||||
flip = 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t tot_arrows = (GRID_SIDE * (GRID_CELL_SIDE/7)) - 1;
|
||||
|
||||
if(ext) { // Horizontal lines
|
||||
uint8_t s_start = x;
|
||||
for(uint8_t cur_arrow = 0; cur_arrow < tot_arrows; cur_arrow++) {
|
||||
for(int8_t s = start, row = 0; s != end; s += step, row++) {
|
||||
front_buf[line_offset_map[y + row] + s_start] = arrows_pic[s];
|
||||
}
|
||||
s_start++;
|
||||
}
|
||||
} else {
|
||||
uint8_t s_start = y;
|
||||
for(uint8_t cur_arrow = 0; cur_arrow < tot_arrows; cur_arrow++) {
|
||||
for(int8_t s = start, row = 0; s != end; s += step, row++) {
|
||||
front_buf[line_offset_map[s_start + row] + x] = flip ? (bit_reverse(arrows_pic[s]) >> 1) : arrows_pic[s];
|
||||
}
|
||||
s_start += ARROWS_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/game_graphics.h
Normal file
34
src/game_graphics.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef _GAME_GRAPHICS_HEADER_
|
||||
#define _GAME_GRAPHICS_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define BRD_DOUBLING_UP(a) (a & 0x01)
|
||||
#define BRD_DOUBLING_DOWN(a) (a & 0x02)
|
||||
#define BRD_DOUBLING_LEFT(a) (a & 0x04)
|
||||
#define BRD_DOUBLING_RIGHT(a) (a & 0x08)
|
||||
|
||||
#define BRD_SKIP_UP(a) (a & 0x10)
|
||||
#define BRD_SKIP_DOWN(a) (a & 0x20)
|
||||
#define BRD_SKIP_LEFT(a) (a & 0x40)
|
||||
#define BRD_SKIP_RIGHT(a) (a & 0x80)
|
||||
|
||||
#define GRAPH_ARROW_UP 0
|
||||
#define GRAPH_ARROW_DOWN 1
|
||||
#define GRAPH_ARROW_LEFT 2
|
||||
#define GRAPH_ARROW_RIGHT 3
|
||||
|
||||
void initialize_display_buffers(void);
|
||||
void ddraw_field_borders_on_buffer(uint8_t brd);
|
||||
void draw_game_background(uint16_t hi_score);
|
||||
void draw_number(uint16_t n, uint8_t len, uint8_t x, uint8_t y);
|
||||
void draw_tiles(void);
|
||||
void ddraw_single_tile(uint8_t offset);
|
||||
void swap_display_buffers(void);
|
||||
void clear_display_buffers(void);
|
||||
void clear_box(uint8_t w, uint8_t h, uint8_t off_x, uint8_t off_y, uint8_t *disp_buf);
|
||||
void ddraw_direction_arrows(uint8_t dir);
|
||||
void ddraw_endgame_box(int8_t done, uint16_t score, uint16_t hi_score);
|
||||
void sync_display1_buffer(void);
|
||||
|
||||
#endif /* _GAME_GRAPHICS_HEADER_ */
|
||||
25
src/input.c
Normal file
25
src/input.c
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include "input.h"
|
||||
|
||||
#include "utility.h"
|
||||
#include "mem_registers.h"
|
||||
|
||||
uint8_t __internal_read_kb(void);
|
||||
|
||||
uint8_t read_kb(void) {
|
||||
static uint8_t last_press = K_NONE;
|
||||
uint8_t cur_press = __internal_read_kb();
|
||||
|
||||
if (cur_press != last_press) {
|
||||
last_press = cur_press;
|
||||
return cur_press;
|
||||
} else {
|
||||
return K_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t read_any_key(void) {
|
||||
PEEK(IO_KB_CTRL_LOW);
|
||||
|
||||
POKE(IO_DATAOUT, 0xFF);
|
||||
return PEEK(IO_DATAIN) & DATAIN_KB_MASK;
|
||||
}
|
||||
19
src/input.h
Normal file
19
src/input.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef _INPUT_HEADER_
|
||||
#define _INPUT_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define K_NONE 0
|
||||
#define K_UP 1
|
||||
#define K_DOWN 2
|
||||
#define K_LEFT 3
|
||||
#define K_RIGHT 4
|
||||
#define K_CTRL_R 5
|
||||
#define K_CTRL_S 6
|
||||
#define K_CTRL_L 7
|
||||
|
||||
uint8_t read_kb(void);
|
||||
|
||||
uint8_t read_any_key(void);
|
||||
|
||||
#endif /* _INPUT_HEADER_ */
|
||||
85
src/input_asm.s
Normal file
85
src/input_asm.s
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
.rtmodel version,"1"
|
||||
.rtmodel core,"6502"
|
||||
|
||||
.extern _Vfp
|
||||
.extern _Vsp
|
||||
.extern _Zp
|
||||
|
||||
K_NONE: .equ 0
|
||||
K_UP .equ 1
|
||||
K_DOWN .equ 2
|
||||
K_LEFT .equ 3
|
||||
K_RIGHT .equ 4
|
||||
K_CTRL_R .equ 5
|
||||
K_CTRL_S .equ 6
|
||||
K_CTRL_L .equ 7
|
||||
|
||||
|
||||
__internal_read_kb:
|
||||
IO_DATAOUT$:.equ 0xC000
|
||||
IO_DATAIN$: .equ 0xC010
|
||||
IO_KB_CTRL_LOW$: .equ 0xC05E
|
||||
IO_KB_CTRL_HI$: .equ 0xC05F
|
||||
|
||||
lda IO_KB_CTRL_HI$
|
||||
lda IO_DATAIN$
|
||||
ror a
|
||||
bcc NoCtrl$
|
||||
lda #0x04
|
||||
sta IO_DATAOUT$
|
||||
lda IO_DATAIN$
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
lda #K_CTRL_R
|
||||
bcs Return$
|
||||
lda #0x02
|
||||
sta IO_DATAOUT$
|
||||
lda IO_DATAIN$
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
lda #K_CTRL_S
|
||||
bcs Return$
|
||||
lda #0x40
|
||||
sta IO_DATAOUT$
|
||||
lda IO_DATAIN$
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
lda #K_CTRL_L
|
||||
bcs Return$
|
||||
NoCtrl$:
|
||||
lda IO_KB_CTRL_LOW$
|
||||
ldy #3
|
||||
NextKey$: lda _rkb_key_inp,y
|
||||
sta IO_DATAOUT$
|
||||
lda IO_DATAIN$
|
||||
ror a
|
||||
lda _rkb_key_ret,y
|
||||
bcs Return$
|
||||
dey
|
||||
bpl NextKey$
|
||||
|
||||
lda #K_NONE
|
||||
Return$:
|
||||
rts
|
||||
|
||||
_rkb_key_ret:
|
||||
.byte K_LEFT
|
||||
.byte K_RIGHT
|
||||
.byte K_DOWN
|
||||
.byte K_UP
|
||||
|
||||
_rkb_key_inp:
|
||||
.byte 0x08
|
||||
.byte 0x10
|
||||
.byte 0x20
|
||||
.byte 0x40
|
||||
|
||||
;;; Declaration of public symbols
|
||||
.public __internal_read_kb
|
||||
39
src/intro_main.c
Normal file
39
src/intro_main.c
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#include <stubs.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <calypsi/intrinsics6502.h>
|
||||
|
||||
#include "monitor_subroutines.h"
|
||||
#include "utility.h"
|
||||
#include "mem_map.h"
|
||||
#include "shared_page.h"
|
||||
|
||||
// External initialization requirements
|
||||
#pragma require __preserve_zp
|
||||
#pragma require __data_initialization_needed
|
||||
|
||||
static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE;
|
||||
|
||||
void main(void) {
|
||||
uint8_t wait_count = 0x40;
|
||||
|
||||
// Initialize the register for LFSR
|
||||
lfsr_init(0xF00D);
|
||||
|
||||
// Clear the memory used to pass parameters to the next module
|
||||
memset((void*)(shared_page->module_data), 0, MODULE_DATA_SIZE);
|
||||
|
||||
// TODO: Detect expansion hardware, and choose what to load according to that
|
||||
|
||||
// Delay a bit
|
||||
while(wait_count--) WAIT(0xFF);
|
||||
|
||||
// Clear the display memory
|
||||
memset((void*)DISPLAY_PAGE_2, 0, DISPLAY_PAGE_SIZE);
|
||||
memset((void*)DISPLAY_PAGE_1, 0, DISPLAY_PAGE_SIZE);
|
||||
|
||||
shared_page->master_command = MASTER_COMMAND_NONE;
|
||||
shared_page->next_module_idx = 3; // DLOG module is next!
|
||||
|
||||
return;
|
||||
}
|
||||
22
src/line_data.c
Normal file
22
src/line_data.c
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
const uint16_t line_offset_map[SCREEN_HEIGHT] = {
|
||||
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x0080, 0x0480, 0x0880, 0x0C80,
|
||||
0x1080, 0x1480, 0x1880, 0x1C80, 0x0100, 0x0500, 0x0900, 0x0D00, 0x1100, 0x1500, 0x1900, 0x1D00,
|
||||
0x0180, 0x0580, 0x0980, 0x0D80, 0x1180, 0x1580, 0x1980, 0x1D80, 0x0200, 0x0600, 0x0A00, 0x0E00,
|
||||
0x1200, 0x1600, 0x1A00, 0x1E00, 0x0280, 0x0680, 0x0A80, 0x0E80, 0x1280, 0x1680, 0x1A80, 0x1E80,
|
||||
0x0300, 0x0700, 0x0B00, 0x0F00, 0x1300, 0x1700, 0x1B00, 0x1F00, 0x0380, 0x0780, 0x0B80, 0x0F80,
|
||||
0x1380, 0x1780, 0x1B80, 0x1F80, 0x0028, 0x0428, 0x0828, 0x0C28, 0x1028, 0x1428, 0x1828, 0x1C28,
|
||||
0x00A8, 0x04A8, 0x08A8, 0x0CA8, 0x10A8, 0x14A8, 0x18A8, 0x1CA8, 0x0128, 0x0528, 0x0928, 0x0D28,
|
||||
0x1128, 0x1528, 0x1928, 0x1D28, 0x01A8, 0x05A8, 0x09A8, 0x0DA8, 0x11A8, 0x15A8, 0x19A8, 0x1DA8,
|
||||
0x0228, 0x0628, 0x0A28, 0x0E28, 0x1228, 0x1628, 0x1A28, 0x1E28, 0x02A8, 0x06A8, 0x0AA8, 0x0EA8,
|
||||
0x12A8, 0x16A8, 0x1AA8, 0x1EA8, 0x0328, 0x0728, 0x0B28, 0x0F28, 0x1328, 0x1728, 0x1B28, 0x1F28,
|
||||
0x03A8, 0x07A8, 0x0BA8, 0x0FA8, 0x13A8, 0x17A8, 0x1BA8, 0x1FA8, 0x0050, 0x0450, 0x0850, 0x0C50,
|
||||
0x1050, 0x1450, 0x1850, 0x1C50, 0x00D0, 0x04D0, 0x08D0, 0x0CD0, 0x10D0, 0x14D0, 0x18D0, 0x1CD0,
|
||||
0x0150, 0x0550, 0x0950, 0x0D50, 0x1150, 0x1550, 0x1950, 0x1D50, 0x01D0, 0x05D0, 0x09D0, 0x0DD0,
|
||||
0x11D0, 0x15D0, 0x19D0, 0x1DD0, 0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50,
|
||||
0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, 0x0350, 0x0750, 0x0B50, 0x0F50,
|
||||
0x1350, 0x1750, 0x1B50, 0x1F50, 0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0
|
||||
};
|
||||
9
src/line_data.h
Normal file
9
src/line_data.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef _LINE_DATA_HEADER_
|
||||
#define _LINE_DATA_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "utility.h"
|
||||
|
||||
extern const uint16_t line_offset_map[SCREEN_HEIGHT];
|
||||
|
||||
#endif /* _LINE_DATA_HEADER */
|
||||
53
src/main.c
Normal file
53
src/main.c
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#include <stubs.h>
|
||||
|
||||
#include <calypsi/intrinsics6502.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "monitor_subroutines.h"
|
||||
#include "utility.h"
|
||||
#include "mem_map.h"
|
||||
#include "mem_registers.h"
|
||||
#include "disk2.h"
|
||||
|
||||
// External initialization requirements
|
||||
#pragma require __preserve_zp
|
||||
#pragma require __data_initialization_needed
|
||||
|
||||
#define DEFAULT_DRIVE_CONTROLLER_OFFSET 0x10
|
||||
|
||||
static void init(void);
|
||||
|
||||
// Low level initialization
|
||||
static void init(void) {
|
||||
POKE(P3_PWRDUP, 0); // Dirty the value checked by the reset vector
|
||||
PEEK(IO_ROMSEL); // Make sure the ROM is selected
|
||||
PEEK(DISPLAY_PAGE_1); // Select display page 1
|
||||
PEEK(IO_DISPLAY_BW); // Disable colors
|
||||
|
||||
// Generate the decoding table
|
||||
dii_generate_6bit_decoding_mapping_table((uint8_t*)DECODING_MAPPING_TABLE_DEFAULT_ADDRESS);
|
||||
// Generate the encoding table
|
||||
dii_generate_6bit_encoding_mapping_table((uint8_t*)ENCODING_MAPPING_TABLE_DEFAULT_ADDRESS);
|
||||
}
|
||||
|
||||
__task int main(void) {
|
||||
uint8_t cur_trk = 0;
|
||||
uint8_t cur_file = 0;
|
||||
uint8_t keep_going = 1;
|
||||
|
||||
__disable_interrupts();
|
||||
|
||||
init();
|
||||
|
||||
__enable_interrupts();
|
||||
|
||||
|
||||
do {
|
||||
|
||||
} while(keep_going);
|
||||
|
||||
__asm volatile(" brk\n":::);
|
||||
|
||||
return 0;
|
||||
}
|
||||
14
src/mem_map.h
Normal file
14
src/mem_map.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef _MEMORY_MAP_HEADER_
|
||||
#define _MEMORY_MAP_HEADER_
|
||||
|
||||
#define DISPLAY_PAGE_SIZE 0x2000
|
||||
#define DISPLAY_PAGE_1 0x2000
|
||||
#define DISPLAY_PAGE_2 0xA000
|
||||
|
||||
#define ROMRAM_BANK 0xC100
|
||||
|
||||
#define SHARED_PAGE 0x9B00
|
||||
#define STATE_PAGE 0x9A00
|
||||
#define MODULE_PAGE 0x4000
|
||||
|
||||
#endif /* _MEMORY_MAP_HEADER_ */
|
||||
48
src/mem_registers.h
Normal file
48
src/mem_registers.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef _MEM_REGISTERS_HEADER_
|
||||
#define _MEM_REGISTERS_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ROM_MONITOR 0xFF61
|
||||
|
||||
#define ZP_WNDLFT 0x0020 // 0, left column of scroll window
|
||||
#define ZP_WNDWDTH 0x0021 // 40, width of scroll window
|
||||
#define ZP_WNDTOP 0x0022 // 0, top line of the scroll window
|
||||
#define ZP_WNDBTM 0x0023 // 24, bottom line of the scroll window
|
||||
|
||||
#define ZP_CH 0x0024 // Displacement from window left for the cursor
|
||||
#define ZP_CV 0x0025 // Displacement from top of screen (not window!) for the cursor
|
||||
|
||||
#define ZP_INVFLAG 0x0032 // Either 0x00 or 0x7F, set text color inversion
|
||||
#define ZP_PROMPT 0x0033 // Prompt character
|
||||
|
||||
#define ZP_RND 0x004E // Note that this is a 16bit register incremented by the RDKEY func
|
||||
|
||||
#define P3_PWRDUP_REF 0x03F3
|
||||
#define P3_PWRDUP 0x03F4 // Already-powered-up indicator. If it is set to the content of 0x03F3 XOR'd with 0xA5, the soft reset vector is considered valid
|
||||
|
||||
|
||||
#define DATAIN_KB_MASK 0x3F
|
||||
#define DATAIN_PRNT_MASK 0x40
|
||||
#define DATAIN_TAPEIN_MASK 0x80
|
||||
|
||||
#define IO_DATAOUT 0xC000 // (W) To keyboard and printer port
|
||||
#define IO_DATAIN 0xC010 // (R) Data input from keyboard (0:5), printer (6) and tape (7)
|
||||
#define IO_TAPEOUT 0xC020 // (R) Data output for tape, read from here to output bit on tape
|
||||
#define IO_SPEAKER 0xC030 // (R) Speaker toggle
|
||||
#define IO_DISPLAY_COLOR 0xC050 // (R / W) Access here to enable the colorburst
|
||||
#define IO_DISPLAY_BW 0xC051 // (R / W) Access here to disable the colorburst
|
||||
#define IO_MTA_OFF 0xC052 // (?)
|
||||
#define IO_MTA_ON 0xC053 // (?)
|
||||
#define IO_DISPLAY_PAGE1 0xC054 // (R / W) Access here to select the primary display page
|
||||
#define IO_DISPLAY_PAGE2 0xC055 // (R / W) Access here to select the secondary display page
|
||||
#define IO_MTB_OFF 0xC056 // (?)
|
||||
#define IO_MTB_ON 0xC057 // (?)
|
||||
#define IO_PRNT_STRB_LO 0xC058 // (R / W) Access LO/HI/LO or HI/LO/HI consecutively depending on the type of strobe pulse to create
|
||||
#define IO_PRNT_STRB_HI 0xC059 // (R / W)
|
||||
#define IO_ROMSEL 0xC05A // (R / W) Access here will make region C100-FFFF a ROM area
|
||||
#define IO_RAMSEL 0xC05B // (R / W) Access here will make region C100-FFFF a RAM area
|
||||
#define IO_KB_CTRL_LOW 0xC05E // (R / W) Set the CTRL line to 0, access is through DATAIN
|
||||
#define IO_KB_CTRL_HI 0xC05F // (R / W) Set the CTRL line to 1
|
||||
|
||||
#endif /* _MEM_REGISTERS_HEADER_ */
|
||||
19
src/monitor_subroutines.c
Normal file
19
src/monitor_subroutines.c
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#include "monitor_subroutines.h"
|
||||
|
||||
void sbrt_prntax(uint8_t msb, uint8_t lsb) {
|
||||
__asm(
|
||||
" jsr 0xF941\n"
|
||||
:
|
||||
: "Ka" (msb), "Kx" (lsb)
|
||||
:
|
||||
);
|
||||
}
|
||||
|
||||
void sbrt_prbl2(uint8_t count) {
|
||||
__asm(
|
||||
" jsr 0xF94A\n"
|
||||
:
|
||||
: "Kx" (count)
|
||||
:
|
||||
);
|
||||
}
|
||||
85
src/monitor_subroutines.h
Normal file
85
src/monitor_subroutines.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef _MONITOR_SUBROUTINES_HEADER_
|
||||
#define _MONITOR_SUBROUTINES_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t key;
|
||||
uint8_t ch;
|
||||
} rdkey_res;
|
||||
|
||||
inline void sbrt_crout(void);
|
||||
inline void sbrt_crout1(void);
|
||||
void sbrt_prntax(uint8_t a, uint8_t x);
|
||||
inline void sbrt_prblnk(void);
|
||||
void sbrt_prbl2(uint8_t count);
|
||||
inline void sbrt_bell(void);
|
||||
inline rdkey_res sbrt_rdkey(void);
|
||||
|
||||
/*** ***/
|
||||
|
||||
#define COUT(a) (((void (*)(char))(0xFDED))(a))
|
||||
#define COUT1(a) (((void (*)(char))(0xFDF0))(a))
|
||||
#define COUTZ(a) (((void (*)(char))(0xFDF6))(a))
|
||||
#define PRBYTE(a) (((void (*)(uint8_t))(0xFDDA))(a))
|
||||
#define PRHEX(a) (((void (*)(uint8_t))(0xFDE3))(a))
|
||||
#define WAIT(a) (((void (*)(uint8_t))(0xFCA8))(a))
|
||||
#define BELL1() (((void (*)(uint8_t))(0xFBD9))(0x87))
|
||||
#define SETINV() (((void (*)(void))(0xFE80))())
|
||||
#define SETNORM() (((void (*)(void))(0xFE84))())
|
||||
|
||||
inline void sbrt_crout(void) {
|
||||
__asm volatile(
|
||||
" jsr 0xFD8E\n"
|
||||
:
|
||||
:
|
||||
:
|
||||
);
|
||||
}
|
||||
|
||||
inline void sbrt_crout1(void) {
|
||||
__asm volatile(
|
||||
" jsr 0xFD8B\n"
|
||||
:
|
||||
:
|
||||
:
|
||||
);
|
||||
}
|
||||
|
||||
inline void sbrt_prblnk(void) {
|
||||
__asm volatile(
|
||||
" jsr 0xF948\n"
|
||||
:
|
||||
:
|
||||
: "a", "x"
|
||||
);
|
||||
}
|
||||
|
||||
inline void sbrt_bell(void) {
|
||||
__asm volatile(
|
||||
" jsr 0xFF3A\n"
|
||||
:
|
||||
:
|
||||
: "a"
|
||||
);
|
||||
}
|
||||
|
||||
inline rdkey_res sbrt_rdkey(void) {
|
||||
rdkey_res res;
|
||||
uint8_t key;
|
||||
uint8_t ch;
|
||||
|
||||
__asm volatile(
|
||||
" jsr 0xFD0C\n"
|
||||
: "=Ka"(key), "=Ky"(ch)
|
||||
:
|
||||
: "a", "y"
|
||||
);
|
||||
|
||||
res.key = key;
|
||||
res.ch = ch;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* _MONITOR_SUBROUTINES_HEADER_ */
|
||||
12
src/preserve_zero_pages.s
Normal file
12
src/preserve_zero_pages.s
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
.rtmodel version, "1"
|
||||
.rtmodel core, "*"
|
||||
|
||||
;; External declarations
|
||||
.section registers ; pseudo registers in zero page
|
||||
|
||||
.section zpsave, noinit
|
||||
.pubweak __preserve_zp
|
||||
__preserve_zp: ; This symbol meant to be required
|
||||
.space 256
|
||||
.require __preserve_zp_needed
|
||||
.require __restore_zp_needed
|
||||
16
src/sound.h
Normal file
16
src/sound.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef _SOUND_HEADER_
|
||||
#define _SOUND_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SND_TAP() (snd_beep_lower(0x08, 0xFF))
|
||||
|
||||
void snd_beep(uint8_t duration, uint8_t pitch);
|
||||
void snd_beep_lower(uint8_t duration, uint8_t pitch);
|
||||
|
||||
void snd_start(void);
|
||||
void snd_sad_scale(void);
|
||||
void snd_festive(void);
|
||||
void snd_mod_button(void);
|
||||
|
||||
#endif /* _SOUND_HEADER_ */
|
||||
161
src/sound.s
Normal file
161
src/sound.s
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
.rtmodel version,"1"
|
||||
.rtmodel core,"6502"
|
||||
|
||||
.extern _Vfp
|
||||
.extern _Vsp
|
||||
.extern _Zp
|
||||
|
||||
;;; I/O ports
|
||||
IO_SPK: .equ 0xC030
|
||||
|
||||
;;; Monitor routines
|
||||
MON_WAIT .equ 0xFCA8
|
||||
|
||||
;;; snd_beep
|
||||
;;; Generates a sound of configurable duration and pitch
|
||||
;;; See https://github.com/tilleul/apple2/blob/master/2liners/the%20art%20of%202-liners/SCRN_PLOT_your_sound_routine.md
|
||||
;;; Parameters:
|
||||
;;; - duration [A]: duration of the sound
|
||||
;;; - pitch [_Zp[0]]: sound pitch
|
||||
;;;
|
||||
;;; Returns: Nothing
|
||||
snd_beep:
|
||||
P_PITCH$: .equ _Zp+0
|
||||
tax ; Move accumulator with duration into register x
|
||||
-- ldy zp:P_PITCH$ ; Load the pitch from zp
|
||||
lda IO_SPK ; Toggle the speaker
|
||||
- dey
|
||||
bne -
|
||||
dex ; Decrement the time left
|
||||
bne -- ; Back at loading the pitch if we need to go on
|
||||
rts
|
||||
|
||||
snd_beep_lower:
|
||||
P_PITCH$: .equ _Zp+0
|
||||
tax ; Move accumulator with duration into register x
|
||||
-- ldy zp:P_PITCH$ ; Load the pitch from zp
|
||||
lda IO_SPK ; Toggle the speaker
|
||||
- dey
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
bne -
|
||||
dex ; Decrement the time left
|
||||
bne -- ; Back at loading the pitch if we need to go on
|
||||
rts
|
||||
|
||||
snd_mod_button:
|
||||
P_PITCH$: .equ _Zp+0
|
||||
lda #0xAA
|
||||
sta zp:P_PITCH$
|
||||
lda #0x25
|
||||
jsr snd_beep_lower
|
||||
lda #0xCC
|
||||
sta zp:P_PITCH$
|
||||
lda #0x0A
|
||||
jsr snd_beep_lower
|
||||
rts
|
||||
|
||||
snd_start:
|
||||
P_PITCH$: .equ _Zp+0
|
||||
lda #0xCC
|
||||
sta zp:P_PITCH$
|
||||
lda #0x99
|
||||
jsr snd_beep_lower
|
||||
lda #0xAA
|
||||
sta zp:P_PITCH$
|
||||
lda #0x80
|
||||
jsr snd_beep_lower
|
||||
lda #0x88
|
||||
sta zp:P_PITCH$
|
||||
lda #0xff
|
||||
jsr snd_beep_lower
|
||||
rts
|
||||
|
||||
snd_sad_scale:
|
||||
T_IDX$: .equ _Zp+1
|
||||
P_PITCH$: .equ _Zp+0
|
||||
ldy #6
|
||||
- lda __sad_scale_pitches,y
|
||||
sta zp:P_PITCH$
|
||||
lda #0x90
|
||||
sty zp:T_IDX$
|
||||
jsr snd_beep_lower
|
||||
ldy zp:T_IDX$
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
|
||||
__sad_scale_pitches:
|
||||
.byte 0xFF
|
||||
.byte 0xFF
|
||||
.byte 0xFF
|
||||
.byte 0xCC
|
||||
.byte 0xAA
|
||||
.byte 0x88
|
||||
.byte 0x88
|
||||
|
||||
snd_festive:
|
||||
T_IDX$: .equ _Zp+1
|
||||
P_PITCH$: .equ _Zp+0
|
||||
ldy #10
|
||||
- lda __festive_pitches,y
|
||||
sta zp:P_PITCH$
|
||||
lda __festive_duration,y
|
||||
sty zp:T_IDX$
|
||||
jsr snd_beep
|
||||
ldy zp:T_IDX$
|
||||
|
||||
lda __festive_wait,y
|
||||
jsr MON_WAIT ; Call the monitor wait routine
|
||||
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
|
||||
__festive_pitches:
|
||||
.byte 0xEE
|
||||
.byte 0xCC
|
||||
.byte 0xAA
|
||||
.byte 0x99
|
||||
.byte 0xAA
|
||||
.byte 0xCC
|
||||
.byte 0xCC
|
||||
.byte 0x88
|
||||
.byte 0xAA
|
||||
.byte 0xCC
|
||||
.byte 0xCC
|
||||
|
||||
__festive_duration:
|
||||
.byte 0xFF
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
.byte 0x80
|
||||
|
||||
__festive_wait:
|
||||
.byte 0x70
|
||||
.byte 0x70
|
||||
.byte 0x70
|
||||
.byte 0xA0
|
||||
.byte 0xA0
|
||||
.byte 0xA0
|
||||
.byte 0xA0
|
||||
.byte 0xA0
|
||||
.byte 0xA0
|
||||
.byte 0xA0
|
||||
.byte 0xA0
|
||||
|
||||
;;; Declaration of public symbols
|
||||
.public snd_beep
|
||||
.public snd_beep_lower
|
||||
.public snd_sad_scale
|
||||
.public snd_festive
|
||||
.public snd_mod_button
|
||||
.public snd_start
|
||||
125
src/tk2k_startup.s
Normal file
125
src/tk2k_startup.s
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
.rtmodel cstartup,"tk2k"
|
||||
.rtmodel version, "1"
|
||||
.rtmodel core, "*"
|
||||
|
||||
;; External declarations
|
||||
.section cstack
|
||||
.section heap
|
||||
.section data_init_table
|
||||
.section registers ; pseudo registers in zero page
|
||||
.section zpsave ; this is where we'll save the zero page data
|
||||
|
||||
.extern main, exit, __low_level_init
|
||||
.extern _Zp, _Vsp, _Vfp
|
||||
|
||||
.pubweak __program_root_section, __program_start
|
||||
|
||||
call: .macro dest
|
||||
jsr \dest
|
||||
.endm
|
||||
|
||||
.section programStart, root
|
||||
__program_root_section:
|
||||
jmp __program_start
|
||||
.section startup, root, noreorder
|
||||
__program_start:
|
||||
.section startup, noreorder
|
||||
.pubweak __preserve_zp_needed
|
||||
__preserve_zp_needed:
|
||||
ldx #0x00 ; Save the whole zero-page, a bit wasteful, will have to look into it in the future
|
||||
- lda zp:0x00,x
|
||||
sta .sectionStart zpsave,x
|
||||
inx
|
||||
bne -
|
||||
|
||||
.section startup, root, noreorder
|
||||
lda #.byte0(.sectionEnd cstack)
|
||||
sta zp:_Vsp
|
||||
lda #.byte1(.sectionEnd cstack)
|
||||
sta zp:_Vsp+1
|
||||
jsr __low_level_init
|
||||
|
||||
;;; Initialize data sections if needed.
|
||||
.section startup, noroot, noreorder
|
||||
.pubweak __data_initialization_needed
|
||||
.extern __initialize_sections
|
||||
__data_initialization_needed:
|
||||
lda #.byte0 (.sectionStart data_init_table)
|
||||
sta zp:_Zp
|
||||
lda #.byte1 (.sectionStart data_init_table)
|
||||
sta zp:_Zp+1
|
||||
lda #.byte0 (.sectionEnd data_init_table)
|
||||
sta zp:_Zp+2
|
||||
lda #.byte1 (.sectionEnd data_init_table)
|
||||
sta zp:_Zp+3
|
||||
call __initialize_sections
|
||||
|
||||
.section startup, noroot, noreorder
|
||||
.pubweak __call_initialize_global_streams
|
||||
.extern __initialize_global_streams
|
||||
__call_initialize_global_streams:
|
||||
call __initialize_global_streams
|
||||
|
||||
;;; **** Initialize heap if needed.
|
||||
.section startup, noroot, noreorder
|
||||
.pubweak __call_heap_initialize
|
||||
.extern __heap_initialize, __default_heap
|
||||
__call_heap_initialize:
|
||||
lda #.byte0 __default_heap
|
||||
sta zp:_Zp+0
|
||||
lda #.byte1 __default_heap
|
||||
sta zp:_Zp+1
|
||||
lda #.byte0 (.sectionStart heap)
|
||||
sta zp:_Zp+2
|
||||
lda #.byte1 (.sectionStart heap)
|
||||
sta zp:_Zp+3
|
||||
lda #.byte0 (.sectionSize heap)
|
||||
sta zp:_Zp+4
|
||||
lda #.byte1 (.sectionSize heap)
|
||||
sta zp:_Zp+5
|
||||
call __heap_initialize
|
||||
|
||||
.section startup, root, noreorder
|
||||
tsx
|
||||
stx _InitialStack ; for exit()
|
||||
lda #0 ; argc = 0
|
||||
sta zp:_Zp
|
||||
sta zp:_Zp+1
|
||||
jsr main
|
||||
|
||||
;;; Restore the zeropage
|
||||
ldx #0x00 ; Restore the whole zero-page
|
||||
- lda .sectionStart zpsave,x
|
||||
sta zp:0x00,x
|
||||
inx
|
||||
bne -
|
||||
|
||||
rts
|
||||
|
||||
;;; jmp exit
|
||||
|
||||
;;; ***************************************************************************
|
||||
;;;
|
||||
;;; __low_level_init - custom low level initialization
|
||||
;;;
|
||||
;;; This default routine just returns doing nothing. You can provide your own
|
||||
;;; routine, either in C or assembly for doing custom low leve initialization.
|
||||
;;;
|
||||
;;; ***************************************************************************
|
||||
|
||||
.section code
|
||||
.pubweak __low_level_init
|
||||
__low_level_init:
|
||||
rts
|
||||
|
||||
;;; ***************************************************************************
|
||||
;;;
|
||||
;;; Keep track of the initial stack pointer so that it can be restores to make
|
||||
;;; a return back on exit().
|
||||
;;;
|
||||
;;; ***************************************************************************
|
||||
|
||||
.section zdata, bss
|
||||
.pubweak _InitialStack
|
||||
_InitialStack:
|
||||
.space 1
|
||||
65
src/utility.c
Normal file
65
src/utility.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include "utility.h"
|
||||
|
||||
#include "mem_registers.h"
|
||||
#include "monitor_subroutines.h"
|
||||
#include "line_data.h"
|
||||
|
||||
void num_to_decbuf(uint16_t n, uint8_t len, uint8_t *buf) {
|
||||
|
||||
for(uint8_t idx = 0; idx < len; idx++) {
|
||||
buf[idx] = n % 10;
|
||||
n /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte
|
||||
uint8_t bit_reverse(uint8_t b) {
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/14009765/fastest-way-to-count-bits
|
||||
uint8_t bit_count(uint8_t b) {
|
||||
b = (b & 0x55) + (b >> 1 & 0x55);
|
||||
b = (b & 0x33) + (b >> 2 & 0x33);
|
||||
b = (b & 0x0f) + (b >> 4 & 0x0f);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void lfsr_init(uint16_t reg) {
|
||||
*((uint16_t*)LFSR_REGISTER_ADDRESS) = 0xF00D;
|
||||
}
|
||||
|
||||
uint16_t lfsr_update(void) {
|
||||
uint16_t *lfsr = ((uint16_t*)LFSR_REGISTER_ADDRESS);
|
||||
|
||||
*lfsr ^= (*lfsr) >> 7;
|
||||
*lfsr ^= (*lfsr) << 9;
|
||||
*lfsr ^= (*lfsr) >> 13;
|
||||
|
||||
return *lfsr;
|
||||
}
|
||||
|
||||
#define CRC8RDALLAS_POLY 0x31
|
||||
uint8_t calculate_crc8(uint8_t* data, uint8_t len) {
|
||||
uint8_t crc = 0;
|
||||
|
||||
for(uint8_t data_idx = 0; data_idx < len; data_idx++) {
|
||||
uint8_t carry;
|
||||
uint8_t d = data[data_idx];
|
||||
|
||||
for (uint8_t i = 8; i > 0; i--) {
|
||||
carry = (crc & 0x80);
|
||||
crc <<= 1;
|
||||
if (d & 1) crc |= 1;
|
||||
d >>= 1;
|
||||
if (carry) crc ^= CRC8RDALLAS_POLY;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
34
src/utility.h
Normal file
34
src/utility.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef _UTILITY_HEADER_
|
||||
#define _UTILITY_HEADER_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SCREEN_HEIGHT 192
|
||||
#define SCREEN_WIDTH_BYTES 128
|
||||
#define BYTES_PER_LINE 40
|
||||
|
||||
#define PEEKZ(a) (*(volatile uint8_t* __attribute__((zpage)))(a))
|
||||
#define POKEZ(a, b) ((*(volatile uint8_t* __attribute__((zpage)))(a)) = b)
|
||||
|
||||
#define PEEK(a) (*(volatile uint8_t*)(a))
|
||||
#define POKE(a, b) ((*(volatile uint8_t*)(a)) = b)
|
||||
|
||||
#define PEEKW(a) (*(volatile uint16_t*)(a))
|
||||
#define POKEW(a, b) ((*(volatile uint16_t*)(a)) = b)
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
// 0x356 - 0x357 actually fall inside the
|
||||
// decoding table for DISK II sector reading,
|
||||
// but they're unused bytes! So we can use them
|
||||
// to store the lfsr_update
|
||||
#define LFSR_REGISTER_ADDRESS 0x0356
|
||||
|
||||
void num_to_decbuf(uint16_t n, uint8_t len, uint8_t *buf);
|
||||
uint8_t bit_reverse(uint8_t b);
|
||||
uint8_t bit_count(uint8_t b);
|
||||
void lfsr_init(uint16_t reg);
|
||||
uint16_t lfsr_update(void);
|
||||
uint8_t calculate_crc8(uint8_t* data, uint8_t len);
|
||||
|
||||
#endif /* _UTILITY_HEADER_ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue