diff --git a/024.simple_text/Makefile b/024.simple_text/Makefile
new file mode 100644
index 0000000..4940b17
--- /dev/null
+++ b/024.simple_text/Makefile
@@ -0,0 +1,25 @@
+EXAMPLE_NAME=simple_text
+
+USERSTACK_ADDRESS=7fffc # userstack used by bootblock
+BASE_ADDRESS=4000 # if you change this you must also change link.script.x
+ # note: this must be high enough not to conflict with MFMbufE
+
+BOOTBLOCK_ASM=alpine_bootblock.s
+OBJS=out/init.o out/utils.o out/trackloader.o out/image.o out/disk.o out/blit.o out/cpu.o
+
+
+IMAGES=font.png
+IMAGEDATA=$(addprefix out/, $(IMAGES:.png=.bin))
+VASM_EXTRA_ARGS=
+
+#SYMBOL_INFO=-M
+LINK_COMMANDLINE=vlink -T link.script.x -brawbin1 $< $(OBJS) $(SYMBOL_INFO) -o out/main.bin
+
+include ../shared/base.mk
+
+
+out/%.bin: ../assets/%.png
+ $(IMAGECON) --input $< $(IMAGECON_ARGS) --output-bitplanes --output-palette-asm --output-palette --output out/$* --colors=16 --use-palette palette.pal --output-mask
+
+out/main.o: $(IMAGEDATA) constants.i Makefile link.script.x palette.pal
+out/init.o: constants.i Makefile
\ No newline at end of file
diff --git a/024.simple_text/README.md b/024.simple_text/README.md
new file mode 100644
index 0000000..aacc3ee
--- /dev/null
+++ b/024.simple_text/README.md
@@ -0,0 +1,19 @@
+photo slideshow
+===============
+
+The main theme of this demo is trackloading data whilst the application is running.
+
+We cram a DD floppy full of image and music data, then load and display each image in turn whilst playing a P61 module.
+
+There are quite a few new things in here:
+
+ * We use a [modified bootloader](alpine_bootloader.s) that reads the size of the main program from the disk before trackloading it.
+ * Photon's trackloader is also now copied into the main program so it can be used to load subsequent data.
+ * We use multiple sections and a [linker script](link.script.x) to help with organising data on the ADF image. The linker script is not technically required, however I wanted to see what I could do with vlink and linker scripts.
+ * We switched to the [doynamite68k](https://github.com/AxisOxy/Planet-Rocklobster/tree/master/tools/doynamite68k) compressor/decompressor. The compressed file are larger than those generated by Shinkler, however the decompressor is a lot faster.
+ * P61 is configured to use CIA mode.
+
+try it
+------
+ * [Download disk image](bin/slideshow.adf?raw=true)
+ * Run in SAE
diff --git a/024.simple_text/alpine_bootblock.s b/024.simple_text/alpine_bootblock.s
new file mode 100644
index 0000000..1953463
--- /dev/null
+++ b/024.simple_text/alpine_bootblock.s
@@ -0,0 +1,352 @@
+*** hardware trackloader bootblock
+*** original BootLoader.S by Photon ;NOTE: PC-relative code is PREFERRED.
+*** see http://coppershade.org/asmskool/SOURCES/Photon-snippets/DDE5-BootLoader.S
+*** this version hacked by alpine9000
+
+ include "../include/registers.i"
+
+LoaderVars equ $100 ;Useful variables, see CPUinfo:
+Loader equ $120 ;start of load script
+
+MyUserStack equ USERSTACK_ADDRESS ;SSP is at a safe place, but set user
+ ;stack.
+
+MFMsync equ $4489 ;AmigaDOS standard sync marker.
+MFMlen equ 12980 ;Legacy trackdata read length in bytes
+
+ShortWt:MACRO ;CPU-independent nop;nop replacement
+ tst.w (a6)
+ ENDM
+
+ *** Boot Block starts here ***
+
+Boot: dc.b 'DOS',0
+ dc.l 0,880
+
+BootCode: ;gathers some data, turns off OS, copies itself to $100
+
+ *--- Fetch system info ---*
+
+ move.l 4.w,a6 ;execbase (will soon be destroyed)
+ move.l 294(a6),d4 ;CPUinfo in lowest byte for your use.
+ sub.l a4,a4 ;VBR will always be 0 when booting
+ ;from floppy. No GetVBR needed.
+ *--- Fastmem available? ---*
+
+ if 1
+ move.l #$20004,d1 ;fast+largest
+ else
+ move.l #$20002,d1 ;chip+largest
+ endif
+ jsr -216(a6) ;AvailMem()
+ move.l d0,d5
+
+ sub.l #2048,d5 ;leave room for stacks to grow
+ moveq #4,d1
+ jsr -198(a6) ;AllocMem()
+ and.l #-8,d0
+ move.l d0,a5 ;Start Address
+
+ *--- OS off ---* ;you're nice'n all, but now you die.
+
+ lea $dff002,a6 ;Loader uses this custom base addr
+
+ tst.w (a6) ;wait out blitter
+.wblit: btst #6,(a6)
+ bne.s .wblit
+
+ move.l #$7fff7fff,d1
+ move.l d1,$9a-2(a6) ;disable interrupts & req
+ move.w d1,$9c-2(a6) ;play it again Sam
+ sub.w #$20,d1 ;don't affect Sprite DMA until Vblank
+ move.w d1,$96-2(a6) ;disable DMA
+
+ lea MyUserStack,a7 ;some safe place compatible with
+ ;platform requirements
+ *--- Copy rest of code/data to fixed address ---*
+
+ lea CopyStart(PC),a0
+ lea (LoaderVars).w,a1
+ moveq #(BootE-CopyStart)/8,d2
+.copyl: move.l (a0)+,(a1)+
+ move.l (a0)+,(a1)+
+ dbf d2,.copyl
+ JMP (Loader).w ;Info from Exec passed in 4 registers
+
+******************** $100.w ********************
+
+CopyStart:
+
+CPUinfo: dc.l 0 ;$100
+FastMemSize: dc.l 0 ;$104
+SysVBR: dc.l 0 ;$108
+FastMemStart: dc.l 0 ;$10c
+
+MFMcyl: dc.w 0
+MFMhead: dc.w 0
+MFMdrv: dc.w 0
+MFMchk: dc.l 0
+
+ dc.w 0 ;padding to $120
+ dc.w 0 ;
+ dc.w 0 ;
+
+LoadScript: ;At $120, sysinfo in 4 regs, a6=$dff002
+ lea CPUinfo(PC),a3 ;Use this for PC-rel in a pinch.
+ movem.l d4/d5/a4/a5,(a3)
+ bsr.s WaitEOF
+ lea NullCop(PC),a0
+ move.l a0,$80-2(a6) ;blank copper
+ move.w #$87d0,$96-2(a6) ;enable DMA (sprites enabled when used)
+
+ *--- load first part ---*
+
+ lea BASE_ADDRESS,a0 ; main entry point
+
+ if 1
+ moveq #2,d0 ;from sector 2
+ move.w #-1,d1 ;num sectors, - ==Step0
+ jsr LoadMFMB
+
+ move.l 4(a0),d1
+ add.l #512,d1
+ lsr.l #6,d1
+ lsr.l #3,d1
+ moveq #2,d0 ;from sector 2
+ jsr LoadMFMB
+
+ lea $dff000,a6 ;restore plain custombase addr for demo
+
+ move.l (a0),a0
+ jmp (a0) ; -> main entry point
+
+ else
+
+ -moveq #2,d0 ;from sector 2
+ move.w #-((mainEnd-mainStart)/512),d1;num sectors, - ==Step0
+ jsr LoadMFMB
+
+ jmp (a0) ; -> main entry point
+
+ endif
+
+ *** MFMLoader.S by Photon *** ;requires a6=$dff002
+
+WaitEOF:
+ btst #0,5-2(a6)
+ beq.s WaitEOF
+.w1: cmp.b #$37,6-2(a6)
+ bne.s .w1
+.w2: cmp.b #$37,6-2(a6) ;wait for last PAL line, $138
+ beq.s .w2
+ rts
+
+LoadMFMB: ;loadsectors.a0=dst,d0=startsec.W,d1=nrsecs.W(-=Step0)
+ MOVEM.L D0-D7/A0-A6,-(SP)
+ lea $bfd100,a4
+ bsr MotorOn
+ tst.w d1 ;if neg length,then Step0 first
+ bpl.s .NoSt0
+ neg.w d1
+.St0: btst #4,$f01(a4) ;head on cyl 0?
+ beq.s .Rdy0
+ bsr.s StepOut
+ bra.s .St0
+.Rdy0: lea MFMcyl(PC),a1
+ clr.w (a1)
+.NoSt0: and.l #$ffff,d0
+ divu #22,d0 ;startcyl
+ sub.w MFMcyl(PC),d0 ;delta-step
+ beq.s .StRdy
+ bmi.s .StOut
+ subq.w #1,d0
+.StIn: bsr.s StepIn
+ dbf d0,.StIn
+ bra.s .StRdy
+ not.w d0 ;=neg+sub#1
+.StOut: bsr.s StepOut
+ dbf d0,.StIn
+.StRdy: swap d0 ;startsec within cyl
+ cmp.w #11,d0
+ blt.s .Head0
+ sub.w #11,d0
+ bra.s .Head1
+.Head0: bset #2,(a4)
+ lea MFMhead(PC),a1
+ clr.w (a1)
+ bsr LoadTrak ;read track+decode
+ beq.s .End
+.Head1: bclr #2,(a4) ;Head 1
+ lea MFMhead(PC),a1
+ move.w #1,(a1)
+ bsr LoadTrak ;read track+decode
+ beq.s .End
+ bsr.s StepIn ;1 cyl forward
+ bra.s .Head0
+.End: bsr.s MotorOff
+ MOVEM.L (SP)+,D0-D7/A0-A6
+ RTS
+
+StepOut:
+ bset #1,(a4)
+ lea MFMcyl(PC),a1
+ subq.w #1,(a1)
+ ShortWt
+ bclr #0,(a4)
+ ShortWt
+ bset #0,(a4)
+ bsr.s StepWt
+ RTS
+
+StepIn:
+ bclr #1,(a4)
+ lea MFMcyl(PC),a1
+ addq.w #1,(a1)
+ ShortWt
+ bclr #0,(a4)
+ ShortWt
+ bset #0,(a4)
+ bsr.s StepWt
+ RTS
+
+StepWt:
+ moveq #67,d6 ;wait >3 ms
+LeaveLine:
+.loop1: move.b 6-2(a6),d7
+.loop2: cmp.b 6-2(a6),d7
+ beq.s .loop2
+ dbf d6,.loop1
+ RTS
+
+MotorOn:
+ move.w MFMdrv(PC),d7
+ addq.w #3,d7
+ or.b #$78,(a4)
+ bset d7,(a4)
+ ShortWt
+ bclr #7,(a4) ;turns motor on
+ ShortWt
+ bclr d7,(a4)
+ ShortWt
+.DiskR: btst #5,$f01(a4) ;wait until motor running
+ bne.s .DiskR
+ RTS
+
+MotorOff:
+ move.w MFMdrv(PC),d7
+ addq.w #3,d7
+ bset d7,(a4)
+ ShortWt
+ bset #7,(a4)
+ ShortWt
+ bclr d7,(a4)
+ RTS
+
+LoadTrak: ;loadtrack+decode.a0=dst,d0=secoffs,d1=secsleft
+ MOVE.W D0,-(SP)
+ MOVE.W D1,-(SP)
+ ;; lea (MFMbuf).w,a1
+ lea (MFMbuf),a1
+ move.w #2,$9c-2(a6) ;Clr Req
+ move.l a1,$20-2(a6)
+ move.w #$8210,$96-2(a6) ;DskEna
+ move.w #MFMsync,$7e-2(a6)
+ move.w #$9500,$9e-2(a6)
+ move.w #$4000,$24-2(a6)
+ move.w #$8000+MFMlen/2,$24-2(a6);DskLen(12980)+DmaEn
+ move.w #$8000+MFMlen/2,$24-2(a6);start reading MFMdata
+.Wrdy:
+ btst #1,$1f-2(a6) ;wait until data read
+ beq.s .Wrdy
+ move.w d0,d2
+ add.w d1,d2 ;highest sec# (d0=lowest)
+ cmp.w #11,d2
+ ble.s .NoOvr
+ moveq #11,d2
+.NoOvr: sub.w d0,d2 ;nrsecs
+ move.l #$55555555,d3 ;and-const
+ move.w d2,d1
+ subq.w #1,d1 ;loopctr
+.FindS: cmp.w #MFMsync,(a1)+ ;search for a sync word
+ bne.s .FindS
+ cmp.b (a1),d3 ;search for 0-nibble
+ bne.s .FindS
+ move.l (a1)+,d4 ;decode fmtbyte/trk#,sec#,eow#
+ move.l (a1)+,d5
+ and.w d3,d4
+ and.w d3,d5
+ add.w d4,d4
+ or.w d5,d4
+ lsr.w #8,d4 ;sec#
+ sub.w d0,d4 ;do we want this sec?
+ bmi.s .Skip
+ cmp.w d2,d4
+ blt.s .DeCode
+.Skip: lea 48+1024(a1),a1 ;nope
+ bra.s .FindS
+.DeCode:lea 40(a1),a1 ;found a sec,skip unnecessary data
+ move.l a1,d6
+ lea MFMchk(PC),a1
+ clr.l (a1)
+ move.l d6,a1
+ move.l (a1)+,d6 ;decode data chksum.L
+ move.l (a1)+,d5
+ and.l d3,d6
+ and.l d3,d5
+ add.l d6,d6
+ or.l d5,d6 ;chksum
+ lea 512(a1),a2
+ add.w d4,d4 ;x512
+ lsl.w #8,d4
+ lea (a0,d4.w),a3 ;dest addr for this sec
+ moveq #127,d7
+.DClup: move.l (a1)+,d4
+ move.l (a2)+,d5
+ and.l d3,d4
+ and.l d3,d5
+ eor.l d4,d6 ;EOR with checksum
+ eor.l d5,d6 ;EOR with checksum
+ add.l d4,d4
+ or.l d5,d4
+ move.l d4,(a3)+
+ dbf d7,.DClup ;chksum should now be 0 if correct
+ lea MFMchk(PC),a1
+ or.l d6,(a1) ;or with track total chksum
+ move.l a2,a1
+ dbf d1,.FindS ;decode next sec
+ MOVE.W (SP)+,D1
+ MOVE.W (SP)+,D0
+ move.l MFMchk(PC),d3 ;track total chksum OK?
+ bne LoadTrak ;no,retry
+ moveq #0,d0 ;set to start of track
+ move.w d2,d3
+ add.w d3,d3
+ lsl.w #8,d3
+ add.w d3,a0
+ sub.w d2,d1 ;sub #secs loaded
+ RTS
+
+NullCop:
+ dc.w $1fc,0
+ dc.w $100,$0200
+ dc.w $96,$0020 ;ensure sprite DMA is off until needed
+ dc.w $ffdf,$fffe
+ dc.l -2
+BootE:
+
+ *** Boot Block ends here ***
+
+ dc.b "BootLoader by Photon/Scoopex"
+ ;pad bootblock to correct size
+ cnop 0,1024
+
+;MFMbuf is placed here after bootblock end, $3c0.w or so when copied.
+MFMbuf equ LoaderVars+(BootE-CopyStart)
+MFMbufE equ MFMbuf+MFMlen ;lowest free address. $372e for a full bootblock.
+
+mainStart:
+ incbin "out/main.bin"
+ cnop 0,512
+mainEnd:
+ end
+
diff --git a/024.simple_text/blit.s b/024.simple_text/blit.s
new file mode 100644
index 0000000..be994fc
--- /dev/null
+++ b/024.simple_text/blit.s
@@ -0,0 +1,99 @@
+ include "includes.i"
+ xdef blitObject64
+
+;; BLTCON? configuration
+;; http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node011C.html
+;; blitter logic function minterm truth table
+;; fill in D column for desired function
+;; A(mask) B(bob) C(bg) D(dest)
+;; - - - -
+;; 0 0 0 0
+;; 0 0 1 1
+;; 0 1 0 0
+;; 0 1 1 1
+;; 1 0 0 0
+;; 1 0 1 0
+;; 1 1 0 1
+;; 1 1 1 1
+;; read D column from bottom up = 11001010 = $ca
+;; this is used in the LF? bits
+
+BLIT_LF_MINTERM equ $ca
+BLIT_DEST equ $100
+BLIT_SRCC equ $200
+BLIT_SRCB equ $400
+BLIT_SRCA equ $800
+BLIT_ASHIFTSHIFT equ 12 ;Bit index of ASH? bits
+
+BLIT_BOB_WIDTH equ 32 ; Must blit extra word to allow shifting
+BLIT_BOB_HEIGHT equ 12
+BLIT_BOB_WIDTH_BYTES equ BLIT_BOB_WIDTH/8
+BLIT_BOB_WIDTH_WORDS equ BLIT_BOB_WIDTH/16
+
+blitWait:
+ tst DMACONR(a6) ;for compatibility
+.waitblit:
+ btst #6,DMACONR(a6)
+ bne.s .waitblit
+ rts
+
+ ;; blitobject
+ ;; d0 - xpos
+ ;; d1 - ypos
+ ;; a0 - display
+ ;; a1 - object
+ ;; a2 - mask
+blitObject64:
+ movem.l d0-a6,-(sp)
+ bsr blitWait
+
+ ;; d0 = XPOS
+ ;; d1 = YPOS
+ ;; d4 = XPOS_BYTES
+
+ move.w d0,d4 ; d4 = XPOS
+ lsr.w #3,d4 ; d4 = XPOS_BYTES
+
+ if 1
+ sub.w #8,d0
+ endif
+
+ ;; this shift will give us the bits to shift (bits 0-3) in bits (12-15) of d0
+ lsl.w #8,d0 ; BLIT_SOURCE_SHIFT< sectors
+ lsr.l #3,d0
+ add.l #2,d0 ; offset for bootblock
+
+
+ add.l #512,d1
+ lsr.l #6,d1 ; bytes -> sectors
+ lsr.l #3,d1
+ neg.w d1
+ jsr LoadMFMB ; load the data!
+
+ movem.l (sp)+,d0-a6
+ rts
diff --git a/024.simple_text/font.png b/024.simple_text/font.png
new file mode 100644
index 0000000..67f0b54
Binary files /dev/null and b/024.simple_text/font.png differ
diff --git a/024.simple_text/image.s b/024.simple_text/image.s
new file mode 100644
index 0000000..a0b31f8
--- /dev/null
+++ b/024.simple_text/image.s
@@ -0,0 +1,54 @@
+ include "includes.i"
+
+ xdef DisplayBitplane
+
+LoadImage:
+ ;; d0 - size
+ ;; a1 - start address
+ ;; a2 - InstallColorPalette(X) address
+
+ lea bitplanes3,a0
+ bsr LoadDiskData ; load data from disk
+
+ ;; move.l nextbitplane,a1 ; decompress into offscreen bitplanesp(1/2)
+
+ ;; a0 = Input buffer to be decompressed. Must be 16-bit aligned!
+ ;; a1 = Output buffer. Points to the end of the data at exit
+ jsr Depack
+
+ jsr WaitVerticalBlank ; avoid tearing when we show the new image
+ jsr (a2) ; install new color palette
+ jsr DisplayBitplane ; display new image
+
+ rts
+
+
+DisplayBitplane:
+ ;; a0 - bitplane address
+ movem.l d0/a0-a1,-(sp)
+ moveq.l #0,d0
+ move.l a0,a1
+ lea copperListBplPtr,a0
+ jsr PokeBitplanePointers
+ movem.l (sp)+,d0/a0-a1
+ rts
+
+PokeBitplanePointers:
+ ; d0 = frame offset in bytes
+ ; a0 = BPLP copper list address
+ ; a1 = bitplanes pointer
+ movem.l d0-a6,-(sp)
+ add.l d0,a1 ; Offset for odd/even frames
+ moveq #SCREEN_BIT_DEPTH-1,d0
+.bitplaneloop:
+ move.l a1,d1
+ move.w d1,2(a0)
+ swap d1
+ move.w d1,6(a0)
+ lea SCREEN_WIDTH_BYTES(a1),a1
+ addq #8,a0
+ dbra d0,.bitplaneloop
+ movem.l (sp)+,d0-a6
+ rts
+
+
diff --git a/024.simple_text/includes.i b/024.simple_text/includes.i
new file mode 100644
index 0000000..e5906ae
--- /dev/null
+++ b/024.simple_text/includes.i
@@ -0,0 +1,7 @@
+ include "../include/registers.i"
+ include "../include/vector.i"
+ include "../include/beambits.i"
+ include "../include/bplconbits.i"
+ include "hardware/dmabits.i"
+ include "hardware/intbits.i"
+ include "constants.i"
\ No newline at end of file
diff --git a/024.simple_text/init.s b/024.simple_text/init.s
new file mode 100644
index 0000000..ba47adc
--- /dev/null
+++ b/024.simple_text/init.s
@@ -0,0 +1,32 @@
+ include "includes.i"
+
+ xdef Init
+
+ ;; custom chip base globally in a6
+Init:
+ movem.l d0-a6,-(sp)
+ ;; move #$7ff,DMACON(a6) ; disable all dma
+ ;; move #$7fff,INTENA(a6) ; disable all interrupts
+
+ ;; lea Level3InterruptHandler,a3
+ ;; move.l a3,LVL3_INT_VECTOR
+
+ ;; set up playfield
+ move.w #(RASTER_Y_START<<8)|RASTER_X_START,DIWSTRT(a6)
+ move.w #((RASTER_Y_STOP-256)<<8)|(RASTER_X_STOP-256),DIWSTOP(a6)
+
+ move.w #(RASTER_X_START/2-SCREEN_RES),DDFSTRT(a6)
+ move.w #(RASTER_X_START/2-SCREEN_RES)+(8*((SCREEN_WIDTH/16)-1)),DDFSTOP(a6)
+
+ move.w #(SCREEN_BIT_DEPTH<<12)|COLOR_ON,BPLCON0(a6)
+ move.w #SCREEN_WIDTH_BYTES*SCREEN_BIT_DEPTH-SCREEN_WIDTH_BYTES,BPL1MOD(a6)
+ move.w #SCREEN_WIDTH_BYTES*SCREEN_BIT_DEPTH-SCREEN_WIDTH_BYTES,BPL2MOD(a6)
+
+ ;; install copper list, then enable dma and selected interrupts
+ lea copperList,a0
+ move.l a0,COP1LC(a6)
+ move.w COPJMP1(a6),d0
+ move.w #(DMAF_BLITTER|DMAF_SETCLR!DMAF_COPPER!DMAF_RASTER!DMAF_MASTER),DMACON(a6)
+ ;; move.w #(INTF_SETCLR|INTF_VERTB|INTF_INTEN),INTENA(a6)
+ movem.l (sp)+,d0-a6
+ rts
\ No newline at end of file
diff --git a/024.simple_text/link.script.x b/024.simple_text/link.script.x
new file mode 100644
index 0000000..4933239
--- /dev/null
+++ b/024.simple_text/link.script.x
@@ -0,0 +1,28 @@
+MEMORY
+{
+ disk: org = 0x4000, len = 901120-0x4000
+ ram: org = 0x00000, len = 0x80000
+}
+
+SECTIONS
+{
+ load : {
+ startCode = .;
+ *(.text)
+ *(.data)
+ *(CODE)
+ *(DATA)
+ endCode = .;
+ } > disk
+
+ noload ALIGN(512) : {
+ startData = .;
+ *(.noload)
+ endData = .;
+ } > disk
+
+ bss (NOLOAD) : {
+ . = endCode;
+ *(.bss)
+ } > ram;
+}
\ No newline at end of file
diff --git a/024.simple_text/module.s b/024.simple_text/module.s
new file mode 100644
index 0000000..fd10212
--- /dev/null
+++ b/024.simple_text/module.s
@@ -0,0 +1,21 @@
+ xdef LoadModule
+
+LoadModule:
+ movem.l d0-a6,-(sp)
+ lea bitplanes1,a0
+ lea compressedModule,a1
+ move.l #endCompressedModule,d0
+ sub.l #compressedModule,d0
+ ;; a0 - destination address
+ ;; a1 - start address
+ ;; d0 - size
+ jsr LoadDiskData
+
+ ;; a0 - input buffer to be decompressed. Must be 16-bit aligned!
+ ;; a1 - output buffer. Points to the end of the data at exit
+ lea bitplanes1,a0
+ lea Module,a1
+ jsr Depack
+
+ movem.l (sp)+,d0-a6
+ rts
\ No newline at end of file
diff --git a/024.simple_text/palette.pal b/024.simple_text/palette.pal
new file mode 100644
index 0000000..85de45b
--- /dev/null
+++ b/024.simple_text/palette.pal
@@ -0,0 +1,17 @@
+000 000 000 000
+255 255 255 255
+255 000 000 255
+000 255 000 255
+000 000 255 255
+555 555 555 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+255 255 000 255
+
diff --git a/024.simple_text/simple_text.s b/024.simple_text/simple_text.s
new file mode 100644
index 0000000..42d83ac
--- /dev/null
+++ b/024.simple_text/simple_text.s
@@ -0,0 +1,139 @@
+ include "includes.i"
+
+ xdef PokeBitplanePointers
+ xdef copperList
+ xdef copperListAlternate
+ xdef bitplanes1
+ xdef bitplanes2
+ xdef bitplanes3
+ xdef Module
+ xdef copperListBplPtr
+ xdef copperListAlternateBplPtr
+
+byteMap:
+ dc.l Entry
+ dc.l endCode-byteMap
+
+Entry:
+ lea userstack,a7
+ lea CUSTOM,a6
+
+ move #$7ff,DMACON(a6) ; disable all dma
+ move #$7fff,INTENA(a6) ; disable all interrupts
+
+ jsr InstallPalette
+
+ lea Level3InterruptHandler,a3
+ move.l a3,LVL3_INT_VECTOR
+
+ move.w #(INTF_SETCLR|INTF_VERTB|INTF_INTEN),INTENA(a6)
+
+ lea bitplanes1,a0
+ move.l #4,d0 ;color
+ jsr CpuFillColor
+
+
+ jsr DisplayBitplane ; select it
+
+ if 0
+ lea bitplanes2,a0
+ move.l #3,d0 ;color
+ jsr CpuFillColor
+ endif
+
+
+ jsr Init ; enable the playfield
+
+.mainLoop:
+ jsr WaitVerticalBlank
+ jsr WaitVerticalBlank
+ jsr WaitVerticalBlank
+ jsr WaitVerticalBlank
+ jsr WaitVerticalBlank
+
+ ;; blitobject64
+ ;; d0 - xpos
+ ;; d1 - ypos
+ ;; a0 - display
+ ;; a1 - object
+ ;; a2 - mask
+
+ ;; add.l #1,xpos
+ move.l #4,xpos
+ move.l xpos,d0
+ move.l #50,d1
+ lea bitplanes1,a0
+ if 1
+ sub.l #2,a0
+ endif
+ lea font,a1
+ lea fontMask,a2
+ jsr blitObject64
+
+ bra .mainLoop
+
+xpos:
+ dc.l 0
+Level3InterruptHandler:
+ movem.l d0-a6,-(sp)
+ lea CUSTOM,a6
+.checkVerticalBlank:
+ move.w INTREQR(a6),d0
+ and.w #INTF_VERTB,d0
+ beq.s .checkCopper
+
+.verticalBlank:
+ move.w #INTF_VERTB,INTREQ(a6) ; clear interrupt bit
+
+.checkCopper:
+ move.w INTREQR(a6),d0
+ and.w #INTF_COPER,d0
+ beq.s .interruptComplete
+.copperInterrupt:
+ move.w #INTF_COPER,INTREQ(a6) ; clear interrupt bit
+
+.interruptComplete:
+ movem.l (sp)+,d0-a6
+ rte
+
+
+copperList:
+copperListBplPtr:
+ dc.w BPL1PTL,0
+ dc.w BPL1PTH,0
+ dc.w BPL2PTL,0
+ dc.w BPL2PTH,0
+ dc.w BPL3PTL,0
+ dc.w BPL3PTH,0
+ dc.w BPL4PTL,0
+ dc.w BPL4PTH,0
+ dc.w BPL5PTL,0
+ dc.w BPL5PTH,0
+ dc.w BPL6PTL,0
+ dc.w BPL6PTH,0
+ dc.w $106,$c00 ;AGA sprites, palette and dual playfield reset
+ dc.w $1FC,0 ;AGA sprites and burst reset
+ dc.l $fffffffe
+ dc.l $fffffffe
+
+InstallPalette:
+ include "out/font-palette.s"
+ rts
+
+font:
+ incbin "out/font.bin"
+fontMask:
+ incbin "out/font-mask.bin"
+
+ section .bss
+bitplanes1:
+ ds.b IMAGESIZE+(512)
+bitplanes2:
+ ds.b IMAGESIZE+(512*2)
+bitplanes3:
+ ds.b IMAGESIZE+(512*3)
+startUserstack:
+ ds.b $1000 ; size of stack
+userstack:
+
+
diff --git a/024.simple_text/trackloader.s b/024.simple_text/trackloader.s
new file mode 100644
index 0000000..92cf07a
--- /dev/null
+++ b/024.simple_text/trackloader.s
@@ -0,0 +1,203 @@
+*** MFMLoader.S by Photon *** ;requires a6=$dff002
+
+ xdef LoadMFMB
+
+MFMsync equ $4489 ;AmigaDOS standard sync marker.
+MFMlen equ 12980 ;Legacy trackdata read length in bytes
+
+ShortWt:MACRO ;CPU-independent nop;nop replacement
+ tst.w (a6)
+ ENDM
+
+MFMcyl: dc.w 0
+MFMhead: dc.w 0
+MFMdrv: dc.w 0
+MFMchk: dc.l 0
+
+LoadMFMB: ;loadsectors.a0=dst,d0=startsec.W,d1=nrsecs.W(-=Step0)
+ MOVEM.L D0-D7/A0-A6,-(SP)
+ lea $bfd100,a4
+ bsr MotorOn
+ tst.w d1 ;if neg length,then Step0 first
+ bpl.s .NoSt0
+ neg.w d1
+.St0: btst #4,$f01(a4) ;head on cyl 0?
+ beq.s .Rdy0
+ bsr.s StepOut
+ bra.s .St0
+.Rdy0: lea MFMcyl(PC),a1
+ clr.w (a1)
+.NoSt0: and.l #$ffff,d0
+ divu #22,d0 ;startcyl
+ sub.w MFMcyl(PC),d0 ;delta-step
+ beq.s .StRdy
+ bmi.s .StOut
+ subq.w #1,d0
+.StIn: bsr.s StepIn
+ dbf d0,.StIn
+ bra.s .StRdy
+ not.w d0 ;=neg+sub#1
+.StOut: bsr.s StepOut
+ dbf d0,.StIn
+.StRdy: swap d0 ;startsec within cyl
+ cmp.w #11,d0
+ blt.s .Head0
+ sub.w #11,d0
+ bra.s .Head1
+.Head0: bset #2,(a4)
+ lea MFMhead(PC),a1
+ clr.w (a1)
+ bsr LoadTrak ;read track+decode
+ beq.s .End
+.Head1: bclr #2,(a4) ;Head 1
+ lea MFMhead(PC),a1
+ move.w #1,(a1)
+ bsr LoadTrak ;read track+decode
+ beq.s .End
+ bsr.s StepIn ;1 cyl forward
+ bra.s .Head0
+.End: bsr.s MotorOff
+ MOVEM.L (SP)+,D0-D7/A0-A6
+ RTS
+
+StepOut:
+ bset #1,(a4)
+ lea MFMcyl(PC),a1
+ subq.w #1,(a1)
+ ShortWt
+ bclr #0,(a4)
+ ShortWt
+ bset #0,(a4)
+ bsr.s StepWt
+ RTS
+
+StepIn:
+ bclr #1,(a4)
+ lea MFMcyl(PC),a1
+ addq.w #1,(a1)
+ ShortWt
+ bclr #0,(a4)
+ ShortWt
+ bset #0,(a4)
+ bsr.s StepWt
+ RTS
+
+StepWt:
+ moveq #67,d6 ;wait >3 ms
+LeaveLine:
+.loop1: move.b 6-2(a6),d7
+.loop2: cmp.b 6-2(a6),d7
+ beq.s .loop2
+ dbf d6,.loop1
+ RTS
+
+MotorOn:
+ move.w MFMdrv(PC),d7
+ addq.w #3,d7
+ or.b #$78,(a4)
+ bset d7,(a4)
+ ShortWt
+ bclr #7,(a4) ;turns motor on
+ ShortWt
+ bclr d7,(a4)
+ ShortWt
+.DiskR: btst #5,$f01(a4) ;wait until motor running
+ bne.s .DiskR
+ RTS
+
+MotorOff:
+ move.w MFMdrv(PC),d7
+ addq.w #3,d7
+ bset d7,(a4)
+ ShortWt
+ bset #7,(a4)
+ ShortWt
+ bclr d7,(a4)
+ RTS
+
+LoadTrak: ;loadtrack+decode.a0=dst,d0=secoffs,d1=secsleft
+ MOVE.W D0,-(SP)
+ MOVE.W D1,-(SP)
+ lea MFMbuf,a1
+ move.w #2,$9c-2(a6) ;Clr Req
+ move.l a1,$20-2(a6)
+ move.w #$8210,$96-2(a6) ;DskEna
+ move.w #MFMsync,$7e-2(a6)
+ move.w #$9500,$9e-2(a6)
+ move.w #$4000,$24-2(a6)
+ move.w #$8000+MFMlen/2,$24-2(a6);DskLen(12980)+DmaEn
+ move.w #$8000+MFMlen/2,$24-2(a6);start reading MFMdata
+.Wrdy:
+ btst #1,$1f-2(a6) ;wait until data read
+ beq.s .Wrdy
+ move.w d0,d2
+ add.w d1,d2 ;highest sec# (d0=lowest)
+ cmp.w #11,d2
+ ble.s .NoOvr
+ moveq #11,d2
+.NoOvr: sub.w d0,d2 ;nrsecs
+ move.l #$55555555,d3 ;and-const
+ move.w d2,d1
+ subq.w #1,d1 ;loopctr
+.FindS: cmp.w #MFMsync,(a1)+ ;search for a sync word
+ bne.s .FindS
+ cmp.b (a1),d3 ;search for 0-nibble
+ bne.s .FindS
+ move.l (a1)+,d4 ;decode fmtbyte/trk#,sec#,eow#
+ move.l (a1)+,d5
+ and.w d3,d4
+ and.w d3,d5
+ add.w d4,d4
+ or.w d5,d4
+ lsr.w #8,d4 ;sec#
+ sub.w d0,d4 ;do we want this sec?
+ bmi.s .Skip
+ cmp.w d2,d4
+ blt.s .DeCode
+.Skip: lea 48+1024(a1),a1 ;nope
+ bra.s .FindS
+.DeCode:lea 40(a1),a1 ;found a sec,skip unnecessary data
+ move.l a1,d6
+ lea MFMchk(PC),a1
+ clr.l (a1)
+ move.l d6,a1
+ move.l (a1)+,d6 ;decode data chksum.L
+ move.l (a1)+,d5
+ and.l d3,d6
+ and.l d3,d5
+ add.l d6,d6
+ or.l d5,d6 ;chksum
+ lea 512(a1),a2
+ add.w d4,d4 ;x512
+ lsl.w #8,d4
+ lea (a0,d4.w),a3 ;dest addr for this sec
+ moveq #127,d7
+.DClup: move.l (a1)+,d4
+ move.l (a2)+,d5
+ and.l d3,d4
+ and.l d3,d5
+ eor.l d4,d6 ;EOR with checksum
+ eor.l d5,d6 ;EOR with checksum
+ add.l d4,d4
+ or.l d5,d4
+ move.l d4,(a3)+
+ dbf d7,.DClup ;chksum should now be 0 if correct
+ lea MFMchk(PC),a1
+ or.l d6,(a1) ;or with track total chksum
+ move.l a2,a1
+ dbf d1,.FindS ;decode next sec
+ MOVE.W (SP)+,D1
+ MOVE.W (SP)+,D0
+ move.l MFMchk(PC),d3 ;track total chksum OK?
+ bne LoadTrak ;no,retry
+ moveq #0,d0 ;set to start of track
+ move.w d2,d3
+ add.w d3,d3
+ lsl.w #8,d3
+ add.w d3,a0
+ sub.w d2,d1 ;sub #secs loaded
+ RTS
+
+;MFMbuf is placed here after bootblock end, $3c0.w or so when copied.
+MFMbuf:
+ dcb.b MFMlen
diff --git a/024.simple_text/utils.s b/024.simple_text/utils.s
new file mode 100644
index 0000000..e907da9
--- /dev/null
+++ b/024.simple_text/utils.s
@@ -0,0 +1,44 @@
+ xdef WaitVerticalBlank
+ xdef WaitRaster
+ xdef Depack
+
+WaitVerticalBlank:
+ movem.l d0,-(sp)
+.loop: move.l $dff004,d0
+ and.l #$1ff00,d0
+ cmp.l #303<<8,d0 ; wait for the scan line
+ bne.b .loop
+.loop2 move.l $dff004,d0
+ and.l #$1ff00,d0
+ cmp.l #303<<8,d0 ; wait for the scal line to pass (A4000 is fast!)
+ beq.b .loop2
+ movem.l (sp)+,d0
+ rts
+
+
+WaitRaster: ;wait for rasterline d0.w. Modifies d0-d2/a0.
+ movem.l d0-a6,-(sp)
+ move.l #$1ff00,d2
+ lsl.l #8,d0
+ and.l d2,d0
+ lea $dff004,a0
+.wr: move.l (a0),d1
+ and.l d2,d1
+ cmp.l d1,d0
+ bne.s .wr
+ movem.l (sp)+,d0-a6
+ rts
+
+
+Depack:
+ ;a0 = input buffer to be decompressed. Must be 16-bit aligned!
+ ;a1 = output buffer. Points to the end of the data at exit
+ movem.l d0-a6,-(sp)
+ bsr doynaxdepack ; decompress data
+ movem.l (sp)+,d0-a6
+ rts
+
+ ;; this is one FAST decompression routine!
+ include "../tools/external/doynamite68k/depacker_doynax.asm"
+
+