├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── apireport.py
├── counterlogic.asm
├── delay.asm
├── fetchdirectptr.asm
├── font.asm
├── freedisksys.asm
├── gamepads.asm
├── gethardcodedpointers.asm
├── getvrambufferbyte.asm
├── irq.asm
├── loadtileset.asm
├── nametable.asm
├── nmi.asm
├── ppumask.asm
├── random.asm
├── reset.asm
├── vrambuffers.asm
├── vramfill.asm
├── vramstrings.asm
└── vramstructwrite.asm
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=auto
3 |
4 | # Explicitly declare text files you want to always be normalized and converted
5 | # to native line endings on checkout.
6 | *.asm text
7 | *.h text
8 |
9 | # Declare files that will always have CRLF line endings on checkout.
10 |
11 | # Denote all files that are truly binary and should not be modified.
12 | *.png binary
13 | *.jpg binary
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | *.bin
3 | *.lst
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FreeDiskSysROM
2 |
3 | This project has one goal - a compatible re-implementation of the Famicom Disk System BIOS under an OSS license. Unlike the Famicom console itself, which has no program ROM built-in, the Famicom Disk System includes an 8 KiB PRG-ROM containing disk I/O routines, VRAM transfer routines, joypad reading code, an animation featuring Mario and Luigi when no disk is present in the drive, and more. The code, data, graphics, music, and animation contained in the original BIOS are copyrighted by Nintendo.
4 |
5 | FreeDiskSysROM aims to provide a replacement for the original FDS BIOS that can be freely redistributed and that is capable of running all published FDS software.
6 |
7 | # Audience
8 |
9 | ## Emulators
10 |
11 | Famicom and NES emulators historically require a dump of the Famicom Disk System BIOS to be able to emulate FDS titles. Emulators can ship FreeDiskSysROM with their installers instead of requiring end-users to either copy the ROM out of their own FDS hardware or breaking copyright law by downloading a BIOS rip from elsewhere on the Internet.
12 |
13 | ## Clone hardware
14 |
15 | Modern hardware clones of the FDS RAM Adapter or FPGA re-implementations of the entire Famicom Disk System also need a BIOS.
16 |
17 | # Status
18 |
19 | ## APIs
20 |
21 | | Address | Name | # of Games | Implemented |
22 | | ------- | ---- | ------- | ----------- |
23 | | $e149 | Delay131 | | :white_check_mark: |
24 | | $e153 | Delayms | | :white_check_mark: |
25 | | $e161 | DisPFObj | | :white_check_mark: |
26 | | $e16b | EnPFObj | | :white_check_mark: |
27 | | $e171 | DisObj | | :white_check_mark: |
28 | | $e178 | EnObj | | :white_check_mark: |
29 | | $e17e | DisPF | | :white_check_mark: |
30 | | $e185 | EnPF | | :white_check_mark: |
31 | | $e18b | NMI | | :white_check_mark: |
32 | | $e1b2 | VINTWait | | :white_check_mark: |
33 | | $e1c7 | IRQ | | :white_check_mark: |
34 | | $e1f8 | LoadFiles | | |
35 | | $e237 | AppendFile | | |
36 | | $e239 | WriteFile | | |
37 | | $e2b7 | CheckFileCount | | |
38 | | $e2bb | AdjustFileCount | | |
39 | | $e301 | SetFileCount1 | | |
40 | | $e305 | SetFileCount | | |
41 | | $e32a | GetDiskInfo | | |
42 | | $e3da | AddYtoPtr0A | | |
43 | | $e3e7 | GetHardCodedPointers | | |
44 | | $e3ea | GetHardCodedPointersWriteProtected | | |
45 | | $e445 | CheckDiskHeader | | |
46 | | $e484 | GetNumFiles | | |
47 | | $e492 | SetNumFiles | | |
48 | | $e4a0 | FileMatchTest | 0 | |
49 | | $e4da | SkipFiles | 0 | |
50 | | $e4f9 | LoadData | | |
51 | | $e506 | ReadData | | |
52 | | $e5b5 | SaveData | | |
53 | | $e64d | WaitForDriveReady | | |
54 | | $e685 | StopMotor | | |
55 | | $e68f | CheckBlockType | | |
56 | | $e6b0 | WriteBlockType | | |
57 | | $e6e3 | StartXfer | | |
58 | | $e706 | EndOfBlockRead | | |
59 | | $e729 | EndOfBlkWrite | | |
60 | | $e778 | XferDone | | |
61 | | $e794 | Xfer1stByte | | |
62 | | $e7a3 | XferByte | | |
63 | | $e7bb | VRAMStructWrite | | :white_check_mark: |
64 | | $e844 | FetchDirectPtr | | :white_check_mark: |
65 | | $e86a | WriteVRAMBuffers | | :white_check_mark: |
66 | | $e8b3 | ReadIndividualVRAMBytes | | :white_check_mark: |
67 | | $e8d2 | PrepareVRAMString | | :white_check_mark: |
68 | | $e8e1 | PrepareVRAMStrings | | :white_check_mark: |
69 | | $e94f | GetVRAMBufferByte | | :white_check_mark: |
70 | | $e97d | Pixel2NamConv | | :white_check_mark: |
71 | | $e997 | Nam2PixelConv | | :white_check_mark: |
72 | | $e9b1 | Random | | :white_check_mark: |
73 | | $e9c8 | SpriteDMA | | :white_check_mark: |
74 | | $e9d3 | CounterLogic | | :white_check_mark: |
75 | | $e9eb | ReadPads | | :white_check_mark: |
76 | | $ea1a | ReadDownPads | | :white_check_mark: |
77 | | $ea1f | ReadOrDownPads | | :white_check_mark: |
78 | | $ea36 | ReadDownVerifyPads | | :white_check_mark: |
79 | | $ea4c | ReadOrDownVerifyPads | | :white_check_mark: |
80 | | $ea68 | ReadDownExpPads | | :white_check_mark: |
81 | | $ea84 | VRAMFill | | :white_check_mark: |
82 | | $ead2 | MemFill | | :white_check_mark: |
83 | | $eaea | SetScroll | | :white_check_mark: |
84 | | $eafd | JumpEngine | | :white_check_mark: |
85 | | $eb13 | ReadKeyboard | 0 | |
86 | | $ebaf | LoadTileset | | :white_check_mark: |
87 | | $ec22 | unk_EC22 | | |
88 | | $ee17 | StartMotor | | |
89 |
90 | ## Initialization
91 |
92 | # Building
93 |
94 | To build, use [asm6f](https://github.com/freem/asm6f).
95 |
96 | ```asm6f freedisksys.asm```
97 |
98 | The `-l` flag is very useful for development - it shows the addresses assigned
99 | to each instruction, so you can easily see how much room remains for a given
100 | subroutine.
101 |
102 | # Contributing
103 |
104 | Rules:
105 |
106 | 1. Do not even look at a disassembly of the original FDS BIOS.
107 | 1. Only contribute code to which you hold the copyright or which is already under a compatible license.
108 |
109 | # License
110 |
111 | FreeDiskSysROM is licensed under the GNU LGPL v3. The intent in using this license is to allow anyone to replace the 8 KiB official FDS BIOS with FreeDiskSysROM, whether for commerical or non-commercial purposes, so long as the source of FreeDiskSysROM (including any modifications) is made available to the end-user under the same license.
112 |
113 | Although the Famicom does not have an OS or any concept of dynamic linking, the FDS BIOS is analogous to a system library in practice. FDS titles, FDS emulators, and FDS clone systems are all permitted to utilize FreeDiskSysROM without regard to or changes to the licenses of their own code.
114 |
--------------------------------------------------------------------------------
/apireport.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Scan the current working directory for *.fds files, and count calls
3 | # into the FDS BIOS from PRG code.
4 |
5 | import glob
6 | import os
7 | import re
8 | import stat
9 | import string
10 | import struct
11 |
12 | apis = {
13 | 0xe149: ['Delay132', 0],
14 | 0xe153: ['Delayms', 0],
15 | 0xe161: ['DisPFObj', 0],
16 | 0xe16b: ['EnPFObj', 0],
17 | 0xe171: ['DisObj', 0],
18 | 0xe178: ['EnObj', 0],
19 | 0xe17e: ['DisPF', 0],
20 | 0xe185: ['EnPF', 0],
21 | 0xe1b2: ['VINTWait', 0],
22 | 0xe1f8: ['LoadFiles', 0],
23 | 0xe237: ['AppendFile', 0],
24 | 0xe239: ['WriteFile', 0],
25 | 0xe2b7: ['CheckFileCount', 0],
26 | 0xe2bb: ['AdjustFileCount', 0],
27 | 0xe301: ['SetFileCount1', 0],
28 | 0xe305: ['SetFileCount', 0],
29 | 0xe32a: ['GetDiskInfo', 0],
30 | 0xe3da: ['AddYtoPtr0A', 0],
31 | 0xe3e7: ['GetHardCodedPointers', 0],
32 | 0xe3ea: ['GetHardCodedPointersWriteProtected', 0],
33 | 0xe445: ['CheckDiskHeader', 0],
34 | 0xe484: ['GetNumFiles', 0],
35 | 0xe492: ['SetNumFiles', 0],
36 | 0xe4a0: ['FileMatchTest', 0],
37 | 0xe4da: ['SkipFiles', 0],
38 | 0xe4f9: ['LoadData', 0],
39 | 0xe506: ['ReadData', 0],
40 | 0xe5b5: ['SaveData', 0],
41 | 0xe64d: ['WaitForDriveReady', 0],
42 | 0xe685: ['StopMotor', 0],
43 | 0xe68f: ['CheckBlockType', 0],
44 | 0xe6b0: ['WriteBlockType', 0],
45 | 0xe6e3: ['StartXfer', 0],
46 | 0xe706: ['EndOfBlockRead', 0],
47 | 0xe729: ['EndOfBlkWrite', 0],
48 | 0xe778: ['XferDone', 0],
49 | 0xe794: ['Xfer1stByte', 0],
50 | 0xe7a3: ['XferByte', 0],
51 | 0xe7bb: ['VRAMStructWrite', 0],
52 | 0xe844: ['FetchDirectPtr', 0],
53 | 0xe86a: ['WriteVRAMBuffer', 0],
54 | 0xe8b3: ['ReadVRAMBuffer', 0],
55 | 0xe8d2: ['PrepareVRAMString', 0],
56 | 0xe8e1: ['PrepareVRAMStrings', 0],
57 | 0xe94f: ['GetVRAMBufferByte', 0],
58 | 0xe97d: ['Pixel2NamConv', 0],
59 | 0xe997: ['Nam2PixelConv', 0],
60 | 0xe9b1: ['Random', 0],
61 | 0xe9c8: ['SpriteDMA', 0],
62 | 0xe9d3: ['CounterLogic', 0],
63 | 0xe9eb: ['ReadPads', 0],
64 | 0xea1a: ['ReadDownPads', 0],
65 | 0xea1f: ['ReadOrDownPads', 0],
66 | 0xea36: ['ReadDownVerifyPads', 0],
67 | 0xea4c: ['ReadOrDownVerifyPads', 0],
68 | 0xea68: ['ReadDownExpPads', 0],
69 | 0xea84: ['VRAMFill', 0],
70 | 0xead2: ['MemFill', 0],
71 | 0xeaea: ['SetScroll', 0],
72 | 0xeafd: ['JumpEngine', 0],
73 | 0xeb13: ['ReadKeyboard', 0],
74 | 0xebaf: ['LoadTileset', 0],
75 | 0xec22: ['unk_EC22', 0],
76 | 0xee17: ['StartMotor', 0],
77 | }
78 |
79 | unknown_apis = {}
80 |
81 | opcodeUnofficialTable = [
82 | False, False, True, True, True, False, False, True,
83 | False, False, False, True, True, False, False, True,
84 |
85 | False, False, True, True, True, False, False, True,
86 | False, False, True, True, True, False, False, True,
87 |
88 | False, False, True, True, False, False, False, True,
89 | False, False, False, True, False, False, False, True,
90 |
91 | False, False, True, True, True, False, False, True,
92 | False, False, True, True, True, False, False, True,
93 |
94 | False, False, True, True, True, False, False, True,
95 | False, False, False, True, False, False, False, True,
96 |
97 | False, False, True, True, True, False, False, True,
98 | False, False, True, True, True, False, False, True,
99 |
100 | False, False, True, True, True, False, False, True,
101 | False, False, False, True, False, False, False, True,
102 |
103 | False, False, True, True, True, False, False, True,
104 | False, False, True, True, True, False, False, True,
105 |
106 | True, False, True, True, False, False, False, True,
107 | False, True, False, True, False, False, False, True,
108 |
109 | False, False, True, True, False, False, False, True,
110 | False, False, False, True, True, False, True, True,
111 |
112 | False, False, False, True, False, False, False, True,
113 | False, False, False, True, False, False, False, True,
114 |
115 | False, False, True, True, False, False, False, True,
116 | False, False, False, True, False, False, False, True,
117 |
118 | False, False, True, True, False, False, False, True,
119 | False, False, False, True, False, False, False, True,
120 |
121 | False, False, True, True, True, False, False, True,
122 | False, False, True, True, True, False, False, True,
123 |
124 | False, False, True, True, False, False, False, True,
125 | False, False, False, True, False, False, False, True,
126 |
127 | False, False, True, True, True, False, False, True,
128 | False, False, True, True, True, False, False, True,
129 | ]
130 |
131 | roms = glob.glob("*.fds")
132 |
133 | for r in roms:
134 | info = os.stat(r)
135 | disks = []
136 | counts = {}
137 |
138 | with open(r, 'rb') as f:
139 | print(r + ':')
140 |
141 | if info.st_size % 65500 == 16:
142 | # skip optional FDS header
143 | f.seek(16)
144 |
145 | for side in range(info.st_size // 65500):
146 | disks.append(f.read(65500))
147 |
148 | try:
149 | for disk in disks:
150 | # skip blocks 1 and 2
151 | if disk[0] != 1:
152 | raise Exception('Expected block 1, got {}'.format(disk[0]))
153 |
154 | if disk[56] != 2:
155 | raise Exception('Expected block 2, got {}'.format(disk[56]))
156 |
157 | num_files = disk[57]
158 | current_file = 0
159 |
160 | pos = 58
161 | while pos < 65500:
162 | # Now, read blocks 3 and 4 until the end of the disk
163 | header = disk[pos:pos+16]
164 | pos += 16
165 |
166 | # all zeroes at the end of the disk
167 | if header[0] == 0:
168 | break
169 |
170 | header_fields = struct.unpack(' num_files:
176 | # Disk side should end with all zeroes, but this one doesn't. Move on.
177 | break
178 | else:
179 | raise Exception('Expected a block 3, got {} before file {} of {}'.format(header_fields[0], current_file, num_files))
180 |
181 | filesize = header_fields[5]
182 | filetype = header_fields[6]
183 |
184 | if disk[pos] != 4:
185 | raise Exception('Expected a block 4, got {} before file {} of {}'.format(disk[pos], current_file, num_files))
186 |
187 | pos += 1
188 |
189 | contents = disk[pos:pos+filesize]
190 | pos += filesize
191 |
192 | if filetype != 0:
193 | continue
194 |
195 | # $20 == JSR, then low byte and high byte of absolute address, then following opcode
196 | matches = re.findall(b'\x20(.)([\xe0-\xff])(.)', contents)
197 |
198 | for m in matches:
199 | pieces = struct.unpack('.
15 |
16 | ; Decrement several counters in Zeropage. The first counter is a decimal
17 | ; counter 9 -> 8 -> 7 -> ... -> 1 -> 0 -> 9 -> ... Counters 1...A are simply
18 | ; decremented and stays at 0. Counters A+1...Y are decremented when the first
19 | ; counter does a 0 -> 9 transition, and stays at 0.
20 | ; Parameters: A, Y = end Zeropage address of counters, X = start zeropage address of counters
21 | ; Affects: A, X, $00
22 | API_ENTRYPOINT $e9d3
23 | CounterLogic:
24 | STX $00 ; 2 bytes
25 | DEC 0,X ; 2 bytes
26 | ; if negative, set the first counter to 9, otherwise continue
27 | BPL @countersToA ; 2 bytes
28 | LDA #$09 ; 2 bytes
29 | STA 0,X ; 2 bytes
30 | ; Do the A+1...Y counters now, since we know that the first counter rolled over
31 | @countersAplus1toY:
32 | ; there's no zp,y addressing mode, only zp,x so copy Y to X through A
33 | TYA ; 1 byte
34 | @countersToA:
35 | TAX ; 1 byte
36 | @loop:
37 | LDA 0,X ; 2 bytes
38 | ; skip counters that are already 0
39 | BEQ @looptest ; 2 bytes
40 | DEC 0,X ; 2 bytes
41 | @looptest:
42 | DEX ; 1 byte
43 | CPX $00 ; 2 bytes
44 | BNE @loop ; 2 bytes
45 | RTS ; 1 byte
46 |
--------------------------------------------------------------------------------
/delay.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Fritters away 131 cycles. (NesDev Wiki says 132 cycles, but there's no way
17 | ; to waste that amount in just 10 bytes of instructions without clobbering
18 | ; something.)
19 | API_ENTRYPOINT $e149
20 | Delay131:
21 | CLC ; 1 byte, 2 cycles
22 | PHA ; 1 byte, 3 cycles
23 | LDA #$E9 ; 2 bytes, 2 cycles
24 | ; now waste 114 cycles
25 | @loop:
26 | ADC #1 ; 2 bytes, 2 cycles
27 | BCC @loop ; 2 bytes, 3 cycles except for last time when it's 2
28 | PLA ; 1 byte, 4 cycles
29 | RTS ; 1 byte, 6 cycles
30 |
31 | ; Delays roughly Y ms, affects X, Y
32 | ; If y == 0, then delay 256 ms
33 | API_ENTRYPOINT $e153
34 | Delayms:
35 | ; Every cycle is 1/1789.7725 ms. Each iteration of the outer loop spins 1790 cycles to delay 1 ms
36 | LDX #255 ; 2 bytes, 2 cycles
37 | ; first inner loop burns 1274 cycles
38 | @inner1:
39 | DEX ; 1 byte, 2 cycles
40 | BNE @inner1 ; 2 bytes, 3 cycles except for the last time when it's 2
41 | LDX #102 ; 2 bytes, 2 cycles
42 | ; second inner loop burns 509 cycles (ideal would be 507)
43 | @inner2:
44 | DEX ; 1 byte, 2 cycles
45 | BNE @inner2 ; 2 bytes, 3 cycles except for the last time when it's 2
46 | DEY ; 1 byte, 2 cycles
47 | BNE Delayms ; 2 bytes, 3 cycles except for the last time when it's 2
48 | RTS ; 1 byte, 6 cycles
49 |
--------------------------------------------------------------------------------
/fetchdirectptr.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Fetch a direct pointer located at the apparent return address of the routine
17 | ; that calls this one, save the pointer at ($00), and fix the return address.
18 | ; Assumes nothing more was pushed onto the stack by the caller.
19 | ; Affects: A, X, Y, $05, $06
20 | ; Returns: $00, $01 = pointer fetched
21 | API_ENTRYPOINT $e844
22 | FetchDirectPtr:
23 | ; The stack pointer is currently 1 below the return address of the caller.
24 | ; We need the return address of the caller's caller, three bytes later.
25 | TSX
26 | LDA $103,X
27 | STA $05
28 | LDY $104,X
29 | STY $06
30 | CLC
31 | ADC #2 ; fix return address of caller's caller (it's the fetched pointer + 2)
32 | BCC @storeFixedPointer
33 | INY ; add the carry
34 | @storeFixedPointer:
35 | STA $103,X ; write low byte of fixed return address to stack
36 | TYA ; no abs,x addressing mode for STY, so transfer Y to A
37 | STA $104,X ; write high byte of fixed return address to stack
38 | LDY #1 ; the return address is always 1 less than the real location
39 | LDA ($05),Y ; get low byte of direct pointer
40 | STA $00 ; store low byte of direct pointer
41 | INY
42 | LDA ($05),Y ; get high byte of direct pointer
43 | STA $01 ; store high byte of direct pointer
44 | RTS
45 |
--------------------------------------------------------------------------------
/font.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2020 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; The actual binary font data in this file is not copyrighted, as no bitmap
17 | ; font is copyrightable.
18 |
19 | ; The characters in the FDS font are 1 bpp, not 2 bpp as you might expect.
20 | ; Each byte of the character is copied twice to fill out the two bitplanes in
21 | ; CHR-RAM.
22 |
23 | ; If you kinda squint, you can see the characters in the 1s and 0s below, since
24 | ; it's in binary. The hex representation of this data is not useful.
25 |
26 | ; 0
27 | DB 00111000b
28 | DB 01001100b
29 | DB 11000110b
30 | DB 11000110b
31 | DB 11000110b
32 | DB 01100100b
33 | DB 00111000b
34 | DB 00000000b
35 |
36 | ; 1
37 | DB 00011000b
38 | DB 00111000b
39 | DB 00011000b
40 | DB 00011000b
41 | DB 00011000b
42 | DB 00011000b
43 | DB 01111110b
44 | DB 00000000b
45 |
46 | ; 2
47 | DB 01111100b
48 | DB 11000110b
49 | DB 00001110b
50 | DB 00111100b
51 | DB 01111000b
52 | DB 11100000b
53 | DB 11111110b
54 | DB 00000000b
55 |
56 | ; 3
57 | DB 01111110b
58 | DB 00001100b
59 | DB 00011000b
60 | DB 00111100b
61 | DB 00000110b
62 | DB 11000110b
63 | DB 01111100b
64 | DB 00000000b
65 |
66 | ; 4
67 | DB 00011100b
68 | DB 00111100b
69 | DB 01101100b
70 | DB 11001100b
71 | DB 11111110b
72 | DB 00001100b
73 | DB 00001100b
74 | DB 00000000b
75 |
76 | ; 5
77 | DB 11111100b
78 | DB 11000000b
79 | DB 11111100b
80 | DB 00000110b
81 | DB 00000110b
82 | DB 11000110b
83 | DB 01111100b
84 | DB 00000000b
85 |
86 | ; 6
87 | DB 00111100b
88 | DB 01100000b
89 | DB 11000000b
90 | DB 11111100b
91 | DB 11000110b
92 | DB 11000110b
93 | DB 01111100b
94 | DB 00000000b
95 |
96 | ; 7
97 | DB 11111110b
98 | DB 11000110b
99 | DB 00001100b
100 | DB 00011000b
101 | DB 00110000b
102 | DB 00110000b
103 | DB 00110000b
104 | DB 00000000b
105 |
106 | ; 8
107 | DB 01111100b
108 | DB 11000110b
109 | DB 11000110b
110 | DB 01111100b
111 | DB 11000110b
112 | DB 11000110b
113 | DB 01111100b
114 | DB 00000000b
115 |
116 | ; 9
117 | DB 01111100b
118 | DB 11000110b
119 | DB 11000110b
120 | DB 01111110b
121 | DB 00000110b
122 | DB 00001100b
123 | DB 01111000b
124 | DB 00000000b
125 |
126 | ; A
127 | DB 00111000b
128 | DB 01101100b
129 | DB 11000110b
130 | DB 11000110b
131 | DB 11111110b
132 | DB 11000110b
133 | DB 11000110b
134 | DB 00000000b
135 |
136 | ; B
137 | DB 11111100b
138 | DB 11000110b
139 | DB 11000110b
140 | DB 11111100b
141 | DB 11000110b
142 | DB 11000110b
143 | DB 11111100b
144 | DB 00000000b
145 |
146 | ; C
147 | DB 00111100b
148 | DB 01100110b
149 | DB 11000000b
150 | DB 11000000b
151 | DB 11000000b
152 | DB 01100110b
153 | DB 00111100b
154 | DB 00000000b
155 |
156 | ; D
157 | DB 11111000b
158 | DB 11001100b
159 | DB 11000110b
160 | DB 11000110b
161 | DB 11000110b
162 | DB 11001100b
163 | DB 11111000b
164 | DB 00000000b
165 |
166 | ; E
167 | DB 11111110b
168 | DB 11000000b
169 | DB 11000000b
170 | DB 11111100b
171 | DB 11000000b
172 | DB 11000000b
173 | DB 11111110b
174 | DB 00000000b
175 |
176 | ; F
177 | DB 11111110b
178 | DB 11000000b
179 | DB 11000000b
180 | DB 11111100b
181 | DB 11000000b
182 | DB 11000000b
183 | DB 11000000b
184 | DB 00000000b
185 |
186 | ; G
187 | DB 00111110b
188 | DB 01100000b
189 | DB 11000000b
190 | DB 11011110b
191 | DB 11000110b
192 | DB 01100110b
193 | DB 01111110b
194 | DB 00000000b
195 |
196 | ; H
197 | DB 11000110b
198 | DB 11000110b
199 | DB 11000110b
200 | DB 11111110b
201 | DB 11000110b
202 | DB 11000110b
203 | DB 11000110b
204 | DB 00000000b
205 |
206 | ; I
207 | DB 01111110b
208 | DB 00011000b
209 | DB 00011000b
210 | DB 00011000b
211 | DB 00011000b
212 | DB 00011000b
213 | DB 01111110b
214 | DB 00000000b
215 |
216 | ; J
217 | DB 00011110b
218 | DB 00000110b
219 | DB 00000110b
220 | DB 00000110b
221 | DB 11000110b
222 | DB 11000110b
223 | DB 01111100b
224 | DB 00000000b
225 |
226 | ; K
227 | DB 11000110b
228 | DB 11001100b
229 | DB 11011000b
230 | DB 11110000b
231 | DB 11111000b
232 | DB 11011100b
233 | DB 11001110b
234 | DB 00000000b
235 |
236 | ; L
237 | DB 01100000b
238 | DB 01100000b
239 | DB 01100000b
240 | DB 01100000b
241 | DB 01100000b
242 | DB 01100000b
243 | DB 01111110b
244 | DB 00000000b
245 |
246 | ; M
247 | DB 11000110b
248 | DB 11101110b
249 | DB 11111110b
250 | DB 11111110b
251 | DB 11010110b
252 | DB 11000110b
253 | DB 11000110b
254 | DB 00000000b
255 |
256 | ; N
257 | DB 11000110b
258 | DB 11100110b
259 | DB 11110110b
260 | DB 11111110b
261 | DB 11011110b
262 | DB 11001110b
263 | DB 11000110b
264 | DB 00000000b
265 |
266 | ; O
267 | DB 01111100b
268 | DB 11000110b
269 | DB 11000110b
270 | DB 11000110b
271 | DB 11000110b
272 | DB 11000110b
273 | DB 01111100b
274 | DB 00000000b
275 |
276 | ; P
277 | DB 11111100b
278 | DB 11000110b
279 | DB 11000110b
280 | DB 11000110b
281 | DB 11111100b
282 | DB 11000000b
283 | DB 11000000b
284 | DB 00000000b
285 |
286 | ; Q
287 | DB 01111100b
288 | DB 11000110b
289 | DB 11000110b
290 | DB 11000110b
291 | DB 11011110b
292 | DB 11001100b
293 | DB 01111010b
294 | DB 00000000b
295 |
296 | ; R
297 | DB 11111100b
298 | DB 11000110b
299 | DB 11000110b
300 | DB 11001110b
301 | DB 11111000b
302 | DB 11011100b
303 | DB 11001110b
304 | DB 00000000b
305 |
306 | ; S
307 | DB 01111000b
308 | DB 11001100b
309 | DB 11000000b
310 | DB 01111100b
311 | DB 00000110b
312 | DB 11000110b
313 | DB 01111100b
314 | DB 00000000b
315 |
316 | ; T
317 | DB 01111110b
318 | DB 00011000b
319 | DB 00011000b
320 | DB 00011000b
321 | DB 00011000b
322 | DB 00011000b
323 | DB 00011000b
324 | DB 00000000b
325 |
326 | ; U
327 | DB 11000110b
328 | DB 11000110b
329 | DB 11000110b
330 | DB 11000110b
331 | DB 11000110b
332 | DB 11000110b
333 | DB 01111100b
334 | DB 00000000b
335 |
336 | ; V
337 | DB 11000110b
338 | DB 11000110b
339 | DB 11000110b
340 | DB 11101110b
341 | DB 01111100b
342 | DB 00111000b
343 | DB 00010000b
344 | DB 00000000b
345 |
346 | ; W
347 | DB 11000110b
348 | DB 11000110b
349 | DB 11010110b
350 | DB 11111110b
351 | DB 11111110b
352 | DB 11101110b
353 | DB 11000110b
354 | DB 00000000b
355 |
356 | ; X
357 | DB 11000110b
358 | DB 11101110b
359 | DB 01111100b
360 | DB 00111000b
361 | DB 01111100b
362 | DB 11101110b
363 | DB 11000110b
364 | DB 00000000b
365 |
366 | ; Y
367 | DB 01100110b
368 | DB 01100110b
369 | DB 01100110b
370 | DB 00111100b
371 | DB 00011000b
372 | DB 00011000b
373 | DB 00011000b
374 | DB 00000000b
375 |
376 | ; Z
377 | DB 11111110b
378 | DB 00001110b
379 | DB 00011100b
380 | DB 00111000b
381 | DB 01110000b
382 | DB 11100000b
383 | DB 11111110b
384 | DB 00000000b
385 |
386 | ; space
387 | DB 00000000b
388 | DB 00000000b
389 | DB 00000000b
390 | DB 00000000b
391 | DB 00000000b
392 | DB 00000000b
393 | DB 00000000b
394 | DB 00000000b
395 |
396 | ; ,
397 | DB 00000000b
398 | DB 00000000b
399 | DB 00000000b
400 | DB 00000000b
401 | DB 00110000b
402 | DB 00110000b
403 | DB 00100000b
404 | DB 00000000b
405 |
406 | ; .
407 | DB 00000000b
408 | DB 00000000b
409 | DB 00000000b
410 | DB 00000000b
411 | DB 00110000b
412 | DB 00110000b
413 | DB 00000000b
414 | DB 00000000b
415 |
416 | ; .,
417 | DB 00000000b
418 | DB 00000000b
419 | DB 00000000b
420 | DB 00000000b
421 | DB 01101100b
422 | DB 01101100b
423 | DB 00001000b
424 | DB 00000000b
425 |
426 | ; registered trademark symbol
427 | DB 00111000b
428 | DB 01000100b
429 | DB 10111010b
430 | DB 10101010b
431 | DB 10110010b
432 | DB 10101010b
433 | DB 01000100b
434 | DB 00111000b
435 |
--------------------------------------------------------------------------------
/freedisksys.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; zero-page registers
17 | ZP_PPUCTRL EQU $FF; value last written to $2000 $80 on reset.
18 | ZP_PPUMASK EQU $FE; value last written to $2001 $06 on reset
19 | ZP_PPUSCROLL1 EQU $FD; value last written to $2005/1 $00 on reset.
20 | ZP_PPUSCROLL2 EQU $FC; value last written to $2005/2 $00 on reset.
21 | ZP_JOYPAD1 EQU $FB; value last written to $4016 $00 on reset.
22 | ZP_FDSCTRL EQU $FA; value last written to $4025 $2E on reset.
23 | ZP_EXTCONN EQU $F9; value last written to $4026 $FF on reset.
24 |
25 | ; [$0102]/[$0103]: PC action on reset
26 |
27 | ; PPU registers
28 | PPUCTRL EQU $2000
29 | PPUMASK EQU $2001
30 | PPUSTATUS EQU $2002
31 | OAMADDR EQU $2003
32 | OAMDATA EQU $2004
33 | PPUSCROLL EQU $2005
34 | PPUADDR EQU $2006
35 | PPUDATA EQU $2007
36 | OAMDMA EQU $4014
37 |
38 | ; undocumented instructions not explicitly supported by asm6f
39 | ; TOP (abs)
40 | MACRO TOP address
41 | DB $0C
42 | DW #address
43 | ENDM
44 |
45 | ; Input registers
46 | JOYPAD1 EQU $4016
47 | JOYPAD2 EQU $4017
48 |
49 | ; FDS registers
50 | IRQLOW EQU $4020
51 | IRQHIGH EQU $4021
52 | IRQCTRL EQU $4022
53 | MASTERIO EQU $4023
54 | WRITEDATA EQU $4024
55 | FDSCTRL EQU $4025
56 | EXTCONNWR EQU $4026
57 | DISKSTATUS EQU $4030
58 | READDATA EQU $4031
59 | DRIVESTATUS EQU $4032
60 | EXTCONNRD EQU $4033
61 |
62 | ; Error codes:
63 | OK EQU $00 ; no error
64 | DISK_NOT_SET EQU $01 ; disk set, ($4032.0) disk not set
65 | POWER_SUPPLY_FAILURE EQU $02 ; battery, ($4033.7) power supply failure
66 | WRITE_PROTECTED EQU $03 ; ($4032.2) disk is write protected
67 | WRONG_MAKER_ID EQU $04 ; Wrong maker ID
68 | WRONG_GAME EQU $05 ; Wrong game
69 | WRONG_GAME_VER EQU $06 ; Wrong game version
70 | WRONG_SIDE_NUM EQU $07 ; a,b side, wrong side number
71 | WRONG_DISK_NUM EQU $08 ; disk no., wrong disk number
72 | WRONG_ADDL_DISK_ID1 EQU $09 ; wrong additional disk ID 1
73 | WRONG_ADDL_DISK_ID2 EQU $0a ; wrong additional disk ID 2
74 | APPROVAL_CHECK_FAILED EQU $20 ; disk trouble, approval check failed
75 | WRONG_SIGNATURE EQU $21 ; disk trouble, '*NINTENDO-HVC*' string in block 1 doesn't match
76 | BLOCK_TYPE_1_EXPECTED EQU $22 ; disk trouble, block type 1 expected
77 | BLOCK_TYPE_2_EXPECTED EQU $23 ; disk trouble, block type 2 expected
78 | BLOCK_TYPE_3_EXPECTED EQU $24 ; disk trouble, block type 3 expected
79 | BLOCK_TYPE_4_EXPECTED EQU $25 ; disk trouble, block type 4 expected
80 | BLOCK_FAILED_CRC EQU $27 ; disk trouble, ($4030.4) block failed CRC
81 | EOF_READ EQU $28 ; disk trouble, ($4030.6) file ends prematurely during read
82 | EOF_WRITE EQU $29 ; disk trouble, ($4030.6) file ends prematurely during write
83 | DISK_FULL EQU $30 ; disk trouble, ($4032.1) disk is full
84 |
85 | MACRO API_ENTRYPOINT address
86 | IF $ > #address
87 | ERROR "Previous function overflowed into following public API"
88 | ELSE
89 | PAD #address
90 | ENDIF
91 | ENDM
92 |
93 | ; Fill with a KIL opcode, so we die immediately if we jump into crazytown
94 | FILLVALUE $72
95 |
96 | ; Start assembling at the beginning of the FDS ROM area
97 | ORG $E000
98 | DB $00
99 |
100 | INCLUDE font.asm
101 | INCLUDE delay.asm
102 | INCLUDE ppumask.asm
103 | INCLUDE nmi.asm
104 | INCLUDE irq.asm
105 |
106 | ; Loads files specified by DiskID into memory from disk. Load addresses are
107 | ; decided by the file's header.
108 | ; Parameters: Pointer to Disk ID, Pointer to File List
109 | ; Returns: A = error #, Y = # of files loaded
110 | API_ENTRYPOINT $e1f8
111 | LoadFiles:
112 | RTS
113 |
114 | ; Appends the file data given by DiskID to the disk. This means that the file
115 | ; is tacked onto the end of the disk, and the disk file count is incremented.
116 | ; The file is then read back to verify the write. If an error occurs during
117 | ; verification, the disk's file count is decremented (logically hiding the
118 | ; written file).
119 | ; Parameters: Pointer to Disk ID, Pointer to File Header
120 | ; Returns: A = error #
121 | API_ENTRYPOINT $e237
122 | AppendFile:
123 | RTS
124 |
125 | ; Same as "Append File", but instead of writing the file to the end of the
126 | ; disk, A specifies the sequential position on the disk to write the file (0
127 | ; is the first). This also has the effect of setting the disk's file count to
128 | ; the A value, therefore logically hiding any other files that may reside after
129 | ; the written one.
130 | ; Parameters: Pointer to Disk ID, Pointer to File Header, A = file #
131 | ; Returns: A = error #
132 | API_ENTRYPOINT $e239
133 | WriteFile:
134 | RTS
135 |
136 | ; Reads in disk's file count, compares it to A, then sets the disk's file count
137 | ; to A.
138 | ; Parameters: Pointer to Disk ID, A = # to set file count to
139 | ; Returns: A = error #
140 | API_ENTRYPOINT $e2b7
141 | CheckFileCount:
142 | RTS
143 |
144 | ; Reads in disk's file count, decrements it by A, then writes the new value
145 | ; back.
146 | ; Parameters: Pointer to Disk ID, A = number to reduce current file count by
147 | ; Returns: A = error #
148 | API_ENTRYPOINT $e2bb
149 | AdjustFileCount:
150 | RTS
151 |
152 | ; Set the file count to A + 1
153 | ; Parameters: Pointer to Disk ID, A = file count minus one = # of the last file
154 | ; Returns: A = error #
155 | API_ENTRYPOINT $e301
156 | SetFileCount1:
157 | RTS
158 |
159 | ; Set the file count to A
160 | ; Parameters: Pointer to Disk ID, A = file count
161 | ; Returns: A = error #
162 | API_ENTRYPOINT $e305
163 | SetFileCount:
164 | RTS
165 |
166 | ; Fills provided DiskInfo structure with data read off the current disk.
167 | ; Parameters: Pointer to Disk Info
168 | ; Returns: A = error #
169 | API_ENTRYPOINT $e32a
170 | GetDiskInfo:
171 | RTS
172 |
173 | API_ENTRYPOINT $e3da
174 | AddYtoPtr0A:
175 | RTS
176 |
177 | INCLUDE gethardcodedpointers.asm
178 |
179 | ; Compares the first 10 bytes on the disk coming after the FDS string, to 10
180 | ; bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be
181 | ; placed in the equivelant place in the compare string. Otherwise, if the
182 | ; comparison fails, an appropriate error will be generated.
183 | ; Parameters: Pointer to 10 byte string at $00
184 | API_ENTRYPOINT $e445
185 | CheckDiskHeader:
186 | RTS
187 |
188 | ; Reads number of files stored on disk, stores the result in $06
189 | API_ENTRYPOINT $e484
190 | GetNumFiles:
191 | RTS
192 |
193 | ; Writes new number of files to disk header.
194 | ; Parameters: A = number of files
195 | API_ENTRYPOINT $e492
196 | SetNumFiles:
197 | RTS
198 |
199 | ; Uses a byte string pointed at by Ptr($02) to tell the disk system which files
200 | ; to load. The file ID's number is searched for in the string. If an exact
201 | ; match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are
202 | ; found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If
203 | ; the first byte in the string is -1, the BootID number is used for matching
204 | ; files (any FileID that is not greater than the BootID qualifies as a match).
205 | ; Parameters: Pointer to FileID list at $02
206 | API_ENTRYPOINT $e4a0
207 | FileMatchTest:
208 | RTS
209 |
210 | ; Skips over specified number of files.
211 | ; Parameters: Number of files to skip in $06
212 | API_ENTRYPOINT $e4da
213 | SkipFiles:
214 | RTS
215 |
216 | API_ENTRYPOINT $e4f9
217 | LoadData:
218 | RTS
219 |
220 | API_ENTRYPOINT $e506
221 | ReadData:
222 | RTS
223 |
224 | API_ENTRYPOINT $e5b5
225 | SaveData:
226 | RTS
227 |
228 | API_ENTRYPOINT $e64d
229 | WaitForDriveReady:
230 | RTS
231 |
232 | API_ENTRYPOINT $e685
233 | StopMotor:
234 | RTS
235 |
236 | API_ENTRYPOINT $e68f
237 | CheckBlockType:
238 | RTS
239 |
240 | API_ENTRYPOINT $e6b0
241 | WriteBlockType:
242 | RTS
243 |
244 | API_ENTRYPOINT $e6e3
245 | StartXfer:
246 | RTS
247 |
248 | API_ENTRYPOINT $e706
249 | EndOfBlockRead:
250 | RTS
251 |
252 | API_ENTRYPOINT $e729
253 | EndOfBlkWrite:
254 | RTS
255 |
256 | API_ENTRYPOINT $e778
257 | XferDone:
258 | RTS
259 |
260 | ; Waits for the first byte to be transferred between the drive and RAM adapter.
261 | ; Does not know or care whether it's a read or write. An interrupt is involved,
262 | ; but the stack is manipulated in the ISR such that control returns to the
263 | ; caller of this function as if it were a simple subroutine.
264 | ; Parameters: A = byte to write to disk (if this is a write)
265 | ; Affects: X, $101, $FA
266 | ; Returns: A = byte read from disk (if this is a read)
267 | API_ENTRYPOINT $e794
268 | Xfer1stByte:
269 | RTS
270 |
271 | ; Waits for a byte to be transferred between the drive and the RAM adapter.
272 | ; Does not know or care whether it's a read or write. An interrupt is involved,
273 | ; but the stack is manipulated in the ISR such that control returns to the
274 | ; caller of this function as if it were a simple subroutine.
275 | ; Parameters: A = byte to write to disk (if this is a write)
276 | ; Affects: X
277 | ; Returns: A = byte read from disk (if this is a read)
278 | API_ENTRYPOINT $e7a3
279 | XferByte:
280 | RTS
281 |
282 | ; VRAM Buffers
283 | ; The structure of VRAM buffers are as follows:
284 | ;
285 | ; SIZE CONTENTS
286 | ; 2 VRAM Address (big endian)
287 | ; 1 bit 0-5 length of data ($0 means a length of 64)
288 | ; bit 6 : 0 = copy, 1 = fill
289 | ; bit 7 : 0 = increment by 1, 1 = increment by 32
290 | ; n Data to copy to VRAM
291 | ; ..... repeated as many times as needed
292 | ; 1 $ff
293 | ;
294 | ; * The main structure is terminated by a byte >= $80 (High address is always
295 | ; supposed to be in $00..$3f range)
296 | ; * $4c is a "call" command. The 2 bytes that follow is the address of a sub-
297 | ; VRAM structure. The sub-structure can call another sub-structure and so on.
298 | ; * $60 is a "return" command. It will terminate a sub-structure.
299 | ; * If Fill mode is used, the routine takes only 1 byte of data which is
300 | ; repeated.
301 | ;
302 | ; The VRAM buffer is located at $300-$3xx. $300 holds the size of the buffer
303 | ; (maximum), and $301 holds the end index of the buffer. The actual buffer lies
304 | ; at $302-$3xx, and is of variable length.
305 | ;
306 | ; * $300 is initialized to the value $7d, effectively making the buffer lie at
307 | ; $300-$37f. It's possible to change the value here to make it bigger or
308 | ; smaller, but the biggest possible value is $fd, making the buffer lie at
309 | ; $300-$3ff.
310 | ; * Format of the buffer is equivalent to the VRAM structure above, except that
311 | ; there are no sub-structures, no increment by 32 flag and no fill flag.
312 | ; * For this reason, the VRAM buffer at $302 can be used as a sub-structure.
313 | ; * A call to WriteVRAMBuffers will execute faster than a call to
314 | ; VRAMStructWrite with $302 as an argument, but both will have the same
315 | ; effect.
316 | ;
317 | ; The structure of the VRAM read buffer itself is trivial - only single bytes
318 | ; are read (there's no runs of data). All reads are mapped to a structure of 3
319 | ; bytes in the read buffer:
320 | ;
321 | ; SIZE CONTENTS
322 | ; 2 VRAM Address (big endian)
323 | ; 1 data
324 | ;
325 | ; Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved
326 | ; in the read buffer. Once data from VRAM has been read, if it must be written
327 | ; back after a modification, the user need to copy it to the write buffer
328 | ; manually.
329 |
330 | INCLUDE vramstructwrite.asm
331 | INCLUDE fetchdirectptr.asm
332 | INCLUDE vrambuffers.asm
333 | INCLUDE vramstrings.asm
334 | INCLUDE getvrambufferbyte.asm
335 | INCLUDE nametable.asm
336 | INCLUDE random.asm
337 |
338 | ; Run Sprite DMA from RAM $200-$2FF
339 | ; Affects: A
340 | API_ENTRYPOINT $e9c8
341 | SpriteDMA:
342 | LDA #0
343 | STA OAMADDR
344 | LDA #$02
345 | STA OAMDMA
346 | RTS
347 |
348 | INCLUDE counterlogic.asm
349 | INCLUDE gamepads.asm
350 | INCLUDE vramfill.asm
351 |
352 | ; Fill RAM pages with specified value.
353 | ; Parameters: A = fill value, X = first page #, Y = last page #
354 | ; Affects: A, X, Y, $00, $01
355 | API_ENTRYPOINT $ead2
356 | MemFill:
357 | ; Start from the high end and count down
358 | ; The address of the start of the last page is just the Y register in the high byte of the pointer and 0 in the low byte
359 | STY $01
360 | @outerloop:
361 | LDY #0
362 | STY $00
363 | @loop:
364 | STA ($00),Y
365 | INY
366 | BNE @loop
367 | DEC $01 ; work on the next page down
368 | CPX $01
369 | BMI @outerloop ; cover the X < $01 case
370 | BEQ @outerloop ; cover the X == $01 case
371 | RTS
372 |
373 | ; Set scroll registers according to values in $FC, $FD and $FF.
374 | ; Should typically be called in VBlank after VRAM updates
375 | ; Parameters: $FC, $FD, $FF
376 | ; Affects: A
377 | API_ENTRYPOINT $eaea
378 | SetScroll:
379 | LDA PPUSTATUS ; reset PPU's "w" register to 0
380 | LDA ZP_PPUSCROLL1
381 | STA PPUSCROLL
382 | LDA ZP_PPUSCROLL2
383 | STA PPUSCROLL
384 | LDA ZP_PPUCTRL
385 | STA PPUCTRL
386 | RTS
387 |
388 | ; The instruction calling this is supposed to be followed by a jump table
389 | ; (16-bit pointers little endian, up to 128 pointers). A is the entry # to jump
390 | ; to, return address on stack is used to get jump table entries.
391 | ; Parameters: A = Jump table entry
392 | ; Affects: A, X, Y, $00, $01
393 | API_ENTRYPOINT $eafd
394 | JumpEngine:
395 | ; the top of the stack is 1 less than the address of the table
396 | SEC ; to rotate a 1 into the LSB, set the carry
397 | ROL A ; Each entry is 2 bytes, so multiply A by two to get the offset
398 | TAY ; now A is freed up and Y has the offset, which will be useful later
399 | ; get the address of the jump table
400 | PLA ; low byte of the jump table address
401 | STA $00
402 | PLA ; high byte of the jump table address
403 | STA $01
404 | ; post-indexed indirect load of the entry in the jump table
405 | LDA ($00),Y
406 | TAX
407 | INY
408 | LDA ($00),Y
409 | STX $00
410 | STA $01
411 | ; (indirect) jump!
412 | JMP ($00)
413 |
414 | ; Read Family Basic Keyboard expansion
415 | API_ENTRYPOINT $eb13
416 | ReadKeyboard:
417 | RTS
418 |
419 | INCLUDE loadtileset.asm
420 |
421 | ; Some kind of logic that some games use. (detail is under analysis)
422 | ; Parameters: $00-$01 Pointer to structure... ?
423 | ; Affects: A, X, Y, $02, $03, $04, $05, $06, $07, $08, $09
424 | API_ENTRYPOINT $ec22
425 | unk_EC22:
426 | RTS
427 |
428 | API_ENTRYPOINT $ee17
429 | StartMotor:
430 | RTS
431 |
432 | ; private functions
433 |
434 | ; Checks whether the little-endian address provided in ($02) plus the offset in
435 | ; $04 is in the range $3Fxx (or one of its mirrors). Checks the current PPU
436 | ; increment mode to do the correct arithmetic. If the current PPU address is in
437 | ; the palette, reset to $0000. Assumes that the PPUADDR latch is currently
438 | ; clear.
439 | ; Parameters: $00-$01 = PPU address, $02 = offset
440 | ; Affects: A, X, Y
441 | PreventPalettePpuAddr:
442 | LDA #%00000100 ; increment mode flag bit test
443 | AND ZP_PPUCTRL
444 | BEQ @incr1
445 | LDA $02 ; low address byte
446 | LDY $03 ; high address byte
447 | LDX $04 ; offset
448 | BEQ @check ; if the offset is 0, jump straight to the check
449 | @incr32:
450 | CLC
451 | ADC #32 ; for (X = $04; X != 0; X--) $02,$03 += 32;
452 | BCC @nextiter
453 | INY
454 | @nextiter:
455 | DEX
456 | BNE @incr32
457 | BEQ @check
458 | @incr1:
459 | LDA $02 ; low address byte
460 | LDY $03 ; high address byte
461 | CLC
462 | ADC $04 ; add length to low address byte
463 | BCC @check
464 | INY ; add C to high address byte
465 | @check:
466 | TYA
467 | AND #$3F ; we might be in the mirror $7Fxx range instead of $3Fxx
468 | CMP #$3F ; now check for exactly $3F
469 | BNE @done
470 | LDA #0 ; reset PPUADDR to 0 to get it out of the palette area
471 | STA PPUADDR
472 | STA PPUADDR
473 | @done:
474 | RTS
475 |
476 | INCLUDE reset.asm
477 |
478 | ; the hard-coded interrupt vectors at the end of ROM
479 | API_ENTRYPOINT $fffa
480 | DW NMI
481 | DW RESET
482 | DW IRQ
483 |
--------------------------------------------------------------------------------
/gamepads.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Read hard-wired Famicom joypads.
17 | ; Returns: $F5 = Joypad #1 data, $F6 = Joypad #2 data, $00 = Expansion #1 data, $01 = Expansion #2 data
18 | ; Affects: A, X, $00, $01, $F5, $F6, $FB
19 | API_ENTRYPOINT $e9eb
20 | ReadPads:
21 | ; If this is being called to also read expansion port pads, bit 1 of $FB
22 | ; will be 1, otherwise it will be 0. To strobe the controllers in either
23 | ; case, just set the 1s bit on the value in $FB, then clear it to read.
24 | LDX ZP_JOYPAD1
25 | INX ; now X[0] is 1, assuming that no other function will set that bit to 1
26 | ; While the strobe bit is set, buttons will be continuously reloaded.
27 | ; This means that reading from JOYPAD1 will only return the state of the
28 | ; first button: button A.
29 | STX JOYPAD1
30 | DEX ; now X[0] is 0
31 | ; By storing bit 0 into JOYPAD1, the strobe bit is cleared and the reloading stops.
32 | ; This allows all 8 buttons (newly reloaded) to be read from JOYPAD1.
33 | STX JOYPAD1
34 | LDX #8 ; 8 bits to load
35 | @loop:
36 | LDA JOYPAD1
37 | LSR A ; if bit 0 was a 1, the carry flag will be set
38 | ROR $F5 ; rotate the carry flag's value into $F5, where the joypad #1 data lives
39 | LSR A ; bit 0 was already shifted out, now shift bit 1 into the carry flag
40 | ROR $00 ; rotate the carry flag's value into $00, where the expansion #1 data lives
41 | LDA JOYPAD2
42 | LSR A ; if bit 0 was a 1, the carry flag will be set
43 | ROR $F6 ; rotate the carry flag's value into $F6, where the joypad #2 data lives
44 | LSR A ; bit 0 was already shifted out, now shift bit 1 into the carry flag
45 | ROR $01 ; rotate the carry flag's value into $01, where the expansion #2 data lives
46 | DEX
47 | BNE @loop
48 | RTS
49 |
50 | ; Combine the reports from the built-in and expansion gamepads by OR'ing them
51 | ; together, storing them in the built-in gamepads' variables ($F5 and $F6)
52 | OrPads:
53 | LDA $00
54 | ORA $F5
55 | STA $F5
56 | LDA $01
57 | ORA $F6
58 | STA $F6
59 | RTS
60 |
61 | ; Read hard-wired Famicom joypads, and detect up->down button transitions
62 | ; Returns: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data
63 | ; Affects: A, X, Y, $00, $01, $F5, $F6, $F7, $F8
64 | API_ENTRYPOINT $ea1a
65 | ReadDownPads:
66 | JSR ReadPads
67 | BEQ DetectUpToDownTransitions; skip over ReadOrDownPads
68 |
69 | ; Read both hard-wired Famicom and expansion port joypads (OR'd together), and detect up->down
70 | ; button transitions.
71 | ; Returns: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data
72 | ; Affects: A, X, Y, $00, $01, $F5, $F6, $F7, $F8
73 | API_ENTRYPOINT $ea1f
74 | ReadOrDownPads:
75 | JSR ReadPads
76 | JSR OrPads
77 | ; fall through to detection
78 |
79 | DetectUpToDownTransitions:
80 | LDX #1 ; handle pad #2 first
81 | DetectUpToDownOnePad:
82 | LDA $F5,X ; load current joypad state
83 | TAY ; preserve the current state in Y
84 | EOR $F7,X ; exclusive-OR between previous and current state says which buttons have changed
85 | AND $F5,X ; AND of that with current state says which buttons have changed to down
86 | STA $F5,X ; save up-down transitions
87 | STY $F7,X ; overwrite previous state with current state via temporary
88 | DEX
89 | BPL DetectUpToDownOnePad ; when X underflows to -127, this branch will not be taken
90 | RTS
91 |
92 | ; Read hard-wired Famicom joypads, and detect up->down button transitions. Data
93 | ; is read until two consecutive read matches to work around the DMC reading
94 | ; glitches.
95 | ; Returns: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data
96 | ; Affects: A, X, Y, $00, $01, $F5, $F6, $F7, $F8
97 | API_ENTRYPOINT $ea36
98 | ReadDownVerifyPads:
99 | JSR ReadPads
100 | @remember:
101 | LDA $F5 ; get controller #1's value
102 | PHA ; store controller #1's value on the stack
103 | LDY $F6 ; ReadPads does not touch Y, so controller #2's value can be preserved there
104 | JSR ReadPads
105 | PLA
106 | EOR $F5 ; do the previous and current reads of JOYPAD1 match?
107 | BNE @remember ; if they don't match, read again
108 | CPY $F6 ; do the previous and current reads of JOYPAD2 match?
109 | BNE @remember ; if they don't match, read again
110 | BEQ DetectUpToDownTransitions ; if they do match, branch (using BEQ which we know will work) to DetectUpToDownTransitions
111 |
112 | ; Read both hard-wired Famicom and expansion port joypads and detect up->down
113 | ; button transitions. Data is read until two consecutive read matches to work
114 | ; around the DMC reading glitches.
115 | ; Returns: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data
116 | ; Affects: A, X, Y, $00, $01, $F5, $F6, $F7, $F8
117 | API_ENTRYPOINT $ea4c
118 | ReadOrDownVerifyPads:
119 | JSR ReadPads
120 | @remember:
121 | LDA $F5 ; get controller #1's value
122 | PHA ; store controller #1's value on the stack
123 | LDY $F6 ; ReadPads does not touch Y, so controller #2's value can be preserved there
124 | JSR ReadPads
125 | PLA
126 | EOR $F5 ; do the previous and current reads of JOYPAD1 match?
127 | BNE @remember ; if they don't match, read again
128 | CPY $F6 ; do the previous and current reads of JOYPAD2 match?
129 | BNE @remember ; if they don't match, read again
130 | JSR OrPads ; OR the built-in and expansion pads together
131 | VerifyDownPads:
132 | JSR DetectUpToDownTransitions
133 | RTS
134 |
135 | ; Read both hard-wired Famicom and expansion port joypad, but store their data
136 | ; separately instead of ORing them together like the other routines do. This
137 | ; routine is NOT DMC fortified.
138 | ; Returns: $f1-$f4 = joypad data, $f5-$f8 = up-to-down transitions in the order : Pad1, Pad2, Expansion1, Expansion2
139 | ; Affects: A, X, Y, $00, $01, $F1, $F2, $F3, $F4, $F5, $F6, $F7, $F8
140 | API_ENTRYPOINT $ea68
141 | ReadDownExpPads:
142 | JSR ReadPads
143 | ; exp1 and exp2 are in $00 and $01, so copy them to their destinations
144 | LDA $00
145 | STA $F7
146 | LDA $01
147 | STA $F8
148 | LDX #3 ; handle expansion pad #2 first
149 | @DetectUpToDownOnePadExp:
150 | LDA $F5,X ; load current joypad state
151 | TAY ; preserve the current state in Y
152 | EOR $F1,X ; exclusive-OR between previous and current state says which buttons have changed
153 | AND $F5,X ; AND of that with current state says which buttons have changed to down
154 | STA $F5,X ; save up-down transitions
155 | STY $F1,X ; overwrite previous state with current state via temporary
156 | DEX ; handle pad #1 next (offset 0), otherwise return
157 | BPL @DetectUpToDownOnePadExp ; when X underflows to -127, this branch will not be taken
158 | RTS
159 |
--------------------------------------------------------------------------------
/gethardcodedpointers.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; This routine does 3 things. First, it fetches 1 or 2 hardcoded 16-bit
17 | ; pointers that follow the second return address. Second, it checks the
18 | ; disk set or even write-protect status of the disk, and if the checks fail,
19 | ; the first return address on the stack is discarded, and program control is
20 | ; returned to the second return address. Finally, it saves the position of
21 | ; the stack so that when an error occurs, program control will be returned to
22 | ; the same place.
23 | ; Parameters: A == -1 means there are two 16-bit pointer parameters, otherwise just 1.
24 | ; Returns: A == OK (0) if no error, or DISK_NOT_SET (1) if the disk is not set. ($00) contains where results were loaded
25 | ; Affects:
26 | ;params
27 | ;------
28 | ;2nd call addr 1 or 2 16-bit pointers
29 |
30 | ;A -1 2 16-bit pointers are present
31 | ; other values 1 16-bit pointer present
32 |
33 |
34 | ;rtns (no error)
35 | ;---------------
36 | ;PC original call address
37 |
38 | ;A 00
39 |
40 | ;[$00] where parameters were loaded (A is placed in [$02] if not -1)
41 |
42 |
43 | ;(error)
44 | ;-------
45 | ;PC second call address
46 |
47 | ;Y byte stored in [$0E]
48 |
49 | ;A 01 if disk wasn't set
50 | ; 03 if disk is write-protected
51 |
52 | API_ENTRYPOINT $e3e7
53 | GetHardCodedPointers:
54 | CLC
55 | BCC GetHardCodedPointersImpl
56 |
57 | ; Same as GetHardCodedPointers, except that
58 | ; Returns: A == OK (0) if no error, DISK_NOT_SET (1) if disk is not set, and WRITE_PROTECTED (3) if the disk is write-protected.
59 | API_ENTRYPOINT $e3ea
60 | GetHardCodedPointersWriteProtected:
61 | SEC
62 | GetHardCodedPointersImpl:
63 |
64 | BCC @end ; skip write-protect check if C is false
65 | LDA DRIVESTATUS
66 | AND #%00000100 ; bit 2 contains the write-protect status
67 | BEQ @end
68 |
69 | ; error handling
70 | @end:
71 | RTS
72 |
--------------------------------------------------------------------------------
/getvrambufferbyte.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; This routine was likely planned to be used in order to avoid useless latency
17 | ; on VRAM reads. It compares the VRAM address in ($00) with the address in the
18 | ; Yth slot (minimum 1) of the read buffer. If both addresses match, the
19 | ; corresponding data byte is returned in A with the carry flag clear. If the
20 | ; addresses are different, the address in the Yth slot is overwritten with the
21 | ; address in ($00), and the carry flag is set on return.
22 | ; Parameters: X = starting index of read buffer, Y = read buffer slot # (minimum 1), $00, $01 = VRAM address to compare with address in read buffer slot
23 | ; Returns: carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer
24 | ; Affects: A, X, Y
25 | API_ENTRYPOINT $e94f
26 | GetVRAMBufferByte:
27 | DEX
28 | DEX
29 | DEX
30 | TXA
31 | @loop:
32 | CLC
33 | ADC #3
34 | DEY
35 | BNE @loop
36 | TAX
37 | LDA $00
38 | CMP $300,X
39 | BNE @UpdateAddressHi
40 | LDA $01
41 | CMP $301,X
42 | BNE @UpdateAddressLo
43 | LDA $302,X
44 | CLC
45 | RTS
46 |
47 | @UpdateAddressHi:
48 | STA $300,X
49 | LDA $01
50 | @UpdateAddressLo:
51 | STA $301,X
52 | SEC
53 | RTS
54 |
--------------------------------------------------------------------------------
/irq.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Bits 6 and 7 of IRQ_ACTION determine the behavior of IRQs.
17 | ; ($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)
18 | ; $E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)
19 | ; $E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)
20 | ; $E1D9 : BIOS disk skip bytes (if [$0101] = %00nnnnnn)
21 | ; Set to $80 on reset, aka BIOS acknowledge and delay.
22 | IRQ_ACTION EQU $0101
23 | IRQ_VEC EQU $DFFE
24 |
25 | IRQ:
26 | BIT IRQ_ACTION
27 | BMI @actions2and3
28 | BVS @diskXfer
29 |
30 | ; BIOS disk skip bytes - skip n+1 bytes, where n is the lower 6 bits of $101.
31 | ; Each IRQ reduces the number of bytes to skip by 1.
32 | PHA
33 | LDA IRQ_ACTION
34 | SEC
35 | SBC #1 ; another byte skipped, so decrement the bytes remaining to skip
36 | BCC @byteskipped
37 | ; once the subtraction underflows, n+1 bytes have been skipped. Set the
38 | ; IRQ action back to the disk's ISR and clear the byte transfer flag.
39 | STA IRQ_ACTION
40 | LDA READDATA ; clear the byte transfer flag in $4030
41 | @byteskipped:
42 | PLA ; restore A
43 | RTI
44 |
45 | ; BIOS disk transfer - assumes that the IRQ was enabled by one of the disk
46 | ; routines attempting to transfer data. It doesn't know whether read or write
47 | ; is desired, so it does both - it writes to the output register and reads
48 | ; from the input register. It also manipulates the stack to behave like a
49 | ; subroutine call from the caller of Xfer1stByte or XferByte.
50 | @diskXfer:
51 | STA WRITEDATA
52 | PLA ; pop processor status off the stack
53 | PLA ; discard the ISR return address
54 | PLA ; so we can return to the caller of Xfer1stByte or XferByte
55 | LDA READDATA
56 | RTS
57 |
58 | @actions2and3:
59 | BVS @gameIRQ
60 |
61 | ; BIOS acknowledge and delay
62 | PHA
63 | LDA DISKSTATUS ; clear byte transfer flag
64 | JSR Delay131
65 | PLA
66 | RTI
67 |
68 | @gameIRQ:
69 | JMP (IRQ_VEC) ; game's IRQ vector
70 |
--------------------------------------------------------------------------------
/loadtileset.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; $eb66 is the beginning of the LoadTileset section of the ROM, but the
17 | ; entrypoint isn't until $ebaf
18 | API_ENTRYPOINT $eb66
19 | AdvanceTileBy8:
20 | LDA #8
21 | AdvanceTileByA:
22 | LDY #0 ; reset the buffer index
23 | PHP ; we need to preserve the carry bit - it has the transfer direction
24 | CLC
25 | ADC $00
26 | STA $00
27 | LDA #0
28 | ADC $01
29 | STA $01
30 | PLP ; restore the carry bit
31 | DEC $03
32 | RTS
33 |
34 | Fill8:
35 | LDX #8
36 | LDA $04
37 | @loop:
38 | BCC @write8
39 | LDA PPUDATA ; advance PPUADDR 1 byte
40 | BCS @next
41 | @write8:
42 | STA PPUDATA
43 | @next:
44 | DEX
45 | BNE @loop
46 | RTS
47 |
48 | Copy8:
49 | LDX #8
50 | @loop:
51 | BCC @write8
52 | LDA PPUDATA
53 | STA ($00),Y
54 | BCS @next
55 | @write8:
56 | LDA ($00),Y
57 | STA PPUDATA
58 | @next:
59 | INY
60 | DEX
61 | BNE @loop
62 | RTS
63 |
64 | Write8XOR:
65 | LDX #8
66 | @loop:
67 | LDA ($00),Y
68 | EOR $04
69 | STA PPUDATA
70 | INY
71 | DEX
72 | BNE @loop
73 | RTS
74 |
75 | ; This routine can read and write 2BP and 1BP tilesets to/from VRAM.
76 | ; The flags parameters are as follows:
77 | ;
78 | ; 7 bit 0
79 | ; ---------
80 | ; AAAA MMIT
81 | ; |||| ||||
82 | ; |||| |||+- Fill bit
83 | ; |||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)
84 | ; |||| ++--- Bitplane type (see below)
85 | ; ++++------ Low VRAM Address (aka tile # within a row)
86 | ;
87 | ; 1st bitplane 2nd bitplane Description
88 | ; ----------- ----------- -----------
89 | ; 0: data data+8 Normal 2-bitplane graphics
90 | ; 1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3
91 | ; 2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3
92 | ; 3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2
93 | ; This makes it possible for single bitplane tiles to take all possible color
94 | ; schemes when they end up in VRAM. However, it is not possible to (natively)
95 | ; load single bitplane graphics directly from the disk into VRAM; it should be
96 | ; loaded into PRG-RAM before transferring the data into VRAM. In read mode, all
97 | ; non "data" bitplanes are replaced by dummy reads. Also, mode 3 ONLY works in
98 | ; write mode!
99 | ; Parameters: A = Low VRAM Address & Flags, Y = Hi VRAM Address, X = # of tiles to transfer to/from VRAM, Direct Pointer = CPU address
100 | ; Affects: A, X, Y, $00, $01, $02, $03, $04, $05, $06
101 | API_ENTRYPOINT $ebaf
102 | LoadTileset:
103 | TOP PPUSTATUS ; clear PPU address latch
104 | STY PPUADDR ; set high byte of PPU address
105 | STA $02 ; store flags until we need them
106 | AND #$F0
107 | STA PPUADDR ; set low byte of VRAM address
108 | STX $03 ; store length
109 | JSR FetchDirectPtr
110 |
111 | LDA ZP_PPUCTRL
112 | AND #%11111011 ; set increment mode to 1
113 | STA ZP_PPUCTRL
114 | STA PPUCTRL
115 |
116 | LDA #0
117 | TAY ; Y = 0, start at beginning of buffer
118 | LSR $02
119 | BCC @fillClear
120 | LDA #$FF
121 | @fillClear:
122 | STA $04 ; save fill byte
123 |
124 | LSR $02 ; shift the transfer direction into the carry flag and keep it there
125 | BCC @writeMode
126 | LDA PPUDATA ; read and throw away a byte, to prime the PPU read buffer
127 | @writeMode:
128 | LDA #%00000011 ; mask the lowest two bits, where the mode has been shifted to
129 | AND $02
130 | BEQ @mode0
131 | CMP #2
132 | BEQ @mode2 ; if (mode == 2)
133 | BCC @mode1 ; if (mode < 2)
134 |
135 | @mode3:
136 | JSR Write8XOR
137 | LDY #0 ; Read the same data
138 | JSR Copy8
139 | JSR AdvanceTileBy8
140 | BNE @mode3
141 | RTS
142 |
143 | @mode0:
144 | JSR Copy8
145 | JSR Copy8
146 | LDA #16
147 | JSR AdvanceTileByA
148 | BNE @mode0
149 | RTS
150 |
151 | @mode1:
152 | JSR Copy8
153 | JSR Fill8
154 | JSR AdvanceTileBy8
155 | BNE @mode1
156 | RTS
157 |
158 | @mode2:
159 | JSR Fill8
160 | JSR Copy8
161 | JSR AdvanceTileBy8
162 | BNE @mode2
163 | RTS
164 |
--------------------------------------------------------------------------------
/nametable.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Convert pixel screen coordinates to corresponding nametable address (assumes
17 | ; no scrolling, and points to first nametable at $2000-$23ff).
18 | ; Parameters: $03 = Pixel X cord, $02 = Pixel Y cord
19 | ; Returns: $00 = High nametable address, $01 = Low nametable address
20 | ; Affects: A
21 | API_ENTRYPOINT $e97d
22 | Pixel2NamConv:
23 | ; right shift X 3 times to divide by 8 (each NT byte represents an 8x8 area)
24 | LDA $03
25 | LSR A
26 | LSR A
27 | LSR A
28 | STA $01
29 |
30 | ; Set the high part of the address to $20 but shifted right twice, so that
31 | ; it will be shifted left twice later
32 | LDA #$08
33 | STA $00
34 |
35 | ; Every 8 pixels of Y represents 32 bytes of NT, so instead of dividing by
36 | ; 8 and then multiplying by 32, just multiply by 4 and mask the rest
37 | LDA $02
38 | ASL A
39 | ROL $00
40 | ASL A
41 | ROL $00
42 | AND #%11100000
43 | ; carry is guaranteed to be clear - $00 was 8 before ROL touched it
44 | ADC $01
45 | STA $01
46 |
47 | RTS
48 |
49 | ; Convert a nametable address to corresponding pixel coordinates (assume no
50 | ; scrolling).
51 | ; Parameters: $00 = High nametable address, $01 = low nametable address
52 | ; Returns: $03 = Pixel X cord, $02 = Pixel Y cord
53 | ; Affects: A
54 | API_ENTRYPOINT $e997
55 | Nam2PixelConv:
56 | LDA $01
57 | ; three left shifts doesn't just multiply by 8, it also shifts out the Y
58 | ; coordinate portion of the address
59 | ASL A
60 | ASL A
61 | ASL A
62 | STA $03
63 |
64 | ; the top three bits of the low address are part of the Y coordinate
65 | LDA $01
66 | AND #%11100000
67 | STA $02
68 | LDA $00
69 | ; The two lowest bits of the high address are the two high bits of the Y
70 | ; coordinate. Shift right to get those bits into the carry flag, and
71 | ; rotate the carry flag into the Y coordinate
72 | LSR A
73 | ROR $02
74 | LSR A
75 | ROR $02
76 |
77 | RTS
78 |
--------------------------------------------------------------------------------
/nmi.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Bits 6 and 7 of NMI_ACTION choose which of the disk's 3 NMI vectors to use,
17 | ; or '0' to indicate the use of VINTWait. The three vectors are stored in
18 | ; order starting at $DFF6. Set to $C0 on reset, aka NMI vector 3.
19 | NMI_ACTION EQU $0100
20 | NMI_VEC1 EQU $DFF6
21 | NMI_VEC2 EQU $DFF8
22 | NMI_VEC3 EQU $DFFA
23 |
24 | ; The BIOS NMI provides programs the choice of up to 3 user-selectable NMI
25 | ; vectors, or using VINTWait (the "everything in main" way.) If VINTWait
26 | ; was used, then the BIOS NMI does the following:
27 | ; * Clears the VBlank flag in $2002.
28 | ; * Disables future NMIs, assuming that the program will just call VINTWait
29 | ; again.
30 | ; * Manipulates the stack to make the NMI behave as a subroutine call instead
31 | ; of an ISR.
32 | ; * Pulls the NMI vector selection and accumulator from the stack, then RTS
33 | ; to return to the caller of VINTWait.
34 | ; Affects: A
35 | API_ENTRYPOINT $e18b
36 | NMI:
37 | BIT NMI_ACTION
38 | BMI @vecs2and3
39 | BVS @vec1
40 |
41 | ; value was 0, assuming VINTWait was used
42 | LDA PPUSTATUS ; clear the VBlank flag
43 | LDA ZP_PPUCTRL
44 | AND #%01111111 ; clear NMI enable flag
45 | STA PPUCTRL
46 | STA ZP_PPUCTRL
47 | PLA ; discard the saved processor flags
48 | PLA ; discard the return address pushed onto the stack by the interrupt
49 | PLA ; (presumably it pointed to the infinite loop in VINTWait)
50 | PLA ; restore NMI_ACTION
51 | STA NMI_ACTION
52 | PLA ; restore VINTWait's caller's accumulator
53 | RTS ; return to VINTWait's caller
54 |
55 | @vec1:
56 | JMP (NMI_VEC1)
57 | @vecs2and3:
58 | BVS @vec3
59 | JMP (NMI_VEC2)
60 | @vec3:
61 | JMP (NMI_VEC3)
62 |
63 | ; Spin until next VBlank NMI fires, for programs that do it the "everything in
64 | ; main" way. The accumulator and the NMI vector selection at $100 are saved to
65 | ; the stack, and further VBlanks are disabled.
66 | ; Affects: $FF
67 | API_ENTRYPOINT $e1b2
68 | VINTWait:
69 | PHA ; save current A
70 | LDA NMI_ACTION ; get current NMI selection
71 | PHA ; and save it on the stack
72 | LDA #0
73 | STA NMI_ACTION ; tell the BIOS to disable NMIs
74 | LDA ZP_PPUCTRL ; but make sure the next NMI happens
75 | ORA #%10000000 ; so the spinning stops when the NMI fires
76 | STA PPUCTRL
77 | STA ZP_PPUCTRL
78 | @spin:
79 | BNE @spin ; now nothing left to do but wait
80 |
--------------------------------------------------------------------------------
/ppumask.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Disable sprites and playfield, affects A, $FE
17 | API_ENTRYPOINT $e161
18 | DisPFObj:
19 | LDA ZP_PPUMASK ; Get the existing value
20 | AND #%11100111 ; clear bits 3 and 4
21 | WritePPUMask:
22 | STA ZP_PPUMASK ; Write it to RAM
23 | STA PPUMASK ; Write it to PPUMASK
24 | RTS
25 |
26 | ; Enable sprites and playfield, affects A, $FE
27 | API_ENTRYPOINT $e16b
28 | EnPFObj:
29 | LDA ZP_PPUMASK ; Get the existing value
30 | ORA #%00011000 ; set bits 3 and 4
31 | ; re-use DisPFObj's implementation of writing the registers to save bytes
32 | ; only six bytes allowed in this function, and JMP would make it 7.
33 | ; use BNE to jump backwards because Z is guaranteed to be 0 (from the
34 | ; non-zero ORA immediate) and that's only 2 bytes
35 | BNE WritePPUMask
36 |
37 | ; Disable sprites, affects A, $FE
38 | API_ENTRYPOINT $e171
39 | DisObj:
40 | LDA ZP_PPUMASK ; Get the existing value
41 | AND #%11101111 ; clear bit 4
42 | ; Can't trust BNE like EnPFObj, because the result of the preceding AND
43 | ; could be 0. However, we have 7 bytes to play with, so JMP works fine.
44 | JMP WritePPUMask
45 |
46 | ; Enable sprites, affects A, $FE
47 | API_ENTRYPOINT $e178
48 | EnObj:
49 | LDA ZP_PPUMASK ; Get the existing value
50 | ORA #%00010000 ; set bit 4
51 | ; Branch to DisPFObj's implementation of writing the result to save bytes
52 | BNE WritePPUMask
53 |
54 | ; Disable playfield, affects A, $FE
55 | API_ENTRYPOINT $e17e
56 | DisPF:
57 | LDA ZP_PPUMASK ; Get the existing value
58 | AND #%11110111 ; clear bit 3
59 | ; Can't trust BNE like EnPFObj, because the result of the preceding AND
60 | ; could be 0. However, we have 7 bytes to play with, so JMP works fine.
61 | JMP WritePPUMask
62 |
63 | ; Enable playfield, affects A, $FE
64 | API_ENTRYPOINT $e185
65 | EnPF:
66 | LDA ZP_PPUMASK ; Get the existing value
67 | ORA #%00001000 ; set bit 3
68 | ; Branch to DisPFObj's implementation of writing the result to save bytes
69 | BNE WritePPUMask
70 |
--------------------------------------------------------------------------------
/random.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Shift-register based random number generator, normally takes 2 bytes (using
17 | ; more won't affect random sequence). On reset the program is supposed to
18 | ; write some non-zero values here (BIOS uses writes $d0, $d0), and call this
19 | ; routine several times before the data is actually random. Each call of this
20 | ; routine will shift the bytes right.
21 | ;
22 | ; Algorithm:
23 | ; 1. exclusive-OR the bit 1s of [X] and [X+1].
24 | ; 2. Rotate [X] to the right, rotating a 1 into the MSB if step 1 was non-zero.
25 | ; 3. Rotate each of the remaining bytes [X+1] ... [X+(Y-1)] to the right,
26 | ; carrying the LSB of the previous byte into the MSB of the next.
27 | ;
28 | ; Parameters: X = Zero Page address where the random bytes are placed
29 | ; Y = # of shift register bytes (normally 2)
30 | ; Affects: A, X, Y, $00
31 | API_ENTRYPOINT $e9b1
32 | Random:
33 | LDA $00,X
34 | AND #$02
35 | STA $00
36 | LDA $01,X
37 | AND #$02
38 | EOR $00
39 | ; rotate a 1 into the first byte if the XOR was non-zero
40 | SEC
41 | BNE @shifter
42 | ; it was 0, so rotate a 0 into the first byte
43 | CLC
44 | @shifter:
45 | ROR $00,X
46 | INX
47 | DEY
48 | BNE @shifter
49 | RTS
50 |
--------------------------------------------------------------------------------
/reset.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ;[$0102]/[$0103]: PC action on reset
17 | ;($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)
18 | RESET_ACTION_1 EQU $0102
19 | RESET_ACTION_2 EQU $0103
20 | DISK_RESET_VEC EQU $DFFC
21 |
22 |
23 | RESET:
24 | SEI ; disable interrupts
25 | CLD ; clear decimal mode flag, which doesn't work on the 2A03 anyway
26 |
27 | ; don't allow NMIs until we're ready
28 | LDY #$00
29 | STY ZP_PPUCTRL
30 | STY PPUCTRL
31 |
32 | ; disable rendering, but enable left 8 pixels
33 | LDA #$06
34 | STA ZP_PPUMASK
35 | STA PPUMASK
36 |
37 | ; disable disk I/O and IRQs
38 | STY IRQCTRL
39 | STY MASTERIO
40 |
41 | ; the PPU takes a while to warm up. Wait for at least 2 VBlanks before
42 | ; trying to set the scroll registers
43 | LDX #2
44 | @waitForVBL:
45 | LDA PPUSTATUS
46 | BPL @waitForVBL
47 | DEX
48 | BNE @waitForVBL
49 |
50 | ; clear the scroll registers
51 | LDA #0
52 | STA ZP_PPUSCROLL1
53 | STA PPUSCROLL
54 | STA ZP_PPUSCROLL2
55 | STA PPUSCROLL
56 |
57 | ; nothing has been written to the joypads or expansion port
58 | STA ZP_JOYPAD1
59 |
60 | LDA #$2E
61 | STA ZP_FDSCTRL
62 | STA FDSCTRL
63 |
64 | LDA #$FF
65 | STA ZP_EXTCONN
66 |
67 | ; ready, now we can turn on NMIs
68 | LDA #$80
69 | STA ZP_PPUCTRL
70 | STA PPUCTRL
71 |
72 | ; prepare the VRAM buffer
73 | LDA #$7D ; initial write buffer found at $302-$37F
74 | STA $300
75 | LDA #0
76 | STA $301
77 | LDA #$80 ; "end" opcode for the write buffer
78 | STA $302
79 |
--------------------------------------------------------------------------------
/vrambuffers.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Write buffers from the VRAM Write Buffer, starting at $302, to VRAM. Each
17 | ; buffer is described and preceded by a 3 byte header.
18 | ; The first two bytes specify the destination VRAM address.
19 | ; - To mark the end of the list of buffers, put a value >= $80 instead in the
20 | ; first byte.
21 | ; - The original version checks if each structure's address STARTS in the
22 | ; palette (addr >= $3F00), then it sets PPUADDR to $3F00 followed by $0000
23 | ; after copying that structure. It appears to be an attempt to prevent the
24 | ; PPU from drawing the colors of the palette to the screen, AKA, the
25 | ; "background palette hack," which occurs whenever rendering is off and the
26 | ; PPUADDR is somewhere in the palette. (FWIW, rendering is likely to be off
27 | ; when this function is called.)
28 | ; To reliably prevent the palette from being drawn to the screen, what this
29 | ; implementation does instead is to check whether the address of just the
30 | ; last structure PLUS its length is in the $3F00-$3FFF area (or the mirror at
31 | ; $7F00-$7FFF).
32 | ; The third byte contains the length of the buffer.
33 | ; - There's no check for 0-length buffers. Specifying a length of 0 will cause
34 | ; a buffer overflow.
35 | ; Before returning, the write buffer is cleared (0 written to $301, $80 written
36 | ; to $302.)
37 | ; Affects: A, X, Y, $02, $03, $04, $301, $302
38 | API_ENTRYPOINT $e86a
39 | WriteVRAMBuffers:
40 | LDA ZP_PPUCTRL
41 | AND #%11111011 ; Address increment of 1
42 | STA PPUCTRL
43 | STA ZP_PPUCTRL
44 | LDA PPUSTATUS ; clear PPUADDR latch
45 | LDY #0 ; Start at the beginning of $302
46 | @structure:
47 | LDA $302,Y ; load high byte of destination PPU Address
48 | BMI @done ; an "opcode" of $80 or more marks the end of the list
49 | STA $03 ; save the address (little endian) so we can check later for PPUADDR in the palette
50 | STA PPUADDR ; set the high byte of the initial address
51 | INY
52 | LDA $302,Y ; load low byte of destination PPU address
53 | STA $02 ; save the low byte of the address (little endian)
54 | STA PPUADDR ; set the low byte of the initial address
55 | INY
56 | LDX $302,Y ; load buffer length from third byte
57 | STX $04 ; save the length so we can check later for PPUADDR in the palette
58 | INY
59 | @loop:
60 | LDA $302,Y
61 | STA PPUDATA
62 | INY
63 | DEX
64 | BNE @loop
65 | BEQ @structure ; this structure is done, so move onto the next one
66 | @done:
67 | STA $302 ; write the $80 "opcode" to the first structure's address to clear the list
68 | LDA #0 ; clear the write buffer, i.e., move the read buffer to the beginning
69 | STA $301
70 | ; Finally, clear PPUADDR if it's currently in the palette
71 | JSR PreventPalettePpuAddr
72 | RTS
73 |
74 | ; Read individual bytes from VRAM to the VRAMBuffer. Each byte in the buffer is
75 | ; preceded by the source address - (1|32) in VRAM; in other words, it takes 3
76 | ; bytes of space in the VRAM Buffer for every byte to read, and the byte
77 | ; written to the buffer comes from 1 byte or 32 bytes LATER than the specified
78 | ; address, as this function does NOT change the address increment mode.
79 | ; Affects A, X, Y
80 | ; Parameters: X = start address of read buffer, Y = # of bytes to read
81 | API_ENTRYPOINT $e8b3
82 | ReadIndividualVRAMBytes:
83 | LDA PPUSTATUS ; clear PPUADDR latch
84 | @loop:
85 | LDA $300,X ; high address byte
86 | STA PPUADDR
87 | INX
88 | LDA $300,X ; load low address byte
89 | STA PPUADDR
90 | INX
91 | LDA PPUDATA ; read (and throw away) buffered byte from VRAM
92 | LDA PPUDATA ; read buffered byte from VRAM
93 | STA $300,X
94 | INX
95 | DEY
96 | BNE @loop
97 | RTS
98 |
--------------------------------------------------------------------------------
/vramfill.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; memset for VRAM.
17 | ; If A < $20, it fills pattern table data with the value in X for 16 * Y tiles,
18 | ; or Y * 256 bytes.
19 | ; If A >= $20, it fills the corresponding nametable with the value in X and
20 | ; attribute table with the value in Y.
21 | ; Parameters: A = High VRAM Address (aka tile row #), X = Fill value, Y = # of tile rows OR attribute fill data
22 | ; Affects: A, $00, $01, $02, $FF (aka PPUCTRL)
23 | API_ENTRYPOINT $ea84
24 | VRAMFill:
25 | STY $02 ; preserve Y so it can be restored later
26 | ; every write to PPUADDR needs to ensure that the PPUADDR latch is clear
27 | ; one nice feature of TOP is that it reads without clobbering anything
28 | TOP PPUSTATUS
29 | ; set the address
30 | STA PPUADDR
31 | LDA #0
32 | STA PPUADDR
33 | ; set the correct VRAM increment mode of 1
34 | LDA ZP_PPUCTRL
35 | AND #%11111011
36 | STA PPUCTRL
37 | STA ZP_PPUCTRL
38 | ; pattern or nametable fill?
39 | CMP #$20
40 | BCS @VramNametableFill
41 | @VramPatternFill:
42 | CLC
43 | ; The address has been loaded, so now we can use A as a counter
44 | ; 16 tiles in a row, 16 bytes per tile = 256 bytes to write per row
45 | LDA #0
46 | @tile:
47 | STX PPUDATA
48 | ADC #1
49 | BCC @tile
50 | DEY
51 | BNE @VramPatternFill
52 |
53 | ; restore Y (X never changed)
54 | LDY $02
55 | RTS
56 |
57 | @VramNametableFill:
58 | ; fill the nametable with X for 960 bytes, or 4 * 240 bytes
59 | STX $01 ; preserve X so it can be restored later
60 | TXA
61 | LDX #4
62 | @quarterNT
63 | LDY #240
64 | @name:
65 | STA PPUDATA
66 | DEY
67 | BNE @name
68 | DEX
69 | BNE @quarterNT
70 | ; restore the attr file value in Y
71 | LDY $02
72 | ; then fill the attributes with Y for 64 bytes
73 | LDX #64
74 | @attr:
75 | STY PPUDATA
76 | DEX
77 | BNE @attr
78 |
79 | ; restore X (Y is already restored)
80 | LDX $01
81 | RTS
82 |
--------------------------------------------------------------------------------
/vramstrings.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Copy pointed data into the VRAM buffer.
17 | ; Parameters: A = High VRAM address, X = Low VRAM address, Y = string length, Direct Pointer = data to be written to VRAM
18 | ; Returns: A = $ff : no error, A = $01 : string didn't fit in buffer
19 | ; Affects: A, X, Y, $00, $01, $02, $03, $04, $05, $06
20 | API_ENTRYPOINT $e8d2
21 | PrepareVRAMString:
22 | STA $02
23 | STX $03
24 | STY $05
25 | JSR FetchDirectPtr
26 | LDY #0
27 | LDA #1
28 | BEQ PrepareVRAMStringsGeneric
29 |
30 | ; Copy a 2D string into the VRAM buffer. The first byte of the data determines
31 | ; the width and height of the following string (in tiles):
32 | ; Upper nybble = height, lower nybble = width.
33 | ; Parameters: A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM
34 | ; Returns: A = $ff : no error, A = $01 : data didn't fit in buffer
35 | ; Affects: A, X, Y, $00, $01, $02, $03, $04, $05, $06
36 | API_ENTRYPOINT $e8e1
37 | PrepareVRAMStrings:
38 | STA $02
39 | STX $03
40 | JSR FetchDirectPtr
41 | LDY #0
42 | LDA #$0F ; width
43 | AND ($00),Y
44 | STA $05
45 | LDA ($00),Y ; get the first byte of the data, which indicates the width and height
46 | INY ; Y is the read index
47 | LSR ; Shift the upper nibble 4 bits into the lower nibble
48 | LSR
49 | LSR
50 | LSR
51 | ; for (row = $04; row > 0; row--)
52 | ; $04 holds # of rows to go
53 | PrepareVRAMStringsGeneric:
54 | STA $04 ; store the height
55 | LDX $301 ; X is the write index
56 | @buffer:
57 | ; write destination address (BIG ENDIAN)
58 | LDA $02
59 | STA $302,X
60 | JSR IncrementWriteIndex
61 | LDA $03
62 | STA $302,X
63 | JSR IncrementWriteIndex
64 | CLC
65 | ADC #32; the next string should write to the next row
66 | STA $03
67 | BCC @writeLength
68 | INC $02
69 | @writeLength:
70 | LDA $05
71 | STA $302,X
72 | JSR IncrementWriteIndex
73 | STA $06
74 | @writeString:
75 | LDA ($00),Y
76 | INY
77 | STA $302,X
78 | JSR IncrementWriteIndex
79 | DEC $06
80 | BNE @writeString ; reached the end of this string?
81 | DEC $04
82 | BNE @buffer ; reached the end of the strings?
83 | LDA #$FF ; no error
84 | STA $302,X ; write end opcode to write buffer
85 | STX $301 ; update write buffer end index
86 | RTS
87 |
88 | ; Increment the write position in X and check if that would overflow the buffer
89 | IncrementWriteIndex:
90 | INX
91 | CPX $300
92 | BCC @done
93 | ; uh oh, out of space! Pop the return address pointing to
94 | ; PrepareVRAMStrings off the stack so that we return to PrepareVRAMStrings'
95 | ; caller instead.
96 | PLA
97 | PLA
98 | LDX $301 ; since it overflowed, put the "end" opcode back where we started
99 | LDA #$FF
100 | STA $302,X ; write end opcode to write buffer
101 | LDA #1 ; 1 is the error return code
102 | @done:
103 | RTS
104 |
--------------------------------------------------------------------------------
/vramstructwrite.asm:
--------------------------------------------------------------------------------
1 | ; FreeDiskSysROM
2 | ; Copyright (c) 2018 James Athey
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify it under
5 | ; the terms of the GNU Lesser General Public License version 3 as published by
6 | ; the Free Software Foundation.
7 | ;
8 | ; This program is distributed in the hope that it will be useful, but WITHOUT
9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 | ; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | ; details.
12 | ;
13 | ; You should have received a copy of the GNU Lesser General Public License
14 | ; along with this program. If not, see .
15 |
16 | ; Affects: A, X, Y, $00, $01, $02, $03, $04, $FF
17 | ; Parameters: Direct Pointer to VRAM buffer to be written
18 | API_ENTRYPOINT $e7bb
19 | VRAMStructWrite:
20 | LDA PPUSTATUS ; clear PPUADDR latch
21 | JSR FetchDirectPtr ; start of the buffer is now in ($00)
22 | @buffer:
23 | LDY #0
24 | @structure:
25 | LDA ($00),Y ; load high byte of destination PPU Address
26 | BMI @done ; >= $80 is the finish opcode
27 | CMP #$4C ; "call" opcode
28 | BEQ @call
29 | CMP #$60 ; "return" opcode
30 | BEQ @return
31 | STA $03 ; save the high byte of the address (little endian)
32 | STA PPUADDR ; set the high byte of the initial address
33 | INY
34 |
35 | LDA ($00),Y ; load low byte of destination PPU address
36 | STA $02 ; save the low byte of the address (little endian)
37 | STA PPUADDR ; set the low byte of the initial address
38 | INY
39 |
40 | LDX ($00),Y ; load buffer length and flags from third byte
41 | TXA
42 | AND #%00111111 ; if bits 0-5 are all zero (length of 0), then the length is really 64
43 | BNE @savelength
44 | LDA #64
45 | @savelength:
46 | STA $04 ; save the length so we can check later for PPUADDR in the palette
47 | INY
48 | TXA
49 | BMI @incr32 ; if bit 7 set, increment mode is 32
50 | LDA ZP_PPUCTRL
51 | AND #%11111011 ; Address increment of 1
52 | JMP @setincr
53 | @incr32:
54 | LDA ZP_PPUCTRL
55 | ORA #%00000100 ; Address increment of 32
56 | @setincr:
57 | STA PPUCTRL
58 | STA ZP_PPUCTRL
59 | TXA
60 | AND #%01000000 ; check the mode flag
61 | BEQ @copy
62 | ; must be fill then
63 |
64 | @fill:
65 | LDX $04
66 | LDA ($00),Y
67 | INY
68 | @fillloop:
69 | STA PPUDATA
70 | DEX
71 | BNE @fillloop
72 | BEQ @structure ; this structure is done, so move onto the next one
73 |
74 | @copy:
75 | LDX $04
76 | @copyloop:
77 | LDA ($00),Y
78 | STA PPUDATA
79 | INY
80 | DEX
81 | BNE @copyloop
82 | BEQ @structure ; this structure is done, so move onto the next one
83 |
84 | @call:
85 | ; ($00) will be overwritten and Y will be cleared, so they need to be preserved
86 | LDA $01
87 | PHA ; preserve the high address byte on the stack
88 | LDA $00
89 | PHA ; preserve the low address byte on the stack
90 | INY
91 | LDA ($00),Y ; get low address byte of substructure
92 | TAX ; hold onto that low byte
93 | INY
94 | LDA ($00),Y ; get high address byte of substructure
95 | INY
96 | STX $00 ; write the address of the substructure
97 | STA $01
98 | TYA
99 | PHA ; push the Y index onto the stack
100 | BNE @buffer
101 |
102 | @return:
103 | PLA ; restore the Y index
104 | TAY
105 | PLA ; restore the low address byte
106 | STA $00
107 | PLA ; restore the high address byte
108 | STA $01
109 | BNE @structure
110 |
111 | @done:
112 | JSR PreventPalettePpuAddr
113 | RTS
114 |
--------------------------------------------------------------------------------