1
;---------------------------------------------------------------------------------------------------------
2
; Source code converted by MSXBAS2ASM - MSX BASIC TO Z80 ASSEMBLY CONVERTER
3
; MSXBAS2ASM developed by Amaury Carvalho, 2019, Brazil
4
; http://launchpad.net/msxbas2asm
5
;---------------------------------------------------------------------------------------------------------
7
;--------------------------------------------------------
8
; MSX BIOS DATA/FUNCTION POINTERS
9
;--------------------------------------------------------
11
;---------------------------------------------------------------------------------------------------------
13
;---------------------------------------------------------------------------------------------------------
15
BIOS_CALBAS: equ 0x0159
16
BIOS_OUTDO: equ 0x0018 ; output to current device (i.e. screen)
17
BIOS_CHPUT: equ 0x00A2
19
BIOS_POSIT: equ 0x00C6
21
BIOS_CHGET: equ 0x009F
22
BIOS_CHSNS: equ 0x009C
23
BIOS_INLIN: equ 0x00B1
24
BIOS_PINLIN: equ 0x00AE
25
BIOS_QINLIN: equ 0x00B4
26
BIOS_GTSTCK: equ 0x00D5
27
BIOS_GTTRIG: equ 0x00D8
28
BIOS_GTPAD: equ 0x00DB
29
BIOS_GTPDL: equ 0x00DE
30
BIOS_DISSCR: equ 0x0041
31
BIOS_ENASCR: equ 0x0044
32
BIOS_CHGMOD: equ 0x005F
33
BIOS_CHGCLR: equ 0x0062
34
BIOS_CLRSPR: equ 0x0069
35
BIOS_INITXT: equ 0x006C ; init text mode 40 columns
36
BIOS_INIT32: equ 0x006F ; init text mode 32 columns
37
BIOS_INIGRP: equ 0x0072
38
BIOS_INIMLT: equ 0x0075
39
BIOS_SETTXT: equ 0x0078 ; set text mode 40 columns
40
BIOS_SETT32: equ 0x007B ; set text mode 32 columns
41
BIOS_SETGRP: equ 0x007E
42
BIOS_SETMLT: equ 0x0081
43
BIOS_CALPAT: equ 0x0084
44
BIOS_CALATR: equ 0x0087
45
BIOS_GSPSIZ: equ 0x008A
46
BIOS_GRPPRT: equ 0x008D
47
BIOS_ERAFNK: equ 0x00CC
48
BIOS_DSPFNK: equ 0x00CF
49
BIOS_TOTEXT: equ 0x00D2
50
BIOS_BREAKX: equ 0x00B7
51
BIOS_ISCNTC: equ 0x03FB
52
BIOS_CHKRAM: equ 0x0000
53
BIOS_GICINI: equ 0x0090
54
BIOS_WRTPSG: equ 0x0093
55
BIOS_REDPSG: equ 0x0096
56
BIOS_STRTMS: equ 0x0099
57
BIOS_KEYINT: equ 0x0038
58
BIOS_CALSLT: equ 0x001C
59
BIOS_ENASLT: equ 0x0024
60
BIOS_RSLREG: equ 0x0138
61
BIOS_SCALXY: equ 0x010E
62
BIOS_MAPXYC: equ 0x0111 ; in BC = X, DE = Y
63
BIOS_READC: equ 0x011D ; out A = color of current pixel
64
BIOS_SETATR: equ 0x011A ; in A = color code
65
BIOS_SETC: equ 0x0120 ; set current pixel to color from SETATR
66
BIOS_NSETCX: equ 0x0123 ; in HL = pixel fill count
67
BIOS_SCANR: equ 0x012C ; in B=Fill switch, DE=Skip count, out DE=Skip remainder, HL=Pixel count
68
BIOS_SCANL: equ 0x012F ; out HL=Pixel count
69
BIOS_FETCHC: equ 0x0114 ; out A = cursor mask, HL = VRAM address of cursor
70
BIOS_STOREC: equ 0x0117 ; in A = cursor mask, HL = VRAM address of cursor
71
BIOS_RESET: equ 0x7D17 ; restart BASIC
72
BIOS_IOALLOC: equ 0X7e6b ; memory setup
74
BIOS_GETVCP: equ 0x0150 ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
75
BIOS_GETVC2: equ 0x0153 ; get PSG voice buffer address (VOICEN = voice number, in L = byte number 0-36, out HL = address)
77
BIOS_CHPUT_LF: equ 0x0908
78
BIOS_CHPUT_CR: equ 0x0A81
79
BIOS_CHPUT_TAB: equ 0x0A71
82
BIOS_CHKNEW: equ 0x0165 ; C-flag set if screenmode = 5, 6, 7 or 8
83
BIOS_EXTROM: equ 0x015F
84
BIOS_SCALXY2: equ 0x008D ; in BC = X, DE = Y
85
BIOS_MAPXYC2: equ 0x0091 ; in BC = X, DE = Y
86
BIOS_SETC2: equ 0x009D ; set current pixel to color from SETATR
87
BIOS_READC2: equ 0x0095 ; out A = color of current pixel
88
BIOS_CHGMOD2: equ 0x00D1 ; in A = screenmode
89
BIOS_DOBOXF: equ 0x0079 ; hl = basic text pointer
90
BIOS_GRPPRT2: equ 0x0089 ; a = character
91
BIOS_CHGCLR2: equ 0x0111 ; change color, a = screen mode
92
BIOS_CALPAT2: equ 0x00F9
93
BIOS_CALATR2: equ 0x00FD
94
BIOS_GSPSIZ2: equ 0x0101
95
BIOS_CLRSPR2: equ 0x00F5
97
;---------------------------------------------------------------------------------------------------------
99
;---------------------------------------------------------------------------------------------------------
101
BIOS_VERSION: equ 0x002D ; 0 = MSX1, 1 = MSX2, 2 = MSX2+, 3 = MSXturboR
102
BIOS_FORCLR: equ 0xF3E9
103
BIOS_BAKCLR: equ 0xF3EA
104
BIOS_BDRCLR: equ 0xF3EB
105
BIOS_ATRBYT: equ 0xF3F2
106
BIOS_INTFLG: equ 0xFC9B
107
BIOS_EXPTBL: equ 0xFCC1
108
BIOS_JIFFY: equ 0xFC9E
109
BIOS_BOTTOM: equ 0xFC48
110
BIOS_HIMEM: equ 0xFC4A
111
BIOS_SCRMOD: equ 0xFCAF ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode.
112
BIOS_CLIKSW: equ 0xF3DB ; 0=keyboard click off, 1=keyboard click on
113
BIOS_GRPACX: equ 0xFCB7
114
BIOS_GRPACY: equ 0xFCB9
115
BIOS_DATLIN: equ 0xF6A3 ; 2 - line number of DATA statement read by READ statement
116
BIOS_DATPTR: equ 0xF6C8 ; 2 - address of data read by executing READ statement
117
BIOS_FLGINP: equ 0xF6A6 ; 1 - flag used in INPUT or READ
118
BIOS_TEMP: equ 0xF6A7 ; 2
119
BIOS_TEMP2: equ 0xF6BC ; 2
120
BIOS_TEMP3: equ 0xF69D ; 2
121
BIOS_TEMP8: equ 0xF69F ; 2
122
BIOS_TEMP9: equ 0xF7B8 ; 2
123
BIOS_OLDSCR: equ 0xFCB0 ; screen mode of the last text mode set
124
BIOS_LINL40: equ 0xF3AE ; width for 40 columns screen mode
125
BIOS_LINL32: equ 0xF3AF ; width for 32 columns screen mode
126
BIOS_LINLEN: equ 0xF3B0 ; current width for text screen mode
127
BIOS_CLMLST: equ 0xF3B2 ; minimum number of columns that must still be available on a line for a CRLF
128
BIOS_TXTNAM: equ 0xF3B3 ; characters table name
130
BIOS_VOICEN: equ 0xFB38 ; PSG voice number
131
BIOS_MCLTAB: equ 0xF956
132
BIOS_PRSCNT: equ 0xFB35
133
BIOS_SAVSP: equ 0xFB36
134
BIOS_QUEUEN: equ 0xFB3E
135
BIOS_MUSICF: equ 0xFB3F ;contains 3 bit flags set by the STRTMS. Bits 0, 1 and 2 correspond to VOICAQ, VOICBQ and VOICCQ.
136
BIOS_PLYCNT: equ 0xFB40
138
BIOS_DRWFLG: equ 0xFCBB
139
BIOS_MCLFLG: equ 0xF958
141
BIOS_SLTROM: equ 0xFCC1
142
BIOS_RAMAD0: equ 0xF341 ; Main-RAM Slot (00000h~03FFFh)
143
BIOS_RAMAD1: equ 0xF342 ; Main-RAM Slot (04000h~07FFFh)
144
BIOS_RAMAD2: equ 0xF343 ; Main-RAM Slot (08000h~0BFFFh)
145
BIOS_RAMAD3: equ 0xF344 ; Main-RAM Slot (0C000h~0FFFFh)
149
;--------------------------------------------------------
150
; MSX BASIC DATA/FUNCTION POINTERS
151
;--------------------------------------------------------
153
;---------------------------------------------------------------------------------------------------------
154
; MSX BASIC FUNCTIONS
155
;---------------------------------------------------------------------------------------------------------
157
BASIC_AUTO: equ 0x3973
158
BASIC_AND: equ 0x3A18
159
BASIC_ATTR: equ 0x39FE
160
BASIC_BASE: equ 0x39BE
161
BASIC_BSAVE: equ 0x39CC
162
BASIC_BLOAD: equ 0x39CA
163
BASIC_BEEP: equ 0x39AC
164
BASIC_CALL: equ 0x39C0
165
BASIC_CLOSE: equ 0x3994
166
BASIC_COPY: equ 0x39D8
167
BASIC_CONT: equ 0x395E
168
BASIC_CLEAR: equ 0x3950
169
BASIC_CLOAD: equ 0x3962
170
BASIC_CSAVE: equ 0x3960
171
BASIC_CSRLIN: equ 0x39FC
172
BASIC_CIRCLE: equ 0x39A4
173
BASIC_COLOR: equ 0x39A6
174
BASIC_CLS: equ 0x396A
175
BASIC_CMD: equ 0x39DA
176
BASIC_DELETE: equ 0x397C
177
BASIC_DATA: equ 0x3934
178
BASIC_DIM: equ 0x3938
179
BASIC_DEFSTR: equ 0x3982
180
BASIC_DEFINT: equ 0x3984
181
BASIC_DEFSNG: equ 0x3986
182
BASIC_DEFDBL: equ 0x3988
183
BASIC_DSKO: equ 0x39CE
184
BASIC_DEF: equ 0x395A
185
BASIC_DSKI: equ 0x3A00
186
BASIC_DRAW: equ 0x39A8
187
BASIC_ELSE: equ 0x396E
188
BASIC_END: equ 0x392E
189
BASIC_ERASE: equ 0x3976
190
BASIC_ERROR: equ 0x3978
191
BASIC_ERL: equ 0x39EE
192
BASIC_ERR: equ 0x39F0
193
BASIC_EQU: equ 0x3A1E
194
BASIC_FOR: equ 0x3920
195
BASIC_FIELD: equ 0x398E
196
BASIC_FILES: equ 0x39AA
198
BASIC_GOTO: equ 0x393E
199
BASIC_GOSUB: equ 0x3948
200
BASIC_GET: equ 0x3990
201
BASIC_INPUT: equ 0x3936
203
BASIC_INSTR: equ 0x39F6
204
BASIC_IMP: equ 0x3A20
205
BASIC_INKEY: equ 0x3A04
206
BASIC_IPL: equ 0x39D6
207
BASIC_KILL: equ 0x39D4
208
BASIC_KEY: equ 0x3964
209
BASIC_LPRINT: equ 0x394C
210
BASIC_LLIST: equ 0x3968
211
BASIC_LET: equ 0x393C
212
BASIC_LOCATE: equ 0x39DC
213
BASIC_LINE: equ 0x398A
214
BASIC_LOAD: equ 0x3996
215
BASIC_LSET: equ 0x399C
216
BASIC_LIST: equ 0x3952
217
BASIC_LFILES: equ 0x39A2
218
BASIC_MOTOR: equ 0x39C8
219
BASIC_MERGE: equ 0x3998
220
BASIC_MOD: equ 0x3A22
221
BASIC_MAX: equ 0x39C6
222
BASIC_NEXT: equ 0x3932
223
BASIC_NAME: equ 0x39D2
224
BASIC_NEW: equ 0x3954
225
BASIC_NOT: equ 0x39EC
226
BASIC_OPEN: equ 0x398C
227
BASIC_OUT: equ 0x3964
230
BASIC_OFF: equ 0x3A02
231
BASIC_PRINT: equ 0x394E
232
BASIC_PUT: equ 0x3992
233
BASIC_POKE: equ 0x395C
234
BASIC_PSET: equ 0x39B0
235
BASIC_PRESET: equ 0x39B2
236
BASIC_POINT: equ 0x3A06
237
BASIC_PAINT: equ 0x39AA
238
BASIC_PLAY: equ 0x39AE
239
BASIC_RETURN: equ 0x3948
240
BASIC_READ: equ 0x393A
241
BASIC_RUN: equ 0x3940
242
BASIC_RESTORE:equ 0x3944
243
BASIC_REM: equ 0x394A
244
BASIC_RESUME: equ 0x397A
245
BASIC_RSET: equ 0x399E
246
BASIC_RENUM: equ 0x3980
247
BASIC_SCREEN: equ 0x39B6
248
BASIC_SPRITE: equ 0x39BA
249
BASIC_STOP: equ 0x394C
250
BASIC_SWAP: equ 0x3974
251
BASIC_SET: equ 0x39D0
252
BASIC_SAVE: equ 0x39A0
253
BASIC_SPC: equ 0x39EA
254
BASIC_STEP: equ 0x39E4
255
BASIC_STRING: equ 0x39F2
256
BASIC_SPACE1: equ 0x397E
257
BASIC_SOUND: equ 0x39B4
258
BASIC_THEN: equ 0x39E0
259
BASIC_TRON: equ 0x3970
260
BASIC_TROFF: equ 0x3972
261
BASIC_TAB: equ 0x39E2
263
BASIC_TIME: equ 0x39C2
264
BASIC_USING: equ 0x39F4
265
BASIC_USR: equ 0x39E6
266
BASIC_VARPTR: equ 0x39FA
267
BASIC_VDP: equ 0x39BC
268
BASIC_VPOKE: equ 0x39B8
269
BASIC_WIDTH: equ 0x396C
270
BASIC_WAIT: equ 0x3958
271
BASIC_XOR: equ 0x3A1C
272
BASIC_ABS: equ 0x39E8
273
BASIC_ATN: equ 0x39F8
274
BASIC_ASC: equ 0x3A06
275
BASIC_BIN: equ 0x3A16
276
BASIC_CINT: equ 0x3A18
277
BASIC_CSNG: equ 0x3A1A
278
BASIC_CDBL: equ 0x3A1C
279
BASIC_CVI: equ 0x3A2C
280
BASIC_CVS: equ 0x3A2E
281
BASIC_CVD: equ 0x3A30
282
BASIC_COS: equ 0x39F4
283
BASIC_CHR: equ 0x3A08
284
BASIC_DSKF: equ 0x3A28
285
BASIC_EXP: equ 0x39F2
286
BASIC_EOF: equ 0x3A32
287
BASIC_FRE: equ 0x39FA
288
BASIC_FIX: equ 0x3A1E
289
BASIC_FPOS: equ 0x3A2A
290
BASIC_HEX: equ 0x3A12
291
BASIC_INT: equ 0x39E6
292
BASIC_INP: equ 0x39FC
293
BASIC_LPOS: equ 0x3A14
294
BASIC_LOG: equ 0x39F0
295
BASIC_LOC: equ 0x3A34
296
BASIC_LEN: equ 0x3A00
297
BASIC_LEFT: equ 0x39DE
298
BASIC_LOF: equ 0x3A36
299
BASIC_MKI: equ 0x3A38
300
BASIC_MKS: equ 0x3A3A
301
BASIC_MKD: equ 0x3A3C
302
BASIC_MID: equ 0x39E2
303
BASIC_OCT: equ 0x3A10
304
BASIC_POS: equ 0x39FE
305
BASIC_PEEK: equ 0x3A0A
306
BASIC_PDL: equ 0x3A24
307
BASIC_PAD: equ 0x3A26
308
BASIC_RIGHT: equ 0x39E0
309
BASIC_RND: equ 0x39EC
310
BASIC_SGN: equ 0x39E4
311
BASIC_SQR: equ 0x39EA
312
BASIC_SIN: equ 0x39EE
313
BASIC_STR: equ 0x3A02
314
BASIC_SPACE2: equ 0x3A0E
315
BASIC_STICK: equ 0x3A20
316
BASIC_STRIG: equ 0x3A22
317
BASIC_TAN: equ 0x39F6
318
BASIC_VAL: equ 0x3A04
319
BASIC_VPEEK: equ 0x3A0C
321
BASIC_TRAP_ENABLE: equ 0x631B ; ON INTERVAL/KEY/SPRITE/STOP/STRIG - hl = pointer to trap block
322
BASIC_TRAP_DISABLE: equ 0x632B ; hl = pointer to trap block
323
BASIC_TRAP_ACKNW: equ 0x6358 ; hl, acknowledge trap (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
324
BASIC_TRAP_PAUSE: equ 0x6331 ; hl
325
BASIC_TRAP_UNPAUSE: equ 0x633E ; hl
326
BASIC_TRAP_CLEAR: equ 0x636E
328
BASIC_PLAY_DIRECT: equ 0x744C
329
BASIC_DRAW_DIRECT: equ 0x568C
331
BASIC_READYR: equ 0x409B
332
BASIC_READYC: equ 0x7D17
333
BASIC_FACEVAL: equ 0x4DC7
335
BASIC_ERROR_HANDLER:equ 0x406F
336
BASIC_ERROR_SYNTAX: equ 0x4055
337
BASIC_ERROR_DIVZER: equ 0x4058
338
BASIC_ERROR_OVRFLW: equ 0x4067
339
BASIC_ERROR_ARRAY: equ 0x405E
340
BASIC_ERROR_TYPMIS: equ 0x406D
342
; BASIC ERROR CODES TO BASIC_ERROR_HANDLER
343
; 01 NEXT without FOR 19 Device I/O error
344
; 02 Syntax error 20 Verify error
345
; 03 RETURN without GOSUB 21 No RESUME
346
; 04 Out of DATA 22 RESUME without error
347
; 05 Illegal function call 23 Unprintable error
348
; 06 Overflow 24 Missing operand
349
; 07 Out of memory 25 Line buffer overflow
350
; 08 Undefined line number 50 FIELD overflow
351
; 09 Subscript out of range 51 Internal error
352
; 10 Redimensioned array 52 Bad file number
353
; 11 Division by zero 53 File not found
354
; 12 Illegal direct 54 File already open
355
; 13 Type mismatch 55 Input past end
356
; 14 Out of string space 56 Bad file name
357
; 15 String too long 57 Direct statement in file
358
; 16 String formula too complex 58 Sequential I/O only
359
; 17 Can't CONTINUE 59 File not OPEN
360
; 18 Undefined user function
362
;---------------------------------------------------------------------------------------------------------
363
; MSX BASIC WORK AREAS
364
;---------------------------------------------------------------------------------------------------------
366
BASIC_DAC: equ 0xF7F6 ; 16
367
BASIC_ARG: equ 0xF847 ; 16
368
BASIC_VALTYP: equ 0xF663
369
BASIC_RNDX: equ 0xF857
370
BASIC_BUF: equ 0xF55E ; 259
371
BASIC_KBUF: equ 0xF41F ; 318
372
BASIC_SWPTMP: equ 0xF7BC ; 8
373
BASIC_STRBUF: equ 0xF7C5 ; 43
374
BASIC_TXTTAB: equ 0xF676
375
BASIC_VARTAB: equ 0xF6C2
376
BASIC_ARYTAB: equ 0xF6C4
377
BASIC_STREND: equ 0xF6C6
378
BASIC_STKTOP: equ 0xF674
379
BASIC_FRETOP: equ 0xF69B
380
BASIC_MEMSIZ: equ 0xF672
382
BASIC_TEMPPT: equ 0xF678 ; 2 Starting address of unused area of temporary descriptor.
383
BASIC_TEMPST: equ 0xF67A ; 30 Temporary descriptors.
385
BASIC_DATPTR: equ 0xF6C8 ; 2 Pointer to next data to read from the instruction DATA. Modified by RESTORE.
386
BASIC_DATLIN: equ 0xF6A3 ; 2 Número de linha do comando DATA para o comando READ.
387
BASIC_DORES: equ 0xF664 ; 1 Usada pelo comando DATA para manter o texto no formato ASCII.
388
BASIC_DEFTBL: equ 0xF6CA ; 26 table of variables defined by DEFINT, DEFSTR, DEFSNG and DEFDBL for each alphabet letter (2 = integer, 3 = String, 4 = Simple precision, 8 = Double precision).
390
BASIC_CURLIN: equ 0xF41C ; BASIC current line number
391
BASIC_INTVAL: equ 0xFCA0 ; interval value
392
BASIC_INTCNT: equ 0xFCA2 ; interval current count
394
BASIC_PRMPRV: equ 0xF74C ; Pointer to previous parameter block in PARM1
396
BASIC_TRPTBL: equ 0xFC4C ; 78 trap table - array of 3 bytes - state[1] (bit 0=on, bit 1=stop, bit 2=active) + address[2]
398
BASIC_TRPTBL_KEY: equ 0xFC4C ; 30 ON KEY GOSUB
399
BASIC_TRPTBL_STOP: equ 0xFC6A ; 3 ON STOP GOSUB
400
BASIC_TRPTBL_SPRITE: equ 0xFC6D ; 3 ON SPRITE GOSUB
401
BASIC_TRPTBL_STRIG: equ 0xFC70 ; 15 ON STRIG GOSUB
402
BASIC_TRPTBL_INTERVAL: equ 0xFC7F ; 3 ON INTERVAL GOSUB
403
BASIC_TRPTBL_OTHER: equ 0xFC82 ; 24 reserved for expansion
405
BASIC_ONGSBF: equ 0xFBD8 ; 1 trap occurred counter (0=not occurred)
409
;--------------------------------------------------------
411
;--------------------------------------------------------
413
;--------------------------------------------------------
415
;--------------------------------------------------------
416
COMPILE_TO_ROM: EQU 1
418
MACRO __call_basic,CALL_PARM
423
if defined COMPILE_TO_DOS
425
MACRO __call_bios,CALL_PARM
426
;ld iy,(BIOS_EXPTBL-1)
428
call BIOS_CALBAS ; BIOS_CALSLT
433
MACRO __call_bios,CALL_PARM
440
push hl ; save parameter
444
pop iy ; restore PC of caller
445
pop hl ; get next parameter
446
push iy ; save PC of caller
450
pop iy ; restore PC of caller
451
push hl ; save return parameter
452
push iy ; save PC of caller
456
pop iy ; restore PC of caller
457
push hl ; save return parameter
458
push iy ; save PC of caller
462
MACRO set.line.number, line_number
463
ld bc, line_number ; current line number
464
ld (BASIC_CURLIN), bc
468
ld a, (BIOS_INTFLG) ; verify CTRL+BREAK
474
ld a, (BASIC_ONGSBF) ; trap occured counter
480
;---------------------------------------------------------------------------------------------------------
482
;---------------------------------------------------------------------------------------------------------
484
romSize: equ 0x8000 ; ROM size (32k)
485
pageSize: equ 0x4000 ; Page size (16k)
486
lowLimitSize: equ 0x400 ; 10% of a page size
488
if defined COMPILE_TO_BIN
490
pgmArea: equ 0x8000 ; page 2 - program area
491
ramArea: equ 0xc000 ; page 3 - free RAM start area
493
org pgmArea ; program binary type start address
494
db 0FEh ; binary file ID
495
dw start_pgm ; begin address
496
dw end_file - 1 ; end address
497
dw start_pgm ; program execution address (for ,R option)
500
if defined COMPILE_TO_ROM
502
pgmArea: equ 0x4000 ; page 1 and 2 - program area
503
ramArea: equ 0xc000 ; page 3 - free RAM start area
505
org pgmArea ; program rom type start address
506
db 'AB' ; rom file ID
508
dw 0x0000 ; STATEMENT
515
pgmArea: equ 0x8000 ; page 2 - program area
516
ramArea: equ 0xc000 ; page 3 - free RAM start area
518
org pgmArea ; program DOS type start address ; 0x0100
524
;---------------------------------------------------------------------------------------------------------
526
;---------------------------------------------------------------------------------------------------------
528
if defined COMPILE_TO_ROM
531
call PROGRAM_SLOT_2_RESTORE
532
__call_basic BASIC_READYR ; warm start Basic
537
call PROGRAM_SLOT_GET
538
ld (BIOS_RAMAD2), a ; Save RAM slot of page 8000h-BFFFh
541
PROGRAM_SLOT_2_RESTORE:
544
jp BIOS_ENASLT ; Select the RAM on page 8000h-BFFFh
546
PROGRAM_SLOT_2_ENABLE:
548
call PROGRAM_SLOT_ENABLE_SUB
550
jp BIOS_ENASLT ; Select the ROM on page 8000h-BFFFh
552
PROGRAM_SLOT_1_ENABLE:
556
call PROGRAM_SLOT_ENABLE_SUB
558
jp BIOS_ENASLT ; Select the ROM on page 4000h-7FFFh
560
PROGRAM_SLOT_ENABLE_SUB:
563
and 3 ;Keep bits corresponding to the page
582
; a <- slot ID formatted FxxxSSPP
583
; Modifies: af, bc, de, hl
584
; ref: https://www.msx.org/forum/msx-talk/development/fusion-c-and-htimi#comment-366469
588
jr z,PrimaryShiftContinue
593
PrimaryShiftContinue:
595
jr z,PrimaryShiftDone
610
inc hl ; move to SLTTBL
617
jr z,SecondaryShiftContinue
622
SecondaryShiftContinue:
624
jr nz,SecondaryShiftDone
634
if defined COMPILE_TO_DOS
639
__call_bios BIOS_ENASLT ; Select main ROM on page 0 (0000h~3FFFh)
645
__call_bios BIOS_ENASLT ; Select main ROM on page 1 (4000h~7FFFh)
652
;---------------------------------------------------------------------------------------------------------
654
;---------------------------------------------------------------------------------------------------------
656
start_pgm: ; start of the program
658
if defined COMPILE_TO_DOS
660
call BIOS_SLOT_ENABLE ; enable bios on page 0
661
call BASIC_SLOT_ENABLE ; enable basic on page 1
664
if defined COMPILE_TO_ROM
666
call PROGRAM_SLOT_2_SAVE ; save slot on page 2
667
call PROGRAM_SLOT_2_ENABLE ; enable program on page 2
672
__call_bios BIOS_ERAFNK ; turn off function keys display
673
__call_bios BIOS_GICINI ; initialize sound system
674
__call_bios BIOS_INITXT ; initialize text screen
676
ld (BIOS_CLIKSW), a ; disable keyboard click
678
ld (BASIC_CURLIN), bc ; interpreter in direct mode
679
__call_basic BASIC_TRAP_CLEAR ; clear traps work space
680
;call INITIALIZE_PARAMETERS ; initialize parameters stack
681
call memory.init ; initialize memory allocation
682
call INITIALIZE_VARIABLES ; initialize variables
686
set.line.number 100 ; current line number
689
set.line.number 101 ; current line number
692
set.line.number 105 ; current line number
693
ld hl, LIT_9 ; parameter
695
call COLOR_BORDER ; action call
696
ld hl, LIT_7 ; parameter
698
call COLOR_BACKGROUND ; action call
699
ld hl, LIT_5 ; parameter
701
call COLOR_FOREGROUND ; action call
702
call COLOR ; action call
705
set.line.number 106 ; current line number
706
ld hl, LIT_11 ; parameter
708
call SCREEN ; action call
709
ld hl, LIT_13 ; parameter
711
call WIDTH ; action call
714
set.line.number 110 ; current line number
715
call TIME ; action call
716
call MATH.NEG ; action call
717
call RND ; action call
718
ld hl, IDF_14 ; parameter
720
call LET ; action call
723
set.line.number 115 ; current line number
724
ld hl, LIT_20 ; parameter
726
ld hl, IDF_19 ; parameter
728
call LET ; action call
729
ld hl, LIT_23 ; parameter
731
call SPACE ; action call
732
ld hl, IDF_21 ; parameter
734
call LET ; action call
735
ld hl, IDF_21 ; parameter
737
call VARPTR ; action call
738
ld hl, IDF_24 ; parameter
740
call LET ; action call
741
ld hl, IDF_24 ; parameter
743
ld hl, LIT_27 ; parameter
745
call MATH.ADD ; action call
746
call PEEK ; action call
747
ld hl, IDF_24 ; parameter
749
ld hl, LIT_29 ; parameter
751
call MATH.ADD ; action call
752
call PEEK ; action call
753
ld hl, LIT_30 ; parameter
755
call MATH.MULT ; action call
756
call MATH.ADD ; action call
757
ld hl, IDF_24 ; parameter
759
call LET ; action call
762
set.line.number 120 ; current line number
763
FOR_1 : ; start of FOR command
764
ld hl, LIT_35 ; parameter
766
ld hl, IDF_34 ; parameter
768
call LET ; action call
769
jp FOR.WHILE_1 ; jump to test if end of FOR
770
FOR.STEP_1 : ; STEP action
771
ld hl, LIT_37 ; parameter
773
ld hl, IDF_34 ; parameter
775
call MATH.ADD ; action call
776
ld hl, IDF_34 ; parameter
778
call LET ; action call
779
FOR.WHILE_1 : ; test if end of FOR
780
ld hl, IDF_34 ; parameter
782
ld hl, LIT_40 ; parameter
784
call BOOLEAN.LE ; action call
785
call BOOLEAN.IF ; verify IF condition result, out in A
787
jp z, ENDFOR_1 ; end the loop if while condition is false
788
FOR.BODY_1 : ; start of FOR user code
789
ld hl, LIT_42 ; parameter
791
ld hl, LIT_43 ; parameter
793
call RND ; action call
794
call MATH.MULT ; action call
795
ld hl, IDF_41 ; parameter
797
call LET ; action call
800
set.line.number 125 ; current line number
801
IF_1 : ; start of IF command
802
ld hl, IDF_41 ; parameter
804
ld hl, LIT_45 ; parameter
806
call BOOLEAN.GT ; action call
807
call BOOLEAN.IF ; verify IF condition result, out in A
809
jp z, ELSE_1 ; if false, jump to ELSE actions
810
THEN_1 : ; THEN actions
812
jp ENDIF_1 ; jump to END of IF command
813
ELSE_1 : ; ELSE actions
814
ENDIF_1 : ; end of IF command
817
set.line.number 130 ; current line number
818
ld hl, IDF_41 ; parameter
820
ld hl, LIT_50 ; parameter
822
call MATH.MULT ; action call
823
ld hl, IDF_41 ; parameter
825
call LET ; action call
826
ld hl, LIT_52 ; parameter
828
ld hl, LIT_53 ; parameter
830
call RND ; action call
831
call MATH.MULT ; action call
832
call INT ; action call
833
ld hl, IDF_14 ; parameter
835
call LET ; action call
838
set.line.number 135 ; current line number
839
FOR_2 : ; start of FOR command
840
ld hl, LIT_56 ; parameter
842
call BASE ; action call
843
ld hl, IDF_54 ; parameter
845
call LET ; action call
846
jp FOR.WHILE_2 ; jump to test if end of FOR
847
FOR.STEP_2 : ; STEP action
848
ld hl, LIT_57 ; parameter
850
ld hl, IDF_54 ; parameter
852
call MATH.ADD ; action call
853
ld hl, IDF_54 ; parameter
855
call LET ; action call
856
FOR.WHILE_2 : ; test if end of FOR
857
ld hl, IDF_54 ; parameter
859
ld hl, LIT_58 ; parameter
861
call BASE ; action call
862
ld hl, LIT_59 ; parameter
864
call MATH.ADD ; action call
865
call BOOLEAN.LE ; action call
866
call BOOLEAN.IF ; verify IF condition result, out in A
868
jp z, ENDFOR_2 ; end the loop if while condition is false
869
FOR.BODY_2 : ; start of FOR user code
872
set.line.number 140 ; current line number
873
ld hl, IDF_41 ; parameter
875
ld hl, IDF_14 ; parameter
877
call MATH.ADD ; action call
878
ld hl, IDF_54 ; parameter
880
ld hl, IDF_34 ; parameter
882
call MATH.ADD ; action call
883
call VPOKE ; action call
886
set.line.number 145 ; current line number
887
ld hl, IDF_14 ; parameter
889
ld hl, LIT_61 ; parameter
891
call MATH.ADD ; action call
892
ld hl, LIT_62 ; parameter
894
call BOOLEAN.AND ; action call
895
ld hl, IDF_14 ; parameter
897
call LET ; action call
898
jp FOR.STEP_2 ; repeat actions
899
ENDFOR_2 : ; END of FOR command
902
set.line.number 150 ; current line number
903
jp FOR.STEP_1 ; repeat actions
904
ENDFOR_1 : ; END of FOR command
907
set.line.number 155 ; current line number
908
ld hl, IDF_24 ; parameter
910
ld hl, IDF_65 ; parameter
912
call LET ; action call
913
FOR_3 : ; start of FOR command
914
ld hl, LIT_66 ; parameter
916
ld hl, IDF_14 ; parameter
918
call LET ; action call
919
jp FOR.WHILE_3 ; jump to test if end of FOR
920
FOR.STEP_3 : ; STEP action
921
ld hl, LIT_67 ; parameter
923
ld hl, IDF_14 ; parameter
925
call MATH.ADD ; action call
926
ld hl, IDF_14 ; parameter
928
call LET ; action call
929
FOR.WHILE_3 : ; test if end of FOR
930
ld hl, IDF_14 ; parameter
932
ld hl, LIT_68 ; parameter
934
call BOOLEAN.LE ; action call
935
call BOOLEAN.IF ; verify IF condition result, out in A
937
jp z, ENDFOR_3 ; end the loop if while condition is false
938
FOR.BODY_3 : ; start of FOR user code
941
set.line.number 160 ; current line number
942
ld hl, LIT_70 ; parameter
944
ld hl, LIT_71 ; parameter
946
call RND ; action call
947
call MATH.MULT ; action call
948
ld hl, IDF_69 ; parameter
950
call LET ; action call
951
ld hl, IDF_69 ; parameter
953
ld hl, IDF_65 ; parameter
955
call POKE ; action call
956
ld hl, IDF_65 ; parameter
958
ld hl, LIT_73 ; parameter
960
call MATH.ADD ; action call
961
ld hl, IDF_65 ; parameter
963
call LET ; action call
966
set.line.number 165 ; current line number
967
FOR_4 : ; start of FOR command
968
ld hl, LIT_74 ; parameter
970
ld hl, IDF_41 ; parameter
972
call LET ; action call
973
jp FOR.WHILE_4 ; jump to test if end of FOR
974
FOR.STEP_4 : ; STEP action
975
ld hl, LIT_75 ; parameter
977
ld hl, IDF_41 ; parameter
979
call MATH.ADD ; action call
980
ld hl, IDF_41 ; parameter
982
call LET ; action call
983
FOR.WHILE_4 : ; test if end of FOR
984
ld hl, IDF_41 ; parameter
986
ld hl, LIT_76 ; parameter
988
call BOOLEAN.LE ; action call
989
call BOOLEAN.IF ; verify IF condition result, out in A
991
jp z, ENDFOR_4 ; end the loop if while condition is false
992
FOR.BODY_4 : ; start of FOR user code
995
set.line.number 170 ; current line number
996
ld hl, IDF_19 ; parameter
998
ld hl, IDF_69 ; parameter
1000
ld hl, IDF_41 ; parameter
1002
call BOOLEAN.EQ ; action call
1003
call MATH.NEG ; action call
1004
call MATH.MULT ; action call
1005
ld hl, IDF_14 ; parameter
1007
ld hl, IDF_41 ; parameter
1009
call MATH.ADD ; action call
1010
call VPOKE ; action call
1011
jp FOR.STEP_4 ; repeat actions
1012
ENDFOR_4 : ; END of FOR command
1013
jp FOR.STEP_3 ; repeat actions
1014
ENDFOR_3 : ; END of FOR command
1017
set.line.number 175 ; current line number
1018
FOR_5 : ; start of FOR command
1019
ld hl, LIT_78 ; parameter
1021
call BASE ; action call
1022
ld hl, IDF_14 ; parameter
1024
call LET ; action call
1025
jp FOR.WHILE_5 ; jump to test if end of FOR
1026
FOR.STEP_5 : ; STEP action
1027
ld hl, LIT_79 ; parameter
1029
ld hl, IDF_14 ; parameter
1031
call MATH.ADD ; action call
1032
ld hl, IDF_14 ; parameter
1034
call LET ; action call
1035
FOR.WHILE_5 : ; test if end of FOR
1036
ld hl, IDF_14 ; parameter
1038
ld hl, LIT_80 ; parameter
1040
call BASE ; action call
1041
ld hl, LIT_81 ; parameter
1043
call MATH.ADD ; action call
1044
call BOOLEAN.LE ; action call
1045
call BOOLEAN.IF ; verify IF condition result, out in A
1047
jp z, ENDFOR_5 ; end the loop if while condition is false
1048
FOR.BODY_5 : ; start of FOR user code
1049
ld hl, IDF_41 ; parameter
1051
call READ ; action call
1054
set.line.number 180 ; current line number
1055
ld hl, IDF_41 ; parameter
1057
ld hl, LIT_83 ; parameter
1059
call MATH.MULT ; action call
1060
ld hl, IDF_14 ; parameter
1062
call VPOKE ; action call
1063
jp FOR.STEP_5 ; repeat actions
1064
ENDFOR_5 : ; END of FOR command
1067
set.line.number 185 ; current line number
1068
ld hl, LIT_90 ; parameter
1070
call SET_TIME ; action call
1071
ld hl, IDF_24 ; parameter
1073
ld hl, IDF_65 ; parameter
1075
call LET ; action call
1076
FOR_6 : ; start of FOR command
1077
ld hl, LIT_91 ; parameter
1079
ld hl, IDF_14 ; parameter
1081
call LET ; action call
1082
jp FOR.WHILE_6 ; jump to test if end of FOR
1083
FOR.STEP_6 : ; STEP action
1084
ld hl, LIT_92 ; parameter
1086
ld hl, IDF_14 ; parameter
1088
call MATH.ADD ; action call
1089
ld hl, IDF_14 ; parameter
1091
call LET ; action call
1092
FOR.WHILE_6 : ; test if end of FOR
1093
ld hl, IDF_14 ; parameter
1095
ld hl, LIT_93 ; parameter
1097
call BOOLEAN.LE ; action call
1098
call BOOLEAN.IF ; verify IF condition result, out in A
1100
jp z, ENDFOR_6 ; end the loop if while condition is false
1101
FOR.BODY_6 : ; start of FOR user code
1102
ld hl, IDF_65 ; parameter
1104
call PEEK ; action call
1105
ld hl, IDF_41 ; parameter
1107
call LET ; action call
1110
set.line.number 190 ; current line number
1111
ld hl, LIT_94 ; parameter
1113
call BASE ; action call
1114
ld hl, LIT_95 ; parameter
1116
ld hl, IDF_14 ; parameter
1118
call MATH.MULT ; action call
1119
call MATH.ADD ; action call
1120
ld hl, IDF_69 ; parameter
1122
call LET ; action call
1123
ld hl, LIT_96 ; parameter
1125
ld hl, IDF_69 ; parameter
1127
ld hl, IDF_41 ; parameter
1129
call MATH.ADD ; action call
1130
call VPOKE ; action call
1133
set.line.number 195 ; current line number
1134
ld hl, IDF_41 ; parameter
1136
ld hl, IDF_14 ; parameter
1138
call MATH.ADD ; action call
1139
ld hl, LIT_97 ; parameter
1141
call MATH.ADD ; action call
1142
ld hl, LIT_98 ; parameter
1144
call BOOLEAN.AND ; action call
1145
ld hl, IDF_41 ; parameter
1147
call LET ; action call
1148
ld hl, IDF_19 ; parameter
1150
ld hl, IDF_69 ; parameter
1152
ld hl, IDF_41 ; parameter
1154
call MATH.ADD ; action call
1155
call VPOKE ; action call
1158
set.line.number 200 ; current line number
1159
ld hl, IDF_41 ; parameter
1161
ld hl, IDF_65 ; parameter
1163
call POKE ; action call
1164
ld hl, IDF_65 ; parameter
1166
ld hl, LIT_99 ; parameter
1168
call MATH.ADD ; action call
1169
ld hl, IDF_65 ; parameter
1171
call LET ; action call
1172
jp FOR.STEP_6 ; repeat actions
1173
ENDFOR_6 : ; END of FOR command
1176
set.line.number 205 ; current line number
1177
IF_2 : ; start of IF command
1178
call TIME ; action call
1179
ld hl, LIT_100 ; parameter
1181
call BOOLEAN.LT ; action call
1182
call BOOLEAN.IF ; verify IF condition result, out in A
1184
jp z, ELSE_2 ; if false, jump to ELSE actions
1185
THEN_2 : ; THEN actions
1187
jp ENDIF_2 ; jump to END of IF command
1188
ELSE_2 : ; ELSE actions
1190
ENDIF_2 : ; end of IF command
1192
;---------------------------------------------------------------------------------------------------------
1194
;---------------------------------------------------------------------------------------------------------
1196
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
1198
ld (BIOS_CLIKSW), a ; enable keyboard click
1200
if defined COMPILE_TO_ROM
1203
__call_basic BASIC_READYR ; warm start Basic
1206
ret ; end of the program
1208
;__call_bios BIOS_GICINI ; initialize sound system
1209
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
1210
; __call_bios BIOS_RESET ; restart Basic
1212
; __call_basic BASIC_END ; end to Basic
1216
;---------------------------------------------------------------------------------------------------------
1217
; MSX BASIC KEYWORDS
1218
;---------------------------------------------------------------------------------------------------------
1223
; out IX = variable assigned address
1224
pop.parm ; get variable address parameter
1225
push hl ; just to transfer hl to ix
1227
ld a, (ix) ; get variable type
1228
cp 3 ; test if string
1229
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
1230
ld a, (ix + 3) ; get variable string length
1232
jr z, LET.PARM ; if zero, it isn't necessary to free memory
1233
ld c, (ix + 4) ; get old string address low
1234
ld b, (ix + 5) ; get old string address high
1235
push ix ; save variable address
1236
push bc ; just to transfer bc (old string address) to ix
1238
call memory.free ; free memory
1239
pop ix ; restore variable address
1240
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
1241
ld a, (ix + 2) ; get variable type flag
1242
or a ; cp 0 - test type flag (0=any, 255=fixed)
1243
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
1244
LET.ANY: push ix ; just to transfer ix (variable address) to de
1246
ldi ; copy 1 byte from hl (data address) to de (variable address)
1247
inc de ; go to variable data area
1249
inc hl ; go to data data area
1251
ld bc, 8 ; data = 8 bytes
1252
ldir ; copy bc bytes from hl (data address) to de (variable address)
1253
ld a, (ix) ; get variable type
1254
cp 3 ; test if string
1255
ret nz ; if not string, return
1256
jp LET.STRING ; else do string treatment (in ix = variable address)
1257
LET.FIXED: push ix ; save variable destination address
1258
push hl ; save variable source address
1259
ld a, (ix) ; get variable fixed type, and hl has parameter data address
1260
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
1262
pop ix ; restore variable address
1263
ld a, (ix) ; get variable destination type again
1264
cp 3 ; test if string
1265
jr nz, LET.VALUE ; if not string, do value treatment
1266
ld a, (de) ; get variable source type again
1267
cp 3 ; test if string
1268
jr nz, LET.FIX1 ; if not string, get casted string size
1273
ld (ix + 3), a ; source string size
1276
call GET_STR.LENGTH ; get string length (in HL, out B)
1278
ld (ix + 3), b ; set variable length
1279
LET.FIX2: ld (ix + 4), l ; casted data address low
1280
ld (ix + 5), h ; casted data address high
1281
jp LET.STRING ; do string treatment (in ix = variable address)
1282
LET.VALUE: push ix ; just to transfer ix (variable address) to de
1284
inc de ; go to variable data area (and the data from its casted)
1287
ld bc, 8 ; data = 8 bytes
1288
ldir ; copy bc bytes from hl (data address) to de (variable address)
1290
LET.STRING: ld a, (ix + 3) ; string size
1291
or a ; cp 0 - test if null
1292
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
1293
ld bc, LIT_NULL_STR ; else, set to a null string literal
1294
ld (ix + 4), c ; variable address low
1295
ld (ix + 5), b ; variable address high
1297
LET.ALLOC: push ix ; save variable address
1298
ld l, (ix + 4) ; source string address low
1299
ld h, (ix + 5) ; source string address high
1300
push hl ; save copy from address
1301
ld c, (ix + 3) ; get variable length
1303
inc bc ; string length have one more byte from zero terminator
1304
push bc ; save variable lenght + 1
1305
call memory.alloc ; in bc = size, out ix = address, nz=OK
1307
push ix ; just to transfer memory address from ix to de
1309
pop bc ; restore bytes to be copied
1310
pop hl ; restore copy from string address
1311
push de ; save copy to address
1312
ldir ; copy bc bytes from hl (data address) to de (variable address)
1315
pop de ; restore copy to address
1316
pop ix ; restore variable address
1317
ld (ix + 4), e ; put memory address low into variable
1318
ld (ix + 5), d ; put memory address high into variable
1319
ret ; variable assigned
1324
pop.parm ; get parameter boolean result in hl
1327
ld a, (ix+5) ; put boolean integer result in a
1334
__call_bios BIOS_CHGCLR ; change VDP colors
1340
pop.parm ; get first parameter
1342
call GET_INT.VALUE ; output BC with integer value
1343
ld a, c ; A = pixel color
1344
;call gfxSetForeColor
1345
ld (BIOS_FORCLR), a ; foreground color
1352
pop.parm ; get first parameter
1354
call GET_INT.VALUE ; output BC with integer value
1355
ld a, c ; A = pixel color
1356
;call gfxSetBackColor
1357
ld (BIOS_BAKCLR), a ; foreground color
1363
pop.parm ; get first parameter
1365
call GET_INT.VALUE ; output BC with integer value
1366
ld a, c ; A = pixel color
1367
;call gfxSetBorderColor
1368
ld (BIOS_BDRCLR), a ; border color
1374
pop.parm ; get first parameter
1376
call GET_INT.VALUE ; output BC with integer value
1377
ld a, c ; A = screen number (0 to 3)
1379
jr c, SCREEN.1 ; if mode < 9, jump
1380
ld a, 8 ; else, fix to 8
1382
if defined EXIST_DATA_SET
1383
call gfxSetScreenMode
1387
jp gfxClearTileScreen
1395
pop.parm ; get first parameter
1397
call GET_INT.VALUE ; output BC with integer value
1398
ld a, (BIOS_OLDSCR) ; last text screen mode
1399
and a ; test if zero
1400
ld a, c ; copy parameter to A
1401
jr z, WIDTH.40 ; if zero = 40 columns
1402
WIDTH.32: ld (BIOS_LINL32), a
1404
WIDTH.40: ld (BIOS_LINL40), a
1405
WIDTH.SET: ld (BIOS_LINLEN), a
1412
ld a, 0x0C ; new page (clear the screen)
1413
rst BIOS_OUTDO ; output to screen
1419
pop.parm ; get parameter
1420
call COPY_TO.DAC ; put in DAC
1421
and 12 ; test if single/double
1422
jr nz, RND.1 ; if already double
1423
__call_bios MATH_FRCDBL ; convert DAC to double
1424
RND.1: __call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
1425
jp MATH.PARM.PUSH ; return a dummy double variable from DAC
1430
ld ix, BIOS_JIFFY ; time counter address
1432
ld c, (ix) ; get time counter (low)
1433
ld b, (ix+1) ; get time counter (high)
1435
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
1441
call CLEAR.DAC ; put zero in DAC
1442
pop.parm ; get parameter
1443
call COPY_TO.ARG ; put in ARG
1444
cp 2 ; test if integer
1445
jp z, MATH.SUB.INT ;
1446
cp 4 ; test if single
1447
jp z, MATH.SUB.SGL ;
1448
cp 8 ; test if double
1449
jp z, MATH.SUB.DBL ;
1455
pop.parm ; get first parameter
1457
call GET_INT.VALUE ; output BC with integer value
1459
call memory.alloc ; in bc = size, out ix = address, nz=OK
1472
call COPY_TO.VAR_DUMMY.STR ; create a fake string variable from HL in HL
1478
pop.parm ; get first parameter in HL
1486
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
1492
pop.parm ; get first parameter in HL
1494
call GET_INT.VALUE ; put parameter into BC
1495
;push bc ; address of data
1505
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
1511
call MATH.PARM.POP ; get parameters into DAC/ARG
1512
ld a, (BASIC_VALTYP) ;
1513
cp 2 ; test if integer
1514
jp z, MATH.ADD.INT ;
1515
cp 3 ; test if string
1516
jp z, STRING.CONCAT ;
1517
cp 4 ; test if single
1518
jp z, MATH.ADD.SGL ;
1519
jp MATH.ADD.DBL ; it is a double
1524
call MATH.PARM.POP ; get parameters into DAC/ARG
1525
ld a, (BASIC_VALTYP) ;
1526
cp 2 ; test if integer
1527
jp z, MATH.MULT.INT ;
1528
cp 3 ; test if string
1530
cp 4 ; test if single
1531
jp z, MATH.MULT.SGL ;
1532
jp MATH.MULT.DBL ; it is a double
1536
; abstract virtual FOR
1540
call MATH.PARM.POP ; get parameters into DAC/ARG
1541
ld a, (BASIC_VALTYP) ;
1542
cp 2 ; test if integer
1543
jp z, BOOLEAN.LE.INT ;
1544
cp 3 ; test if string
1545
jp z, BOOLEAN.LE.STR ;
1546
cp 4 ; test if single
1547
jp z, BOOLEAN.LE.SGL ;
1548
jp BOOLEAN.LE.DBL ; it is a double
1553
call MATH.PARM.POP ; get parameters into DAC/ARG
1554
ld a, (BASIC_VALTYP) ;
1555
cp 2 ; test if integer
1556
jp z, BOOLEAN.GT.INT ;
1557
cp 3 ; test if string
1558
jp z, BOOLEAN.GT.STR ;
1559
cp 4 ; test if single
1560
jp z, BOOLEAN.GT.SGL ;
1561
jp BOOLEAN.GT.DBL ; it is a double
1565
; abstract virtual GOTO
1569
pop.parm ; get first parameter in HL
1571
call GET_INT.VALUE ; put parameter into BC
1572
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
1578
pop.parm ; get first parameter in HL
1580
call GET_INT.VALUE ; put parameter into BC
1589
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
1595
pop.parm ; get first parameter in HL
1597
call GET_INT.VALUE ; put parameter into BC
1598
ld (BIOS_TEMP), bc ; address
1599
pop.parm ; get second parameter in HL
1601
call GET_INT.VALUE ; put parameter into BC
1602
ld hl, (BIOS_TEMP) ; address
1603
ld a, c ; data (low)
1606
;ld a, b ; data (high)
1613
call MATH.PARM.POP ; get parameters into DAC/ARG
1614
ld a, (BASIC_VALTYP) ;
1615
cp 2 ; test if integer
1616
jp z, BOOLEAN.AND.INT ;
1617
jp MATH.ERROR ; it is a double
1621
; abstract virtual NEXT
1625
pop.parm ; get first parameter in HL
1627
call GET_INT.VALUE ; put parameter into BC
1628
ld (BIOS_TEMP), bc ; address
1629
pop.parm ; get second parameter in HL
1631
call GET_INT.VALUE ; put parameter into BC
1632
ld ix, (BIOS_TEMP) ; address
1633
ld (ix), c ; data (low)
1634
;ld (ix+1), b ; data (high)
1640
call MATH.PARM.POP ; get parameters into DAC/ARG
1641
ld a, (BASIC_VALTYP) ;
1642
cp 2 ; test if integer
1643
jp z, BOOLEAN.EQ.INT ;
1644
cp 3 ; test if string
1645
jp z, BOOLEAN.EQ.STR ;
1646
cp 4 ; test if single
1647
jp z, BOOLEAN.EQ.SGL ;
1648
jp BOOLEAN.EQ.DBL ; it is a double
1653
pop.parm ; get first parameter (variable to be read)
1654
push hl ; save input variable...
1656
ld hl, (BASIC_DATLIN) ; index of data items
1657
ld de, DATA_ITEMS_COUNT ; count of data items
1658
sbc hl, de ; compare hl >= de
1659
jr z, READ.END ; jump if equal
1660
jr nc, READ.END ; jump if above
1661
pop de ; restore saved input variable to DE
1662
ld ix, (BASIC_DATPTR) ; data items pointer
1665
push.parm ; LET parameter 2 - data as right operand
1666
ex de, hl ; put DE into HL
1667
push.parm ; LET parameter 1 - input variable as left operand
1668
call LET ; put data into variable
1669
ld hl, (BASIC_DATLIN) ; old index of data items
1671
ld (BASIC_DATLIN), hl ; save new index of data items
1672
ld hl, (BASIC_DATPTR) ; old data items pointer
1676
ld (BASIC_DATPTR), hl ; save new data items pointer
1682
; abstract virtual DATA
1686
pop.parm ; get first parameter in HL
1688
call GET_INT.VALUE ; put parameter into BC
1697
call MATH.PARM.POP ; get parameters into DAC/ARG
1698
ld a, (BASIC_VALTYP) ;
1699
cp 2 ; test if integer
1700
jp z, BOOLEAN.LT.INT ;
1701
cp 3 ; test if string
1702
jp z, BOOLEAN.LT.STR ;
1703
cp 4 ; test if single
1704
jp z, BOOLEAN.LT.SGL ;
1705
jp BOOLEAN.LT.DBL ; it is a double
1709
;---------------------------------------------------------------------------------------------------------
1710
; MSX BASIC SUPPORT CODE
1711
;---------------------------------------------------------------------------------------------------------
1713
if defined ON_ERROR or defined ON_INTERVAL or defined ON_KEY_START or defined ON_SPRITE or defined ON_STOP or defined ON_STRIG_START or defined TRAP_ENABLED or defined TRAP_DISABLED or defined TRAP_PAUSE or defined TRAP_UNPAUSE
1717
RUN_TRAPS.1: push hl
1728
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1730
ld a, (hl) ; trap status
1731
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1732
ret nz ; return if false
1734
ld e, (hl) ; get trap address
1741
ret z ; return if address zero
1743
__call_basic BASIC_TRAP_ACKNW
1744
__call_basic BASIC_TRAP_PAUSE
1745
ld hl, TRAP_HANDLER.1
1746
ld a, (BASIC_ONGSBF) ; save traps execution
1749
ld (BASIC_ONGSBF), a ; disable traps execution
1750
push hl ; next return will be to trap handler
1751
push de ; indirect jump to trap address
1753
TRAP_HANDLER.1: pop af
1754
ld (BASIC_ONGSBF), a ; restore traps execution
1757
cp 1 ; trap enabled?
1759
__call_basic BASIC_TRAP_UNPAUSE
1762
; hl = trap block, de = trap handler
1764
ld (hl), a ; trap block status
1766
ld (hl), e ; trap block handler (pointer)
1773
if defined SET_PLAY_VOICE_1 or defined SET_PLAY_VOICE_2 or defined SET_PLAY_VOICE_3 or defined DO_PLAY or defined MUSIC_PLAY or defined MUSIC_NEXT or defined MUSIC_STOP
1776
ld (BIOS_TEMP), a ; save voice number
1780
ret nz ; return if not string
1783
ld (BIOS_TEMP2), a ; save string size
1784
push hl ; string address
1785
ld a, (BIOS_TEMP) ; restore voice number
1786
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1788
ld a, (BIOS_TEMP2) ; restore string size
1789
ld (hl), a ; string size
1791
ld (hl), e ; string address
1795
ld D,H ; voice stack
1810
ld hl, BIOS_TEMP ; voice count
1824
__call_basic BASIC_PLAY_DIRECT
1831
;---------------------------------------------------------------------------------------------------------
1832
; VARIABLES ROUTINES
1833
;---------------------------------------------------------------------------------------------------------
1835
; input hl = variable address
1836
; input bc = variable name
1837
; input d = variable type
1838
INIT_VAR: ld (hl), d ; variable type
1840
ld (hl), c ; variable name 1
1842
ld (hl), b ; variable name 2
1856
CLEAR.VAR.LOOP: inc hl
1857
ld (hl), 0 ; data address/value
1860
; input HL = variable address
1861
; input A = variable output type
1862
; output HL = casted data address
1872
; input HL = variable address
1873
; output HL = variable address
1874
CAST_TO.INT: ;push af
1879
jp z, CAST_STR_TO.INT
1881
jp z, CAST_SGL_TO.INT
1883
jp z, CAST_DBL_TO.INT
1886
; input HL = variable address
1887
; output HL = variable address
1888
CAST_TO.STR: ;push af
1891
jp z, CAST_INT_TO.STR
1895
jp z, CAST_SGL_TO.STR
1897
jp z, CAST_DBL_TO.STR
1900
; input HL = variable address
1901
; output HL = variable address
1902
CAST_TO.SGL: ;push af
1905
jp z, CAST_INT_TO.SGL
1907
jp z, CAST_STR_TO.SGL
1911
jp z, CAST_DBL_TO.SGL
1914
; input HL = variable address
1915
; output HL = variable address
1916
CAST_TO.DBL: ;push af
1919
jp z, CAST_INT_TO.DBL
1921
jp z, CAST_STR_TO.DBL
1923
jp z, CAST_SGL_TO.DBL
1928
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1929
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1930
CAST_INT_TO.STR: call COPY_TO.DAC
1932
__call_bios MATH_FOUT ; convert DAC to string
1935
CAST_INT_TO.SGL: call COPY_TO.DAC
1936
__call_bios MATH_FRCSGL
1939
CAST_INT_TO.DBL: call COPY_TO.DAC
1940
__call_bios MATH_FRCDBL
1943
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1944
CAST_DBL_TO.INT: call COPY_TO.DAC
1945
__call_bios MATH_FRCINT
1948
CAST_STR_TO.INT: call CAST_STR_TO.VAL ;
1949
__call_bios MATH_FRCINT ;
1952
CAST_STR_TO.SGL: call CAST_STR_TO.VAL ;
1953
__call_bios MATH_FRCSGL ;
1956
CAST_STR_TO.DBL: call CAST_STR_TO.VAL ;
1957
__call_bios MATH_FRCDBL ;
1960
CAST_STR_TO.VAL: call GET_STR.ADDR ;
1962
__call_bios MATH_FIN ; convert string to a value type
1965
GET_INT.VALUE: inc hl ; output BC with integer value
1971
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1972
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1973
GET_INT.ADDR: ; same as GET_DBL.ADDR
1974
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1975
GET_DBL.ADDR: inc hl
1980
GET_STR.ADDR: push hl
1986
; input hl = string address
1987
; output b = string length
1988
GET_STR.LENGTH: ld b, 0
1989
GET_STR.LEN.NEXT: ld a, (hl)
1996
jr z, GET_STR.LEN.ERR
1998
GET_STR.LEN.ERR: ld b, 0
2000
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
2001
ld iy, (BASIC_ARG+1) ; string 2
2002
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
2003
cp (iy) ; char s1 = char s2?
2004
jr nz, STRING.COMPARE.NE ; if not equal...
2006
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
2007
ld a, (iy) ; next char from string 2
2009
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
2012
jr STRING.COMPARE.NX ; get next char pair
2013
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
2015
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
2016
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
2017
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
2018
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
2020
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
2022
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
2024
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
2025
ld a, (BASIC_ARG) ; s2 size
2026
add a, (ix) ; s3 size = s1 size + s2 size
2030
inc bc ; add 1 byte to size
2031
call memory.alloc ; in bc size, out ix new memory address, nz=OK
2032
jp z, memory.error ;
2036
ld a, (BASIC_DAC) ; s1 size
2037
ld hl, (BASIC_DAC + 1) ; string 1
2038
call COPY_TO.STR ; copy to new memory
2039
ld a, (BASIC_ARG) ; s2 size
2040
ld hl, (BASIC_ARG + 1) ; string 2
2041
call COPY_TO.STR ; copy to new memory
2043
ld (de), a ; null terminated
2046
call COPY_TO.VAR_DUMMY.STR ;
2047
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
2048
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
2050
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
2052
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
2053
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
2054
or a ; cp 0 - is it the string end?
2056
__call_bios BIOS_CHPUT ; put the char (a) into text screen
2058
jr STRING.PRINT.T ; repeat
2059
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
2060
or a ; cp 0 - is it the string end?
2062
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
2064
jr STRING.PRINT.G1 ; repeat
2065
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
2066
or a ; cp 0 - is it the string end?
2068
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
2071
jr STRING.PRINT.G2 ; repeat
2073
; a = string size to copy
2074
; input hl = string from
2075
; input de = string to
2077
ret z ; avoid copy if size = zero
2079
ld c, a ; string size
2080
ldir ; copy bc bytes from hl to de
2082
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
2083
ld a, (LIT_QUOTE_CHAR)
2086
COPY_BAS_BUF.LOOP: ld a, (hl)
2088
jr z, COPY_BAS_BUF.EXIT
2092
jr COPY_BAS_BUF.LOOP
2093
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
2100
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
2102
jr nz, COPY_TO.VAR_DUMMY.DBL
2104
call GET_STR.LENGTH ; get string length
2106
ld a, b ; string length
2107
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
2108
ld (ix), 3 ; data type string
2110
ld (ix+2), 255 ; var type fixed
2111
ld (ix+3), a ; string length
2112
ld (ix+4), l ; data address low
2113
ld (ix+5), h ; data address high
2114
;call GET_STR.LENGTH ; get string length
2115
;ld (ix+3), b ; string length
2116
push ix ; output var address...
2119
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
2120
ld (ix), 2 ; data type string
2131
push ix ; output var address...
2134
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
2135
ld (ix), a ; data type
2140
push ix ; just to copy ix to de
2145
ldir ; copy bc bytes from hl (data address) to de (variable address)
2146
push ix ; output var address...
2149
GET_VAR_DUMMY.ADDR: push af ;
2152
ld ix, (VAR_DUMMY.POINTER) ;
2153
ld a, (VAR_DUMMY.COUNTER) ;
2154
GET_VAR_DUMMY.NEXT: add ix, de ;
2157
jr nz, GET_VAR_DUMMY.EXIT ;
2159
ld ix, VAR_DUMMY.DATA ;
2160
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
2161
ld (VAR_DUMMY.COUNTER), a ;
2162
ld a, (ix) ; get last var dummy type
2163
cp 3 ; is it string?
2164
call z, GET_VAR_DUMMY.FREE ; free string memory
2171
ld l, (ix+4) ; get string data address
2175
call memory.free ; free memory
2179
; input hl = variable address
2180
COPY_TO.DAC: ld de, BASIC_DAC
2181
COPY_TO.DAC.DATA: ld a, (hl)
2182
ld (BASIC_VALTYP), a
2186
ld bc, 8 ; data = 8 bytes
2187
ldir ; copy bc bytes from hl (data address) to de (variable address)
2189
COPY_TO.ARG: ld de, BASIC_ARG ;
2190
jr COPY_TO.DAC.DATA ;
2191
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
2193
ld bc, 8 ; data = 8 bytes
2194
ldir ; copy bc bytes from hl (data address) to de (variable address)
2196
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
2198
ld bc, 8 ; data = 8 bytes
2199
ldir ; copy bc bytes from hl (data address) to de (variable address)
2201
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
2202
ld de, BASIC_SWPTMP ;
2203
ld bc, 8 ; data = 8 bytes
2204
ldir ; copy bc bytes from hl (data address) to de (variable address)
2206
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
2208
ld bc, 8 ; data = 8 bytes
2209
ldir ; copy bc bytes from hl (data address) to de (variable address)
2212
exx ; save registers
2215
ld de, BASIC_SWPTMP ;
2216
ldir ; copy bc bytes from hl to de
2220
ldir ; copy bc bytes from hl to de
2222
ld hl, BASIC_SWPTMP ;
2224
ldir ; copy bc bytes from hl to de
2225
exx ; restore registers
2228
CLEAR.DAC: ld de, BASIC_DAC
2229
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
2232
ld bc, 8 ; data = 8 bytes
2233
ldir ; copy bc bytes from hl (data address) to de (variable address)
2235
CLEAR.ARG: ld de, BASIC_ARG
2240
;---------------------------------------------------------------------------------------------------------
2241
; MATH 16 BITS ROUTINES
2242
;---------------------------------------------------------------------------------------------------------
2244
MATH.PARM.POP: pop af ; get PC from caller stack
2245
ex af, af' ; save PC to temp
2246
pop.parm ; get first parameter
2247
call COPY_TO.ARG ; put HL in ARG (return var type in A)
2248
pop.parm ; get second parameter
2249
ex af, af' ; restore PC from temp
2250
push af ; put again PC from caller in stack
2251
ex af, af' ; restore 1st data type
2252
push af ; save 1st data type
2253
call COPY_TO.DAC ; put HL in DAC (return var type in A)
2254
pop bc ; restore 1st data type (ARG) in B
2255
cp b ; test if data type in A (DAC) = data type in B (ARG)
2256
ret z ; return if is equal data types
2257
MATH.PARM.CAST: push bc ; else cast both to double
2258
and 12 ; test if single/double
2259
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
2260
__call_bios MATH_FRCDBL ; convert DAC to double
2261
MATH.PARM.CST1: pop af ;
2262
and 12 ; test if single/double
2263
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
2264
ld (BASIC_VALTYP), a ;
2265
call COPY_TO.DAC_TMP ;
2266
call COPY_TO.ARG_DAC ;
2267
__call_bios MATH_FRCDBL ; convert ARG to double
2268
call COPY_TO.DAC_ARG ;
2269
call COPY_TO.TMP_DAC ;
2270
MATH.PARM.CST2: ld a, 8 ;
2271
ld (BASIC_VALTYP), a ;
2273
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
2274
pop af ; get PC from caller stack
2275
ex af, af' ; save PC to temp
2276
pop.parm ; get first parameter
2277
ld a, (hl) ; get parameter type
2278
and 2 ; test if integer
2279
jr z, MATH.PARM.POP.I1 ; do cast if not integer
2280
call COPY_TO.ARG ; put HL in ARG (return var type in A)
2281
jr MATH.PARM.POP.I2 ; go to next parameter
2282
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
2283
__call_bios MATH_FRCINT ; convert DAC to int
2284
call COPY_TO.DAC_ARG ; copy DAC to ARG
2285
MATH.PARM.POP.I2: pop.parm ; get second parameter
2286
call COPY_TO.DAC ; put HL in DAC (return var type in A)
2287
and 2 ; test if integer
2288
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
2289
__call_bios MATH_FRCINT ; convert DAC to int
2291
ld (BASIC_VALTYP), a ;
2293
ex af, af' ; restore PC from temp
2294
push af ; put again PC from caller in stack
2296
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
2302
; output in parm stack
2303
; http://www.z80.info/zip/zaks_book.pdf - page 104
2304
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
2305
ld bc, (BASIC_ARG+2) ;
2307
ld (BASIC_DAC+2), hl ;
2312
if defined MATH.SUB or defined MATH.NEG
2315
; output in parm stack
2316
; http://www.z80.info/zip/zaks_book.pdf - page 104
2317
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
2318
ld de, (BASIC_ARG+2) ;
2321
ld (BASIC_DAC+2), hl ;
2326
if defined MATH.MULT
2329
; output in parm stack
2330
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
2331
ld bc, (BASIC_ARG+2) ;
2333
ld (BASIC_DAC+2), hl ;
2336
; input HL = multiplicand
2337
; input BC = multiplier
2338
; output HL = result
2339
; http://www.z80.info/zip/zaks_book.pdf - page 131
2340
MATH.MULT.16: ld a, c ; low multiplier
2341
ld c, b ; high multiplier
2343
ld d, h ; multiplicand
2346
MULT16LOOP: srl c ; right shift multiplier high
2347
rra ; rotate right multiplier low
2348
jr nc, MULT16NOADD ; test carry
2349
add hl, de ; add multiplicand to result
2350
MULT16NOADD: ex de, hl
2351
add hl, hl ; double - shift multiplicand
2358
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
2360
; input AC = dividend
2361
; input DE = divisor
2362
; output AC = quotient
2363
; output HL = remainder
2364
; http://www.z80.info/zip/zaks_book.pdf - page 140
2365
MATH.DIV.16: ld hl, 0 ; clear accumulator
2366
ld b, 16 ; set counter
2367
DIV16LOOP: rl c ; rotate accumulator result left
2369
adc hl, hl ; left shift
2370
sbc hl, de ; trial subtract divisor
2371
jr nc, $ + 3 ; subtract was OK ($ = current location)
2372
add hl, de ; restore accumulator
2373
ccf ; calculate result bit
2374
djnz DIV16LOOP ; counter not zero
2375
rl c ; shift in last result bit
2381
if defined GFX_FAST or defined LINE
2383
; compare two signed 16 bits integers
2384
; HL < DE: Carry flag
2385
; HL = DE: Zero flag
2386
; http://www.z80.info/zip/zaks_book.pdf - page 531
2387
MATH.COMP.S16: ld a, h ; test high order byte
2388
and 0x80 ; test sign, clear carry
2389
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
2391
ret nz ; de is negative (and hl is positive)
2393
cp d ; signs are both positive, so normal compare
2395
ld a, l ; test low order byte
2398
MATH.COMP.S16.NEGM1:
2400
rla ; sign bit into carry
2401
ret c ; signs different
2403
cp d ; both signs negative
2413
MATH.ADD.SGL: ld a, 8 ;
2414
ld (BASIC_VALTYP), a ;
2415
MATH.ADD.DBL: __call_bios MATH_DECADD ;
2420
if defined MATH.SUB or defined MATH.NEG
2422
MATH.SUB.SGL: ld a, 8 ;
2423
ld (BASIC_VALTYP), a ;
2424
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
2429
if defined MATH.MULT
2431
MATH.MULT.SGL: ld a, 8 ;
2432
ld (BASIC_VALTYP), a ;
2433
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
2441
; output in parm stack
2442
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
2445
ld (BASIC_VALTYP), a ;
2446
__call_bios MATH_FRCDBL ; convert ARG to double
2448
MATH.DIV.SGL: ld a, 8 ;
2449
ld (BASIC_VALTYP), a ;
2450
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
2455
if defined MATH.IDIV
2458
; output in parm stack
2459
MATH.IDIV.SGL: ld a, 8 ;
2460
ld (BASIC_VALTYP), a ;
2461
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
2464
ld (BASIC_VALTYP), a ;
2465
__call_bios MATH_FRCINT ; convert ARG to integer
2467
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
2470
ld de, (BASIC_ARG+2) ;
2474
ld (BASIC_DAC+2), hl ; quotient
2481
MATH.POW.INT: ld (BASIC_VALTYP), a ;
2482
__call_bios MATH_FRCDBL ; convert DAC to double
2485
ld (BASIC_VALTYP), a ;
2486
__call_bios MATH_FRCDBL ; convert ARG to double
2488
MATH.POW.SGL: ld a, 8 ;
2489
ld (BASIC_VALTYP), a ;
2490
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
2497
;MATH.MOD.SGL: ld a, 8 ;
2498
; ld (BASIC_VALTYP), a ;
2499
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
2500
; call SWAP.DAC.ARG ;
2502
; ld (BASIC_VALTYP), a ;
2503
; __call_bios MATH_FRCINT ; convert ARG to integer
2504
; call SWAP.DAC.ARG ;
2505
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
2508
ld de, (BASIC_ARG+2) ;
2510
ld (BASIC_DAC+2), hl ; remainder
2517
; fast 16-bit integer square root
2518
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
2519
; 92 bytes, 344-379 cycles (average 362)
2520
; v2 - 3 t-state optimization spotted by Russ McNulty
2521
; call with hl = number to square root
2522
; returns a = square root
2599
if defined RANDOMIZE or defined SEED
2601
MATH.RANDOMIZE: di ;
2602
ld bc, (BIOS_JIFFY) ;
2605
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
2606
push bc ; in bc = new integer seed
2610
ld (BASIC_DAC+2), bc ; copy bc to dac
2611
ld a, 2 ; type integer
2612
ld (BASIC_VALTYP), a ;
2613
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
2614
__call_bios MATH_NEG ; DAC = -DAC
2615
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
2620
MATH.ERROR: ld e, 13 ; type mismatch
2621
__call_basic BASIC_ERROR_HANDLER ;
2625
;---------------------------------------------------------------------------------------------------------
2627
;---------------------------------------------------------------------------------------------------------
2629
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
2631
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
2633
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
2634
ld de, (BASIC_ARG+2) ;
2635
__call_bios MATH_ICOMP ;
2637
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
2638
ld de, (BASIC_ARG+2) ;
2639
__call_bios MATH_DCOMP ;
2641
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
2643
BOOLEAN.CMP.STR: call STRING.COMPARE ;
2646
if defined BOOLEAN.GT
2648
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
2650
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
2652
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
2654
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
2656
BOOLEAN.GT.RET: cp 0x01 ;
2657
jp z, BOOLEAN.RET.TRUE ;
2658
jp BOOLEAN.RET.FALSE ;
2661
if defined BOOLEAN.LT
2663
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
2665
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
2667
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
2669
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
2671
BOOLEAN.LT.RET: cp 0xFF ;
2672
jp z, BOOLEAN.RET.TRUE ;
2673
jp BOOLEAN.RET.FALSE ;
2677
if defined BOOLEAN.GE
2679
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
2681
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
2683
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
2685
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
2687
BOOLEAN.GE.RET: cp 0x01 ;
2688
jp z, BOOLEAN.RET.TRUE ;
2690
jp z, BOOLEAN.RET.TRUE ;
2691
jp BOOLEAN.RET.FALSE ;
2695
if defined BOOLEAN.LE
2697
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
2699
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
2701
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
2703
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
2705
BOOLEAN.LE.RET: cp 0xFF ;
2706
jp z, BOOLEAN.RET.TRUE ;
2708
jp z, BOOLEAN.RET.TRUE ;
2709
jp BOOLEAN.RET.FALSE ;
2713
if defined BOOLEAN.NE
2715
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
2717
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
2719
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
2721
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
2723
BOOLEAN.NE.RET: or a ; cp 0
2724
jp nz, BOOLEAN.RET.TRUE ;
2725
jp BOOLEAN.RET.FALSE ;
2729
if defined BOOLEAN.EQ
2731
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
2733
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
2735
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
2737
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2739
BOOLEAN.EQ.RET: or a ; cp 0
2740
jp z, BOOLEAN.RET.TRUE ;
2741
jp BOOLEAN.RET.FALSE ;
2745
if defined BOOLEAN.AND
2747
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2748
ld hl, BASIC_ARG+2 ;
2750
ld (BASIC_DAC+2), a ;
2752
ld a, (BASIC_DAC+3) ;
2754
ld (BASIC_DAC+3), a ;
2760
if defined BOOLEAN.OR
2762
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2763
ld hl, BASIC_ARG+2 ;
2765
ld (BASIC_DAC+2), a ;
2767
ld a, (BASIC_DAC+3) ;
2769
ld (BASIC_DAC+3), a ;
2775
if defined BOOLEAN.XOR
2777
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2778
ld hl, BASIC_ARG+2 ;
2780
ld (BASIC_DAC+2), a ;
2782
ld a, (BASIC_DAC+3) ;
2784
ld (BASIC_DAC+3), a ;
2790
if defined BOOLEAN.EQV
2792
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2793
ld hl, BASIC_ARG+2 ;
2796
ld (BASIC_DAC+2), a ;
2798
ld a, (BASIC_DAC+3) ;
2801
ld (BASIC_DAC+3), a ;
2807
if defined BOOLEAN.IMP
2809
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2810
ld hl, BASIC_ARG+2 ;
2813
ld (BASIC_DAC+2), a ;
2815
ld a, (BASIC_DAC+3) ;
2818
ld (BASIC_DAC+3), a ;
2824
if defined BOOLEAN.SHR
2826
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2827
ld a, (BASIC_ARG+2) ;
2829
jp z, MATH.PARM.PUSH ; return if not shift
2830
ld b, a ; shift count
2831
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2834
djnz BOOLEAN.SHR.INT.N ; next shift
2836
jp MATH.PARM.PUSH ; return DAC
2840
if defined BOOLEAN.SHL
2842
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2843
ld a, (BASIC_ARG+2) ;
2845
jp z, MATH.PARM.PUSH ; return if not shift
2846
ld b, a ; shift count
2847
BOOLEAN.SHL.INT.N: rl (ix) ;
2850
djnz BOOLEAN.SHL.INT.N ; next shift
2852
jp MATH.PARM.PUSH ; return DAC
2856
if defined BOOLEAN.NOT
2858
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2860
ld (BASIC_DAC+2), a ;
2861
ld a, (BASIC_DAC+3) ;
2863
ld (BASIC_DAC+3), a ;
2871
;---------------------------------------------------------------------------------------------------------
2872
; MEMORY ALLOCATION ROUTINES
2873
;---------------------------------------------------------------------------------------------------------
2874
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2875
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2876
; https://www.msx.org/users/samsaga2
2877
;---------------------------------------------------------------------------------------------------------
2878
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2879
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2880
block.next: equ 0 ; next free block address
2881
block.size: equ 2 ; size of block including header
2882
block: equ 4 ; block.next + block.size
2886
ld ix,memory.heap_start ; first block
2887
ld hl,memory.heap_start+block ; second block
2888
;; first block NEXT=secondblock, SIZE=0
2889
;; with this block we have a fixed start location
2890
;; because never will be allocated
2891
ld (ix+block.next),l
2892
ld (ix+block.next+1),h
2893
ld (ix+block.size),0
2894
ld (ix+block.size+1),0
2895
;; second block NEXT=0, SIZE=all
2896
;; the first and only free block have all available memory
2897
ld (ix+block.next+block),0
2898
ld (ix+block.next+block+1),0
2900
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2903
ld de, memory.heap_start + (block * 2) + 100
2905
;ld de, block * 2 + 100
2907
ld (ix+block.size+block),l
2908
ld (ix+block.size+block+1),h
2912
;; IN BC=size, OUT IX=memptr, NZ=ok
2920
ld ix,memory.heap_start ; this
2923
ld l,(ix+block.size)
2924
ld h,(ix+block.size+1)
2927
jp z, memory.alloc.exactfit
2928
jp c, memory.alloc.nextblock
2929
;; split found block
2930
memory.alloc.splitfit:
2931
;; free space must allow at least two blocks headers (current + next)
2933
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2936
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2937
memory.alloc.splitfit.do:
2938
;; newfreeblock = this + BC
2942
;; prevblock->next = newfreeblock
2943
ld (iy+block.next),l
2944
ld (iy+block.next+1),h
2945
;; newfreeblock->next = this->next
2947
pop iy ; iy = newfreeblock
2948
ld l,(ix+block.next)
2949
ld h,(ix+block.next+1)
2950
ld (iy+block.next),l
2951
ld (iy+block.next+1),h
2952
;; newfreeblock->size = this->size - BC
2953
ld l,(ix+block.size)
2954
ld h,(ix+block.size+1)
2957
ld (iy+block.size),l
2958
ld (iy+block.size+1),h
2960
ld (ix+block.size),c
2961
ld (ix+block.size+1),b
2963
;; use whole found block
2964
memory.alloc.exactfit:
2965
;; prevblock->next = this->next - remove block from free list
2966
ld l,(ix+block.next)
2967
ld h,(ix+block.next+1)
2968
ld (iy+block.next),l
2969
ld (iy+block.next+1),h
2978
memory.alloc.nextblock:
2979
ld l,(ix+block.next)
2980
ld h,(ix+block.next+1)
2987
;; this = this->next
2990
jp memory.alloc.find
2995
;; HL = IX - block_header_size
3002
ld ix,memory.heap_start
3004
ld e,(ix+block.next)
3005
ld d,(ix+block.next+1)
3008
jp z, memory.free.passedend
3009
sbc hl,de ; test this (HL) against next (DE)
3010
jr c, memory.free.found ; if DE > HL
3011
add hl,de ; restore hl value
3013
pop ix ; current = next
3016
;; ix=prev, hl=this, de=next
3018
add hl,de ; restore hl value
3019
ld (ix+block.next), l
3020
ld (ix+block.next+1), h ; prev->next = this
3023
ld (iy+block.next), e
3024
ld (iy+block.next+1), d ; this->next = next
3025
push ix ; prev x this
3030
call memory.free.coalesce
3031
pop ix ; this x next
3032
jr memory.free.coalesce
3036
memory.free.coalesce:
3037
ld c, (iy+block.size)
3038
ld b, (iy+block.size+1) ; bc = this->size
3042
adc hl, bc ; hl = this + this->size
3046
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
3047
jr z, memory.free.coalesce.do
3048
push ix ; else, new *this = *next
3051
memory.free.coalesce.do:
3052
ld l, (ix+block.size)
3053
ld h, (ix+block.size+1) ; hl = next->size
3055
adc hl, bc ; hl += this->size
3056
ld (iy+block.size), l
3057
ld (iy+block.size+1), h ; this->size = hl
3058
ld l, (ix+block.next)
3059
ld h, (ix+block.next+1) ; hl = next->next
3060
ld (iy+block.next), l
3061
ld (iy+block.next+1), h ; this->next = hl
3064
memory.free.passedend:
3065
;; append block at the end of the free list
3066
ld (ix+block.next),l
3067
ld (ix+block.next+1),h
3070
ld (iy+block.next),0
3071
ld (iy+block.next+1),0
3077
ld ix,memory.heap_start
3079
memory.get_free.count:
3081
add a,(ix+block.size)
3084
adc a,(ix+block.size+1)
3086
ld l,(ix+block.next)
3087
ld h,(ix+block.next+1)
3093
jr memory.get_free.count
3095
memory.error: ld e, 7 ; out of memory
3096
__call_basic BASIC_ERROR_HANDLER ;
3101
;---------------------------------------------------------------------------------------------------------
3103
; By: Amaury Carvalho, 2019
3104
;---------------------------------------------------------------------------------------------------------
3106
; https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Algorithm_for_integer_arithmetic
3107
; https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
3108
; https://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm#C
3109
; https://www.msx.org/wiki/MSX-BASIC_Instructions
3110
;---------------------------------------------------------------------------------------------------------
3112
;---------------------------------------------------------------------------------------------------------
3114
;---------------------------------------------------------------------------------------------------------
3116
BIOS_WRTVDP: EQU 0x0047
3117
BIOS_RDVRM: EQU 0x004A
3118
BIOS_WRTVRM: EQU 0x004D
3119
BIOS_LDIRVM: EQU 0x005C
3120
BIOS_LDIRMV: EQU 0x0059
3121
BIOS_PNTINI: EQU 0x18CF
3122
BIOS_RIGHTC: EQU 0x16C5 ; Move current pixel physical address right
3123
BIOS_TRIGHTC: EQU 0x16AC ; Test then RIGHTC if legal
3124
BIOS_LEFTC: EQU 0x16EE ; Move current pixel physical address left
3125
BIOS_TLEFTC: EQU 0x16D8 ; Test then LEFTC if legal
3126
BIOS_UPC: EQU 0x175D ; Move current pixel physical address up
3127
BIOS_TUPC: EQU 0x173C ; Test then UPC if legal
3128
BIOS_DOWNC: EQU 0x172A ; Move current pixel physical address down
3129
BIOS_TDOWNC: EQU 0x170A ; Test then DOWNC if legal
3130
BIOS_DCOMPR: EQU 0x146A ; compare HL and DE (Flag NC if HL>DE, Flag Z if HL=DE, Flag C if HL<DE)
3131
BIOS_FILVRM: EQU 0x0056 ; fill VRAM with value
3133
BIOS_BIGFIL: EQU 0x016B ; msx 2
3134
BIOS_NRDVRM: EQU 0x0174 ; msx 2
3135
BIOS_NWRVRM: EQU 0x0177 ; msx 2
3136
BIOS_NRDVDP: EQU 0x013E ; msx 2
3137
BIOS_VDPSTA: EQU 0x0131 ; msx 2
3138
BIOS_NWRVDP: EQU 0x012D ; msx 2 (0x0647)
3140
BASIC_SUB_LINE: equ 0x58fc
3141
BASIC_SUB_LINEBOX: equ 0x5912
3142
BASIC_SUB_LINEBOXFILLED: equ 0x58C1
3143
BASIC_SUB_CIRCLE: equ 0x5B19
3144
BASIC_SUB_PAINT1: equ 0x59DA ;0x59C8
3145
BASIC_SUB_PAINT2: equ 0x0069 ;0x2664 ;0x2651+3
3147
;---------------------------------------------------------------------------------------------------------
3149
;---------------------------------------------------------------------------------------------------------
3151
BIOS_RG0SAV: EQU 0xF3DF
3152
BIOS_RG1SAV: EQU 0xF3E0
3153
BIOS_RG8SAV: EQU 0xFFE7
3154
BIOS_BDRATR: EQU 0xFCB2
3155
BIOS_STATFL: EQU 0xF3E7 ; VDP status register
3157
BIOS_CXOFF: EQU 0xF945
3158
BIOS_CYOFF: EQU 0xF947
3159
BIOS_GXPOS: EQU 0xFCB3
3160
BIOS_GYPOS: EQU 0xFCB5
3162
BIOS_GRPNAM: EQU 0xF3C7 ; pattern name table
3163
BIOS_GRPCOL: EQU 0xF3C9 ; colour table
3164
BIOS_GRPCGP: EQU 0xF3CB ; pattern generator table
3165
BIOS_GRPATR: EQU 0xF3CD ; sprite attribute table
3166
BIOS_GRPPAT: EQU 0xF3CF ; sprite generator table
3167
BIOS_CGPNT: EQU 0xF920 ; 2 - current MSX Font location (0x1BBF)
3168
BIOS_ATRBAS: EQU 0xF928 ; sprite attribute table
3170
BIOS_MLTNAM: EQU 0xF3D1 ; pattern name table (screen 3, multicolor)
3171
BIOS_MLTCOL: EQU 0xF3D3 ; colour table (screen 3, multicolor)
3172
BIOS_MLTCGP: EQU 0xF3D5 ; pattern generator table (screen 3, multicolor)
3173
BIOS_MLTATR: EQU 0xF3D7 ; sprite attribute table (screen 3, multicolor)
3174
BIOS_MLTPAT: EQU 0xF3D9 ; sprite generator table (screen 3, multicolor)
3176
BIOS_ASPECT: equ 0xF931 ;2 Aspect ratio of the circle; set by <ratio> of CIRCLE.
3177
BIOS_CENCNT: equ 0xF933 ;2 Counter used by CIRCLE.
3178
BIOS_CLINEF: equ 0xF935 ;1 Flag to draw line to centre, Used set by CIRCLE
3179
BIOS_CNPNTS: equ 0xF936 ;2 Point to be plottted in a 45° segment, Used set by CIRCLE
3180
BIOS_CPLOTF: equ 0xF938 ;1 Plot polarity flag, Used set by CIRCLE
3181
BIOS_CPCNT: equ 0xF939 ;2 Number of points in 1/8 of circle, Used set by CIRCLE.
3182
BIOS_CPCNT8: equ 0xF93B ;2 Number of points in the circle. Used by CIRCLE.
3183
BIOS_CRCSUM: equ 0xF93D ;2 Cyclic redundancy check sum of the circle. Used by CIRCLE.
3184
BIOS_CSTCNT: equ 0xF93F ;2 Variable to maintain the number of points of the starting angle. Used by the instruction CIRCLE
3185
BIOS_CSCLXY: equ 0xF941 ;1 Scale of X & Y. Used by the instruction CIRCLE
3186
BIOS_ASPCT1: equ 0xF40B ;2 256/aspect ratio for Basic instruction CIRCLE.
3187
BIOS_ASPCT2: equ 0xF40D ;2 256*aspect ratio for Basic instruction CIRCLE.
3188
BIOS_MAXUPD: equ 0xF3EC ;3 Work area used by the instruction CIRCLE, contains JP 0000h at start.
3189
BIOS_MINUPD: equ 0xF3EF ;3 Work area used by the instruction CIRCLE, contains JP 0000h at start.
3191
BIOS_PARM1: EQU 0xF6E8 ; 100
3192
BIOS_PARM2: EQU 0xF750 ; 100
3194
GFX_TEMP: EQU BIOS_PARM1 ; 2
3195
GFX_TEMP1: EQU GFX_TEMP + 2 ; 2
3196
GFX_TEMP2: EQU GFX_TEMP1 + 2 ; 2
3197
GFX_TEMP3: EQU GFX_TEMP2 + 2 ; 2
3198
GFX_TEMP4: EQU GFX_TEMP3 + 2 ; 2
3199
GFX_TEMP5: EQU GFX_TEMP4 + 2 ; 2
3200
GFX_TEMP6: EQU GFX_TEMP5 + 2 ; 2
3201
GFX_TEMP7: EQU GFX_TEMP6 + 2 ; 2
3202
GFX_TEMP8: EQU GFX_TEMP7 + 2 ; 2
3203
GFX_TEMP9: EQU GFX_TEMP8 + 2 ; 2
3205
GFX_MAX_X: EQU 0xFCA4 ; 1 (CASSETE LOWLIM)
3206
GFX_MAX_Y: EQU 0xFCA5 ; 1 (CASSETE WINWID)
3208
GFX_SPRITE_FLAGS: EQU 0xF40A ; 1 (CASSETE HEADER) - bits 0=check screen limits, 1=check walls, 2=check hotspots,
3209
; 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
3210
GFX_SPRITE_SIZE_DAT: EQU 0xF3FC ; 1 (CASSETE CS1200)
3211
GFX_SPRITE_SIZE_SCR: EQU 0xF3FD ; 1
3212
GFX_SPRITE_WALLS: EQU 0xF406 ; 2 (CASSETE LOW)
3213
GFX_SPRITE_HOTSPOTS: EQU 0xF408 ; 2 (CASSETE HIGH)
3214
GFX_SPRITE_HOTSPOT_TILE: EQU 0xF405 ; 1 (CASSETE CS2400)
3215
GFX_SPRITE_COLLIDER: EQU 0xF400 ; 1 (CASSETE CS1200)
3216
GFX_SPRITE_COLLISION: EQU 0xF7B5 ; 2 (ARYTA2)
3218
GFX_MUSIC_START: EQU 0xF401 ; 2 (CASSETE CS2400)
3219
GFX_MUSIC_NEXT: EQU 0xF403 ; 2
3220
GFX_MUSIC_PREV: EQU 0xF74C ; 2 (PRMPRV)
3222
GFX_TEMP10: EQU 0xF3FE ; 2 (CASSETE CS1200)
3224
;BIOS_SCR_SIZE_X: dw 240, 256, 256, 64, 256, 256, 512, 512, 256, 512, 256, 256, 256
3225
;BIOS_SCR_SIZE_Y: dw 192, 192, 192, 48, 192, 212, 212, 212, 212, 384, 212, 212, 212
3226
BIOS_SCR_SIZE_X: db 239, 255, 255, 63, 255, 255, 255, 255, 255, 255, 255, 255, 255
3227
BIOS_SCR_SIZE_Y: db 191, 191, 191, 47, 191, 211, 211, 211, 211, 255, 211, 211, 211
3230
;---------------------------------------------------------------------------------------------------------
3231
; gfxIsScreenModeMSX2
3232
; return if screen mode is from MSX 2
3233
; out C is set, if MSX2 and screen mode above 3
3234
;---------------------------------------------------------------------------------------------------------
3236
gfxIsScreenModeMSX2:
3237
ld a, (BIOS_VERSION)
3239
jp nz, BIOS_CHKNEW ; if not MSX1, jump to CHKNEW
3243
;---------------------------------------------------------------------------------------------------------
3245
; set current screen mode
3246
; in A = screen number
3247
;---------------------------------------------------------------------------------------------------------
3251
call gfxInitScreenWorkspace
3253
ld a, (BIOS_VERSION)
3255
jr nz, gfxSetScreenMode.1 ; if not MSX1, jump
3258
call nc, gfxSetScreenMode.0 ; if screen mode >= 4, change to screen 2
3259
__call_bios BIOS_CHGMOD ; change the screen mode (msx1)
3260
jr gfxSetScreenMode.2
3268
ld ix, BIOS_CHGMOD2 ; change the screen mode (msx2)
3272
call gfxGetScreenHeight
3273
call gfxGetScreenWidth
3274
call gfxGetSpriteSize
3275
jp gfxFillSpriteCollisionTable
3277
gfxInitScreenWorkspace:
3279
ld (GFX_SPRITE_FLAGS), a ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
3281
ld (GFX_SPRITE_WALLS), a
3282
ld (GFX_SPRITE_WALLS+1), a
3283
ld (GFX_SPRITE_HOTSPOTS), a
3284
ld (GFX_SPRITE_HOTSPOTS+1), a
3285
ld (GFX_MUSIC_START), a
3286
ld (GFX_MUSIC_START+1), a
3287
ld (GFX_MUSIC_NEXT), a
3288
ld (GFX_MUSIC_NEXT+1), a
3289
ld (GFX_MUSIC_PREV), a
3290
ld (GFX_MUSIC_PREV+1), a
3291
ld (GFX_SPRITE_HOTSPOT_TILE), a
3292
ld (GFX_SPRITE_COLLIDER), a
3295
;---------------------------------------------------------------------------------------------------------
3297
; return current screen mode
3298
; out A = screen number (0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode)
3299
;---------------------------------------------------------------------------------------------------------
3305
;---------------------------------------------------------------------------------------------------------
3307
; set current screen location
3310
;---------------------------------------------------------------------------------------------------------
3313
ld (BIOS_GRPACX), bc ; x
3314
;ld (BIOS_GXPOS), bc
3315
ld (BIOS_GRPACY), de ; y
3316
;ld (BIOS_GYPOS), de
3319
;---------------------------------------------------------------------------------------------------------
3321
; refresh current screen location
3322
;---------------------------------------------------------------------------------------------------------
3325
ld bc, (BIOS_GRPACX) ; x
3326
ld de, (BIOS_GRPACY) ; y
3328
call gfxIsScreenModeMSX2
3329
jr nc, gfxRefreshXY.2 ; if MSX2 and screen mode above 3
3330
__call_bios BIOS_SCALXY ; BC = X, DE = Y
3331
__call_bios BIOS_MAPXYC ; in BC = X, DE = Y
3334
ld ix, BIOS_SCALXY2 ; BC = X, DE = Y
3336
ld ix, BIOS_MAPXYC2 ; in BC = X, DE = Y
3339
;---------------------------------------------------------------------------------------------------------
3341
; get current screen location
3344
;---------------------------------------------------------------------------------------------------------
3347
ld bc, (BIOS_GRPACX) ; x
3348
ld de, (BIOS_GRPACY) ; y
3351
;---------------------------------------------------------------------------------------------------------
3352
; gfxGetScreenHeight
3354
; out a = screen height
3355
;---------------------------------------------------------------------------------------------------------
3360
ld hl, BIOS_SCR_SIZE_Y
3371
;---------------------------------------------------------------------------------------------------------
3374
; out a = screen height
3375
;---------------------------------------------------------------------------------------------------------
3380
ld hl, BIOS_SCR_SIZE_X
3391
;---------------------------------------------------------------------------------------------------------
3393
; get sprite data size
3394
; out a = sprite data size
3395
;---------------------------------------------------------------------------------------------------------
3400
ld a, (BIOS_RG1SAV) ; bit 0 = double size, bit 1 = sprite size (0=8 pixels, 1=16 pixels)
3402
jr z, gfxGetSpriteSize.1
3407
jr z, gfxGetSpriteSize.2
3411
ld (GFX_SPRITE_SIZE_DAT), bc
3417
if defined GFX_FAST and defined PAINT
3419
;---------------------------------------------------------------------------------------------------------
3421
; move screen current location up
3422
; out: carry if off screen
3423
;---------------------------------------------------------------------------------------------------------
3428
ld de, (BIOS_GRPACY)
3433
ld (BIOS_GRPACY), de
3444
;---------------------------------------------------------------------------------------------------------
3446
; move screen current location down
3447
; out: carry if off screen
3448
;---------------------------------------------------------------------------------------------------------
3455
ld de, (BIOS_GRPACY)
3460
ld (BIOS_GRPACY), de
3471
;---------------------------------------------------------------------------------------------------------
3473
; move screen current location left
3474
; out: carry if off screen
3475
;---------------------------------------------------------------------------------------------------------
3480
ld de, (BIOS_GRPACX)
3485
ld (BIOS_GRPACX), de
3496
;---------------------------------------------------------------------------------------------------------
3498
; move screen current location right
3499
; out: carry if off screen
3500
;---------------------------------------------------------------------------------------------------------
3507
ld de, (BIOS_GRPACX)
3512
ld (BIOS_GRPACX), de
3525
;---------------------------------------------------------------------------------------------------------
3527
; push current screen location
3528
;---------------------------------------------------------------------------------------------------------
3533
ld iy, (BIOS_GRPACX) ; x
3535
ld iy, (BIOS_GRPACY) ; y
3541
;---------------------------------------------------------------------------------------------------------
3543
; pop current screen location
3546
;---------------------------------------------------------------------------------------------------------
3555
;---------------------------------------------------------------------------------------------------------
3557
; set current foreground color
3559
;---------------------------------------------------------------------------------------------------------
3562
ld (BIOS_FORCLR), a ; foreground color
3566
;---------------------------------------------------------------------------------------------------------
3568
; get current foreground color
3570
;---------------------------------------------------------------------------------------------------------
3573
ld a, (BIOS_FORCLR) ; foreground color
3576
;---------------------------------------------------------------------------------------------------------
3578
; set current background color
3580
;---------------------------------------------------------------------------------------------------------
3583
ld (BIOS_BAKCLR), a ; foreground color
3586
;---------------------------------------------------------------------------------------------------------
3588
; get current background color
3590
;---------------------------------------------------------------------------------------------------------
3593
ld a, (BIOS_BAKCLR) ; foreground color
3596
;---------------------------------------------------------------------------------------------------------
3598
; set current border color
3600
;---------------------------------------------------------------------------------------------------------
3603
ld (BIOS_BDRCLR), a ; border color
3606
;---------------------------------------------------------------------------------------------------------
3608
; get current border color
3610
;---------------------------------------------------------------------------------------------------------
3613
ld a, (BIOS_BDRCLR) ; border color
3616
;---------------------------------------------------------------------------------------------------------
3618
; set fill border color
3620
;---------------------------------------------------------------------------------------------------------
3623
ld (BIOS_BDRATR), a ; border color
3626
;---------------------------------------------------------------------------------------------------------
3628
; get fill border color
3630
;---------------------------------------------------------------------------------------------------------
3633
ld a, (BIOS_BDRATR) ; border color
3636
;---------------------------------------------------------------------------------------------------------
3638
; set current color (foreground, background and border)
3639
;---------------------------------------------------------------------------------------------------------
3644
jp nz, BIOS_CHGCLR2 ; change VDP colors - msx2
3646
jp nz, BIOS_CHGCLR2 ; change VDP colors - msx2
3647
jp BIOS_CHGCLR ; change VDP colors
3648
; __call_bios BIOS_SETATR ; change the pixel color
3651
;---------------------------------------------------------------------------------------------------------
3653
; set pixel in current position to current foreground color
3654
;---------------------------------------------------------------------------------------------------------
3657
call gfxIsScreenModeMSX2
3658
jr nc, gfxSetPixel.1 ; if MSX2 and screen mode above 3
3659
__call_bios BIOS_SETC
3665
;---------------------------------------------------------------------------------------------------------
3667
; get pixel color in current position
3668
; out A = pixel color
3669
;---------------------------------------------------------------------------------------------------------
3672
call gfxIsScreenModeMSX2
3673
jr nc, gfxGetPixel.1 ; if MSX2 and screen mode above 3
3674
__call_bios BIOS_READC
3682
;---------------------------------------------------------------------------------------------------------
3684
; plot a line from current position to informed destination
3685
; in BC = destination x
3686
; DE = destination y
3687
; https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Algorithm_for_integer_arithmetic
3688
; https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
3689
;---------------------------------------------------------------------------------------------------------
3690
;void line(int x0, int y0, int x1, int y1) {
3691
; int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
3692
; int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
3693
; int err = (dx>dy ? dx : -dy)/2, e2;
3696
; if (x0==x1 && y0==y1) break;
3698
; if (e2 >-dx) { err -= dy; x0 += sx; }
3699
; if (e2 < dy) { err += dx; y0 += sy; }
3704
if not defined GFX_FAST
3705
ld hl, (BIOS_GRPACX)
3707
ld hl, (BIOS_GRPACY)
3709
__call_basic BASIC_SUB_LINE
3714
ld (GFX_TEMP2), bc ; x
3715
ld (GFX_TEMP3), de ; y
3717
ld hl, (BIOS_GRPACX) ; x0
3718
ld de, (GFX_TEMP2) ; x
3721
;jp po, gfxLine.1 ; x0 >= x? else jump to endif
3723
jp c, gfxLine.1 ; x0 < x? jump
3726
ld (GFX_TEMP4), hl ; dx = x0 - x
3728
ld (GFX_TEMP5), hl ; sx = -1
3732
ld de, (BIOS_GRPACX) ; x0
3733
ld hl, (GFX_TEMP2) ; x
3736
ld (GFX_TEMP4), hl ; dx = x - x0
3738
ld (GFX_TEMP5), hl ; sx = 1
3741
ld hl, (BIOS_GRPACY) ; y0
3742
ld de, (GFX_TEMP3) ; y
3745
;jp po, gfxLine.3 ; y0 >= y? else, jump to endif
3747
jp c, gfxLine.3 ; y0 < y? jump
3750
ld (GFX_TEMP6), hl ; dy = y0 - y
3752
ld (GFX_TEMP7), hl ; sy = -1
3756
ld de, (BIOS_GRPACY) ; y0
3757
ld hl, (GFX_TEMP3) ; y
3760
ld (GFX_TEMP6), hl ; dy = y - y0
3762
ld (GFX_TEMP7), hl ; sy = 1
3765
ld hl, (GFX_TEMP6) ; dy
3768
jp z, gfxLine.h ; dy = 0?
3770
ld hl, (GFX_TEMP4) ; dx
3773
jp z, gfxLine.v ; dx = 0?
3775
ld de, (GFX_TEMP4) ; dx
3776
ld hl, (GFX_TEMP6) ; dy
3781
ld (GFX_TEMP9), hl ; dxy = dx + dy
3783
ld de, (GFX_TEMP4) ; dx
3784
ld hl, (GFX_TEMP6) ; dy
3787
;jp pe, gfxLine.5 ; dy < dx? else, jump to endif
3789
jp z, gfxLine.5 ; dy = dx? jump
3790
jp nc, gfxLine.5 ; dy > dx? jump
3792
ld de, (GFX_TEMP6) ; dy
3798
ld (GFX_TEMP8), de ; err = -dy / 2
3802
ld hl, (GFX_TEMP4) ; dx
3806
ld (GFX_TEMP8), hl ; err = dx/2
3811
ld hl, (GFX_TEMP9) ; dxy
3814
jp nc, gfxLine.loop.0 ; dxy > 0? jump
3815
;jp z, gfxLine.loop.0 ; dxy = 0? jump
3821
ld de, (GFX_TEMP4) ; dx
3824
ld de, (GFX_TEMP8) ; e2 = err
3828
;jp pe, gfxLine.loop.1 ; if -dx < e2, else jump to endif
3830
jp z, gfxLine.loop.1 ; -dx = e2? jump
3831
jp nc, gfxLine.loop.1 ; -dx > e2? jump
3832
ld hl, (GFX_TEMP8) ; err
3833
ld de, (GFX_TEMP6) ; dy
3836
ld (GFX_TEMP8), hl ; err -= dy
3838
ld hl, (GFX_TEMP9) ; dxy
3840
ld (GFX_TEMP9), hl ; dxy -= 1
3842
ld hl, (BIOS_GRPACX)
3846
ld (BIOS_GRPACX), hl ; x0 += sx
3850
ld de, (GFX_TEMP6) ; dy
3853
;jp pe, gfxLine.loop.2 ; if e2 < dy, else jump to endif
3855
jp z, gfxLine.loop.2 ; e2 = dy? jump
3856
jp nc, gfxLine.loop.2 ; e2 > dy? jump
3857
ld hl, (GFX_TEMP8) ; err
3858
ld de, (GFX_TEMP4) ; dx
3861
ld (GFX_TEMP8), hl ; err += dx
3863
ld hl, (GFX_TEMP9) ; dxy
3865
ld (GFX_TEMP9), hl ; dxy -= 1
3867
ld hl, (BIOS_GRPACY)
3871
ld (BIOS_GRPACY), hl ; y0 += sy
3878
ld a, (GFX_TEMP5) ; sx
3880
jr z, gfxLine.h.1 ; if a is positive
3881
ld hl, (BIOS_GRPACX)
3885
ld (BIOS_GRPACX), hl
3889
__call_bios BIOS_FETCHC
3890
ld hl, (GFX_TEMP4) ; dx
3892
call gfxDrawHorLine ; HL = pixel count
3896
ld a, (GFX_TEMP7) ; sy
3898
jr z, gfxLine.v.1 ; if a is positive
3899
ld hl, (BIOS_GRPACY)
3903
ld (BIOS_GRPACY), hl
3908
ld hl, (GFX_TEMP6) ; dy
3911
jp gfxBox.drawVerLine
3916
if defined BOX or defined FBOX or defined BOX_STEP or defined FBOX_STEP
3918
;---------------------------------------------------------------------------------------------------------
3920
; plot a box from current position to informed destination
3921
; in BC = destination x
3922
; DE = destination y
3923
; A = filled flag (0 = not filled, <>0 = filled)
3924
;---------------------------------------------------------------------------------------------------------
3928
if not defined GFX_FAST
3929
ld hl, (BIOS_GRPACX)
3931
ld hl, (BIOS_GRPACY)
3933
ld hl, BASIC_SUB_LINEBOX
3936
call gfxIsScreenModeMSX2
3938
ld hl, BASIC_SUB_LINEBOXFILLED
3943
jp BIOS_CALBAS ; BIOS_CALSLT
3954
call gfxAdjustDestXY
3956
jr nz, gfxBox.filled
3960
call gfxBox.drawHorLine
3962
ld de, (BIOS_GRPACX)
3966
ld (BIOS_GRPACX), hl
3968
call gfxBox.drawVerLine
3970
call gfxBox.drawVerLine
3971
call gfxBox.drawHorLine
3979
ld bc, (BIOS_GRPACX)
3981
call gfxBox.drawHorLine
3983
ld (BIOS_GRPACX), bc
3984
ld bc, (BIOS_GRPACY)
3986
ld (BIOS_GRPACY), bc
3992
jr nz, gfxBox.filled.loop
4004
gfxBox.drawVerLine.loop:
4012
jr nz, gfxBox.drawVerLine.loop
4015
;---------------------------------------------------------------------------------------------------------
4017
; draw a horizontal line
4019
;---------------------------------------------------------------------------------------------------------
4024
jr c, gfxDrawHorLine.2 ; if screen mode < 5 then jump
4026
ret nz ; return if negative
4029
jr nz, gfxDrawHorLine.1
4031
ret z ; return if hl = 0
4040
jr nz, gfxDrawHorLine.1
4044
__call_bios BIOS_NSETCX ; HL = fill count
4047
;---------------------------------------------------------------------------------------------------------
4049
; invert if dest XY is less than current XY position
4052
;---------------------------------------------------------------------------------------------------------
4056
ld (GFX_TEMP2), bc ; x
4057
ld (GFX_TEMP3), de ; y
4059
; verify x againt current position
4060
ld hl, (BIOS_GRPACX)
4063
sbc hl, de ; dx = x1 - x0
4064
bit 7, h ; result is negative?
4065
jr z, gfxAdjustDestXY.1
4067
ld (BIOS_GRPACX), hl
4075
; verify y againt current position
4076
ld hl, (BIOS_GRPACY)
4079
sbc hl, de ; dy = y1 - y0
4080
bit 7, h ; result is negative?
4081
jr z, gfxAdjustDestXY.2
4083
ld (BIOS_GRPACY), hl
4091
; refresh new position
4101
;---------------------------------------------------------------------------------------------------------
4103
; plot a circle centered in current position
4104
; BC = tracing end x
4105
; DE = tracing end y
4107
; A = filled flag (0 = not filled, <>0 = filled)
4108
; https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
4109
; https://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm#C
4110
;---------------------------------------------------------------------------------------------------------
4114
if not defined GFX_FAST
4116
ret nz ; return if negative radius
4117
ld (BIOS_GXPOS), hl ; circle ray
4119
ld bc, (BIOS_GRPACY)
4123
;jr nz, gfxDrawCircle.1
4130
__call_basic BASIC_SUB_CIRCLE
4133
ld (GFX_TEMP), hl ; radius
4136
ret z ; return if zero radius
4138
ret nz ; return if negative radius
4141
ld (GFX_TEMP1), bc ; x0
4142
ld (GFX_TEMP2), de ; y0
4145
jp nz, gfxCircle.filled
4147
gfxCircle.notFilled:
4149
ld de, (GFX_TEMP) ; radius
4152
ld (GFX_TEMP3), hl ; f = 1 - radius
4155
ld (GFX_TEMP4), hl ; ddF_x = 0
4164
ld (GFX_TEMP5), hl ; ddF_y = -2 * radius
4167
ld (GFX_TEMP6), hl ; x = 0
4170
ld (GFX_TEMP7), hl ; y = radius
4172
; plot(x0, y0 + radius)
4173
ld de, (GFX_TEMP) ; radius
4174
ld hl, (GFX_TEMP2) ; y0
4178
ld bc, (GFX_TEMP1) ; x0
4182
; plot(x0, y0 - radius)
4183
ld de, (GFX_TEMP) ; radius
4184
ld hl, (GFX_TEMP2) ; y0
4188
ld bc, (GFX_TEMP1) ; x0
4192
; plot(x0 + radius, y0)
4193
ld hl, (GFX_TEMP1) ; x0
4194
ld de, (GFX_TEMP) ; radius
4201
ld de, (GFX_TEMP2) ; y0
4205
; plot(x0 - radius, y0)
4206
ld hl, (GFX_TEMP1) ; x0
4207
ld de, (GFX_TEMP) ; radius
4214
ld de, (GFX_TEMP2) ; y0
4217
jp gfxCircle.notFilled.3
4219
gfxCircle.notFilled.1:
4220
ld hl, (GFX_TEMP3) ; f
4222
jr nz, gfxCircle.notFilled.2 ; if( f < 0 ), jump
4224
ld hl, (GFX_TEMP7) ; y -= 1
4228
ld hl, (GFX_TEMP5) ; ddF_y += 2
4233
ld hl, (GFX_TEMP3) ; f
4234
ld de, (GFX_TEMP5) ; ddF_y
4237
ld (GFX_TEMP3), hl ; f += ddF_y
4239
gfxCircle.notFilled.2:
4240
ld hl, (GFX_TEMP6) ; x
4242
ld (GFX_TEMP6), hl ; x++
4244
ld hl, (GFX_TEMP4) ; ddF_x += 2
4249
ld hl, (GFX_TEMP3) ; f
4250
ld de, (GFX_TEMP4) ; ddF_x
4254
ld (GFX_TEMP3), hl ; f += ddF_x + 1
4256
; plot(x0 + x, y0 + y)
4257
ld hl, (GFX_TEMP1) ; x0
4258
ld de, (GFX_TEMP6) ; x
4265
ld hl, (GFX_TEMP2) ; y0
4266
ld de, (GFX_TEMP7) ; y
4273
; plot(x0 - x, y0 + y)
4274
ld hl, (GFX_TEMP1) ; x0
4275
ld de, (GFX_TEMP6) ; x
4282
ld hl, (GFX_TEMP2) ; y0
4283
ld de, (GFX_TEMP7) ; y
4290
; plot(x0 + x, y0 - y)
4291
ld hl, (GFX_TEMP1) ; x0
4292
ld de, (GFX_TEMP6) ; x
4299
ld hl, (GFX_TEMP2) ; y0
4300
ld de, (GFX_TEMP7) ; y
4307
; plot(x0 - x, y0 - y)
4308
ld hl, (GFX_TEMP1) ; x0
4309
ld de, (GFX_TEMP6) ; x
4316
ld hl, (GFX_TEMP2) ; y0
4317
ld de, (GFX_TEMP7) ; y
4324
; plot(x0 + y, y0 + x)
4325
ld hl, (GFX_TEMP1) ; x0
4326
ld de, (GFX_TEMP7) ; y
4333
ld hl, (GFX_TEMP2) ; y0
4334
ld de, (GFX_TEMP6) ; x
4341
; plot(x0 - y, y0 + x)
4342
ld hl, (GFX_TEMP1) ; x0
4343
ld de, (GFX_TEMP7) ; y
4350
ld hl, (GFX_TEMP2) ; y0
4351
ld de, (GFX_TEMP6) ; x
4358
; plot(x0 + y, y0 - x)
4359
ld hl, (GFX_TEMP1) ; x0
4360
ld de, (GFX_TEMP7) ; y
4367
ld hl, (GFX_TEMP2) ; y0
4368
ld de, (GFX_TEMP6) ; x
4375
; plot(x0 - y, y0 - x)
4376
ld hl, (GFX_TEMP1) ; x0
4377
ld de, (GFX_TEMP7) ; y
4384
ld hl, (GFX_TEMP2) ; y0
4385
ld de, (GFX_TEMP6) ; x
4392
gfxCircle.notFilled.3:
4393
ld hl, (GFX_TEMP6) ; x
4394
ld de, (GFX_TEMP7) ; y
4397
jp c, gfxCircle.notFilled.1 ; while( x < y )
4398
ld bc, (GFX_TEMP1) ; x0
4399
ld de, (GFX_TEMP2) ; y0
4404
call gfxCircle.notFilled
4405
ld hl, (BIOS_BDRATR)
4407
ld hl, (BIOS_FORCLR)
4408
ld (BIOS_BDRATR), hl
4412
ld (BIOS_BDRATR), hl
4418
if defined PAINT or (defined CIRCLE and defined GFX_FAST)
4420
;---------------------------------------------------------------------------------------------------------
4422
; Fill current region delimited by border attribute color changing pixels to foreground color
4423
; in: a = fill type (0 = not symmetric, 1 = symmetric)
4424
;---------------------------------------------------------------------------------------------------------
4428
if not defined GFX_FAST
4430
ld bc, (BIOS_GRPACX)
4432
ld de, (BIOS_GRPACY)
4450
call gfxIsScreenModeMSX2
4451
jr nc, gfxBorderFill.1 ; if MSX2 and screen mode above 3, jump
4455
__call_basic BASIC_SUB_PAINT1
4459
__call_basic BASIC_SUB_PAINT1
4461
;ld ix, BASIC_SUB_PAINT2
4467
ld hl, (BIOS_GRPACY)
4469
call gfxBorderFill.down
4472
ld (BIOS_GRPACY), hl
4474
call gfxBorderFill.up
4476
ld (BIOS_GRPACY), hl
4481
call gfxBorderFill.line
4487
jr nz, gfxBorderFill.down
4491
call gfxBorderFill.line
4497
jr nz, gfxBorderFill.up
4501
ld de, (BIOS_GRPACX)
4504
ld de, 1 ; skip count
4505
__call_bios BIOS_SCANR
4507
;ld (GFX_TEMP2), hl ; pixel count transversed
4510
ld (BIOS_GRPACX), de
4513
;cp 0 ; 0 = not symmetric, 1 = symmetric
4514
;jr z, gfxBorderFill.line.1
4516
;__call_bios BIOS_NSETCX ; HL = fill count
4517
;jr gfxBorderFill.line.2
4518
gfxBorderFill.line.1:
4520
ld de, 0 ; skip count
4521
__call_bios BIOS_SCANL
4522
gfxBorderFill.line.2:
4524
ld (BIOS_GRPACX), de
4528
;---------------------------------------------------------------------------------------------------------
4530
; Fload fill current region changing current pixel color to foreground color
4531
; https://en.wikipedia.org/wiki/Flood_fill
4532
;---------------------------------------------------------------------------------------------------------
4536
ld (GFX_TEMP), a ; replacement-color
4537
call gfxFloadFill.recursive
4540
gfxFloadFill.recursive:
4541
; 1. If target-color is equal to replacement-color, return.
4547
; 2. ElseIf the color of node is not equal to target-color, return.
4553
; 3. Else Set the color of node to replacement-color.
4556
; 4. Perform Flood-fill (one step to the left of node, target-color, replacement-color).
4557
gfxFloadFill.recursive.left:
4559
jr c, gfxFloadFill.recursive.right
4560
call gfxFloadFill.recursive
4563
; Perform Flood-fill (one step to the right of node, target-color, replacement-color).
4564
gfxFloadFill.recursive.right:
4566
jr c, gfxFloadFill.recursive.up
4567
call gfxFloadFill.recursive
4570
; Perform Flood-fill (one step to the up of node, target-color, replacement-color).
4571
gfxFloadFill.recursive.up:
4573
jr c, gfxFloadFill.recursive.down
4574
call gfxFloadFill.recursive
4577
; Perform Flood-fill (one step to the down of node, target-color, replacement-color).
4578
gfxFloadFill.recursive.down:
4581
call gfxFloadFill.recursive
4589
if defined SPRITEMODE
4591
;---------------------------------------------------------------------------------------------------------
4593
; set current sprite mode
4595
; 0: Spritesize is 8 by 8 pixels - default value
4596
; 1: Spritesize is 8 by 8 pixels, magnified to 16 by 16 pixels
4597
; 2: Spritesize is 16 by 16 pixels
4598
; 3: Spritesize is 16 by 16 pixels, magnified to 32 by 32 pixels
4599
; RG1SAV bit 0 = magnify sprite (double size)
4600
; RG1SAV bit 1 = sprite size (0=8 pixels, 1=16 pixels)
4601
;---------------------------------------------------------------------------------------------------------
4604
and 3 ; keeps only bits 0 and 1 from A
4607
ld a, (BIOS_RG1SAV) ; get copy from register #1 of VDP
4608
and 0xFC ; clear bits 0 and 1 from A
4609
or b ; put parameter to A (bits 0 and 1)
4610
ld (BIOS_RG1SAV), a ; restore to register #1 of VDP
4611
ld b, a ; value to write
4612
ld c, 1 ; register number to write
4613
call gfxWRTVDP ; write register to VDP
4614
call gfxCLRSPR ; clear sprites
4616
call gfxGetSpriteSize
4617
jp gfxFillSpriteCollisionTable
4621
if defined SPRITE or defined COLOR_SPRITE or defined PUT_SPRITE_COLOR or defined PUT_SPRITE_STEP_COLOR or defined PUT_SPRITE_COLOR_PATNUM or defined PUT_SPRITE_STEP_COLOR_PATNUM or defined LOAD_SET
4623
;---------------------------------------------------------------------------------------------------------
4624
; gfxSetSpriteColorInt
4627
;---------------------------------------------------------------------------------------------------------
4629
gfxSetSpriteColorInt:
4630
ld b, a ; save sprite number
4631
call gfxCALATR ; get sprite attribute table address
4636
;call gfxSetSpriteColor.Adjust
4641
ret c ; if screen mode < 3, do not adjust sprite multicolor
4643
ld a, b ; recover sprite number
4644
call gfxGetSpriteColorTable
4647
;call gfxSetSpriteColor.Adjust
4648
ld b, 16 ; array of 16 bytes
4650
gfxSetSpriteColorInt.1:
4655
djnz gfxSetSpriteColorInt.1
4658
;gfxSetSpriteColor.Adjust:
4668
;---------------------------------------------------------------------------------------------------------
4669
; gfxSetSpriteColorStr
4671
; DE = address to color byte array
4672
; BC = color byte array size
4673
;---------------------------------------------------------------------------------------------------------
4675
gfxSetSpriteColorStr:
4676
ld b, a ; save sprite number
4679
call gfxCALATR ; get sprite attribute table
4683
ld a, c ; byte array zero length?
4691
;call gfxSetSpriteColor.Adjust
4696
ret c ; if screen mode < 3, do not adjust sprite mode 2
4698
ld a, b ; recover sprite number
4699
call gfxGetSpriteColorTable
4701
ld b, 16 ; array of 16 bytes (color table)
4702
ld a, c ; size of color array
4704
pop iy ; save array start
4706
gfxSetSpriteColorStr.1:
4709
;call gfxSetSpriteColor.Adjust
4715
jr nz, gfxSetSpriteColorStr.2
4716
ld a, c ; recover array size
4718
pop de ; recover array start
4720
gfxSetSpriteColorStr.2:
4721
djnz gfxSetSpriteColorStr.1
4726
;---------------------------------------------------------------------------------------------------------
4727
; gfxGetSpriteColorTable
4729
; HL = address to color table
4730
;---------------------------------------------------------------------------------------------------------
4732
gfxGetSpriteColorTable:
4736
ld l, a ; recover sprite number
4740
add hl, hl ; multiply by 16 (shift left 4)
4743
call gfxCALATR ; get sprite attribute table address
4748
sbc hl, de ; address of color table from sprite multicolor
4755
if defined PUT_SPRITE or defined PUT_SPRITE_STEP or defined PUT_SPRITE_COLOR or defined PUT_SPRITE_STEP_COLOR or defined PUT_SPRITE_COLOR_PATNUM or defined PUT_SPRITE_STEP_COLOR_PATNUM or defined PUT_SPRITE_PATNUM or defined PUT_SPRITE_STEP_PATNUM
4757
;---------------------------------------------------------------------------------------------------------
4761
;---------------------------------------------------------------------------------------------------------
4765
call gfxCALATR ; get sprite attribute table address
4774
;---------------------------------------------------------------------------------------------------------
4775
; gfxSpriteStepCheck
4777
;---------------------------------------------------------------------------------------------------------
4784
ld de, (GFX_SPRITE_SIZE_DAT)
4785
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4786
and 7 ; clear touched and collided flags
4787
ld (GFX_SPRITE_FLAGS), a
4790
jr z, gfxSpriteStepCheck.corners
4792
gfxSpriteStepCheck.limits:
4795
cp b ; jump if x > max_x?
4796
jr c, gfxSpriteStepCheck.limits.touched
4800
cp c ; jump if y > max_y?
4801
jr c, gfxSpriteStepCheck.limits.touched
4803
jr gfxSpriteStepCheck.corners
4805
gfxSpriteStepCheck.limits.touched:
4806
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4807
or 8 ; set limit touched flag
4808
ld (GFX_SPRITE_FLAGS), a
4810
gfxSpriteStepCheck.corners:
4811
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4812
and 2+4 ; check walls or hotspots
4813
jr z, gfxSpriteStepCheck.end
4816
call gfxSpriteStepCheck.corner
4818
ld a, (GFX_SPRITE_SIZE_DAT)
4821
call gfxSpriteStepCheck.corner
4823
ld a, (GFX_SPRITE_SIZE_DAT)
4826
call gfxSpriteStepCheck.corner
4829
ld a, (GFX_SPRITE_SIZE_DAT)
4832
call gfxSpriteStepCheck.corner
4834
gfxSpriteStepCheck.end:
4840
gfxSpriteStepCheck.corner:
4841
call gfxGetTileFromXY
4842
ld d, a ; tile to be searched
4844
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4846
call nz, gfxSpriteStepCheck.walls
4848
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4850
call nz, gfxSpriteStepCheck.hotspots
4853
gfxSpriteStepCheck.walls:
4854
ld hl, (GFX_SPRITE_WALLS)
4855
ld e, 16 ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4856
jp gfxSpriteStepCheck.search
4858
gfxSpriteStepCheck.hotspots:
4859
ld hl, (GFX_SPRITE_HOTSPOTS)
4860
ld e, 32 ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4861
call gfxSpriteStepCheck.search
4863
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4868
ld (GFX_SPRITE_HOTSPOT_TILE), a
4871
; d = tile, hl = search table, e = flag
4872
gfxSpriteStepCheck.search:
4877
ld a, d ; search for this tile
4883
cpir ; inc HL searching for A until BC=0 (Z flag settled if found)
4887
gfxSpriteStepCheck.search.found:
4888
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4889
or e ; set touched flag
4890
ld (GFX_SPRITE_FLAGS), a
4905
call gfxGetScreenTile ; b = y, c = x, a = tile
4911
if defined SPRITE or defined PUT_SPRITE or defined PUT_SPRITE_PATNUM or defined PUT_SPRITE_STEP_PATNUM or defined PUT_SPRITE_COLOR_PATNUM or defined PUT_SPRITE_STEP_COLOR_PATNUM
4913
;---------------------------------------------------------------------------------------------------------
4914
; gfxSetSpritePattern
4916
; BC = pattern number
4917
;---------------------------------------------------------------------------------------------------------
4919
gfxSetSpritePattern:
4921
call gfxCALATR ; get sprite attribute table address
4925
ld a, (BIOS_RG1SAV) ; bit 0 = double size, bit 1 = sprite size (0=8 pixels, 1=16 pixels)
4927
jr z, gfxSetSpritePattern.1
4930
gfxSetSpritePattern.1:
4931
ld a, c ; pattern number
4939
;---------------------------------------------------------------------------------------------------------
4941
; HL = point to sprite data as a string of 8 or 32 characters according the sprites size (8x8 or 16x16)
4943
;---------------------------------------------------------------------------------------------------------
4948
call gfxCALPAT ; get sprite pattern data address
4950
call gfxGSPSIZ ; return in 'a' sprite default size
4954
jr z, gfxSetSpriteData.1
4955
jr nc, gfxSetSpriteData.1
4969
if defined GFX_SPRITES
4971
;---------------------------------------------------------------------------------------------------------
4973
; initialises all sprites
4974
;---------------------------------------------------------------------------------------------------------
4980
;---------------------------------------------------------------------------------------------------------
4982
; set sprite default x, y, pattern and color
4984
;---------------------------------------------------------------------------------------------------------
4989
ld a, (BIOS_GRPACY) ; y
4992
ld a, (BIOS_GRPACX) ; x
4998
ld a, (BIOS_FORCLR) ; color
5004
if defined EXIST_DATA_SET
5006
;---------------------------------------------------------------------------------------------------------
5008
;---------------------------------------------------------------------------------------------------------
5017
;---------------------------------------------------------------------------------------------------------
5018
; gfxClearTileScreen
5019
;---------------------------------------------------------------------------------------------------------
5024
ld hl, (BIOS_GRPNAM)
5028
call gfxSetTileDefaultColor
5030
call gfxSetTileDefaultColor
5032
jp gfxSetTileDefaultColor
5034
gfxSetTileDefaultColor
5035
call gfxGetTileColorAddr
5037
call gfxGetTileDefaultColor
5041
gfxGetTileDefaultColor:
5054
; HL = tile color pointer
5055
; BC = tile color size
5058
;---------------------------------------------------------------------------------------------------------
5061
; HL = tile data pointer
5062
; BC = tile data size
5064
; A = flip (0=no, 1=yes)
5065
;---------------------------------------------------------------------------------------------------------
5069
jr nz, gfxSetTileDataFlip
5071
gfxSetTileDataNoFlip:
5072
call gfxGetTileDataAddr
5076
call gfxGetTileDataAddr
5078
gfxSetTileDataFlip.Loop:
5087
jr nz, gfxSetTileDataFlip.Loop
5090
; in de = tile number
5091
; out de = tile number address
5097
add hl, hl ; tile number * 8
5098
ld de, (BIOS_GRPCGP)
5104
;---------------------------------------------------------------------------------------------------------
5107
; HL = tile color pointer
5108
; BC = tile color size
5110
;---------------------------------------------------------------------------------------------------------
5113
call gfxGetTileColorAddr
5116
; in de = tile number
5117
; out de = tile number address
5118
gfxGetTileColorAddr:
5123
add hl, hl ; tile number * 8
5124
ld de, (BIOS_GRPCOL)
5130
;---------------------------------------------------------------------------------------------------------
5132
; set screen tile at x,y
5136
;---------------------------------------------------------------------------------------------------------
5148
; ld l, b ; slow y * 32
5165
ld de, (BIOS_GRPNAM)
5172
if defined gfxGetTileFromXY or defined EXIST_DATA_SET
5174
;---------------------------------------------------------------------------------------------------------
5176
; get screen tile at x,y
5180
;---------------------------------------------------------------------------------------------------------
5202
ld de, (BIOS_GRPNAM)
5208
;---------------------------------------------------------------------------------------------------------
5209
; Sprite collision table routines
5210
;---------------------------------------------------------------------------------------------------------
5212
gfxInitSpriteCollisionTable:
5215
ld (GFX_SPRITE_COLLISION), ix
5218
; copy sprite attribute table to ram
5219
gfxFillSpriteCollisionTable:
5220
ld hl, (BIOS_ATRBAS) ; source: attribute table
5221
ld de, (GFX_SPRITE_COLLISION) ; dest: ram
5222
ld bc, 128 ; 32*4 = size of attribute table
5225
; pre-calculate each sprite width
5226
gfxCalculateSpriteCollisionTable:
5227
ld ix, (GFX_SPRITE_COLLISION) ; start of sprites attributes
5228
ld b, 32 ; sprite count
5230
gfxCalculateSpriteCollisionTable.start:
5231
ld a, (GFX_SPRITE_SIZE_DAT) ; sprite size
5233
ld d, 208 ; Y no-display flag
5237
cp 3 ; above screen 3?
5238
jr c, gfxCalculateSpriteCollisionTable.next
5239
ld d, 216 ; Y no-display flag
5241
gfxCalculateSpriteCollisionTable.next:
5242
; test IC flag (no collision) in color table
5243
; test EC flag (early clock, shift 32 dots to the left) in color table
5244
; set x1 = x + size, y1 = y + size
5246
cp d ; test if sprite will not be displayed
5259
djnz gfxCalculateSpriteCollisionTable.next
5262
; BC = sprite number to check
5263
gfxUpdateSpriteCollisionTable:
5271
ld hl, (GFX_SPRITE_COLLISION) ; dest: ram
5274
ld hl, (BIOS_ATRBAS) ; source: attribute table
5276
ld bc, 4 ; 4 = size of attribute table to 1 sprite]
5280
ld b, 1 ; sprite count
5282
call gfxCalculateSpriteCollisionTable.start
5285
ld a, (GFX_SPRITE_FLAGS)
5286
and 0xBF ; clear collision flag
5287
ld (GFX_SPRITE_FLAGS), a
5289
ld a, (BIOS_STATFL) ; verify if collision occurred
5292
jr gfxCheckSpriteCollisionTable.start
5294
; BC = sprite number to check
5295
gfxCheckSpriteCollisionTable:
5296
; get target sprite address (iy)
5300
ld de, (GFX_SPRITE_COLLISION)
5307
gfxCheckSpriteCollisionTable.start:
5308
; start test against others sprites
5309
ld ix, (GFX_SPRITE_COLLISION)
5311
ld d, 208 ; Y no-display flag
5315
cp 3 ; above screen 3?
5316
jr c, gfxCheckSpriteCollisionTable.test
5317
ld d, 216 ; Y no-display flag
5319
gfxCheckSpriteCollisionTable.test:
5320
; skip target sprite
5323
jr z, gfxCheckSpriteCollisionTable.next
5325
; test if x1 > nx and x < nx1 and y1 > ny and y < ny1
5327
cp d ; test if sprite will not be displayed
5328
ret z ; return false
5331
jr nc, gfxCheckSpriteCollisionTable.next
5335
jr nc, gfxCheckSpriteCollisionTable.next
5339
jr nc, gfxCheckSpriteCollisionTable.next
5343
jr nc, gfxCheckSpriteCollisionTable.next
5345
; if so, save collider sprite
5347
ld (GFX_SPRITE_COLLIDER), a
5349
ld a, (GFX_SPRITE_FLAGS)
5350
or 0x40 ; set collision flag
5351
ld (GFX_SPRITE_FLAGS), a
5355
gfxCheckSpriteCollisionTable.next:
5363
ret z ; return false
5364
jr gfxCheckSpriteCollisionTable.test
5368
;---------------------------------------------------------------------------------------------------------
5369
; VDP / VRAM support routines
5370
;---------------------------------------------------------------------------------------------------------
5374
; c = register number
5375
; a = register number
5378
ret nz ; is negative? read only
5380
ret z ; is register 8? then status register 0 (read only)
5381
jr nc, gfxWRTVDP.1 ; is > 8? then control registers numbers added 1
5396
;jp nz, BIOS_NWRVDP ; msx 2
5398
;jp nz, BIOS_NWRVDP ; msx 2
5399
jp BIOS_WRTVDP ; msx 1
5402
; in a = register number
5406
jr nz, gfxRDVDP.1 ; is negative? then status register 1 to 9
5408
jr z, gfxRDVDP.2 ; is register 8? then status register 0
5410
jr nc, gfxRDVDP.3 ; is >= 9? then control registers numbers added 1
5411
ld hl, BIOS_RG0SAV ; else is correct control registers numbers
5423
jp BIOS_NRDVDP ;BIOS_VDPSTA
5430
ld hl, BIOS_RG8SAV-9
5438
; in: A=Data byte, BC=Length, HL=VRAM address
5447
; in: A=Sprite pattern number
5448
; out: HL=Sprite pattern address
5458
; in: A=Sprite number
5459
; out: HL=Sprite attribute address
5469
; out: A=Bytes in sprite pattern (8 or 32)
5489
; in: BC=Length, dest DE=VRAM address, source HL=RAM address
5494
; in: BC=Length, dest DE=RAM address, source HL=VRAM address
5521
; 8 bytes / 206 cycles
5522
; http://www.retroprogramming.com/2014/01/fast-z80-bit-reversal.html
5529
djnz gfxReverseA.loop
5534
;---------------------------------------------------------------------------------------------------------
5536
;---------------------------------------------------------------------------------------------------------
5545
RET_MATH_LIB: call COPY_TO.TMP_DAC
5551
MATH_DECADD: ld ix, addSingle
5556
if defined MATH.SUB or defined MATH.NEG
5558
MATH_DECSUB: ld ix, subSingle
5563
if defined MATH.MULT
5565
MATH_DECMUL: ld ix, mulSingle
5572
MATH_DECDIV: ld ix, divSingle
5580
MATH_SNGEXP: ld ix, powSingle
5587
MATH_COS: ld ix, cosSingle
5594
MATH_SIN: ld ix, sinSingle
5601
MATH_TAN: ld ix, tanSingle
5608
MATH_ATN: ld ix, atanSingle
5615
MATH_SQR: ld ix, sqrtSingle
5622
MATH_LOG: ld ix, lnSingle
5629
MATH_EXP: ld ix, expSingle
5636
MATH_ABSFN: ld ix, absSingle
5641
if defined MATH.SEED or defined MATH.NEG
5643
MATH_NEG: ld ix, negSingle
5650
MATH_SGN: ld ix, sgnSingle
5655
if defined RND or defined MATH.SEED
5657
MATH_RND: ld ix, randSingle
5662
MATH_FRCINT: ld hl, BASIC_DAC
5675
ld (BASIC_VALTYP), a
5678
MATH_FRCDBL: ; same as MATH_FRCSGL
5679
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
5680
ld bc, BASIC_DAC ; output address
5683
ld (BASIC_VALTYP), a
5686
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
5688
jr nz, MATH_ICOMP.NE.HIGH
5691
jr nz, MATH_ICOMP.NE.LOW
5693
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
5695
jr nz, MATH_DCOMP.GT
5697
MATH_ICOMP.GT.HIGH: bit 7, d
5700
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
5703
MATH_XDCOMP: ; same as MATH_DCOMP
5704
MATH_DCOMP: ld ix, cmpSingle
5708
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
5710
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
5712
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
5715
if defined CAST_STR_TO.VAL
5717
MATH_FIN: ; HL has the source string
5718
ld a, (BASIC_VALTYP)
5719
cp 2 ; test if integer
5721
ld hl, (BASIC_DAC+2)
5726
MATH_FIN.1: ld BC, BASIC_DAC
5732
if defined CAST_INT_TO.STR
5734
MATH_FOUT: ld a, (BASIC_VALTYP)
5735
cp 2 ; test if integer
5737
ld hl, (BASIC_DAC+2)
5742
MATH_FOUT.1: ld hl, BASIC_DAC
5753
;---------------------------------------------------------------------------------------------------------
5755
; Copyright 2018 Zeda A.K. Thomas
5756
;---------------------------------------------------------------------------------------------------------
5758
; https://github.com/Zeda/z80float
5759
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
5760
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
5761
;---------------------------------------------------------------------------------------------------------
5763
; HL points to the first operand
5764
; DE points to the second operand (if needed)
5765
; IX points to the third operand (if needed, rare)
5766
; BC points to where the result should be output
5767
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
5768
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
5769
; exponent biased by +128.
5770
;---------------------------------------------------------------------------------------------------------
5771
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
5772
;---------------------------------------------------------------------------------------------------------
5774
;---------------------------------------------------------------------------------------------------------
5776
;---------------------------------------------------------------------------------------------------------
5778
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
5779
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
5780
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
5781
scrap: equ BASIC_HOLD8
5782
seed0: equ BASIC_RNDX
5783
seed1: equ seed0 + 4
5784
var48: equ scrap + 4
5787
addend2: equ scrap+7 ;4 bytes
5788
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
5789
var_y: equ var_x + 4 ;4 bytes
5790
var_z: equ var_y + 4 ;4 bytes
5791
var_a: equ var_z + 4 ;4 bytes
5792
var_b: equ var_a + 4 ;4 bytes
5793
var_c: equ var_b + 4 ;4 bytes
5794
temp: equ var_c + 4 ;4 bytes
5795
temp1: equ temp + 4 ;4 bytes
5796
temp2: equ temp1 + 4 ;4 bytes
5797
temp3: equ temp2 + 4 ;4 bytes
5799
pow10exp_single: equ scrap+9
5800
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
5802
;---------------------------------------------------------------------------------------------------------
5804
;---------------------------------------------------------------------------------------------------------
5806
;;Still need to tend to special cases
5874
pop hl ;bigger float
6006
;;Need to adjust sign flag
6029
;;How many push/pops are needed?
6037
;;How many push/pops are needed?
6043
;;How many push/pops are needed?
6044
;;Return bigger number
6051
;---------------------------------------------------------------------------------------------------------
6053
;---------------------------------------------------------------------------------------------------------
6076
jp addInject ;jumps in to the addSingle routine
6078
;---------------------------------------------------------------------------------------------------------
6080
;---------------------------------------------------------------------------------------------------------
6083
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
6084
;Outputs: float1*float2 is stored to (BC)
6085
;573+mul24+{0,35}+{0,30}
6088
;avg: 2055.13839751681cc
6114
;;return float in CHLB
6124
jr z,mulSingle_case0
6136
;jr z,mulSingle_case1
6140
jp z,mulSingle_case1
6145
rra ; |Lots of help from Runer112 and
6146
adc a,a ; |calc84maniac for optimizing
6147
jp po,bad ; |this exponent check.
6156
call mul24 ;BDE*CHL->HLBCDE, returns sign info
6213
;special*x = special
6234
;basically, if b|c has bit 5 set, return NaN
6267
;;avg :1464.9033203125cc (1464+925/1024)
6270
;avg: 1449.63839751681cc
6311
;---------------------------------------------------------------------------------------------------------
6313
;---------------------------------------------------------------------------------------------------------
6316
;;HL points to numerator
6317
;;DE points to denominator
6318
;;BC points to where the quotient gets written
6320
divSingle_no_pushpop:
6326
xor (hl) ; |Get sign of output
6333
ex de,hl ; |Get exponent
6440
call divsub1 ;34 or 66
6458
;34cc or 66cc or 93cc
6473
;---------------------------------------------------------------------------------------------------------
6475
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
6476
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
6477
;---------------------------------------------------------------------------------------------------------
6478
;double mypow( double base, double power, double precision )
6480
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
6481
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
6482
; else if ( precision >= 1 ) {
6483
; if( base >= 0 ) return sqrt( base );
6484
; else return sqrt( -base );
6485
; } else return sqrt( mypow( base, power*2, precision*2 ) );
6488
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
6494
;;BC points to output
6498
ld bc, var_y ; power
6503
ld hl, const_precision
6504
ld bc, var_a ; precision
6507
ld bc, var_z ; result
6516
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
6522
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
6528
; else if ( precision >= 1 ) {
6529
; if( base >= 0 ) return sqrt( base );
6530
; else return sqrt( -base );
6536
; } else return sqrt( mypow( base, power*2, precision*2 ) );
6561
; return 1 / mypow( base, -power, precision );
6580
; return base * mypow( base, power-1, precision );
6599
; if( base >= 0 ) return sqrt( base );
6600
; else return sqrt( -base );
6626
; 2^x = 1.000000001752 + x * (0.693146989552 + x * (0.2402298085906 + x * (5.54833215071e-2 + x * (9.67907584392e-3 + x * (1.243632065103e-3 + x * 2.171671843714e-4)))))
6627
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
6628
;In this case, our polynomial is the same degree, with error different by less than 1 bit, so it's just a waste to range-reduce in this way.
6631
;x-=int(x) ;leaves x in [0,1)
6633
;;if x==inf -> out==inf
6634
;;if x==-inf -> out==0
6635
;;if x==NAN -> out==NAN
6642
push af ;keep track of sign
6652
jr c,_pow_1 ;int(x)=0
6665
jr nz,exp_normalized
6676
jr exp_normalized ;.db $11 ;start of `ld de,**`
6683
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
6692
jp z,exp_underflow+1
6693
;perform 1-(var48+10)--> var48+10
6701
;our 'x' is at var48+10
6702
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
6703
;uses 14 bytes of RAM
6745
;-inf -> +0 because lim approaches 0 from the right
6767
;-inf -> +0 because lim approaches 0 from the right
6769
sbc a,a ;FF if should be 0,
6784
;---------------------------------------------------------------------------------------------------------
6786
;---------------------------------------------------------------------------------------------------------
6788
if defined MATH_SQR or defined MATH_EXP
6790
;Uses 3 bytes at scrap
6792
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
6811
jp z,sqrtSingle_special
6814
push af ;new exponent
6824
;AHL is the new remainder
6825
;Need to divide by 2, then divide by the 16-bit (var_x+4)
6829
;We are just going to approximate it
6911
;Output: DE is the sqrt, AHL is the remainder
6912
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
6936
jr _15a ;.db $FE ;start of `cp *`
6950
jr _16a ;.db $FE ;start of `cp *`
6964
jr _17a ;.db $FE ;start of `cp *`
6978
jr _18a ;.db $FE ;start of `cp *`
6982
;Now we have four more iterations
6983
;The first two are no problem
6995
jr _19a ;.db $FE ;start of `cp *`
7009
jr _20a ;.db $FE ;start of `cp *`
7014
;On the next iteration, HL might temporarily overflow by 1 bit
7016
rl d ;sla e \ rl d \ inc e
7020
adc hl,hl ;This might overflow!
7021
jr c,sqrt32_iter15_br0
7034
;On the next iteration, HL is allowed to overflow, DE could overflow with our current routine, but it needs to be shifted right at the end, anyways
7037
ld b,a ;either 0x00 or 0x80
7058
;returns A as the sqrt, HL as the remainder, D = 0
7072
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
7083
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
7094
dec d ;this resets the low bit of D, so `srl d` resets carry.
7095
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
7117
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
7130
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
7152
;---------------------------------------------------------------------------------------------------------
7154
;---------------------------------------------------------------------------------------------------------
7156
if defined MATH_LOG or defined MATH_LN
7159
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
7160
; a * x ^ (1/a) - a, where a = 100
7163
ld de, const_100_inv
7165
call powSingle ; temp = x ^ (1/100)
7169
call mulSingle ; temp1 = temp * 100
7172
call subSingle ; bc = temp1 - 100
7177
;---------------------------------------------------------------------------------------------------------
7179
;---------------------------------------------------------------------------------------------------------
7196
;---------------------------------------------------------------------------------------------------------
7198
;---------------------------------------------------------------------------------------------------------
7205
;;BC points to the output
7210
;;DE points to lg(y), HL points to x, BC points to output
7219
;---------------------------------------------------------------------------------------------------------
7221
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
7222
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
7223
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
7224
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
7225
;---------------------------------------------------------------------------------------------------------
7227
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
7230
; taylor: x - x^3/6 + x^5/120 - x^7/5040
7231
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
7233
; var_b = round( x / (2*PI), 0 )
7234
; var_c = x - var_b*2*PI
7235
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
7236
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
7237
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
7244
call copySingle ; return 0
7248
call trigRangeReductionSinCos
7253
call mulSingle ; var_b = var_a * var_a
7257
call mulSingle ; temp = x^2/5040
7261
call subSingle ; temp1 = 1/120 - temp
7265
call mulSingle ; temp = x^2 * temp1
7269
call subSingle ; temp1 = 1/6 - temp
7273
call mulSingle ; temp = x^2 * temp1
7277
call subSingle ; temp1 = 1 - temp
7281
call mulSingle ; return x * temp1
7284
trigRangeReductionSinCos:
7287
; var_b = round( x / (2*PI), 0 )
7295
; var_c = x - var_b*2*PI
7299
call mulSingle ; temp = var_b*2*PI
7303
call subSingle ; var_c = x - temp
7304
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
7308
jr nc, trigRangeReductionSinCos.else.2
7311
call copySingle ; temp1 = var_c
7312
jr trigRangeReductionSinCos.endif.2
7313
trigRangeReductionSinCos.else.2:
7317
call addSingle ; temp1 = var_c + 2*PI
7318
trigRangeReductionSinCos.endif.2:
7319
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
7323
jr c, trigRangeReductionSinCos.else.3
7324
jr z, trigRangeReductionSinCos.else.3
7328
call subSingle ; temp2
7329
jr trigRangeReductionSinCos.endif.3
7330
trigRangeReductionSinCos.else.3:
7333
call copySingle ; temp2 = temp1
7334
trigRangeReductionSinCos.endif.3:
7335
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
7336
ld hl, const_half_pi
7339
jr c, trigRangeReductionSinCos.else.4
7340
jr z, trigRangeReductionSinCos.else.4
7344
call subSingle ; var_a
7345
jr trigRangeReductionSinCos.endif.4
7346
trigRangeReductionSinCos.else.4:
7349
call copySingle ; var_a = temp2
7350
trigRangeReductionSinCos.endif.4:
7351
; if( temp > PI, -1, 1 )
7355
jr nc, trigRangeReductionSinCos.endif.5
7359
ld (ix+2), a ; turn var_a to negative
7360
trigRangeReductionSinCos.endif.5:
7366
;---------------------------------------------------------------------------------------------------------
7368
;---------------------------------------------------------------------------------------------------------
7370
if defined MATH_COS or defined MATH_TAN
7373
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
7374
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
7375
; reduction: same as sin
7384
call copySingle ; return 1
7388
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
7389
call trigRangeReductionSinCos
7394
call mulSingle ; var_b = var_a * var_a
7398
call mulSingle ; temp = x^2/720
7402
call subSingle ; temp1 = 1/24 - temp
7406
call mulSingle ; temp = x^2 * temp1
7410
call subSingle ; temp1 = 1/2 - temp
7414
call mulSingle ; temp = x^2 * temp1
7418
call subSingle ; temp1 = 1 - temp
7420
; temp3 = abs(var_c)
7421
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
7428
ld (ix+2), a ; temp3 = abs(var_c)
7430
ld de, const_half_pi
7431
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
7432
jr nc, cosSingle.endif.1
7436
ld (ix+2), a ; temp1 = -temp1
7440
call copySingle ; return temp1
7445
;---------------------------------------------------------------------------------------------------------
7447
;---------------------------------------------------------------------------------------------------------
7468
;---------------------------------------------------------------------------------------------------------
7470
;---------------------------------------------------------------------------------------------------------
7475
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
7476
; x < -1: atan - PI/2
7477
; x >= 1: PI/2 - atan
7478
;reduction: abs(X) > 1 : Y = 1 / X
7479
; abs(X) <= 1: Y = X
7488
call copySingle ; return 0
7492
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
7493
call trigRangeReductionAtan
7499
call mulSingle ; var_b = var_a * var_a
7503
call mulSingle ; temp = (4*x)^2
7507
call divSingle ; temp1 = temp/9
7511
call addSingle ; temp = 7 + temp1
7515
call mulSingle ; temp1 = var_b * 9
7519
call divSingle ; temp2 = temp1 / temp
7523
call addSingle ; temp = 5 + temp2
7527
call mulSingle ; temp1 = var_b * 4
7531
call divSingle ; temp2 = temp1 / temp
7535
call addSingle ; temp = 3 + temp2
7539
call divSingle ; temp2 = var_b / temp
7543
call addSingle ; temp = 1 + temp2
7547
call divSingle ; temp2 = var_a / temp
7549
; x >= 1: PI/2 - atan
7553
ld hl, const_half_pi
7560
; x < -1: atan - PI/2
7571
ld de, const_half_pi
7580
call copySingle ; return temp2
7583
trigRangeReductionAtan:
7584
;reduction: abs(X) > 1 : Y = 1 / X
7585
; abs(X) <= 1: Y = X
7594
ld (ix+2), a ; abs(x)
7598
jr nc, trigRangeReductionAtan.1
7604
jr trigRangeReductionAtan.2
7605
trigRangeReductionAtan.1:
7610
trigRangeReductionAtan.2:
7614
jr c, trigRangeReductionAtan.3
7618
ld (ix+2), a ; y = -y
7619
trigRangeReductionAtan.3:
7624
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
7626
;---------------------------------------------------------------------------------------------------------
7628
;---------------------------------------------------------------------------------------------------------
7642
;---------------------------------------------------------------------------------------------------------
7644
;---------------------------------------------------------------------------------------------------------
7713
if defined MATH_ABSFN
7715
;---------------------------------------------------------------------------------------------------------
7717
;---------------------------------------------------------------------------------------------------------
7720
;;HL points to the float
7721
;;BC points to where to output the result
7740
;---------------------------------------------------------------------------------------------------------
7742
;---------------------------------------------------------------------------------------------------------
7745
;;HL points to the float
7746
;;BC points to where to output the result
7751
if defined powSingle or defined sgnSingle or defined MATH_NEG
7753
;---------------------------------------------------------------------------------------------------------
7755
;---------------------------------------------------------------------------------------------------------
7758
;;HL points to the float
7759
;;BC points to where to output the result
7765
jr nz, negSingle.test.sign
7768
jr nz, negSingle.test.sign
7771
jr nz, negSingle.test.sign
7774
jr nz, negSingle.test.sign
7785
negSingle.test.sign:
7788
jr z, negSingle.positive
7792
call negSingle.positive
7811
if defined MATH_DCOMP or defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN or defined MATH_SIN or defined MATH_TAN or defined MATH_COS or defined MATH_ATN
7813
;---------------------------------------------------------------------------------------------------------
7815
;---------------------------------------------------------------------------------------------------------
7818
;Input: HL points to float1, DE points to float2
7820
; float1 >= float2 : nc
7821
; float1 < float2 : c,nz
7822
; float1 == float2 : z
7823
; There is a margin of error allowed in the lower 2 bits of the mantissa.
7825
;Currently fails when both numbers have magnitude less than about 2^-106
7860
ld a,(scrap+3) ;new power
7861
pop bc ;B is old power
7871
or 1 ;not equal, so reset z flag
7872
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
7882
;---------------------------------------------------------------------------------------------------------
7884
;---------------------------------------------------------------------------------------------------------
7887
;Stores a pseudo-random number on [0,1)
7888
;it won't produce values on (0,2^-23)
7897
;DEHL is the mantissa, B is the exponent
7913
;If we needed to shift more than 8 bits, we'll load in more random data
7918
jp nc,rand_no_more_rand_data
7926
rand_no_more_rand_data:
7945
;;Tested and passes all CAcert tests
7946
;;Uses a very simple 32-bit LCG and 32-bit LFSR
7947
;;it has a period of 18,446,744,069,414,584,320
7948
;;roughly 18.4 quintillion.
7949
;;LFSR taps: 0,2,6,7 = 11000101
7951
;;Thanks to Runer112 for his help on optimizing the LCG and suggesting to try the much simpler LCG. On their own, the two are terrible, but together they are great.
7952
;Uses 64 bits of state
7988
if defined MATH_FOUT
7990
;---------------------------------------------------------------------------------------------------------
7992
; in HL = Single address
7993
; BC = String address
7994
; out A = String size
7995
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
7996
; http://0x80.pl/articles/convert-float-to-integer.html
7997
;---------------------------------------------------------------------------------------------------------
8011
; Move the float to scrap
8015
; Make the float negative, write a '-' if already negative
8024
ld a,'-' ; write '-' simbol
8032
; Check if the exponent field is 0 (a special value)
8039
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
8047
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
8055
ld (pow10exp_single),a ;The base-10 exponent
8059
ld de,pow10LUT ;get the table of 10^-(2^k)
8061
ld hl, pow10exp_single
8063
call singletostr_mul
8064
call singletostr_mul
8065
call singletostr_mul
8066
call singletostr_mul
8067
call singletostr_mul
8068
call singletostr_mul
8069
;now the number is pretty close to a nice value
8071
; If it is less than 1, multiply by 10
8076
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
8082
ld hl,pow10exp_single
8088
; Convert to a fixed-point number !
8102
;We need to get 7 digits
8104
pop hl ;Points to the string
8106
;The first digit can be as large as 20, so it'll actually be two digits
8110
;Increment the exponent :)
8111
ld de,(pow10exp_single-1)
8113
ld (pow10exp_single-1),de
8122
; Get the remaining digits.
8129
call singletostrmul10
8134
;Save the pointer to the end of the string
8141
jr c,rounding_done_single
8142
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
8151
rounding_done_single:
8154
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
8166
;Now lets move HL-DE bytes at DE+1 to DE
8178
;If z flag is reset, this means that the exponent should be bumped up 1
8179
ld a,(pow10exp_single)
8182
ld (pow10exp_single),a
8185
;if -4<=A<=6, then need to insert the decimal place somewhere.
8190
;for this, we need to insert the decimal after the first digit
8191
;Then, we need to append the exponent string
8193
ld de,strout_single-1
8195
cp '-' ;negative sign
8203
;remove any stray zeroes at the end before appending the exponent
8207
; Write the exponent
8210
ld a,(pow10exp_single)
8213
ld (hl),'-' ;negative sign
8231
ld de, strout_single
8234
ld a, l ; string size
8236
ld hl,strout_single-1
8240
ld a,(pow10exp_single)
8244
;need to put zeroes before everything
8247
cp '-' ;negative sign
8273
ld de,strout_single-1
8277
cp '-' ;negative sign
8288
ld hl,strout_single-1
8306
;multiply the 0.24 fixed point number at scrap by 10
8307
;overflow in A register
8342
;Check that the last digit isn't a decimal!
8396
;---------------------------------------------------------------------------------------------------------
8398
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
8399
;---------------------------------------------------------------------------------------------------------
8404
ptr_sto: equ scrap+9
8406
;;#Routines/Single Precision
8408
;; HL points to the string
8409
;; BC points to where the float is output
8411
;; scrap+9 is the pointer to the end of the string
8413
;; 11 bytes at scrap ?
8418
;Check if there is a negative sign.
8427
;Skip all leading zeroes
8430
jr z,$-4 ;jumps back to the `inc hl`
8433
;Check if the next char is char_DEC
8435
or a ;to reset the carry flag
8437
jr _54a ;.db $FE ;start of cp *
8444
jr z,$-5 ;jumps back to the `dec b`
8447
;Now we read in the next 8 digits
8453
;Now `scrap` holds the 4-digit base-100 number.
8455
;if carry flag is set, just need to get rid of remaining digits
8456
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
8467
jp z,strToSingle_inf
8470
;Now check for engineering `E` to modify the exponent
8474
;Gotta multiply the number at (scrap) by 2^24
8477
call scrap_times_256
8480
call scrap_times_256
8483
call scrap_times_256
8486
call scrap_times_256
8489
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
8497
jp z,strToSingle_zero-1
8501
jp m,strToSingle_normed
8502
;Will need to iterate at most three times
8515
;Move the number to scrap
8524
;now (scrap) is our number, need to multiply by power of 10!
8525
;Power of 10 is stored in B, need to put in A first
8533
jp nc,strToSingle_inf+1
8536
jp nc,strToSingle_zero
8560
cp char_NEG ;negative exponent?
8612
call scrap_times_sub
8625
jr nz,strToSingle_inf
8643
if defined roundSingle or defined MATH_FRCSGL
8645
;---------------------------------------------------------------------------------------------------------
8647
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
8648
;---------------------------------------------------------------------------------------------------------
8655
ld l, (ix) ; convert integer parameter to single float
8657
ld bc, 0x1000 ; bynary digits count + sign
8659
int2Single.test.zero:
8661
or h ; test if hl is not zero
8662
jr nz, int2Single.test.negative
8664
jr nz, int2Single.test.negative
8669
int2Single.test.negative:
8670
bit 7, h ; test if hl is negative
8671
jr z, int2Single.normalize
8672
ld c, 0x80 ; sign negative
8681
int2Single.normalize:
8684
jr nz, int2Single.mount
8687
jr int2Single.normalize
8690
res 7, h ; turn off upper bit
8692
ld a, c ; restore sign
8694
ld h, a ; ...into upper mantissa
8696
ld e, h ; sign+mantissa
8697
ld h, l ; high mantissa
8698
ld l, 0 ; low mantissa
8700
ld a, b ; binary digits count
8701
or 0x80 ; exponent bias
8706
ld (ix), l ; low mantissa
8707
ld (ix+1), h ; high mantissa
8708
ld (ix+2), e ; sign + mantissa
8709
ld (ix+3), d ; expoent
8718
if defined roundSingle or defined MATH_FRCINT
8720
;---------------------------------------------------------------------------------------------------------
8722
; http://0x80.pl/articles/convert-float-to-integer.html
8723
;---------------------------------------------------------------------------------------------------------
8726
; HL points to the single-precision float
8728
; HL is the 16-bit signed integer part of the float
8729
; BC points to 16-bit signed integer
8746
jr c,no_shift_single_to_int16
8748
jr nc,no_shift_single_to_int16
8770
jr _67a ;.db $11 ;start of ld de,*
8782
no_shift_single_to_int16:
8804
;---------------------------------------------------------------------------------------------------------
8805
; Auxiliary routines
8806
;---------------------------------------------------------------------------------------------------------
8813
const_pi: db $DB,$0F,$49,$81
8814
const_e: db $54,$f8,$2d,$81
8815
const_lg_e: db $3b,$AA,$38,$80
8816
const_ln_2: db $18,$72,$31,$7f
8817
const_log2: db $9b,$20,$1a,$7e
8818
const_lg10: db $78,$9a,$54,$81
8819
const_0: db $00,$00,$00,$00
8820
const_1: db $00,$00,$00,$80
8821
const_2: dw 0, 33024
8822
const_3: dw 0, 33088
8823
const_4: dw 0, 33280
8824
const_5: dw 0, 33312
8825
const_7: dw 0, 33376
8826
const_9: dw 0, 33552
8827
const_16: dw 0, 33792
8828
const_100: db $00,$00,$48,$86
8829
const_100_inv: dw 55050, 31011
8830
const_precision: db $77,$CC,$2B,$65 ;10^-8
8831
const_half_1: dw 0, 32512
8832
const_inf: db $00,$00,$40,$00
8833
const_NegInf: db $00,$00,$C0,$00
8834
const_NaN: db $00,$00,$20,$00
8835
const_log10_e: db $D9,$5B,$5E,$7E
8836
const_2pi: db $DB,$0F,$49,$82
8837
const_2pi_inv: db $83,$F9,$22,$7D
8838
const_half_pi: dw 4059, 32841
8839
const_p25: db $00,$00,$00,$7E
8840
const_p5: db $00,$00,$00,$7F
8843
sin_a1: dw 43691, 32042
8844
sin_a2: dw 34952, 30984
8845
sin_a3: dw 3329, 29520
8846
cos_a1: equ const_half_1
8847
cos_a2: dw 43691, 31530
8848
cos_a3: dw 2914, 30262
8849
exp_a1: db $15,$72,$31,$7F ;.693146989552
8850
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
8851
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
8852
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
8853
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
8854
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
8855
const_1p40625: db $00,$00,$34,$80 ;1.40625
8857
if defined MATH_CONSTSINGLE
8865
;A is the constant ID#
8866
;returns nc if failed, c otherwise
8867
;HL points to the constant
8868
cp (end_const-start_const)>>2
8875
;#if ((end_const-4)>>8)!=(start_const>>8)
8888
db $CD,$CC,$4C,$7C ;.1
8889
db $0A,$D7,$23,$79 ;.01
8890
db $17,$B7,$51,$72 ;.0001
8891
db $77,$CC,$2B,$65 ;10^-8
8892
db $95,$95,$66,$4A ;10^-16
8893
db $1F,$B1,$4F,$15 ;10^-32
8896
db $00,$00,$20,$83 ;10
8897
db $00,$00,$48,$86 ;100
8898
db $00,$40,$1C,$8D ;10000
8899
db $20,$BC,$3E,$9A ;10^8
8900
db $CA,$1B,$0E,$B5 ;10^16
8901
db $AE,$C5,$1D,$EA ;10^32
8908
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
8909
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
8910
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
8911
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
8912
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
8913
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
8914
;C>=2 15+{0,20+{0,8}}
8917
;avg: 349.21279907227cc
9008
;26 bytes, adds 118cc to the traditional routine
9043
;c flag means don't increment the exponent
9046
jr c,ascii_to_uint8_noexp
9048
jr z,ascii_to_uint8_noexp-2
9052
jr nc,ascii_to_uint8_noexp_end
9064
jr z,ascii_to_uint8_noexp_2nd
9068
jr nc,ascii_to_uint8_noexp_end
9079
ascii_to_uint8_noexp:
9082
jr nc,ascii_to_uint8_noexp_end
9089
ascii_to_uint8_noexp_2nd:
9094
jr nc,ascii_to_uint8_noexp_end
9097
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
9098
ascii_to_uint8_noexp_end:
9108
if defined MATH_RSUBSINGLE
9129
jp addInject ;jumps in to the addSingle routine
9133
if defined MATH_MOD1SINGLE
9135
;This routine performs `x mod 1`, returning a non-negative value.
9158
jr z,mod1Single_special
9171
;If it is zero, need to set exponent to zero and return
9194
;make sure it isn't zero else we need to add 1
9206
;If INF, need to return NaN instead
9207
;For 0 and NaN, just return itself :)
9227
if defined MATH_FOUT
9229
; --------------------------------------------------------------
9230
; Converts a signed integer value to a zero-terminated ASCII
9231
; string representative of that value (using radix 10).
9233
; Brandon Wilson WikiTI
9234
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
9235
; --------------------------------------------------------------
9237
; HL Value to convert (two's complement integer).
9238
; DE Base address of string destination. (pointer).
9239
; --------------------------------------------------------------
9242
; --------------------------------------------------------------
9243
; REGISTERS/MEMORY DESTROYED
9245
; --------------------------------------------------------------
9251
; Detect sign of HL.
9255
; HL is negative. Output '-' to string and negate HL.
9260
; Negate HL (using two's complement)
9264
ld a, 0 ; Note that XOR A or SUB A would disturb CF
9268
; Convert HL to digit characters
9270
ld b, 0 ; B will count character length of number
9273
call div_hl_c; HL = HL / A, A = remainder
9280
; Retrieve digits from stack
9288
; Terminate string with NULL
9299
ld a, l ; string size
9307
;===============================================================
9308
; Convert a string of base-10 digits to a 16-bit value.
9309
; http://z80-heaven.wikidot.com/math#toc32
9311
; DE points to the base 10 number string in RAM.
9313
; HL is the 16-bit value of the number
9314
; DE points to the byte after the number
9319
; A (actually, add 30h and you get the ending token)
9322
; n is the number of digits
9324
; at most 595 cycles for any 16-bit decimal value
9325
;===============================================================
9328
ld hl,0 ; 10 : 210000
9345
jr nc,ConvLoop ;12|23: 30EE
9347
jr ConvLoop ; --- : 18EB
9354
; return remainder in a
9355
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
9376
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
9406
djnz div_dehl_c.loop
9414
;---------------------------------------------------------------------------------------------------------
9415
; VARIABLES INITIALIZE
9416
;---------------------------------------------------------------------------------------------------------
9420
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
9421
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
9422
ld (VAR_DUMMY.POINTER), hl
9423
ld b, VAR_DUMMY.LENGTH
9428
djnz INITIALIZE_DUMMY.1
9433
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
9435
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
9438
INITIALIZE_VARIABLES:
9439
call INITIALIZE_DATA
9440
call INITIALIZE_DUMMY
9443
call gfxInitSpriteCollisionTable
9446
;if defined COMPILE_TO_ROM
9447
; ld ix, BIOS_JIFFY ; initialize rom clock
9455
ld d, 2 ; explicitly coded defined type
9456
ld c, 0 ; variable name 1 (variable number)
9457
ld b, 255 ; variable name 2 (type flag=fixed)
9458
call INIT_VAR ; variable initialize
9460
ld d, 2 ; explicitly coded defined type
9461
ld c, 1 ; variable name 1 (variable number)
9462
ld b, 255 ; variable name 2 (type flag=fixed)
9463
call INIT_VAR ; variable initialize
9466
ld c, 2 ; variable name 1 (variable number)
9467
ld b, 255 ; variable name 2 (type flag=fixed)
9468
call INIT_VAR ; variable initialize
9470
ld d, 2 ; explicitly coded defined type
9471
ld c, 3 ; variable name 1 (variable number)
9472
ld b, 255 ; variable name 2 (type flag=fixed)
9473
call INIT_VAR ; variable initialize
9475
ld d, 2 ; explicitly coded defined type
9476
ld c, 4 ; variable name 1 (variable number)
9477
ld b, 255 ; variable name 2 (type flag=fixed)
9478
call INIT_VAR ; variable initialize
9480
ld d, 2 ; explicitly coded defined type
9481
ld c, 5 ; variable name 1 (variable number)
9482
ld b, 255 ; variable name 2 (type flag=fixed)
9483
call INIT_VAR ; variable initialize
9485
ld d, 2 ; explicitly coded defined type
9486
ld c, 6 ; variable name 1 (variable number)
9487
ld b, 255 ; variable name 2 (type flag=fixed)
9488
call INIT_VAR ; variable initialize
9490
ld d, 2 ; explicitly coded defined type
9491
ld c, 7 ; variable name 1 (variable number)
9492
ld b, 255 ; variable name 2 (type flag=fixed)
9493
call INIT_VAR ; variable initialize
9495
ld d, 2 ; explicitly coded defined type
9496
ld c, 8 ; variable name 1 (variable number)
9497
ld b, 255 ; variable name 2 (type flag=fixed)
9498
call INIT_VAR ; variable initialize
9502
;---------------------------------------------------------------------------------------------------------
9503
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
9504
;---------------------------------------------------------------------------------------------------------
9506
if defined COMPILE_TO_ROM
9509
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
9511
if pgmPage1.pad >= 0
9514
; .WARNING "There's no free space left on program page 1"
9519
VAR_STACK.START: equ ramArea
9520
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
9522
VAR_STACK.POINTER: equ VAR_STACK.START
9524
PRINT.CRLF: db 3, 0, 0, 2
9525
dw PRINT.CRLF.DATA, 0, 0, 0
9526
PRINT.CRLF.DATA: db 13,10,0
9528
PRINT.TAB: db 3, 0, 0, 1
9529
dw PRINT.TAB.DATA, 0, 0, 0
9530
PRINT.TAB.DATA: db 09,0
9533
LIT_NULL_DBL: dw 0, 0, 0, 0
9539
LIT_QUOTE_CHAR: db '\"'
9542
LIT_TRUE: db 2, 0, 0
9546
LIT_FALSE: db 2, 0, 0
9571
IDF_14: equ VAR_STACK.POINTER + 0
9574
IDF_19: equ VAR_STACK.POINTER + 11
9581
IDF_21: equ VAR_STACK.POINTER + 22
9588
IDF_24: equ VAR_STACK.POINTER + 33
9603
IDF_34: equ VAR_STACK.POINTER + 44
9618
IDF_41: equ VAR_STACK.POINTER + 55
9649
IDF_54: equ VAR_STACK.POINTER + 66
9676
IDF_65: equ VAR_STACK.POINTER + 77
9691
IDF_69: equ VAR_STACK.POINTER + 88
9805
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 99
9807
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
9808
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
9809
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
9810
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
9812
VAR_DUMMY.SIZE: equ 8
9813
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
9814
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
9815
VAR_STACK.END: equ VAR_DUMMY.END + 1
9817
;--------------------------------------------------------
9819
;--------------------------------------------------------
9826
DATA_ITEMS_COUNT: equ 4
9828
DATA_SET_ITEMS_START:
9829
DATA_SET_ITEMS_COUNT: equ 0
9832
;---------------------------------------------------------------------------------------------------------
9834
;---------------------------------------------------------------------------------------------------------
9836
if defined COMPILE_TO_ROM
9840
pgmPage2.pad: equ romSize - (romPad - pgmArea)
9842
if pgmPage2.pad >= 0
9845
if pgmPage2.pad < lowLimitSize
9846
.WARNING "There's only less than 5% free space on this ROM"
9849
.ERROR "There's no free space left on this ROM"
9854
end_file: end start_pgm ; label start is the entry point