-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathloader.asm
More file actions
982 lines (849 loc) · 32.1 KB
/
loader.asm
File metadata and controls
982 lines (849 loc) · 32.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
#include "params.h"
;
; Based on the loader version modified by Chema and Fabrice used in Blake's 7
; This version has the following changes:
; - Uses the Telestrat align macros to compensate for the Telestrat hardware controller bugs
; - Fixed Jasmin implementation
; - Support for saving files
;
#define OPCODE_RTS $60
#define OPCODE_JMP $4C
; These macros align code to avoid the Telestrat bug
; It seems that maybe older Microdisc units have the
; same bug, so it is safer to keep them on.
; The thing is that the instruction following an access to an
; FDC register must have the lower two bits (0,1) with the same
; value as the register address.
; Access length is 3 bytes (e.g. lda FDC_Status), so alignment of
; accesses must be:
; FDC Register Memory address Alignment of access instruction
; Status/Cmd (00) $0310 xxxxxx01
; Track (01) $0311 xxxxxx10
; Sector (10) $0312 xxxxxx11
; Data (11) $0313 xxxxxx00
; Two versions:
; - PROTECT(X) aligns an access to FDC register X, made following the macros.
; - PROTECT2(X,Y) same but the instruction is Y bytes ahead.
;
#define TELESTRAT_ALIGN
#ifdef TELESTRAT_ALIGN
#define PROTECT(X) .dsb (((X)&3)-((*+3)&3))&3,$ea
#define PROTECT2(X,Y) .dsb (((X)&3)-((*+(Y)+3)&3))&3,$ea
#else
#define PROTECT(X)
#define PROTECT2(X,Y)
#endif
;
; VIA registers definition
;
#define via_portb $0300
#define via_ddrb $0302
#define via_ddra $0303
#define via_t1cl $0304
#define via_t1ch $0305
#define via_t1ll $0306
#define via_t1lh $0307
#define via_t2ll $0308
#define via_t2ch $0309
#define via_sr $030a
#define via_acr $030b
#define via_pcr $030c
#define via_ifr $030d
#define via_ier $030e
#define via_porta $030f
;
; Microdisc FDC register access addresses
;
#define FDC_command_register $0310
#define FDC_status_register $0310
#define FDC_track_register $0311
#define FDC_sector_register $0312
#define FDC_data $0313
; On Microdisc, location $314 contains the following flags on write operations
; bit 7: Eprom select (active low)
; bit 6-5: drive select (0 to 3)
; bit 4: side select
; bit 3: double density enable (0: double density, 1: single density)
; bit 2: along with bit 3, selects the data separator clock divisor (1: double density, 0: single-density)
; bit 1: ROMDIS (active low). When 0, internal Basic rom is disabled.
; bit 0: enable FDC INTRQ to appear on read location $0314 and to drive cpu IRQ
; and $0318 bit 7 contains the state of DRQ
#define FDC_flags $0314
#define FDC_drq $0318
#define FDC_Flags_Mask %10000100 ; Disable ROM/EPROM, no FDC interrupt requests, A drive, Side 0
#define FDC_Flag_DiscSide %00010000 ; Accesses second side of the disk
; COMMAND SUMMARY (models 1791, 1792, 1793, 1794)
;
; Type Command b7 b6 b5 b4 b3 b2 b1 b0
; I Restore 0 0 0 0 h V r1 r0
; I Seek 0 0 0 1 h V r1 r0
; I Step 0 0 1 T h V r1 r0
; I Step-In 0 1 0 T h V r1 r0
; I Step-Out 0 1 1 T h V r1 r0
; II Read Sector 1 0 0 m S E C 0
; II Write Sector 1 0 1 m S E C a0
; III Read Address 1 1 0 0 0 E 0 0
; III Read Track 1 1 1 0 0 E 0 0
; III Write Track 1 1 1 1 0 E 0 0
; IV Force Interrupt 1 1 0 1 i3 i2 i1 i0
; r1 r0 Stepping Motor Rate
; V Track Number Verify Flag (0: no verify, 1: verify on dest track);
; h Head Load Flag (1: load head at beginning, 0: unload head)
; T Track Update Flag (0: no update, 1: update Track Register)
; a0 Data Address Mark (0: FB, 1: F8 (deleted DAM))
; C Side Compare Flag (0: disable side compare, 1: enable side comp)
; E 15 ms delay (0: no 15ms delay, 1: 15 ms delay)
; S Side Compare Flag (0: compare for side 0, 1: compare for side 1)
; m Multiple Record Flag (0: single record, 1: multiple records)
; i3 i2 i1 i0 Interrupt Condition Flags
; i3-i0 = 0 Terminate with no interrupt (INTRQ)
; i3 = 1 Immediate interrupt, requires a reset
; i2 = 1 Index pulse
; i1 = 1 Ready to not ready transition
; i0 = 1 Not ready to ready transition
; r1 r0 Stepping rate
; 0 0 6 ms
; 0 1 12 ms
; 1 0 20 ms
; 1 1 30 ms
#define CMD_ReadSector $80
#define CMD_WriteSector $a0
#define CMD_Seek $1F ; Fabrice uses 1C here (6ms stepping rate), which is faster, but 30ms works with old drives
;
; Jasmin FDC register access addresses
;
#define FDC_JASMIN_command_register $03f4
#define FDC_JASMIN_status_register $03f4
#define FDC_JASMIN_track_register $03f5
#define FDC_JASMIN_sector_register $03f6
#define FDC_JASMIN_data $03f7
; In Jasmin there is no location to read DRQ alone.
; The corresponding bit in the status register should be polled.
; DRQ line is connected to the system IRQ line so it allows for
; interrupt-driven transfers (however, two consecutive bytes are separated by 31.25 micro-seconds)
; - $03F8 bit 0: side select
; - $03F9 disk controller reset (writing any value will reset the FDC)
; - $03FA bit 0: overlay ram access (1 means overlay ram enabled)
; - $03FB bit 0: ROMDIS (1 means internal Basic rom disabled)
; - $03FC, $03FD, $03FE, $03FF : writing to one of these locations will select the corresponding drive
#define FDC_JASMIN_flags $03f8
#define FDC_JASMIN_Flag_DiscSide %00000001
; I am not sure why the Read Sector command was redefined for Jasmin
; with flags S and E active, but C kept inactive:
; C Side Compare Flag (0: disable side compare, 1: enable side comp)
; E 15 ms delay (0: no 15ms delay, 1: 15 ms delay)
; S Side Compare Flag (0: compare for side 0, 1: compare for side 1)
; True that in 1772 FDD from WD these bits are (for a READ SECTOR command) C=0, E and H:
; H Motor On Flag (Bit 3) 1=Enable Spin-up Sequence
; so maybe they are trying to add 15ms of delay and enabling the spin-up sequence, but
; the Jasmin has a 1773, not a 1772.
#define CMD_JASMIN_ReadSector $8c
#include "../build/floppy_description.h" ; This file is generated by the floppy builder
.zero
*=LOADER_BASE_ZERO_PAGE
; If you add or remove any variables, make sure that LOADER_BASE_ZERO_PAGE is still correct (defined in the floppy builder script)
current_track .dsb 1 ; Index of the track being loaded
current_sector .dsb 1 ; Index of the sector being loaded
current_side .dsb 1 ; Has the bit 4 set to 0 or 1 to be used as a mask on the Microdisc control register (other bits have to be set to zero)
ptr_destination .dsb 2 ; Destination address where we depack
ptr_destination_end .dsb 2 ; Point on the end of the depacked stuff
ptr_source_back .dsb 2 ; Temporary used to hold a pointer on depacked stuff
offset .dsb 2 ; Used by the LZ depacker
mask_value .dsb 1 ; Used by the LZ depacker
nb_dst .dsb 1 ; used by the LZ depacker
.text
*=FLOPPY_LOADER_ADDRESS
; ------------------------------------------------------------------------------
; Startup section
; ------------------------------------------------------------------------------
;
; This section of the loader can be overwritten after the loader has been installed in memory.
; It contains initialization code that just need to be run once at the start of the application.
; If there are specific initialization, setup of video mode, etc... you need to do, that's the place.
;
; By default the Microdisc/Jasmin setup code is performed here:
; The code of the loader is setup to load things from a Microdisc, but if we are called with X not null,
; then we patch all the values to replace them by Jasmin equivalents
;
_LoaderTemporaryStart
sei ; Make sure interrupts are disabled
cld ; Force decimal mode
stx _LoaderApiSystemType ; We store the information so it's easy to retrieve later
cpx #0 ; If we are on Jasmin, patch all the FDC related values
beq end_jasmin_init
; Important: Most (all?) Oric emulators actually don't properly emulate the Jasmin.
; You can learn about the following changes in this forum thread https://forum.defence-force.org/viewtopic.php?t=2783
; Among the findings:
; - Jasmin hardware is extremely unreliable and subject to bus noise
; - The ROM/Overlay switch was working in emulation but not on the real hardware
; - The Jasmin FDC requires slower step-rates and delays compared to the Microdisc
lda #<FDC_JASMIN_command_register
sta 1+auto_fdc_command_1
sta 1+auto_fdc_command_2
sta 1+auto_fdc_command_3
sta 1+auto_fdc_command_w
lda #<FDC_JASMIN_status_register
sta 1+auto_fdc_status_1
sta 1+auto_fdc_status_2
sta 1+auto_fdc_status_3
sta 1+auto_fdc_status_4
sta 1+auto_fdc_status_w
lda #<FDC_JASMIN_track_register
sta 1+auto_fdc_track_1
lda #<FDC_JASMIN_sector_register
sta 1+auto_fdc_sector_1
lda #<FDC_JASMIN_data
sta 1+auto_fdc_data_1
sta 1+auto_fdc_data_2
sta 1+auto_fdc_data_w
lda #<FDC_JASMIN_flags
sta 1+auto_fdc_flags_1
sta 1+auto_fdc_flags_2
lda #CMD_JASMIN_ReadSector
sta 1+auto_fdc_readsector
lda #FDC_JASMIN_Flag_DiscSide
sta 1+auto_fdc_discside
sta 1+auto_fdc_discside1
; Jasmin needs longer delays after issuing FDC commands (Chema: values below 7 fail)
lda #17
sta 1+auto_tempo_read
sta 1+auto_tempo_write
; FTDOS uses SEEK command $14 (no head load, verify, 6ms stepping)
; instead of $1F (head load, verify, 30ms stepping)
lda #$14
sta 1+auto_fdc_seek
end_jasmin_init
ldx #$ff ; Reset the stack pointer
txs
lda #$ff ; Initialize the VIA to known values (code is from Atmos ROM)
sta via_ddra
lda #$f7
sta via_ddrb
lda #$b7
sta via_portb
lda #$dd
sta via_pcr
lda #$7f
sta via_ier
lda #$40
sta via_acr
lda #$c0
sta via_ier
lda #$10
sta via_t1ll
sta via_t1cl
lda #$27
sta via_t1lh
sta via_t1ch
jmp _LoaderResidentStart
; -------------------------------------------------------------------------------
; The space between here and _LoaderResidentStart is temporary and will be
; overwritten by module overlay data. This is expected and by design.
; _LoaderResidentStart must be above the highest module overlay address (~$FA90).
; Adjust LOADER_RESIDENT_START if the resident code outgrows the $FFDE limit.
; -------------------------------------------------------------------------------
#if * > FLOPPY_LOADER_RESIDENT_ADDRESS
#error Temporary init section exceeds FLOPPY_LOADER_RESIDENT_ADDRESS
#endif
.dsb FLOPPY_LOADER_RESIDENT_ADDRESS - *
; -------------------------------------------------------------------------------
; Resident section
; -------------------------------------------------------------------------------
;
; This section of the loader stays in memory at all time.
; It contains all the code for loading, saving, as well as memory areas used by the
; API to communicated between the main application and the loader.
;
_LoaderResidentStart
jsr _LoadData ; Load the main game (parameters are directly initialized in the loader variables at the end of the file)
cli ; Enable IRQs again
jsr _LoaderApiJump ; Give control to the application and hope it knows what to do
jmp _LoaderResidentStart
;
; Sets the side,track and sector variables, by using the pair
; track/sector value in the file tables
;
_SetSideTrackSector
lda #FDC_Flags_Mask ; Disable the FDC (Eprom select + FDC Interrupt request)
auto_fdc_flags_1
sta FDC_flags
; Starting track
ldy #%00000000 ; Side 0
lda _LoaderApiFileStartTrack ; If the track id is larger than 128, it means it is on the other side of the floppy
bpl first_side
; The file starts on the second side
auto_fdc_discside1
ldy #FDC_Flag_DiscSide ; Side 1
and #%01111111 ; Mask out the extra bit
first_side
sty current_side
sta current_track
; First sector
lda _LoaderApiFileStartSector
and #%01111111 ; Clear out the top bit (compressed flag)
sta current_sector
rts
;------------------------------------------
; Commands the floppy drive to
; SEEK for the track/sector
; if current_sector is bigger than
; the sectors per track, it automatically
; changes the track too.
;------------------------------------------
_PrepareTrack
lda current_sector ; Check if we have reached the end of the track
cmp #FLOPPY_SECTOR_PER_TRACK+1
bne end_change_track
inc current_track ; Move to the next track
lda current_track
cmp #FLOPPY_TRACK_NUMBER
bne end_side_change
lda #0 ; Reset to the first track on the other side
sta current_track
auto_fdc_discside
lda #FDC_Flag_DiscSide
sta current_side
end_side_change
lda #1 ; Reset the sector position
sta current_sector
end_change_track
lda current_sector ; Update sector to read
PROTECT(FDC_sector_register)
auto_fdc_sector_1
sta FDC_sector_register
inc current_sector
lda current_track ; Check if the drive is on the correct track
; CHEMA: Trying to avoid the Cumulus bug!
; Description: Cumulus firmware version 0.5 has a bug in the WRITE_SECTOR command which issues
; an extra DRQ at the end of the sector. This would not be a problem if it emulated properly
; the behaviour of the BUSY bit in the STATUS register, but it also doesn't (it is not flagged
; at all). As a result a tight loop writting several sectors on the same track may en up here before
; the extra DRQ is flagged. As the Track has not changed, this code is skipped and quickly gets
; back to the writing loop, which catches this extra DRQ, sending the first byte of the sector, which
; is not read by the Cumulus, and gets missed.
; Until a new firmware version is released, the only thing we can do is issuing a SEEK command even when
; we are on the correct track. As this takes time, a var will be used to flag when this will be done: only
; when writing.
ldy avoid_cumulus_bug
bne retryseek
; If we are already on the correct track, don't issue a SEEK command
PROTECT(FDC_track_register)
auto_fdc_track_1
cmp FDC_track_register
beq stay_on_the_track
; Do a SEEK command
retryseek
PROTECT(FDC_data)
auto_fdc_data_1
sta FDC_data ; Set the new track
auto_fdc_seek
lda #CMD_Seek
PROTECT(FDC_command_register)
auto_fdc_command_1
sta FDC_command_register
jsr _WaitCompletion ; Wait for the completion of the command
.(
; Chema: the same 16 cycle wait as in sector_2-microdisc. I am not sure if this
; is needed or why, but it was crucial for the disk to boot in sector_2-microdisc
; so no harm if done here again, I guess..
ldy #3
waitc
dey ;2
bne waitc;2+1
; = 16 cycles
.)
; Added this for reliability
; What if there is a seek error???
; Do the restore_track0 code
PROTECT(FDC_status_register)
auto_fdc_status_3
lda FDC_status_register
and #$18
beq stay_on_the_track
restore_track0
lda #$0c
PROTECT(FDC_command_register)
auto_fdc_command_3
sta FDC_command_register
jsr _WaitCompletion
PROTECT(FDC_status_register)
auto_fdc_status_4
lda FDC_status_register
and #$10
bne restore_track0 ; If error restoring track 0 loop forever
beq retryseek ; Now that track0 is restored, do the seek command again
; We are now on the track
stay_on_the_track
lda #FDC_Flags_Mask ; Apply the side selection mask
ora current_side
auto_fdc_flags_2
sta FDC_flags
rts
;---------------------------------------
; Loads data from a file descriptor
; deals with both compressed and
; uncompressed files
; X=File index
;----------------------------------------
_LoadData
jsr _StartAccess
ldy #0
sty __fetchByte+1
; We have to start somewhere no matter what, compressed or not
jsr _SetSideTrackSector
clc
lda _LoaderApiAddressLow
sta ptr_destination+0
adc _LoaderApiFileSizeLow
sta ptr_destination_end+0
lda _LoaderApiAddressHigh
sta ptr_destination+1
adc _LoaderApiFileSizeHigh
sta ptr_destination_end+1
; Now at this stage we have to check if the data is compressed or not
lda _LoaderApiFileStartSector
bmi LoadCompressedData
;---------------------------------------
; This loads data which is uncompressed
; It will be loaded in the buffer and
; then copied to the destination.
; this is bad and good. Bad because it
; takes time. Good because it alows for
; loading chunks < 256 bytes :)
;---------------------------------------
LoadUncompressedData
ldy #0
read_sectors_loop
jsr GetNextByte ; Read from source stream
sta (ptr_destination),y
; We increase the current destination pointer, by a given value, white checking if we reach the end of the buffer.
inc ptr_destination
bne skip_destination_inc1
inc ptr_destination+1
skip_destination_inc1
lda ptr_destination
cmp ptr_destination_end
lda ptr_destination+1
sbc ptr_destination_end+1
bcc read_sectors_loop
; Data successfully loaded (we hope)
jmp ClearLoadingPic ; CHEMA: Clears the loading picture (jsr/rts)
;--------------------------------------
; This loads data which is compressed
;--------------------------------------
LoadCompressedData
; Initialise variables
; We try to keep "y" null during all the code, so the block copy routine has to be sure that Y is null on exit
lda #1
sta mask_value
unpack_loop
; Handle bit mask
lsr mask_value
bne end_reload_mask
jsr GetNextByte ; Read from source stream
ror
sta mask_value
end_reload_mask
bcc back_copy
write_byte
; Copy one byte from the source stream
jsr GetNextByte ; Read from source stream
sta (ptr_destination),y
lda #1
sta nb_dst
_UnpackEndLoop
; We increase the current destination pointer, by a given value, white checking if we reach the end of the buffer.
clc
lda ptr_destination
adc nb_dst
sta ptr_destination
bcc skip_destination_inc
inc ptr_destination+1
skip_destination_inc
cmp ptr_destination_end
lda ptr_destination+1
sbc ptr_destination_end+1
bcc unpack_loop
ClearLoadingPic
jmp _EndAccess
back_copy
;BreakPoint jmp BreakPoint
; Copy a number of bytes from the already unpacked stream
; Here we know that Y is null. So no need for clearing it: Just be sure it's still null at the end.
; At this point, the source pointer points to a two byte value that actually contains a 4 bits counter, and a 12 bit offset to point back into the depacked stream.
; The counter is in the 4 high order bits.
;clc <== No need, since we access this routine from a BCC
jsr GetNextByte ; Read from source stream
adc #1
sta offset
jsr GetNextByte ; Read from source stream
tax
and #$0f
adc #0
sta offset+1
txa
lsr
lsr
lsr
lsr
clc
adc #3
sta nb_dst
tax ; X = byte count for loop
sec
lda ptr_destination
sbc offset
sta ptr_source_back
lda ptr_destination+1
sbc offset+1
sta ptr_source_back+1
; Beware, in that loop, the direction is important since RLE like depacking is done by recopying the
; very same byte just copied... Do not make it a reverse loop to achieve some speed gain...
.(
copy_loop
lda (ptr_source_back),y ; Read from already unpacked stream
sta (ptr_destination),y ; Write to destination buffer
iny
dex
bne copy_loop
.)
ldy #0
beq _UnpackEndLoop
;-----------------------------------------
; Gets the next byte from the stream,
; reading data from disk to the buffer
; if necessary.
;-----------------------------------------
GetNextByte
php
lda __fetchByte+1
bne __fetchByte
jsr _ReadNextSector
ldx #0
ldy #0
__fetchByte
lda LOADER_SECTOR_BUFFER
inc __fetchByte+1
plp
rts
;--------------------------------------------
; Reads the next sector of the current file
;---------------------------------------------
_ReadNextSector
; CHEMA: this is the critical section. Disable IRQs here
sei
jsr _PrepareTrack
RetryRead
auto_fdc_readsector
lda #CMD_ReadSector
PROTECT(FDC_command_register)
auto_fdc_command_2
sta FDC_command_register
; Chema: this loop is needed if checking for partial
; loading of a sector, as we cannot check the STATUS
; directly after issuing a command.
; Fabrice provided this table and the code, which takes 21 cycles+extra (ldx and lda below) :
; Operation Next Operation Delay required (MFM mode)
; Write to Command Reg. Read Busy Bit (bit 0) 24 µsec
; Write to Command Reg. Read Status bits 1-7 32 µsec
; Write Register Read Same Register 16 µsec
auto_tempo_read
ldy #4
tempoloop
dey
bne tempoloop
; Read the sector data
; This is the code suggested by Fabrice, which
; makes use of the STATUS bit to check when the command
; finishes, so not only the status flags are correct after
; computing CRC, but also if it is aborted due to a read error.
; Additionaly, to support the Jasmin, DRQ at FDC_drq cannot be polled
; as this signal is not exhibited in any address. One should use bit 1
; of the Status register. BTW, Fabrice provided a code that, after aligning
; the first access to the status register, does not need more nops for the data register
; so timing for the read loop is correct. In his own words:
; Worst case delay :
; lda status (2), lsr (2), ror (2), bcc (3), bpl (2), lda status (4), lsr (2), ror (2), bcc (2), lda data (4)
; => 25 cycles, that's not bad !
; (yeap, I tweaked the code for proper alignment :-)
ldx #0
beq waitdrq ;<--- always jumps
PROTECT2(FDC_status_register,2)
checkbusy
bpl end_of_command
waitdrq
auto_fdc_status_1
lda FDC_status_register
lsr
ror
bcc checkbusy
PROTECT(FDC_data)
auto_fdc_data_2
lda FDC_data
sta LOADER_SECTOR_BUFFER,x ; Store the byte in the sector buffer
inx
jmp waitdrq
end_of_command
and #($7c>>2) ; Chema changed the original vaue: 1C
; If error repeat forever:
bne RetryRead
; Finished!
cli
rts
;---------------------------------------------
; Waits for the completion of a command
; As we have not set the FDC IRQ we cannot simply
; poll bit 7 in $318, we have to check the busy
; bit on the status register.
; According to the datasheet we have to wait 24us
; before reading the status after a write to a command register.
; The loop takes a bit longer: 26 us (see sector_2-microdisc)
;------------------------------------------------
_WaitCompletion
txa
pha
ldx #5
r_wait_completion
dex
bne r_wait_completion
PROTECT(FDC_status_register)
r2_wait_completion
auto_fdc_status_2
lda FDC_status_register
lsr
bcs r2_wait_completion ; If s0 (busy) is not zero, wait.
pla
tax
rts
;-----------------------------
; Default ISRs for vectors.
;-----------------------------
_IrqHandler
bit $304
jsr _RefreshAccessIndicator ; So we get the access indicator between modules
IrqDoNothing
rti
;-------------------------------------
; Write data. Uncompressed and
; directly to disk (no buffering)
; so deals with 256-byte chunks only
;-------------------------------------
; CHEMA: I am re-using the var ptr_destination, though it
; is now the ptr to the buffer to write.
_WriteData
;jmp _WriteData
; Flag we want to start avoiding the cumulus bug in the write sector command
inc avoid_cumulus_bug
jsr _SetSideTrackSector
lda _LoaderApiAddressLow
sta ptr_destination+0
lda _LoaderApiAddressHigh
sta ptr_destination+1
loopwrite
; Begin of critical section
sei
jsr _PrepareTrack
retrywrite
; Writes a sector to disk.
auto_fdc_writesector
lda #CMD_WriteSector
PROTECT(FDC_command_register)
auto_fdc_command_w
sta FDC_command_register
; According to the datasheet table of needed delays:
; Write to Command Reg. Read Status bits 1-7 32 µsec
; The next loop is 23, plus some extra due to the beq / lda /tax /jmp (3+5+2+3=13)
; Total is 36... could do with 4 iterations... perhaps
auto_tempo_write
ldy #5 ;2
w_wait_completion
dey ;2
bne w_wait_completion ;2+1
; = 23 cycles
; This is the correct loop, which keeps track of all kind
; of errors, as it is done when reading data.
; It is necessary to feed data at the disk
; rate, which is (in double density) 250 Kbps, that is
; 1 byte ~every 30 usec.
;ldy #0
beq next_byte
PROTECT2(FDC_status_register,2)
check_busy2
bpl end_of_commandw
waitdrqw
auto_fdc_status_w
lda FDC_status_register
; Check DRQ & BUSY
lsr
ror
bcc check_busy2
PROTECT(FDC_data)
auto_fdc_data_w
stx FDC_data
iny
next_byte
lda (ptr_destination),y
tax
jmp waitdrqw
end_of_commandw
and #($3c>>2)
bne retrywrite ; If error repeat forever:
cli
; Now onto next sector
inc ptr_destination+1
dec _LoaderApiFileSizeHigh
bne loopwrite
; Flag we want to stop avoiding the cumulus bug in the write sector command
dec avoid_cumulus_bug
; Clear loading pic and return
jmp ClearLoadingPic ; This is jsr/rts
;****************** CHEMA added these variables ****************
; I ran out of space in ZP for the var needed to avoid
; the Cumulus Bug, so I'll define it here. Besides,
; this way I can give it a default value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
avoid_cumulus_bug .byt 0
; Blinking light indicator in the bottom right of the screen
_StartAccess
php
sei
lda $bfdf ; Save the current loading indicator byte value
sta auto_restore_indicator
lda #OPCODE_NOP ; Enable the IRQ handler
sta auto_nop_or_rts
plp
rts
_EndAccess
php
sei
lda #OPCODE_RTS ; Disable the IRQ handler
sta auto_nop_or_rts
auto_restore_indicator=*+1
lda #1 ; Restore the loading indicator byte value
sta $bfdf
plp
rts
_RefreshAccessIndicator
auto_nop_or_rts
nop ; NOP or RTS depending
pha
lda $BFDF
clc
adc #16+1
and #16+7
sta $BFDF
pla
rts
// About 520 bytes for 130 files, exposes the following tables:
// - FileStartSector - 130 bytes
// - FileStartTrack - 130 bytes
// - FileSizeLow - 130 bytes
// - FileSizeHigh - 130 bytes
#define ASSEMBLER
#define LOADER ; We request the actual table data to be included in the file
_FloppyBuilderFileData
#include "../build/floppy_description.h" ; This file is generated by the floppy builder
; Assumes that _LoaderApiEntryIndex contains a valid value (temp)
; As well as _LoaderApiAddress
; It fills the rest
_InitializeFileFromDirectory
ldx _LoaderApiEntryIndex
lda FileStartSector,x
sta _LoaderApiFileStartSector
lda FileStartTrack,x
sta _LoaderApiFileStartTrack
lda FileSizeLow,x
sta _LoaderApiFileSizeLow
lda FileSizeHigh,x
sta _LoaderApiFileSizeHigh
rts
_LoadFileFromDirectory
jsr _InitializeFileFromDirectory
jmp _LoadData
; CHEMA: Support saving
; Assumes that _LoaderApiEntryIndex contains a valid value (temp)
; As well as _LoaderApiAddress
; It fills the rest
_SaveFileFromDirectory
jsr _InitializeFileFromDirectory
jmp _WriteData
_EndLoaderCode
;
; This is free memory that can be used, when it reaches zero then the loader start address should be changed
;
.dsb $FFDE - _EndLoaderCode
_Vectors
#if ( _Vectors <> $FFDE )
#error - Vector address is incorrect, loader will crash
#else
;
; Here are the functions that the user can call from his own application
;
_LoaderApiInitializeFileFromDirectory .byt OPCODE_JMP,<_InitializeFileFromDirectory,>_InitializeFileFromDirectory ; $FFDE-$FFE0
_LoaderApiLoadFileFromDirectory .byt OPCODE_JMP,<_LoadFileFromDirectory,>_LoadFileFromDirectory ; $FFE1-$FFE3
_LoaderApiSaveFileFromDirectory .byt OPCODE_JMP,<_SaveFileFromDirectory,>_SaveFileFromDirectory ; $FFE4-$FFE6
_LoaderApiEntryIndex .byt 0 ; $FFE7 - ID of the file to load
_LoaderApiSystemType .byt 0 ; $FFE8 - 0=Microdisc, 1=Jasmin
; Chema: WriteSupport
_LoaderApiLoadingAnimation .byt OPCODE_JMP,<_RefreshAccessIndicator,>_RefreshAccessIndicator ; $FFE9-$FFEB
_LoaderApiSaveData .byt OPCODE_JMP,<_WriteData,>_WriteData ; $FFEC-$FFEE
#ifdef ENABLE_SPLASH
_LoaderApiFileStartSector .byt LOADER_SPLASH_PROGRAM_SECTOR ; $FFEF
_LoaderApiFileStartTrack .byt LOADER_SPLASH_PROGRAM_TRACK ; $FFF0
_LoaderApiFileSize
_LoaderApiFileSizeLow .byt <LOADER_SPLASH_PROGRAM_SIZE ; $FFF1
_LoaderApiFileSizeHigh .byt >LOADER_SPLASH_PROGRAM_SIZE ; $FFF2
; Could have a JMP here as well to launch the loaded program
_LoaderApiJump .byt OPCODE_JMP ; $FFF3
_LoaderApiAddress
_LoaderApiAddressLow .byt <LOADER_SPLASH_PROGRAM_ADDRESS ; $FFF4
_LoaderApiAddressHigh .byt >LOADER_SPLASH_PROGRAM_ADDRESS ; $FFF5
_LoaderXxxxxx_available .byt 0 ; $FFF6
_LoaderApiLoadFile .byt OPCODE_JMP,<_LoadData,>_LoadData ; $FFF7-$FFF9
#else
#ifdef ENABLE_INTRO
_LoaderApiFileStartSector .byt LOADER_INTRO_PROGRAM_SECTOR ; $FFEF
_LoaderApiFileStartTrack .byt LOADER_INTRO_PROGRAM_TRACK ; $FFF0
_LoaderApiFileSize
_LoaderApiFileSizeLow .byt <LOADER_INTRO_PROGRAM_SIZE ; $FFF1
_LoaderApiFileSizeHigh .byt >LOADER_INTRO_PROGRAM_SIZE ; $FFF2
; Could have a JMP here as well to launch the loaded program
_LoaderApiJump .byt OPCODE_JMP ; $FFF3
_LoaderApiAddress
_LoaderApiAddressLow .byt <LOADER_INTRO_PROGRAM_ADDRESS ; $FFF4
_LoaderApiAddressHigh .byt >LOADER_INTRO_PROGRAM_ADDRESS ; $FFF5
_LoaderXxxxxx_available .byt 0 ; $FFF6
_LoaderApiLoadFile .byt OPCODE_JMP,<_LoadData,>_LoadData ; $FFF7-$FFF9
#else // Directly load the game
_LoaderApiFileStartSector .byt LOADER_GAME_PROGRAM_SECTOR ; $FFEF
_LoaderApiFileStartTrack .byt LOADER_GAME_PROGRAM_TRACK ; $FFF0
_LoaderApiFileSize
_LoaderApiFileSizeLow .byt <LOADER_GAME_PROGRAM_SIZE ; $FFF1
_LoaderApiFileSizeHigh .byt >LOADER_GAME_PROGRAM_SIZE ; $FFF2
; Could have a JMP here as well to launch the loaded program
_LoaderApiJump .byt OPCODE_JMP ; $FFF3
_LoaderApiAddress
_LoaderApiAddressLow .byt <LOADER_GAME_PROGRAM_ADDRESS ; $FFF4
_LoaderApiAddressHigh .byt >LOADER_GAME_PROGRAM_ADDRESS ; $FFF5
_LoaderXxxxxx_available .byt 0 ; $FFF6
_LoaderApiLoadFile .byt OPCODE_JMP,<_LoadData,>_LoadData ; $FFF7-$FFF9
#endif
#endif
;
; These three HAVE to be at these precise adresses, they map to hardware registers
;
#if ( * <> $FFFA )
#error - Vector address is incorrect, loader will crash
#else
_VectorNMI .word IrqDoNothing ; $FFFA-$FFFB - NMI Vector (Usually points to $0247)
_VectorReset .word IrqDoNothing ; $FFFC-$FFFD - RESET Vector (Usually points to $F88F)
_VectorIRQ .word _IrqHandler ; $FFFE-$FFFF - IRQ Vector (Normally points to $0244)
#if DISPLAYINFO=1
#echo Remaining space in the loader code:
#print (_Vectors - _EndLoaderCode)
#if ( (_Vectors - _EndLoaderCode) < 0 )
#error - Loader ran out of memory, loader will crash
#endif
#endif
#endif
; End of the loader - Nothing should come after because it's out of the addressable memory range :)