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
;--------------------------------------------------------
414
;--------------------------------------------------------
416
;--------------------------------------------------------
417
COMPILE_TO_ROM: EQU 1
419
MACRO __call_basic,CALL_PARM
424
if defined COMPILE_TO_DOS
426
MACRO __call_bios,CALL_PARM
427
;ld iy,(BIOS_EXPTBL-1)
429
call BIOS_CALBAS ; BIOS_CALSLT
434
MACRO __call_bios,CALL_PARM
441
push hl ; save parameter
445
pop iy ; restore PC of caller
446
pop hl ; get next parameter
447
push iy ; save PC of caller
451
pop iy ; restore PC of caller
452
push hl ; save return parameter
453
push iy ; save PC of caller
457
pop iy ; restore PC of caller
458
push hl ; save return parameter
459
push iy ; save PC of caller
463
MACRO set.line.number, line_number
464
ld bc, line_number ; current line number
465
ld (BASIC_CURLIN), bc
469
ld a, (BIOS_INTFLG) ; verify CTRL+BREAK
475
ld a, (BASIC_ONGSBF) ; trap occured counter
481
;---------------------------------------------------------------------------------------------------------
483
;---------------------------------------------------------------------------------------------------------
485
romSize: equ 0x8000 ; ROM size (32k)
486
pageSize: equ 0x4000 ; Page size (16k)
487
lowLimitSize: equ 0x400 ; 10% of a page size
489
if defined COMPILE_TO_BIN
491
pgmArea: equ 0x8000 ; page 2 - program area
492
ramArea: equ 0xc000 ; page 3 - free RAM start area
494
org pgmArea ; program binary type start address
495
db 0FEh ; binary file ID
496
dw start_pgm ; begin address
497
dw end_file - 1 ; end address
498
dw start_pgm ; program execution address (for ,R option)
501
if defined COMPILE_TO_ROM
503
pgmArea: equ 0x4000 ; page 1 and 2 - program area
504
ramArea: equ 0xc000 ; page 3 - free RAM start area
506
org pgmArea ; program rom type start address
507
db 'AB' ; rom file ID
509
dw 0x0000 ; STATEMENT
516
pgmArea: equ 0x8000 ; page 2 - program area
517
ramArea: equ 0xc000 ; page 3 - free RAM start area
519
org pgmArea ; program DOS type start address ; 0x0100
525
;---------------------------------------------------------------------------------------------------------
527
;---------------------------------------------------------------------------------------------------------
529
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
532
call PROGRAM_SLOT_2_RESTORE
533
__call_basic BASIC_READYR ; warm start Basic
538
call PROGRAM_SLOT_GET
539
ld (BIOS_RAMAD2), a ; Save RAM slot of page 8000h-BFFFh
542
PROGRAM_SLOT_2_RESTORE:
545
jp BIOS_ENASLT ; Select the RAM on page 8000h-BFFFh
547
PROGRAM_SLOT_2_ENABLE:
551
and 3 ;Keep bits corresponding to the page
568
jp BIOS_ENASLT ; Select the ROM on page 8000h-BFFFh
571
; a <- slot ID formatted FxxxSSPP
572
; Modifies: af, bc, de, hl
573
; ref: https://www.msx.org/forum/msx-talk/development/fusion-c-and-htimi#comment-366469
577
jr z,PrimaryShiftContinue
582
PrimaryShiftContinue:
584
jr z,PrimaryShiftDone
599
inc hl ; move to SLTTBL
606
jr z,SecondaryShiftContinue
611
SecondaryShiftContinue:
613
jr nz,SecondaryShiftDone
623
if defined COMPILE_TO_DOS
628
__call_bios BIOS_ENASLT ; Select main ROM on page 0 (0000h~3FFFh)
635
;---------------------------------------------------------------------------------------------------------
637
;---------------------------------------------------------------------------------------------------------
639
start_pgm: ; start of the program
641
if defined COMPILE_TO_DOS
643
call BIOS_SLOT_ENABLE ; enable bios on page 0
644
;call BASIC_SLOT_ENABLE ; enable basic on page 1
648
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
650
call PROGRAM_SLOT_2_SAVE ; save slot on page 2
651
call PROGRAM_SLOT_2_ENABLE ; enable program on page 2
655
__call_bios BIOS_ERAFNK ; turn off function keys display
656
__call_bios BIOS_GICINI ; initialize sound system
657
__call_bios BIOS_INITXT ; initialize text screen
659
ld (BIOS_CLIKSW), a ; disable keyboard click
661
ld (BASIC_CURLIN), bc ; interpreter in direct mode
662
__call_basic BASIC_TRAP_CLEAR ; clear traps work space
663
;call INITIALIZE_PARAMETERS ; initialize parameters stack
664
call memory.init ; initialize memory allocation
665
call INITIALIZE_VARIABLES ; initialize variables
669
ld hl, LIT_4 ; parameter
671
call SET_PLAY_VOICE_1 ; action call
672
ld hl, LIT_6 ; parameter
674
call SET_PLAY_VOICE_2 ; action call
675
call DO_PLAY ; action call
678
ld hl, LIT_11 ; parameter
680
call INPUT.FUNCTION.STR ; action call
681
ld hl, IDF_8 ; parameter
683
call LET ; action call
686
ld hl, LIT_12 ; parameter
688
call SET_PLAY_VOICE_1 ; action call
689
ld hl, LIT_13 ; parameter
691
call SET_PLAY_VOICE_2 ; action call
692
call DO_PLAY ; action call
695
ld hl, LIT_14 ; parameter
697
call INPUT.FUNCTION.STR ; action call
698
ld hl, IDF_8 ; parameter
700
call LET ; action call
703
ld hl, LIT_15 ; parameter
705
call SET_PLAY_VOICE_1 ; action call
706
call DO_PLAY ; action call
708
;---------------------------------------------------------------------------------------------------------
710
;---------------------------------------------------------------------------------------------------------
712
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
714
ld (BIOS_CLIKSW), a ; enable keyboard click
716
if defined COMPILE_TO_ROM
719
if defined COMPILE_TO_DOS
722
__call_basic BASIC_READYR ; warm start Basic
726
ret ; end of the program
728
;__call_bios BIOS_GICINI ; initialize sound system
729
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
730
; __call_bios BIOS_RESET ; restart Basic
732
; __call_basic BASIC_END ; end to Basic
736
;---------------------------------------------------------------------------------------------------------
738
;---------------------------------------------------------------------------------------------------------
743
; out IX = variable assigned address
744
pop.parm ; get variable address parameter
745
push hl ; just to transfer hl to ix
747
ld a, (ix) ; get variable type
748
cp 3 ; test if string
749
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
750
ld a, (ix + 3) ; get variable string length
752
jr z, LET.PARM ; if zero, it isn't necessary to free memory
753
ld c, (ix + 4) ; get old string address low
754
ld b, (ix + 5) ; get old string address high
755
push ix ; save variable address
756
push bc ; just to transfer bc (old string address) to ix
758
call memory.free ; free memory
759
pop ix ; restore variable address
760
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
761
ld a, (ix + 2) ; get variable type flag
762
or a ; cp 0 - test type flag (0=any, 255=fixed)
763
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
764
LET.ANY: push ix ; just to transfer ix (variable address) to de
766
ldi ; copy 1 byte from hl (data address) to de (variable address)
767
inc de ; go to variable data area
769
inc hl ; go to data data area
771
ld bc, 8 ; data = 8 bytes
772
ldir ; copy bc bytes from hl (data address) to de (variable address)
773
ld a, (ix) ; get variable type
774
cp 3 ; test if string
775
ret nz ; if not string, return
776
jp LET.STRING ; else do string treatment (in ix = variable address)
777
LET.FIXED: push ix ; save variable destination address
778
push hl ; save variable source address
779
ld a, (ix) ; get variable fixed type, and hl has parameter data address
780
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
782
pop ix ; restore variable address
783
ld a, (ix) ; get variable destination type again
784
cp 3 ; test if string
785
jr nz, LET.VALUE ; if not string, do value treatment
786
ld a, (de) ; get variable source type again
787
cp 3 ; test if string
788
jr nz, LET.FIX1 ; if not string, get casted string size
793
ld (ix + 3), a ; source string size
795
LET.FIX1: call GET_STR.LENGTH ; get string length (in HL, out a)
796
ld (ix + 3), a ; set variable length
797
LET.FIX2: ld (ix + 4), l ; casted data address low
798
ld (ix + 5), h ; casted data address high
799
jp LET.STRING ; do string treatment (in ix = variable address)
800
LET.VALUE: push ix ; just to transfer ix (variable address) to de
802
inc de ; go to variable data area (and the data from its casted)
805
ld bc, 8 ; data = 8 bytes
806
ldir ; copy bc bytes from hl (data address) to de (variable address)
808
LET.STRING: ld a, (ix + 3) ; string size
809
or a ; cp 0 - test if null
810
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
811
ld bc, LIT_NULL_STR ; else, set to a null string literal
812
ld (ix + 4), c ; variable address low
813
ld (ix + 5), b ; variable address high
815
LET.ALLOC: push ix ; save variable address
816
ld l, (ix + 4) ; source string address low
817
ld h, (ix + 5) ; source string address high
818
push hl ; save copy from address
819
ld c, (ix + 3) ; get variable length
821
inc bc ; string length have one more byte from zero terminator
822
push bc ; save variable lenght + 1
823
call memory.alloc ; in bc = size, out ix = address, nz=OK
825
push ix ; just to transfer memory address from ix to de
827
pop bc ; restore bytes to be copied
828
pop hl ; restore copy from string address
829
push de ; save copy to address
830
ldir ; copy bc bytes from hl (data address) to de (variable address)
833
pop de ; restore copy to address
834
pop ix ; restore variable address
835
ld (ix + 4), e ; put memory address low into variable
836
ld (ix + 5), d ; put memory address high into variable
837
ret ; variable assigned
842
pop.parm ; get parameter boolean result in hl
845
ld a, (ix+5) ; put boolean integer result in a
868
pop.parm ; get first parameter
870
call GET_INT.VALUE ; output BC with integer value
871
call GET_STRING_SPACE ; in bc = size, out hl = address, out a = string size
873
jr z, INPUT.FUNCTION.STR.2
877
INPUT.FUNCTION.STR.1:
878
__call_bios BIOS_CHGET
883
djnz INPUT.FUNCTION.STR.1
886
INPUT.FUNCTION.STR.2:
887
call COPY_TO.VAR_DUMMY.STR ; create a fake string variable from HL in HL
892
;---------------------------------------------------------------------------------------------------------
893
; MSX BASIC SUPPORT CODE
894
;---------------------------------------------------------------------------------------------------------
896
if defined CHR or defined INKEY.STR
902
ld bc, 2 ; string size
903
call memory.alloc ; in bc size, out ix new memory address, nz=OK
904
jr z, COPY_CHAR_TO_STR.ERROR
910
pop hl ; HL = string address
913
COPY_CHAR_TO_STR.ERROR:
919
;if defined INPUT or defined LINE_INPUT or defined SPC or defined SPACE or defined INPUT.FUNCTION.STR
922
; out: a = size, hl = address
931
call memory.alloc ; in bc = size, out ix = address, nz=OK
941
djnz GET_STRING_SPACE.1
948
; in hl = string, out hl = temp string
950
call GET_STR.LENGTH ; get string length, in hl = address, out a = size
954
call GET_STRING_SPACE ; in bc = size, out hl = address, out a = string size
960
call COPY_TO.VAR_DUMMY.STR ; create a fake string variable from HL in HL
965
ldir ; copy bc bytes from hl to de
972
if defined INPUT or defined LINE_INPUT
975
jr c, INPUT.EXIT ; exit if CTRL+STOP
977
inc hl ; string start
978
call COPY_TO.TEMP_STR
979
call COPY_TO.VAR_DUMMY.STR ; make a fake string variable from HL
980
push.parm ; LET parameter 2 - fake string variable as right operand
982
push.parm ; LET parameter 1 - input variable as left operand
983
call LET ; put string into variable
986
ld (BIOS_CLIKSW), a ; disable keyboard click
991
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
1006
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1008
ld a, (hl) ; trap status
1009
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1010
ret nz ; return if false
1012
ld e, (hl) ; get trap address
1019
ret z ; return if address zero
1021
__call_basic BASIC_TRAP_ACKNW
1022
__call_basic BASIC_TRAP_PAUSE
1023
ld hl, TRAP_HANDLER.1
1024
ld a, (BASIC_ONGSBF) ; save traps execution
1027
ld (BASIC_ONGSBF), a ; disable traps execution
1028
push hl ; next return will be to trap handler
1029
push de ; indirect jump to trap address
1031
TRAP_HANDLER.1: pop af
1032
ld (BASIC_ONGSBF), a ; restore traps execution
1035
cp 1 ; trap enabled?
1037
__call_basic BASIC_TRAP_UNPAUSE
1040
; hl = trap block, de = trap handler
1042
ld (hl), a ; trap block status
1044
ld (hl), e ; trap block handler (pointer)
1051
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
1054
ld (BIOS_TEMP), a ; save voice number
1058
ret nz ; return if not string
1061
ld (BIOS_TEMP2), a ; save string size
1062
push hl ; string address
1063
ld a, (BIOS_TEMP) ; restore voice number
1064
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1066
ld a, (BIOS_TEMP2) ; restore string size
1067
ld (hl), a ; string size
1069
ld (hl), e ; string address
1073
ld D,H ; voice stack
1087
;---------------------------------------------------------------------------------------------------------
1088
; VARIABLES ROUTINES
1089
;---------------------------------------------------------------------------------------------------------
1091
; input hl = variable address
1092
; input bc = variable name
1093
; input d = variable type
1094
INIT_VAR: ld (hl), d ; variable type
1096
ld (hl), c ; variable name 1
1098
ld (hl), b ; variable name 2
1112
CLEAR.VAR.LOOP: inc hl
1113
ld (hl), 0 ; data address/value
1116
; input HL = variable address
1117
; input A = variable output type
1118
; output HL = casted data address
1128
; input HL = variable address
1129
; output HL = variable address
1130
CAST_TO.INT: ;push af
1135
jp z, CAST_STR_TO.INT
1137
jp z, CAST_SGL_TO.INT
1139
jp z, CAST_DBL_TO.INT
1142
; input HL = variable address
1143
; output HL = variable address
1144
CAST_TO.STR: ;push af
1147
jp z, CAST_INT_TO.STR
1151
jp z, CAST_SGL_TO.STR
1153
jp z, CAST_DBL_TO.STR
1156
; input HL = variable address
1157
; output HL = variable address
1158
CAST_TO.SGL: ;push af
1161
jp z, CAST_INT_TO.SGL
1163
jp z, CAST_STR_TO.SGL
1167
jp z, CAST_DBL_TO.SGL
1170
; input HL = variable address
1171
; output HL = variable address
1172
CAST_TO.DBL: ;push af
1175
jp z, CAST_INT_TO.DBL
1177
jp z, CAST_STR_TO.DBL
1179
jp z, CAST_SGL_TO.DBL
1184
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1185
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1186
CAST_INT_TO.STR: call COPY_TO.DAC
1188
__call_bios MATH_FOUT ; convert DAC to string
1189
call COPY_TO.TEMP_STR
1191
CAST_INT_TO.SGL: call COPY_TO.DAC
1192
__call_bios MATH_FRCSGL
1195
CAST_INT_TO.DBL: call COPY_TO.DAC
1196
__call_bios MATH_FRCDBL
1199
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1200
CAST_DBL_TO.INT: call COPY_TO.DAC
1201
__call_bios MATH_FRCINT
1204
CAST_STR_TO.INT: ld a, 2
1205
call CAST_STR_TO.VAL ;
1207
__call_bios MATH_FRCINT
1211
CAST_STR_TO.SGL: ld a, 4
1212
call CAST_STR_TO.VAL ;
1214
__call_bios MATH_FRCSGL
1218
CAST_STR_TO.DBL: ld a, 8
1219
call CAST_STR_TO.VAL ;
1221
__call_bios MATH_FRCDBL
1225
CAST_STR_TO.VAL: ld (BASIC_VALTYP), a
1228
__call_bios MATH_FIN ; convert string to a value type
1231
GET_INT.VALUE: inc hl ; output BC with integer value
1237
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1238
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1239
GET_INT.ADDR: ; same as GET_DBL.ADDR
1240
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1241
GET_DBL.ADDR: inc hl
1246
GET_STR.ADDR: push hl
1252
; input hl = string address
1253
; output a = string length
1254
GET_STR.LENGTH: push hl
1257
cpir ; hl = source, a = compare char, bc = count
1264
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
1265
ld iy, (BASIC_ARG+1) ; string 2
1266
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
1267
cp (iy) ; char s1 = char s2?
1268
jr nz, STRING.COMPARE.NE ; if not equal...
1270
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
1271
ld a, (iy) ; next char from string 2
1273
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
1276
jr STRING.COMPARE.NX ; get next char pair
1277
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
1279
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
1280
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
1281
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
1282
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
1284
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
1286
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
1288
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
1289
ld a, (BASIC_ARG) ; s2 size
1290
add a, (ix) ; s3 size = s1 size + s2 size
1294
inc bc ; add 1 byte to size
1295
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1296
jp z, memory.error ;
1300
ld a, (BASIC_DAC) ; s1 size
1301
ld hl, (BASIC_DAC + 1) ; string 1
1302
call COPY_TO.STR ; copy to new memory
1303
ld a, (BASIC_ARG) ; s2 size
1304
ld hl, (BASIC_ARG + 1) ; string 2
1305
call COPY_TO.STR ; copy to new memory
1307
ld (de), a ; null terminated
1310
call COPY_TO.VAR_DUMMY.STR ;
1311
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
1312
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
1314
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
1316
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
1317
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
1318
or a ; cp 0 - is it the string end?
1320
__call_bios BIOS_CHPUT ; put the char (a) into text screen
1322
jr STRING.PRINT.T ; repeat
1323
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
1324
or a ; cp 0 - is it the string end?
1326
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
1328
jr STRING.PRINT.G1 ; repeat
1329
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
1330
or a ; cp 0 - is it the string end?
1332
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
1335
jr STRING.PRINT.G2 ; repeat
1337
; a = string size to copy
1338
; input hl = string from
1339
; input de = string to
1341
ret z ; avoid copy if size = zero
1343
ld c, a ; string size
1344
ldir ; copy bc bytes from hl to de
1346
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
1347
ld a, (LIT_QUOTE_CHAR)
1350
COPY_BAS_BUF.LOOP: ld a, (hl)
1352
jr z, COPY_BAS_BUF.EXIT
1356
jr COPY_BAS_BUF.LOOP
1357
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
1364
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
1366
jr nz, COPY_TO.VAR_DUMMY.DBL
1367
call GET_STR.LENGTH ; get string length, in hl = address, out a = size
1368
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
1369
ld (ix), 3 ; data type string
1371
ld (ix+2), 255 ; var type fixed
1372
ld (ix+3), a ; string length
1373
ld (ix+4), l ; data address low
1374
ld (ix+5), h ; data address high
1375
push ix ; output var address...
1378
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
1379
ld (ix), 2 ; data type string
1390
push ix ; output var address...
1393
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
1394
ld (ix), a ; data type
1399
push ix ; just to copy ix to de
1404
ldir ; copy bc bytes from hl (data address) to de (variable address)
1405
push ix ; output var address...
1408
GET_VAR_DUMMY.ADDR: push af ;
1411
ld ix, (VAR_DUMMY.POINTER) ;
1412
ld a, (VAR_DUMMY.COUNTER) ;
1413
GET_VAR_DUMMY.NEXT: add ix, de ;
1416
jr nz, GET_VAR_DUMMY.EXIT ;
1418
ld ix, VAR_DUMMY.DATA ;
1419
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
1420
ld (VAR_DUMMY.COUNTER), a ;
1421
ld a, (ix) ; get last var dummy type
1422
cp 3 ; is it string?
1423
call z, GET_VAR_DUMMY.FREE ; free string memory
1430
ld a, (ix+3) ; string size
1431
ld l, (ix+4) ; get string data address
1436
call nz, memory.free ; free memory
1440
; input hl = variable address
1441
COPY_TO.DAC: ld de, BASIC_DAC
1442
COPY_TO.DAC.DATA: ld a, (hl)
1443
ld (BASIC_VALTYP), a
1447
ld bc, 8 ; data = 8 bytes
1448
ldir ; copy bc bytes from hl (data address) to de (variable address)
1450
COPY_TO.ARG: ld de, BASIC_ARG ;
1451
jr COPY_TO.DAC.DATA ;
1452
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
1454
ld bc, 8 ; data = 8 bytes
1455
ldir ; copy bc bytes from hl (data address) to de (variable address)
1457
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
1459
ld bc, 8 ; data = 8 bytes
1460
ldir ; copy bc bytes from hl (data address) to de (variable address)
1462
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
1463
ld de, BASIC_SWPTMP ;
1464
ld bc, 8 ; data = 8 bytes
1465
ldir ; copy bc bytes from hl (data address) to de (variable address)
1467
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
1469
ld bc, 8 ; data = 8 bytes
1470
ldir ; copy bc bytes from hl (data address) to de (variable address)
1473
exx ; save registers
1476
ld de, BASIC_SWPTMP ;
1477
ldir ; copy bc bytes from hl to de
1481
ldir ; copy bc bytes from hl to de
1483
ld hl, BASIC_SWPTMP ;
1485
ldir ; copy bc bytes from hl to de
1486
exx ; restore registers
1489
CLEAR.DAC: ld de, BASIC_DAC
1490
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
1493
ld bc, 8 ; data = 8 bytes
1494
ldir ; copy bc bytes from hl (data address) to de (variable address)
1496
CLEAR.ARG: ld de, BASIC_ARG
1501
;---------------------------------------------------------------------------------------------------------
1502
; MATH 16 BITS ROUTINES
1503
;---------------------------------------------------------------------------------------------------------
1505
MATH.PARM.POP: pop af ; get PC from caller stack
1506
ex af, af' ; save PC to temp
1507
pop.parm ; get first parameter
1508
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1509
pop.parm ; get second parameter
1510
ex af, af' ; restore PC from temp
1511
push af ; put again PC from caller in stack
1512
ex af, af' ; restore 1st data type
1513
push af ; save 1st data type
1514
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1515
pop bc ; restore 1st data type (ARG) in B
1516
cp b ; test if data type in A (DAC) = data type in B (ARG)
1517
ret z ; return if is equal data types
1518
MATH.PARM.CAST: push bc ; else cast both to double
1519
and 12 ; test if single/double
1520
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
1521
__call_bios MATH_FRCDBL ; convert DAC to double
1522
MATH.PARM.CST1: pop af ;
1523
and 12 ; test if single/double
1524
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
1525
ld (BASIC_VALTYP), a ;
1526
call COPY_TO.DAC_TMP ;
1527
call COPY_TO.ARG_DAC ;
1528
__call_bios MATH_FRCDBL ; convert ARG to double
1529
call COPY_TO.DAC_ARG ;
1530
call COPY_TO.TMP_DAC ;
1531
MATH.PARM.CST2: ld a, 8 ;
1532
ld (BASIC_VALTYP), a ;
1534
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
1535
pop af ; get PC from caller stack
1536
ex af, af' ; save PC to temp
1537
pop.parm ; get first parameter
1538
ld a, (hl) ; get parameter type
1539
and 2 ; test if integer
1540
jr z, MATH.PARM.POP.I1 ; do cast if not integer
1541
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1542
jr MATH.PARM.POP.I2 ; go to next parameter
1543
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
1544
__call_bios MATH_FRCINT ; convert DAC to int
1545
call COPY_TO.DAC_ARG ; copy DAC to ARG
1546
MATH.PARM.POP.I2: pop.parm ; get second parameter
1547
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1548
and 2 ; test if integer
1549
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
1550
__call_bios MATH_FRCINT ; convert DAC to int
1552
ld (BASIC_VALTYP), a ;
1554
ex af, af' ; restore PC from temp
1555
push af ; put again PC from caller in stack
1557
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
1563
; output in parm stack
1564
; http://www.z80.info/zip/zaks_book.pdf - page 104
1565
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
1566
ld bc, (BASIC_ARG+2) ;
1568
ld (BASIC_DAC+2), hl ;
1573
if defined MATH.SUB or defined MATH.NEG
1576
; output in parm stack
1577
; http://www.z80.info/zip/zaks_book.pdf - page 104
1578
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
1579
ld de, (BASIC_ARG+2) ;
1582
ld (BASIC_DAC+2), hl ;
1587
if defined MATH.MULT
1590
; output in parm stack
1591
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
1592
ld bc, (BASIC_ARG+2) ;
1594
ld (BASIC_DAC+2), hl ;
1597
; input HL = multiplicand
1598
; input BC = multiplier
1599
; output HL = result
1600
; http://www.z80.info/zip/zaks_book.pdf - page 131
1601
MATH.MULT.16: ld a, c ; low multiplier
1602
ld c, b ; high multiplier
1604
ld d, h ; multiplicand
1607
MULT16LOOP: srl c ; right shift multiplier high
1608
rra ; rotate right multiplier low
1609
jr nc, MULT16NOADD ; test carry
1610
add hl, de ; add multiplicand to result
1611
MULT16NOADD: ex de, hl
1612
add hl, hl ; double - shift multiplicand
1619
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
1621
; input AC = dividend
1622
; input DE = divisor
1623
; output AC = quotient
1624
; output HL = remainder
1625
; http://www.z80.info/zip/zaks_book.pdf - page 140
1626
MATH.DIV.16: ld hl, 0 ; clear accumulator
1627
ld b, 16 ; set counter
1628
DIV16LOOP: rl c ; rotate accumulator result left
1630
adc hl, hl ; left shift
1631
sbc hl, de ; trial subtract divisor
1632
jr nc, $ + 3 ; subtract was OK ($ = current location)
1633
add hl, de ; restore accumulator
1634
ccf ; calculate result bit
1635
djnz DIV16LOOP ; counter not zero
1636
rl c ; shift in last result bit
1642
if defined GFX_FAST or defined LINE
1644
; compare two signed 16 bits integers
1645
; HL < DE: Carry flag
1646
; HL = DE: Zero flag
1647
; http://www.z80.info/zip/zaks_book.pdf - page 531
1648
MATH.COMP.S16: ld a, h ; test high order byte
1649
and 0x80 ; test sign, clear carry
1650
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
1652
ret nz ; de is negative (and hl is positive)
1654
cp d ; signs are both positive, so normal compare
1656
ld a, l ; test low order byte
1659
MATH.COMP.S16.NEGM1:
1661
rla ; sign bit into carry
1662
ret c ; signs different
1664
cp d ; both signs negative
1674
MATH.ADD.SGL: ld a, 8 ;
1675
ld (BASIC_VALTYP), a ;
1676
MATH.ADD.DBL: __call_bios MATH_DECADD ;
1681
if defined MATH.SUB or defined MATH.NEG
1683
MATH.SUB.SGL: ld a, 8 ;
1684
ld (BASIC_VALTYP), a ;
1685
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
1690
if defined MATH.MULT
1692
MATH.MULT.SGL: ld a, 8 ;
1693
ld (BASIC_VALTYP), a ;
1694
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
1702
; output in parm stack
1703
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
1706
ld (BASIC_VALTYP), a ;
1707
__call_bios MATH_FRCDBL ; convert ARG to double
1709
MATH.DIV.SGL: ld a, 8 ;
1710
ld (BASIC_VALTYP), a ;
1711
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
1716
if defined MATH.IDIV
1719
; output in parm stack
1720
MATH.IDIV.SGL: ld a, 8 ;
1721
ld (BASIC_VALTYP), a ;
1722
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1725
ld (BASIC_VALTYP), a ;
1726
__call_bios MATH_FRCINT ; convert ARG to integer
1728
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
1731
ld de, (BASIC_ARG+2) ;
1735
ld (BASIC_DAC+2), hl ; quotient
1742
MATH.POW.INT: ld (BASIC_VALTYP), a ;
1743
__call_bios MATH_FRCDBL ; convert DAC to double
1746
ld (BASIC_VALTYP), a ;
1747
__call_bios MATH_FRCDBL ; convert ARG to double
1749
MATH.POW.SGL: ld a, 8 ;
1750
ld (BASIC_VALTYP), a ;
1751
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
1758
;MATH.MOD.SGL: ld a, 8 ;
1759
; ld (BASIC_VALTYP), a ;
1760
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1761
; call SWAP.DAC.ARG ;
1763
; ld (BASIC_VALTYP), a ;
1764
; __call_bios MATH_FRCINT ; convert ARG to integer
1765
; call SWAP.DAC.ARG ;
1766
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
1769
ld de, (BASIC_ARG+2) ;
1771
ld (BASIC_DAC+2), hl ; remainder
1778
; fast 16-bit integer square root
1779
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
1780
; 92 bytes, 344-379 cycles (average 362)
1781
; v2 - 3 t-state optimization spotted by Russ McNulty
1782
; call with hl = number to square root
1783
; returns a = square root
1860
if defined RANDOMIZE or defined SEED
1862
MATH.RANDOMIZE: di ;
1863
ld bc, (BIOS_JIFFY) ;
1866
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
1867
push bc ; in bc = new integer seed
1871
ld (BASIC_DAC+2), bc ; copy bc to dac
1872
ld a, 2 ; type integer
1873
ld (BASIC_VALTYP), a ;
1874
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
1875
__call_bios MATH_NEG ; DAC = -DAC
1876
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
1881
MATH.ERROR: ld e, 13 ; type mismatch
1882
__call_basic BASIC_ERROR_HANDLER ;
1886
;---------------------------------------------------------------------------------------------------------
1888
;---------------------------------------------------------------------------------------------------------
1890
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
1892
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
1894
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
1895
ld de, (BASIC_ARG+2) ;
1896
__call_bios MATH_ICOMP ;
1898
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
1899
ld de, (BASIC_ARG+2) ;
1900
__call_bios MATH_DCOMP ;
1902
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
1904
BOOLEAN.CMP.STR: call STRING.COMPARE ;
1907
if defined BOOLEAN.GT
1909
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
1911
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
1913
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
1915
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
1917
BOOLEAN.GT.RET: cp 0x01 ;
1918
jp z, BOOLEAN.RET.TRUE ;
1919
jp BOOLEAN.RET.FALSE ;
1922
if defined BOOLEAN.LT
1924
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
1926
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
1928
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
1930
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
1932
BOOLEAN.LT.RET: cp 0xFF ;
1933
jp z, BOOLEAN.RET.TRUE ;
1934
jp BOOLEAN.RET.FALSE ;
1938
if defined BOOLEAN.GE
1940
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
1942
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
1944
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
1946
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
1948
BOOLEAN.GE.RET: cp 0x01 ;
1949
jp z, BOOLEAN.RET.TRUE ;
1951
jp z, BOOLEAN.RET.TRUE ;
1952
jp BOOLEAN.RET.FALSE ;
1956
if defined BOOLEAN.LE
1958
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
1960
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
1962
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
1964
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
1966
BOOLEAN.LE.RET: cp 0xFF ;
1967
jp z, BOOLEAN.RET.TRUE ;
1969
jp z, BOOLEAN.RET.TRUE ;
1970
jp BOOLEAN.RET.FALSE ;
1974
if defined BOOLEAN.NE
1976
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
1978
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
1980
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
1982
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
1984
BOOLEAN.NE.RET: or a ; cp 0
1985
jp nz, BOOLEAN.RET.TRUE ;
1986
jp BOOLEAN.RET.FALSE ;
1990
if defined BOOLEAN.EQ
1992
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
1994
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
1996
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
1998
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2000
BOOLEAN.EQ.RET: or a ; cp 0
2001
jp z, BOOLEAN.RET.TRUE ;
2002
jp BOOLEAN.RET.FALSE ;
2006
if defined BOOLEAN.AND
2008
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2009
ld hl, BASIC_ARG+2 ;
2011
ld (BASIC_DAC+2), a ;
2013
ld a, (BASIC_DAC+3) ;
2015
ld (BASIC_DAC+3), a ;
2021
if defined BOOLEAN.OR
2023
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2024
ld hl, BASIC_ARG+2 ;
2026
ld (BASIC_DAC+2), a ;
2028
ld a, (BASIC_DAC+3) ;
2030
ld (BASIC_DAC+3), a ;
2036
if defined BOOLEAN.XOR
2038
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2039
ld hl, BASIC_ARG+2 ;
2041
ld (BASIC_DAC+2), a ;
2043
ld a, (BASIC_DAC+3) ;
2045
ld (BASIC_DAC+3), a ;
2051
if defined BOOLEAN.EQV
2053
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2054
ld hl, BASIC_ARG+2 ;
2057
ld (BASIC_DAC+2), a ;
2059
ld a, (BASIC_DAC+3) ;
2062
ld (BASIC_DAC+3), a ;
2068
if defined BOOLEAN.IMP
2070
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2071
ld hl, BASIC_ARG+2 ;
2074
ld (BASIC_DAC+2), a ;
2076
ld a, (BASIC_DAC+3) ;
2079
ld (BASIC_DAC+3), a ;
2085
if defined BOOLEAN.SHR
2087
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2088
ld a, (BASIC_ARG+2) ;
2090
jp z, MATH.PARM.PUSH ; return if not shift
2091
ld b, a ; shift count
2092
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2095
djnz BOOLEAN.SHR.INT.N ; next shift
2097
jp MATH.PARM.PUSH ; return DAC
2101
if defined BOOLEAN.SHL
2103
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2104
ld a, (BASIC_ARG+2) ;
2106
jp z, MATH.PARM.PUSH ; return if not shift
2107
ld b, a ; shift count
2108
BOOLEAN.SHL.INT.N: rl (ix) ;
2111
djnz BOOLEAN.SHL.INT.N ; next shift
2113
jp MATH.PARM.PUSH ; return DAC
2117
if defined BOOLEAN.NOT
2119
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2121
ld (BASIC_DAC+2), a ;
2122
ld a, (BASIC_DAC+3) ;
2124
ld (BASIC_DAC+3), a ;
2132
;---------------------------------------------------------------------------------------------------------
2133
; MEMORY ALLOCATION ROUTINES
2134
;---------------------------------------------------------------------------------------------------------
2135
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2136
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2137
; https://www.msx.org/users/samsaga2
2138
;---------------------------------------------------------------------------------------------------------
2139
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2140
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2141
block.next: equ 0 ; next free block address
2142
block.size: equ 2 ; size of block including header
2143
block: equ 4 ; block.next + block.size
2147
ld ix,memory.heap_start ; first block
2148
ld hl,memory.heap_start+block ; second block
2149
;; first block NEXT=secondblock, SIZE=0
2150
;; with this block we have a fixed start location
2151
;; because never will be allocated
2152
ld (ix+block.next),l
2153
ld (ix+block.next+1),h
2154
ld (ix+block.size),0
2155
ld (ix+block.size+1),0
2156
;; second block NEXT=0, SIZE=all
2157
;; the first and only free block have all available memory
2158
ld (ix+block.next+block),0
2159
ld (ix+block.next+block+1),0
2161
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2164
ld de, memory.heap_start + (block * 2) + 100
2166
;ld de, block * 2 + 100
2168
ld (ix+block.size+block),l
2169
ld (ix+block.size+block+1),h
2173
;; IN BC=size, OUT IX=memptr, NZ=ok
2181
ld ix,memory.heap_start ; this
2184
ld l,(ix+block.size)
2185
ld h,(ix+block.size+1)
2188
jp z, memory.alloc.exactfit
2189
jp c, memory.alloc.nextblock
2190
;; split found block
2191
memory.alloc.splitfit:
2192
;; free space must allow at least two blocks headers (current + next)
2194
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2197
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2198
memory.alloc.splitfit.do:
2199
;; newfreeblock = this + BC
2203
;; prevblock->next = newfreeblock
2204
ld (iy+block.next),l
2205
ld (iy+block.next+1),h
2206
;; newfreeblock->next = this->next
2208
pop iy ; iy = newfreeblock
2209
ld l,(ix+block.next)
2210
ld h,(ix+block.next+1)
2211
ld (iy+block.next),l
2212
ld (iy+block.next+1),h
2213
;; newfreeblock->size = this->size - BC
2214
ld l,(ix+block.size)
2215
ld h,(ix+block.size+1)
2218
ld (iy+block.size),l
2219
ld (iy+block.size+1),h
2221
ld (ix+block.size),c
2222
ld (ix+block.size+1),b
2224
;; use whole found block
2225
memory.alloc.exactfit:
2226
;; prevblock->next = this->next - remove block from free list
2227
ld l,(ix+block.next)
2228
ld h,(ix+block.next+1)
2229
ld (iy+block.next),l
2230
ld (iy+block.next+1),h
2239
memory.alloc.nextblock:
2240
ld l,(ix+block.next)
2241
ld h,(ix+block.next+1)
2248
;; this = this->next
2251
jp memory.alloc.find
2256
;; HL = IX - block_header_size
2263
ld ix,memory.heap_start
2265
ld e,(ix+block.next)
2266
ld d,(ix+block.next+1)
2269
jp z, memory.free.passedend
2270
sbc hl,de ; test this (HL) against next (DE)
2271
jr c, memory.free.found ; if DE > HL
2272
add hl,de ; restore hl value
2274
pop ix ; current = next
2277
;; ix=prev, hl=this, de=next
2279
add hl,de ; restore hl value
2280
ld (ix+block.next), l
2281
ld (ix+block.next+1), h ; prev->next = this
2284
ld (iy+block.next), e
2285
ld (iy+block.next+1), d ; this->next = next
2286
push ix ; prev x this
2291
call memory.free.coalesce
2292
pop ix ; this x next
2293
jr memory.free.coalesce
2297
memory.free.coalesce:
2298
ld c, (iy+block.size)
2299
ld b, (iy+block.size+1) ; bc = this->size
2303
adc hl, bc ; hl = this + this->size
2307
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
2308
jr z, memory.free.coalesce.do
2309
push ix ; else, new *this = *next
2312
memory.free.coalesce.do:
2313
ld l, (ix+block.size)
2314
ld h, (ix+block.size+1) ; hl = next->size
2316
adc hl, bc ; hl += this->size
2317
ld (iy+block.size), l
2318
ld (iy+block.size+1), h ; this->size = hl
2319
ld l, (ix+block.next)
2320
ld h, (ix+block.next+1) ; hl = next->next
2321
ld (iy+block.next), l
2322
ld (iy+block.next+1), h ; this->next = hl
2325
memory.free.passedend:
2326
;; append block at the end of the free list
2327
ld (ix+block.next),l
2328
ld (ix+block.next+1),h
2331
ld (iy+block.next),0
2332
ld (iy+block.next+1),0
2338
ld ix,memory.heap_start
2340
memory.get_free.count:
2342
add a,(ix+block.size)
2345
adc a,(ix+block.size+1)
2347
ld l,(ix+block.next)
2348
ld h,(ix+block.next+1)
2354
jr memory.get_free.count
2356
memory.error: ld e, 7 ; out of memory
2357
__call_basic BASIC_ERROR_HANDLER ;
2362
;---------------------------------------------------------------------------------------------------------
2364
;---------------------------------------------------------------------------------------------------------
2373
RET_MATH_LIB: call COPY_TO.TMP_DAC
2379
MATH_DECADD: ld ix, addSingle
2384
if defined MATH.SUB or defined MATH.NEG
2386
MATH_DECSUB: ld ix, subSingle
2391
if defined MATH.MULT
2393
MATH_DECMUL: ld ix, mulSingle
2400
MATH_DECDIV: ld ix, divSingle
2408
MATH_SNGEXP: ld ix, powSingle
2415
MATH_COS: ld ix, cosSingle
2422
MATH_SIN: ld ix, sinSingle
2429
MATH_TAN: ld ix, tanSingle
2436
MATH_ATN: ld ix, atanSingle
2443
MATH_SQR: ld ix, sqrtSingle
2450
MATH_LOG: ld ix, lnSingle
2457
MATH_EXP: ld ix, expSingle
2464
MATH_ABSFN: ld ix, absSingle
2469
if defined MATH.SEED or defined MATH.NEG
2471
MATH_NEG: ld ix, negSingle
2478
MATH_SGN: ld ix, sgnSingle
2483
if defined RND or defined MATH.SEED
2485
MATH_RND: ld ix, randSingle
2490
MATH_FRCINT: ld hl, BASIC_DAC
2503
ld (BASIC_VALTYP), a
2506
MATH_FRCDBL: ; same as MATH_FRCSGL
2507
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
2508
ld bc, BASIC_DAC ; output address
2511
ld (BASIC_VALTYP), a
2514
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
2516
jr nz, MATH_ICOMP.NE.HIGH
2519
jr nz, MATH_ICOMP.NE.LOW
2521
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
2523
jr nz, MATH_DCOMP.GT
2525
MATH_ICOMP.GT.HIGH: bit 7, d
2528
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
2531
MATH_XDCOMP: ; same as MATH_DCOMP
2532
MATH_DCOMP: ld ix, cmpSingle
2536
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
2538
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
2540
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
2543
if defined CAST_STR_TO.VAL
2545
MATH_FIN: ; HL has the source string
2546
ld a, (BASIC_VALTYP)
2547
cp 2 ; test if integer
2549
;ld hl, (BASIC_DAC+2)
2550
;ld de, BASIC_STRBUF
2555
ld (BASIC_DAC+2), hl
2556
ld (BASIC_DAC+4), bc
2557
ld (BASIC_DAC+6), bc
2558
;ld hl, BASIC_STRBUF
2560
MATH_FIN.1: ld BC, BASIC_DAC
2566
if defined CAST_INT_TO.STR
2568
MATH_FOUT: ld a, (BASIC_VALTYP)
2569
cp 2 ; test if integer
2571
ld hl, (BASIC_DAC+2)
2576
MATH_FOUT.1: ld hl, BASIC_DAC
2587
;---------------------------------------------------------------------------------------------------------
2589
; Copyright 2018 Zeda A.K. Thomas
2590
;---------------------------------------------------------------------------------------------------------
2592
; https://github.com/Zeda/z80float
2593
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
2594
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
2595
;---------------------------------------------------------------------------------------------------------
2597
; HL points to the first operand
2598
; DE points to the second operand (if needed)
2599
; IX points to the third operand (if needed, rare)
2600
; BC points to where the result should be output
2601
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
2602
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
2603
; exponent biased by +128.
2604
;---------------------------------------------------------------------------------------------------------
2605
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
2606
;---------------------------------------------------------------------------------------------------------
2608
;---------------------------------------------------------------------------------------------------------
2610
;---------------------------------------------------------------------------------------------------------
2612
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
2613
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
2614
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
2615
scrap: equ BASIC_HOLD8
2616
seed0: equ BASIC_RNDX
2617
seed1: equ seed0 + 4
2618
var48: equ scrap + 4
2621
addend2: equ scrap+7 ;4 bytes
2622
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
2623
var_y: equ var_x + 4 ;4 bytes
2624
var_z: equ var_y + 4 ;4 bytes
2625
var_a: equ var_z + 4 ;4 bytes
2626
var_b: equ var_a + 4 ;4 bytes
2627
var_c: equ var_b + 4 ;4 bytes
2628
temp: equ var_c + 4 ;4 bytes
2629
temp1: equ temp + 4 ;4 bytes
2630
temp2: equ temp1 + 4 ;4 bytes
2631
temp3: equ temp2 + 4 ;4 bytes
2633
pow10exp_single: equ scrap+9
2634
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
2636
;---------------------------------------------------------------------------------------------------------
2638
;---------------------------------------------------------------------------------------------------------
2640
;;Still need to tend to special cases
2708
pop hl ;bigger float
2840
;;Need to adjust sign flag
2863
;;How many push/pops are needed?
2871
;;How many push/pops are needed?
2877
;;How many push/pops are needed?
2878
;;Return bigger number
2885
;---------------------------------------------------------------------------------------------------------
2887
;---------------------------------------------------------------------------------------------------------
2910
jp addInject ;jumps in to the addSingle routine
2912
;---------------------------------------------------------------------------------------------------------
2914
;---------------------------------------------------------------------------------------------------------
2917
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
2918
;Outputs: float1*float2 is stored to (BC)
2919
;573+mul24+{0,35}+{0,30}
2922
;avg: 2055.13839751681cc
2948
;;return float in CHLB
2958
jr z,mulSingle_case0
2970
;jr z,mulSingle_case1
2974
jp z,mulSingle_case1
2979
rra ; |Lots of help from Runer112 and
2980
adc a,a ; |calc84maniac for optimizing
2981
jp po,bad ; |this exponent check.
2990
call mul24 ;BDE*CHL->HLBCDE, returns sign info
3047
;special*x = special
3068
;basically, if b|c has bit 5 set, return NaN
3101
;;avg :1464.9033203125cc (1464+925/1024)
3104
;avg: 1449.63839751681cc
3145
;---------------------------------------------------------------------------------------------------------
3147
;---------------------------------------------------------------------------------------------------------
3150
;;HL points to numerator
3151
;;DE points to denominator
3152
;;BC points to where the quotient gets written
3154
divSingle_no_pushpop:
3160
xor (hl) ; |Get sign of output
3167
ex de,hl ; |Get exponent
3274
call divsub1 ;34 or 66
3292
;34cc or 66cc or 93cc
3307
;---------------------------------------------------------------------------------------------------------
3309
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
3310
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
3311
;---------------------------------------------------------------------------------------------------------
3312
;double mypow( double base, double power, double precision )
3314
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3315
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3316
; else if ( precision >= 1 ) {
3317
; if( base >= 0 ) return sqrt( base );
3318
; else return sqrt( -base );
3319
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3322
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
3328
;;BC points to output
3332
ld bc, var_y ; power
3337
ld hl, const_precision
3338
ld bc, var_a ; precision
3341
ld bc, var_z ; result
3350
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3356
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3362
; else if ( precision >= 1 ) {
3363
; if( base >= 0 ) return sqrt( base );
3364
; else return sqrt( -base );
3370
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3395
; return 1 / mypow( base, -power, precision );
3414
; return base * mypow( base, power-1, precision );
3433
; if( base >= 0 ) return sqrt( base );
3434
; else return sqrt( -base );
3460
; 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)))))
3461
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
3462
;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.
3465
;x-=int(x) ;leaves x in [0,1)
3467
;;if x==inf -> out==inf
3468
;;if x==-inf -> out==0
3469
;;if x==NAN -> out==NAN
3476
push af ;keep track of sign
3486
jr c,_pow_1 ;int(x)=0
3499
jr nz,exp_normalized
3510
jr exp_normalized ;.db $11 ;start of `ld de,**`
3517
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
3526
jp z,exp_underflow+1
3527
;perform 1-(var48+10)--> var48+10
3535
;our 'x' is at var48+10
3536
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
3537
;uses 14 bytes of RAM
3579
;-inf -> +0 because lim approaches 0 from the right
3601
;-inf -> +0 because lim approaches 0 from the right
3603
sbc a,a ;FF if should be 0,
3618
;---------------------------------------------------------------------------------------------------------
3620
;---------------------------------------------------------------------------------------------------------
3622
if defined MATH_SQR or defined MATH_EXP
3624
;Uses 3 bytes at scrap
3626
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
3645
jp z,sqrtSingle_special
3648
push af ;new exponent
3658
;AHL is the new remainder
3659
;Need to divide by 2, then divide by the 16-bit (var_x+4)
3663
;We are just going to approximate it
3745
;Output: DE is the sqrt, AHL is the remainder
3746
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
3770
jr _15a ;.db $FE ;start of `cp *`
3784
jr _16a ;.db $FE ;start of `cp *`
3798
jr _17a ;.db $FE ;start of `cp *`
3812
jr _18a ;.db $FE ;start of `cp *`
3816
;Now we have four more iterations
3817
;The first two are no problem
3829
jr _19a ;.db $FE ;start of `cp *`
3843
jr _20a ;.db $FE ;start of `cp *`
3848
;On the next iteration, HL might temporarily overflow by 1 bit
3850
rl d ;sla e \ rl d \ inc e
3854
adc hl,hl ;This might overflow!
3855
jr c,sqrt32_iter15_br0
3868
;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
3871
ld b,a ;either 0x00 or 0x80
3892
;returns A as the sqrt, HL as the remainder, D = 0
3906
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3917
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3928
dec d ;this resets the low bit of D, so `srl d` resets carry.
3929
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
3951
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3964
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3986
;---------------------------------------------------------------------------------------------------------
3988
;---------------------------------------------------------------------------------------------------------
3990
if defined MATH_LOG or defined MATH_LN
3993
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
3994
; a * x ^ (1/a) - a, where a = 100
3997
ld de, const_100_inv
3999
call powSingle ; temp = x ^ (1/100)
4003
call mulSingle ; temp1 = temp * 100
4006
call subSingle ; bc = temp1 - 100
4011
;---------------------------------------------------------------------------------------------------------
4013
;---------------------------------------------------------------------------------------------------------
4030
;---------------------------------------------------------------------------------------------------------
4032
;---------------------------------------------------------------------------------------------------------
4039
;;BC points to the output
4044
;;DE points to lg(y), HL points to x, BC points to output
4053
;---------------------------------------------------------------------------------------------------------
4055
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
4056
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
4057
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
4058
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
4059
;---------------------------------------------------------------------------------------------------------
4061
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4064
; taylor: x - x^3/6 + x^5/120 - x^7/5040
4065
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
4067
; var_b = round( x / (2*PI), 0 )
4068
; var_c = x - var_b*2*PI
4069
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4070
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4071
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4078
call copySingle ; return 0
4082
call trigRangeReductionSinCos
4087
call mulSingle ; var_b = var_a * var_a
4091
call mulSingle ; temp = x^2/5040
4095
call subSingle ; temp1 = 1/120 - temp
4099
call mulSingle ; temp = x^2 * temp1
4103
call subSingle ; temp1 = 1/6 - temp
4107
call mulSingle ; temp = x^2 * temp1
4111
call subSingle ; temp1 = 1 - temp
4115
call mulSingle ; return x * temp1
4118
trigRangeReductionSinCos:
4121
; var_b = round( x / (2*PI), 0 )
4129
; var_c = x - var_b*2*PI
4133
call mulSingle ; temp = var_b*2*PI
4137
call subSingle ; var_c = x - temp
4138
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4142
jr nc, trigRangeReductionSinCos.else.2
4145
call copySingle ; temp1 = var_c
4146
jr trigRangeReductionSinCos.endif.2
4147
trigRangeReductionSinCos.else.2:
4151
call addSingle ; temp1 = var_c + 2*PI
4152
trigRangeReductionSinCos.endif.2:
4153
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4157
jr c, trigRangeReductionSinCos.else.3
4158
jr z, trigRangeReductionSinCos.else.3
4162
call subSingle ; temp2
4163
jr trigRangeReductionSinCos.endif.3
4164
trigRangeReductionSinCos.else.3:
4167
call copySingle ; temp2 = temp1
4168
trigRangeReductionSinCos.endif.3:
4169
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4170
ld hl, const_half_pi
4173
jr c, trigRangeReductionSinCos.else.4
4174
jr z, trigRangeReductionSinCos.else.4
4178
call subSingle ; var_a
4179
jr trigRangeReductionSinCos.endif.4
4180
trigRangeReductionSinCos.else.4:
4183
call copySingle ; var_a = temp2
4184
trigRangeReductionSinCos.endif.4:
4185
; if( temp > PI, -1, 1 )
4189
jr nc, trigRangeReductionSinCos.endif.5
4193
ld (ix+2), a ; turn var_a to negative
4194
trigRangeReductionSinCos.endif.5:
4200
;---------------------------------------------------------------------------------------------------------
4202
;---------------------------------------------------------------------------------------------------------
4204
if defined MATH_COS or defined MATH_TAN
4207
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
4208
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4209
; reduction: same as sin
4218
call copySingle ; return 1
4222
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4223
call trigRangeReductionSinCos
4228
call mulSingle ; var_b = var_a * var_a
4232
call mulSingle ; temp = x^2/720
4236
call subSingle ; temp1 = 1/24 - temp
4240
call mulSingle ; temp = x^2 * temp1
4244
call subSingle ; temp1 = 1/2 - temp
4248
call mulSingle ; temp = x^2 * temp1
4252
call subSingle ; temp1 = 1 - temp
4254
; temp3 = abs(var_c)
4255
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
4262
ld (ix+2), a ; temp3 = abs(var_c)
4264
ld de, const_half_pi
4265
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
4266
jr nc, cosSingle.endif.1
4270
ld (ix+2), a ; temp1 = -temp1
4274
call copySingle ; return temp1
4279
;---------------------------------------------------------------------------------------------------------
4281
;---------------------------------------------------------------------------------------------------------
4302
;---------------------------------------------------------------------------------------------------------
4304
;---------------------------------------------------------------------------------------------------------
4309
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4310
; x < -1: atan - PI/2
4311
; x >= 1: PI/2 - atan
4312
;reduction: abs(X) > 1 : Y = 1 / X
4313
; abs(X) <= 1: Y = X
4322
call copySingle ; return 0
4326
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4327
call trigRangeReductionAtan
4333
call mulSingle ; var_b = var_a * var_a
4337
call mulSingle ; temp = (4*x)^2
4341
call divSingle ; temp1 = temp/9
4345
call addSingle ; temp = 7 + temp1
4349
call mulSingle ; temp1 = var_b * 9
4353
call divSingle ; temp2 = temp1 / temp
4357
call addSingle ; temp = 5 + temp2
4361
call mulSingle ; temp1 = var_b * 4
4365
call divSingle ; temp2 = temp1 / temp
4369
call addSingle ; temp = 3 + temp2
4373
call divSingle ; temp2 = var_b / temp
4377
call addSingle ; temp = 1 + temp2
4381
call divSingle ; temp2 = var_a / temp
4383
; x >= 1: PI/2 - atan
4387
ld hl, const_half_pi
4394
; x < -1: atan - PI/2
4405
ld de, const_half_pi
4414
call copySingle ; return temp2
4417
trigRangeReductionAtan:
4418
;reduction: abs(X) > 1 : Y = 1 / X
4419
; abs(X) <= 1: Y = X
4428
ld (ix+2), a ; abs(x)
4432
jr nc, trigRangeReductionAtan.1
4438
jr trigRangeReductionAtan.2
4439
trigRangeReductionAtan.1:
4444
trigRangeReductionAtan.2:
4448
jr c, trigRangeReductionAtan.3
4452
ld (ix+2), a ; y = -y
4453
trigRangeReductionAtan.3:
4458
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4460
;---------------------------------------------------------------------------------------------------------
4462
;---------------------------------------------------------------------------------------------------------
4476
;---------------------------------------------------------------------------------------------------------
4478
;---------------------------------------------------------------------------------------------------------
4547
if defined MATH_ABSFN
4549
;---------------------------------------------------------------------------------------------------------
4551
;---------------------------------------------------------------------------------------------------------
4554
;;HL points to the float
4555
;;BC points to where to output the result
4574
;---------------------------------------------------------------------------------------------------------
4576
;---------------------------------------------------------------------------------------------------------
4579
;;HL points to the float
4580
;;BC points to where to output the result
4585
if defined powSingle or defined sgnSingle or defined MATH_NEG
4587
;---------------------------------------------------------------------------------------------------------
4589
;---------------------------------------------------------------------------------------------------------
4592
;;HL points to the float
4593
;;BC points to where to output the result
4599
jr nz, negSingle.test.sign
4602
jr nz, negSingle.test.sign
4605
jr nz, negSingle.test.sign
4608
jr nz, negSingle.test.sign
4619
negSingle.test.sign:
4622
jr z, negSingle.positive
4626
call negSingle.positive
4645
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
4647
;---------------------------------------------------------------------------------------------------------
4649
;---------------------------------------------------------------------------------------------------------
4652
;Input: HL points to float1, DE points to float2
4654
; float1 >= float2 : nc
4655
; float1 < float2 : c,nz
4656
; float1 == float2 : z
4657
; There is a margin of error allowed in the lower 2 bits of the mantissa.
4659
;Currently fails when both numbers have magnitude less than about 2^-106
4694
ld a,(scrap+3) ;new power
4695
pop bc ;B is old power
4705
or 1 ;not equal, so reset z flag
4706
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
4716
;---------------------------------------------------------------------------------------------------------
4718
;---------------------------------------------------------------------------------------------------------
4721
;Stores a pseudo-random number on [0,1)
4722
;it won't produce values on (0,2^-23)
4731
;DEHL is the mantissa, B is the exponent
4747
;If we needed to shift more than 8 bits, we'll load in more random data
4752
jp nc,rand_no_more_rand_data
4760
rand_no_more_rand_data:
4779
;;Tested and passes all CAcert tests
4780
;;Uses a very simple 32-bit LCG and 32-bit LFSR
4781
;;it has a period of 18,446,744,069,414,584,320
4782
;;roughly 18.4 quintillion.
4783
;;LFSR taps: 0,2,6,7 = 11000101
4785
;;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.
4786
;Uses 64 bits of state
4822
if defined MATH_FOUT
4824
;---------------------------------------------------------------------------------------------------------
4826
; in HL = Single address
4827
; BC = String address
4828
; out A = String size
4829
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
4830
; http://0x80.pl/articles/convert-float-to-integer.html
4831
;---------------------------------------------------------------------------------------------------------
4845
; Move the float to scrap
4849
; Make the float negative, write a '-' if already negative
4858
ld a,'-' ; write '-' simbol
4866
; Check if the exponent field is 0 (a special value)
4873
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
4881
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
4889
ld (pow10exp_single),a ;The base-10 exponent
4893
ld de,pow10LUT ;get the table of 10^-(2^k)
4895
ld hl, pow10exp_single
4897
call singletostr_mul
4898
call singletostr_mul
4899
call singletostr_mul
4900
call singletostr_mul
4901
call singletostr_mul
4902
call singletostr_mul
4903
;now the number is pretty close to a nice value
4905
; If it is less than 1, multiply by 10
4910
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
4916
ld hl,pow10exp_single
4922
; Convert to a fixed-point number !
4936
;We need to get 7 digits
4938
pop hl ;Points to the string
4940
;The first digit can be as large as 20, so it'll actually be two digits
4944
;Increment the exponent :)
4945
ld de,(pow10exp_single-1)
4947
ld (pow10exp_single-1),de
4956
; Get the remaining digits.
4963
call singletostrmul10
4968
;Save the pointer to the end of the string
4975
jr c,rounding_done_single
4976
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
4985
rounding_done_single:
4988
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
5000
;Now lets move HL-DE bytes at DE+1 to DE
5012
;If z flag is reset, this means that the exponent should be bumped up 1
5013
ld a,(pow10exp_single)
5016
ld (pow10exp_single),a
5019
;if -4<=A<=6, then need to insert the decimal place somewhere.
5024
;for this, we need to insert the decimal after the first digit
5025
;Then, we need to append the exponent string
5027
ld de,strout_single-1
5029
cp '-' ;negative sign
5037
;remove any stray zeroes at the end before appending the exponent
5041
; Write the exponent
5044
ld a,(pow10exp_single)
5047
ld (hl),'-' ;negative sign
5065
ld de, strout_single
5068
ld a, l ; string size
5070
ld hl,strout_single-1
5074
ld a,(pow10exp_single)
5078
;need to put zeroes before everything
5081
cp '-' ;negative sign
5107
ld de,strout_single-1
5111
cp '-' ;negative sign
5122
ld hl,strout_single-1
5140
;multiply the 0.24 fixed point number at scrap by 10
5141
;overflow in A register
5176
;Check that the last digit isn't a decimal!
5230
;---------------------------------------------------------------------------------------------------------
5232
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
5233
;---------------------------------------------------------------------------------------------------------
5238
ptr_sto: equ scrap+9
5240
;;#Routines/Single Precision
5242
;; HL points to the string
5243
;; BC points to where the float is output
5245
;; scrap+9 is the pointer to the end of the string
5247
;; 11 bytes at scrap ?
5252
;Check if there is a negative sign.
5261
;Skip all leading zeroes
5264
jr z,$-4 ;jumps back to the `inc hl`
5267
;Check if the next char is char_DEC
5269
or a ;to reset the carry flag
5271
jr _54a ;.db $FE ;start of cp *
5278
jr z,$-5 ;jumps back to the `dec b`
5281
;Now we read in the next 8 digits
5287
;Now `scrap` holds the 4-digit base-100 number.
5289
;if carry flag is set, just need to get rid of remaining digits
5290
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
5301
jp z,strToSingle_inf
5304
;Now check for engineering `E` to modify the exponent
5308
;Gotta multiply the number at (scrap) by 2^24
5311
call scrap_times_256
5314
call scrap_times_256
5317
call scrap_times_256
5320
call scrap_times_256
5323
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
5331
jp z,strToSingle_zero-1
5335
jp m,strToSingle_normed
5336
;Will need to iterate at most three times
5349
;Move the number to scrap
5358
;now (scrap) is our number, need to multiply by power of 10!
5359
;Power of 10 is stored in B, need to put in A first
5367
jp nc,strToSingle_inf+1
5370
jp nc,strToSingle_zero
5394
cp char_NEG ;negative exponent?
5446
call scrap_times_sub
5459
jr nz,strToSingle_inf
5477
if defined roundSingle or defined MATH_FRCSGL
5479
;---------------------------------------------------------------------------------------------------------
5481
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
5482
;---------------------------------------------------------------------------------------------------------
5489
ld l, (ix) ; convert integer parameter to single float
5491
ld bc, 0x1000 ; bynary digits count + sign
5493
int2Single.test.zero:
5495
or h ; test if hl is not zero
5496
jr nz, int2Single.test.negative
5498
jr nz, int2Single.test.negative
5503
int2Single.test.negative:
5504
bit 7, h ; test if hl is negative
5505
jr z, int2Single.normalize
5506
ld c, 0x80 ; sign negative
5515
int2Single.normalize:
5518
jr nz, int2Single.mount
5521
jr int2Single.normalize
5524
res 7, h ; turn off upper bit
5526
ld a, c ; restore sign
5528
ld h, a ; ...into upper mantissa
5530
ld e, h ; sign+mantissa
5531
ld h, l ; high mantissa
5532
ld l, 0 ; low mantissa
5534
ld a, b ; binary digits count
5535
or 0x80 ; exponent bias
5540
ld (ix), l ; low mantissa
5541
ld (ix+1), h ; high mantissa
5542
ld (ix+2), e ; sign + mantissa
5543
ld (ix+3), d ; expoent
5552
if defined roundSingle or defined MATH_FRCINT
5554
;---------------------------------------------------------------------------------------------------------
5556
; http://0x80.pl/articles/convert-float-to-integer.html
5557
;---------------------------------------------------------------------------------------------------------
5560
; HL points to the single-precision float
5562
; HL is the 16-bit signed integer part of the float
5563
; BC points to 16-bit signed integer
5580
jr c,no_shift_single_to_int16
5582
jr nc,no_shift_single_to_int16
5604
jr _67a ;.db $11 ;start of ld de,*
5616
no_shift_single_to_int16:
5638
;---------------------------------------------------------------------------------------------------------
5639
; Auxiliary routines
5640
;---------------------------------------------------------------------------------------------------------
5647
const_pi: db $DB,$0F,$49,$81
5648
const_e: db $54,$f8,$2d,$81
5649
const_lg_e: db $3b,$AA,$38,$80
5650
const_ln_2: db $18,$72,$31,$7f
5651
const_log2: db $9b,$20,$1a,$7e
5652
const_lg10: db $78,$9a,$54,$81
5653
const_0: db $00,$00,$00,$00
5654
const_1: db $00,$00,$00,$80
5655
const_2: dw 0, 33024
5656
const_3: dw 0, 33088
5657
const_4: dw 0, 33280
5658
const_5: dw 0, 33312
5659
const_7: dw 0, 33376
5660
const_9: dw 0, 33552
5661
const_16: dw 0, 33792
5662
const_100: db $00,$00,$48,$86
5663
const_100_inv: dw 55050, 31011
5664
const_precision: db $77,$CC,$2B,$65 ;10^-8
5665
const_half_1: dw 0, 32512
5666
const_inf: db $00,$00,$40,$00
5667
const_NegInf: db $00,$00,$C0,$00
5668
const_NaN: db $00,$00,$20,$00
5669
const_log10_e: db $D9,$5B,$5E,$7E
5670
const_2pi: db $DB,$0F,$49,$82
5671
const_2pi_inv: db $83,$F9,$22,$7D
5672
const_half_pi: dw 4059, 32841
5673
const_p25: db $00,$00,$00,$7E
5674
const_p5: db $00,$00,$00,$7F
5677
sin_a1: dw 43691, 32042
5678
sin_a2: dw 34952, 30984
5679
sin_a3: dw 3329, 29520
5680
cos_a1: equ const_half_1
5681
cos_a2: dw 43691, 31530
5682
cos_a3: dw 2914, 30262
5683
exp_a1: db $15,$72,$31,$7F ;.693146989552
5684
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
5685
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
5686
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
5687
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
5688
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
5689
const_1p40625: db $00,$00,$34,$80 ;1.40625
5691
if defined MATH_CONSTSINGLE
5699
;A is the constant ID#
5700
;returns nc if failed, c otherwise
5701
;HL points to the constant
5702
cp (end_const-start_const)>>2
5709
;#if ((end_const-4)>>8)!=(start_const>>8)
5722
db $CD,$CC,$4C,$7C ;.1
5723
db $0A,$D7,$23,$79 ;.01
5724
db $17,$B7,$51,$72 ;.0001
5725
db $77,$CC,$2B,$65 ;10^-8
5726
db $95,$95,$66,$4A ;10^-16
5727
db $1F,$B1,$4F,$15 ;10^-32
5730
db $00,$00,$20,$83 ;10
5731
db $00,$00,$48,$86 ;100
5732
db $00,$40,$1C,$8D ;10000
5733
db $20,$BC,$3E,$9A ;10^8
5734
db $CA,$1B,$0E,$B5 ;10^16
5735
db $AE,$C5,$1D,$EA ;10^32
5742
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
5743
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
5744
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
5745
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
5746
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
5747
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
5748
;C>=2 15+{0,20+{0,8}}
5751
;avg: 349.21279907227cc
5842
;26 bytes, adds 118cc to the traditional routine
5877
;c flag means don't increment the exponent
5880
jr c,ascii_to_uint8_noexp
5882
jr z,ascii_to_uint8_noexp-2
5886
jr nc,ascii_to_uint8_noexp_end
5898
jr z,ascii_to_uint8_noexp_2nd
5902
jr nc,ascii_to_uint8_noexp_end
5913
ascii_to_uint8_noexp:
5916
jr nc,ascii_to_uint8_noexp_end
5923
ascii_to_uint8_noexp_2nd:
5928
jr nc,ascii_to_uint8_noexp_end
5931
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
5932
ascii_to_uint8_noexp_end:
5942
if defined MATH_RSUBSINGLE
5963
jp addInject ;jumps in to the addSingle routine
5967
if defined MATH_MOD1SINGLE
5969
;This routine performs `x mod 1`, returning a non-negative value.
5992
jr z,mod1Single_special
6005
;If it is zero, need to set exponent to zero and return
6028
;make sure it isn't zero else we need to add 1
6040
;If INF, need to return NaN instead
6041
;For 0 and NaN, just return itself :)
6061
if defined MATH_FOUT
6063
; --------------------------------------------------------------
6064
; Converts a signed integer value to a zero-terminated ASCII
6065
; string representative of that value (using radix 10).
6067
; Brandon Wilson WikiTI
6068
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
6069
; --------------------------------------------------------------
6071
; HL Value to convert (two's complement integer).
6072
; DE Base address of string destination. (pointer).
6073
; --------------------------------------------------------------
6076
; --------------------------------------------------------------
6077
; REGISTERS/MEMORY DESTROYED
6079
; --------------------------------------------------------------
6085
; Detect sign of HL.
6089
; HL is negative. Output '-' to string and negate HL.
6094
; Negate HL (using two's complement)
6098
ld a, 0 ; Note that XOR A or SUB A would disturb CF
6102
; Convert HL to digit characters
6104
ld b, 0 ; B will count character length of number
6107
call div_hl_c; HL = HL / A, A = remainder
6114
; Retrieve digits from stack
6122
; Terminate string with NULL
6133
ld a, l ; string size
6141
;===============================================================
6142
; Convert a string of base-10 digits to a 16-bit value.
6143
; http://z80-heaven.wikidot.com/math#toc32
6145
; DE points to the base 10 number string in RAM.
6147
; HL is the 16-bit value of the number
6148
; DE points to the byte after the number
6153
; A (actually, add 30h and you get the ending token)
6156
; n is the number of digits
6158
; at most 595 cycles for any 16-bit decimal value
6159
;===============================================================
6162
ld hl,0 ; 10 : 210000
6164
StrToInt.Skip_spaces:
6170
jr z, StrToInt.Skip_spaces
6174
jr nz, StrToInt.ConvLoop
6176
StrToInt.ConvLoop: ;
6194
jr nc, StrToInt.ConvLoop ;12|23: 30EE
6196
jr StrToInt.ConvLoop ; --- : 18EB
6214
; return remainder in a
6215
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
6236
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
6266
djnz div_dehl_c.loop
6274
;---------------------------------------------------------------------------------------------------------
6275
; VARIABLES INITIALIZE
6276
;---------------------------------------------------------------------------------------------------------
6280
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
6281
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
6282
ld (VAR_DUMMY.POINTER), hl
6283
ld b, VAR_DUMMY.LENGTH
6288
djnz INITIALIZE_DUMMY.1
6293
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
6295
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
6298
INITIALIZE_VARIABLES:
6299
call INITIALIZE_DATA
6300
call INITIALIZE_DUMMY
6303
call gfxInitSpriteCollisionTable
6306
;if defined COMPILE_TO_ROM
6307
; ld ix, BIOS_JIFFY ; initialize rom clock
6316
ld c, 0 ; variable name 1 (variable number)
6317
ld b, 255 ; variable name 2 (type flag=fixed)
6318
call INIT_VAR ; variable initialize
6322
;---------------------------------------------------------------------------------------------------------
6323
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
6324
;---------------------------------------------------------------------------------------------------------
6326
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6329
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
6331
if pgmPage1.pad >= 0
6334
; .WARNING "There's no free space left on program page 1"
6339
VAR_STACK.START: equ ramArea
6340
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
6342
VAR_STACK.POINTER: equ VAR_STACK.START
6344
PRINT.CRLF: db 3, 0, 0, 2
6345
dw PRINT.CRLF.DATA, 0, 0, 0
6346
PRINT.CRLF.DATA: db 13,10,0
6348
PRINT.TAB: db 3, 0, 0, 1
6349
dw PRINT.TAB.DATA, 0, 0, 0
6350
PRINT.TAB.DATA: db 09,0
6353
LIT_NULL_DBL: dw 0, 0, 0, 0
6359
LIT_QUOTE_CHAR: db '\"'
6362
LIT_TRUE: db 2, 0, 0
6366
LIT_FALSE: db 2, 0, 0
6371
LIT_4: db 3, 0, 0, 3
6374
LIT_4_DATA: db "CCC", 0
6377
LIT_6: db 3, 0, 0, 3
6380
LIT_6_DATA: db "CDE", 0
6383
IDF_8: equ VAR_STACK.POINTER + 0
6390
LIT_12: db 3, 0, 0, 3
6391
dw LIT_12_DATA, 0, 0
6393
LIT_12_DATA: db "DDD", 0
6396
LIT_13: db 3, 0, 0, 3
6397
dw LIT_13_DATA, 0, 0
6399
LIT_13_DATA: db "EDC", 0
6406
LIT_15: db 3, 0, 0, 3
6407
dw LIT_15_DATA, 0, 0
6409
LIT_15_DATA: db "EEE", 0
6411
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 11
6413
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
6414
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
6415
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
6416
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
6418
VAR_DUMMY.SIZE: equ 8
6419
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
6420
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
6421
VAR_STACK.END: equ VAR_DUMMY.END + 1
6423
;--------------------------------------------------------
6425
;--------------------------------------------------------
6428
DATA_ITEMS_COUNT: equ 0
6430
DATA_SET_ITEMS_START:
6431
DATA_SET_ITEMS_COUNT: equ 0
6434
;---------------------------------------------------------------------------------------------------------
6435
; PROGRAM BASIC ROM HOOKS
6436
;---------------------------------------------------------------------------------------------------------
6438
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6443
__call_bios BIOS_ENASLT ; Select main ROM on page 1 (4000h~7FFFh)
6446
PROGRAM_SLOT_1_ENABLE:
6452
and 3 ;Keep bits corresponding to the page
6469
jp BIOS_ENASLT ; Select the ROM on page 4000h-7FFFh
6476
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6477
ld de, PROGRAM_SLOT_1_ENABLE
6485
push hl ; address of string
6486
push af ; size of string
6494
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6495
call BASIC_SLOT_ENABLE
6498
jp BASIC_DRAW_DIRECT
6502
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
6505
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6506
ld de, PROGRAM_SLOT_1_ENABLE
6514
ld hl, BIOS_TEMP ; voice count
6529
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6530
call BASIC_SLOT_ENABLE
6533
jp BASIC_PLAY_DIRECT
6537
if defined gfxBorderFill
6540
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6541
ld de, PROGRAM_SLOT_1_ENABLE
6545
ld bc, (BIOS_GRPACX)
6547
ld de, (BIOS_GRPACY)
6568
call gfxIsScreenModeMSX2
6569
jr nc, PAINT_HOOK.1 ; if MSX2 and screen mode above 3, jump (BASIC_SUB_PAINT2)
6574
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6575
call BASIC_SLOT_ENABLE
6587
;---------------------------------------------------------------------------------------------------------
6589
;---------------------------------------------------------------------------------------------------------
6591
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6595
pgmPage2.pad: equ romSize - (romPad - pgmArea)
6597
if pgmPage2.pad >= 0
6600
if pgmPage2.pad < lowLimitSize
6601
.WARNING "There's only less than 5% free space on this ROM"
6604
.ERROR "There's no free space left on this ROM"
6609
end_file: end start_pgm ; label start is the entry point